数据库压缩到底怎么做?

运维 数据库运维
redis的压缩是针对key的压缩,只针对string和list的value,需要配置文件配置rdb_compression rdb压缩才会生效。

redis

redis的压缩是针对key的压缩

只针对string和list的value

所有的压缩最终都会调用lzf_compress/lzf_decompress

需要配置文件配置rdb_compression rdb压缩才会生效

lzf压缩限制长度要大于20,即使是aaaaaaaaaaaaaaaaaaaa也压不了,大于20才能压。原因没有深究

rdb内部的压缩

  • 如何确认这个record是被压缩/解压的?

rdb解析每条数据,都有标识字段,压缩的record自然是单独的类型

  1. ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) { 
  2. ... 
  3.     comprlen = lzf_compress(s, len, out, outlen); 
  4.     if (comprlen == 0) { 
  5.         zfree(out); 
  6.         return 0
  7.     } 
  8.     ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len); 
  9. ... 
  10.  
  11. ssize_t rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len, 
  12.                        size_t original_len) { 
  13. ... 
  14.     /* Data compressed! Let's save it on disk */ 
  15.     byte = (RDB_ENCVAL<<6)|RDB_ENC_LZF; 
  16.     if ((n = rdbWriteRaw(rdb,&byte,1)) == -1goto writeerr; 
  17.     nwritten += n; 
  18. ... 

解压缩

  1. void *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr) { 
  2. ... 
  3.     if (isencoded) { 
  4.         switch(len) { 
  5.         case RDB_ENC_INT8: 
  6.         case RDB_ENC_INT16: 
  7.         case RDB_ENC_INT32: 
  8.             return rdbLoadIntegerObject(rdb,len,flags,lenptr); 
  9.         case RDB_ENC_LZF: 
  10.             return rdbLoadLzfStringObject(rdb,flags,lenptr); 
  11.         default
  12.             rdbReportCorruptRDB("Unknown RDB string encoding type %llu",len); 
  13.             return NULL; 
  14.         } 
  15.     } 
  16.  ... 
  17.   
  18.  void *rdbLoadLzfStringObject(rio *rdb, int flags, size_t *lenptr) { 
  19. ... 
  20.  
  21.     /* Load the compressed representation and uncompress it to target. */ 
  22.     if (rioRead(rdb,c,clen) == 0goto err; 
  23.     if (lzf_decompress(c,clen,val,len) != len) { 
  24.         rdbReportCorruptRDB("Invalid LZF compressed string"); 
  25. ... 

接口简单容易定位

所有的类型string/hash具体到底层,都是string,就会走这个压缩的过程rdbSaveRawString,内部来调用rdbSaveLzfStringObject

  1. ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key, int dbid) { 
  2.     ssize_t n = 0, nwritten = 0
  3.  
  4.     if (o->type == OBJ_STRING) { 
  5.         /* Save a string value */ 
  6.         if ((n = rdbSaveStringObject(rdb,o)) == -1return -1
  7.         nwritten += n; 
  8.     } else if (o->type == OBJ_LIST) { 
  9.  
  10.                 if (quicklistNodeIsCompressed(node)) { 
  11.                     void *data; 
  12.                     size_t compress_len = quicklistGetLzf(node, &data); 
  13.                     if ((n = rdbSaveLzfBlob(rdb,data,compress_len,node->sz)) == -1return -1
  14.                     nwritten += n; 
  15.                 } else { 
  16.                     if ((n = rdbSaveRawString(rdb,node->zl,node->sz)) == -1return -1
  17.                     nwritten += n; 
  18.                 } 
  19.                 node = node->next; 
  20.             } 
  21.         } else { 
  22.             serverPanic("Unknown list encoding"); 
  23.         } 
  24. 。。。 

quicklist的压缩

链表压缩可以选择深度,quicklist是redis list的底层数据结构

什么时候做压缩?

  1. /* Insert 'new_node' after 'old_node' if 'after' is 1. 
  2.  * Insert 'new_node' before 'old_node' if 'after' is 0. 
  3.  * Note: 'new_node' is *always* uncompressed, so if we assign it to 
  4.  *       head or tail, we do not need to uncompress it. */ 
  5. REDIS_STATIC void __quicklistInsertNode(quicklist *quicklist, 
  6.                                         quicklistNode *old_node, 
  7.                                         quicklistNode *new_node, int after) { 
  8.     if (after) { 
  9.         new_node->prev = old_node; 
  10.         if (old_node) { 
  11.             new_node->next = old_node->next; 
  12.             if (old_node->next) 
  13.                 old_node->next->prev = new_node; 
  14.             old_node->next = new_node; 
  15.         } 
  16.         if (quicklist->tail == old_node) 
  17.             quicklist->tail = new_node; 
  18.     } else { 
  19.         new_node->next = old_node; 
  20.         if (old_node) { 
  21.             new_node->prev = old_node->prev; 
  22.             if (old_node->prev) 
  23.                 old_node->prev->next = new_node; 
  24.             old_node->prev = new_node; 
  25.         } 
  26.         if (quicklist->head == old_node) 
  27.             quicklist->head = new_node; 
  28.     } 
  29.     /* If this insert creates the only element so far, initialize head/tail. */ 
  30.     if (quicklist->len == 0) { 
  31.         quicklist->head = quicklist->tail = new_node; 
  32.     } 
  33.  
  34.     /* Update len first, so in __quicklistCompress we know exactly len */ 
  35.     quicklist->len++; 
  36.  
  37.     if (old_node) 
  38.         quicklistCompress(quicklist, old_node); 

也就是说,头尾不会压缩,其他的节点会压缩,在修改的时候同事把旧的节点给压缩了

这里有个问题,这里的节点压缩了,rdb存储的时候还要特别处理一下,判定已经压缩过,走rdbSaveLzfBlob

需要有个record头来记录一个compression的标记

rocksdb

类似redis,还是很好找的,UncompressData/CompressData

针对sst的压缩

调用关系

UncompressBlockContentsForCompressionType -> UncompressData

WriteBlock/BGWorkCompression -> CompressAndVerifyBlock -> CompressBlock -> CompressData

block本身有信息标记是否是压缩

写入的时候才压缩

blobdb

CompressBlobIfNeeded -> CompressData

GetCompressedSlice -> CompressData

总结

  • 需要文件本身知道自己是压缩的,有元信息记录
  • 在内存中是否压缩要考虑业务场景,比如redis这个quicklist 压缩,因为list最近访问的就是头尾,其他不重要

 

责任编辑:张燕妮 来源: 王很水的博客
相关推荐

2019-07-02 10:22:15

TCP流量数据

2023-09-27 22:44:18

数据迁移数据库

2024-10-10 14:34:49

2024-05-31 11:37:20

2022-07-06 11:30:57

数据分析预测模型

2018-08-26 15:39:03

数据库MySQL索引

2011-06-30 16:57:03

数据压缩

2022-12-26 11:57:41

数据库治理

2021-11-02 14:46:50

数据

2016-06-13 13:50:26

云计算

2011-03-28 09:27:52

数据库压缩日志

2020-07-28 08:36:54

数据安全数据泄露数据

2023-06-02 11:55:02

数据分析营销

2017-11-08 12:25:37

小程序运营公众号

2017-06-12 18:24:25

数据库压缩技术

2022-03-10 11:25:51

InnoDB优化

2020-02-03 09:36:47

数据库数据库迁移

2016-01-05 16:17:59

云梦数据仓

2018-02-07 09:00:09

2021-11-10 05:00:58

数据分析运营
点赞
收藏

51CTO技术栈公众号