redis优化

一、声明

本文为整理的学习笔记,主要内容来自与redis学习中的那些参考资料。

这些学习资料里面最详细的是《redis核心技术与实战》和《可能是北半球最全面的Redis6.x系列文章》,都是很详细的讲解redis。

因为时间、以及文字表达能力有限,所以文中有很多内容就直接摘抄使用了原文。有一些地方有标记,有一些地方没有标记。

如果转载此文章,需要把参考资料中的所有链接都标记上。

二、acl权限控制(来自王义凯_Rick博客)

acl是Redis6.0版本以后新增加的。6.0之前redis只支持使用默认用户名和配置密码方式登陆。


acl(Access Control List)访问控制列表,可以设置多个用户,并给每个用户单独设置密码、命令权限、数据权限。

1. acl配置模式

acl支持config文件、外部aclfile文件、命令行三种配置模式。

其中config文件和aclfile文件模式只能选择一种如果使用了aclfile配置,则config文件中的配置失效。

推荐使用aclfile文件配置。因为在aclfile文件中配置的规则,可以使用通过acl load命令将规则载入。

1.1 config文件模式

使用redis.conf文件配置default和其他用户的ACL权限

1.1.1 在config文件中配置default用户的密码

requirepass 123456 

1.1.2 在config文件中添加DSL命令配置用户ACL权限

使用方式参考acl规则

1.1.3 在config文件中注释aclfile的路径配置(默认是注释的)

#aclfile /opt/app/redis6/users.acl 

1.1.4 重启redis服务

/opt/redis/redis-server /opt/redis/redis.conf

1.2 aclfile文件模式

1.2.1 注释redis.conf中所有已授权的ACL命令,如:

#user default on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* +@all 

1.2.2 在config文件中注释default用户的密码,因为开启aclfile之后,requirepass的密码就失效了:

#requirepass 123456 

1.2.3 在config文件中配置aclfile的路径,然后创建该文件,否则重启redis服务会报错找不到该文件

aclfile /opt/redis/users.acl
touch /opt/redis/users.acl 

1.2.4 在外部aclfile文件中添加DSL命令配置用户ACL权限使用方式参考acl规则示例:

user default on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* +@all

1.2.5 重启redis服务或使用aclfile load命令加载权限

1.3 两种文件模式对比

redis.confusers.acl
配置方式DSLDSL
加载ACL配置重启Redis服务ACL LOAD命令
持久化ACL配置CONFIG REWRITE命令ACL SAVE命令

1.4 命令行模式

可以在命令行中直接配置acl规则。配置后使用持久化命令将acl规则持久化到文件中。

如果使用config模式,将ACL权限持久化到redis.conf文件中使用下面的命令:

 config rewrite 

如果使用aclfile模式,将ACL权限持久化到users.acl文件中使用下面的命令:

acl save

2. acl规则

ACL是使用DSL(Domain specific language)定义的,该DSL描述了用户能够执行的操作。

该规则始终从上到下,从左到右应用,因为规则的顺序对于理解用户的实际权限很重要。

ACL规则可以在redis.conf文件以及users.acl文件中配置DSL,也可以在命令行中通过ACL。

acl规则示例

user default on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* +@all

2.1 启用和禁用用户

  • on:启用用户:可以以该用户身份进行认证。
  • off:禁用用户:不再可以使用此用户进行身份验证,但是已经通过身份验证的连接仍然可以使用。

2.2 允许和禁止调用命令

  • +<command>:将命令添加到用户可以调用的命令列表中。
  • -<command>:将命令从用户可以调用的命令列表中移除。
  • +@<category>:允许用户调用 <category> 类别中的所有命令,有效类别为@admin,@set,@sortedset等,可通过调用ACL CAT命令查看完整列表。特殊类别@all表示所有命令,包括当前和未来版本中存在的所有命令。
  • -@<category>:禁止用户调用<category> 类别中的所有命令。
  • +<command>|subcommand:允许使用已禁用命令的特定子命令。
  • allcommands:+@all的别名。包括当前存在的命令以及将来通过模块加载的所有命令。
  • nocommands:-@all的别名,禁止调用所有命令。

