Redis 存储压缩实战

背景简述

xx的模型分计算,由于客户对响应要求比较高,要求在20ms内就需要返回, 同时QPS要求为60W .

所以存储这一块的选则就是使用的Redis的集群(无主从,更多资源用来存储,同时高可用要求不高)

一开始使用最直接使用了String的Key-Value入库 ; 结果,才入了八亿的数据,内存就去到了140g .对单点分析之后得出对象数量3000w, 数据内存消耗:2.72g,大约一条记录数据大小是97字节,但实际节点占用内存:5.13g . 运维给出来的答复是 :


Image.png
数据格式

数据量: 50亿+
数据格式: MD5(imei) :Score1(double),Score2(double)
单条纯数据量: 32字节

相关知识点

Redis 的ZipList

首先ZipList是作为Redis的一种底层数据结构, 在一定条件下可作用于HashTable,List,Set的底层实现,
由于其设定是使用一整块的连续内存 (可看似数组结构) ,减少了内存碎片的产生,同时在提供了较高的效率 .

结构
Redis ZipList结构.png
  • zlbytes: 4字节,表示ziplist占用的字节总数(也包括本身占用的4个字节)。
  • zltail: 4字节,表示ziplist表中最后一项(entry)在ziplist中的偏移字节数。
  • zllen: 2个字节, 表示ziplist中数据项(entry)的个数。zllen字段因为只有16bit,
  • entry: 表示真正存放数据的数据项,长度不定。一个数据项(entry)也有它自己的内部结构,这个稍后再解释。
  • zlend : ziplist最后1个字节,是一个结束标记,值固定等于255。
注意事项

由于ZipList的初始是申请一个连续的长度为zlbytes的内存 , 所以正常情况下对ZipList的修改都会触发内存的重分配, 同时有可能发生数据的复制开销比较大.
这也是ZipList不适合用于存放过大量数据.

以下是HashTable底层使用ZipList的相关配置:

hash-max-ziplist-entries 512   //当entry数量小于这个数量就会使用ZipList作为存储,大于他则会转化为dict存储
hash-max-ziplist-value 64   //ziplist entry的大小阈值

实践

要使用ZipList作为存储结构,所以使用HashTable的时候要将entry的数量控制在ZipList的hash-max-ziplist-entries阈值内 (极光这里配置的是256 先不做修改,按这个条件来)

计算Redis Key数量

预估Key数量范围

根据上面的需要将entry数量控制再256以内 , 所以按照50亿的总数量计算的话那么, Redis Key 的KeyNum应该是:

image.png

这里的KeyNum根据实际情况可以预估大一点以备数据量增加

确定Redis Key的数量

通过上一步我们确定了KeyNum的范围 ,那么这一步就要考虑如何将要做的就是找到一个确定的数量使得我们50亿的数据可以均匀的分布再这 KeyNum 个HashTable里

如下图所示,我们的Key是32位的MD5(imei), 为了达到更好的内存合理使用,所以这里考虑将这32位的十六进制拆分成两部分, 一部分作为Redis的Key,一部分作为HashTable的field.

MD5的初始画图.png

如果 使用六位的 十六进制作为Redis Key那么他的KeyNum为:
2^24 = 16,777,216
同理 七位 :
2^28 = 268,435,456
同理 八位 :
2^32 = 4,294,967,296

与上面 计算出来的 19,531,250比较, 使用七位的十六进制作为Key 的话,那么理论上他的每个HashTable的Entries数量就是大概20个左右. 所以直接选择了7位的十六进制

这里六位的十六进制也可以选择的,只需要将上述的hash-max-ziplist-entries 配置的稍微大一点,但是带来的就是hget的时候会比七位十六进制的慢. 由于没有做过hget差异实验 ,所以就保险的先选择7位.

确定Redis Key

如下图所示,进行拆分,由于七位的十六进制 只有3.5个字节,所以这里需要补一个十六进制的 '0' 凑整4个字节.

MD5的初始画图 (1).png
代码实现如下:
        m1_score = struct.pack('d', round(float(_['m1_score']),8))
        m2_score = struct.pack('d', round(float(_['m2_score']),8))
        
        pip.hset(bytes((_['imei'][25:32] + '0').decode('hex')), bytes((_['imei'][:25] + 
'0').decode('hex')),bytes(m1_score + m2_score))


为了使切分七位十六进制后的数据更均匀分布,应该对选择的目标七位十六进制进行groupBy,然后看看,数据分布的是不是足够的均匀. 假设,如果有大量MD5(imei) 的前七位都是零,那么就会造成 key为00000000的这个HashTable 过于庞大,而没法使用ZipList . 经过对玖富的数据进行分析,最终选取后七位

思考

1.可以通过减少RedisKey的数量, 达到增加每个HashTable的Entry的数量,达到内存缩减

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355