经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MySQL » 查看文章
mysql事务详细介绍
来源:jb51  时间:2021/12/8 8:38:16  对本文有异议

简介

事务是由一组sql语句组成的逻辑处理单元

事务四个特性

  1. 原子性(Atomicity):
  2. 要么都成功要么都失败
  3. undo log实现
  4. 一致性(Consistent):
  5. 如转账前后两个数额总合保持不变
  6. 隔离性(lsolation):数据库提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境下运行
  7. 锁,mvcc多版本并发控制
  8. 持久性(Durable):事务提交持久化磁盘
  9. redo log

事务隔离级别

数据库的事务隔离级别有四种,分别是读未提交,读已提交,可重复读,序列化,不同的隔离级别会产生脏读,幻读,不可重复读等相关问题,因此,在选择隔离级别的时候要根据应用场景来决定,使用不同的隔离级别

隔离级别 脏读 不可重复读 幻读
READ-UNCOMMITTED
READ-COMMITED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

事务隔离级别带来的问题

  1. 脏读(Dirty Reads一个事务访问到了另外一个事务未提交的数据):
  2. 当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另一个事务也访问这个数据,然后使用了这个数据。
  3. 不可重复度(Non-Repeatable Reads 一个事务两次同样的查询,查询到了不同的数据):
  4. 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,发现和以前读出的数据不一致
  5. 更新删除
  6. 幻读(Phantom Reads 一个事务两次同样的查询,查询到了不同的数据):
  7. 一个事务按照相同的查询条件重新读取以前查询过的数据,却发现其他事务插入了满足其查询条件的新数据
  8. 插入

验证

查看事务的隔离级别show variables like ‘tx_isolation';

查看事务是否自动提交show variables like ‘autocommit';

关闭自动提交事务=0|OFF

set autocommit = 0;

脏读:

  1. 设置事务隔离级别AB
  2. set session transaction isolation level read uncommitted;
  3. sessionA
  4. 开启事务
  5. start transaction;
  6. 插入一条数据
  7. INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi');
  8. sessionB
  9. 另一个连接进行查询
  10. select * from t_user;
  11. +----+----------+
  12. | id | name |
  13. +----+----------+
  14. | 1 | ZhangSan |
  15. | 2 | LiSi |
  16. | 3 | WangWu |
  17. | 4 | LaoWang |
  18. | 5 | DuQi |
  19. +----+----------+
  20. 此时连接B查询到连接A未提交的事务的记录id5
  21. 到这里验证了一个session读取到了另一个事务未提交的数据

不可重复读:

  1. 修改事务隔离级别
  2. set session transaction isolation level read committed;
  3. A开启事务
  4. start transaction;
  5. 验证更新
  6. B执行查询语句
  7. MySQL [db_test]> select * from t_user;
  8. +----+----------+
  9. | id | name |
  10. +----+----------+
  11. | 1 | ZhangSan |
  12. | 2 | LiSi |
  13. | 3 | WangWu |
  14. | 4 | LaoWang |
  15. | 5 | DuQi |
  16. +----+----------+
  17. A执行更新语句
  18. update t_user set name = 'duqi' where id = 5;
  19. B执行查询语句
  20. start transaction;
  21. MySQL [db_test]> select * from t_user;
  22. +----+----------+
  23. | id | name |
  24. +----+----------+
  25. | 1 | ZhangSan |
  26. | 2 | LiSi |
  27. | 3 | WangWu |
  28. | 4 | LaoWang |
  29. | 5 | DuQi |
  30. +----+----------+
  31. A提交事务
  32. commit;
  33. B执行查询语句(同一个事务两次查询结果不一致)
  34. MySQL [db_test]> select * from t_user;
  35. +----+----------+
  36. | id | name |
  37. +----+----------+
  38. | 1 | ZhangSan |
  39. | 2 | LiSi |
  40. | 3 | WangWu |
  41. | 4 | LaoWang |
  42. | 5 | duqi |
  43. +----+----------+
  44. 继续验证删除
  45. A 开启事务 B开启事务
  46. start transaction ;
  47. A删除一条记录
  48. delete from t_user where id = 5;
  49. B事务查询正常,查询被删除的记录还在
  50. MySQL [db_test]> select * from t_user;
  51. +----+----------+
  52. | id | name |
  53. +----+----------+
  54. | 1 | ZhangSan |
  55. | 2 | LiSi |
  56. | 3 | WangWu |
  57. | 4 | LaoWang |
  58. | 5 | DuQi |
  59. +----+----------+
  60. A commit;
  61. B 继续查询 发现同一事物中多次查询结果不一致
  62. MySQL [db_test]> select * from t_user;
  63. +----+----------+
  64. | id | name |
  65. +----+----------+
  66. | 1 | ZhangSan |
  67. | 2 | LiSi |
  68. | 3 | WangWu |
  69. | 4 | LaoWang |
  70. +----+----------+
  71.  
  72. 验证插入
  73. AB 开启事务
  74. start transaction;
  75. A 插入记录
  76. INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi');
  77. B进行查询
  78. MySQL [db_test]> select * from t_user;
  79. +----+----------+
  80. | id | name |
  81. +----+----------+
  82. | 1 | ZhangSan |
  83. | 2 | LiSi |
  84. | 3 | WangWu |
  85. | 4 | LaoWang |
  86. +----+----------+
  87. A提交事务
  88. commit;
  89. B查询 也是能查询到A提交的事务
  90. MySQL [db_test]> select * from t_user;
  91. +----+----------+
  92. | id | name |
  93. +----+----------+
  94. | 1 | ZhangSan |
  95. | 2 | LiSi |
  96. | 3 | WangWu |
  97. | 4 | LaoWang |
  98. | 5 | DuQi |
  99. +----+----------+

