事务的实现原理
-
锁
mysql在读写操作的时候锁定,commit或rollback时候解锁
-
行(记录)锁:解决并发写的问题(可重复读级别)。若要操作的行是有索引的,则会直接在索引字段找到并加行锁;无索引加行锁的方式是:先锁整张表,然后过滤非操作行,留下的就是行锁,故而性能较差,大表建议加索引。
行锁特点:锁粒度小,并发性能高,开销大,加锁慢,会出现死锁,mysql的Innodb支持(myisam不支持),分为共享锁和排它锁,锁冲突概率低。
-
表锁:锁粒度大,并发性能低,简单,开销小,加锁快,不会出现死锁,很多数据库引擎都支持,分为共享锁和排它锁,锁冲突概率高。
lock/unlock table
语句可以显示开启 -
页级锁:介于行锁和表锁之间,各种指标中规中矩。
-
next-key(临键)锁:行锁+间隙锁(解决并发写和幻读),假如age两条字段10、30都加了索引,底层维护一套B+树分割区间,现在操作age=10,则10加行锁,(-∞,10]加间隙锁,(10,30]加间隙锁, 30和(30,+∞)不受影响。
mysql利用next-key锁解决了在可重复读下的幻读问题。
间隙锁的危害:有些间隙范围被无辜上锁,影响性能。所以一般是逻辑上的删除(一个标致字段),避免产生间隙锁。
-
读锁(共享、S)锁:可并发读,不可并发写。可以用
select xx lock in share mode
语句可以显示开启 -
写(排它、独占、X)锁:只有一个获取到这个锁的事务能够读和写,其他没锁的就阻塞。
select xx for update
语句可以显示开启 -
意向锁:Intention Share(Exclusive) Lock,即IS、IX锁,加行级的读、写锁之前在表级进行标记,避免其他事务想加锁还需要一行行遍历看是否加过锁了。Innodb自动加,无需用户干预。
-
死锁:比如事务A、B,A持有某个行锁或间隙锁,B持有某个行锁或间隙锁,A、B继续操作都需要等待另外一个事务给他资源,互相等待形成死锁。形成了死锁闭环后,innodb会等待锁释放超时:
innodb_lock_wait_timeout=50s
,超时后会让其中一个事务让出资源,另外的事务执行成功。死锁排查show engine innodb status
。
-
-
基于日志
-
bin log:主从复制用的日志,记录着主库当前已完成的修改操作信息,从库也执行这些操作,就得到和主库一样的数据。
-
redo log:记录修改后的值,出于性能考虑,mysql不会每次都把修改实时同步到磁盘,而是先存到buffer pool(缓存池)中,后台有专门线程慢慢将缓存池数据持久化到磁盘,为了避免宕机缓存数据丢失来不及同步问题,还需要加一层保障,那就是redo log,在每次提交操作之后就立马将信息持久化到磁盘的redo log日志中,即是宕机了,下次系统还会从redo log恢复已提交的操作。 故redo log保证持久性
buffer pool(缓存池)的存在大大提升了数据库的性能,但因为是缓存,也有宕机丢失的风险。
redo log 一般分为4组,每组1G,记录位置分为
write pos
跟checkpoint
write pos
:是当前记录的位置,一边写一边后移,写到第3号文件末尾后就回到0号文件开头。check point
:是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。 若
write pos
追上了checkpoint
,此时就阻塞,先擦除一些数据,checkpoint
往前面跑,然后write pos
再跟上来。事务执行过程中发生宕机怎么处理(redo log二阶段提交?)
innodb容灾能力叫
crash-safe
,由redo log和bin log的逻辑同步保证,即redolog二阶段提交保证,分为两个阶段:1 prepare阶段(写入redo log) --> 2 写binlog --> 3 commit。
当在2之前崩溃时,重启恢复后发现没有commit,回滚。备份恢复:没有binlog 。一致
当在3之前崩溃时,重启恢复发现虽没有commit,但满足prepare和binlog完整,所以重启后会
自动
commit。备份:有binlog. 一致 -
日志恢复和checkpoint:崩溃前最后的提交信息还没有标记成已检查点END_CKPT,恢复后从最后的已检查点开始恢复redo log,将崩溃时未写入的已提交信息写入磁盘。
-
undo log:记录修改前的值,回滚日志,记录和写操作相反的操作,当事务回滚,就执行这些反操作,将数据恢复成修改前的模样。保证的是原子性
分为2种,insert undo log和 update undo log
-
MVCC (MultiVersion Concurrency Control):通过数据的多版本来实现读写分离,从而实现不加锁且读写并行。依赖undo log与read view(用于判断当前版本数据可见性)。
每个事务都有自己的事务id、创建时间、过期时间,记录在undo log中,相当于对数据进行一个版本控制,每个事务根据版本信息就只能查到自己操作的那个数据版本,其他版本是查不到的,比如事务2只能查到创建时间小于等于它的创建时间、以及过期时间要大于等于它的创建时间的 所有操作,这样就保证了事务的隔离性。
-
mysql通过 保证持久性(redo log)、原子性(undo log)、隔离性(读写锁+MVCC) 来最终实现 一致性!!!