文章目录
参考:
Mysql事务隔离级别详细解析
数据库事务与Mysql事务总结
Mysql锁总结
Mysql中的连接、实例、会话
Mysql中的实例(instance)、会话(session)、数据库与线程之间的关系
-
Mysql是单进程多线程的,Mysql实例在系统上表现就是一个服务进程
-
一般情况下一个实例操作一个或者多个数据库,在集群情况下多个实例操作一个或者多个数据库
-
一个实例是由线程和内存组成的
-
在操作系统概念里,会话就是线程
-
mysql中建立一个会话是指和某个实例建立会话,一个会话可以操作一个实例上的多个数据库
-
一个连接是指一个客户端和专有服务器的网络连接
-
创建一个连接实际上是在某个实例中创建一个或者多个线程
-
一个连接可以没有会话也可以有多个会话
-
会话与事务的关系:一个会话可以创建多个事务,一个事务只能由一个会话产生,一个事务可能会产生一个或者多个线程,一个线程在同一时间只能执行一个事务
事务特点
注:Mysql事务都是指在InnoDB引擎下,MyISAM引擎不支持事务。
事务的四个特性:ACID
原子性(Atomicity):一个事务中的操作要么全部成功提交,要么全部失败回滚,不能只执行一部分。
一致性(Consistency):数据库总是从一个一致性状态转移到另一个一致性状态。
隔离性(Isolation):通常来说,一个事务中的操作在提交之前对其他事务是不可见的。
持久性(Durability):一旦事务提交,其所做的更改将会永久保存在数据库中,即使数据库崩溃也不会消失。
事务的隔离级别
并发事务带来的问题
-
更新丢失当多个事务选择同一行,基于最初选定的值更新改行的时候,每个事务不知道彼此的存在,最后的更新会覆盖之前的更新,也就是更新丢失问题解决方法:使得一个事务处理这一行的时候其他事务等待
-
脏读读到了其他事务未提交的操作
-
不可重复读(针对更新操作)一个事务两次对同一个数据读取到的值不同,也就是说其他事务提交的修改对该事务是可见的
-
幻读(针对插入操作)假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
事务隔离级别
以下隔离级别从上到下隔离度逐渐变强,并发度逐渐变弱。
-
读未提交(READ CUNCOMMITTED)
-
读已提交(READ COMMITTED)
-
可重复读(REPEATABLE READ)Mysql的默认隔离级别
-
串行化(SERIALIZABLE)
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
串行化 | 不可能 | 不可能 | 不可能 |
多版本并发控制(MVCC)
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现。这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然存储的并不是真实的时间而是系统版本号(system version number)。
MVCC只工作在读已提交和可重复读两种隔离级别下。他是行级锁的一个变种,很多情况下避免了加锁操作。
可重复读下MVCC的工作方式:
select:只查找版本号早于当前事务版本的数据行,这样可以保证事务读取的行要么是在开始事务之前存在的,要么是当前事务修改的;行的删除版本号要么未定义,要么大于当前版本号,这样可以确保事务读取到的行在事务开始之前没有被删除。
insert:为插入的每一行保存当前系统版本号作为行版本号。
delete:为删除的每一行保存当前系统版本号为删除版本号。
update:为插入的一行新纪录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识。
可重复读和读已提交在MVCC工作方式上的不同:
二者最主要的区别是在快照的创建上,可重复读只在事务开始时创建一次,而读已提交在每次执行语句的时候都创建一次。
对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:
-
当前事务内的更新,可以读到;
-
版本未提交,不能读到;
-
版本已提交,但是却在快照创建后提交的,不能读到;
-
版本已提交,且是在快照创建前提交的,可以读到;
事务日志及事务实现原理
-
使用事务日志使得存储引擎在修改表的时候只需要修改其内存拷贝,再把该修改行为记录到硬盘的事务日志上,而不用每次都将修改的数据本身持久化到磁盘。
-
事务日志采用追加的方式,写日志的操作时磁盘上一小块区域内的顺序IO,而不是随机IO,所以采用事务日志的方式相对来说更快。
-
事务日志持久之后,内存中的数据会慢慢刷到磁盘。
-
如果数据的修改以及记录到事务日志并持久化,但是数据本身没有写回到磁盘,此时系统崩溃,存储引擎在重启的时候可以自动恢复这一部分修改的日志。
mysql的事务实现原理
事务的原子性、一致性和持久性则是通过事务日志实现。说到事务日志,不得不说的就是redo和undo。
- redolog
在innoDB的存储引擎中,事务日志通过重做(redo)日志和innoDB存储引擎的日志缓冲(InnoDB Log Buffer)实现。事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)。当事务提交之后,在Buffer Pool中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。
在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录Redo Log,通过顺序IO来改善性能。所有的事务共享redo log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起。如下一个简单示例:
记录1:<trx1, insert…>
记录2:<trx2, delete…>
记录3:<trx3, update…>
记录4:<trx1, update…>
记录5:<trx3, insert…>
- undolog
undo log主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。
以下是undo+redo事务的简化过程
假设有2个数值,分别为A和B,值为1,2
-
start transaction;
-
记录 A=1 到undo log;
-
update A = 3;
-
记录 A=3 到redo log;
-
记录 B=2 到undo log;
-
update B = 4;
-
记录B = 4 到redo log;
-
将redo log刷新到磁盘
-
commit
在1-8的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。
redolog保障了事务的持久型和一致性,undolog保障了事务的原子性
事务的使用
设置隔离级别
设置隔离级别的语句:set [作用域] transaction isolation level [事务隔离级别],SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
其中作用域可以是当前session也可以是global全局的例如设置全局隔离级别为读已提交:
mysql> set global transaction isolation level read committed
事务的开始与提交
begin不代表事务的开始,事务开始于begin命令之后第一条语句执行的时候,以下语句中select * from xxx才是事务的开始
begin;
select * from xxx;
commit; -- 或者 rollback;
可以查询当前有多少事务在运行
select * from information_schema.innodb_trx;