MySQL有InnoDb、MyISAM、NDB、Memory、Archive、Federated、Maria共计7种常见的存储引擎,但是只有InnoDb引擎支持数据库事务,下面介绍的数据库隔离级别讲的就是MySQL.InnoDb引擎下的隔离级别。在介绍MySQL隔离级别先介绍事务4大特征、数据库会话、数据连接等基础知识。
连接是指客户端进程和数据库实例之间的一条物理路径,例如网络连接、IPC,这个连接可能连接到一个专用服务器进程(dedicated server),或者是一个调度器进程(shared server)。
会话是指数据库中的一个逻辑实体,客户端进程可以通过他在数据库中执行SQL等操作,你所看到的会话状态信息,代表了你的会话在实例内存中的数据结构的集合,会话是在数据库上执行 SQL、提交事务和运行存储过程的地方。
多个会话可以通过一个连接关联一起,会话可以独立于连接而存在(无连接)。2、一个连接可以创建0个或者多个会话。3、一个连接上各个会话可以属于不同的数据库用户。
事务具有4个特性:原子性、一致性、隔离性、持久性,这四个属性通常称为 ACID 特性
事务应该是一个不可分割的单位,事务中包括的操作要么都成功,要么都不成功。
事务必须是使数据库从一个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的。
事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据在事物未提交前对并发的其他事务是隔离的,并发执行的各个事务之间不能互相影响。
事务一旦成功提交,它对数据库中数据的改变就应该是永久性的,接下来的其他操作或故障不应该对其有任何影响。
研究MySQL隔离级别,我们需要如何查看MySQL事务隔离级别,如何控制MySQL自动手动提交事务。在介绍MySQL事务隔离之前,先了解下MySQl事务的阶段,MySQL分为两个事务阶段 写入Redo Log阶段、Redo Commit这是保障MySQL事务的基础。
读未提交会导致脏读:指的是一个事务读取了别的事务未提交的事务,这个阶段的MySQL数据并没有Commit导致读取的数据可能是脏数据,读未提交比较简单就不演示了。
读已提交会导致不可重复读、幻读:指事务只能读取另一个事务已经提交的修改,但是他无法解决不可重复读及幻读。不可重复读指的是一个事务不受另外一个事务的干扰(修改:导致不可重复读,增加删除:幻读)导致结果和预期不一致。比如我们将要查询一条记录此时记录的属性为A,此时我们已经对查询A的SQL加上事务,但是此时又有一条对我们查询结果修改的SQL将A修改为B。那么在读已提交(READ COMMITTED)的模式下,我们看到的结果为B和预期结果不一致。在可重复读(REPEATABLE-READ)、串行化(SERIALIZABLE)模式下我们看到的还是A和预期结果一致。
可重复读会导致幻读:指事务多次读取相同的数据返回的结果是一样的,但是他无法解决幻读的问题。 幻读指一个事务受另外一个事务的新增记录导致查询结果和预期不一致。比如:我们将要查询name='小明'的记录共计10条查询过程中开启事务,此时有一个事务增加1条name='小明'的记录,那么我此时我们查询结果就是11条。 读已提交和可重复读到底什么区别?从范围上讲可重复读是读已提交显现的子集合。可重复读在2个事务在查询新增的时候出现幻读,读已提交在2个事务查询新增出现幻读,在查询修改场景出现不可重复读。[当前mysql通过加间隙锁的方式解决幻读]
串行化不支持并发执行,所有SQL排队执行,效率低性能差。串行化比较简单就不在演示了。
MySQL隔离级别 | 现象 | 查询SQL1 | 变更SQL2 | 执行结果 | 执行结果分析 |
---|---|---|---|---|---|
读已提交 READ-COMMITTED | 不可 重读 与 可重 复读 |
set tx_isolation='READ-COMMITTED'; set autocommit = 0; BEGIN; select * from test where id=2; select sleep (10); select * from test where id=2; commit; |
set autocommit = 1; set tx_isolation='READ-COMMITTED'; SET SQL_SAFE_UPDATES = 0; update test set name='3' where id=2 |
name=3 | SQL1查询一个name=2的记录,但是却被SQL2修改name=3,在READ-COMMITTED隔离级别下name=3,查询结果出现不可重复读的现象。 |
可重复读 REPEATABLE-READ | set tx_isolation='REPEATABLE-READ'; set autocommit = 0; BEGIN; select * from test where id=2; select sleep (10); select * from test where id=2; commit; |
set autocommit = 1; set tx_isolation='REPEATABLE-READ'; SET SQL_SAFE_UPDATES = 0; update test set name='3' where id=2 |
name=2 | SQL1查询一个name=2的记录,但是却被SQL2修改name=3,在READ-COMMITTED隔离级别下name=2,查询结果没有出现不可重复读的现象。 | |
可重复读 REPEATABLE-READ | 可重 复读 与 串行 化 |
set tx_isolation='REPEATABLE-READ'; set autocommit = 0; BEGIN; select count(*) from test where name=1; select sleep (10); select count(*) from test where name=1; commit; |
set autocommit = 1; set tx_isolation='REPEATABLE-READ'; SET SQL_SAFE_UPDATES = 0; insert into test(name) values("1"); |
7 | SQL1查询记录总数为7,此时SQL2添加一条记录,查询的结果还是7,说明MySQL已经在可重复读模型中解决幻读了。 |
MySQL是如何保证事务持久性呢?MySQL主要通过InnoDb引擎生成有序的事务日志,MySQL引擎有三类日志Redo Log、undo log ,MySQL在写入数据库前先写入Redo Log然后通过MVCC版本控制处理回滚。308
Redo Log是保证MySQL事务的持久性,MySQL在事务提交前会将日志顺序写入重做日志缓存里面。为了保证重做日志入磁盘,必须在重做日志入缓存进行fsync刷入磁盘,因此事务的性能取决于刷盘的性能,而刷盘的性能有取决于磁盘的性能。频繁的刷盘看似能提高事务的可靠性,但是降低磁盘的吞吐量为代价。因此,MySQL 通过innodb_flush_log_at_trx_commit参数控制刷盘的频率,1:日志进入缓存就进行一次刷盘,0:master thread每秒刷盘一次,2:日志进入缓存不进行刷盘。可靠性1最好吞吐量2最好,在系统崩溃0、2会丢失缓存里面的数据,1只会丢失一条数据,不要顺随便设置这个参数会导致MySQL丧失持久性,可见MySQL的事务也不是百分百的可靠。
innodb_flush_log_at_trx_commit | 执行时间 | 持久性支持 | 系统奔溃是否丢数据 |
---|---|---|---|
0 | 13.90秒 | 弱 | 丢失最多 |
1 | 113.11秒 | 强 | 丢失一条 |
2 | 23.37秒 | 中 | 丢失一般 |
作用简介
Undo Log的作用是保证MySQL事务的原子性,他是一种用于撤销回退的日志,用于事务没提交之前,会先记录存放到 Undo 日志文件里,当事务回滚时或者数据库崩溃时,可以利用 Undo 日志回 退事务的,Undo Log存储由InnoDB存储引擎实现,数据保存在InnoDB的数据文件中,Innodb存储引擎对Undo的管理采用段(segment)的方式,具体来说是一种命名为回滚段(rollback segment)的数据结构。
工作原理-读写
Undo Log在事务开启之前就产生,当事务提交的时候,不会删除Undo Log,因为可能需要RollBack操作,要执行回滚(rollback)操作时,从缓存中读取数据。InnoDB会将事务对应的日志保存在删除list对象中,后台通过purge线程进行回收处理。 当MySQL读取数据的时候,此时可能还有未提交的事务刷盘,故Select是不会读取磁盘的会直接读取Undo Log保证了MySQL事务的原子性。
工作原理-并发之MVCC
上面介绍了Undo Log写入时机与MySQL的查询原理。那么MySQL如何处理并发的更新操作?常规的办法MySQL在Update、Delete更新同一条记录,先执行的事务会对此记录加锁,直到锁释放掉在执行后续事务,【本人测试对同条事务执行修改、删除事务还是会等待的】
因 Undo Log日志有版本编号,目的是解决MySQL的幻读问题,Insert没有历史版本不需要做版本的区分, Update、Delete需要通过版本控制返回的数据。
binlog指二进制日志,它记录了数据库上的所有改变,并以二进制的形式保存在磁盘中,它可以用来查看数据库的变更历史、数据库增量备份和恢复、MySQL的复制(主从数据库的复制)。
binlog有三种格式:
statement:基于SQL语句的复制(statement-based replication,SBR)
row:基于行的复制(row-based replication,RBR)
mixed:混合模式复制(mixed-based replication,MBR)
mysql的原子性、隔离性就是为了保证mysql的一致性。
Gap锁(间隙锁+mysql数据存储结构??)
select version();
select @@tx_isolation;
select @@global.tx_isolation;
set tx_isolation='REPEATABLE-READ';