-
数据库是文件的集合,我们说的“数据库”指的是数据库实例。也就是操控文件的程序
MySQL体系结构
来跟我一起默记一边:Connection Pool, Utilities, SQL interface, SQL parser, Optimizer, Caches, Pluggable engine, File System
- 注意,存储引擎是基于表的(我也知道是基于表的呀…数据库是在上面一层了)
InnoDB特点
- 支持事务,行锁,外键。
- 并且支持类似Oracle的不加锁读
- 存储引擎的工作原理是:将数据放入一个逻辑的表空间中,然后对这个表进行操作。
- InnoDB通过多版本并发控制(MVVC)来实现高并发性(慢慢看,现在还看不懂)!!!
- 实现了标准的四种隔离级别(为什么是四种隔离级别),默认为Repeatable级别。
- 使用一种被称为next-key locking的策略来避免幻读
- 还提供了 插入缓冲,二次写,自适应哈希索引,预读 等高性能功能
(插一嘴讲一下四种隔离级别)
隔离级别从低到高:
- RU未提交读(read uncommited):即使事务没有commit,其他事务仍可以读到未提交的数据。脏读、不可重复读、幻读都有可能发生
- RC已提交读(read commited):事务只能读到已经commit的数据。不可重复读和幻读可能发生。Oracle的默认级别
- RR可重复读(repeatable read):一个事务的多次read不会受到其他事务的影响。幻读可能发生。MySQL的默认级别
- 可串行化(serializable):所有读写操作完全串行。完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
OK,那我们来聊聊这几种读出现错误的情况
-
脏读:一个事务正在访问数据,并且对数据进行了修改,但是这个修改还没有写回数据库。此时,另外一个事务也在访问这个数据,然后使用了这个数据。
-
不可重复读: 一个事务多次读取同一数据。而另一个同时在访问这个数据的事务在中间改变了数据的值,导致第一个事务两次读取的数据不一样
-
幻读: 第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好像发上了幻觉似的(欸我不是修改了所有行吗???为什么这里有一行没改???喵喵喵???)
users: id 主键
1、T1:select * from users where id = 1;
2、T2:insert into
users
(id
,name
) values (1, ‘big cat’);3、T1:insert into
users
(id
,name
) values (1, ‘big cat’);T1 :主事务,检测表中是否有 id 为 1 的记录,没有则插入,这是我们期望的正常业务逻辑。
T2 :干扰事务,目的在于扰乱 T1 的正常的事务执行。
在 RR 隔离级别下,1、2 是会正常执行的,3 则会报错主键冲突,对于 T1 的业务来说是执行失败的,这里 T1 就是发生了幻读,因为T1读取的数据状态并不能支持他的下一步的业务,见鬼了一样。
InnoDB
InnoDB体系结构
-
后台线程:负责刷新内存池中的数据,保证缓冲池中的内存缓存是最近的数据。此外负责将已修改的数据写入到磁盘,还保证数据库发生异常的时候InnoDB能恢复到正常运行状态
- 很容易就看出来,InnoDB是多线程的模型,后台线程可以分为下面几类:
- master thread:核心线程,负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性。
- IO thread:处理IO请求的回调
- Purge thread:回收已经使用并分配的undo页
- Page cleaner thread
- 内存:
- 缓存池:InnoDB是基于磁盘存储的,如果死吃磁盘的话..就凉了,CPU速度和磁盘速度压根不是一个数量级,性能会差到令人发指。所以缓存池肯定是必须的,其实缓存池类似于Cache对于memory的作用。缓存池换入换出的单位是页(数据库的页)
对于页的修改操作,肯定是先修改缓存池中的数据。然后另外一边会有线程异步的以一定的频率刷新到磁盘上。但是需要注意,不是一改完就触发写回磁盘,而是通过Checkpoint机制。(system设计基本原则之二:接口双方处理速度差距很大的时候,可以引入一个缓存机制,这种情况屡见不鲜:CPU和内存用Cache,CPU和Cache还用register…)。缓存中缓存的数据页类型
- 索引页
- 数据页
- undo页
- 插入缓冲页(insert buffer)
- 自适应哈希索引
- InnoDB存储的锁信息
- 数据字典信息(data dictionary)
- 还是看图吧….、
(现在这个图里的东西我基本只知道数据页和锁信息..)
- LRU list、 Free List、 Flush List
- 一般来说数据库缓存池通过LRU算法来进行管理:最近使用的页放在LRU列表的前端,最早使用的页放在LRU列表末端。当不能存放新读取到的页时,将首先释放LRU列表尾部的页。
- 缓存池的页大小默认是16KB
- InnoDB的LRU做了一些改动:新读进来页,不会放到LRU首部,二是放到LRU列表的midpoint(自定义位置,默认是LRU长度的5/8处)(不采用朴素LRU,因为有些操作(如索引和数据扫描)会访问表中很多页,这样的话会把LRU中经常用的页都挤出来,特别影响效率)
- 在LRU列表中的页被修改以后,这个页就叫做“脏页”(dirty page):这个脏字很好理解,因为缓存的和磁盘的不一样。此时会通过CheckPoint机制将脏页刷回磁盘
- Flush List:存储脏页的列表,管理将页刷新回磁盘
- 缓存池:InnoDB是基于磁盘存储的,如果死吃磁盘的话..就凉了,CPU速度和磁盘速度压根不是一个数量级,性能会差到令人发指。所以缓存池肯定是必须的,其实缓存池类似于Cache对于memory的作用。缓存池换入换出的单位是页(数据库的页)
对于页的修改操作,肯定是先修改缓存池中的数据。然后另外一边会有线程异步的以一定的频率刷新到磁盘上。但是需要注意,不是一改完就触发写回磁盘,而是通过Checkpoint机制。(system设计基本原则之二:接口双方处理速度差距很大的时候,可以引入一个缓存机制,这种情况屡见不鲜:CPU和内存用Cache,CPU和Cache还用register…)。缓存中缓存的数据页类型
- CheckPoint:不能一有脏页就写回磁盘。如果脏页写入磁盘时候宕机了,那么数据就不能恢复了。所以为了保证ACID的D,数据库系统一般采用Write Ahead log的策略,即先写入重做日志,再将页刷入磁盘。(每个checkpoint做的事,无非就是将缓存池中的脏页刷回磁盘(先写重做日志))
- 解决以下问题
- 缩短数据的恢复时间
- 缓存池不够用的时候,将脏页刷新到磁盘
- 重做日志不可用的时候,刷新脏页
- checkpoint分类
- Sharp Checkpoint:发生在数据库关闭时。所有的脏页都写回磁盘
- Fuzzy Checkpoint:刷新一部分的脏页。大部分情况都是Fuzzy
- 当数据库宕机的时候,数据库不需要重做所有的日志,只需要将Checkpoint之后的日志进行重做。(因此大大缩短了恢复时间)