<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Concurrent on Tech Explorer</title><link>https://byronhe.com/categories/concurrent/</link><description>C++、Linux、算法、数据结构与密码学的学习笔记与实践记录。</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 BH</copyright><lastBuildDate>Tue, 14 May 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://byronhe.com/categories/concurrent/index.xml" rel="self" type="application/rss+xml"/><item><title>shm_lru_cache 共享内存 并发 变长 LRU 缓存</title><link>https://byronhe.com/post/2024/05/14/shm-concurrent-lru-cache/</link><pubDate>Tue, 14 May 2024 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2024/05/14/shm-concurrent-lru-cache/</guid><description>&lt;h2 class="relative group"&gt;一个基于共享内存，多线程多进程并发安全，key value 变长 的 LRU 缓存。 内部基于 TLSF 内存分配器, 和 LevelDB 的 LRUCache 代码实现&#10; &lt;div id="一个基于共享内存多线程多进程并发安全key-value-变长-的-lru-缓存-内部基于-tlsf-内存分配器-和-leveldb-的-lrucache-代码实现" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e4%b8%aa%e5%9f%ba%e4%ba%8e%e5%85%b1%e4%ba%ab%e5%86%85%e5%ad%98%e5%a4%9a%e7%ba%bf%e7%a8%8b%e5%a4%9a%e8%bf%9b%e7%a8%8b%e5%b9%b6%e5%8f%91%e5%ae%89%e5%85%a8key-value-%e5%8f%98%e9%95%bf-%e7%9a%84-lru-%e7%bc%93%e5%ad%98-%e5%86%85%e9%83%a8%e5%9f%ba%e4%ba%8e-tlsf-%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8-%e5%92%8c-leveldb-%e7%9a%84-lrucache-%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;hr&gt;&#10;&#10;&lt;h2 class="relative group"&gt;一，介绍&#10; &lt;div id="一介绍" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e4%bb%8b%e7%bb%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;一个基于共享内存，多线程多进程并发安全，key value 变长 的 LRU 缓存。 内部基于 TLSF 内存分配器, 和 LevelDB 的 LRUCache 代码&lt;/p&gt;</description><content:encoded>&#10;&lt;h2 class="relative group"&gt;一个基于共享内存，多线程多进程并发安全，key value 变长 的 LRU 缓存。 内部基于 TLSF 内存分配器, 和 LevelDB 的 LRUCache 代码实现&#10; &lt;div id="一个基于共享内存多线程多进程并发安全key-value-变长-的-lru-缓存-内部基于-tlsf-内存分配器-和-leveldb-的-lrucache-代码实现" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e4%b8%aa%e5%9f%ba%e4%ba%8e%e5%85%b1%e4%ba%ab%e5%86%85%e5%ad%98%e5%a4%9a%e7%ba%bf%e7%a8%8b%e5%a4%9a%e8%bf%9b%e7%a8%8b%e5%b9%b6%e5%8f%91%e5%ae%89%e5%85%a8key-value-%e5%8f%98%e9%95%bf-%e7%9a%84-lru-%e7%bc%93%e5%ad%98-%e5%86%85%e9%83%a8%e5%9f%ba%e4%ba%8e-tlsf-%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8-%e5%92%8c-leveldb-%e7%9a%84-lrucache-%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;hr&gt;&#10;&#10;&lt;h2 class="relative group"&gt;一，介绍&#10; &lt;div id="一介绍" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e4%bb%8b%e7%bb%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;一个基于共享内存，多线程多进程并发安全，key value 变长 的 LRU 缓存。 内部基于 TLSF 内存分配器, 和 LevelDB 的 LRUCache 代码&lt;/p&gt;&#10;&lt;p&gt;基于 TLSF 内存分配器。存入多少数据就占用多少内存。释放掉的 key/value 等内存空间自动回收重用。&lt;/p&gt;&#10;&lt;p&gt;基于 LevelDB 的 LRUCache ，读操作和写操作可以并发执行，内部通过读引用计数实现。&lt;/p&gt;&#10;&lt;p&gt;读操作直接返回指向共享内存的指针，零拷贝。&lt;/p&gt;&#10;&lt;p&gt;支持 CompareAndSwap CAS 乐观锁操作&lt;/p&gt;&#10;&lt;p&gt;多进程多线程并发读写 ShmLRUCache 时, 如果个别进程异常退出，则其他进程会在读写的时候检测到， 会删除共享内存文件将当前进程退出，等待进程被重启之后重建新的共享内存使用。利用 pthread mutex 的 robust list 实现&lt;/p&gt;&#10;&lt;p&gt;支持设置 TTL 过期时间，达到过期时间的 key 会在哈希桶 Lookup 访问过程中被淘汰删除。&lt;/p&gt;&#10;&lt;p&gt;开发了配套工具 dump_shm_lru_cache ，可以在运行时只读打印共享内存中的所有 key value&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二，使用方法&#10; &lt;div id="二使用方法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h3 class="relative group"&gt;暂不开源，没有使用方法&#10; &lt;div id="暂不开源没有使用方法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%9a%82%e4%b8%8d%e5%bc%80%e6%ba%90%e6%b2%a1%e6%9c%89%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;常用共享内存相关工具命令：&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;top&lt;/code&gt; 共享内存都算入 top 看到的 SHR 即共享内存这一列，同时也算入 RES 即驻留内存这一列。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;du -sh /dev/shm/*&lt;/code&gt; 查看共享内存文件实际占用多少物理内存&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;lsof -p $pid |egrep 'NAME|/dev/shm'&lt;/code&gt; 查看进程 $pid 实际打开了哪些共享内存文件&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;pmap -X $pid |egrep 'Rss|finder_'&lt;/code&gt; 查看进程的共享内存文件 finder_* 都映射到哪些地址段&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;ls -lhS /dev/shm/*&lt;/code&gt; 查看共享内存文件最大允许的大小。注意不是实际占用大小。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;三，性能测试，固定使用 4 进程 * 4 线程 = 16 线程&#10; &lt;div id="三性能测试固定使用-4-进程--4-线程--16-线程" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%89%e6%80%a7%e8%83%bd%e6%b5%8b%e8%af%95%e5%9b%ba%e5%ae%9a%e4%bd%bf%e7%94%a8-4-%e8%bf%9b%e7%a8%8b--4-%e7%ba%bf%e7%a8%8b--16-%e7%ba%bf%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;下面的性能测试工具代码也和代码在同一个目录下，可以随时自己编译自己复现。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. 纯读，96% 命中率时 550W QPS，50% 命中率时 640W QPS&#10; &lt;div id="1-纯读96-命中率时-550w-qps50-命中率时-640w-qps" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e7%ba%af%e8%af%bb96-%e5%91%bd%e4%b8%ad%e7%8e%87%e6%97%b6-550w-qps50-%e5%91%bd%e4%b8%ad%e7%8e%87%e6%97%b6-640w-qps" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. 覆盖写, 43 WQPS&#10; &lt;div id="2-覆盖写-43-wqps" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e8%a6%86%e7%9b%96%e5%86%99-43-wqps" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. 读写并发进行 ( 4x4=16 个线程读，同时另外有 4x4=16 个线程写)，此时读性能 420W QPS，写性能 41W QPS&#10; &lt;div id="3-读写并发进行--4x416-个线程读同时另外有-4x416-个线程写此时读性能-420w-qps写性能-41w-qps" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e8%af%bb%e5%86%99%e5%b9%b6%e5%8f%91%e8%bf%9b%e8%a1%8c--4x416-%e4%b8%aa%e7%ba%bf%e7%a8%8b%e8%af%bb%e5%90%8c%e6%97%b6%e5%8f%a6%e5%a4%96%e6%9c%89-4x416-%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%86%99%e6%ad%a4%e6%97%b6%e8%af%bb%e6%80%a7%e8%83%bd-420w-qps%e5%86%99%e6%80%a7%e8%83%bd-41w-qps" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&#10;&lt;h3 class="relative group"&gt;4. 典型业务逻辑“先查询如果 miss 就写”，77% 命中率的情况下，400 W QPS&#10; &lt;div id="4-典型业务逻辑先查询如果-miss-就写77-命中率的情况下400-w-qps" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-%e5%85%b8%e5%9e%8b%e4%b8%9a%e5%8a%a1%e9%80%bb%e8%be%91%e5%85%88%e6%9f%a5%e8%af%a2%e5%a6%82%e6%9e%9c-miss-%e5%b0%b1%e5%86%9977-%e5%91%bd%e4%b8%ad%e7%8e%87%e7%9a%84%e6%83%85%e5%86%b5%e4%b8%8b400-w-qps" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&#10;&lt;h2 class="relative group"&gt;四，几个方面的关键设计&#10; &lt;div id="四几个方面的关键设计" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9b%9b%e5%87%a0%e4%b8%aa%e6%96%b9%e9%9d%a2%e7%9a%84%e5%85%b3%e9%94%ae%e8%ae%be%e8%ae%a1" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. 为什么不搞无锁？&#10; &lt;div id="1-为什么不搞无锁" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e6%90%9e%e6%97%a0%e9%94%81" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;因为无锁只能对 8/16 字节的简单内存操作实现原子性，而想实现变长就要搞个主流一点的 best fit 类的内存分配器算法，这类算法都有复杂的管理数据结构，比如由多个链表，bitmap，红黑树 等组成的复合数据结构，其上的操作基本不可能实现成无锁。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. 怎么应对多进程下某些进程的异常退出？&#10; &lt;div id="2-怎么应对多进程下某些进程的异常退出" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e6%80%8e%e4%b9%88%e5%ba%94%e5%af%b9%e5%a4%9a%e8%bf%9b%e7%a8%8b%e4%b8%8b%e6%9f%90%e4%ba%9b%e8%bf%9b%e7%a8%8b%e7%9a%84%e5%bc%82%e5%b8%b8%e9%80%80%e5%87%ba" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;背景：pthread_mutex 有个 robust list 功能 (PTHREAD_MUTEX_ROBUST) ，其语义是“当多个进程 attach 到同一块共享内存，并且其中一个进程在加锁进入临界区之后被 kill ，则其他进程在下次 pthread_mutex_lock 时会收到特殊返回码 EOWNERDEAD”。&lt;/p&gt;&#10;&lt;p&gt;基于 robust list，可以把 “被 kill ” 分为 2 种来做应对：&lt;/p&gt;&#10;&lt;p&gt;在临界区内被 kill，这种情况通过 EOWNERDEAD 可以检测到，直接删掉共享内存文件，重启进程就行。&lt;/p&gt;&#10;&lt;p&gt;在临界区外被 kill， 只要保证所有修改数据结构的操作都在临界区中，则这种情况不会影响到 lru cache 的数据一致性。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. 在每个进程映射到同样的起始虚拟地址，还是不同的起始虚拟地址即搞 boost::interprocess::offset_ptr 那种？&#10; &lt;div id="3-在每个进程映射到同样的起始虚拟地址还是不同的起始虚拟地址即搞-boostinterprocessoffset_ptr-那种" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e5%9c%a8%e6%af%8f%e4%b8%aa%e8%bf%9b%e7%a8%8b%e6%98%a0%e5%b0%84%e5%88%b0%e5%90%8c%e6%a0%b7%e7%9a%84%e8%b5%b7%e5%a7%8b%e8%99%9a%e6%8b%9f%e5%9c%b0%e5%9d%80%e8%bf%98%e6%98%af%e4%b8%8d%e5%90%8c%e7%9a%84%e8%b5%b7%e5%a7%8b%e8%99%9a%e6%8b%9f%e5%9c%b0%e5%9d%80%e5%8d%b3%e6%90%9e-boostinterprocessoffset_ptr-%e9%82%a3%e7%a7%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;这里选择了多进程映射到同样起始地址，同样起始地址的好处是里面的数据结构可以用裸指针，代码实现简单。&lt;/p&gt;&#10;&lt;p&gt;背景是因为我发现 X86-64 平台上 48bit 地址空间非常巨大，pmap 看到其实大多数地址空间是没有使用的，目前是从 90TB 这个虚拟地址开始尝试分配，如果冲突就后移。并且这里利用了新一点 Linux kernel 的 MAP_FIXED_NOREPLACE 选项，可以避免覆盖已有的内存映射。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;4. 起始虚拟地址是怎么分配出来的？&#10; &lt;div id="4-起始虚拟地址是怎么分配出来的" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-%e8%b5%b7%e5%a7%8b%e8%99%9a%e6%8b%9f%e5%9c%b0%e5%9d%80%e6%98%af%e6%80%8e%e4%b9%88%e5%88%86%e9%85%8d%e5%87%ba%e6%9d%a5%e7%9a%84" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;创建 shm 文件的时候进行分配，读 /proc/self/maps 获知本进程的地址空间分布，从中找到从 90TB 开始并且没有被占用的空洞，来进行 mmap，如果冲突了即地址空间已经被占用了，再次尝试。&lt;/p&gt;&#10;&lt;p&gt;其他进程 attach 已经存在的 shm 文件的时候，有个 CheckAttach 的检查逻辑，如果发现本进程的地址空间被占了，会返回 kMMap2BaseAddrConflicted , 然后 Remove 删掉共享内存文件。（Linux 下进程打开文件后，如果被 deleted， 还是可以正常读写的，不受影响。）&lt;/p&gt;&#10;&lt;p&gt;这里的过程就有点像个活锁，经过多次互相删除后，最终所有进程会达成统一，在相同的虚拟地址上映射同一个共享内存文件。&lt;/p&gt;&#10;&lt;p&gt;不同的 shm_name 文件，会分配出不同的起始地址。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;5. 怎么限制内存使用？&#10; &lt;div id="5-怎么限制内存使用" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#5-%e6%80%8e%e4%b9%88%e9%99%90%e5%88%b6%e5%86%85%e5%ad%98%e4%bd%bf%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;目前有 2 种限制策略：总条数限制，和总字节数限制。当达到限制时，会进行淘汰，有 2 种淘汰策略：LRU 和 TTL。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;6. 用什么内存分配器算法？&#10; &lt;div id="6-用什么内存分配器算法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#6-%e7%94%a8%e4%bb%80%e4%b9%88%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8%e7%ae%97%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;找了目前业界比较成熟的一个嵌入式分配器 TLSF 直接复用。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;7. LRU Cache 数据结构怎么实现？&#10; &lt;div id="7-lru-cache-数据结构怎么实现" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#7-lru-cache-%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e6%80%8e%e4%b9%88%e5%ae%9e%e7%8e%b0" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;直接把 LevelDB 中的经典 LRU Cache 代码复制出来做改造，该实现的一大亮点是搞了个引用计数，使得读写可以并发进行。并且我直接继承了其很多单元测试，用来保证代码正确性。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;8. 节省内存的技巧&#10; &lt;div id="8-节省内存的技巧" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#8-%e8%8a%82%e7%9c%81%e5%86%85%e5%ad%98%e7%9a%84%e6%8a%80%e5%b7%a7" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;使用了 varint 7bit 编码保存 key/value 的长度 。&lt;/p&gt;&#10;&lt;p&gt;LRU 中节点的三个指针，换算成到基地址的 offset，并考虑了 align 和 48bit 地址空间后，用位运算压缩到了每个指针 35bit 。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;五. 和业界类似项目对比&#10; &lt;div id="五-和业界类似项目对比" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%94-%e5%92%8c%e4%b8%9a%e7%95%8c%e7%b1%bb%e4%bc%bc%e9%a1%b9%e7%9b%ae%e5%af%b9%e6%af%94" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. boost::interprocess&#10; &lt;div id="1-boostinterprocess" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-boostinterprocess" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;(1). shm_lru_cache 在 robust list 检测到 corrupted 后，能定制 多进程 rpc 环境下的恢复策略 : _exit()。&lt;/p&gt;&#10;&lt;p&gt;(2). shm 上 robust list 的 mutex 的实现，需要自己实现多进程对 OWNER DIED 状态的发现机制。&lt;/p&gt;&#10;&lt;p&gt;(3). shm_allocator 实现了 stateless allocator 无状态的分配器，用于各种库更省内存，比如各种带 Alloc 模板参数的 std 类，容器等。 boost::interprocess 的 allocator 是有状态的。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. memcached&#10; &lt;div id="2-memcached" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-memcached" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;(1). 没有网络层，因此不可能有网络层的故障&lt;/p&gt;&#10;&lt;p&gt;(2). 性能大幅度提升，&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. libshmcache&#10; &lt;div id="3-libshmcache" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-libshmcache" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/happyfish100/libshmcache" target="_blank" rel="noreferrer"&gt;https://github.com/happyfish100/libshmcache&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;shm_lru_cache 利用了 robust list 实现的 corrupted 检测，这才是严密的。&lt;/p&gt;&#10;&lt;p&gt;TLSF 有 merge/absorb 逻辑，能适应 value 长度的各种变化。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;4. lmdb/mdbx&#10; &lt;div id="4-lmdbmdbx" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-lmdbmdbx" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;设计用途不同，lmdb/mdbx 是 B+ 树有序 map 数据结构 key-value 存储引擎。性能有限。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;5. redis&#10; &lt;div id="5-redis" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#5-redis" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;“Redis 6.0 主备实例测试数据”：&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://support.huaweicloud.com/pwp-dcs/dcs_10_0001.html#section2" target="_blank" rel="noreferrer"&gt;https://support.huaweicloud.com/pwp-dcs/dcs_10_0001.html#section2&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://redis.io/docs/management/optimization/benchmarks/" target="_blank" rel="noreferrer"&gt;https://redis.io/docs/management/optimization/benchmarks/&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;GET/SET 大概是 15W QPS，当然 redis 是单核还有网络层而且是文件持久化的，适用场景有点不一样。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;6. dragonfly&#10; &lt;div id="6-dragonfly" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#6-dragonfly" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/dragonflydb/dragonfly/blob/main/README.zh-CN.md#-" target="_blank" rel="noreferrer"&gt;https://github.com/dragonflydb/dragonfly/blob/main/README.zh-CN.md#-&lt;/a&gt; 基准测试&amp;gt;&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;7. golang 的各种 cache&#10; &lt;div id="7-golang-的各种-cache" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#7-golang-%e7%9a%84%e5%90%84%e7%a7%8d-cache" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;这些都不是基于共享内存的，而且性能也有差距&lt;/p&gt;&#10;</content:encoded></item></channel></rss>