作者:雷鹏,Terark核心技术发明人。曾就职奇虎360,负责搜索引擎核心研发;曾就职Yahoo!北研所负责搜索广告、广告交易(AdExchange)等项目。在数据库、高性能计算、分布式、系统架构上都深有造诣。
责编:仲培艺(zhongpy@youkuaiyun.com)
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅《程序员》
作为数据库,在系统资源(CPU、内存、SSD、磁盘等)一定的前提下,我们希望:
- 存储的数据更多:采用压缩,这个世界上有各种各样的压缩算法;
- 访问的速度更快:更快的压缩(写)/解压(读)算法、更大的缓存。
几乎所有压缩算法都严重依赖上下文:
- 位置相邻的数据,一般情况下相关性更高,内在冗余度更大;
- 上下文越大,压缩率的上限越大(有极限值)。
块压缩
传统数据库中的块压缩技术
对于普通的以数据块/文件为单位的压缩,传统的(流式)数据压缩算法工作得不错,时间长了,大家也都习惯了这种数据压缩的模式。基于这种模式的数据压缩算法层出不穷,不断有新的算法实现。包括使用最广泛的gzip、bzip2、Google的Snappy、新秀Zstd等。
- gzip几乎在在所有平台上都有支持,并且也已经成为一个行业标准,压缩率、压缩速度、解压速度都比较均衡;
- bzip2是基于BWT变换的一种压缩,本质是上对输入分块,每个块单独压缩,优点是压缩率很高,但压缩和解压速度都比较慢;
- Snappy是Google出品,优点是压缩和解压都很快,缺点是压缩率比较低,适用于对压缩率要求不高的实时压缩场景;
- LZ4是Snappy一个强有力的竞争对手,速度比Snappy更快,特别是解压速度;
- Zstd是一个压缩新秀,压缩率比LZ4和Snappy都高不少,压缩和解压速度略低;相比gzip,压缩率不相上下,但压缩/解压速度要高很多。
对于数据库,在计算机世界的太古代,为I/O优化的Btree一直是不可撼动的,为磁盘优化的Btree block/page size比较大,正好让传统数据压缩算法能得到较大的上下文,于是,基于block/page的压缩也就自然而然地应用到了各种数据库中。在这个蛮荒时代,内存的性能、容量与磁盘的性能、容量泾渭分明,各种应用对性能的需求也比较小,大家都相安无事。
现在,我们有了SSD、PCIe SSD、3D XPoint等,内存也越来越大,块压缩的缺点也日益突出:
- 块选小了,压缩率不够;块选大了,性能没法忍;
- 更致命的是,块压缩节省的只是更大更便宜的磁盘、SSD;
- 更贵更小的内存不但没有节省,反而更浪费了(双缓存问题)。
于是,对于很多实时性要求较高的应用,只能关闭压缩。
块压缩的原理
使用通用压缩技术(Snappy、LZ4、zip、bzip2、Zstd等),按块/页(block/page)进行压缩(块尺寸通常是4KB~32KB,以压缩率著称的TokuDB块尺寸是2MB~4MB),这个块是逻辑块,而不是内存分页、块设备概念中的那种物理块。
启用压缩时,随之而来的是访问速度下降,这是因为:
- 写入时,很多条记录被打包在一起压缩成一个个的块,增大块尺寸,压缩算法可以获得更大的上下文,从而提高压缩率;相反地,减小块尺寸,会降低压缩率。
- 读取时,即便是读取很短的数据,也需要先把整个块解压,再去读取解压后的数据。这样,块尺寸越大,同一个块内包含的记录数目越多。为读取一条数据,所做的不必要解压就也就越多,性能也就越差。相反地,块尺寸越小,性能也就越好。
一旦启用压缩,为了缓解以上问题,传统数据库一般都需要比较大的专用缓存,用来缓存解压后的数据,这样可以大幅提高热数据的访问性能,但又引起了双缓存的空间占用问题:一是操作系统缓存中的压缩数据;二是专用缓存(例如RocksDB中的DBCache)中解压后的数据。还有一个同样很严重的问题:专用缓存终归是缓存,当缓存未命中时,仍需要解压整个块,这就是慢Query问题的一个主要来源(慢Query的另一个主要来源是在操作系统缓存未命中时)。
这些都导致现有传统数据库在访问速度和空间占用上是一个此消彼长、无法彻