前言

 

说到分布式缓存,可能大多数人脑海浮现的就是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的话题,其实还有很多,比如发布订阅,持久化机制,集群等。很多最佳实践都需要结合自身的业务不断摸索,还是那句话,适合自己的才是最好的。

文章始发于微信公众号《深夜里的程序猿》,转载请注明出处,侵权必究。

posted on 2019-04-08 09:35  深夜里的程序猿  阅读(1894)  评论(4编辑  收藏  举报