2.3 允许和禁止访问某些key

  • ~<pattern>:添加可以在命令中提及的键模式。例如~*和* allkeys 允许所有键。
  • * resetkeys:使用当前模式覆盖所有允许的模式。如: ~foo:* ~bar:* resetkeys ~objects:* ,客户端只能访问匹配 object:* 模式的 KEY。

2.4 为用户配置有效密码

  • ><password>:将此密码添加到用户的有效密码列表中。例如,>mypass将“mypass”添加到有效密码列表中。该命令会清除用户的nopass标记。每个用户可以有任意数量的有效密码。
  • <<password>:从有效密码列表中删除此密码。若该用户的有效密码列表中没有此密码则会返回错误信息。
  • #<hash>:将此SHA-256哈希值添加到用户的有效密码列表中。该哈希值将与为ACL用户输入的密码的哈希值进行比较。允许用户将哈希存储在users.acl文件中,而不是存储明文密码。仅接受SHA-256哈希值,因为密码哈希必须为64个字符且小写的十六进制字符。
  • !<hash>:从有效密码列表中删除该哈希值。当不知道哈希值对应的明文是什么时很有用。
  • nopass:移除该用户已设置的所有密码,并将该用户标记为nopass无密码状态:任何密码都可以登录。resetpass命令可以清除nopass这种状态。
  • resetpass:情况该用户的所有密码列表。而且移除nopass状态。resetpass之后用户没有关联的密码同时也无法使用无密码登录,因此resetpass之后必须添加密码或改为nopass状态才能正常登录。
  • reset:重置用户状态为初始状态。执行以下操作resetpass,resetkeys,off,-@all。

3. acl相关命令

3.1 ACL HELP

使用下面的命令查看help文档:

127.0.0.1:6379> acl help
 1) ACL <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
 2) CAT [<category>]
 3)     List all commands that belong to <category>, or all command categories
 4)     when no category is specified.
 5) DELUSER <username> [<username> ...]
 6)     Delete a list of users.
 7) GETUSER <username>
 8)     Get the user's details.
 9) GENPASS [<bits>]
10)     Generate a secure 256-bit user password. The optional `bits` argument can
11)     be used to specify a different size.
12) LIST
13)     Show users details in config file format.
14) LOAD
15)     Reload users from the ACL file.
16) LOG [<count> | RESET]
17)     Show the ACL log entries.
18) SAVE
19)     Save the current config to the ACL file.
20) SETUSER <username> <attribute> [<attribute> ...]
21)     Create or modify a user with the specified attributes.
22) USERS
23)     List all the registered usernames.
24) WHOAMI
25)     Return the current connection username.
26) HELP
27)     Prints this help. help


3.2 ACL LIST

我们可以使用ACL LIST命令来查看当前活动的ACL,默认情况下,有一个“default”用户:

127.0.0.1:6379> acl list 1) "user default on nopass sanitize-payload ~* &* +@all"

3.3 acl users

显示所有用户

127.0.0.1:6379> acl users 1) “default”

3.4 ACL WHOAMI

返回当前用户名

127.0.0.1:6379> acl whoami "default"

3.5 acl cat

查看命令类别,用于授权:

127.0.0.1:6379> acl cat
 1) "keyspace"
 2) "read"
 3) "write"
 4) "set"
 5) "sortedset"
 6) "list"
 7) "hash"
 8) "string"
 9) "bitmap"
10) "hyperloglog"
11) "geo"
12) "stream"
13) "pubsub"
14) "admin"
15) "fast"
16) "slow"
17) "blocking"
18) "dangerous"
19) "connection"
20) "transaction"
21) "scripting"

3.6 acl setuser

使用下面的命令创建或修改用户属性,username区分大小写

#username区分大小写
#若用户不存在则按默认规则创建用户,若存在则修改用户属性
SETUSER <username> [attribs ...]
 
#若用户不存在,则按默认规则创建用户。若用户存在则该命令不做任何操作。
ACL SETUSER <username> 
 
