Memcached

Memcached 深度解析

一、工作原理与内部机制
  1. 数据模型

    • 纯内存键值存储(Key-Value),键最大 250250250 字节,值最大 111 MB
    • 数据结构 item 包含元数据(过期时间、标志位)和实际数据
  2. 内存管理(Slab Allocator)

    • 内存预分割为多个 Slab Class(如 64B/128B/256B64B/128B/256B64B/128B/256B
    • 每个 Slab Class 包含固定大小的 Chunk
    • 分配流程:
      1. 计算数据大小 size=key_len+value_len+32Bsize = key\_len + value\_len + 32Bsize=key_len+value_len+32B(元数据开销)
      2. 选择最小满足 sizesizesize 的 Slab Class
      3. 从对应 LRU 链表分配 Chunk
  3. 淘汰机制

    • LRU 算法:每个 Slab Class 维护独立链表
    • 惰性删除:访问时检查过期时间
    • LRU Crawler:后台线程扫描过期项(需启用 -o lru_crawler
  4. 哈希索引

    • 链式哈希表解决冲突
    • 默认哈希函数:CRC32CRC32CRC32,可配置为 MurmurHash3MurmurHash3MurmurHash3
    • 时间复杂度 O(1)O(1)O(1)
二、配置方法(Linux 示例)
# 1. 安装依赖  
sudo apt-get install libevent-dev  

# 2. 编译安装  
wget https://memcached.org/latest  
tar -zxvf memcached-*.tar.gz  
cd memcached-*  
./configure --prefix=/usr/local/memcached  
make && sudo make install  

# 3. 启动参数详解  
/usr/local/memcached/bin/memcached \  
  -m 2048              # 分配2GB内存 \  
  -p 11211             # 监听端口 \  
  -c 4096              # 最大连接数 \  
  -o lru_crawler       # 启用LRU扫描器 \  
  -U 11211             # 启用UDP协议 \  
  -d                   # 守护进程模式  
三、性能优化策略
  1. 降低剔除率(Eviction)

    • 监控 evicted_items 指标:
      echo "stats" | nc localhost 11211 | grep evicted  
      
    • 解决方案:
      • 扩容内存(增加 -m 参数)
      • 调整 Slab 分布:-o slab_reassign
      • 预热缓存:重启后主动加载热点数据
  2. 网络优化

    • 启用 UDP 协议减少连接开销
    • 调整连接池大小(Java 客户端如 spymemcached
      ConnectionFactoryBuilder()  
         .setOpTimeout(1000)  
         .setMaxConnections(500)  
      
  3. 键设计规范

    • 命名空间分层:user:123:profile
    • 避免大 Key:超过 111 KB 的数据需拆分
四、与 Redis 核心对比
维度MemcachedRedis
数据结构仅 String支持 555 种复杂结构
持久化不支持RDB/AOF 双机制
线程模型多线程(I/O 与工作线程分离)单线程(6.0+ I/O 多线程)
内存管理Slab 预分配动态分配 + 淘汰策略
分布式客户端分片原生 Cluster 支持
适用场景高频读写的简单缓存缓存/队列/实时统计等复杂场景

选型决策树
{纯缓存 & 高吞吐→Memcached需持久化/复杂结构→Redis \begin{cases} \text{纯缓存 \& 高吞吐} & \rightarrow \text{Memcached} \\ \text{需持久化/复杂结构} & \rightarrow \text{Redis} \end{cases} {纯缓存 & 高吞吐需持久化/复杂结构MemcachedRedis

五、分布式场景实践
  1. 一致性哈希算法

    • 虚拟节点解决数据倾斜
    • Java 定位逻辑:
      // FNV1_32_HASH 算法  
      int hash = (key.hashCode() >>> 16) ^ key.hashCode();  
      int index = Math.abs(hash % serverList.size());  
      
  2. 故障恢复

    • 进程级:通过 watchdog 自动重启
    • 数据级
      • 冷启动异步回填
      executor.submit(() -> loadHotDataToCache());  
      
  3. 数据一致性

    • 双删策略:
      1. 删除缓存 → 写数据库 → 延时删除缓存  
      
    • 设置较短过期时间(如 303030 秒)
总结

Memcached 通过 Slab 内存预分配多线程架构 实现百万级 QPS 吞吐,适用于简单键值缓存场景。关键优化点包括:

  1. 监控 evicted_items 及时扩容内存
  2. 启用 LRU Crawler 减少内存碎片
  3. 客户端一致性哈希分片保障分布式扩展性
  4. 与 Redis 形成互补架构(Memcached 作一级缓存)
思维导图

在这里插入图片描述


Memcached 技术原理详解

1. 技术原理

Memcached 基于分布式内存键值存储模型,核心逻辑如下:

  • 数据缓存流程
    应用先查询 Memcached,命中则返回数据;未命中时查询数据库,并将结果写入缓存(含空值缓存)
    应用请求
    Memcached 命中?
    返回缓存数据
    查询数据库
    数据写入Memcached
  • 内存管理
    使用 Slab Allocator 内存分配器预防碎片:
    • 将内存划分为不同大小的 Chunk(如 64B/128B/256B)
    • 数据按大小存入匹配的 Slab Class
    • 内存满时按 LRU(最近最少使用)算法剔除旧数据
2. 核心算法
  • 分布式定位:一致性哈希

    • 节点通过哈希函数映射到哈希环(如 hash(serverip)hash(server_ip)hash(serverip)
    • 数据键哈希值 hash(key)hash(key)hash(key) 顺时针定位到最近节点
    • 节点增减时仅影响相邻数据,最小化数据迁移
    // Java 伪代码:一致性哈希定位
    int serverIndex = consistentHash(key.hashCode(), serverList.size());
    
  • 哈希算法
    默认使用 CRC32FNV1 计算键的哈希值,确保均匀分布:
    hash(key)=CRC32(key)mod  server_count \text{hash}(key) = \text{CRC32}(key) \mod \text{server\_count} hash(key)=CRC32(key)modserver_count

3. 数据结构
  • 存储单元:Item 结构体
    struct item {
        uint32_t flags;   // 数据类型标识
        time_t exptime;   // 过期时间
        size_t nbytes;    // 数据长度
        char key[];       // 变长键
        char data[];      // 变长值
    };
    
  • 内存组织
    • Slab Class 数组:管理不同尺寸的 Chunk
    • LRU 链表数组:按访问时间排序的 Item 链表
    • 哈希表O(1)O(1)O(1) 时间复杂度定位键(分离链表法解决冲突)
4. 架构功能图
graph TB
  Client[客户端] -->|Set/Get| Proxy[代理层]
  subgraph Memcached 集群
    Proxy --> Node1[节点1]
    Proxy --> Node2[节点2]
    subgraph Node1
      Slab1[Slab Class 64B] --> LRU1[LRU链表]
      Slab2[Slab Class 128B] --> LRU2[LRU链表]
      HashTable[哈希表]
    end
  end
  Node1 --> DB[(数据库)]
5. 核心组件功能
组件功能说明
内存分配器通过 Slab 机制高效管理内存,减少碎片
LRU 驱逐器自动淘汰过期或低频数据(统计显示剔除过高需优化策略)
哈希索引快速定位键值对位置,哈希冲突时使用链表解决
网络模块基于事件驱动(如 libevent)处理高并发请求
分布式代理客户端或中间件实现一致性哈希路由(如采用 K8s Service 负载均衡)
6. 优缺点分析
优点缺点
高速读写(内存操作)无持久化(重启数据丢失)
水平扩展能力强不支持复杂数据结构(对比 Redis)
协议简单,多语言客户端支持内存满时高频剔除影响性能
减轻数据库压力无内置安全机制(需网络隔离)

Java 客户端实现示例

import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;

public class MemcachedDemo {
    public static void main(String[] args) throws Exception {
        // 1. 连接 Memcached 服务器
        MemcachedClient client = new MemcachedClient(
            new InetSocketAddress("localhost", 11211)
        );
        
        // 2. 存储数据(过期时间 60 秒)
        String key = "user:1001";
        String value = "{\"name\":\"Alice\",\"age\":28}";
        client.set(key, 60, value);  // 参数:键, 过期秒数, 值
        
        // 3. 查询数据
        Object cached = client.get(key);
        if (cached != null) {
            System.out.println("命中缓存: " + cached);
        } else {
            System.out.println("缓存未命中,从数据库加载...");
            // 模拟数据库查询
            String dbValue = fetchFromDB(key); 
            client.set(key, 60, dbValue); // 回填缓存
        }
        
        client.shutdown(); // 关闭连接
    }
    
    private static String fetchFromDB(String key) {
        // 实际项目此处访问数据库
        return "{\"name\":\"Bob\",\"age\":32}"; 
    }
}
代码注释说明
  1. 连接管理:通过 InetSocketAddress 指定服务器地址
  2. 数据写入set(key, expireTime, value) 设置键值对及过期时间
  3. 缓存查询get(key) 返回 Object 需判空处理未命中场景
  4. 回填策略:数据库查询后调用 set() 回填缓存(含空值)
  5. 资源释放:结束时调用 shutdown() 释放网络连接
性能优化建议
  1. 防缓存穿透:对空值设置短过期时间(如 5 秒)
  2. 内存配置:监控剔除率,若过高需增加内存或优化 Slab 分布
  3. 键设计:采用命名空间(如 product:123)避免冲突
  4. 客户端分片:使用一致性哈希减少节点变更时的数据迁移
思维导图

在这里插入图片描述


Memcached 深度解析

一、工作原理与内部机制
  1. 数据模型

    • 纯内存键值存储(Key-Value),键最大 250字节,值最大 1MB
    • 数据存储单位 item 结构:
      struct item {  
          uint32_t flags;    // 数据类型标识  
          time_t exptime;    // 绝对过期时间戳  
          size_t nbytes;     // 数据长度  
          char key[];        // 变长键(以\0结尾)  
          char data[];       // 变长值  
          struct item* next; // LRU链表指针  
      };  
      
  2. 内存管理(Slab Allocator)

    • 内存池划分
      • 将内存预分割为多个 Slab Class(如 64B/128B/256B)
      • 每个 Slab Class 包含多个相同大小的 Chunk
    • 分配流程
      1. 计算数据大小 size=sizeof(key)+sizeof(value)+32Bsize = sizeof(key) + sizeof(value) + 32Bsize=sizeof(key)+sizeof(value)+32B(元数据开销)
      2. 选择最小满足 sizesizesize 的 Slab Class
      3. 从对应 LRU 链表分配 Chunk
  3. 淘汰机制

    • LRU 算法:每个 Slab Class 维护独立 LRU 链表
    • 惰性删除:访问时检查过期时间,写入新数据时触发淘汰
    • LRU Crawler:后台线程定期扫描过期项(需开启 -o lru_crawler
  4. 哈希索引

    • 使用 链式哈希表(分离链表法解决冲突)
    • 哈希函数:默认 CRC32,可配置为 MurmurHash3
    • O(1)O(1)O(1) 时间复杂度定位数据
二、安装配置(Linux 示例)
# 1. 安装依赖  
sudo apt-get install libevent-dev  

# 2. 编译安装(最新版1.6.22)  
wget https://memcached.org/latest  
tar -zxvf memcached-1.6.22.tar.gz  
cd memcached-1.6.22  
./configure --prefix=/usr/local/memcached  
make && sudo make install  

# 3. 启动服务(分配2GB内存,启用LRU Crawler)  
/usr/local/memcached/bin/memcached -m 2048 -p 11211 -o lru_crawler,lru_maintainer -d  
三、与 Redis 核心对比
维度MemcachedRedis
数据结构仅 StringString/Hash/List/Set/SortedSet 等
持久化不支持RDB/AOF 两种机制
线程模型多线程(网络I/O与工作线程分离)单线程(6.0+支持I/O多线程)
内存管理Slab 预分配动态分配 + 淘汰策略
分布式客户端分片原生 Cluster 支持
适用场景高频读写的简单键值缓存缓存/消息队列/实时统计等复杂场景

选型建议

  • 纯缓存场景用 Memcached(性能更高)
  • 需复杂数据结构选 Redis(功能更丰富)
四、性能优化实践
  1. 解决高剔除率(Eviction)

    • 扩容内存:监控 evicted_items 指标,超过阈值时增加 -m 参数
    • 优化 Slab 分布:调整 slab_automove 参数自动平衡 Chunk 分布
    • 预热缓存:重启后主动加载热点数据
    # 查看Slab统计  
    echo "stats slabs" | nc localhost 11211  
    
  2. 网络优化

    • 启用 UDP 协议:-U 11211(减少连接开销)
    • 调整最大连接数:-c 4096(默认1024)
  3. 键设计规范

    • 使用命名空间:product:123:detail
    • 避免大Key:拆分超过 1KB 的值
五、分布式数据一致性
  1. 客户端分片方案

    • 采用 一致性哈希算法 定位节点
    • Java 实现代码:
      import com.google.common.hash.Hashing;  
      
      public class ConsistentHash {  
          // 虚拟节点数(解决分布不均)  
          private static final int VIRTUAL_NODES = 200;  
      
          public static String getServer(String key, List<String> servers) {  
              // 使用Guava的MurmurHash  
              int hash = Hashing.murmur3_32().hashUnencodedChars(key).asInt();  
              int index = (hash & Integer.MAX_VALUE) % servers.size();  
              return servers.get(index);  
          }  
      }  
      
  2. 数据同步问题

    • 最终一致性:Memcached 本身不保证,需业务层解决
    • 解决方案
      • 写数据库后双删缓存
      • 设置缓存较短过期时间(如 30秒)
六、故障恢复机制
  1. 进程崩溃恢复

    • 通过 watchdog 脚本 自动重启:
      while true; do  
          /usr/local/memcached/bin/memcached -m 2048 -p 11211  
          sleep 10  
      done  
      
  2. 数据恢复策略

    • 冷启动方案
      1. 空缓存期降级读数据库
      2. 异步线程回填热点数据
      // Java 回填示例  
      ExecutorService executor = Executors.newFixedThreadPool(4);  
      executor.submit(() -> warmUpCache());  
      
七、Java 项目选型标准
场景推荐方案理由
简单商品详情页缓存Memcached吞吐量 >150K QPS,延迟 <1ms
购物车数据存储Redis需 Hash 结构修改单个字段
分布式SessionRedis需持久化防止丢失
热点数据缓存Memcached纯内存操作性能极致
八、一致性哈希算法实现细节
  1. 算法流程

    • 物理节点映射
      nodePos=hash(serverip)%232 nodePos = hash(server_ip) \% 2^{32} nodePos=hash(serverip)%232
    • 虚拟节点:每个物理节点生成 NNN 个虚拟节点(如 192.168.1.1#1
    • 数据定位
      server=min⁡{nodePosi∣nodePosi≥hash(key)%232} server = \min\left\{ nodePos_i \mid nodePos_i \geq hash(key) \% 2^{32} \right\} server=min{nodePosinodePosihash(key)%232}
  2. Java 完整实现

import java.util.SortedMap;  
import java.util.TreeMap;  

public class ConsistentHash {  
    private final SortedMap<Integer, String> ring = new TreeMap<>();  
    private final int virtualNodes;  

    public ConsistentHash(int virtualNodes) {  
        this.virtualNodes = virtualNodes;  
    }  

    // 添加节点  
    public void addNode(String node) {  
        for (int i = 0; i < virtualNodes; i++) {  
            int hash = hash(node + "#" + i);  
            ring.put(hash, node);  
        }  
    }  

    // 定位节点  
    public String getNode(String key) {  
        if (ring.isEmpty()) return null;  
        int hash = hash(key);  
        SortedMap<Integer, String> tailMap = ring.tailMap(hash);  
        int nodeHash = tailMap.isEmpty() ? ring.firstKey() : tailMap.firstKey();  
        return ring.get(nodeHash);  
    }  

    // FNV1_32_HASH 算法  
    private int hash(String key) {  
        final int p = 16777619;  
        int hash = (int) 2166136261L;  
        for (int i = 0; i < key.length(); i++) {  
            hash = (hash ^ key.charAt(i)) * p;  
        }  
        hash += hash << 13;  
        hash ^= hash >> 7;  
        hash += hash << 3;  
        hash ^= hash >> 17;  
        hash += hash << 5;  
        return hash < 0 ? Math.abs(hash) : hash;  
    }  
}  

关键优化

  1. 虚拟节点解决数据倾斜(推荐 virtualNodes=160virtualNodes=160virtualNodes=160
  2. 采用 FNV 哈希算法保证均匀性
总结

Memcached 作为高性能分布式缓存的基石,其核心价值在于 简单场景下的极致性能。通过 Slab 内存管理、LRU 淘汰机制和一致性哈希分片,可实现百万级 QPS 的吞吐能力。在 Java 项目中,建议:

  1. 纯缓存场景优先选择 Memcached
  2. 需复杂操作时结合 Redis 使用
  3. 通过客户端分片和冷启动方案保障高可用
思维导图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值