数据库设计的初衷是处理并发问题。
1. MVCC是什么,怎么实现的?
MVCC是多版本并发控制,是InnoDB存储引擎为了实现对“事务”的支持而实现的一种机制,可以将其看作是行级锁的一个变种,但是可以在很多情况下避免加锁操作,开销更低。
MVCC的具体实现:
- 每个事务都有一个由系统分配的id:transaction_id;单调递增,系统内唯一;
- 每一行数据都有一个隐藏的列:row_trx_id,用于记录操作这行数据的事务的id,每一行数据都都有多个版本,它们组成一个“链表”式的结构,称为“undo log”;
在实现上,InnoDB为每个事务在开始时构造一个“数组”,其中保存在这个事务启动瞬间正在活跃的所有事务ID(所谓“活跃”指的是“启动了还未提交”)。
↓当前事务
[已提交事务] [ 数组 ] [未开始事务]
低水位 高水位
数组中的事务id最小值 数组中的事务id最大值
通过这个数组,生成了 当前事务的“一致性视图”。
视图数组将每个行数据的 row_trx_id 分成了三种不同的情况:
(1)
(2)
(3)
MVCC中的读和写操作:
在MVCC中,读操作是“快照读”,写操作是“当前读”。
更新数据都是先读后写,这个读必须是当前读。
MVCC对于RC和RR的区别:
在InnoDB中,“RR(可重复读)”和“RC(不可重复读)”都是通过MVCC实现的,区别在于:
RR:只在事务开始时生成一个一致性视图(就是那个以数组为基础的+低水位、高水位的视图),所以RR在事务过程中可重复读;
RC:每次语句执行前,都会生成一个一致性视图,所以RC在事务过程中是不可重复读。
MVCC中的读操作如何进行“当前读”:
MVCC中的读操作默认都是“快照读”的,如果想要进行当前读:
select * from 'table_name' lock in share mode; //共享锁
select * from 'table_name' for update; //排他锁
事务开始的时间:
启动一个事务时,begin或者start transaction命令并不是一个事务的起点,执行到第一个操作InnoDB表的命令时才是,
可以强制在begin时开启事务:
begin with consist...
幻读和间隙锁:
在InnoDB中,幻读只会出现在“当前读”时,快照读不会出现幻读。
InnoDB解决幻读的方法是加 “gap lock” 间隙锁。
2. MySQL中的锁:
分三种:
全局锁、表锁、行级锁
表锁:
分两种:
- 表级锁:
- 元数据锁:
//表级锁:
lock table read;
lock table write;
//元数据锁:
//1. 当执行select 查询时就会加锁元数据锁,避免有人增加或删除列:
//2. 如果【在事务中】执行alter table ... add column_table ,会触发事务自动提交,导致事务无法回滚
alter table 'table_name' add column 'column_name'; //加元数据锁
select * from 'table_name'; //加元数据锁
3. MySQL的两阶段提交:
“先写日志,再写磁盘”,
这样做的目的是为了提高MySQL的性能,在空闲时进行写磁盘的操作。
但这样做引入了一个问题:crash-safe。
如果内存中的数据还没来得及写盘的时候就发生了宕机或进程crash,就会造成数据丢失。
以前的MyISAM 存储引擎是不支持crash-safe的,InnoDB支持。
MySQL Server层实现了binlog,InnoDB实现了redo log,
redo log是循环写,binlog是追加写,
所以在宕机恢复后只要恢复redo log中的内容就可以了。
redo log也是要写磁盘的。
具体咋实现的???
主备同步的过程:
redo log ----> binlog ----> relay log
4. 为什么用B+树而不是二叉树、B树:
二叉树:减少读磁盘的次数
B树:B+树只在叶子节点上存储元素,B+树中存储在叶子节点上的数据都是按顺序防止的【双向链表】,更适合做范围查找;而B树上的所有节点都可以存储元素,当进行范围查找时,会涉及到回溯,增加了实现的复杂度。