#若用户不存在,则按默认规则创建用户,并为其增加<rules>。若用户存在则在已有规则上增加 <rules>。
ACL SETUSER <username> <rules> 

3.7 acl getuser

使用下面的命令查看用户的ACL权限

acl getuser <username>

127.0.0.1:6379> acl getuser default
 1) "flags"
 2) 1) "on"
    2) "allkeys"
    3) "allchannels"
    4) "allcommands"
    5) "nopass"
    6) "sanitize-payload"
 3) "passwords"
 4) (empty array)
 5) "commands"
 6) "+@all"
 7) "keys"
 8) 1) "*"
 9) "channels"
10) 1) "*"

3.8 ACL DELUSER

删除指定的用户:acl deluser <username>

3.9 ACL SAVE

将acl权限持久化到磁盘的aclfile中

acl save 

如果使用redis.conf配置ACL,则使用config rewrite命令将ACL持久化到redis.conf中

config rewrite

4.0 ACL LOAD

#将aclfile中的权限加载至redis服务中

acl load

4.1 ACL GENPASS

随机返回sha256密码,我们可以直接使用该密文配置ACL密码:

#随机返回一个256bit的32字节的伪随机字符串,并将其转换为64字节的字母+数字组合字符串
acl genpass
 
#可指定位数
acl genpass 32
acl genpass 64

4.2 ACL LOG

查看acl安全日志

ACL LOG

4.3 AUTH

切换用户

AUTH <username> <password>

三、redis优化

1. cpu优化

1.1 CPU NUMA架构详解

NUMA架构的编号

在 CPU 的 NUMA 架构下,对 CPU 核的编号规则,并不是先把一个 CPU Socket 中的所有逻辑核编完,再对下一个 CPU Socket 中的逻辑核编码,而是先给每个 CPU Socket 中每个物理核的第一个逻辑核依次编号,再给每个 CPU Socket 中的物理核的第二个逻辑核依次编号。

1.2 优化

1.2.1 将redis示例和网络中断处理程序绑定到一个物理CPU上面。让redis实例和网络中断程序可以共享物理CPU的缓存,加快处理速度。

1.2.2 修改redis源代码,将子进程和后台线程绑到不同的 CPU 核上。

1.2.3 原理

四、redis问题处理

1. 穿透

1.1 什么是缓存穿透?

访问请求的数据不再redis中,也不再数据库中。因为数据库中没有数据,所以无法将数据写入缓存,这样后面的每一次请求都会重复请求redis、数据库这个流程。如果应用持续有大量的请求访问数据,就会给缓存和数据库带来巨大压力。

1.2 缓存穿透发生的情况

业务层误操作:缓存中的数据和数据库中的数据被误删除了,所以缓存和数据库中都没有数据;

恶意攻击:专门访问数据库中没有的数据。

1.3 解决方案

1.3.1 发生缓存穿透时,在redis中缓存一个空值或者和业务层协商缓存一个默认值(例如,库存的缺省值可以设置为0)。这样后续的请求查询,就会直接从redis查询。

3.1.3.2 使使用布隆过滤器快速判断数据是否存在,避免从数据库查询数据是否存在。


3.1.3.3 在入口前端进行请求检测。非正常请求的数据禁止访问。

在请求入口前端,对业务系统接收到的请求进行合法性检测,把恶意的请求(例如请求参数不合理、请求参数是非法值、请求字段不存在)直接过滤掉,不让它们访问后端缓存和数据库。

3.1.3.4 布隆过滤器介绍

2.击穿

2.1 什么是缓存击穿

某个访问非常频繁的热点数据,redis没有数据,数据库有数据。接着有大量的请求访问该数据,因为redis没有,就会访问到数据库,导致数据库压力增大。

2.2 缓存击穿发生的情况

点数据从缓存内失效时,大量访问同时请求这个数据。

2.3 解决方案

热点数据不设置过期时间

3.雪崩

3.1 什么是缓存雪崩

缓存雪崩是指大量的在redis没有数据,在数据库中有数据。接着有大量的请求访问该数据,因为redis没有,就会访问到数据库,导致数据库压力增大。

