这是本人从网上文章、B站等各处参考总结所得出,有不足、不对的 恳请指出
什么是事务比如说有个场景,我们需要插入多条相关联的数据到数据库,这个过程可能会遇到下面这些问题:
1:数据库中途突然因为某些原因挂掉了。
2:因为网络原因连接不上数据库了。
上面的问题都可能会导致数据的不一致性(部分成功)。为了保证数据的一致性,系统必须能够处理这些问题。事务就是抽象出来的一种机制。事务的概念起源于数据库。
总而言之:事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务的简单使用
1:start transaction; //开启事务
2:update ex_drug set number=number-10 where id=‘15’; (数据在内存,未写入磁盘) //执行sql
3:(ROLLBACK;) (基于undo log实现) //回滚
4:commit;(写入磁盘) //提交事务
事务的四大特性
原子性(atomicity):
一个事务就是一个最小单位,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。基于undo log日志实现
一致性(consistency):
执行事务前后,数据保持一致, 例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;(质量守恒定律)
可以理解为事务的目的,而原子性、隔离性、持久性均是为了保障这个目的的手段
隔离性(isolation):
多个用户并发访问数据时,各个事务相互隔离,互不干扰。数据库提供四种隔离级别
Read UnCommitted(读未提交)
Read Committed(读已提交)
Repeatable Read(可重复读)
Serializable(串行)
持久性(durability)
一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有影响。
(redo log日志保证的,下面介绍下三大日志)
binlog(mysql 执行器里)
日志中包含了一系列变更的操作,例如变更表结构、增、删、改数据等,不包括查询数据。
binlog有三种格式
1:statement:记录的是修改SQL语句,会存在主从不一致的情况,比如说执行uuid() 函数随机生成,主、从得到的结果就不一致 (读已提交下有问题,因此mylsql默认隔离级别为可重复读)
2:row(5.1.5开始):记录的是每行实际数据的变更,缺点是日志量大
3:mixed(5.1.8开始):statement和row模式的混合,系统会自动判断 该 用 Statement 还是 Row:一般的语句修改使用 Statement 格式保存 binlog;对于一些 Statement 无法准确完成主从复制的操作,则采用 Row 格式保存 binlog
redo log(innodb里)
redo log主要是为了保证事务持久性(事务提交后各种不可抗拒因素导致的数据丢失),是通过redo log恢复的,redo log记录的是在某个数据页上做了某个修改,所以 redo 日志整体来说是比较小的,存储的信息不多,redo log则是在事务进行中不断地被写入,是循环写,redo log 只会记录未刷入盘的日志,已经刷入磁盘的数据都会从 redo log 日志文件里删除
redo log包括两部分:一个是内存中的日志缓冲(redo log buffer),另一个是磁盘上的日志文件(redo log file)。
redo log有prepare和commit两个状态,是个二阶段提交过程,是一种保护机制(保证持久性,后面有图解)
redo log是InnoDB特有的,且日志上的记录落盘后会被覆盖掉,也就不能用于数据恢复
undo log(innodb里)
主要记录了数据的逻辑变化
原子性由undo log日志(回滚)来保证,因为undo log记载着数据修改前的信息,比如我们要
insert 一条数据了,那undo log 会记录的一条对应的 delete 日志。
update 一条记录时,那undo log会记录之前的旧值的update记录
(保证原子性)
1:脏读(Drity Read):
某个事务已更新一份数据,另一个事务在此时读取了同一份数据,这个时候如果前一个事务RollBack了操作,则后一个事务所读取的数据就会是不正确的。(脏读主要是针对的是事务回滚操作)
(A事务进行转账,B事务看到数据满足要求,就进行了发货,A回滚了 就炸了)
2:重复读(Non-repeatable read):
在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新了原有的数据。不可重复读主要针对的是修改(update)
3:幻读(Phantom Read):
在一个事务的两次查询中数据笔数不一致,例如有一个事务范围查询了几列数据,而另一个事务却在此时插入(删除)了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。幻读主要是针对新增(insert)和删除(delect);
(即使我们把所有的的记录都加上锁,还是阻止不了新插入的记录,innodb引入了间隙锁来解决这个问题,然后更新的数据会加上行锁,行锁加间隙锁的组合也被称为临键锁(Next-Key Lock))
1:Read UnCommitted(读未提交)
是事务中最低的级别,该级别下的事务可以读取到另一个事务中未提交的数据,脏读、重复读、幻读都无法避免。
由于该级别较低,在实际开发中避免不了任何情况,所以一般很少使用。
2:Read Committed(读已提交)
大多数的数据库管理系统的默认隔离级别都是Read Committed(读已提交),该级别下的事务只能读取其他事务中已经提交的内容,可以避免脏读,但是不能避免重复读和幻读的情况。
互联网项目基本都是将隔离级别设为读已提交
缺点处理:
业务不关心数据是不是“某一个时间点的快照”,如果需要 可以加锁,可以手动使用 select … lock for share 或者 select … for update 手动加锁
优点:
在可重复读隔离级别下,条件列未命中索引会锁表!而在已提交读隔离级别下,只锁行;不可重复读问题在开发中是可以接受的,毕竟你数据都已经提交了,读出来本身就没有太大问题
3:Repeatable Read(可重复读)
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但没有彻底解决幻读,是MySQL默认的事务隔离级别
①:这里的没有彻底解决幻读,是指当前读存在幻读问题,SQL标准下会引入mvcc,所以读是快照读,不会有幻读问题
②:对于更新操作中存在的当前读问题,innodb通过引入间隙锁来解决了
③:为什么是myslq默认的隔离级别?
历史原因:binlog当初只支持statement格式,记录的是修改SQL的语句,读已提交隔离级别下会存在主从数据不一致问题(下面有举例),可重复读引入间隙锁,不会有这种问题。
4:Serializable(串行)
事务的最高隔离级别,它会强制对事务进行排序,使它们彼此之间不会发生冲突,从而解决脏读、幻读、重复读的问题。实际上,就是事务在读、写操作时,会加表级的读锁和写锁,直到事务结束才释放。这个级别,可能导致大量超时现象和锁竞争,实际应用中很少使用(分布式事务会使用,道听途说)。
读已提交隔离级别下会存在主从数据不一致问题



