Memcached教程FG009-Memcached高可用架构设计与实战
本文档风哥主要介绍Memcached数据库高可用架构设计与实战相关知识,包括Memcached数据库高可用概述、高可用架构模式、高可用策略、高可用规划、故障转移规划、容灾规划、客户端高可用实战、代理高可用实战、集群高可用实战等内容,风哥教程参考Memcached官方文档High Availability等内容编写,适合DBA人员和运维人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 Memcached数据库高可用概述
Memcached作为分布式缓存系统,其高可用设计与传统数据库有所不同。更多视频教程www.fgedu.net.cn
1.1.1 Memcached高可用特点
# 1. 无主架构
– 所有节点平等
– 无主从复制
– 无同步机制
# 2. 客户端驱动
– 客户端负责数据分布
– 客户端处理故障转移
– 客户端管理服务器列表
# 3. 数据不持久化
– 数据存储在内存中
– 重启后数据丢失
– 需要从数据源重新加载
# 4. 缓存特性
– 允许数据丢失
– 可从数据库恢复
– 对一致性要求相对较低
# 高可用目标
1. 服务连续性:单节点故障不影响整体服务
2. 数据可恢复:故障后能快速恢复缓存
3. 透明切换:客户端自动切换到健康节点
4. 快速恢复:故障节点快速恢复服务
1.1.2 高可用指标
# 可用性指标
– 可用性:99.9% ~ 99.99%
– 年停机时间:52分钟 ~ 5分钟
– 故障恢复时间:秒级
# 性能指标
– 缓存命中率:> 90%
– 响应时间:< 10ms
- 吞吐量:> 10000 QPS
# 容错指标
– 单节点故障:自动切换
– 多节点故障:降级服务
– 机房故障:跨机房切换
# 高可用计算公式
可用性 = (总时间 – 故障时间) / 总时间 * 100%
# 示例计算
年度总时间 = 365 * 24 * 60 = 525600 分钟
99.9% 可用性允许故障时间 = 525600 * 0.1% = 525.6 分钟
99.99% 可用性允许故障时间 = 525600 * 0.01% = 52.56 分钟
1.2 Memcached数据库高可用架构模式
Memcached高可用架构主要有以下几种模式。学习交流加群风哥微信: itpux-com
1.2.1 客户端高可用模式
# 架构图
┌─────────────────────────────────────────────────────────────┐
│ 应用服务器集群 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ App1 │ │ App2 │ │ App3 │ │ App4 │ │
│ │ Client │ │ Client │ │ Client │ │ Client │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼────────────┼────────────┼────────────┼─────────────┘
│ │ │ │
└────────────┴────────────┴────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Memcached │ │ Memcached │ │ Memcached │
│ Server 1 │ │ Server 2 │ │ Server 3 │
│ 192.168.1.101 │ │ 192.168.1.102 │ │ 192.168.1.103 │
└───────────────┘ └───────────────┘ └───────────────┘
# 工作原理
1. 客户端维护服务器列表
2. 使用一致性哈希分布数据
3. 检测节点故障并自动切换
4. 故障节点恢复后自动加入
# 优点
– 架构简单
– 无额外组件
– 性能开销小
# 缺点
– 客户端逻辑复杂
– 故障时部分数据丢失
– 需要客户端支持
1.2.2 代理高可用模式
# 架构图
┌─────────────────────────────────────────────────────────────┐
│ 应用服务器集群 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ App1 │ │ App2 │ │ App3 │ │ App4 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼────────────┼────────────┼────────────┼─────────────┘
│ │ │ │
└────────────┴────────────┴────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 代理层(Twemproxy/Mcrouter) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Proxy 1 │ │ Proxy 2 │ │
│ │ (主/备/负载) │ │ (主/备/负载) │ │
│ └────────┬────────┘ └────────┬────────┘ │
└───────────┼────────────────────┼───────────────────────────┘
│ │
┌───┴───┐ ┌───┴───┐
│ │ │ │
▼ ▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│Memcached │ │Memcached │ │Memcached │ │Memcached │
│ Server 1 │ │ Server 2 │ │ Server 3 │ │ Server 4 │
└───────────┘ └───────────┘ └───────────┘ └───────────┘
# 常用代理
1. Twemproxy(nutcracker)
– Twitter开源
– 支持一致性哈希
– 支持多Memcached集群
2. Mcrouter
– Facebook开源
– 支持更复杂的路由
– 支持复制和故障转移
# 优点
– 客户端简单
– 统一管理
– 支持更复杂的路由策略
# 缺点
– 增加代理层开销
– 代理成为单点(需高可用)
– 增加运维复杂度
1.2.3 双写复制模式
# 架构图
┌─────────────────────────────────────────────────────────────┐
│ 应用服务器 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 写入逻辑 │ │
│ │ 写入主集群 ────────────────── 写入备集群 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌──────────────────┴──────────────────┐
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ 主集群 │ │ 备集群 │
│ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │
│ │MC-1 │ │MC-2 │ │ 异步复制 │ │MC-3 │ │MC-4 │ │
│ └─────┘ └─────┘ │ ◄──────────── │ └─────┘ └─────┘ │
│ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │
│ │MC-3 │ │MC-4 │ │ │ │MC-7 │ │MC-8 │ │
│ └─────┘ └─────┘ │ │ └─────┘ └─────┘ │
└───────────────────┘ └───────────────────┘
# 工作原理
1. 写入时同时写入主备集群
2. 读取时优先从主集群读取
3. 主集群故障时切换到备集群
# 实现方式
1. 客户端双写
2. 代理层复制(Mcrouter)
3. 应用层实现
# 优点
– 数据冗余
– 快速切换
– 数据不丢失
# 缺点
– 写入性能降低
– 实现复杂
– 成本增加
1.3 Memcached数据库高可用策略
1.3.1 故障检测策略
# 1. 客户端心跳检测
– 定期发送version命令
– 检测响应时间
– 标记故障节点
# 2. 连接超时检测
– 设置连接超时时间
– 超时后标记故障
– 自动重连机制
# 3. 操作失败检测
– 操作失败计数
– 达到阈值标记故障
– 自动切换节点
# 故障检测参数
连接超时:1000-3000ms
操作超时:3000-5000ms
重试次数:2-3次
故障阈值:3次连续失败
# 故障检测流程
┌─────────────────────────────────────────────────────────────┐
│ 开始检测 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 发送检测请求(version/stats) │
└─────────────────────┬───────────────────────────────────────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌───────────┐ ┌───────────┐
│ 成功 │ │ 失败 │
│ 重置计数 │ │ 增加计数 │
└───────────┘ └─────┬─────┘
│
▼
┌─────────────────┐
│ 计数 >= 阈值? │
└────────┬────────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌───────────┐ ┌───────────┐
│ 是 │ │ 否 │
│ 标记故障 │ │ 继续 │
│ 移除节点 │ │ 检测 │
└───────────┘ └───────────┘
1.3.2 故障转移策略
# 1. 自动切换
– 故障节点自动移除
– 请求路由到其他节点
– 对应用透明
# 2. 降级服务
– 部分节点故障时
– 降低缓存命中率
– 从数据库读取
# 3. 快速恢复
– 故障节点恢复后
– 自动重新加入
– 预热缓存数据
# 故障转移流程
┌─────────────────────────────────────────────────────────────┐
│ 检测到节点故障 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 从服务器列表移除故障节点 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 更新一致性哈希环 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 后续请求路由到健康节点 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 发送告警通知 │
└─────────────────────────────────────────────────────────────┘
# 恢复流程
┌─────────────────────────────────────────────────────────────┐
│ 节点恢复 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 健康检查通过 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 重新加入服务器列表 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 更新一致性哈希环 │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 预热热点数据(可选) │
└─────────────────────────────────────────────────────────────┘
Part02-生产环境规划与建议
2.1 Memcached数据库高可用规划
合理的高可用规划是保障系统稳定运行的关键。学习交流加群风哥QQ113257174
2.1.1 节点规划
# 最小节点数
– 开发环境:2节点
– 测试环境:2节点
– 生产环境:4节点以上
# 推荐节点数
– 小型系统:4节点
– 中型系统:6-8节点
– 大型系统:10节点以上
# 节点配置建议
┌─────────────────────────────────────────────────────────────┐
│ 生产环境节点规划 │
├─────────────────────────────────────────────────────────────┤
│ 节点数量:4-8节点 │
│ 单节点内存:32-64GB │
│ 总内存:128-512GB │
│ 网络:千兆/万兆 │
│ 机架分布:至少2个机架 │
└─────────────────────────────────────────────────────────────┘
# 节点分布原则
1. 分散在不同机架
2. 分散在不同交换机
3. 分散在不同电源
4. 考虑跨机房部署
# 示例:8节点生产环境
节点1: 192.168.1.101 (机架A, 交换机1)
节点2: 192.168.1.102 (机架A, 交换机2)
节点3: 192.168.1.103 (机架B, 交换机1)
节点4: 192.168.1.104 (机架B, 交换机2)
节点5: 192.168.1.105 (机架A, 交换机1)
节点6: 192.168.1.106 (机架A, 交换机2)
节点7: 192.168.1.107 (机架B, 交换机1)
节点8: 192.168.1.108 (机架B, 交换机2)
2.1.2 容量规划
# 内存容量计算
总内存需求 = 数据量 * (1 + 冗余系数)
冗余系数 = 20%-50%(用于应对数据增长)
# 示例计算
业务数据量:100GB
冗余系数:30%
总内存需求 = 100GB * 1.3 = 130GB
# 单节点内存
单节点内存 = 总内存需求 / 节点数
单节点内存 = 130GB / 4 = 32.5GB
取整:32GB/节点
# 连接数规划
单节点最大连接数 = 应用服务器数 * 每服务器连接数
示例:20台应用服务器 * 10连接 = 200连接
建议配置:-c 2048 或更高
# 网络带宽规划
峰值QPS:100000
平均Value大小:1KB
网络带宽 = QPS * Value大小 = 100000 * 1KB = 100MB/s
建议:千兆网络或更高
2.2 Memcached数据库故障转移规划
2.2.1 故障转移策略规划
# 故障分类
1. 单节点故障
– 影响:部分缓存丢失
– 处理:自动切换到其他节点
– 恢复:重启或替换节点
2. 多节点故障
– 影响:大量缓存丢失
– 处理:降级服务,从数据库读取
– 恢复:逐个恢复节点
3. 机房故障
– 影响:整个机房不可用
– 处理:切换到备用机房
– 恢复:机房恢复后同步数据
# 故障转移时间目标
单节点故障:< 10秒
多节点故障:< 30秒
机房故障:< 5分钟
# 故障转移配置
# 客户端配置(PHP示例)
$memcached->setOptions([
// 故障检测
Memcached::OPT_CONNECT_TIMEOUT => 1000, // 连接超时1秒
Memcached::OPT_RETRY_TIMEOUT => 1, // 重试间隔1秒
Memcached::OPT_SERVER_FAILURE_LIMIT => 2, // 2次失败标记故障
// 故障转移
Memcached::OPT_AUTO_EJECT_HOSTS => true, // 自动剔除故障主机
]);
2.2.2 故障恢复规划
# 恢复流程
1. 故障诊断
– 检查服务状态
– 检查系统日志
– 检查资源使用
2. 故障修复
– 重启服务
– 修复配置
– 替换硬件
3. 服务恢复
– 健康检查
– 重新加入集群
– 预热缓存
# 恢复脚本示例
#!/bin/bash
# recovery.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
NODE_IP=”192.168.1.101″
NODE_PORT=”11211″
echo “开始恢复节点: $NODE_IP:$NODE_PORT”
# 1. 检查服务状态
if systemctl is-active –quiet memcached; then
echo “服务正在运行”
else
echo “服务未运行,尝试启动…”
systemctl start memcached
fi
# 2. 等待服务就绪
sleep 5
# 3. 健康检查
if echo “stats” | nc -w 2 $NODE_IP $NODE_PORT | grep -q “version”; then
echo “节点健康检查通过”
else
echo “节点健康检查失败”
exit 1
fi
# 4. 预热热点数据(可选)
echo “预热热点数据…”
# 执行预热脚本
echo “节点恢复完成”
2.3 Memcached数据库容灾规划
2.3.1 跨机房容灾规划
# 架构模式
┌─────────────────────────────────────────────────────────────┐
│ 负载均衡 │
│ (DNS/GSLB/F5) │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ 机房A(主) │ │ 机房B(备) │
│ │ │ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ 应用集群 │ │ │ │ 应用集群 │ │
│ └───────────────┘ │ │ └───────────────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ Memcached集群 │ │ │ │ Memcached集群 │ │
│ │ MC1 MC2 │ │ │ │ MC3 MC4 │ │
│ │ MC3 MC4 │ │ │ │ MC7 MC8 │ │
│ └───────────────┘ │ │ └───────────────┘ │
│ │ │ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ 数据库 │◄├───────┼►│ 数据库 │ │
│ └───────────────┘ │ 同步 │ └───────────────┘ │
└───────────────────┘ └───────────────────┘
# 容灾策略
1. 主备模式
– 主机房正常服务
– 备机房待命
– 故障时切换
2. 双活模式
– 两机房同时服务
– 数据双写
– 故障时降级
# 切换策略
– 自动切换:GSLB自动检测并切换
– 手动切换:运维人员手动切换
– 半自动:检测告警后人工确认切换
# 切换时间目标
检测时间:< 1分钟
决策时间:< 2分钟
切换时间:< 2分钟
总恢复时间:< 5分钟
2.3.2 数据备份与恢复规划
# 备份策略
Memcached数据存储在内存中,重启后丢失
但可以备份热点Key列表和预热脚本
# 备份内容
1. 热点Key列表
2. 配置文件
3. 启动脚本
4. 预热脚本
# 备份脚本示例
#!/bin/bash
# backup_hotkeys.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
BACKUP_DIR=”/backup/memcached”
DATE=$(date +%Y%m%d)
BACKUP_FILE=”$BACKUP_DIR/hotkeys_$DATE.txt”
mkdir -p $BACKUP_DIR
# 获取所有Key
echo “stats cachedump 1 0” | nc 192.168.1.101 11211 | \
awk ‘{print $2}’ > $BACKUP_FILE
echo “热点Key备份完成: $BACKUP_FILE”
# 恢复策略
1. 启动Memcached服务
2. 执行预热脚本
3. 从数据库加载热点数据
4. 验证缓存命中率
# 预热脚本示例
#!/bin/bash
# warmup.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
HOTKEYS_FILE=”/backup/memcached/hotkeys_latest.txt”
DB_HOST=”192.168.1.200″
MC_HOST=”192.168.1.101″
MC_PORT=”11211″
echo “开始预热缓存…”
while read key; do
# 从数据库获取数据
value=$(mysql -h $DB_HOST -N -e “SELECT data FROM cache WHERE key=’$key'”)
# 写入Memcached
if [ -n “$value” ]; then
echo -e “set $key 0 3600 ${#value}\n$value” | nc $MC_HOST $MC_PORT
fi
done < $HOTKEYS_FILE
echo "预热完成"
Part03-生产环境项目实施方案
3.1 Memcached数据库客户端高可用实战
3.1.1 客户端故障检测实现
servers = $servers;
$this->memcached = new Memcached(‘ha_pool’);
if (!count($this->memcached->getServerList())) {
$this->memcached->addServers($servers);
}
// 配置故障检测
$this->memcached->setOptions([
Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
Memcached::OPT_CONNECT_TIMEOUT => 1000,
Memcached::OPT_SEND_TIMEOUT => 2000,
Memcached::OPT_RECV_TIMEOUT => 2000,
Memcached::OPT_RETRY_TIMEOUT => 1,
Memcached::OPT_SERVER_FAILURE_LIMIT => 2,
Memcached::OPT_AUTO_EJECT_HOSTS => true,
]);
// 启动后台健康检查
$this->startHealthCheck();
}
/**
* 获取数据(带故障转移)
*/
public function get($key) {
$result = $this->memcached->get($key);
if ($result === false) {
$code = $this->memcached->getResultCode();
// 如果是连接错误,尝试故障转移
if ($code == Memcached::RES_CONNECTION_FAILURE ||
$code == Memcached::RES_TIMEOUT) {
$this->handleFailure();
// 重试一次
$result = $this->memcached->get($key);
}
}
return $result;
}
/**
* 存储数据
*/
public function set($key, $value, $expire = 3600) {
$result = $this->memcached->set($key, $value, $expire);
if (!$result) {
$code = $this->memcached->getResultCode();
if ($code == Memcached::RES_CONNECTION_FAILURE) {
$this->handleFailure();
$result = $this->memcached->set($key, $value, $expire);
}
}
return $result;
}
/**
* 处理故障
*/
private function handleFailure() {
$stats = $this->memcached->getStats();
foreach ($stats as $server => $info) {
if (empty($info) || !isset($info[‘version’])) {
$this->markServerFailed($server);
}
}
$this->sendAlert();
}
/**
* 标记服务器故障
*/
private function markServerFailed($server) {
if (!in_array($server, $this->failedServers)) {
$this->failedServers[] = $server;
error_log(“Memcached节点故障: $server”);
}
}
/**
* 发送告警
*/
private function sendAlert() {
if (!empty($this->failedServers)) {
$message = “Memcached故障节点: ” . implode(‘, ‘, $this->failedServers);
// 发送邮件/短信告警
error_log($message);
}
}
/**
* 启动健康检查
*/
private function startHealthCheck() {
// 在实际生产中,可以使用进程管理工具启动后台检查
// 这里简化为每次操作时检查
}
/**
* 健康检查
*/
public function healthCheck() {
$stats = $this->memcached->getStats();
$healthy = [];
$unhealthy = [];
foreach ($stats as $server => $info) {
if (!empty($info) && isset($info[‘version’])) {
$healthy[] = $server;
// 如果之前标记为故障,现在恢复了
if (in_array($server, $this->failedServers)) {
$this->markServerRecovered($server);
}
} else {
$unhealthy[] = $server;
}
}
return [
‘healthy’ => $healthy,
‘unhealthy’ => $unhealthy,
];
}
/**
* 标记服务器恢复
*/
private function markServerRecovered($server) {
$key = array_search($server, $this->failedServers);
if ($key !== false) {
unset($this->failedServers[$key]);
error_log(“Memcached节点恢复: $server”);
}
}
}
// 使用示例
$mc = new MemcachedHA([
[‘192.168.1.101’, 11211],
[‘192.168.1.102’, 11211],
[‘192.168.1.103’, 11211],
[‘192.168.1.104’, 11211],
]);
// 存储数据
$mc->set(‘fgedu_test’, ‘value’, 3600);
// 获取数据
$value = $mc->get(‘fgedu_test’);
echo “获取值: $value\n”;
// 健康检查
$health = $mc->healthCheck();
print_r($health);
?>
3.1.2 Java客户端高可用实现
package com.fgedu.cache.ha;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.utils.AddrUtil;
import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.*;
public class MemcachedHAService {
private MemcachedClient client;
private Set
private ScheduledExecutorService scheduler;
public MemcachedHAService(List
// 创建客户端
MemcachedClientBuilder builder = new XMemcachedClientBuilder(
AddrUtil.getAddresses(String.join(” “, servers))
);
// 配置高可用参数
builder.setSessionLocator(new KetamaMemcachedSessionLocator());
builder.setConnectionPoolSize(5);
builder.setConnectTimeout(3000);
builder.setOpTimeout(5000);
builder.setFailureMode(true);
builder.setHealSessionInterval(5000);
this.client = builder.build();
// 启动健康检查
startHealthCheck();
}
/**
* 获取数据
*/
public
try {
return client.get(key);
} catch (Exception e) {
handleException(e);
try {
return client.get(key);
} catch (Exception ex) {
return null;
}
}
}
/**
* 存储数据
*/
public boolean set(String key, int expire, Object value) {
try {
return client.set(key, expire, value);
} catch (Exception e) {
handleException(e);
try {
return client.set(key, expire, value);
} catch (Exception ex) {
return false;
}
}
}
/**
* 处理异常
*/
private void handleException(Exception e) {
// 记录故障
System.err.println(“Memcached操作异常: ” + e.getMessage());
}
/**
* 启动健康检查
*/
private void startHealthCheck() {
scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
try {
checkHealth();
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 10, TimeUnit.SECONDS);
}
/**
* 健康检查
*/
private void checkHealth() {
Map
for (Map.Entry
String server = entry.getKey().toString();
Map
if (info == null || info.isEmpty()) {
failedServers.add(server);
sendAlert(server, “故障”);
} else {
if (failedServers.remove(server)) {
sendAlert(server, “恢复”);
}
}
}
}
/**
* 发送告警
*/
private void sendAlert(String server, String status) {
System.out.println(“告警: 服务器 ” + server + ” ” + status);
// 实现邮件/短信告警
}
/**
* 关闭
*/
public void shutdown() {
scheduler.shutdown();
client.shutdown();
}
}
3.2 Memcached数据库代理高可用实战
3.2.1 Twemproxy配置实战
# 1. 安装Twemproxy
$ sudo dnf install -y autoconf automake libtool
$ cd /tmp
$ git clone https://github.com/twitter/twemproxy.git
$ cd twemproxy
$ autoreconf -fvi
$ ./configure –prefix=/memcached/app/twemproxy
$ make -j$(nproc)
$ sudo make install
# 2. 配置文件
$ sudo vi /memcached/app/twemproxy/conf/nutcracker.yml
fgedu_memcached:
listen: 0.0.0.0:22122
hash: fnv1a_64
distribution: ketama
timeout: 500
backlog: 1024
preconnect: true
auto_eject_hosts: true
server_retry_timeout: 30000
server_failure_limit: 3
servers:
– 192.168.1.101:11211:1
– 192.168.1.102:11211:1
– 192.168.1.103:11211:1
– 192.168.1.104:11211:1
# 3. 启动Twemproxy
$ /memcached/app/twemproxy/sbin/nutcracker \
-c /memcached/app/twemproxy/conf/nutcracker.yml \
-p /memcached/app/twemproxy/logs/nutcracker.pid \
-d
# 4. 验证
$ telnet 127.0.0.1 22122
stats
# 输出统计信息
# 5. 配置Systemd服务
$ sudo vi /etc/systemd/system/twemproxy.service
[Unit]
Description=Twemproxy (Nutcracker)
After=network.target
[Service]
Type=simple
ExecStart=/memcached/app/twemproxy/sbin/nutcracker \
-c /memcached/app/twemproxy/conf/nutcracker.yml
Restart=on-failure
[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl enable twemproxy
$ sudo systemctl start twemproxy
# 6. 客户端连接
# 客户端连接Twemproxy端口22122,而不是直接连接Memcached
$memcached = new Memcached();
$memcached->addServer(‘127.0.0.1’, 22122);
3.2.2 Twemproxy高可用部署
# 架构:使用Keepalived实现Twemproxy高可用
# 节点规划
Twemproxy主: 192.168.1.10
Twemproxy备: 192.168.1.11
VIP: 192.168.1.100
# 1. 安装Keepalived
$ sudo dnf install -y keepalived
# 2. 主节点配置
$ sudo vi /etc/keepalived/keepalived.conf
vrrp_script check_twemproxy {
script “/memcached/app/twemproxy/scripts/check_twemproxy.sh”
interval 3
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass fgedu1234
}
virtual_ipaddress {
192.168.1.100
}
track_script {
check_twemproxy
}
}
# 3. 备节点配置
$ sudo vi /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass fgedu1234
}
virtual_ipaddress {
192.168.1.100
}
}
# 4. 健康检查脚本
$ sudo vi /memcached/app/twemproxy/scripts/check_twemproxy.sh
#!/bin/bash
# check_twemproxy.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
if ! pgrep -x “nutcracker” > /dev/null; then
exit 1
fi
if ! echo “stats” | nc -w 2 127.0.0.1 22122 | grep -q “total_connections”; then
exit 1
fi
exit 0
$ sudo chmod +x /memcached/app/twemproxy/scripts/check_twemproxy.sh
# 5. 启动服务
$ sudo systemctl enable keepalived
$ sudo systemctl start keepalived
# 6. 验证VIP
$ ip addr show eth0 | grep 192.168.1.100
# 7. 客户端连接VIP
$memcached = new Memcached();
$memcached->addServer(‘192.168.1.100’, 22122);
3.3 Memcached数据库集群高可用实战
3.3.1 集群监控脚本
#!/bin/bash
# cluster_monitor.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
# 集群节点列表
NODES=(
“192.168.1.101:11211”
“192.168.1.102:11211”
“192.168.1.103:11211”
“192.168.1.104:11211”
)
# 告警阈值
MEMORY_THRESHOLD=90
HITRATE_THRESHOLD=80
# 告警函数
send_alert() {
local message=$1
echo “[ALERT] $(date ‘+%Y-%m-%d %H:%M:%S’) $message”
# 这里可以添加邮件/短信告警逻辑
}
# 检查节点健康
check_node() {
local node=$1
local host=$(echo $node | cut -d: -f1)
local port=$(echo $node | cut -d: -f2)
# 检查连接
if ! echo “quit” | nc -w 2 $host $port > /dev/null 2>&1; then
send_alert “节点 $node 无法连接”
return 1
fi
# 获取统计信息
local stats=$(echo “stats” | nc $host $port)
# 检查内存使用率
local bytes=$(echo “$stats” | grep “^STAT bytes” | awk ‘{print $3}’)
local limit=$(echo “$stats” | grep “limit_maxbytes” | awk ‘{print $3}’)
if [ -n “$bytes” ] && [ -n “$limit” ] && [ $limit -gt 0 ]; then
local usage=$((bytes * 100 / limit))
if [ $usage -gt $MEMORY_THRESHOLD ]; then
send_alert “节点 $node 内存使用率过高: ${usage}%”
fi
fi
# 检查命中率
local hits=$(echo “$stats” | grep “get_hits” | awk ‘{print $3}’)
local misses=$(echo “$stats” | grep “get_misses” | awk ‘{print $3}’)
if [ -n “$hits” ] && [ -n “$misses” ]; then
local total=$((hits + misses))
if [ $total -gt 0 ]; then
local hitrate=$((hits * 100 / total))
if [ $hitrate -lt $HITRATE_THRESHOLD ]; then
send_alert “节点 $node 命中率过低: ${hitrate}%”
fi
fi
fi
# 检查淘汰次数
local evictions=$(echo “$stats” | grep “^STAT evictions” | awk ‘{print $3}’)
if [ -n “$evictions” ] && [ $evictions -gt 10000 ]; then
send_alert “节点 $node 淘汰次数过多: $evictions”
fi
return 0
}
# 主检查循环
echo “开始检查Memcached集群…”
echo “================================”
healthy_count=0
unhealthy_count=0
for node in “${NODES[@]}”; do
echo “检查节点: $node”
if check_node $node; then
echo ” 状态: 健康”
((healthy_count++))
else
echo ” 状态: 异常”
((unhealthy_count++))
fi
done
echo “================================”
echo “检查完成: 健康 $healthy_count 个, 异常 $unhealthy_count 个”
# 如果异常节点过多,发送告警
if [ $unhealthy_count -gt $((${#NODES[@]} / 2)) ]; then
send_alert “集群异常节点过多,请立即处理!”
fi
3.3.2 自动故障恢复脚本
#!/bin/bash
# auto_recovery.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
# 节点列表
NODES=(
“192.168.1.101:11211”
“192.168.1.102:11211”
“192.168.1.103:11211”
“192.168.1.104:11211”
)
# 恢复函数
recover_node() {
local node=$1
local host=$(echo $node | cut -d: -f1)
echo “尝试恢复节点: $node”
# SSH到节点执行恢复
ssh $host << 'EOF'
# 检查服务状态
if ! systemctl is-active --quiet memcached; then
echo "服务未运行,尝试启动..."
systemctl start memcached
sleep 5
fi
# 检查服务是否启动成功
if systemctl is-active --quiet memcached; then
echo "服务启动成功"
exit 0
else
echo "服务启动失败"
exit 1
fi
EOF
return $?
}
# 预热函数
warmup_node() {
local node=$1
local host=$(echo $node | cut -d: -f1)
local port=$(echo $node | cut -d: -f2)
echo "预热节点: $node"
# 加载热点数据
# 这里可以根据实际情况实现预热逻辑
echo "预热完成"
}
# 主循环
while true; do
for node in "${NODES[@]}"; do
host=$(echo $node | cut -d: -f1)
port=$(echo $node | cut -d: -f2)
# 检查节点
if ! echo "quit" | nc -w 2 $host $port > /dev/null 2>&1; then
echo “节点 $node 故障”
# 尝试恢复
if recover_node $node; then
echo “节点 $node 恢复成功”
warmup_node $node
else
echo “节点 $node 恢复失败,发送告警”
# 发送告警
fi
fi
done
sleep 30
done
Part04-生产案例与实战讲解
4.1 Memcached数据库故障转移案例
以下是一个真实的故障转移案例。更多学习教程公众号风哥教程itpux_com
4.1.1 故障场景
# 环境
– 4节点Memcached集群
– 客户端使用一致性哈希
– 故障节点:192.168.1.103
# 故障现象
$ echo “stats” | nc 192.168.1.103 11211
Ncat: Connection refused.
# 客户端日志
Memcached节点故障: 192.168.1.103:11211
自动切换到其他节点
# 影响分析
– 故障节点上的缓存数据丢失
– 约25%的缓存需要重新加载
– 缓存命中率暂时下降
# 客户端自动处理
# PHP客户端配置
$memcached->setOptions([
Memcached::OPT_AUTO_EJECT_HOSTS => true,
Memcached::OPT_SERVER_FAILURE_LIMIT => 2,
]);
# 故障节点被自动移除
# 请求自动路由到其他健康节点
4.1.2 故障处理过程
# 1. 故障检测(自动)
时间:10:00:00
事件:检测到节点192.168.1.103无法连接
处理:自动移除故障节点
# 2. 告警通知(自动)
时间:10:00:01
事件:发送告警邮件/短信
内容:Memcached节点192.168.1.103故障
# 3. 故障诊断(人工)
时间:10:00:30
操作:登录故障节点检查
$ ssh 192.168.1.103
$ systemctl status memcached
● memcached.service – Memcached Daemon
Active: failed (Result: exit-code)
$ journalctl -u memcached -n 50
# 查看日志,发现内存不足导致服务崩溃
# 4. 故障修复
时间:10:02:00
操作:增加内存限制并重启服务
$ sudo vi /etc/systemd/system/memcached.service
# 修改内存限制
$ sudo systemctl daemon-reload
$ sudo systemctl start memcached
# 5. 验证恢复
时间:10:02:30
操作:验证服务正常
$ echo “stats” | nc 192.168.1.103 11211 | head -5
STAT pid 12345
STAT uptime 10
STAT time 1712534400
STAT version 1.6.22
# 6. 节点重新加入
时间:10:03:00
操作:客户端自动检测到节点恢复
客户端日志:
Memcached节点恢复: 192.168.1.103:11211
# 7. 预热缓存
时间:10:03:30
操作:执行预热脚本
$ /memcached/scripts/warmup.sh 192.168.1.103
预热完成,加载热点数据10000条
# 总恢复时间:约3分钟
4.2 Memcached数据库故障恢复案例
4.2.1 多节点故障恢复
# 故障场景
– 2个节点同时故障(192.168.1.103, 192.168.1.104)
– 剩余2个节点继续服务
– 约50%缓存丢失
# 处理步骤
# 1. 确认故障范围
$ for node in 192.168.1.101 192.168.1.102 192.168.1.103 192.168.1.104; do
echo “=== $node ===”
echo “stats” | nc $node 11211 | grep version || echo “无法连接”
done
=== 192.168.1.101 ===
STAT version 1.6.22
=== 192.168.1.102 ===
STAT version 1.6.22
=== 192.168.1.103 ===
无法连接
=== 192.168.1.104 ===
无法连接
# 2. 检查剩余节点负载
$ echo “stats” | nc 192.168.1.101 11211 | grep -E “curr_items|bytes”
STAT curr_items 50000
STAT bytes 25000000000
# 内存使用率上升,需要监控
# 3. 逐个恢复故障节点
# 恢复节点1
$ ssh 192.168.1.103 “systemctl start memcached”
$ sleep 30
$ echo “stats” | nc 192.168.1.103 11211 | grep version
STAT version 1.6.22
# 预热节点1
$ /memcached/scripts/warmup.sh 192.168.1.103
# 等待节点1稳定后再恢复节点2
$ sleep 60
# 恢复节点2
$ ssh 192.168.1.104 “systemctl start memcached”
$ /memcached/scripts/warmup.sh 192.168.1.104
# 4. 验证集群状态
$ /memcached/scripts/cluster_monitor.sh
检查完成: 健康 4 个, 异常 0 个
# 5. 监控缓存命中率
# 观察一段时间,确保命中率恢复正常
4.3 Memcached数据库容灾切换案例
4.3.1 跨机房切换案例
# 环境
– 主机房:机房A(192.168.1.0/24)
– 备机房:机房B(192.168.2.0/24)
– GSLB实现自动切换
# 切换场景
主机房网络故障,需要切换到备机房
# 切换步骤
# 1. 检测故障
时间:10:00:00
事件:GSLB检测到主机房不可用
# 2. 自动切换DNS
时间:10:00:30
操作:GSLB将域名解析到备机房IP
# 3. 应用切换
时间:10:01:00
操作:应用连接到备机房Memcached
# 备机房Memcached配置
$ cat /etc/memcached/servers.conf
192.168.2.101:11211
192.168.2.102:11211
192.168.2.103:11211
192.168.2.104:11211
# 4. 预热备机房缓存
时间:10:02:00
操作:从数据库加载热点数据
$ /memcached/scripts/warmup_all.sh
预热完成,加载热点数据50000条
# 5. 验证服务
时间:10:05:00
操作:验证服务正常
$ curl http://app.fgedu.net.cn/api/test
{“status”: “ok”, “cache”: “hit”}
# 6. 监控指标
– 缓存命中率:从0%逐渐恢复到90%
– 响应时间:先升后降
– 数据库负载:先升后降
# 总切换时间:约5分钟
# 关键:备机房缓存预热是关键
Part05-风哥经验总结与分享
5.1 Memcached数据库高可用最佳实践
5.1.1 架构设计建议
- 节点数量建议4个以上,分散故障风险
- 使用客户端高可用模式,简单高效
- 配置自动故障检测和转移
- 实现完善的监控告警系统
- 制定详细的故障恢复流程
5.1.2 配置建议
# 1. 客户端配置
– 启用一致性哈希
– 配置故障检测参数
– 启用自动剔除故障主机
# 2. 服务端配置
– 合理设置内存大小
– 配置足够的连接数
– 启用LRU Crawler
# 3. 监控配置
– 监控节点健康状态
– 监控内存使用率
– 监控缓存命中率
– 监控淘汰次数
# 4. 告警配置
– 节点故障告警
– 内存使用率告警
– 命中率下降告警
– 淘汰次数告警
5.2 Memcached数据库监控告警最佳实践
5.2.1 监控指标
# 基础指标
1. 节点状态(在线/离线)
2. 运行时间
3. 版本信息
# 性能指标
1. 缓存命中率
2. 响应时间
3. QPS(每秒查询数)
# 资源指标
1. 内存使用率
2. 当前Item数
3. 连接数
# 异常指标
1. 淘汰次数
2. 错误次数
3. 重连次数
# 告警阈值建议
内存使用率 > 90%
缓存命中率 < 80%
淘汰次数 > 10000/小时
节点故障 > 0
5.3 Memcached数据库高可用常见问题
5.3.1 故障切换后数据丢失
# 原因
Memcached数据存储在内存中,节点故障后数据丢失
# 解决方案
1. 使用双写模式
2. 实现数据预热机制
3. 从数据库重新加载缓存
# 预热脚本示例
#!/bin/bash
# 从数据库加载热点数据到缓存
mysql -h $DB_HOST -N -e ”
SELECT key, value FROM hot_cache
” | while read key value; do
echo -e “set $key 0 3600 ${#value}\n$value” | nc $MC_HOST $MC_PORT
done
5.3.2 缓存雪崩
# 原因
大量缓存同时过期,请求直接打到数据库
# 解决方案
1. 设置随机过期时间
2. 使用互斥锁防止并发重建
3. 实现熔断机制
# 随机过期时间示例
$baseExpire = 3600;
$randomExpire = $baseExpire + rand(0, 300); // 3600-3900秒
$memcached->set($key, $value, $randomExpire);
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
