redis 主從模式之復(fù)制數(shù)據(jù)延遲解決方案
在 redis 主從復(fù)制模式下可能會出現(xiàn) slave 延遲導(dǎo)致讀寫不一致的問題昂灵。 解決辦法有2種
1. 修改從幾點參數(shù)配置
從節(jié)點的 slave-serve-stale-data 參數(shù)也與此有關(guān)采够,它控制這種情況下從節(jié)點的表現(xiàn) 當(dāng)從庫同主機失去連接或者復(fù)制正在進行稻扬,從機庫有兩種運行方式:
如果slave-serve-stale-data設(shè)置為yes(默認設(shè)置)莹菱,從庫會繼續(xù)響應(yīng)客戶端的請求边器。
如果slave-serve-stale-data設(shè)置為no窃款,除去INFO和SLAVOF命令之外的任何請求都會返回一個錯誤”SYNC with master in progress”踩寇。
2. 編寫外部監(jiān)控程序
將主從模式更換為哨兵模式則無需自己去做監(jiān)控
對于無法容忍大量延遲場景,可以編寫外部監(jiān)控程序監(jiān)聽主從節(jié)點的復(fù)制偏移量晋修,當(dāng)延遲較大時觸發(fā)報警或者通知客戶端避免讀取延遲過高的從節(jié)點吧碾。
編寫監(jiān)控程序,然后定時執(zhí)行
<?php
/**
* [監(jiān)控 redis 主從復(fù)制偏移量:當(dāng)監(jiān)控到延遲高的節(jié)點時墓卦,移除延遲高的從節(jié)點倦春, 即動態(tài)修改讀寫分離配置信息。(可以添加觸發(fā)報警機制)]
* [在做主從復(fù)制時推薦使用 redis 的哨兵模式落剪,則無需自己去監(jiān)控睁本,哨兵模式能自己監(jiān)控并切換主從]
*
* @Author leeprince:2020-05-05 10:29
*/
define('IS_DEBUG', false);
define('MAX_OFFSET', 10);
$redis = new Redis();
$redis->connect("127.0.0.1", 63790);
$redis->auth("leeprince");
$info = $redis->info('replication');
$slaveNum = $info['connected_slaves'];
$masterOffest = $info['master_repl_offset'];
echo "+++++++++++++++++++++++定時開始+++++++++++++++++++++++\r\n";
$invalidSlave = []; // 要移除的從節(jié)點
for ($i = 0; $i < $slaveNum; $i++) {
$slaveKey = "slave{$i}";
$slaveInfo = $info[$slaveKey];
$slaveInfo = explode(',', $slaveInfo);
$slaveHost = explode('=', $slaveInfo[0])[1];
$slavePort = explode('=', $slaveInfo[1])[1];
$slaveState = explode('=', $slaveInfo[2])[1];
$slaveOffset = explode('=', $slaveInfo[3])[1];
$offset = $masterOffest - $slaveOffset;
if ($offset > MAX_OFFSET || $slaveState != 'online') {
$readHostMap = include_once "./hostMap.php";
$slaveString = "tcp://{$slaveHost}:{$slavePort}";
$invalidSlave[] = $readHostMap[$slaveString];
var_dump("當(dāng)前從節(jié)點為高延遲--{$slaveString}");
}
}
if (empty($invalidSlave)) {
if (IS_DEBUG) {
echo "========================================\r\n";
var_dump($info);
echo "========================================\r\n";
}
var_dump('不存在需要移除的從節(jié)點');
exit(0);
}
// redis 動態(tài)配置文件
$confPath = './config.php';
include $confPath;
if (IS_DEBUG) {
echo "========================================\r\n";
var_dump($info);
var_dump($replication);
echo "========================================\r\n";
}
$isHaveChange = false; // 是否需要修改配置≈也溃考慮上一次可能已經(jīng)移除了該從節(jié)點
foreach ($replication as $key => &$value) {
if (in_array($value, $invalidSlave)) {
$isHaveChange = true;
var_dump("需要移除的從節(jié)點存在當(dāng)前配置中--{$value}");
unset($replication[$key]);
}
}
if ($isHaveChange) {
$content = "<?php \n\$replication = ".var_export($replication, true).";";
file_put_contents($confPath, $content);
var_dump('已修改配置文件--', $content);
} else {
var_dump('無需修改配置文件');
}
exit(0);
完整的代碼:包含 linux 定時器與 swoole 毫秒級定時器版本
https://github.com/leeprince/docker/tree/master/redis/monitor
在從節(jié)點種模擬網(wǎng)絡(luò)延遲步驟
$ apt update
$ apt install -y iproute2
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
link/tunnel6 :: brd ::
15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc netem state UP group default qlen 1000
link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 設(shè)置模擬網(wǎng)絡(luò)延遲 5 秒呢堰。 刪除設(shè)置:tc qdisc del dev eth0 root netem delay 5000ms
$ tc qdisc add dev eth0 root netem delay 5000ms
運行結(jié)果: