redis string 原理-Redis 字符串底层原理
Redis 是个挺 funny 的东西,它不像你写代码写进文档那么严肃,更像是一团在深夜里疯狂烧水的小丑。 核心原理就两个词:内存 + 链表。 想象你在开一瓶汽水,汽水罐在内存区躺着。你刚买的时候,罐子就在你手里,随时能拿出来喝。
这就是 string 最原初的状态,数据直接塞进内存,读写速度飞起,根本不用管磁盘。
这种机制让 Redis 能秒开单页,秒删笔记,用户体验上就是那种“我点一下,立马就有”的清爽感,彻底没有网络延迟的卡顿。 但这是个双刃剑。一旦你往罐子里倒水,水越多,罐子就越大。
要是系统里堆满了成千上万瓶汽水,内存直接爆炸,电脑直接融化。
这就是为啥 Redis 务必把数据换个地方住——持久化。为了让那瓶汽水一辈子在你手边,它会被分成几段,塞进磁盘的硬盘里。
这就有了两个文件:`.rdb` 是主文件,记着大约多少水;`.key` 是辅文件,记着每段水具体存有哪。 这就引出了怪诞的“双索引”机制。 数据被切成段,每段都有一个 ID。
这个 ID 是个字符串,省得你一个个记数字。
比如 A(0) 是第 0 段,B(1) 是第 1 段。 但难题来了。
要是一段没断,也不准重新分配空间,它就得一直用。
这就卡住了。Redis 有个挺智慧的算法,叫“双索引”。 当某一段满了,比如 A 段到了容量上限,它不能死守着 A(0)。它会去隔壁 B(1) 找。B 段还有空间吗?要是有,它把 A 段从 A(0) 搬进 B(1),并更新 B 段的 ID 从 1 变成 2。
然后 A 段的 ID 就变成 A(2) 了。 这听起来挺复杂,但效果挺好。你只需求记住新的 ID 就行,不需求管它具体是第几段。 举个例子: 1.你创建 key `test`,存了 100 个值。 2.假设数据刚好填满 A(0),ID 变成 A(1)。 3.下一个 key `test2` 进来,它需求找一段没满的。 4.它翻到 B(2),发现 B 段是空的。 5.便 `test2` 的数据塞进 B(2),ID 变成 B(3)。 6.目前你的数据库里,`test` 占用了 A 段,`test2` 占用了 B 段。 这种架构保证了红雀鸟一辈子不会饿死,出于总有新数据进来填补旧的空闲段。 带压缩的字符串(zset)更是把这套逻辑玩到了极致。 这玩意儿是为了解决“重复数据”的难题。假设某个人在两个地方发了广告,Redis 默认只存一份。
这是对的,出于数据是幂等的(发了两次确实等于没发)。 但要是你要把这个广告变成“带工夫戳的过期工夫”,比如 `{key: 'ads', value: 'buy now', t: 1234567890}`,情况就复杂了。内存里存 `value` 没难题,但 `t` 呢?多个实例可能会在同一个工夫戳存入新数据。 要是只存一个,你就不知道哪个工夫戳的才是最新的。
这就是 zset 的由来。 zset 把字符串切分了。每个段里不仅存数据,还存一个“版本号”。版本号递增,确保数据是有序的。 想象你在刷脸支付,系统问你:“这张脸是第几次用的?” 你回答:“第 1 次。” 系统检查:内存里 `key: face1` 的 ID 是 `1`。 系统检查:内存里 `key: face2` 的 ID 是 `1`。 系统发现 ID 一样,这是同一次,还是重复? 这时候就需求版本号了。 内存里 `key: face1` 的版本号是 `1`。 内存里 `key: face2` 的版本号是 `2`。 系统比较版本号。 `2 > 1`,说明 `face2` 是新的,这次支付才有效。 别看原理是字符串切分,但 zset 内部实际上把保存的字符串按工夫戳切分了存那一堆小文件里。 还有一种挺常见的操作,叫“移动”。 比如你有两个 key,`user_a` 和 `user_b`,它们分别存了 `100` 和 `200` 个槽。 你想把 `user_b` 的数据挪到 `user_a` 上去。 Redis 会先计算 `user_b` 占用了多少个槽。假设占了 3 个。 然后它会把 `user_b` 里的数据,一个个从 `user_b` 的 ID 位置,挪到 `user_a` 的 ID 位置,并更新 ID 版本号。 这就像你搬家,把床从睡觉那屋搬到了客厅。床原来的坐标变了,新客厅的床上也有床了。 底层别看还是链表,但在内存稀疏的时候,这种移动操作实际上挺快的。出于它不需求在磁盘上跳。 最终还得提提持久化。 文本字符串肯定怕断电。一旦服务器关机断电,内存里的汽水罐子就空了,数据全没了。 Redis 解决这个的办法,是那种挺老派的“交替复制”。 它会把内存里的数据,分一半写回磁盘 `.rdb`。另一半在到一半的时候,先偷偷写进磁盘 `.key`。 持续写满,然后再把刚刚写进 `key` 的那一半,再写回 `.rdb` 的对应位置。 这就像你在改稿子。你写到一半,先把草稿纸背面抄一遍(备份到 key),然后持续写正文(写回 rdb)。 等正文写完了,再把草稿纸背面的内容,写到正文的位置去(更新 rdb)。 这样就算杀了进程,重启后,rdb 是最新的,key 里也没丢数据。 别看这个过程要等一半数据写盘一半写内存,耗时比直接写内存略微慢一点点,但为了数据保险,这点代价是不算贵的。 总结来说,Redis 的字符串操作,就是靠内存里那堆好办的字符串 ID,配合磁盘里那些备用副本,把数据在内存和磁盘之间像磁悬浮列车一样瞬间挪。它不追求绝对的极致,而是在性能和一致性之间找了一个挺现实的平衡点。对于绝大多数应用,这种设计是充足快、充足稳的了。
声明:演示网站所有内容,若无特殊说明或标注,均来源于网络转载,仅供学习交流使用,禁止商用。若本站侵犯了你的权益,可联系本站删除。
