事务的基本特性就是ACID
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:
我们需要根据事务的原子性、隔离性和持久性来保证数据的一致性
那么多个事务访问同一个资源时,如何保证资源的可见性呢,这里需要谈到隔离级别了
read uncommit
:读未提交也叫做脏读,可能会读到其他事务未提交的数据
read commit
:读已提交也叫做不可重复读,两次读取结果不一致,oracle的默认级别
repeatable read
:可重复读,这是mysql的默认级别,就是每次读取结果都一样
serializable
:串行,一般不使用。给每一行读取的数据加锁, 会导致大量超时和锁竞争的问题原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
一致性由其他三大特性保证,程序代码要保证业务层面上的数据一致性
隔离性由MVCC来保证
持久性由内存、redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可以从redo log恢复
redo log的刷盘会在系统空闲时进行
将新数据写入内存,然后对InnoDB的redo log写盘,redolog中事务进入prepare状态
如果前面prepare成功,bin log写盘,将事务日志持久化到bin log
如果持久化成功,那么在redo log里面写一个commit 记录,表示InnoDB事务进入commit状态
如果redolog写入完,变为prepare状态,可是断电了,没来得及写入bin log的话,在重新开启后会抛弃已经prepare的事务(因为它没有完成提交,所以不作数)
在innodb中有两个日志,undo log和redo log
在mysql server中有一个叫bin log的日志,也和事务有关
事务的成功不仅要在bin log中留下记录,而且也在redo log中写入commit提交状态的记录,表示事务已经完成提交
MVCC,全称 Multi-Version Concurrency Control
,即多版本并发控制
读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据——版本链
MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存
MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
MVCC只在RC和RR两钟隔离级别下工作,即read commit不可重复读和repeatable read可重复读
脏读每次读的都是最新的数据,不符合
串行会给所有读取的行都加锁,也不符合
一般我们认为MVCC有下面几个特点:
在InnoDB中,会在每行数据后添加两个额外的隐藏的字段来实现MVCC ,一条记录除了包括各个字段值,还包括了当前事务id(trx_id)和一个回滚指针(roll_pointer)。
注:一个事务的事务id在第一次insert/delete/update时生成
其实Read View的最大作用是用来做可见性判断的
也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据
有可能读取到的是最新的数据,也有可能读取的是当前行记录的undolog中某个版本的数据
事务开启后会创建readview,用于维护当前活动的事务的id(活动=未提交的事务),并排序生成一个数组
readview中存储了当前Read View中最大的ID及最小的ID
之前提到了在数据的隐藏列中trx_id用于储存了事务id(获取的是最大的那个id,即最新的id),在读取数据时,会拿到这个事务id去和readview中的比对
up_limit_id是当时活跃事务列表的最小事务id
db_trx_id<up_limit_id
,说明这些数据在事务创建id的时都已经提交,如注释中的描述,这些数据均可见ow_limit_id是当时活跃事务的最大事务id
db_trx_id>=low_limit_id
,说明这些id在此之前的数据都没有提交,如注释中的描述,这些数据都不可见读已提交(不可重复读)隔离级别下的事务在查询的开始会创建一个独立的readview
可重复读隔离级别下的事务在第一次读的时候生成一个readview,之后的读都是复用的这个readview
即:通过不同的readview生成策略来实现不同的隔离级别
原子性:
持久性:
当前读:读取的是数据的最新版本,总是能读取到最新的数据
快照读:读取的是历史版本的快照记录
为什么mysql中会有历史版本的记录呢?
原子性由undo log日志保证,它记录了需要回滚的日志信息,有时候可能会需要回滚,所以才将其记录了下来
在mysql默认的RR不可重复读隔离级别中,select读是快照读,读取的是历史版本的快照记录
而上锁或对表的操作是当前读
lock in share mode
for update
update
insert
delete
共享锁:
IN SHARE MODE
获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作排他锁:
FOR UPDATE
方式获得排他锁delete:
insert:
update:
# 共享锁(S): SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE # 排他锁(X): SELECT * FROM table_name WHERE ... FOR UPDATE
关于mysql锁其实还有很多,可以查看一下这个博客
https://www.cnblogs.com/zhuwenjoyce/p/15060318.html