转载部分

mysql中的事务隔离性

2019-06-18  本文已影响30人  PENG先森_晓宇

事务拥有原子性、隔离性、一致性、持久性(acid)。

注意:mysql中的innodb引擎支持事务,myisam不支持。

mysql中的事务自动提交AUTOCOMMIT

在Innodb存储引擎中事务默认采取自动提交的模式,也就是说,如果不是显式的开始一个事务,则每个sql都当作是一个事务执行操作,同样遵循mvcc,直到显式的执行commit或者rollback表示该事务结束。
在当前连接中可以设置autocommit变量来启用或者关闭自动提交,0为关闭,1为开启。

 mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 行于数据集 (0.04 秒)

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 秒)

测试:客户端A和客户端B设置autocommit为关闭状态,对行加排他锁
1.在客户端A加锁

mysql> update users set name='ds' where id=12;
Query OK, 1 rows affected (0.02 秒)
  1. 在客户端B上也update
mysql> update users set name='ds' where id=12;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

按照之前的理解,在客户端B上肯定是不会加锁的,且是可以执行成功的。事实证明显id为12的数据已经加来排他锁。

  1. 在客户端A上执行commit后,客户端B上id为12的行获得排他锁,执行成功
mysql> commit;
 Query OK, 0 rows affected (0.01 秒)

注意:修改autocommit变量对非事务类型的表,比如myisam或者内存表,不会有任何的影响,对这类表没有commit或者rollback的概念,也可以说一只处于autocommit启用的模式

误区:之前以为只有start transaction来算是事务执行,其实每个sql都是一个事务的提交,只不过在innodb中是采用的自动提交的方式。

并发造成事务隔离性破坏

事务的隔离级别
mysql的innodb引擎的相应的隔离级别可以解决相应的上述问题。innodb默认的隔离级别是repeatable_read,这个级别可以解决第一类更新失败、第二类更新失败、脏读、不可重复读的问题,没解决幻读问题

注意:隔离级别越高,出现的并发问题就越少,但是这样消耗的性能更大,而且并发能力会很低。所以适合自己的隔离级别是最重要的。

由于事务第一类更新丢失和第二类更新丢失不会发生,这里不做讨论。


隔离级别

事务相关命令

测试
在A客户和B客户端同时修改事务隔离级别。使用以下命令来设置隔离级别,使用session。特别注意:更改隔离级别之后测试的事务不能使用之前的事务,之前的事务应该commit或者rollback。在更改完隔离级别之后重新start事务测试。

set session transaction isolation level [read uncommitted | read committed | repeatable read | serializable]




  1. 在客户端B上,更新id为24数据,由于共享锁不能加派台锁,所以报加锁问题

     mysql> update y_login set phone=23 where id=24;
     ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
  2. 在客户端上,新增一条记录,由于每条记录间加了间隙锁,所以报加锁问题。

     mysql> insert into y_login(phone) values(2442);
     ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    

现在解决了写幻读和读幻读的的问题,但是效率很差,很多超时的现象出现。repeatable read隔离级别只能解决写幻读的问题。

  1. 在做个测试,在客户端A查询id小于24的数据集

     mysql> start transaction;
     Query OK, 0 rows affected (0.00 sec)
    
     mysql> select * from y_login where id<24;
     +----+---------+---------+-----------+
     | id | phone   | islogin | logintime |
     +----+---------+---------+-----------+
     | 21 | 5623656 |    NULL | NULL      |
     | 23 | 54115   |    NULL | NULL      |
     +----+---------+---------+-----------+
    

这里是将id小于24的数据集都加共享锁,猜想如果插入一条数据id小于24会报加锁问题,如果id大于24的话应该可以插入。在客户端B插入数据

    insert into y_login(id,phone) values(25,2442);
    Query OK, 1 row affected (0.01 sec)
    insert into y_login(id,phone) values(1,2442);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

证明猜想正确。

思考

前提是innodb存储引擎下,在myisam存储引擎下是不支持事务的。在各个隔离级别下,事务如果没提交,但是sql已经执行完后,会直接修改写入数据库文件吗?还是必须在事务结束后才会写到数据库文件呢?

之前认为是事务开始后,执行了sql语句后,数据库就会立马改变,最后数据库在根据事务的结果进行rollback还是commit。一个不经意的测试,结果并不是这样,数据库而是在事务结束后根据事务的状态来判断是否更新状态。

总结

在隔离性为read uncommitted,read committed,repeatable read和serializable的事务中会对写操作的行加共享锁。在隔离性为serializable的事务中会对读到的数据集加共享锁也会对写操作的行加共享锁。

上一篇 下一篇

猜你喜欢

热点阅读