存储引擎
现在常用的存储引擎是InnoDB,以前常用的是MyISAM。
InnoDB
支持事务、支持行级锁、支持外键、支持崩溃后数据恢复、支持MVCC;
InnoDB的锁算法:
Record lock:记录锁,单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record+gap 临键锁,锁定一个范围,包含记录本身
MyISAM
不支持事务、支持表级锁、不支持外键、不支持崩溃后数据恢复、不支持MVCC
事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
ACID特性:
原子性(Atomicity) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性(Consistency): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
如何实现:
MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性,使用 undo log(回滚日志) 来保证事务的原子性。
MySQL InnoDB 引擎通过 锁机制、MVCC 等手段来保证事务的隔离性( 默认支持的隔离级别是 REPEATABLE-READ )。
保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。
事务并发的问题:
脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。
不可重复读(Unrepeatable read): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
事务的隔离级别:
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
InnoDB默认隔离级别是可重复读,可重复读无法解决幻读问题,需要加锁读来解决可重复读级别下的幻读问题,这个锁就是上面提到的Next-Key Lock;
只有在分布式事务情况下会用到可串行化隔离级别
索引
常见的索引结构:Hash、B树、B+树,InnoDB使用的索引是B+树
B树和B+树的区别
B 树的所有节点既存放键(key) 也存放 数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
索引类型
主键索引(属于聚集索引)
二级索引(属于非聚集索引):
唯一索引
普通索引
前缀索引
全文索引
覆盖索引:如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。
一致性非锁定读
实现一致性非锁定读的方法通常是使用版本号或者时间戳字段,每更新一次就版本号+1或者更新时间戳。而在InnoDB存储引擎中对非锁定读的实现是通过多版本控制(MVCC)实现的,读的数据属于快照读
事务的可重复读级别就是通过MVCC实现的
在一致性非锁定读下,即使读取的记录已被其它事务加上X 锁,这时记录也是可以被读取的,即读取的快照数据。上面说了,在 Repeatable Read 下 MVCC 防止了部分幻读,这边的 “部分” 是指在 一致性非锁定读 情况下,只能读取到第一次查询之前所插入的数据(根据 Read View 判断数据可见性,Read View 在第一次查询时生成)。但是!如果是当前读 ,每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。所以, InnoDB 在实现Repeatable Read 时,如果执行的是当前读,则会对读取的记录使用 Next-key Lock ,来防止其它事务在间隙间插入数据
MVCC
MVCC的实现主要依赖隐藏字段、Read View、undo log。InnoDB会通过隐藏字段里的DB_TRX_ID和Read View来判断数据的可见性,如果不可见,则通过DB_ROLL_PTR 找到undo log对应历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改
Read View 主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”
读提交(RC)级别和可重复读(RR)级别下,MVCC的不同之处在于
RC是每次select查询前都会生成一个ReadView
RR是在事务开启后第一个select数据前生成一个ReadView
锁定读
如果执行的是下列语句,就是 锁定读(Locking Reads)
select ... lock in share mode(加读锁)
select ... for update(加写锁)
insert、update、delete 操作(加写锁)
锁定读读取的是最新版本数据,叫做当前读