这三者本质上都是由于在一次物理数据清理周期内做了大量的插入或删除所导致的慢查询问题。
这种问题常常出现在对某张表频繁做更新或者插入/删除的情形。即使是该表有索引且最开始走索引,但是随着时间的推移其会变为全表扫描,每次对该表的dml会越来越慢。
一、问题描述
-
Oracle的墓碑问题:Oracle的墓碑问题(Tombstone Problem)是指当数据库中存在大量已删除的数据行时,这些已删除的数据行会对性能和存储空间造成不利影响的情况。
-
Oracle的高水位问题:该问题则是指表或分区中存在大量已被删除或更新的数据,但由于未执行适当的表空间或段收缩操作,导致表或分区的物理存储空间没有被释放,高水位(High Water Mark)未得到调整。
-
OceanBase的Queuing表问题:和Oracle的“墓碑问题”一致。
区别与联系:
高水位问题实际上是包含墓碑问题的,墓碑问题时常会导致高水位的出现。因为墓碑问题是针对
删除
,而高水为则是更新、插入且删除
。
二、相关场景
用户触发短信流程时,短信发送后需要将该工单移动至暂存表,待短信网关返回结果后才会将暂存表中工单移入succ或fail表。
暂存表就涉及到大量的插入删除场景。随着时间的推移,该表就有可能发生慢查询问题,从而导致该表的数据积压。
三、原理解释
2.1.Oracle删除原理
通常我们说的Oracle数据库其实是数据文件和实例的集合。数据库是一组存储数据的文件,而数据库实例则是管理数据库文件的内存结构
Oracle的dml操作都是内存中的操作,针对的是oracle的实例
,更是实例中相当重要的的SGA区域。
数据行的底层结构中,Oracle使用一个特殊的标记位来表示该数据行是否被删除。这个标记位通常称为 "Delete Bit" 或 "Delete Flag"。
当执行delete语句时,Oracle优化器将执行计划、绑定变量、表等变量存入共享池(Shared Pool中),或者执行现有的执行计划,数据则在数据缓冲区(DataBase Buffer Cache)中,有索引先走索引,没有则扫描表,当缓存未命中时则会读入数据文件内的数据进入缓冲区。此时,Oracle会将要被删除数据的相应的标记位设置为特定的值,表示该数据行是一个已删除的行,并将操作写入undo日志。
当提交后,被删除部分的数据和undo日志会被写回磁盘同时更新索引。
注意,此时被删除的数据还是存在的,任然占据物理空间
,称为高水位(High Water Mark)。 但是真正被使用到的数据可能会随着之后的删除而减少。不论是走索引还是全表扫描,Oracle在执行语句时触发的IO操作的基本单位是block(8KB),如果真正使用的数据比较分散,则一次IO所要读取的block块则会相当多,从而导致慢查询。
2.2.OceanBase删除原理
OceanBase(即ob)则有些不同,针对单个节点而言,ob将数据划分为内存增量数据(MemTable)
和存储静态数据(SSTable)
。
其中 SSTable 是只读的,一旦生成就不再被修改,存储于磁盘;MemTable支持读写,存储于内存。数据库 DML 操作插入、更新、删除等首先写入 MemTable,等到 MemTable 达到一定大小时转储到磁盘成为SSTable。
对MemTable中指定数据作删除时,也是会对其打上删除标记,但是即使提交也不会立即
写入SSTable,当MemTable的大小达到某个阈值后,ob数据库会将MemTable冻结,然后将其中的数据转存于磁盘上。这并不意味着被删除的数据在此刻会剔除,其依然会存在,真正对删除磁盘上的无用数据是在合并
中,一般期望在一天只做一次合并操作,并且控制在业务低峰期进行,因此有时也会把合并称之为每日合并。
而ob的读取采用基线加增量的设计,一部分数据在基线(即sstable),一部分在增量,原理上每次查询都是既要读基线,也要读增量。最后返回两者归并后的结果。
因此,对于频繁删除或更新(ob更新的本质也是delete+insert)所导致的慢查询尤为明显。甚至于查询所需数据只有数十条,但是实际需要扫描的行数量可能远大于这个量级(可能是几百到上千万)。
四、解决措施
针对Oracle而言,及时的执行表空间或段收缩操作,乃至重建表分区都可以有效的解决。
而ob而言,Queuing表最快、最有效的手段还是通过索引来解决。如果存在可用索引,可以手动绑定指定划,如果没则针对有效字段创建索引后再绑定。最后的解决才是手动触发合并,将删除或更新的数据版本进行清理,降低全表扫描的数据量,提升速度。因为每次合并都很慢,所以需要充分的评估该表在合并期间是否必须要使用到。