<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Redis on Lishengxie</title><link>https://lishengxie.top/tags/redis/</link><description>Recent content in Redis on Lishengxie</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 03 Mar 2025 22:44:38 +0800</lastBuildDate><atom:link href="https://lishengxie.top/tags/redis/index.xml" rel="self" type="application/rss+xml"/><item><title>Redis ziplist、quicklist 和 listpack</title><link>https://lishengxie.top/post/redis-listpack-ziplist/</link><pubDate>Mon, 03 Mar 2025 22:44:38 +0800</pubDate><guid>https://lishengxie.top/post/redis-listpack-ziplist/</guid><description>&lt;h2 id="ziplist"&gt;ziplist&lt;/h2&gt;&#10;&lt;h3 id="为什么需要-ziplist"&gt;为什么需要 ziplist？&lt;/h3&gt;&#10;&lt;p&gt;由于 Redis 是一个内存型数据库，因此在使用相应的数据结构时需要1）节省内存空间，2）提高操作效率。传统的包含指向相邻节点指针的双向链表中在内存使用和操作效率上存在一些问题：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;存储指针需要额外的内存空间，且该空间在指定硬件平台上是固定的。当存储的数据长度较小时，指针占用空间甚至会超过数据占用空间。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;传统双向链表中相邻节点在内存中并不连续，这虽然利用了碎片化的内存空间，但是由于增加了一次额外的指针索引，遍历效率低。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;ziplist 是 Redis 中设计的一种内存紧凑型的数据结构，可以用来代替传统的双向链表结构。ziplist 使用一块连续的内存空间，通过在每个节点中存储当前及前一个节点的长度来实现双向遍历，节省内存空间，操作效率高。&lt;/p&gt;&#10;&lt;h3 id="ziplist-是什么"&gt;ziplist 是什么？&lt;/h3&gt;&#10;&lt;p&gt;下面给出了压缩列表的结构示意图，包括 10byte 的表头元数据，若干个列表节点以及最后的 1byte 的结束标识符。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt; zlbytes &lt;span style="color:#f92672"&gt;|&lt;/span&gt; zltail &lt;span style="color:#f92672"&gt;|&lt;/span&gt; zllen &lt;span style="color:#f92672"&gt;|&lt;/span&gt; entry1 &lt;span style="color:#f92672"&gt;|&lt;/span&gt; entry2 &lt;span style="color:#f92672"&gt;|&lt;/span&gt; ... &lt;span style="color:#f92672"&gt;|&lt;/span&gt; entryN &lt;span style="color:#f92672"&gt;|&lt;/span&gt; zlend &lt;span style="color:#f92672"&gt;|&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 各部分说明&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zlbytes: &lt;span style="color:#960050;background-color:#1e0010"&gt;整个压缩列表占用的字节数，&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;uint32_t&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;byte)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zltail: &lt;span style="color:#960050;background-color:#1e0010"&gt;压缩列表表尾节点距离压缩列表的起始地址有多少字节，&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;uint32_t&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;byte)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zllen: &lt;span style="color:#960050;background-color:#1e0010"&gt;压缩列表包含的节点数，&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;byte)&lt;span style="color:#960050;background-color:#1e0010"&gt;，当该属性值为&lt;/span&gt; UINT16_MAX &lt;span style="color:#960050;background-color:#1e0010"&gt;时，真实节点数量需要遍历整个压缩列表才能得到&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;entry: &lt;span style="color:#960050;background-color:#1e0010"&gt;压缩列表节点&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zlend: &lt;span style="color:#960050;background-color:#1e0010"&gt;用于标记压缩列表末端的特殊值&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0xFF&lt;/span&gt;)&lt;span style="color:#960050;background-color:#1e0010"&gt;，&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;byte&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个压缩列表的节点中存储的内容可以看作一个字节数组或者整数值，每个节点包含以下三部分内容：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt; previous_entry_length &lt;span style="color:#f92672"&gt;|&lt;/span&gt; encoding &lt;span style="color:#f92672"&gt;|&lt;/span&gt; content &lt;span style="color:#f92672"&gt;|&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 各部分说明&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;previous_entry_length: &lt;span style="color:#960050;background-color:#1e0010"&gt;记录前一个节点的长度，长度为&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;byte &lt;span style="color:#960050;background-color:#1e0010"&gt;或&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;byte&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;前一个节点长度小于&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;254&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;字节时，&lt;/span&gt;previous_entry_length &lt;span style="color:#960050;background-color:#1e0010"&gt;长度为&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;字节，记录前一个节点的长度&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;前一个节点长度大于等于&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;254&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;字节时，&lt;/span&gt;previous_entry_length &lt;span style="color:#960050;background-color:#1e0010"&gt;长度为&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;字节，其中第一个字节设置为&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0xFE&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;，后面的四个字节用于记录前一个节点长度&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;encoding: &lt;span style="color:#960050;background-color:#1e0010"&gt;记录节点的&lt;/span&gt; content &lt;span style="color:#960050;background-color:#1e0010"&gt;中所保存数据的类型和长度&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;一字节&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;开头&lt;/span&gt;)&lt;span style="color:#960050;background-color:#1e0010"&gt;、两字节&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;开头&lt;/span&gt;)&lt;span style="color:#960050;background-color:#1e0010"&gt;或五字节&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;开头&lt;/span&gt;)&lt;span style="color:#960050;background-color:#1e0010"&gt;的表示字节数组编码，去除最高两位之后的其他位是字节数组长度&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;一字节长&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;开头&lt;/span&gt;)&lt;span style="color:#960050;background-color:#1e0010"&gt;的表示整数编码，去除最高两位后的其他位可以用于标识整数的类型和位宽&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content: &lt;span style="color:#960050;background-color:#1e0010"&gt;节点值，可以是一个字节数组或者整数值，由&lt;/span&gt; encoding &lt;span style="color:#960050;background-color:#1e0010"&gt;属性决定&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="ziplist-有什么问题"&gt;ziplist 有什么问题？&lt;/h3&gt;&#10;&lt;p&gt;前面提到 ziplist 通过使用连续的内存块和精心设计的编码格式来保存列表数据，并实现双向检索的功能。但是，使用 ziplist 仍然存在一些缺点：&lt;/p&gt;</description></item></channel></rss>