为了监控预读的工作状况,现代数据库引入了一个度量叫缓冲/缓存命中率,用来显示请求的数据在缓存中找到而不是从磁盘读取的频率 。
注:糟糕的缓存命中率不总是意味着缓存工作状态不佳 。更多信息请阅读Oracle文档 。
缓冲只是容量有限的内存空间,因此,为了加载新的数据,它需要移除一些数据 。加载和清除缓存需要一些磁盘和网络I/O的成本 。如果你有个经常执行的查询,那么每次都把查询结果加载然后清除,效率就太低了 。现代数据库用缓冲区置换策略来解决这个问题 。
缓冲区置换策略
多数现代数据库(至少 SQL Server, MySQL, Oracle 和 DB2)使用 LRU 算法 。
LRU
LRU代表最近最少使用(Least Recently Used)算法,背后的原理是:在缓存里保留的数据是最近使用的,所以更有可能再次使用 。
图解:
文章插图
为了更好的理解,我假设缓冲区里的数据没有被闩锁锁住(就是说是可以被移除的) 。在这个简单的例子里,缓冲区可以保存 3 个元素:
- 1:缓存管理器(简称CM)使用数据1,把它放入空的缓冲区
- 2:CM使用数据4,把它放入半载的缓冲区
- 3:CM使用数据3,把它放入半载的缓冲区
- 4:CM使用数据9,缓冲区满了,所以数据1被清除,因为它是最后一个最近使用的,数据9加入到缓冲区
- 5:CM使用数据4,数据4已经在缓冲区了,所以它再次成为第一个最近使用的 。
- 6:CM使用数据1,缓冲区满了,所以数据9被清除,因为它是最后一个最近使用的,数据1加入到缓冲区
- ……
改进
为了防止这个现象,有些数据库增加了特殊的规则,比如Oracle文档中的描述:
『对非常大的表来说,数据库通常使用直接路径来读取,即直接加载区块[……],来避免填满缓冲区 。对于中等大小的表,数据库可以使用直接读取或缓存读取 。如果选择缓存读取,数据库把区块置于LRU的尾部,防止清空当前缓冲区 。』还有一些可能,比如使用高级版本的LRU,叫做 LRU-K 。例如,SQL Server 使用 LRU-2 。
这个算法的原理是把更多的历史记录考虑进来 。简单LRU(也就是 LRU-1),只考虑最后一次使用的数据 。LRU-K呢:
- 考虑数据最后第K次使用的情况
- 数据使用的次数加进了权重
- 一批新数据加载进入缓存,旧的但是经常使用的数据不会被清除(因为权重更高)
- 但是这个算法不会保留缓存中不再使用的数据
- 所以数据如果不再使用,权重值随着时间推移而降低
关于LRU-K更深入的知识,可以阅读早期的研究论文(1993):数据库磁盘缓冲的LRU-K页面置换算法
其他算法
当然还有其他管理缓存的算法,比如:
- 2Q(类LRU-K算法)
- CLOCK(类LRU-K算法)
- MRU(最新使用的算法,用LRU同样的逻辑但不同的规则)
- LRFU(Least Recently and Frequently Used,最近最少使用最近最不常用)
- ……
我只探讨了读缓存 —— 在使用之前预先加载数据 。用来保存数据、成批刷入磁盘,而不是逐条写入数据从而造成很多单次磁盘访问 。
要记住,缓冲区保存的是页(最小的数据单位)而不是行(逻辑上/人类习惯的观察数据的方式) 。缓冲池内的页如果被修改了但还没有写入磁盘,就是脏页 。有很多算法来决定写入脏页的最佳时机,但这个问题与事务的概念高度关联,下面我们就谈谈事务 。
事务管理器最后但同样重要的,是事务管理器,我们将看到这个进程是如何保证每个查询在自己的事务内执行的 。但开始之前,我们需要理解ACID事务的概念 。
“I’m on acid”
一个ACID事务是一个工作单元,它要保证4个属性:
- 原子性(Atomicity): 事务『要么全部完成,要么全部取消』,即使它持续运行10个小时 。如果事务崩溃,状态回到事务之前(事务回滚) 。
- 隔离性(Isolation): 如果2个事务 A 和 B 同时运行,事务 A 和 B 最终的结果是相同的,不管 A 是结束于 B 之前/之后/运行期间 。
推荐阅读
- 如何挑选黑豆
- 不是夫妻合租房犯罪吗
- 医保断缴三个月余额清零?员工可自愿放弃社保?这些谣言别再信了
- 手串颗数大有讲究,千万别戴错了
- “禁止长时间停车”,到底指的是几分钟?交警:最后再说一遍
- 太热了!别再披头散发了,这4款发型够美够清凉
- 没钱还信用卡有什么解决办法
- 信用卡过期卡怎么处理
- 信用卡逾期被注销怎么还钱
- 如何找回抖音私聊记录