复制代码
介绍
memcached是高性能多线程的分布式缓存k-v数据库。Memcached是内存中的键值数据库,用于缓存从数据库调用,API调用的任意小块数据(字符串,对象)。可以通过缓存减轻数据库或IO的负载,从而增加应用的性能。
核心特征和原理
- 分布式
使用一致性hash来进行分布式节点选择,进行数据均衡和负载均衡。
- 缓存
不持久化。采用LRU(最近最少使用)回收空间。
- k-v数据库
功能单一的k-v数据库。
分布式架构
支持。
使用环形hash空间,对server节点和key都进行hash(32位元的循环冗余校验(CRC-32)计算键值),并且选择双方hash值最靠近的server节点。解决普通取模造成大量迁移的情况。
通过对物理节点虚拟更多节点,比如‘10.0.0.0-1~10.0.0.1-n’,解决server过少导致不均衡的问题。如下(扩容前)
如下(扩容后)
memcached需要在客户端维护节点列表,并进行业务分发。
缓存策略
- 使用LRU(最近最少使用)回收空间。
LRU(least recently used)最近最少使用。假设 序列为 4 3 4 2 3 1 4 2物理块有3个 则首轮 4调入内存 4次轮 3调入内存 3 4之后 4调入内存 4 3之后 2调入内存 2 4 3之后 3调入内存 3 2 4之后 1调入内存 1 3 2(因为最少使用的是4,所以丢弃4)之后 4调入内存 4 1 3(原理同上)最后 2调入内存 2 4 1复制代码
- 支持每个kv对的有效时限。
Kv的存储有效时限可以在客户端设置并作为参数传递给服务端
- 偷懒替代法。
服务端采用分类chunk定长记录,因此删除记录可以只做个标记,可以通过覆盖来回收并复用空间。
- 比较优秀的内存管理
相比redis等,应该有更高的内存利用率。
k-v数据库
功能单一的k-v数据库。
基本操作
Add功能:往内存增加一条新的缓存记录Delete功能:从内存删除一条缓存记录Get功能:从内存中提取一条缓存记录Replace功能:替换内存中的缓存记录Set功能:设置或者替换内存中的缓存记录,相当于add + replace的功能。复制代码
原子操作和事务性策略
支持incr,decr,cas等操作
持久化策略---现状
不支持。宕机会丢失数据
需要配合mysql,mariaDb等进行数据持久化设计。或者memcachedb?性能可能会大大降低。
虽然不能持久化,但是目标单纯,功能单一也有优点,可以避免额外设计带来的问题。比如持久化涉及到io的访问,有时候系统io的阻塞,可能会导致缓存性能受影响。
持久化策略---缓存数据一致性策略
- 查询策略
先查询cache,miss时查询db,并写入cache。
- 写入策略
采用writethrough策略。写db成功后,失效cache(写只写db;更新数据先更新db,再失效cache)
1,writethrough。如果配合高性能存储。可以先同步到db,如果成功则让缓存失效。(假设删除缓存不会失败,或极小可能性失败)。如果删除缓存失败(cache miss也算成功,其他不可预期失败算失败),则回退db操作(此间,缓存命中优先从缓存读取数据)。
2,writeback。先写入(删除)缓存,再写入db。在高并发的情况,容易出现数据不一致的场景。比如删除缓存后,写入db成功前,用户发起读请求,会导致读取到旧数据,从而引起数据不一致。
3,readonly。不适用。
- 其他需要考虑的并发性问题
1,线程1查询cache;线程2写db,并失效cache(非原子,非同步写db),写cache
2,事务性问题:一个事务包含多个db操作,db1操作成功,写cache1成功,写db2操作失败,事务回滚,db2数据回滚,cache1无法回滚,导致脏数据。
3,并发性问题:两个更新操作并发,如更新名字,并且cache中key以名字为关键字,更新一写db成功,写缓存XXXX_name1成功。更新二写db成功,写缓存XXXX_name2成功。导致cache脏数据。(多读少写情况下,考虑乐观锁加version解决-类似CAS,如下图)
性能
待测试。
据称在高速网络下,memcached可以轻松处理每秒200,000个以上的请求。并且消耗更少的内存。
貌似不支持组合请求,或合并批量处理。
从其他找到的一个测试如下:memcached貌似不如redis。另外20k以上的大数据会导致请求数显著下降。
其他
- 高可用和冗余设计
不支持。宕机后数据会丢失,需要配合其他设计。
- 并发策略
多线程充分利用多核处理器的优势。
数据通过一致性hash分片存储到不同节点。
- 其他功能
功能较单一。比如只支持单一的数据结构
- 可维护性
一般
- 扩展设计
一致性hash可以保证扩容或故障时,影响较小的数据。
- 商业成熟度
比较成熟。很流行,被广泛使用。
通过memcached的客户端实现分布式,支持多种语言的客户端,包括c,java等等。
- 代码量
1.47大概只有1万行左右。代码可读性不如redis。
- 开源协议
BSD开源协议
- 依赖
依赖libevent
扩展
原作者开发go的替代版groupcache
https://www.csdn.net/article/2013-07-30/2816399-groupcache-readme-go
优化
内存要足够避免使用swap减低性能,并尽量节点间内存一致。
总结
- key长度有限制。
存储的key不超过250字节,数据不能超过1M,最好小于10K。因为数据太大容易导致LRU淘汰比较严重,而且Memcached是CPU密集型的程序,容易降低QPS。
- 支持的数据类型单一
如果需要序列化和反序列化,需要考虑额外开销
- 不使用场景
写频繁的数据不适合使用缓存。
没有持久化和副本。任意节点掉电(非多台整系统掉电)都会导致数据丢失。对一致性,实时性,安全性要求极高的业务,可能不适合缓存。
- 多线程开销
在memcached多线程环境下使用CAS,在高并发上高QPS的情况下可能性能会显著降低。
参考资料
http://www.quweiji.com/memcachedb-tokyo-tyrant-redis-performance-test/
http://www.cnblogs.com/hjwublog/p/5625275.html
http://ruturaj.net/redis-memcached-tokyo-tyrant-and-mysql-comparision/
https://stackoverflow.com/questions/2873249/is-memcached-a-dinosaur-in-comparison-to-redis/8494397#8494397
作者:luciham@gmail.com出处:https://juejin.im/user/5a4e11d96fb9a01c9f5b3367本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。复制代码