mysql中的事务隔离性

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

  • 原子性:要么全成功,要么全失败。
  • 隔离性:事务之间必须保持隔离性,互补干扰,不然会出现脏读,不可重复读和幻读
  • 一致性:事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。分布式中难点就是事务的一致性
  • 持久性:事务提交的结果,将永远保持在数据库中。

注意:

  • mysql中的innodb引擎支持事务,myisam不支持。
  • 代码中不要使用大事务。因为事务都是mysql操作,大多数mysql操作会数据行加共享锁或者排他锁的,这些锁都是阻塞操作,只有在事务结束后这些锁才会释放。如果事务执行时间较长,锁将一直不会释放,如果其他请求也正好获得锁住的数据行,则会一直等待,等待那个大事务结束后才能获得。

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中是采用的自动提交的方式。

并发造成事务隔离性破坏

  • 第一类丢失更新
    在没有事务隔离的情况下,俩个事务都同时更新一条数据,但是第二个事务却中途退出,导致对第一个事务的更新失败。
    例:张三读取火车票,查询剩余500张,同时李四也查询并且购买了一张,剩余499张,并且修改了数据库。随后,张三购买过程中发生异常,导致事务回滚,票数恢复为500,这样就导致了李四的修改失败。

  • 第二类丢失更新,不可重复读的特例
    俩个并发事务同时读取一行数据,然后其中一个对其进行了修改,另一个也进行了修改,然后造成了第一个写操作的失败。
    例:现在火车票还剩500张, 张三读取到票还有500张,操作还未完成,事务还未提交。于此同时,李四购买了一张票,剩余票为499.最后提交了事务。随后,张三也购买了一张,剩余票又为499,但是应该是498。覆盖了张三的事务。

  • 脏读
    是指事务a修改了数据库的某行数据,但并未提交事务时,事务b这时要取这行数据进行相关操作,读到的数据是a已经修改过的了,接着a事务由于某些原因回退。导致b事务读到的这行数据和数据库不一致,这就是脏读。
    例:火车票剩余500张,张三购买了一张火车票,数据库修改但是事务没提交
    随后李四也要购买,查到剩余火车票为499张,接着张三发生突发事情,事务回退,火车票剩余500张,这时张三读到的数据和数据库不一致,导致脏读。

  • 不可重复读
    不可重复读是指在对于数据库中的某条数据,一个事务范围内多次查询返回不同的数据值(这里不同是指某一条或多条数据的内容前后不一致,但数据条数相同),这是由于在查询间隔,该事务需要用到的数据被另一个事务修改并提交了。需要注意的是在某些情况下不可重复读并不是问题。不可重复读可能发生在update,delete操作中

    例:现在火车票有500张, 张三购买火车票,读到还有500张,操作还没有完成,事务还没完成。于此同时李四也要买车票,并且购买了一张,并提交了事务。这是张三又查了一遍火车票,变成了499张,在一个事务中前后读取到的俩次数据不一致,导致了不可重复读。

  • 幻读

幻读:幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读可能发生在update,delete操作中,而幻读发生在insert操作中

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

  • read-uncommited(读取未提交的)
    在该隔离级别,所有事务都可以看到其他事务未提交的执行结果。这是事务最低的隔离级别。解决第一类丢失更新和第二类丢失的问题,但是会出现脏读、不可重复读、幻读 。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少

  • read-commited(读取已提交的)
    保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取其他事务未提交的数据。解决第一类丢失更新、第二类丢失更新和脏读的问题,但会出现不可重复读和幻读问题

  • repeatable-read(可重复读)
    保证一个事务相同条件下前后两次获取的数据是一致的,为什么能保证前后俩次获取的数据是一致的呢?原因就是mvcc,查询时只能查到创建版本小于等于数据的当前版本且删除版本大于当前版本的数据。解决第一类丢失更新,第二类丢失更新,脏读、不可重复读的问题,但会出现部分幻读的问题,为什么是部分呢?请看下面的测试

  • serializable(串行化)
    在这个级别,可能导致大量的超时现象和锁竞争。只要操作产生了共享锁,就不允许其他事务修改!简而言之也是解决幻读的关键--也就是事务会将读取的数据集(数据行)加上共享锁和间隙锁,其他事务不能写,可以读,直到事务结束,共享锁释放。它解决了所有隔离性相关问题,但是生产环境不会使用该隔离级别,效率太低。

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

