Big Bug Ban

兴趣 践行 创新

Archive for the ‘线程’ tag

线程安全和一条sql语句引起的思考(二)

 

接着上次的文章讨论php线程安全

当然不仅仅针对php

范大牛和我争了一下..不过说来问题是确实存在的

那怎么解决呢

其实解决方式有很多种

1. 把运算放在php里面
代码如下:

//如果大于0 就减100
if($money>0){
    $money -=100;
    $res=mysql_query("UPDATE `test` SET `k` = $money where id=1");
    echo "减了!";
}else{
    echo "没做操作";
}
结果:这样虽然会做重复的sql更新操作 但是结果都是正确的值
2.加上限定where
3.使用事务来控制这个地方我想到其实就是传说中的事务控制但是使用的时候又发现了问题
首先必须把mysql的存储方式改成innodb,5.5默认已经是这个了
只有这个才支持事务的控制来看看第一次写的版本
<?
$host="127.0.0.1";
$user="root";
$passwd="123";

mysql_connect($host,$user,$passwd);
mysql_select_db("test");
mysql_query("set names utf8");

mysql_query("BEGIN");
//查询是否是大于0的
$res=mysql_query("select * from test where id=1 and k=100");
$res=mysql_fetch_array($res);
$money=$res['k'];

//如果大于0 就减100
if($money>0){
    $res=mysql_query("UPDATE `test` SET `k` = `k`-100 where id=1");
    $err_msg = mysql_error();
    if($err_msg!=""){
	echo "回滚了";
	mysql_query("ROLLBACK");
	}
     else{
	echo "提交了";
	mysql_query("COMMIT");
      }
}else{
    echo "没做操作";
}

?>
结果是错误的!
和之前的运行效果一样,事务并没有进行处理
为什么呢,一直没搞懂了.难道事务是不管脏数据的?
第二个线程读到的数据已经被第一个改掉了.按理说应该会抛错啊?
直到今天!  终于找到原因了
原来在数据库里面,事务的隔离是分级别的

要加上这一句..
mysql_query(“SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;”);
加在mysql_query(“BEGIN“); 后面就可以了
事务的控制是人为触发.抛错只会在sql执行中
所以其实那个rollback是多余的

不过这样的结果是效率降低
其他的数据库操作需要等待前一个commit之后才能进行
然后还有其他的办法,就是锁行

$res=mysql_query(“select * from test where id=1 and k=100 for update“);

这样改动后其他的必须在执行了update之后才能进行读取,就保证了读取的不是脏数据

参考资料:

http://man.ddvip.com/database/innodbzh/8.htm

Written by princehaku

九月 17th, 2011 at 10:08 下午