幻读:

  1. 修改事务隔离级别
  2. set session transaction isolation level repeatable read;
  3. AB开启事务
  4. start transaction;
  5. A插入一条数据
  6. INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi');
  7. B查询
  8. MySQL [db_test]> select * from t_user;
  9. +----+----------+
  10. | id | name |
  11. +----+----------+
  12. | 1 | ZhangSan |
  13. | 2 | LiSi |
  14. | 3 | WangWu |
  15. | 4 | LaoWang |
  16. +----+----------+
  17. A提交事务
  18. commit;
  19. B事务查询
  20. MySQL [db_test]> select * from t_user;
  21. +----+----------+
  22. | id | name |
  23. +----+----------+
  24. | 1 | ZhangSan |
  25. | 2 | LiSi |
  26. | 3 | WangWu |
  27. | 4 | LaoWang |
  28. | 5 | DuQi |
  29. +----+----------+
  30. 可能发现,不同事务之间,插入是可以查询到的
  31.  
  32. 咱们再继续验证更新和删除
  33. AB开启事务
  34. A更新 update t_user set name = 'duqi' where id = 5;
  35. B查询
  36. MySQL [db_test]> select * from t_user;
  37. +----+----------+
  38. | id | name |
  39. +----+----------+
  40. | 1 | ZhangSan |
  41. | 2 | LiSi |
  42. | 3 | WangWu |
  43. | 4 | LaoWang |
  44. | 5 | DuQi |
  45. +----+----------+
  46. A提交事务commit
  47. B继续查询
  48. MySQL [db_test]> select * from t_user;
  49. +----+----------+
  50. | id | name |
  51. +----+----------+
  52. | 1 | ZhangSan |
  53. | 2 | LiSi |
  54. | 3 | WangWu |
  55. | 4 | LaoWang |
  56. | 5 | DuQi |
  57. +----+----------+
  58. 咱们再继续验证删除
  59. AB开启事务
  60. A事务执行删除操作 delete from t_user where id = 5;
  61. B事务执行查询
  62. MySQL [db_test]> select * from t_user;
  63. +----+----------+
  64. | id | name |
  65. +----+----------+
  66. | 1 | ZhangSan |
  67. | 2 | LiSi |
  68. | 3 | WangWu |
  69. | 4 | LaoWang |
  70. | 5 | duqi |
  71. +----+----------+
  72. A提交事务,B继续查询
  73. MySQL [db_test]> select * from t_user;
  74. +----+----------+
  75. | id | name |
  76. +----+----------+
  77. | 1 | ZhangSan |
  78. | 2 | LiSi |
  79. | 3 | WangWu |
  80. | 4 | LaoWang |
  81. | 5 | duqi |
  82. +----+----------+
  83. 可能大家会发现,REPEATABLE-READ 事务隔离级别解决了删除和更新的问题,但是插入的问题一直存在。

MVCC

多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存

  1. mvccMysql INNODB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

了解mvcc之前首先要了解两个概念,什么是当前读,什么是快照读

当前读

读取最新版本的数据

  1. select lock in share mode(共享锁),select for updateupdateinsertdelete(排他锁)这些操作都是一种当前读,
  2. 为什么叫当前读?
  3. 就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

快照读

读取历史版本的数据

  1. 像不加锁的select操作就是快照读,既不加锁的非阻塞读;
  2. 快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;
  3. 之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本控制并发控制,既MVCC,可以认为mvcc是行锁的一个变种,但它在很多情况下避免了加锁操作,降低了开销;