3.2 缓存雪崩发生的情况

  • redis中大量数据同时过期,导致大量请求无法处理。
  • redis实例宕机。

3.3 解决方案

针对key过期:

  • 在可接受的时间范围内随机设置key的过期时间,分散key的过期时间,以防止大量的key在同一时刻过期;
  • 对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,让一部分查询先将数据缓存起来;
  • 延长热点key的过期时间或者设置永不过期,这一点和缓存击穿中的方案一样;


针对redis实例宕机

  • 在业务系统中实现服务熔断或请求限流机制。
  • 提前预防,构建高可用的redis缓存集群。

4.预热

当系统上线时,缓存没有数据,如果是直接对外提供访问,每个请求都会穿过缓存去访问数据库。如果并发量很大的话,就有可能在上线时出现问题。因此需要在上线前将数据库内的热点数据缓存到redis。这种操作就叫作”缓存预热”.


缓存预热的实现方式有很多,比较通用的方式是写个批任务,在启动项目时或定时去触发将底层数据库内的热点数据加载到缓存内。

5.更新

缓存服务(Redis)和数据服务(底层数据库)是相互独立且异构的系统,在更新缓存或更新数据的时候无法做到原子性的同时更新两边的数据,因此在并发读写或第二步操作异常时会遇到各种数据不一致的问题。如何解决并发场景下更新操作的双写一致是缓存系统的一个重要知识点。


第二步操作异常:缓存和数据的操作顺序中,第二个动作报错。如数据库被更新, 此时失效缓存的时候出错,缓存内数据仍是旧版本;


缓存更新的设计模式有四种:

  • Cache aside:查询:先查缓存,缓存没有就查数据库,然后加载至缓存内;更新:先更新数据库,然后让缓存失效;或者先失效缓存然后更新数据库;
  • Read through:在查询操作中更新缓存,即当缓存失效时,Cache Aside 模式是由调用方负责把数据加载入缓存,而 Read Through 则用缓存服务自己来加载;
  • Write through:在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后由缓存自己更新数据库;
  • Write behind caching:俗称write back,在更新数据的时候,只更新缓存,不更新数据库,缓存会异步地定时批量更新数据库;

Cache aside:

  • 为了避免在并发场景下,多个请求同时更新同一个缓存导致脏数据,因此不能直接更新缓存而是令缓存失效。
  • 先更新数据库后失效缓存:并发场景下,推荐使用延迟失效(写请求完成后给缓存设置1s过期时间),在读请求缓存数据时若redis内已有该数据(其他写请求还未结束)则不更新。当redis内没有该数据的时候(其他写请求已另该缓存失效),读请求才会更新redis内的数据。这里的读请求缓存数据可以加上失效时间,以防第二步操作异常导致的不一致情况。
  • 先失效缓存后更新数据库:并发场景下,推荐使用延迟失效(写请求开始前给缓存设置1s过期时间),在写请求失效缓存时设置一个1s延迟时间,然后再去更新数据库的数据,此时其他读请求仍然可以读到缓存内的数据,当数据库端更新完成后,缓存内的数据已失效,之后的读请求会将数据库端最新的数据加载至缓存内保证缓存和数据库端数据一致性;在这种方案下,第二步操作异常不会引起数据不一致,例如设置了缓存1s后失效,然后在更新数据库时报错,即使缓存失效,之后的读请求仍然会把更新前的数据重新加载到缓存内。

四种缓存更新模式的优缺点:

  • Cache Aside:实现起来较简单,但需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository);
  • Read/Write Through:只需要维护一个数据存储(缓存),但是实现起来要复杂一些;
  • Write Behind Caching:与Read/Write Through 类似,区别是Write Behind Caching的数据持久化操作是异步的,但是Read/Write Through 更新模式的数据持久化操作是同步的。优点是直接操作内存速度快,多次操作可以合并持久化到数据库。缺点是数据可能会丢失,例如系统断电等。

缓存本身就是通过牺牲强一致性来提高性能,因此使用缓存提升性能,就会有数据更新的延迟性。这就需要我们在评估需求和设计阶段根据实际场景去做权衡了。

6.降级

