数据库系统原理知识点整理
数据库系统原理
事务
事务指满足ACID
特性的一组操作,可以通过Commit
提交一个事务,也可以使用Rollback
进行回滚 。
ACID
- 原子性(
Atomicity
):事务是不可分割的最小单元,要么所有操作全部Commit
成功,要么全部失败Rollback
- 一致性(
Consistency
):数据库在事务执行前后保持一致性状态。在一致性状态下,所有事务对同一数据的读取结果都是相同的 - 隔离性(
Isolation
):一个事务所做修改在最终提交之前,对其他事务不可见 - 持久性(
Durability
):一旦事务提交,则所做的修改将会永远保存到数据库中
- 只有满足一致性,事务执行结果才能正确
- 无并发情况下,只要满足原子性,就可以满足一致性
- 并发情况下,多个事务并发执行,需要满足原子性和隔离性,才能满足一致性
- 事务持久性是为了应对系统崩溃的情况
并发一致性问题
在并发环境下,事务的隔离性难以保证,会出现很多并发一致性问题
- 丢失修改:
T1
和T2
都修改了一个数据A
,T1
先修改A=10
,T2
后修改A=20
,T2
的修改会覆盖T1
的修改。 - 读脏数据:
T1
修改了数据A
,T2
随后读取了数据A
。若T1
撤销了此次修改,T2
读取的数据就是脏数据。 - 不可重复读:
T1
读取了数据A
,T2
随后修改了数据A
。若T1
再次读取数据A
,两次读取结果不同。 - 幻影读:
T1
读取了某个范围的数据,T2
随后在这个范围内插入数据,若T1
再次读取这个范围内数据,两次读取结果不同。
不可重复读与幻读相似,区别在于:不可重复读的重点在于修改,而幻读的重点在于新增或删除
封锁
封锁粒度
- 行级锁
- 表级锁
锁定的数据量越少,发生锁争用的可能就越小,系统并发程度就越高。
加锁需要消耗资源,锁的各种操作(如获取锁、释放锁以及检查锁状态)都会增加系统开销,封锁粒度越小,系统开销越大。
封锁类型
- 读写锁
- 互斥锁(
Exclusive
),简写为X
锁,又称写锁 - 共享锁(
Shared
),简写为S
锁,又称读锁 - 一个事务对数据对象
A
加了X
锁,可以对A
进行读取和更新。加锁期间其他事务不能对A
加任何锁 - 一个事务对数据对象
A
加了S
锁,可以对A
进行读取操作,但是不能进行更新操作。加锁期间其他事务能对A
加S
锁,但是不能加X
锁
- 互斥锁(
- 意向锁
- 在存在行级锁和表级锁的情况下,事务
T
想要对表A
加X
锁,就需要先检测是否存在其他事务对表A
或者表A
中的任意一行加了锁,对A
的每一行都检测一次,非常耗时 - 意向锁在
X/S
锁之上引入IX/IS
,IX/IS
都是表锁,用来表示一个事务想要在表中的某个数据行上加了X
锁或者S
锁,有如下规定:- 一个事务获得某个数据行对象的
S
锁之前,必须先获得表的IS
锁或者更强的锁 - 一个事务获得某个数据行对象的
X
锁之前,必须先获得表的IX
锁
- 一个事务获得某个数据行对象的
- 在存在行级锁和表级锁的情况下,事务
- 任意
IX/IS
锁之间兼容,它们只表示想要对表加锁,而不是真正的加锁
封锁协议
三级封锁协议
一级封锁协议
事务
T
想要修改数据A
时必须加X
锁,直到T
结束才释放锁,可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,事务的修改不会被覆盖二级封锁协议
在一级的基础上,要求读取数据
A
时必须加S
锁,读取完释放S
锁,可以解决读脏数据问题,因为根据一级封锁协议,数据加了X
锁,其他事务就不能加S
锁,也就不能读入数据三级封锁协议
在二级的基础上,要求读取数据
A
时必须加S
锁,直到事务结束才释放S
锁,可以解决不可重复读问题,因为读取A
时,其他事务不能对数据A
加X
锁,避免在读期间数据发生变化
两段锁协议
加锁和解锁分为两个阶段进行
MySQL
实现事务ACID
特性的方式总结:
- 原子性:使用
undo log
来实现,如果事务执行过程中出错或者用户执行了rollback
,系统通过undo log
日志返回事务开始的状态- 持久性:使用
redo log
来实现,只要redo log
日志持久化了,当系统崩溃,可以通过redo log
把数据恢复- 隔离性:通过锁以及
MVCC
来实现- 一致性:通过回滚、恢复以及并发情况下的隔离性从而实现一致性
隔离级别
未提交读(
READ UNCOMMITED
)事务中的修改,即使没有提交,对其它事务也是可见的
提交读(
READ COMMITED
)一个事务所做修改在提交之前对其它事务不可见
可重复读(
REPEATABLE READ
)保证在同一个事务中多次读取同一数据的结果是一样的
可串行化(
SERIALIZABLE
)强制事务串行执行,不会出现并发一致性问题,需要加锁保证同一时间只有一个事务执行
多版本并发控制
Next-Key Locks
关系数据库设计理论
函数依赖
异常
- 冗余数据
- 修改异常
- 删除异常
- 插入异常
范式
范式理论是为了解决上述四种异常
高级别范式依赖于低级别范式,
1NF
是最低级别的范式第一范式(
1NF
)属性不可分
第二范式(
2NF
)每个非主属性完全函数依赖于键码
可以通过分解来满足
分解前
以上学生课程关系中,{Sno,Cname}为键码,有如下函数依赖:
- Sno -> Sname,Sdept
- Sdept -> Mname
- Sno ,Cname -> Grade
Grade完全函数依赖于键码,没有任何冗余数据
Sname,Sdept,Mname部分依赖于键码,当一个学生选修多门课时,就会出现冗余
分解后
函数依赖:
- Sno -> Sname,Sdept
- Sdept -> Mname
函数依赖:
- Sno,Cname -> Grade
第三范式(
3NF
)非主属性不传递函数依赖于键码
上面
关系-1
中存在如下传递函数依赖- Sno -> Sdept -> Mname
可以进行分解:
ER图
Entity-Relationship,由实体、属性、联系组成
实体的三种联系
包含一对一、一对多、多对多
- 若A到B是一对多关系,一个带箭头的线段指向B
- 若A到B是一对一,两个带箭头的线段
- 若A到B是多对多,两个不带箭头的线段
索引
索引使用的场景
- 对于非常小的表,大部分情况下简单的全表扫描比建立索引更高效
- 对于中大型的表,索引非常有效
- 对于特大的表,建立和维护索引的代价会变大,需要用到一种技术直接区分出需要查询的一组数据而不是一条条记录的匹配,例如使用分区技术
索引的原则
- 选择唯一性索引
- 为经常需要排序、分组和联合操作的字段建立索引
- 为经常作为查询条件的字段建立索引
- 限制索引的数目
- 尽量使用数据量少的索引
- 尽量使用前缀来索引
- 删除不再使用或者很少使用的索引
- 最左前缀匹配原则
- 尽量选择区分度高的列作为索引
- 索引列不能参与计算,带函数的查询不参与索引
- 尽量的扩展索引,不要新建索引
B Tree原理
- B-Tree
- B+Tree
MySQL事物实现原理
redo log
undo log
MVCC
参考文章:
https://frank-lam.github.io/fullstack-tutorial/#/MySQL
朱小厮的博客:分布式事务科普