当前读、快照读、MVCC关系

  1. MVCC多版本并发控制指的是维持一个数据的多个版本,使得读写操作没有冲突,快照读是mysql为实现mvcc的一个非阻塞读功能。
  2. mvcc模块在Mysql中的具体实现是由三个隐式字段,undo日志,readview三个组件来实现的。

这里补充一点:三个隐式字段中其中有一个是列的唯一标志。有些同学设计表的时候一定要加主键(列依赖主键),即使它几乎无用处也要加上。其实对于配置表,几乎不进行增删操作的表完全没必要加主键,mysql在插入数据的时候会进行判断表有无主键,如果有主键会使用主键作为唯一标示,如果没有主键,会自动生成7byte大小的主键,所以表的合理性要根据不用使用场景进行设计。

mvcc 解决的问题

并发场景

  1. 1、读读:不存在任何问题,也不需要并发控制
  2. 2、读写:有线程安全问题,可能会造成事务隔离级别问题,可能遇到脏读、不可重复读、幻读
  3. 3、写写:有线程安全问题,可能存在更新丢失问题

解决的问题

  1. 1、在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
  2. 2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题

MVCC实现原理

mvcc的实现原理主要依赖于记录中的三个隐藏字段、undolog,read view来实现的。

隐藏字段

行记录除了我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID,BD_ROLL_PTR,DB_ROW_ID等字段

  1. DB_TRX_ID 最近修改事务id
  2. 6字节,记录创建这条记录或者最后一次修改该记录的事务id
  3. DB_ROLL_PTR 回滚指针:
  4. 7字节,指向这条记录的上一个版本,用于配合undolog,指向上一个旧版本
  5. DB_ROW_ID 隐藏主键:
  6. 6字节,如果数据库表没有主键,那么innodb会自动生成一个6字节的row_id

undo log

undo log被称之为回滚日志,表示进行insert,delete,update操作的时候产生的方便回滚日志

  1. 当进行insert操作的时候,产生的undo log只在事务回滚的时候需要,并且在事务提交之后可以被立刻丢弃
  2. 当进行updatedelete操作的时候,产生的undo log不仅仅在事务回滚的时候需要,在快照读的时候也需要,所以不能随便删除,只有在快照读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除(当数据发生更新和删除操作的时候都只是设置一下老记录,如果某个记录的deleted_idtrue,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定可以被清除的)

原理

  1. 当进行insert操作时,会生成对应delete语句
  2. 当进行delete操作时,会备份原数据的insert语句
  3. 当进行update时,会记录原数据的update语句
  4. 这样操作方便记录回滚

read View

READ View是事务进行快照读操作的时候产生的读视图,在该事务执行快照的那一刻,会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的id,事务的id值是递增的。

DB_ROW_ID DB_TRX_ID DB_ROLL_PTR c_name i_age
1 1 zhangsan1 18
2 2 1 zhangsan2 19
  1. READ VIEW的最大作用是用来做可见性判断的,也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能读取到的是最新的数据,也有可能读取到的是最新的数据,也有可能读取的是当前记录的undolog中某个版本的数据
  2. read view遵循的可见性算法主要是将要被修改的数据的最新记录中的DB_TRX_ID取出来,与系统当前其他活跃事务的id去对比,如果DB_TRX_IDREAD VIEW的属性做了比较,不符合可见性,那么就通过DB_ROLL_PTR回滚指针去取出undolog中的DB_TRX_ID做比较,既遍历链表中的DB_TRX_ID,直到找到满足条件的DB_TRX_ID,这个DB_TRX_ID所在的旧记录就是当前事务能看到的最新老版本数据

可见性规则

了解可见性规则之前首先要了解下Read View中的三个全局属性

  1. trx_list:
  2. 一直数值列表,用来维护Read View生成时刻系统正活跃的事务ID
  3. up_limit_id:
  4. 记录trx_list列表中事务ID的最小ID
  5. low_limit_id:
  6. Read View生成时刻系统尚未分配下一个事务ID

比较规则

  1. 1、首先判断DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断
  2. 2、判断DB_TRX_ID >= low_limit_id,如果大于等于则代表DB_TRX_ID所在的记录在Read View生成后才出现的,那么对于当前事务肯定不可见,如果小于,进入下一步判断
  3. 3、判断DB_TRX_ID是否在活跃事务中,如果在,则代表在Read View生成时刻,这个事务还是活跃状态,还没有commit,修改的数据,当前事务也是看不到,如果不在,则说明这个事务在Read View生成之前就已经开始commit,那么修改的结果是能够看到的。

到此这篇关于mysql事务详细介绍的文章就介绍到这了,更多相关mysql事务内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号