前言
说到分布式缓存,可能大多数人脑海浮现的就是 redis 了,为什么 redis 能够在竞争激烈的缓存大战中脱颖而出呢?原因无非有一下几点:性能好,丰富的特性跟数据结构,api 操作简单。但是用的人多了,就会出现很多不规范或者疏忽的地方,严重的时候甚至会导致生产事故,所以我们有必要来聊聊在 Redis 使用过程中的一些“正确姿势“。
切忌裸奔
大家别笑 … 很多初学者或者没经验的开发人员在服务器上用 root 用户装了 redis 以后,打开默认端口直接就愉快的运行起来了,开放了外网及默认端口的连接,甚至对于生产环境也这样,贪图一时的方便,这种情况比较多的出现在一些初创公司(包括 N 年前的我也这么干过 …)
那么会出现什么问题呢?最常见的就是 Redis 未授权访问漏洞。攻击者扫描到互联网开放的 ip 以及默认的 6379 端口后,直接在本地远程连接你服务器的 redis,通过 redis 的命令将本机生成的公钥写入到服务器的 authorized.keys 中,这时候本机就可以 ssh 免密登录进来了。接下来可以写入反弹 shell,提权,然后就可以为所欲为了,这就是为什么你的服务器上面会突然出现有挖矿程序的一个原因。
为了防止出现上述的风险,我们可以从以下几个地方来处理。
修改默认端口 6379
绑定内网访问 bing 127.0.0.1
添加 redis 的密码验证
以低权限的用户运行 redis
必要时设置防火墙策略
禁止“key *”, 用“scan”代替
说到这个,笔者也是满满的泪,在年少无知的时候曾经在生产环境执行过这个命令,后来差点收拾背包提前下班了。为什么这个操作这么可怕呢?“key *”操作的意思是返回数据库中所有匹配的 key,它会扫一次性扫描所有的记录,当你库里的数据量很大的时候,会造成 redis 的阻塞,cpu 使用率飙升,慢慢的拖垮项目中对 redis 的相关请求直至出现各种 timeout…
在 Redis2.8 版本以后,提供了一个更好的遍历 key 的操作 ”scan”, 它类似于我们 jdbc 中 ResultSet,通过一个游标来迭代。使用方法为“SCAN cursor [MATCH pattern] [COUNT count]”。
redis 127.0.0.1:6379> scan 0
1) “17”
2) 1) “key:12”
2) “key:8”
3) “key:4”
4) “key:14”
5) “key:16”
6) “key:17”
7) “key:15”
8) “key:10”
9) “key:3”
10) “key:7”
11) “key:1”
redis 127.0.0.1:6379> scan 17
1) “0”
2) 1) “key:5”
2) “key:18”
3) “key:0”
4) “key:2”
5) “key:19”
6) “key:13”
7) “key:6”
8) “key:9”
9) “key:11”
“scan 0”表示开始一个新的迭代,当返回的第一参数为 0 时,则表示迭代结束,若不为 0,下一次迭代的时候带上这个游标开始下一次遍历,直到返回 0. 第二个参数则是当前遍历出来的值。使用的时候需要注意版本,当版本低于 2.8 时,需升级才能使用。
key 的设计
首先用“:”来分割 key 是一个约定俗成的东西,自己使用的时候就尽量不要用一些比较特殊的字符来代替。关于我们的 key 设计可以参照我们的关系型数据库。
假如有一个 user 表,有 userid,age,username 字段,那么我们 key 就可以 ”user:userid:useridValue:username” 这么来设计,把表名作为 key 的前缀,查询的条件放在最后。中间用字段跟它所对应的 value 分割开来,所有的设计都是为了在查询的时候可以更便捷。
合理使用多个 db
redis 的 db 下标默认 0 -15,也就是有 16 个。通常大部分人都是使用 db0,所有的 k - v 都在一个库中。这其实没多大问题,但是 redis 不是关系型数据库,存储的数据相互耦合不那么大,所以建议可以按照不同的业务把数据分散到各个库中,这样我们可以 select 不同的 db 来执行不同的业务模块操作。
善用 5 种数据结构
redis 提供了 5 种数据结构,但是根据以往的面试结果来看,很多应聘者几乎在项目中只用到 string 类型,甚至对其他类型一知半解。其实当我们能在不同的场景善用不同的结构的话,效率会有很大的提升。下面简单介绍几个例子。
SortSet
它提供了一个优先级(score)来排序,我们可以把 score 的值设置成时间戳,这样我们可以通过一些定时的操作来取出某段时间里面数据,在我们项目的机器人模块中有大量的此类操作。还有常用的地方是排行榜,通过 score 值的变化来快速高效的更新榜单。
list
它的实现是一个双向链表,我们可以把一些需要执行的任务通过 lpush,rpush 存放进来,组成符合我们需求的顺序,最后我们在依次取出来执行,类似于 mq。
set
用来做一些自动去重的操作,比如 redis 的交集命令,可以取出 2 个人的共同好友。
禁用高危命令
redis 中有很多高危命令。如“flushdb”,“config”等,我们可以禁止或者重命名这些命令来使得操作更加安全。
我们需要修改 redis 的配置文件 redis.conf,在 SECURITY 这一项中,新增
rename-command FLUSHALL “”
rename-command CONFIG “”
假如是重命名的话
rename-command FLUSHALL abcdefg
配置完重启后生效。
结语
对于 redis 的话题,其实还有很多,比如发布订阅,持久化机制,集群等。很多最佳实践都需要结合自身的业务不断摸索,还是那句话,适合自己的才是最好的。
喜欢的话,关注一下公众号《深夜里的程序猿》,每天更新高质量 IT 文章