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

事务具有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: 0

mysql> 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事务的绝对线程安全,需要用悲观锁保证。

你可能感兴趣的:(Mysql)