第7集Redis主从架构原理及搭建实操指南 | 字数总计: 2.8k | 阅读时长: 12分钟 | 阅读量:
引言 Redis主从架构是Redis高可用性方案的基础,通过主节点(Master)和从节点(Slave)的协作,实现数据的复制和读写分离。本文将深入探讨Redis主从架构的原理,并提供详细的搭建实操指南。
Redis主从架构概述 什么是主从架构 Redis主从架构是一种数据复制方案,其中:
主节点(Master) :负责处理写操作,并将数据同步到从节点
从节点(Slave) :负责处理读操作,接收主节点的数据同步
主从架构的优势
读写分离 :主节点处理写操作,从节点处理读操作,提升系统性能
数据冗余 :多个从节点提供数据备份,提高数据安全性
负载分担 :读请求分散到多个从节点,减轻主节点压力
故障恢复 :主节点故障时,可以快速切换到从节点
主从复制原理 复制流程 Redis主从复制分为以下几个阶段:
1. 建立连接 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void replicationConnectMaster (void ) { int fd; fd = anetTcpConnect(server.neterr, server.masterhost, server.masterport, NET_FIRST_BIND_ADDR); if (fd == -1 ) { serverLog(LL_WARNING,"Unable to connect to MASTER: %s" , server.neterr); return ; } if (aeCreateFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE, syncWithMaster,NULL ) == AE_ERR) { close(fd); serverLog(LL_WARNING,"Can't create readable event for SYNC" ); return ; } server.repl_transfer_fd = fd; server.repl_transfer_lastio = server.unixtime; server.repl_state = REPL_STATE_CONNECTING; serverLog(LL_NOTICE,"Connecting to MASTER %s:%d" , server.masterhost, server.masterport); }
2. 发送PING命令 1 2 3 4 5 6 7 8 9 10 11 12 13 void replicationSendPing (void ) { if (server.masterhost == NULL ) return ; if (write(server.repl_transfer_fd, "PING\r\n" , 6 ) != 6 ) { serverLog(LL_WARNING,"Error writing PING to master" ); return ; } server.repl_transfer_lastio = server.unixtime; server.repl_state = REPL_STATE_RECEIVE_PONG; }
3. 身份验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void replicationSendAuth (void ) { if (server.masterauth == NULL ) return ; if (write(server.repl_transfer_fd, "AUTH %s\r\n" , server.masterauth) != (int )(6 + strlen (server.masterauth))) { serverLog(LL_WARNING,"Error writing AUTH to master" ); return ; } server.repl_transfer_lastio = server.unixtime; server.repl_state = REPL_STATE_RECEIVE_AUTH; }
4. 发送端口信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void replicationSendPort (void ) { char port[32 ]; int len; len = snprintf (port, sizeof (port), "REPLCONF listening-port %d\r\n" , server.port); if (write(server.repl_transfer_fd, port, len) != len) { serverLog(LL_WARNING,"Error writing REPLCONF listening-port to master" ); return ; } server.repl_transfer_lastio = server.unixtime; server.repl_state = REPL_STATE_RECEIVE_PORT; }
5. 同步数据 1 2 3 4 5 6 7 8 9 10 void replicationSendSync (void ) { if (write(server.repl_transfer_fd, "SYNC\r\n" , 6 ) != 6 ) { serverLog(LL_WARNING,"Error writing SYNC to master" ); return ; } server.repl_transfer_lastio = server.unixtime; server.repl_state = REPL_STATE_RECEIVE_PSYNC; }
复制机制详解 全量复制(RDB) 全量复制是Redis主从复制的初始阶段,主节点将当前数据快照发送给从节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void replicationFeedSlaves (list *slaves, int dictid, robj **argv, int argc) { listIter li; listNode *ln; client *slave; if (server.masterhost != NULL ) return ; listRewind(slaves, &li); while ((ln = listNext(&li))) { slave = ln->value; if (slave->replstate != SLAVE_STATE_WAIT_BGSAVE_START) continue ; if (rdbSaveBackground(server.rdb_filename) != C_OK) { continue ; } slave->replstate = SLAVE_STATE_WAIT_BGSAVE_END; } }
增量复制(AOF) 增量复制通过AOF(Append Only File)实现,主节点将写操作命令发送给从节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void replicationFeedSlaves (list *slaves, int dictid, robj **argv, int argc) { listIter li; listNode *ln; client *slave; if (server.masterhost != NULL ) return ; listRewind(slaves, &li); while ((ln = listNext(&li))) { slave = ln->value; if (slave->replstate != SLAVE_STATE_ONLINE) continue ; addReplyMultiBulkLen(slave, argc); for (int j = 0 ; j < argc; j++) { addReplyBulk(slave, argv[j]); } } }
搭建实操指南 环境准备 1. 安装Redis 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 sudo apt-get update sudo apt-get install redis-server sudo yum install redis sudo yum install epel-release sudo yum install redis wget http://download.redis.io/releases/redis-6.2.6.tar.gz tar xzf redis-6.2.6.tar.gz cd redis-6.2.6make sudo make install
2. 创建目录结构 1 2 3 sudo mkdir -p /opt/redis/{master,slave1,slave2} sudo chown -R redis:redis /opt/redis
主节点配置 1. 配置文件(redis-master.conf) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 port 6379 bind 0.0.0.0daemonize yes pidfile /var/run/redis-master.pid logfile /var/log/redis-master.log dir /opt/redis/mastersave 900 1 save 300 10 save 60 10000 rdbcompression yes rdbchecksum yes dbfilename dump.rdb appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb maxmemory 2gb maxmemory-policy allkeys-lru requirepass master123
2. 启动主节点 1 2 3 4 5 redis-server /opt/redis/master/redis-master.conf redis-cli -p 6379 -a master123 ping
从节点配置 1. 配置文件(redis-slave1.conf) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 port 6380 bind 0.0.0.0daemonize yes pidfile /var/run/redis-slave1.pid logfile /var/log/redis-slave1.log dir /opt/redis/slave1replicaof 127.0.0.1 6379 masterauth master123 replica-read-only yes save 900 1 save 300 10 save 60 10000 rdbcompression yes rdbchecksum yes dbfilename dump.rdb appendonly yes appendfilename "appendonly.aof" appendfsync everysec maxmemory 2gb maxmemory-policy allkeys-lru
2. 配置文件(redis-slave2.conf) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 port 6381 bind 0.0.0.0daemonize yes pidfile /var/run/redis-slave2.pid logfile /var/log/redis-slave2.log dir /opt/redis/slave2replicaof 127.0.0.1 6379 masterauth master123 replica-read-only yes save 900 1 save 300 10 save 60 10000 rdbcompression yes rdbchecksum yes dbfilename dump.rdb appendonly yes appendfilename "appendonly.aof" appendfsync everysec maxmemory 2gb maxmemory-policy allkeys-lru
3. 启动从节点 1 2 3 4 5 6 7 8 9 redis-server /opt/redis/slave1/redis-slave1.conf redis-server /opt/redis/slave2/redis-slave2.conf redis-cli -p 6380 -a master123 ping redis-cli -p 6381 -a master123 ping
验证主从复制 1. 检查主从状态 1 2 3 4 5 6 redis-cli -p 6379 -a master123 info replication redis-cli -p 6380 -a master123 info replication redis-cli -p 6381 -a master123 info replication
2. 测试数据同步 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 redis-cli -p 6379 -a master123 > SET test_key "Hello Redis" > SET counter 100 > HSET user:1 name "Alice" age 25 redis-cli -p 6380 -a master123 > GET test_key > GET counter > HGETALL user:1 redis-cli -p 6381 -a master123 > GET test_key > GET counter > HGETALL user:1
高级配置 1. 复制积压缓冲区 1 2 3 repl-backlog-size 1mb repl-backlog-ttl 3600
2. 复制超时设置 1 2 3 repl-timeout 60 repl-ping-slave-period 10
3. 无盘复制 1 2 3 repl-diskless-sync yes repl-diskless-sync-delay 5
故障处理 1. 主节点故障 手动切换 1 2 3 4 5 6 7 8 9 10 redis-cli -p 6379 -a master123 shutdown redis-cli -p 6380 -a master123 > REPLICAOF NO ONE redis-cli -p 6381 -a master123 > REPLICAOF 127.0.0.1 6380
自动切换(使用Sentinel) 1 2 3 4 5 6 7 port 26379 sentinel monitor mymaster 127.0.0.1 6379 2 sentinel auth-pass mymaster master123 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
2. 从节点故障 1 2 3 4 5 redis-server /opt/redis/slave1/redis-slave1.conf redis-cli -p 6380 -a master123 info replication
3. 网络分区 1 2 3 4 5 ping 127.0.0.1 redis-cli -p 6379 -a master123 ping
性能优化 1. 读写分离 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import redismaster = redis.Redis(host='127.0.0.1' , port=6379 , password='master123' ) slaves = [ redis.Redis(host='127.0.0.1' , port=6380 , password='master123' ), redis.Redis(host='127.0.0.1' , port=6381 , password='master123' ) ] def write_data (key, value ): """写操作使用主节点""" return master.set (key, value) def read_data (key ): """读操作使用从节点""" import random slave = random.choice(slaves) return slave.get(key)
2. 连接池配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from redis import ConnectionPoolmaster_pool = ConnectionPool( host='127.0.0.1' , port=6379 , password='master123' , max_connections=20 , retry_on_timeout=True ) slave_pool = ConnectionPool( host='127.0.0.1' , port=6380 , password='master123' , max_connections=50 , retry_on_timeout=True ) master = redis.Redis(connection_pool=master_pool) slave = redis.Redis(connection_pool=slave_pool)
3. 监控和告警 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 check_replication () { local port=$1 local password=$2 replication_info=$(redis-cli -p $port -a $password info replication) master_link_status=$(echo "$replication_info " | grep "master_link_status" | cut -d: -f2 | tr -d '\r' ) if [ "$master_link_status " != "up" ]; then echo "ERROR: Master link status is down on port $port " return 1 fi echo "OK: Master link status is up on port $port " return 0 } check_replication 6379 master123 check_replication 6380 master123 check_replication 6381 master123
最佳实践 1. 配置建议
内存配置 :主从节点内存配置应该一致
持久化 :建议同时开启RDB和AOF
网络 :确保主从节点网络延迟低
安全 :设置密码认证,限制访问IP
2. 监控指标
复制延迟 :监控主从复制延迟
连接状态 :监控主从连接状态
内存使用 :监控内存使用情况
命令执行 :监控命令执行性能
3. 故障预防
定期备份 :定期备份Redis数据
监控告警 :设置监控告警机制
测试切换 :定期测试主从切换
文档记录 :记录配置和操作文档
实际应用案例 案例1:电商系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class EcommerceRedis : def __init__ (self ): self.master = redis.Redis( host='master.redis.com' , port=6379 , password='ecommerce123' , decode_responses=True ) self.slaves = [ redis.Redis( host='slave1.redis.com' , port=6379 , password='ecommerce123' , decode_responses=True ), redis.Redis( host='slave2.redis.com' , port=6379 , password='ecommerce123' , decode_responses=True ) ] def set_product (self, product_id, product_info ): """设置商品信息(写操作)""" key = f"product:{product_id} " return self.master.hset(key, mapping=product_info) def get_product (self, product_id ): """获取商品信息(读操作)""" key = f"product:{product_id} " import random slave = random.choice(self.slaves) return slave.hgetall(key) def update_inventory (self, product_id, quantity ): """更新库存(写操作)""" key = f"inventory:{product_id} " return self.master.set (key, quantity) def get_inventory (self, product_id ): """获取库存(读操作)""" key = f"inventory:{product_id} " import random slave = random.choice(self.slaves) return slave.get(key)
案例2:用户会话管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class SessionManager : def __init__ (self ): self.master = redis.Redis( host='master.redis.com' , port=6379 , password='session123' , decode_responses=True ) self.slaves = [ redis.Redis( host='slave1.redis.com' , port=6379 , password='session123' , decode_responses=True ) ] def create_session (self, user_id, session_data ): """创建会话(写操作)""" key = f"session:{user_id} " self.master.hset(key, mapping=session_data) self.master.expire(key, 3600 ) def get_session (self, user_id ): """获取会话(读操作)""" key = f"session:{user_id} " slave = self.slaves[0 ] return slave.hgetall(key) def extend_session (self, user_id ): """延长会话(写操作)""" key = f"session:{user_id} " self.master.expire(key, 3600 )
总结 Redis主从架构是构建高可用Redis系统的基础:
核心要点
复制机制 :通过全量复制和增量复制实现数据同步
读写分离 :主节点处理写操作,从节点处理读操作
故障处理 :支持手动和自动的主从切换
性能优化 :通过连接池、监控等手段提升性能
最佳实践
合理配置主从节点参数
实施监控和告警机制
定期测试故障切换
做好数据备份和恢复
注意事项
从节点默认只读,不能执行写操作
主从复制存在延迟,需要根据业务需求调整
网络分区可能导致数据不一致
需要定期监控主从复制状态
理解Redis主从架构的原理和搭建方法,有助于我们构建高可用、高性能的Redis系统。