start transaction 要执行的sql语句 commit
一个事务包含多个sql语句,事务保证这些sql语句要么都执行成功,要不都失败
A - 原子性 C - 一致性 I - 隔离性 D - 持久性
其原理体现在(有一个sql失败,会执行rollback回滚,回滚到事务之前的状态):Undo log实现 当开始事务后,innodb会把每条sql的执行信息记录在undolog中,如果有sql执行失败,innodb会根据undolog中记录的内容去执行相反的操作: 原来insert,,那我就delete 原来delete,那我就insert 原来update,,那我就操作相反的update,把数据再改回去 所以,,,Undo log日志实现了事务的原子性
通过redo log实现 mysql的数据是存在磁盘中,每次读取需要通过io,效率很低, 所以innodb提供一个缓存buffer,这个缓存buffer中包含磁盘数据页的映射 当从数据库读数据时,会先从buffer中取, buffer中如果没有,再去磁盘读,读到后会放到buffer 当向数据库写数据时,也是先写到buffer中,,然后定期将buffer中数据刷新到磁盘上,,做一个持久化操作 这样会有一个问题: buffer的数据还没有同步到磁盘,,此时mysql宕机了, 这样buffer中数据就丢失了,,这就没法保证持久性了? redo log是一个预写式日志: 数据库的每次操作,,他会先写到redo log中,,在更新到buffer中 redo log也是把日志写到磁盘,,它比直接将buffer数据写到磁盘快的原因: 1).buffer的持久化是随机写的io(每次修改的数据位置是随机的),redo log的持久化是追加模式(在文件尾部追加,属于顺序io操作) 2).buffer的持久化是以数据页page为单位,mysql默认的配置页是16k,,一个数据页上小小的一处修改都需要把整个数据页写入,,而redo log 只需要写入真正修改的部分, 这样无效的io就大大减少了 redo log 持久性3种策略: 0-表示当提交事务时,,并不将redolog缓冲区的日志写入磁盘文件,,而是等待主线程每秒刷新 1-在事务提交时将edolog缓冲区的日志同步写入磁盘,,保证一定会写入成功(推荐) 2-在事务提交时将edolog缓冲区的日志异步写入磁盘,,即不能完全保证commit时肯定会写入redolog日志
1)数据库存在的并发操作使用的事务控制
mysql事务的隔离性是通过mvcc+锁来实现控制的... 数据库并发场景有三种,分别为:
读-读:不存在任何问题,也不需要并发控制
读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读...MVCC控制
写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失...锁控制
2)锁的概念:
事务在修改数据之前,这个事务要获取相应的锁,只有拿到锁才能操作数据..... 如果其他事务想操作这个数据,必须等当前事务提交或回滚后释放锁以后, 再去竞争这个锁, 拿到锁后才能执行其自身事务 innodb下有行锁(默认, 但需要索引触发), 表锁, 间隙锁(范围概念,,锁多行) myisam默认支持表锁
3)mvcc概念:
mvcc: 多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制, 主要是为了提高数据库的读写并发性能. mvcc可以更好的处理读请求,做到发生读写请求冲突时不用加锁...这里的读指的是快照读,,而不是当前读(当前读是一种加锁操作,,是悲观锁)...
4)啥是当前读?
它读取的数据库记录是当前最新的版本,,,会对当前读取的数据进行加锁,,,防止其他事务修改数据,,典型的悲观锁操作 以下操作触发当前读: select * from .....lock in share mode(共享锁) select * from .....for update(排它锁) update(排它锁) insert(排它锁) delete(排它锁) 串行化事务隔离级别(串行化指的就是事务一个接一个执行,类似于单线程) 这里解释下数据库的共享锁和排它锁: 共享锁也叫读锁, 是指读可以并发,但是写会阻塞 排它锁也叫写锁, 无论读写都会阻塞
5)啥是快照读?
innodb下普通的select语句都是快照读
它读取的版本可能不是最新版本, 可能是历史版本... 快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读 说白了MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读
6)详解mvcc快照读
先说几个概念: 我们在数据库表中看到的一行记录实际上有多个版本,每个版本的记录除了有数据本身外,包含事务id trx_id(自增的) 和回滚指针roll_pointer, 如此记录的多个版本之间构成版本链, 每个版本记录通过回滚指针指向上一个版本记
ReadView(快照读对象)实现快照读:它让你知道版本链中哪个版本是可用的
ReadView4个属性:
m_ids: 生成ReadView时当前系统中未提交的事务id列表,,
min_trx_id: 最小的事务id,
max_trx_id: 下一个需要分配的事务id,,
creator_trx_id: 生成该ReadView的事务id
对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:
当前事务内的更新,可以读到; 版本未提交,不能读到; 版本已提交,但是却在快照创建后提交的,不能读到; 版本已提交,且是在快照创建前提交的,可以读到;
7)事务四种隔离级别
读未提交, 读已提交, 可重复读, 串行化
8)总结: MySQL是如何实现事务隔离的
读未提交 它是性能最好,也可以说它是最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。但有利就有弊,这基本上就相当于裸奔啊,所以它连脏读的问题都没办法解决 读已提交 是每次执行select语句的时候都重新生成一次快照 可重复读 是在事务开始的时候生成一个当前事务全局性的快照 两者主要的区别就是在快照的创建上,可重复读仅在事务开始是创建一次,而读提交每次执行select语句的时候都要重新创建一次
串行化 是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束 读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读 InnoDB 默默的把所有纯 SELECT 语句都转成了 SELECT ... FOR SHARE ,也就默认都加读锁 特别注意: MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的
事务执行前后数据是完整一致的, 它是事务的最终目的