一、Support Multiple user-defined redis database instances
1.1 Motivation
当前SONiC架构仅部署了一个Redis数据库实例,所有业务数据库(如APPL_DB、ASIC_DB、CONF_DB等)均共享该单一实例。
我们发现,当短时间内出现大规模写入操作时(例如批量创建大量路由条目),这个唯一的数据库实例会成为性能瓶颈,资源占用率极高。
为此我们尝试部署两个独立的Redis实例,并将高负载的写入操作分摊到两个实例中。测试结果显示,整体性能(操作耗时)提升了20%-30%。
此外,多实例架构还支持我们根据业务数据库的操作频率或系统角色进行资源隔离。例如,状态数据库和日志级别数据库属于非核心业务,通过多实例部署可以避免它们的波动影响核心业务数据库(如APPL_DB或ASIC_DB)的读写性能。
1.2 Current implementation
- 所有业务数据库共享单一Redis实例
- 所有数据库配置文件(supervisord.conf、redis.conf、redis.sock、redis.pid等)均在编译阶段生成,运行时无法修改。

设备去安装一个新的image
- 1. 如果
/etc/sonic/目录存在配置文件,则将其备份到/host/old_config/ 2.rc.local服务启动:- 如果
/host/old_config/目录存在,则将备份的配置恢复到/etc/sonic/ - 如果
/host/old_config/目录不存在,则复制默认XML配置文件到/etc/sonic/
- 如果
- 3. database服务启动:
- 根据
/etc/sonic/中的配置执行database.sh start并启动相关Docker容器 - 检查数据库服务是否正常运行
- 根据
- 4. updategraph服务启动:
- 依赖于
rc.local和database服务完成启动 - 如果
/etc/sonic/old_config/目录存在,则将其中的选定文件恢复到/etc/sonic/ - 如果
/etc/sonic/old_config/目录不存在,则基于XML配置文件生成config_db.json
- 依赖于
1.3 New Design of Database Startup
- 我们引入了一个新的配置文件。
- 此文件包含多个 Redis 实例和数据库,以及每个数据库的配置,包括实例、数据库 ID 和分隔符。
{
"INSTANCES": {
"redis":{
"hostname" : "127.0.0.1",
"port" : 6379,
"unix_socket_path" : "/var/run/redis/redis.sock"
}
},
"DATABASES" : {
"APPL_DB" : {
"id" : 0,
"separator": ":",
"instance" : "redis"
},
"ASIC_DB" : {
"id" : 1,
"separator": ":",
"instance" : "redis"
},
"COUNTERS_DB" : {
"id" : 2,
"separator": ":",
"instance" : "redis"
},
"LOGLEVEL_DB" : {
"id" : 3,
"separator": ":",
"instance" : "redis"
},
"CONFIG_DB" : {
"id" : 4,
"separator": "|",
"instance" : "redis"
},
"PFC_WD_DB" : {
"id" : 5,
"separator": ":",
"instance" : "redis"
},
"FLEX_COUNTER_DB" : {
"id" : 5,
"separator": ":",
"instance" : "redis"
},
"STATE_DB" : {
"id" : 6,
"separator": "|",
"instance" : "redis"
},
"SNMP_OVERLAY_DB" : {
"id" : 7,
"separator": "|",
"instance" : "redis"
}
},
"VERSION" : "1.0"
}
- 默认情况下,每个镜像在 SONiC 文件系统中的 /etc/default/sonic-db/ 目录下都具有一个默认的启动数据库配置文件 database_config.json
- 用户可以使用自定义的数据库配置。具体操作是创建一个名为“database_config.json”的文件,并将其放置在“/etc/sonic/”目录下。
- 我们将数据库的 Docker ENTRYPOINT 脚本修改为新添加的“docker-database-init.sh”脚本。
- 我们还将 supervisord.conf 文件转换为 J2 模板模式,因为我们希望在运行时使用 database_config.json 来生成 supervisord.conf 文件。
具体步骤如下:
1. DUT 尝试load 一个images(不做任何更改)
- 如果位于 /etc/sonic/ 目录下的配置文件存在,就按照常规将 /etc/sonic/ 目录下的文件复制到 /host/old_config 目录下。
2. rc.local service(不做任何更改)
- 如果“/host/old_config/”存在,则按照常规将其复制到“/etc/sonic/”中。
- 如果不存在“/host/old_config/”这个文件夹,那就像往常一样复制一些默认的 XML 文件和其他内容。
3. database service
- database Docker 启动,入口脚本为“docker-database-init.sh”
- 如果在 /etc/sonic/ 目录下找到了 database_config.json 文件,这意味着存在自定义的数据库配置。我们将此配置文件复制到 /var/run/redis/sonic-db/ 目录下,这是运行中的数据库配置文件所在位置,所有应用程序都将从这个文件中读取数据库信息。
- 如果在 /etc/sonic/ 目录下找不到 database_config.json 文件,这意味着没有自定义的数据库配置。我们将位于 /etc/default/ 目录下的此配置文件复制到 /var/run/redis/sonic-db/ 目录下,这是镜像自身中的默认启动配置。
- 使用 supervisord.conf.j2 来生成 supervisord.conf 文件
- 执行之前的入口程序 /usr/bin/supervisord,然后所有服务将根据新的 supervisord.conf 文件启动,其中包括启动多少个 Redis 实例。
- 通过“ping_pong_db_insts”脚本检查 Redis 实例是否正在运行或未运行。
4. updategraph service(不做任何更改)
- 依赖 rc.local 和database
- 将选定的文件(/etc/sonic/old_config)恢复到 /etc/sonic 目录下(如果有)
- 如果没有 /etc/sonic/old_config 文件夹,则根据 xml 等生成 config_db.json 文件。
1.4 Potential Redis Cluster Solution
我们能否使用Redis Cluster功能在单实例基础上实现数据库跨节点拆分,替代本设计文档中提到的多单实例部署方案?
Redis Cluster的设计目标是什么?
- 高性能与线性扩展性:支持最多1000个节点的线性扩展,无代理架构,采用异步复制,不对值执行合并操作。
- 可接受的写入安全性:系统会尽力保留来自连接到多数主节点的客户端的所有写入。通常存在较小的窗口可能导致已确认的写入丢失,当客户端处于少数分区时,丢失已确认写入的窗口会更大。
- 高可用性:Redis Cluster能够在多数主节点可达且每个不可达主节点至少有一个可达从节点的分区场景下继续运行。此外,通过副本迁移机制,没有从节点复制的主节点会从有多个从节点的主节点接收一个从节点。
Redis Cluster协议中的客户端与服务器角色
在 Redis Cluster 中,节点(nodes)负责存储数据,并维护整个集群的状态,包括将键(keys)正确映射到对应的节点。集群中的节点还能够自动发现其他节点、检测失效的节点,并在发生故障时将从节点(slave)提升为主节点(master),以确保系统在故障情况下仍能继续运行。
为了完成这些任务,所有集群节点通过一个名为 Redis Cluster Bus(Redis 集群总线) 的 TCP 连接和二进制协议相互连接。集群中的每个节点都与其他所有节点通过该总线建立连接。节点使用一种 Gossip(流言)协议 来传播集群信息,从而实现:
- 发现新加入的节点,
- 发送 ping 包以确认其他节点是否正常工作,
- 发送集群所需的特定消息以通知某些状态变化(如故障、配置变更等)。
此外,集群总线还用于:
- 在整个集群中传播 Pub/Sub 消息,
- 当用户请求手动故障转移(manual failover)时进行协调(这类故障转移并非由 Redis Cluster 的自动故障检测器触发,而是由系统管理员直接发起)。