事务相关命令

  • 查看当前的事务隔离级别

    select @@global.tx_isolation; //查看全局的隔离级别,生产环境使用
    select @@session.tx_isolation;//查看当前会话级别,下面测试使用它查询
    select @@tx_isolation;
    
  • 设置事务隔离级别

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

    1.默认的行为(不带session和global)是为下一个(未开始)事务设置隔离级别,得重新开启事务,不能使用本次事务
    2.如果你使用GLOBAL关键字,是在修改完之后创建的所有新连接(除了不存在的连接)设置默认事务级别,你需要SUPER权限来做这个,生产环境使用global。
    3.使用SESSION 关键字将在当前连接上执行的事务设置默认事务级别,不需要重新开启事务。 任何客户端都能自由改变会话隔离级别(甚至在事务的中间),或者为下一个事务设置隔离级别。

  • mysql命令

    start transaction;//开始事务
    ......
    sql语句
    ......
    commit;//提交事务
    rollback;//事务回滚
    

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

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


  • 客户端A和B隔离级别为read uncommitted
    1.客户端A开启事务:

    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 666666  |    NULL | NULL      |
    +----+---------+---------+-----------+
    3 rows in set (0.00 sec)
    
    1. 客户端b也同时开启事务:
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 666666  |    NULL | NULL      |
    +----+---------+---------+-----------+
    3 rows in set (0.00 sec)
    

    3.客户端A上修改id为22的数据

    mysql> update y_login set phone=888 where id=22;      
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 888     |    NULL | NULL      |
    +----+---------+---------+-----------+
    
    1. 在客户端b上查询id为22的数据

       mysql> select * from y_login;
       +----+---------+---------+-----------+
       | id | phone   | islogin | logintime |
       +----+---------+---------+-----------+
       | 20 | 896     |       2 | NULL      |
       | 21 | 5623656 |    NULL | NULL      |
       | 22 | 888     |    NULL | NULL      |
       +----+---------+---------+-----------+
      

    可以看到读到了客户端A未提交的事务,这时就肯定造成了脏读。用户可以拿到这个未提交的phone数据做一些其他的相关操作,最后客户端A如果rollback的话,关于phone的一系列操作都是错误的。

    5.在客户端B上对id为22的数据进行写操作

    mysql> update y_login set phone=999 where id=22;      
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    

    可以看到由于客户端A对这行数据进行了写操作,所以这行数据加锁了,这行数据可以读,但是不能写,所以事务中进行写操作的行会加排他锁(innodb中是行锁,排他锁简称写锁),就避免了第二类更新丢失的问题。将客户端A的事务commit或者rollback后,客户端A释放排他锁,客户端B抢到排他锁后可进行读写操作,客户端A释放时间为6.94s。

    mysql> update y_login set phone=999 where id=22;
    Query OK, 1 row affected (6.94 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    在客户端 C上,没有开启事务正常执行id为22的写操作时,同样被加锁,只有客户端A释放排他锁时,才能执行id为22的写操作。


  • 客户端A和B隔离级别为read committed
    1.客户端A开启事务:

    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 8823    |    NULL | NULL      |
    +----+---------+---------+-----------+
    3 rows in set (0.00 sec)
    
    1. 客户端b也同时开启事务:
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 8823    |    NULL | NULL      |
    +----+---------+---------+-----------+
    3 rows in set (0.00 sec)
    

    3.客户端A上修改id为22的数据

    mysql> update y_login set phone=888 where id=22;      
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 888     |    NULL | NULL      |
    +----+---------+---------+-----------+
    
    1. 在客户端B上查看数据,id为22的phone依然是88823,不会读取未提交的事务的数据。

       mysql> select * from y_login;
       +----+---------+---------+-----------+
       | id | phone   | islogin | logintime |
       +----+---------+---------+-----------+
       | 20 | 896     |       2 | NULL      |
       | 21 | 5623656 |    NULL | NULL      |
       | 22 | 88823   |    NULL | NULL      |
       +----+---------+---------+-----------+
      
    2. 在做个测试,在客户端B上修改id为22的数据

       mysql> update y_login set phone=888 where id=22;
       ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
      

      可以看到由于客户端A对这行数据进行了写操作,所以这行数据加锁了,这行数据可以读,但是不能写,所以事务中进行写操作的行会加排他锁,就避免了第二类更新丢失的问题。将客户端A的事务commit或者rollback后,客户端A释放排他锁,客户端B抢到排他锁后可进行读写操作,客户端A释放时间为18s。

       mysql> update y_login set phone=999 where id=22;
       Query OK, 1 row affected (18 sec)
       Rows matched: 1  Changed: 1  Warnings: 0
      

    在客户端 C上,没有开启事务正常执行id为22的写操作时,同样被加锁,只有客户端A释放排他锁时,才能执行id为22的写操作。

    1. 当客户端A提交事务,后查看客户端B的数据

      mysql> select * from y_login;
      +----+---------+---------+-----------+
      | id | phone   | islogin | logintime |
      +----+---------+---------+-----------+
      | 20 | 896     |       2 | NULL      |
      | 21 | 5623656 |    NULL | NULL      |
      | 22 | 888     |    NULL | NULL      |
      +----+---------+---------+-----------+
      

    可以看到id为22的数据已经更新,读取的是事务提交后的数据。这样就避免了脏读,但是出现了不可重复读的问题。


  • 客户端A和B隔离级别设置为 repeatable read
    1.客户端A开启事务:

    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 888     |    NULL | NULL      |
    +----+---------+---------+-----------+
    3 rows in set (0.00 sec)
    
    1. 客户端b也同时开启事务:
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 888     |    NULL | NULL      |
    +----+---------+---------+-----------+
    3 rows in set (0.00 sec)
    

    3.客户端A上修改id为22的数据

    mysql> update y_login set phone=333 where id=22;      
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> select * from y_login;
    +----+---------+---------+-----------+
    | id | phone   | islogin | logintime |
    +----+---------+---------+-----------+
    | 20 | 896     |       2 | NULL      |
    | 21 | 5623656 |    NULL | NULL      |
    | 22 | 333     |    NULL | NULL      |
    +----+---------+---------+-----------+
    
    1. 在客户端B上查看数据,id为22的phone依然是888,不会读取未提交的事务的数据。

       mysql> select * from y_login;
       +----+---------+---------+-----------+
       | id | phone   | islogin | logintime |
       +----+---------+---------+-----------+
       | 20 | 896     |       2 | NULL      |
       | 21 | 5623656 |    NULL | NULL      |
       | 22 | 888     |    NULL | NULL      |
       +----+---------+---------+-----------+
      
    2. 在做个测试,在客户端B上修改id为22的数据

       mysql> update y_login set phone=545 where id=22;
       ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
      

    可以看到由于客户端A对这行数据进行了写操作,所以这行数据加锁了,这行数据可以读,但是不能写,所以事务中进行写操作的行会加排他锁,就避免了第二类更新丢失的问题。将客户端A的事务commit或者rollback后,客户端A释放排他锁,客户端B抢到排他锁后可进行读写操作,客户端A释放时间为18s。

        mysql> update y_login set phone=545 where id=22;
        Query OK, 1 row affected (18 sec)
        Rows matched: 1  Changed: 1  Warnings: 0
    

    在客户端 C上,没有开启事务正常执行id为22的写操作时,同样被加锁,只有客户端A释放排他锁时,才能执行id为22的写操作。

    1. 当客户端A提交事务,后查看客户端B的数据

       mysql> select * from y_login;
       +----+---------+---------+-----------+
       | id | phone   | islogin | logintime |
       +----+---------+---------+-----------+
       | 20 | 896     |       2 | NULL      |
       | 21 | 5623656 |    NULL | NULL      |
       | 22 | 888     |    NULL | NULL      |
       +----+---------+---------+-----------+
      

    可以看到id为22的依然是888,并没有因为客户端A的提交而改变数据。所以不可重复读问题解决。

    有的人说repeatable read级别可以解决幻读,有的人说解决不了,其实是解决了一部分,解决读幻读,并没有解决写幻读,请看下面的测试,也可以看这篇文章,对这一现在进行很好的测试。

    有一张表如下


    执行流程如下

    发现事务1中的俩次select结果相同,且只有初始化的一条,后勤部,并没有事务2中insert的数据,这是因为MVCC的原因,mvcc查询时只查询创建版本小于或等于当前版本的原因,所以事务1中并没有查到事务2中的数据,那既然这样串行化serializable级别读貌似就没啥意义了,带着疑问继续测试

测试前数据如下


在事务1中select查询到1条数据,但是在update时却更新了2条数据,出现了幻读的现象。

这种结果告诉我们其实在MySQL可重复读的隔离级别中并不是完全解决了幻读的问题,而是解决了读数据情况下的幻读问题。而对于修改的操作依旧存在幻读问题,就是说MVCC对于幻读的解决时不彻底的。
为了解决写幻的情况,serializable隔离级别就派上用场了,在serializable级别中的类似select * from dept这样的语句也会加上共享锁,且会加上间隙锁(在其他隔离级别中此语句属于快照读,不加锁),这样就导致在事务2中的插入操作为阻塞状态,直到事务1中commit之后才会进行事务2中的update操作,进而解决了写幻读,可以看出serializable级别解决写幻读的关键是间隙锁

  • 客户端A和B设置隔离级别为serializable
    事务会将获取的数据集加上一个共享锁,且每条数据间都会加上间隙锁,相关数据集的写操作都不能进行,直到共享锁释放也就是事务提交。
    1. 在客户端A开始事务并查询

       mysql> start transaction;
       Query OK, 0 rows affected (0.00 sec)
      
       mysql> select * from y_login;
       +----+---------+---------+-----------+
       | id | phone   | islogin | logintime |
       +----+---------+---------+-----------+
       | 21 | 5623656 |    NULL | NULL      |
       | 23 | 54115   |    NULL | NULL      |
       | 24 | 541115  |    NULL | NULL      |
       +----+---------+---------+-----------+
      
    这时就在id为21到24的数据集加上了共享锁和间隙锁,更准确的说是把这个表里面的所有数据行加了共享锁和间隙锁,因为并没有查询条件,之前插入或删除修改该表的数据都会提示加锁,不能操作。
  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

    1. 在客户端A开始事务,此时数据库中id为12的password为42222

       mysql> start transaction;
       Query OK, 0 rows affected (0.01 秒)
      
       mysql> select password from users where id=12;
       +----------+
       | password |
       +----------+
       | 42222    |
       +----------+
      
    2. 继续在客户端操作,更改id为12的password

       mysql> update users set password=333 where id=12;
       Query OK, 1 rows affected (0.01 秒)
      
    3. 在客户端B上查询,已经变为33

       mysql> select password from users where id=12;
       +----------+
       | password |
       +----------+
       | 333      |
       +----------+
      
    4. 查看数据库,此时数据未改变333,仍然未事务开始时数据42222


      WX20190620-175004@2x.png
    5. 在客户端A上事务commit后,数据改变为333。

    测试剩余的隔离级别也是同样情况,磁盘中的mysql文件只有在事务提交后才会相应的改变,也就是在事务结束后才会io操作。至于sql为什么查询出来的改变了?
    引用《高性能mysql》一说中所说,是因为事务日志,事务日志可以提高事务的效率,使用事务日志,存储引擎在修改表数据的时候只需要修改并往内存拷贝,事务提交后在将内存的行记录记录到硬盘中的文件中,而不用每次将修改的数据本身持久化到磁盘中。事务日志持久之后,内存中修改的数据可以慢慢刷新到磁盘中。如果数据的修改已经记录到事务日志并持久化,但数据还没有同步到磁盘,此时系统崩溃的话,存储引擎会在重启后自动回复这部分修改的内容。

    由此知道了,事务的执行顺序是start transaction,随后写入事务日志,如果rollback的话根据事务日志中的事务id进行回退,如果commit的话将事务日志中的内容写入到磁盘中进行持久化。

redo undo日志

说到事务日志就必须说一下redo undo日志,首先介绍一个这俩分别是什么

  • undo日志

用于存放数据修改被修改前的值,假设修改 tba 表中 id=2的行数据,把Name=’B’ 修改为Name = ‘B2’ ,那么undo日志就会用来存放Name=’B’的记录,如果这个修改出现异常,可以使用undo日志来实现回滚操作,保证事务的一致性,事务能够回滚也正是因为undo日志的存在。

  • redo日志

当数据库对数据做修改的时候,需要把数据页从磁盘读到buffer pool中,然后在buffer pool中进行修改,那么这个时候buffer pool中的数据页就与磁盘上的数据页内容不一致,称buffer pool的数据页为dirty page 脏数据,如果这个时候发生非正常的DB服务重启,那么这些数据还没在内存,并没有同步到磁盘文件中(注意,同步到磁盘文件是个随机IO),也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool 中的data page变更结束后,把相应修改记录记录到这个文件(注意,记录日志是顺序IO),那么当DB服务发生crash的情况,恢复DB的时候,也可以根据这个文件的记录内容,重新应用到磁盘文件,数据保持一致。

这个文件就是redo log ,用于记数据修改后的记录,顺序记录。它可以带来这些好处:

  • 当buffer pool中的dirty page 还没有刷新到磁盘的时候,发生crash,启动服务后,可通过redo log 找到需要重新刷新到磁盘文件的记录;
  • buffer pool中的数据直接flush到disk file,是一个随机IO,效率较差,而把buffer pool中的数据记录到redo log,是一个顺序IO,可以提高事务提交的速度;

俩者区别可以看这边文章,我总结下吧,undo日志是记录数据修改前的数据,使事务可以回滚,保证事务的一致性,而redo日志保存的数据修改后的数据,保证了数据不丢失。在事务中redo 和undo日志都会写入。

总结

在隔离性为read committed,repeatable read事务中会对写操作的行加排他锁,读不加锁(mvcc适用于这俩种隔离级别)。在read uncommitted隔离级别中写操作不加排他锁,数据之间会共享。在隔离性为serializable的事务中会对读到的数据集加共享锁也会对写操作的行加排他锁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容