Mysql事务特性

事务概念

  事务可由一条sql或者一组sql组成。事务是访问并更新数据库中各种数据项的一个程序执行单元。

  事务会把数据库从一种一致状态转换为另一种一致状态。在数据提交工作时,可以确保要么所有修改都已经保存了,要么所有修改都不保存。

  事务需要满足ACID特性(不同厂商、mysql不同的存储引擎并非严格遵守ACID)。

ACID特性

  原子性(atomicity)

    事务是不可分割的工作单位,事务中的多个操作是一个整体,要么都做,要么都不做

  一致性(consistency)

    一致性指事务将数据库从一种一致状态转变为下一种一致状态。事务开始之前和结束之后,数据库的完整性约束并没有被破坏。如果事务中某个动作失败了,系统可以自动撤销事务并返回事务开始之前的状态。

  隔离性(isolation)

    多个事务之间彼此隔离,通过锁实现。一个事务对数据的操作在提交前是对其他事务隐藏的,隔离的。

  持久性(durability)

    事务一旦提交,其结果就是永久性的。就是发生宕机等故障,数据库也能将提交的数据恢复。mysql通过redo log实现。

  

   原子性由redo log(重做日志)实现;

   一致性由redo log和undo log(回滚段)实现;

   持久性由redo log实现;

   隔离性由锁实现。

事务隔离级别

  大部分数据库都没有提供真正的隔离性,隔离性可以保证正确性但却无法保证性能。

  RU(Read Uncommitted,读未提交)

    一个事务可以读到其他事务已修改但尚未提交的数据,脏读。安全性最低,并发度(无锁)最高。

  RC(Read Committed,读已提交)

    一个事务中只可以读到已提交的数据,过程中如果数据被其他事务修改并提交,则当前事务再次读时,读到的是已修改后的数据,即不可重复读。

  RR(Repeatable Read,可重复读)

    一个事务只读取当前事务开始时提交的数据,过程中如果数据被其他事务修改并提交,则无影响;事务开始时不存在的数据,被其他事务提交后,当前事务可以读到,即幻读(不同数据库实现不同,mysql在RR隔离级别下使用next-key锁解决了幻读问题)。

  Serializable(序列化/串行)

    事务串行执行,安全性最高,并发度最低。

  

  概念

    脏读

      脏数据指未提交的数据

      脏读是当前事务读到了其他事务中未提交的数据,即读到了脏数据

    不可重复读(侧重update)

      在一个事务中对同一数据的两次/多次读取,读到的数据不一样,即不可重复读

    幻读(侧重insert)

      在一个事务中已经检查过不存在的记录,再次查询时却已存在

  隔离性的实现-锁 

    行锁、间隙所、Next-key锁、Previous-key锁、页锁、意向锁、表锁、MDL(元数据)锁

      行锁

        顾名思义,在数据记录上加的锁,分为X锁和S锁。

      间隙锁

        锁住一段区间,range,不包括当前记录。

      行锁+间隙锁=Next-key锁/Previous-key锁      

        包括当前记录

      意向锁

      

      

      

 

 

 

    

    行级锁分为X(写/独占)锁、S(读/共享)锁

      

    一致性锁定读(加锁,根据情况,阻塞其他加锁请求)

      通过加锁的方式

      select ... for update; 加X锁

      select ... lock in share mode; 加S锁

 

    一致性非锁定读(不加锁,不阻塞)

      mvcc(多版本并发控制,通过undo log实现)

        

        

    死锁

      死锁是指两个或者两个以上事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象

      解决方案:超时、锁等待图

 

演示

  准备

    测试表建表sql

      CREATE TABLE t_a (
        id INT (10) NOT NULL,
        content VARCHAR (10) NOT NULL,
        PRIMARY KEY (id)
      ) ENGINE = INNODB;

    开启3个mysql终端,2个用来测试(以下统一使用t1,t2名称代替),1一个用来查看锁信息(以下统一使用info3名称代替)

    修改事务隔离级别命令

      SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]

  隔离级别

    RU隔离级别下脏读

      设置事务隔离级别为RU

        

      t1中开启事务,向t_a表中插入一条数据,不要提交

        

      t2中开启事务,读t_a表的数据,读到了脏数据(未提交的数据)

        

        如果t2中以此数据作为执行后续逻辑的判断条件,则可能会导致业务错误或者数据混乱。

      t1中rollback

        

      t2中再次查询,已经查不到了

        

    RC隔离级别下无脏读

      设置事务隔离级别为RC

        

       t1中开启事务并向t_a表中插入一条记录,但是不提交

        

       t2中开启事务,查询t_a表数据,发现不会查询出未提交的数据

        

       提交t1中的事务

        

       t2中再次查询,可以查询到已提交的数据,说明无脏读问题

        

    RC隔离级别下不可重复读问题

       先向t_a表中插入一条记录

        

       t1中开启事务,并查询这条记录

        

       t2中开启事务修改这条记录,并提交

        

       t1中再次查询这条记录,发现数据不一致,也就是不可重复读

        

     

    RR隔离级别下可重复读

      设置事务隔离级别为RR

        

      t1中开启事务,并查询t_a表中记录

        

      t2中开启事务,修改表中记录,并提交

        

      t1中再次查询t_a表中的记录,发现数据无变化,即可重复读

        

 

  死锁 

    t1开启事务,对t_a的id为1的记录加X锁

      

    t2中开启事务,对t_a的id为2的记录加X锁

      

    t1中申请t_a的id为2的记录的X锁,被阻塞(t2中的事务正在持有锁)

      

    t2中申请t_a的id为1的记录的X锁,报错,提示死锁

      

 

  X锁、S锁

    阻塞

       t1中开启事务对t_a表中id为1的记录加X锁

        

       t2中开启事务对t_a表中id为1的记录申请加S锁,被阻塞

        

       info3中查询锁信息

        

        

 

    MVCC

        t1中开启事务对t_a表中id为1的记录加X锁

        

       t2中开启事务读t_a表中id为1的记录,未阻塞

        

  实例 

    

    上图中并发情况下,两个线程开启两个事务,同时执行到if代码行,则返回的结果都是true,会造成重复插入。

    一般的解决方案有:

      unique key

      insert into ... select...

        如下面的sql,

        insert into t_a
        select 3, '3'
        from dual
        where not exists (
        select 0 from t_a where content = '3'
        );

        会对t_a表中content=3的记录加锁,阻塞其他插入content='3'的请求,从而避免重复插入

        

 

 

    

 

posted @ 2018-10-23 17:02  zhya_hopeful  阅读(286)  评论(0编辑  收藏  举报