数据库相关知识粗略整理
参考https://github.com/CyC2018/CS-Notes/blob/master/notes/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86.md
事务
- 指的是满足ACID特性的一组操作,可以通过commit提交一个事务,也可以使用rollback进行回滚
ACID
- 原子性(atomicity):事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚
- 回滚可以用回滚日志(Undo log)来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可
- 一致性(consistency):数据库在事务执行前后保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的
- 隔离性(isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的
- 持久性(durability):一旦事务提交,则其所做的修改将会永远保存到数据库中,即使系统发生崩溃,事务执行的结果也不能丢失
- 系统发生崩溃可以用重做日志(redo log)进行恢复,从而实现持久性。与回滚日志记录数据的逻辑修改不同,重做日志记录的是数据页的物理修改
事务的ACID特性概念简单,但是不好理解,主要是因为这几个特性不是一种平级关系: - 只有满足一致性,事务的执行结果才是正确的
- 在无并发的情况下,事务的串行执行,隔离性一定能满足,此时只需要满足原子性,就一定能满足一致性
- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能保证一致性
- 是我母案组持久化是为了能应对系统崩溃的情况
AUTOCOMMIT - MySQL默认采用自动提交模式。也就是说,如果不显示使用START TRANSACTION语句来开始一个事务,那么每个查询操作都会被当一个事务并自动提交
并发一致性问题
丢失修改
- 两个并行事务AB,A先修改了一个数据后提交生效了,B随后进行了修改,覆盖了A的修改
读脏数据
- 两个并行事务AB,A事务修改了一个数据,但是未提交,随后B读取的这个数据,但是A又撤销了这个事务,那么B读到了就是脏数据
不可重复读
- 两个并行事务AB,B先读取了一个数据的值(val=10),随后A对其进行了修改并提交(val=20),B事务在对其进行读取之后,读到的值变为了20,此时B读到的数据结果前后两次不同
幻影读
- 本质上也属于不可重复读的情况,A读取了某个范围的数据,同时B在这个范围中插入了新的数据,A在读取这个范围的数据后,前后两次读取的结果不同
产生并发不一致性问题的主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题
封锁
封锁粒度
- MySQL中提供了两种封锁粒度:行级锁和表级锁
- 尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能越小,系统的并发成都就越高
- 加锁需要消耗资源,锁的各种操作(获取锁、释放锁、检查锁状态)都会增加系统的开销,隐刺封锁粒度越小,系统的开销就越大
- 在选择封锁粒度是,需要在开销和并发成都之间权衡
封锁类型
- 读写锁
- 互斥锁(exclusive),X锁,写锁
- 共享锁(shared),S锁,读锁
有以下两个规定: - 一个事务对数据对象A加了X锁,就可以对A进行读取和更新。在加锁期间其他事务不能对A加任何锁
- 一个事务对数据对象A加了S锁,可以对A进行读取操作,但是不能更新,加锁期间其他事务可以对A加S锁,不能加X锁
- 意向锁
- 使用意向锁(Itention Locks),可以更容易地支持多粒度封锁
在存在行级锁和表级锁的情况下,事务T想要对表A加X锁,就需要先检测是否有其他事务对表A或者表A中的任意行加了锁,那么就需要对表A的每一行进行检测,非常耗时 - 意向锁在原来的X/S锁上引入了IX/IS,IX/IS都是表级锁,用来表示一个事务想要在表中的某个数据行上加X锁或者S锁。
- 一个事务在获得某个数据行对象的S锁之前,必须先获得表的IS锁或者更强的锁
- 一个事务在获得某个数据行对象的X锁之前,必须先获得表的IX锁
- 通过引入意向锁,事务T想要对表A加X锁时,只需检查表A是否有其他的事务对表加了X/IX/S/IS锁,如果加了,表示其他事务针对表A或者表A的某一行进行操作,事务T加X锁失败
- 任意IS/IX锁之间是兼容的,因为意向锁知识表示逍遥对表加锁,不是真正的加锁
- 还有一种情况表级的IX锁和行级的X锁是兼容的,两个事务可以对同一个表加IX锁,事务A对数据行R1进行X锁,事务B对数据行R2进行X锁,这是允许的
封锁协议
- 三级封锁协议
一级封锁协议
事务T要修改数据A时,必须加X锁,直到T结束才释放锁
可以解决丢失修改的问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