# introduction_mc **Repository Path**: xiaolixi/introduction_mc ## Basic Information - **Project Name**: introduction_mc - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-10-31 - **Last Updated**: 2024-11-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # introduction_mc ## 谈谈Memcached - MC是一个分布式是KV内存数据库,没有持久化的机制,仅提供字符串一种存储数据结构。他的删除策略是LRU。 - 客户端可以使用telnet连接服务端,是一种文本协议,MC的内部用的是libevent事件库。 - MC是多线程的IO模型,因此在并发度和处理大key的情况下要比redis要好。 - slab class是MC的内存管理器,实际上就是一个个内存池,客户端设置数据时,MC会找到合适的大小存放数据。 其中有page、chunk、item等概念,由于page是固定1M大小,因此MC可以存储的最大数据是1M, slab class的增长因子默认是1.25,由于mc的这种分配方式,会产生内存碎片。 - MC的API比较简单,支持增删改查,incr/decr等,以及CAS命令。 - MC使用上目前用的比较少了,大多是使用redis。 但是MC在高QPS和大Key情况下比redis好,比如我们公司的MC加了L1缓存,以及master/slave的模式, 对于KV缓存场景,MC可以比redis提供更大的QPS。 ## 测试环境部署 ### docker run `docker run --name memcached -p 11211:11211 bitnami/memcached:latest` ![docker_memcached](images/docker_memcached.png) ### 管理WEB #### phpMemAdmin 看网上说是phpMemAdmin,害不会php,部署又很难。用docker吧,但是好像和网上看到的页面不一样。https://github.com/vesica/memadmin 所以到这个github上自己构建一下,看看是不是可以更新一下,结果没啥改变,那就这样吧。 ```shell git clone https://github.com/vesica/memadmin.git cd memadmin docker build . -t vesica/memadmin docker run -it -p 8080:8080 -e MEMADMIN_USERNAME='memUser' -e MEMADMIN_PASSWORD='memPassword' -e MEMCACHED_HOST='memcached' -e MEMCACHED_PORT='11211' --link memcached vesica/memadmin ``` ![img.png](images/data_php.png) ![img.png](images/host_php.png) #### memadmin memadmin,害不会php,部署又很难...放弃,php都不用了,所以。。。。mc用的越来越少了。。。 https://github.com/junstor/memadmin ### promethues的memcached_exporter ## mc vs redis ![mc_vs_redis](images/mc_vs_redis.png.png) ### MC的劣势 ![mc的劣势.png](images/mc的劣势.png) ## Memcached启动的参数 - 常用的启动命令:-p -m -d -vvv -f -t ```shell D:\software\memcached>memcached.exe -h memcached 1.4.4-14-g9c660c0 -p TCP port number to listen on (default: 11211) -U UDP port number to listen on (default: 11211, 0 is off) -s UNIX socket path to listen on (disables network support) -a access mask for UNIX socket, in octal (default: 0700) -l interface to listen on (default: INADDR_ANY, all addresses) -s unix socket path to listen on (disables network support) -a access mask for unix socket, in octal (default 0700) -l interface to listen on, default is INADDR_ANY -d start tell memcached to start -d restart tell running memcached to do a graceful restart -d stop|shutdown tell running memcached to shutdown -d install install memcached service -d uninstall uninstall memcached service -r maximize core file limit -u assume identity of (only when run as root) -m max memory to use for items in megabytes (default: 64 MB) -M return error on memory exhausted (rather than removing items) -c max simultaneous connections (default: 1024) -k lock down all paged memory. Note that there is a limit on how much memory you may lock. Trying to allocate more than that would fail, so be sure you set the limit correctly for the user you started the daemon with (not for -u user; under sh this is done with 'ulimit -S -l NUM_KB'). -v verbose (print errors/warnings while in event loop) -vv very verbose (also print client commands/reponses) -vvv extremely verbose (also print internal state transitions) -h print this help and exit -i print memcached and libevent license -P save PID in , only used with -d option -f chunk size growth factor (default: 1.25) -n minimum space allocated for key+value+flags (default: 48) -L Try to use large memory pages (if available). Increasing the memory page size could reduce the number of TLB misses and improve the performance. In order to get large pages from the OS, memcached will allocate the total item-cache in one large chunk. -D Use as the delimiter between key prefixes and IDs. This is used for per-prefix stats reporting. The default is ":" (colon). If this option is specified, stats collection is turned on automatically; if not, then it may be turned on by sending the "stats detail on" command to the server. -t number of threads to use (default: 4) -R Maximum number of requests per event, limits the number of requests process for a given connection to prevent starvation (default: 20) -C Disable use of CAS -b Set the backlog queue limit (default: 1024) -B Binding protocol - one of ascii, binary, or auto (default) -I Override the size of each slab page. Adjusts max item size (default: 1mb, min: 1k, max: 128m) D:\software\memcached> ``` ## 应用场景 - 缓存 - 存放session[不推荐] - 计数器/发号器:incr ## FAQ ### MC支持遍历所有的item吗? 从官网的FQA的回答是不可以的。 ![输入图片说明](images/why_mc_not_support_list_item.png) ### 什么是MC的钙化问题 => 重启 明明有剩余内存,但是数据一直被剔除出去,且命中率提高不了的现象。 因为page分给某个slab后就不会被释放。 当缓存的数据大小【访问模式】变了之后,就会用新的slab,但是旧的slab内存空间不会被释放。 ![mc_gaihua_pro.png](images/mc_gaihua_pro.png) ### Memcached的两阶段哈希是什么 第一阶段哈希是计算"key-value"存储在哪个Memcached实例上, 第二阶段哈希是计算"key-value"是存储存储在Memcached实例的哪个chunk里面。 比如我们要在Memcached上存储 key为"Hello",value为"World" 的这么一个键值对。 - 阶段一哈希: 首先在客户端根据key计算出该"键值对"会存储在哪个Memcached实例中,假如是m2。 - 阶段二哈希: 在Memcached实例二中 再次根据key计算出该"键值对"在m2实例中存在的位置。 ### Memcached新建Item分配内存过程 第一步: 快速定位slab classid,先计算Item长度     key键长+flag+suffix(16字节)+value值长+结构大小(32字节),比如计算出来有90byte     如果Item长度计算出来>1M,则无法存储丢弃     取最小冗余的slab class进行存储,比如有:48、96、120, 存储90就会选96 第二步:按顺序寻找可用的chunk     (a) slot : 检查slab回收空间slot里是否有剩余chunk       delete: delete时标记到slot       exptime: get时检查的过期对象标记到slot     (b)end_page_ptr: 检查page中是否有剩余chunk     (c)memory: 内存还有剩余空间可以用于开辟新的slab     (d)LRU (PS:Memcached的数据存储方式的缺点,由于chunk的大小是预先分配好的特定长度,因此如果数据不能完全填满chunk,那么chunk中剩余的空间就浪费了。) ### 监控 [https://github.com/prometheus/memcached_exporter](https://github.com/prometheus/memcached_exporter) ## 命令 ### 命令解释 | 命令 | 解释 | |:---------------|:--------------------------------------------------------------------------------------------------------------------------| | get | 返回Key对应的Value值 | | gets | 获取带有 CAS 令牌的 value(数据值),如果 key 不存在,则返回空。 | | set | 无条件地设置一个Key值,没有就增加,有就覆盖,操作成功提示STORED | | add | 添加一个不存在的Key值,没有则添加成功并提示STORED,Key存在则失败并提示NOT_STORED | | replace | 按照相应的Key值替换数据,如果Key值不存在则会操作失败 | | append | 用于向已存在 key(键) 的 value(数据值) 后面追加数据 。 | | prepend | 命令用于向已存在 key(键) 的 value(数据值) 前面追加数据 。 | | cas | 用于执行一个"检查并设置"的操作,它仅在当前客户端最后一次取值后,该key对应的值没有被其他客户端修改的情况下,才能够将值写入。
检查是通过cas_token参数进行的,这个参数是Memcach指定给已经存在的元素的一个唯一的64位值。 | | incr | 用于对已存在的 key(键) 的数字值进行自增操作。命令操作的数据必须是十进制的32位无符号整数。 | | decr | 用于对已存在的 key(键) 的数字值进行自减操作。命令操作的数据必须是十进制的32位无符号整数。 | | stats | 返回MemCache通用统计信息 | | stats items | 返回各个slab中item的数目和最老的item的年龄(最后一次访问距离现在的秒数) | | stats slabs | 返回MemCache运行期间创建的每个slab的信息(下面有详细解读) | | stats sizes | 用于显示所有item的大小和个数。 | | stats settings | 显示 Memcached 服务器的配置设置。 | | stats conns | 显示关于当前连接的统计信息。 | | flush_all | 清空所有键值,但不会删除items,所以此时MemCache依旧占用内存 | ### 存储命令 ```shell set|add|replace|append|prepend key flags exptime bytes [noreply] value ``` - key:键值 key-value 结构中的 key,用于查找缓存值。 - flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。 - exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远) - bytes:在缓存中存储的字节数 - noreply(可选): 该参数告知服务器不需要返回数据 - value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value) ```shell cas key flags exptime bytes unique_cas_token [noreply] value ``` - key:键值 key-value 结构中的 key,用于查找缓存值。 - flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。 - exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远) - bytes:在缓存中存储的字节数 - unique_cas_token通过 gets 命令获取的一个唯一的64位值。 - noreply(可选): 该参数告知服务器不需要返回数据 - value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value) ```shell incr|decr key increment_value ``` ### 查找命令 ```shell get|gets key [key2 key3 ...] ``` ```shell VALUE 1 ``` ```shell VALUE ``` ### 删除命令 ```shell delete key [noreply] ``` 1. memcached的flags有何用处 一个16位或32位【高版本】的无符号整型参数,客户机使用它存储关于键值对的额外信息。 通常使用者不用在意,多用于客户端实现中用于存储额外信息,用于处理value的编解码。 2. cas命令如何使用 cas是比较并替换的缩写,首先通过gets获取value和cas_value。cas命令设置新值的时候带上cas_value, 当memcached服务中该key的cas_value和cas命令的cas_value一致,说明值没有被改过,就可以设置成新的值, 如果不一致,说明值被其他客户端修改过,此时cas命令失败。 ### 统计命令 #### stats stats是一个比较重要的指令,用于列出当前服务器的状态 ```shell STAT pid 1023 #MemCache服务器的进程id STAT uptime 21069937 #服务器已经运行的秒数 STAT time 1447235954 #服务器当前的UNIX时间戳 STAT version 1.4.5 #MemCache版本 STAT pointer_size 64 #当前操作系统指针大小,反映了操作系统的位数,64意味着MemCache服务器是64位的 STAT rusage_user 1167.020934 #进程的累计用户时间 STAT rusage_system 3346.933170 #进程的累计系统时间 STAT curr_connections 29 #当前打开着的连接数 STAT total_connections 21 #当服务器启动以后曾经打开过的连接数 STAT connection_structures 49 #服务器分配的连接构造数 STAT cmd_get 49 #get命令总请求次数 STAT cmd_set 7458 #set命令总请求次数 STAT cmd_flush 0 #flush_all命令总请求次数 STAT get_hits 7401 #总命中次数,重要,缓存最重要的参数就是缓存命中率,以get_hits / (get_hits + get_misses)表示,比如这个缓存命中率就是99.2% STAT get_misses 57 #总未命中次数 ..(delete、incr、decr、cas的hits和misses数,cas还多一个badval) STAT auth_cmds 0 #认证命令的处理次数 STAT auth_errors 0 #认证失败的处理次数 STAT bytes_read 22026555 #总读取的字节数 STAT bytes_written 8930466 #总发送的字节数 STAT limit_maxbytes 4134304000 #分配给MemCache的内存大小(单位为字节) STAT accepting_conns 1 #是否已经达到连接的最大值,1表示达到,0表示未达到 STAT listen_disabled_num 0 #统计当前服务器连接数曾经达到最大连接的次数,这个次数应该为0或者接近于0,如果这个数字不断增长, 就要小心我们的服务了 STAT threads 4 #当前MemCache总线程数,由于MemCache的线程是基于事件驱动机制的,因此不会一个线程对应一个用户请求 STAT bytes 151255336 #当前服务器存储的items总字节数 STAT current_items 57146 #当前服务器存储的items总数量 STAT total_items 580656 #自服务器启动以后存储的items总数量 STAT evicitions 0 #是否出现数据被踢现象 ``` ##### 各种统计值 1. 当前的key数量: stats的current_items 2. 历史最大key数量: stats的total_items 3. 命中率:get_hits/cmd_get 4. 当前connections数: stats的curr_connections 5. 历史最大connections数: stats的total_connections 6. 拒绝connections数: stats的rejected_connections 7. 线程数:stats的threads 8. 存储的总字节数:stats的bytes #### stats slabs stats slabs 指令解读:从slab【相同chunk大小的集合】的角度统计 ```shell 1 STAT 1:chunk_size 96 #当前slab每个chunk的大小,单位为字节 2 ... 3 STAT 2:chunk_size 144 # 4 STAT 2:chunks_per_page 7281 #每个page可以存放的chunk数目,由于每个page固定为1M即1024*1024字节,所以这个值就是(1024*1024/chunk_size) 5 STAT 2:total_pages 7 #分配给当前slab的page总数 6 STAT 2:total_chunks 50967 #当前slab最多能够存放的chunk数,这个值是total_pages * chunks_per_page 7 STAT 2:used_chunks 45197 #已经被分配给存储对象的chunks数目 8 STAT 2:free_chunks 1 #曾经被使用过但是因为过期而被回收的chunk数 9 STAT 2:free_chunks_end 5769 #新分配但还没有被使用的chunk数,这个值不为0则说明当前slab从来没有出现过容量不够的时候 10 STAT 2:mem_requested 6084638 #当前slab中被请求用来存储数据的内存空间字节总数, #(total_chunks*chunk_size)-mem_requested表示有多少内存在当前slab中是被闲置的,这包括未用的slab+使用的slab中浪费的内存 11 STAT 2:get_hits 48084 #当前slab中命中的get请求数 12 STAT 2:cmd_set 59588271 #当前slab中接收的所有set命令请求数 13 STAT 2:delete_hits 0 #当前slab中命中的delete请求数 14 STAT 2:incr_hits 0 #当前slab中命中的incr请求数 15 STAT 2:decr_hits 0 #当前slab中命中的decr请求数 16 STAT 2:cas_hits 0 #当前slab中命中的cas请求数 17 STAT 2:cas_badval 0 #当前slab中命中但是更新失败的cas请求数 18 ... 19 STAT 3:chunk_size 216 20 ... ``` ![slabs.png](images/slabs.png) #### stats items ```shell stats items STAT items:1:number 2 # slab 1中的key的数量是2 STAT items:1:number_hot 0 STAT items:1:number_warm 0 STAT items:1:number_cold 2 STAT items:1:age_hot 0 STAT items:1:age_warm 0 STAT items:1:age 6954 STAT items:1:mem_requested 138 STAT items:1:evicted 0 STAT items:1:evicted_nonzero 0 STAT items:1:evicted_time 0 STAT items:1:outofmemory 0 STAT items:1:tailrepairs 0 STAT items:1:reclaimed 4 STAT items:1:expired_unfetched 1 STAT items:1:evicted_unfetched 0 STAT items:1:evicted_active 0 STAT items:1:crawler_reclaimed 1 STAT items:1:crawler_items_checked 63 STAT items:1:lrutail_reflocked 18 STAT items:1:moves_to_cold 49 STAT items:1:moves_to_warm 32 STAT items:1:moves_within_lru 0 STAT items:1:direct_reclaims 0 STAT items:1:hits_to_hot 3 STAT items:1:hits_to_warm 0 STAT items:1:hits_to_cold 48 STAT items:1:hits_to_temp 0 STAT items:5:number 1 STAT items:5:number_hot 0 STAT items:5:number_warm 0 STAT items:5:number_cold 1 STAT items:5:age_hot 0 STAT items:5:age_warm 0 STAT items:5:age 1583 STAT items:5:mem_requested 231 STAT items:5:evicted 0 STAT items:5:evicted_nonzero 0 STAT items:5:evicted_time 0 STAT items:5:outofmemory 0 STAT items:5:tailrepairs 0 STAT items:5:reclaimed 0 STAT items:5:expired_unfetched 0 STAT items:5:evicted_unfetched 0 STAT items:5:evicted_active 0 STAT items:5:crawler_reclaimed 0 STAT items:5:crawler_items_checked 1 STAT items:5:lrutail_reflocked 0 STAT items:5:moves_to_cold 4 STAT items:5:moves_to_warm 3 STAT items:5:moves_within_lru 0 STAT items:5:direct_reclaims 0 STAT items:5:hits_to_hot 0 STAT items:5:hits_to_warm 0 STAT items:5:hits_to_cold 4 STAT items:5:hits_to_temp 0 END ``` ![items.png](images/items.png) #### stats sizes 命令用于显示所有item的大小和个数。 该信息返回两列,第一列是 item 的大小,第二列是 item 的个数。 STAT sizes_size number_of_items。 例如:STAT 96 10 这表示有 10 个大小为 96 字节的对象。 ![img.png](images/stats_sizes.png) #### stats conns stats conns显示当前的连接 ```shell stats conns STAT 26:addr tcp:0.0.0.0:11211 STAT 26:state conn_listening STAT 26:secs_since_last_cmd 33172 STAT 27:addr tcp6:[::]:11211 STAT 27:state conn_listening STAT 27:secs_since_last_cmd 33172 STAT 28:addr tcp:172.17.0.1:41728 STAT 28:listen_addr tcp:172.17.0.3:11211 STAT 28:state conn_parse_cmd STAT 28:secs_since_last_cmd 0 END ``` #### stats cachedump 显示某个slab中的前limit_num个key列表,0为全部列出 1. 版本1: 列出的items id,本例中为13,第2个参数为列出的长度,0为全部列出。 ```shell stats cachedump 13 0 //执行命令 ITEM 5b8da9aaa22cccf895e756b6edfeaef4 [1252 b; 1524220693 s] END ``` 显示某个slab中的前limit_num个key列表,0为全部列出 显示格式如下ITEM key_name [ value_length b; expire_time|access_times], 注意:不要试图通过此命令导出Memcache服务中某个slab的所有Key列表,该命令默认只返回1M的内存数据。 1. 版本2: 列出slab 1/5的所有items ```shell stats cachedump 1 0 ITEM lixi [10 b; 0 s] ITEM tom [3 b; 0 s] END stats cachedump 5 0 ITEM wawa [168 b; 0 s] END ``` ## Java客户端 1. memcached client for java 2. spymemcached 3. xmemcached [mc](./mc) ## 内存管理 从业务需求出发。我们通过一条命令(如set)将一条键值对(key,value)插入memcached后,需要: 1、对该键值数据的高效索引; (memcached通过哈希表来对键值数据进行管理,具体的实现中采用链接法来处理hash冲突问题。) 2、系统可能会频繁的创建新数据和删除旧数据,需要高效的内存管理; 最简单的思路是来了新的数据就malloc内存,将新数据保存在这段新分配的内存中,当数据要被删除时就把这段内存free掉。但是频繁的malloc和free将会导致系统的内存碎片问题, 加重系统内存管理的负担。同时malloc和free作为系统调用,在时间方面也存在一定开销。Memcached的解决方式是创建内存池来管理内存分配,具体实现思路是采用Slab Allocator作为内存分配器。) 3、系统应该能够自行删除长期不使用的缓存数据。 ( memcached给每个数据记录过期时间,并将同一个slab class的所有数据通过LRU算法进行组织,当插入新数据时通过检查LRU链表对超时的旧数据进行删除。) ### 术语概念 | 名词 | 解释 | |:------|:-------------------------------------------------------------------------------------------------------------| | page | 物理概念,slab申请的1M大小的内存 | | slab | 逻辑概念,管理特定大小的 chunk 的集合。Memcached每次默认分配的一个连续内存块为1M大小,它们被切分为不同大小的chunk。 | | chunk | 由申请的连续内存块平均切分而成,用来存放Item数据,根据Item大小找到近似的Chunk。
是一个分配给用户使用的最小单元,在一个page下,也就是1M内存大小下,会平均分成相同大小的chunk,
不同的page,内部的chunk不一定相同大小,但是同一个page内的chunk一定一样。 | | item | 为键值数据的实际储存结构。item主要由公共属性、数据部分两个部分组成。
| ![slab_chunk_item](images/slab_chunk_item.png) ![slab_chunk_item_2](images/slab_chunk_item_2.png) ![slab_item_chunk3](images/slab_item_chunk3.png) ![slab_item_chunk3](images/slab_item_chunk4.png) ### 数据结构 1. item:为键值数据的实际储存结构。 item主要由两个部分组成: - 第一个部分是公共属性部分,包括连接其它 item 的指针 (next,prev,h_next),还有最近访问时间(time), 过期的时间(exptime)等,结构长度固定; - 第二部分是item的数据部分,由 CAS, key, suffix, value 组成,由于实际的键值数据长度不确定,因此该部分的结构长度不固定。 ![item](./images/item.png) 2. Chunk:由申请的连续内存块平均切分而成,Chunk是实际分配给item的内存空间. Memcached会维护多个不同大小的chunk内存块,如:item公共属性部分需要48bytes内存空间,数据部分需要52bytes内存空间,该item一共需要100bytes连续内存空间。 该memcached分别维护了88bytes、112 bytes、144 bytes和184 bytes大小的chunk块群。最接近且大于该item所需内存大小的是size为112bytes的chunk块。 因此我们取出一个尚未被使用的112 bytes 的chunk块,并将该item中的数据保存到该chunk的内存空间中。此时该chunk中将有12bytes剩余内存将作为碎片被暂时浪费。 ![chunk](images/chunk.png) 3. Slab Class:管理特定大小的 chunk 的集合。 Memcached每次默认分配的一个连续内存块为1M大小,它们被切分为不同大小的chunk。 但是不同chunk的需求量不同,有的情况下某些大小的chunk只需一个连续内存块切分的数量即可满足业务需要,但有的大小的chunk需求量比较大,需要分配更多的连续内存块来进行切分。 这些切分为相同大小的chunk块群,都由对应的slab class进行管理。 ### memcached的添加一个item内存预分配过程 1. 向memcached添加一个item时候,memcached首先会根据 item的大小,来选择最合适的slab class:例如item的大小为190字节,默认情况下class 4的chunk大小为160字节显然不合适, class 5的chunk大小为200字节,大于190字节,因此该item将放在class 5中(显然这里会有10字节的浪费是不可避免的)。 2. 计算好所要放入的chunk之后,如果这个item对应的slab未出现过,则申请1个page(注意,这1M空间不论是否达到memcached使用内存都可以申请成功)并加该item存入slab中的chunk。 3. 如果item对应的slab出现过,则在该slab中优先选择expired(free_chunks)和delete的chunk进行存储,其次将选择未使用过的chunk(free_chunks_end)进行存储。 4. 如果item对应的slab出现过,但是对应的slab已经存储满了,那么会申请一个新的page,这个page被分为对应大小的chunk,继续存储。 例如我们第一次向memcached中放入一个190字节的item 时,memcached会产生一个slab class 5(也叫一个page), 并会用去一个chunk,剩余5241个chunk供下次有适合大小item时使用,当我们用完这所有的5242个chunk之后,下次再有一个在160~200字节之间的item添加进来时, memcached会再次产生一个class 5的slab(这样就存在了2个pages)。 5. 如果item对应的slab出现过,但是对应的slab已经存储满了并且memcache也达到了最大内存使用。将使用lru算法,清除item(可能将未过期的item清除)此时会有eviction++。 ### Memcached的hash表采用链接法实现。 hashtable被分成多个桶bucket,每个item通过hash函数确定具体的bucket,然后链接到该bucket上,如果该桶中已存在链接的item(即出现了哈希冲突), 则将这个item通过h_next指针形成该bucket下链接的单向链表。 图中,item A和item B都被哈希映射到了bucket[1]中,它们通过h_next组织为单向链表,且bucket[1]作为链表表头。 ![hashtable.png](images/hashtable.png) ### LRU链表 每个slab中都维护了一个LRU链表,来组织该slab中已经被分配的item块,用于记录“最近最少使用”的item信息。其中heads指向链表的头节点,tails指向链表的尾节点。 每当有新chunk被使用时,将会将该chunk的item添加到LRU链表头。或者有原使用的item被修改,也会将其从链表中移动到LRU链表头处。 通过该机制,保证了链表头部分的的item为新创建或新修改的数据,链表尾item为该slab中储存最久的数据。 Memcached采用了惰性删除的机制,系统不会主动监视item中数据是否过期,而是在get的时候查看该item的时间戳,如果已过期就删除并将该chunk释放到空闲链表中。 同时在新数据插入中,Memcached也会优先判断该slab的LRU链表尾部的item节点是否超时,如果超时的话,Memcached也会优先删除并使用已经超时的item的chunk作为新数据的储存空间。 当该slab的LRU链表尾部item节点并未超时,但是slab中无可用chunk,且无法从系统中扩容到新的内存空间时,Memcached将会直接摘取LRU链表中的最后的item, 强行删除并将其空间分配给新的数据记录。Memcached可以配置为禁止使用LRU机制,这样的话当该slab中chunk耗尽且分配不到新内存时将会返回错误。 内存机制好文:https://www.codedump.info/post/20210812-memcached/?hmsr=toutiao.io&utm_campaign=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io MC的使用最佳实践: http://www.360doc.com/content/12/0524/17/9350055_213432371.shtml 微博高并发场景下的分布式缓存架构: http://www.360doc.com/content/18/0225/14/16915_732336673.shtml ## 兼容应用程序 [https://www.cnblogs.com/zhuawang/p/4768123.html](https://www.cnblogs.com/zhuawang/p/4768123.html) memcached的实现和协议都十分简单,因此有很多与memcached兼容的实现。 一些功能强大的扩展可以将memcached的内存数据写到磁盘上,实现数据的持久性和冗余。 这里介绍几个与memcached兼容的应用程序。 - repcached 为memcached提供复制(replication)功能的patch。 - Flared 存储到QDBM。同时实现了异步复制和fail over等功能。 - memcachedb 存储到BerkleyDB。还实现了message queue。 - Tokyo Tyrant 将数据存储到Tokyo Cabinet。不仅与memcached协议兼容,还能通过HTTP进行访问。