当前位置:首页 » 《关注互联网》 » 正文

【MySQL系列】MySQL事务知识点_JobsTribe的博客

22 人参与  2022年01月12日 16:49  分类 : 《关注互联网》  评论

点击全文阅读


事务的特性

ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)

  • Atomicity:原子性,执行的最小单元,不可拆分。
  • Consistency:一致性,事务在执行DML操作时,要么成功,要么失败。
  • Isolation:隔离性。两个事务执行具有隔离性。
  • Durability:持久性。事务执行以后,对数据库的影响是持久的。

mysql事务常用的命令

  • 开启事务:两个都可以

    begin;
    start transaction;
    
  • 提交事务

    commit;
    
  • 回滚事务

    rollback;
    
  • 查看当前对话的隔离级别

    select @@tx_isolation;
    
  • 查看系统当前的隔离级别

    select @@global.tx_isolation;
    
  • 设置会话级隔离级别为读未提交:

    set session transaction isolation level read uncommitted;
    
  • 设置全局级隔离级别为读未提交:

    set global transaction isolation level read uncommitted;
    
  • 设置自动提交

    set autocommit = 0/1; (0为不自动提交,1为自动提交)
    

事务的隔离级别

作用范围

  • 全局级:对所有的会话有效
  • 会话级:只对当前的会话有效

事务隔离级别

从上往下,并发性越来越差,安全性越来越高。

  • 读未提交

    Read UnCommitted。一个事务可以看到另一个事务未提交的数据。一般很少用。

    带来的问题:脏读。

  • 读已提交

    Read Commited,简称RC。大多数数据库系统默认RC。

    定义:一个事务开始时,只能"看见"已经提交的事务做的修改,也就是说:一个事务从开始到提交之前,所做的任何修改对其他事务都是不可见的。

    带来的问题:不可重复读,即两次执行相同的查询,可能会得到不同的结果。

  • 可重复读

    Repeatable Read,简称RR。

    同一个事务多次读取同样的记录的结果是一致的。

    带来的问题:幻读,即当某个事务在读取某个范围内的记录时,另一个事务又在该范围插入了新的记录,当某个事务再次读取该范围内的记录时,会产生幻行。

    InnoDB和XtraDB通过MVCC解决了幻读的问题。

  • 串行化

    Serializable。读写都会加锁,锁住整张表,很少用。

    通过强制事务串行执行,避免幻读问题。Serializable会在每一行数据上都加锁,所以可能会导致大量超时和锁争用的问题。

    事务隔离级别并发问题

https://img-blog.csdnimg.cn/593b630bcb6a47bb816dc61dfcd5ece7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASm9ic1RyaWJl,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center

特别说明点

  • 默认隔离层级:mysql—可重复读。oracle,sql server —读已提交
  • 事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。

如何设置事务级别

使用mysql命令对数据库进行操作。

mysql默认隔离层级

查询mysql的默认事务隔离级别的命令:REPEATABLE-READ

https://img-blog.csdnimg.cn/ad9332d890054f908bcb051f251545c4.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASm9ic1RyaWJl,size_18,color_FFFFFF,t_70,g_se,x_16#pic_center

事务隔离层级

以读未提交为例,其他的内容参考最下面推荐的文章。

执行的内容:在事务A开启读未提交时,事务B更新数据时,事务A就能查到数据。

https://img-blog.csdnimg.cn/d18ae1a318f54c2084cfdb11431e1e2b.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASm9ic1RyaWJl,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center

事务B做操作:开启读未提交命令,还未提交事务,事务A就能查到数据。一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据。

https://img-blog.csdnimg.cn/b7251673804f445e9892b9b39e38a15a.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASm9ic1RyaWJl,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center

如何实现事务隔离

  • 事务隔离的实现:每条记录在更新的时候都会同时记录一条回滚操作。同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。
  • 回滚日志什么时候删除?系统会判断当没有事务需要用到这些回滚日志的时候,回滚日志会被删除。
  • 什么时候不需要了?当系统里没有比这个回滚日志更早的read-view的时候。

长事务

什么是长事务?其实就是运行时间比较长的未提交的事务。

长事务的影响

  • 长事务意味着系统里面会存在很老的事务视图,在这个事务提交之前,回滚记录都要保留,这会导致大量占用存储空间。
  • 长事务还会占用锁资源,可能会拖垮数据库。

如何查询出表中的长事务

select * from information_schema.innodb_trx;

这个表中记录了所有正在运行的事务信息,里面有事务的开始时间。可以从这里看出哪些事务运行的时间比较长。

如何避免长事务

  • 首先,从应用开发端来看:
    • 确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把 MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1。
    • 确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种只读事务可以去掉。
    • 业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。(为什么会意外?在后续的文章中会提到这类案例)
  • 其次,从数据库端来看:
    • 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
    • Percona 的 pt-kill 这个工具不错,推荐使用;
    • 在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
    • 如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成 2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。

事务启动的方式

  • 显式启动事务语句,begin或者start transaction,提交commit,回滚rollback;
  • set autocommit=0,该命令会把这个线程的自动提交关掉。这样只要执行一个select语句,事务就启动,并不会自动提交,直到主动执行commit或rollback或断开连接。

事务与Spring结合

事务如何传播:即方法A传播到方法B。那方法A到方法B,方法A和B中分别要么有事务,要么没有事务。那在传播时有如下的行为:

事务传播行为(Transaction Propagation Behavior)

目前,Spring在TransactionDefinition类中定义了以下7种传播特性,具体特性我们接下来会分析:

  • PROPAGATION_REQUIRED:如果不存在外层事务,就主动创建事务;否则使用外层事务;
  • PROPAGATION_SUPPORTS:如果不存在外层事务,就不开启事务;否则使用外层事务;
  • PROPAGATION_MANDATORY:如果不存在外层事务,就抛出异常;否则使用外层事务;
  • PROPAGATION_REQUIRES_NEW:总是主动开启事务;如果存在外层事务,就将外层事务挂起;
  • PROPAGATION_NOT_SUPPORTED:总是不开启事务;如果存在外层事务,就将外层事务挂起;
  • PROPAGATION_NEVER:总是不开启事务;如果存在外层事务,则抛出异常;
  • PROPAGATION_NESTED:如果不存在外层事务,就主动创建事务;否则创建嵌套的子事务;

死锁问题

死锁是什么

死锁是指两个或者多个事务互相争夺同一个资源导致的。两个或者多个事务在同一个资源上相互占用,并请求锁定对方占用的资源,从而产生恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。

如何避免死锁

数据库系统实现了各种死锁检测和死锁超时检测。InnoDB可以检测到死锁的循环依赖,并立即返回一个错误。InnoDB目前处理死锁的方法是,将持有最少行级排它锁的事务进行回滚(这是相对比较简单的死锁回滚算法)。

锁的行为和顺序是和存储引擎有关的。不同存储引擎在执行语句时,有些会产生死锁,有些则不会。

只有部分或者全部的事务回滚时,才能打破死锁。所以在程序设计时需要考虑如何处理死锁。

推荐文章

Mysql常用命令行大全

MySQL的四种事务隔离级别

Transaction 那点事儿


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/33123.html

事务  死锁  隔离  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1