|
|
|
|
公众号矩阵

数据库压缩到底怎么做?

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

作者:佚名来源:王很水的博客|2021-10-12 10:22

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最近访问的就是头尾,其他不重要

【编辑推荐】

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区
  2. 管理之道:打造一支技术的特种部队
  3. TP(触控)技术 SENSOR设计基础视频课程
  4. Gartner:33%的技术提供商在两年内对AI的投资将达到100万美元以上
  5. 计算机架构的机器学习
  6. 亿级流量架构的服务限流思路与方法
【责任编辑:张燕妮 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微