• mysql 数据库事务


    事务:transaction 一系列sql语句的逻辑单元 类似于java 中的线程

    事务特性:ACID

    A:atomicity 原子性:操作的一致性 事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。

    C:consistant 一致性: 数据的一致性 在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。

    I:isolation 隔离性:和别的事务隔离 数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。

    D:durable 持久性  事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。

     

     多事务并发问题:

    脏写:最后一个事务的更新覆盖了前面事务的更新;两个事务同时更改一条数据,一个先更改事务进行了回滚 导致另外一个事务后更新的数据丢失的现象,因为行锁的存在,这种现象在mysql中不存在

    脏读:读取到了为提交的事务,回滚后出现脏数据;

    不可重复读:同一个事务中,相同查询语句查出来的数据不一致;

    幻读:本事务对其他事务 更改后的语句(insert delete update)提交后,进行更新,会出现别的事务操作的数据;

     

    解决办法1:数据库的隔离级别

      查询当前数据的隔离级别 show variables like 'tx_isolation';

      设置隔离级别:set tx_isolation='repeatable-read' 设置了只对本客户端生效。

      脏读 不可重复读 幻读   理解(操作是指 insert update delete)
    读未提交(read-uncommitted) 可能发生 可能发生 可能发生  
    读已提交(read-committed) 解决 可能发生 可能发生 mvcc 每次查询的时候生成readview ,保证和数据库当前数据保持一致,除非当前事务进行了数据更新,则和当前数据一致。
    可重复读(repeatable-read) 解决  解决 可能发生 mvcc机制 第一次查询的时候生成readview,保证每次查询结果和生成readview时刻数据库的数据一致,除非当前事务进行了数据更新,则和当前数据一致。
    串行化(serializable) 解决  解决 解决 通过互斥锁来保证数据的一致性。效率极低一般不采用

     mvcc multi-version-concurrentcy-control 多版本并发控制机制

     undo日志版本链:日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,按照时间顺序进行排序。并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史记录版本链(见下图,需参考视频里的例子理解)

     

     

     数据库数据:

        提交后写到数据库的数据

     trx_id:

        事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。

     生成tax_id的时间点:

        begin/start transaction命令并不是一个事务的起点,在执行到它们之后的第一个修改操作InnoDB表的语句,事务才真正启动,才会向mysql申请事务id

     readview组成:

        由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)组成

     

     生成readview的时间点:

                                可重复读隔离级别:当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束之前都不会变化;

                                读已提交隔离级别:在每次执行查询sql时都会重新生成

     查询readview的过程:

            根据当前undo日志版本链最新的记录一次查找,直到找到可见的数据,如果到undo链的低端还没有则显示本条数据为空。事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。

            版本链比对规则:

            1.如果row的trx_id落在绿色部分(trx_id<min_id),表示这个版本是已提交的事务生成的,这个数据是可见的;

            2.如果row的trx_id落在红色部分(trx_id>max_id),表示这个版本是由将来启动的事务生成的,是不可见的 (若row的trx_id就是当前自己的事务是可见的);

            3.如果row的trx_id落在黄色部分(min_id<=trx_id<=max_id),那就包括两种情况a.若row的trx_id在视图数组中,表示这个版本是由还没提交的事务生成的,不可见(若row的trx_id就是当前自己的事务是可见的);b.若row的trx_id不在视图数组                           中,表示这个版本是已经提交了的事务生成的,可见。

     删除的情况:

            对于删除的情况可以认为是update的特殊情况,会将版本链上最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时在该条记录的头信息(recordheader)里的(deleted_flag)标记位写上true,来表示当前记录已经被删除,

            在查询时按照上面的规则查到对应的记录如果delete_flag标记位为true,意味着记录已被删除,则不返回数据。

    undo log 版本链在没有事务需要的时候会被删除,就是当系统中没有比这个回滚日志更早的read-view

    长事务:

      显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。

      set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接

      有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。

      因此,建议总是使用 set autocommit=1, 通过显式语句的方式来启动事务。但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数。如果你也有这个顾虑,建议使用 commit work and chain 语法。在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。

    查询长事务:


    select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

    解决办法2 锁  主要解决并发写的问题

      操作颗粒度区分:

      行锁:给数据行加锁。每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。InnoDB与MYISAM的最大不同有两点:InnoDB支持事务(TRANSACTION)InnoDB支持行级锁

      

      表锁:给整张表加锁,主要用于myisam 引擎,或者串行化中 无索引的条件字段 采用表锁。每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。

      增加表锁

      lock table tablename read(write);

      查询当前加锁的表:show open tables;

      解锁:unlock tables;

      加行锁:sql语句后面 for update(排它锁 其余事务不能进行操作该语句查出来的数据) 若条件字段不是索引则锁全表

                        lock in share mode(共享锁) 

      读(select) 操作(insert update delete)   解释
    当前session other session 当前session other session  
    表写锁 阻塞 阻塞  
    表读锁 报错 阻塞  
    行锁 阻塞 InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。
    间隙锁(gap lock) 阻塞 就是锁两个值之间的间隙
    for update(排它锁) 阻塞 同行锁,手动增加的行锁,其他事务不可以增加排斥锁
    lock in share mode(共享锁) 阻塞 同行锁,手动增加的行锁,其他事务可以增加共享锁
    innodb 不加锁(非串行隔离级别) 加行锁 或者表锁  
    myisam 加表读 加表写  
    全局锁(FTWRL)
    阻塞 Flush tables with read lock  全局锁就是对整个数据库实例加锁

     

            

      那么间隙就有id为(3,10),(10,20),(20,正无穷)这三个区间,在Session_1下面执行update account set name='zhuge' where id>8 and id<18;,

      则其他Session没法在这个范围所包含的所有行记录(包括间隙行记录)以及行记录所在的间隙里插入或修改任何数据,即id在(3,20]区间都无法修改数据,注意最后那个20也是包含在内的。间隙锁是在可重复读隔离级别下才会生效。

      临键锁(Next-keyLocks):Next-KeyLocks是行锁与间隙锁的组合。像上面那个例子里的这个(3,20]的整个区间可以叫做临键锁。

     

      性能上区分:

      乐观锁:根据版本区分更新

      悲观锁:

      数据库的操作类型上区分 都属于悲观锁

      读锁:共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响

      写锁:排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁

     

      操作:

      查询行锁的情况

           

      对各个状态量的说明如下:

      Innodb_row_lock_current_waits:当前正在等待锁定的数量

      Innodb_row_lock_time:从系统启动到现在锁定总时间长度

      Innodb_row_lock_time_avg:每次等待所花平均时间

      Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花时间

      Innodb_row_lock_waits:系统启动后到现在总共等待的次数

     

       

      select * from information_schema.INNODB_TRX; 查看所有事务详情;
      show PROCESSLIST; 查看连接的线程
      select * from information_schema.INNODB_LOCKS; 查看拥有锁 并且等待这把锁的事务
      select * from information_schema.INNODB_LOCK_WAITS; 查看等待的事务

      show engine innodb status \G; 查看锁日志

      结束进程:kill 线程id;

     

     
  • 相关阅读:
    jQuery操作radio、checkbox、select 集合
    正则表达式
    ajax传递数组:属性traditional设置
    EF是否存在(Any/Count>0的用法)
    Layui上传图片(1.0版)
    A-01 最小二乘法
    09-01 Tensorflow1基本使用
    08-08 细分构建机器学习应用程序的流程-模型优化
    08-07 细分构建机器学习应用程序的流程-测试模型
    08-06 细分构建机器学习应用程序的流程-训练模型
  • 原文地址:https://www.cnblogs.com/xiatc/p/16257953.html
Copyright © 2020-2023  润新知