Redis Cluster 主要组件:
键(KEYs)分布模型:
HASH_SLOT = CRC16(key) mod 16384
集群节点属性:
每个节点在集群中都拥有一个唯一的名称。该节点名称是一个 160 位随机数的十六进制表示,通常在节点首次启动时通过 /dev/urandom 生成,并永久保存(一般存于 nodes.conf 文件中)。
每个节点都会维护它所知晓的其他集群节点的以下信息:
- 节点 ID(即上述唯一名称)
- IP 地址和端口
- 一组标志位(flags)(例如:master、slave、fail、myself 等)
- 如果该节点是 slave,则记录其所属的 master 节点
- 上次向该节点发送 PING 的时间
- 上次收到该节点 PONG 响应的时间
- 该节点当前的配置纪元(configuration epoch)(此概念将在本规范后续部分详细说明)
- 与该节点的连接状态(link state)(如 connected、disconnected)
- 该节点所服务的哈希槽集合(set of hash slots served)
$ redis-cli cluster nodes
d1861060fe6a534d42d8a19aeb36600e18785e04 127.0.0.1:6379 myself - 0 1318428930 1 connected 0-1364
3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729
d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095
集群总线(The Cluster Bus):
每个 Redis Cluster 节点都会额外开启一个 TCP 端口,用于接收来自其他 Redis Cluster 节点的连接。
这个端口相对于用于接收客户端连接的普通命令端口有一个固定的偏移量:
集群总线端口 = 普通客户端端口 + 10000
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_49477505/article/details/156155780




