• 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'的请求,从而避免重复插入

            

        

  • 相关阅读:
    XCode5中新建工程后强制使用了ARC,如何去掉?
    面向对象程序的设计原则--Head First 设计模式笔记
    ios控件自定义指引
    iOS UITableViewDelegate && UITableViewDataSource 执行顺序
    awakeFromNib方法和viewDidLoad方法区别
    ios 视图的旋转及应用
    线段树模板 (刘汝佳)
    poj 3468
    hdu 2829(四边形优化 && 枚举最后一个放炸弹的地方)
    poj 3517(约瑟夫环问题)
  • 原文地址:https://www.cnblogs.com/zhya/p/9831241.html
Copyright © 2020-2023  润新知