本文整理自网络,侵删。
目录
- 简介
- 事务四个特性
- 事务隔离级别
- 验证
- MVCC
- 当前读
- 快照读
- 当前读、快照读、MVCC关系
- mvcc 解决的问题
- MVCC实现原理
- 可见性规则
简介
事务是由一组sql语句组成的逻辑处理单元
事务四个特性
原子性(Atomicity): 要么都成功要么都失败 undo log实现 一致性(Consistent): 如转账前后两个数额总合保持不变 隔离性(lsolation):数据库提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境下运行 锁,mvcc多版本并发控制 持久性(Durable):事务提交持久化磁盘 redo log
事务隔离级别
数据库的事务隔离级别有四种,分别是读未提交,读已提交,可重复读,序列化,不同的隔离级别会产生脏读,幻读,不可重复读等相关问题,因此,在选择隔离级别的时候要根据应用场景来决定,使用不同的隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
事务隔离级别带来的问题
脏读(Dirty Reads一个事务访问到了另外一个事务未提交的数据): 当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另一个事务也访问这个数据,然后使用了这个数据。 不可重复度(Non-Repeatable Reads 一个事务两次同样的查询,查询到了不同的数据): 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,发现和以前读出的数据不一致 更新删除 幻读(Phantom Reads 一个事务两次同样的查询,查询到了不同的数据): 一个事务按照相同的查询条件重新读取以前查询过的数据,却发现其他事务插入了满足其查询条件的新数据 插入
验证
查看事务的隔离级别show variables like ‘tx_isolation';
查看事务是否自动提交show variables like ‘autocommit';
关闭自动提交事务=0|OFF
set autocommit = 0;
脏读:
设置事务隔离级别A、B set session transaction isolation level read uncommitted; sessionA 开启事务 start transaction; 插入一条数据 INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi'); sessionB 另一个连接进行查询 select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ 此时连接B查询到连接A未提交的事务的记录id为5 到这里验证了一个session读取到了另一个事务未提交的数据
不可重复读:
修改事务隔离级别 set session transaction isolation level read committed; A开启事务 start transaction; 验证更新 B执行查询语句 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ A执行更新语句 update t_user set name = 'duqi' where id = 5; B执行查询语句 start transaction; MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ A提交事务 commit; B执行查询语句(同一个事务两次查询结果不一致) MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | duqi | +----+----------+ 继续验证删除 A 开启事务 B开启事务 start transaction ; A删除一条记录 delete from t_user where id = 5; B事务查询正常,查询被删除的记录还在 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ A commit; B 继续查询 发现同一事物中多次查询结果不一致 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | +----+----------+ 验证插入 A、B 开启事务 start transaction; A 插入记录 INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi'); B进行查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | +----+----------+ A提交事务 commit; B查询 也是能查询到A提交的事务 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+
幻读:
修改事务隔离级别 set session transaction isolation level repeatable read; A、B开启事务 start transaction; A插入一条数据 INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi'); B查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | +----+----------+ A提交事务 commit; B事务查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ 可能发现,不同事务之间,插入是可以查询到的 咱们再继续验证更新和删除 A、B开启事务 A更新 update t_user set name = 'duqi' where id = 5; B查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ A提交事务commit B继续查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | DuQi | +----+----------+ 咱们再继续验证删除 A、B开启事务 A事务执行删除操作 delete from t_user where id = 5; B事务执行查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | duqi | +----+----------+ A提交事务,B继续查询 MySQL [db_test]> select * from t_user; +----+----------+ | id | name | +----+----------+ | 1 | ZhangSan | | 2 | LiSi | | 3 | WangWu | | 4 | LaoWang | | 5 | duqi | +----+----------+ 可能大家会发现,REPEATABLE-READ 事务隔离级别解决了删除和更新的问题,但是插入的问题一直存在。
MVCC
多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存
mvcc在Mysql INNODB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
了解mvcc之前首先要了解两个概念,什么是当前读,什么是快照读
当前读
读取最新版本的数据
像select lock in share mode(共享锁),select for update;update、insert、delete(排他锁)这些操作都是一种当前读, 为什么叫当前读? 就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
快照读
读取历史版本的数据
像不加锁的select操作就是快照读,既不加锁的非阻塞读; 快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读; 之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本控制并发控制,既MVCC,可以认为mvcc是行锁的一个变种,但它在很多情况下避免了加锁操作,降低了开销;
当前读、快照读、MVCC关系
MVCC多版本并发控制指的是维持一个数据的多个版本,使得读写操作没有冲突,快照读是mysql为实现mvcc的一个非阻塞读功能。 mvcc模块在Mysql中的具体实现是由三个隐式字段,undo日志,readview三个组件来实现的。
这里补充一点:三个隐式字段中其中有一个是列的唯一标志。有些同学设计表的时候一定要加主键(列依赖主键),即使它几乎无用处也要加上。其实对于配置表,几乎不进行增删操作的表完全没必要加主键,mysql在插入数据的时候会进行判断表有无主键,如果有主键会使用主键作为唯一标示,如果没有主键,会自动生成7byte大小的主键,所以表的合理性要根据不用使用场景进行设计。
mvcc 解决的问题
并发场景
1、读读:不存在任何问题,也不需要并发控制 2、读写:有线程安全问题,可能会造成事务隔离级别问题,可能遇到脏读、不可重复读、幻读 3、写写:有线程安全问题,可能存在更新丢失问题
解决的问题
1、在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能 2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题
MVCC实现原理
mvcc的实现原理主要依赖于记录中的三个隐藏字段、undolog,read view来实现的。
相关阅读 >>
mysql中database()和current_user()函数的示例详解
更多相关阅读请进入《mysql》频道 >>
数据库系统概念 第6版
机械工业出版社
本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。
转载请注明出处:木庄网络博客 » mysql事务详细介绍
相关推荐
评论
管理员已关闭评论功能...