缓存降级是指当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,即使是有损部分其他服务,仍然需要保证主服务可用。可以将其他次要服务的数据进行缓存降级,从而提升主服务的稳定性。


降级的目的是保证核心服务可用,即使是有损的。如去年双十一的时候淘宝购物车无法修改地址只能使用默认地址,这个服务就是被降级了,这里阿里保证了订单可以正常提交和付款,但修改地址的服务可以在服务器压力降低,并发量相对减少的时候再恢复。


降级可以根据实时的监控数据进行自动降级也可以配置开关人工降级。是否需要降级,哪些服务需要降级,在什么情况下再降级,取决于大家对于系统功能的取舍。

五、使用规范建议

1. key的命名规范

1.1 key的命名可以由业务名做前缀,可以通过key的前缀区分不同的业务数据。

1.2 注意控制key的长度 key的长度越长,底层的数据结构SDS占用大内存空间就会阅读。可以使用相应的英文单词的首字母表示。

2. 避免使用bigkey

Redis 是使用单线程读写数据。bigkey 的读写操作会阻塞线程,降低 Redis 的处理效率。 所以,在应用 Redis 时,关于 value 的设计规范,非常重要的一点就是避免 bigkey。

bigkey 通常有两种情况:

  • 情况一:键值对的值大小本身就很大,例如 value 为 1MB 的 String 类型数据。为了避免 String 类型的 bigkey,在业务层,我们要尽量把 String 类型的数据大小控制在 10KB 以下。
  • 情况二:键值对的值是集合类型,集合元素个数非常多,例如包含 100 万个元素的 Hash 集合类型数据。为了避免集合类型的 bigkey,我给你的设计规范建议是,尽量把集合类型的元素个数控制在 1 万以下。

3. 使用高效序列化方法和压缩方法

4. 使用整数对象池

5. 数据保存规范

5.1 使用redis保存热数据

5.2 不同的业务数据分示例存储

5.3 数据存储是,设置过期时间

5.4 控制redis实例的容量

Redis 单实例的内存大小都不要太大,根据我自己的经验值,建议你设置在 2~6GB 。这样一来,无论是 RDB 快照,还是主从集群进行数据同步,都能很快完成,不会阻塞正常请求的处理。

6 命令使用规范

6.1 线上禁用部分命令

  • KEYS,按照键值对的 key 内容进行匹配,返回符合匹配条件的键值对,该命令需要对 Redis 的全局哈希表进行全表扫描,严重阻塞 Redis 主线程;
  • FLUSHALL,删除 Redis 实例上的所有数据,如果数据量很大,会严重阻塞 Redis 主线程;
  • FLUSHDB,删除当前数据库中的数据,如果数据量很大,同样会阻塞 Redis 主线程。

6.2 慎用MONITOR命令

Redis 的 MONITOR 命令在执行后,会持续输出监测到的各个命令操作,所以,我们通常会用 MONITOR 命令返回的结果,检查命令的执行情况。但是,MONITOR 命令会把监控到的内容持续写入输出缓冲区。


如果线上命令的操作很多,输出缓冲区很快就会溢出了,这就会对 Redis 性能造成影响,甚至引起服务崩溃。


所以,除非十分需要监测某些命令的执行(例如,Redis 性能突然变慢,我们想查看下客户端执行了哪些命令),你可以偶尔在短时间内使用下 MONITOR 命令,否则,我建议你不要使用 MONITOR 命令。


6.3 慎用全量操作命令

获取所有元素的二个建议

6.3.1 使用 SSCAN、HSCAN分批返回

6.3.2 将大集合拆分为多个小集合

六、redis运维工具

5.1 数据迁移工具

是阿里云 Redis 和 MongoDB 团队开发的一个用于 Redis 数据同步的工具。

5.2 数据对比工具

5.3 集群管理工具CacheCloud

CacheCloud是搜狐开发的一个面向 Redis 运维管理的云平台,它实现了主从集群、哨兵集群和 Redis Cluster 的自动部署和管理,用户可以直接在平台的管理界面上进行操作。

Previous Post

二、redis高可用

Next Post

四、redis学习

Related Posts