事务ACID性质
- A – 原子性:整体事务只有全部失败或全部成功。通过恢复日志进行事务的回滚
- C – 一致性:事务操作前后的状态具有一致性。这是指满足开始和结束的时候受到的程序约束是一致的。一致性状态下,事务对同一个数据的读取结果都是相同的。
这里的一致性是指系统从一个正确的状态,迁移到另一个正确的状态.什么叫正确的状态呢?就是当前的状态满足预定的约束就叫做正确的状态.而事务具备ACID里C的特性是说通过事务的AID来保证我们的一致性.
- I – 隔离性:数据执行事务操作时不能被其他事务操作。在提交前,对其他事务是不可见的
- D – 持久性: 事务操作对DB的影响是永久的。
其中事务一致性的保证在串行与并行条件有不同的条件要求,见下图
事务并发一致性保证 — 隔离性
对应数据库的四种隔离级别:
-
Read Uncommitted:允许事务读取另一个尚未被提交的数据。
- 脏读:如果这个数据被另一事务回滚,就会造成脏读
-
Read Committed:事务只能读取已提交的数据,可以解决脏读。
- 不可重复读:如果在事务范围内,读取的数据被修改(UPDATE)并且提交,导致前后读取的数据出现不一致,就会导致不可重复读的情况
-
Repeatable Read:事务开始读取某个数据后,这个数据就不能被其他事务所修改。
- 幻读:可重复读的隔离级别,解决了UPDATE的问题,但是由于无法控制整个表的状态,如果有数据INSET INTO这个表中,而事务中由使用了COUNT()这一类的表级统计函数,就会导致幻读的现象
-
Serialized:将所有的数据库操作串行化,不会有一致性问题,但是性能会被大大降低,一般不会使用这个级别。
Oracle和Sql Server采用的默认隔离级别是 Read Committed,而MySQL的默认级别是Repeatable Read
数据库的锁
加锁需要权衡数据一致性保证以及数据库的读写性能。在满足一致性的前提下,加锁的操作应该尽量少一些
- 互斥锁:X锁,锁住读取和更新操作。一般在对数据进行修改的时候加
- 共享锁:S锁,其他事务也可以读,但是不能修改(加X锁)。
表锁和行锁同时存在时,事务要对表A加X锁,要先检测其他事务是否对A中任意一行加了锁,严重拖慢性能
-
意向锁:引入了IX/IS – 都是表锁
- 事务要对表中某一行数据加锁前,需要先获得表的IX/IS锁,才可以执行
- 此后,如果需要对表加锁,只需要检测它是否有IX/IS锁即可,不需要每一行去遍历
- 任意 IS/IX 锁之间都是兼容的,因为它们只表示想要对表加锁,而不是真正加锁;
封锁协议
-
一级封锁:修改数据必须加X锁,事务结束才能释放。
- 这样不会有两个事务同时修改一个数据
-
二级封锁:在一级的基础上,要读取必须加S锁。读完了就释放
- 有效解决脏读问题。如果数据正在被修改,根据一级协议,就不能再加S锁了
-
三级封锁:在二级基础上,S锁在事务结束才能释放
- 解决不可重复读问题
两段锁协议:加锁和解锁分为两个阶段进行。前面一直在加锁,后面一直在释放锁。
- 在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁。
- 在释放一个封锁之后,事务不再申请和获得其它任何封锁。
- 可串行化调度:通过并发控制,使得并发执行的事务结果与串行结果相同。事物之间相互不干扰,不会出现并发一致性问题
多版本并发控制(MVCC)
MVCC主要用于实现Read Committed和Repeatable Read两种隔离级别。
MVCC的核心思想与CAS类似,是通过匹配版本来决定是否更新数据。
基本思想:
- 写操作(INSERT UPDATE DELETE)会更新数据的快照版本
- 读操作会去读旧版本的数据
脏读和不可重复读最根本的原因是事务读取到其它事务未提交的修改
MVCC通过控制数据的版本,让读取只能读取到旧的数据,如果一个数据修改后未提交,版本号不一致,则不会读取新版本数据。
MVCC解决的是SELECT读取的无锁化,在进行修改操作的时候还是要加锁
InnoDB引擎的MVCC实现:
- 插入数据的时候,会更新一个create version字段
- 修改数据的时候,会将之前的那一行数据删除,然后重新创建那一行对应的新值。DeleteVersion是当前的事务版本号,与新插入的create version相等
- 删除操作时,会将delete version 置为当前版本号。
读取条件:
- 删除版本号未指定,或者大于当前版本号。TX<D
- create version不大于当前事务版本号。即TX <= C
数据库设计三范式
- 确保每一列的原子性。每一列的项不可再拆分。
- 例如:数据库有一列“用户是否在1号或者2号外出”就不满足,因为可以继续拆分成“1号外出”和“2号外出”
- 确保每一列的数据都和主键相关
- 一个表中只能保存一种数据,不可以把多种数据存在同一张DB中
- 下标可以继续拆分成 商品 – 客户。并让他们和订单编号关联
- 确保每列都和主键列直接相关,而不是间接相关
- 例如有一个Student表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)。
- 院校和院校电话和学号并不是之间关联的,所以需要进行分表