mysql中的事务、锁与线程安全

来源:互联网 发布:sdspage电泳结果数据 编辑:程序博客网 时间:2024/05/22 14:14

事务具有ACID特性,锁只是实现这些特性的必须机制。

mysql> SELECT @@tx_isolation;+----------------+| @@tx_isolation |+----------------+| SERIALIZABLE   |+----------------+1 row in set (0.00 sec)mysql> BEGIN ;Query OK, 0 rows affected (0.00 sec)mysql> SELECT @temp:=tag FROM orders WHERE id = 1;+------------+| @temp:=tag |+------------+|         0 |+------------+1 row in set (0.00 sec)mysql> UPDATE orders SET tag=@temp+1 WHERE id = 1;Query OK, 1 row affected (0.04 sec)Rows matched: 1  Changed: 1  Warnings: 0mysql> COMMIT;

以上代码使用了最高的事务隔离级别,是否意味着线程安全?

答案为否。

做如下验证,开启两个SESSION:S1与S2:

  1. S1开启事务并读入@temp
  2. S2开启事务并读入@temp
  3. S1修改tag+1并提交事务
  4. S2修改tag+1并提交事务

最后tag的结果为1,WHY?
原因在于@temp

做如下改动:

mysql> UPDATE orders SET tag= tag+1 WHERE id = 1;Query OK, 1 row affected (0.04 sec)Rows matched: 1  Changed: 1  Warnings: 0

流程不变,结果tag为2,发生了什么?
Mysql在Update时,对记录加了排他锁,直到事务提交以后将其释放。
所以,在事务中,执行单个UPDATE|DELTET|INSERT语句是线程安全的。但是,执行多个语句却未必安全。通常,作为前置条件,@temp的存在是必要的。假设,当@temp>0时,不希望再增加tag的值,也就是,最终希望结果为1。然而,只依赖事务隔离却无法保证结果的准确性。如果对并发情况下的结果要求很严格,那么如何保证事务的线程安全?

加悲观锁:

mysql> SELECT @temp=tag FROM orders WHERE id = 1 FOR UPDATE;+-----------+| @temp=tag |+-----------+|         0 |+-----------+1 row in set (0.00 sec)

这样,S1便独占了排他锁,直到事务提交后释放。
需要注意的是,事务的作用域要尽量小,操作的记录要尽量少,以及记录中避免出现热点数据。

总之,Mysql事务的绝对线程安全,需要用悲观锁保证。

原创粉丝点击