如何正确使用redis
2021/5/17 2:56:05
本文主要是介绍如何正确使用redis,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
如何正确使用redis
1. 概述
简单来说,Redis就是一个数据结构存储器,可以用作数据库、缓存和消息中间件,它和传统数据库主要有两点不同:
- 它是Key-Value型数据库,不是关系型数据库,所有数据以Key-Value的形式存在服务器的内存中,其中Value可以是多种数据结构:字符串(String), 哈希(hashes), 列表(list), 集合(sets) 和有序集合(sorted sets)等类型;
- 它所有运行时数据都存在内存中,总所周知,内存的存取效率比磁盘要高不止一个数量级,Redis的性能必定非常优秀,根据官方数据,Redis可以轻松支持超过10万次QPS的读写频率;
有了以上两个主要特性支撑,虽然它起步较晚,但发展迅速,目前已经成为主流架构中缓存服务的首选。除了以上两个特性,Redis还提供了非常丰富的能力,包括:
- 原子性,Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行,我们能非常方便地实现事务;
- 支持发布、订阅,可以用来实现消息系统;
- 支持过期逻辑,做缓存时非常实用;
- 支持内存中的数据持久化,不用担心服务器宕机带来的灾难性后果;
- 提供了简单的事务功能,能在一定程度上保证事务特性
- 支持Lua脚本,可以利用Lua创造出新的Redis命令;
- 提供了流水线(Pipeline)功能,这样客户端能将一批命令一次性传到Redis,减少了网络的开销,在请求数据较小的情况下,可以大幅提升吞吐量;
- Redis使用单线程模型,预防了多线程可能产生的竞争问题,简单且稳定。
- 原生支持主从复制,为高可用实现提供有力支持;
- 受到社区和各大公司的广泛认可,支持Redis的客户端语言非常多,几乎涵盖了主流的编程语言,例如Java、PHP、Python、C、C++、Nodejs等。
适合Redis的应用场景非常多,本节列几个简单的场景供大家参考:
- 缓存频繁读取但修改频率小的数据 ,比如首页推荐的产品列表等,不怕丢数据,丢了可以从数据库中重新加载;
- 用户Session ,不怕丢数据,丢了用户重新登录即可;
- 缓存批量任务的中间结果。不怕丢数据,丢了重新计算中间数据就可以了;
- 分布式锁。
2. 性能
由于各种原因,Redis的性能非常好,也由于Redis的性能非常好,才会有这么多人关注Redis,但是到底有多好,没有实验数据做支撑,别人问咱们的时候,咱们也不好张嘴就来,在本节中我们来做个简单的性能测试,测试目的主要有两点,第一就是看看Redis的性能到底有多好,在数据上有个感性认识,第二,我们来分析下影响Redis性能的因素到底有哪些。
首先我们总结下Redis性能好的原因:
- 业务数据的存取基于内存实现,内存的IO速度很快;
- Redis使用单线程模型,预防了多线程可能产生的CPU资源争夺造成的性能损耗;
- 支持Pipeline,将一批命令一次性传到Redis,减少了网络的开销。
2.1 性能测试环境
在做性能测试之前,我们把本次测试的环境说清楚。
-
机器性能: 使用一台1核CPU,内存为1G的阿里云服务器去压测一台2核CPU,内存4G的Redis服务器,Redis服务器的CPU型号为2.5 GHz主频的Intel ® Xeon ® E5-2682 v4(Broadwell) 确保压测客户端的机器性能不会遇到瓶颈
-
网络环境: 两台服务器的网卡都使用1000M,在同一个局域网内,内网带宽1000M 两台机器的内网IP分别为:172.17.167.56(Redis服务器)、172.17.167.55(压测机)
-
系统环境 两台服务器都使用CentOS 7,将一下两个参数设置成: vm.overcommit_memory = 1 net.core.somaxconn = 2048
-
Redis相关配置 有三个配置需要注意一下: #后代运行 daemonize yes #不开启applend形式的数据持久化能力 appendfsync no #不开启快照能力
#save
- 测试工具 使用Redis自带的测试工具redis-benchmark进行测试,下面是一条在压测机上运行的测试命令:
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 100 -P 8 -q
我们简单介绍下这条命令的各个参数的语义:
-h 目标Redis服务网络地址
-p 目标Reids服务的端口
-c 客户端并发长连接数
-n 本次测试需要发起的请求数
-t 测试请求的方法
-d 测试请求的数据大小
-P 开启Pipeline模式,并制定Pipeline通道数量
-q 只显示requests per second这一个结果
上面这条命令的语义就是,向172.17.167.56:6379这个Redis发送100万个请求,使用20个长连接发送,所有请求都是set命令,每个set命令的包体为100字节,使用8条Pipeline通道发送,并且只显示requests per second这一个结果。
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 100 -P 8 -q SET: 534759.38 requests per second
2.2 基本性能测试
本节中的性能测试主要观察一个指标,就是Redis每秒处理多少个请求,RPS(Request per second)。
2.2.1 客户端长连接数量对性能的影响
我们做四个测试,分别使用1个长连接,5个长连接,10个长连接,50个长连接发送100万个请求大小为100字节的请求,对比四个测试结果看看客户端长连接数量对Redis服务性能有什么影响:
- 1个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 1 -n 1000000 -t set -d 100 -q SET: 8768.55 requests per second
- 5个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 5 -n 1000000 -t set -d 100 -q SET: 35334.44 requests per second
- 10个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 100 -q SET: 52430.14 requests per second
- 50个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 50 -n 1000000 -t set -d 100 -q SET: 52413.65 requests per second
连接数 | 整体RPS | 单连接RPS |
---|---|---|
1个 | 8768.55 | 8768.55 |
5个 | 35334.44 | 7066.88 |
10个 | 52430.14 | 5243.01 |
50个 | 52413.65 | 1048.27 |
从上面三个测试用例的测试结果来看,我们可以发现:
- 只有一个长连接通信时,RPS是8700左右;
- 在长连接数量增加时,RPS的值会接近线性地增加;
- 长连接数量增加到一定数值时,整个Redis的RPS就稳定了,稳定在52400左右;
客户端长连接的数量会影响Redis整体吞吐量,但长连接数量增长到一个平衡值之后,长连接的数量不再影响系统的整体吞吐量,这个平衡值要看实际情况,网速、请求包大小等因素都会有影响。
2.2.2 请求包大小的影响
请求包的大小肯定会影响Redis每秒处理请求数量,这个是毋庸置疑的,但是具体是怎么影响的,我们做几个实验来观察下:
- 请求包大小为2字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 2 -q SET: 52474.16 requests per second CPU平均损耗:42%
- 请求包大小为1000字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 1000 -q SET: 52430.14 requests per second CPU损耗:48%
- 请求包大小为1400字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 1400 -q SET: 45396.77 requests per second CPU平均损耗:41%
- 请求包大小为1500字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 1500 -q SET: 25518.67 requests per second CPU平均损耗:29%
- 请求包大小为5000字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 5000 -q SET: 12736.74 requests per second CPU平均损耗:24%
- 请求包大小为10000字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 10000 -q SET: 6476.81 requests per second CPU平均损耗:18%
请求包体大小(字节) | 整体RPS | redis server cpu |
---|---|---|
2 | 52474.16 | 42% |
1000 | 52430.14 | 48% |
1400 | 45396.77 | 41% |
1500 | 25518.67 | 29% |
5000 | 12736.74 | 24% |
10000 | 6476.81 | 18% |
从上面六个实验的结果来分析,我们可以得出以下结论:
- 在Redis服务器的CPU资源充足的情况下,2个字节的请求和1000个字节的请求对于Redis的整体吞吐量无任何影响;
- 请求大小在1400字节以内时,Redis的性能表现稳定,当请求大小等于1500字节时,Redis整体性能下降得很厉害。由于一般TCPIP网络的MTU设置为1500字节,一旦测试数据尺寸超过1500字节时会被拆分为多个数据包在网络上传输,加剧了性能下降的幅度。
- 请求大小大于1500之后,Redis的性能随着包体的增加成接近线性关系地下降。
2.3 Pipleline模式
Redis是基于同步的请求应答模型提供服务的,正常情况下,客户端发送一个请求,在等到Redis的应答后才会继续发送第二个请求。在这种情况下,如果同时需要执行大量的命令,每一个长连接的利用率不高,大多数时间都在等待,这种模式长连接的利用率不高,如下图。
Redis 提供了一种聚合请求和应答的pipeline模式,简单说就是讲多个命令聚合在一个请求中发送给Redis,Redis执行这一批命令,在执行过程中,讲执行结果缓存到内存中,等这所有一批命令都被执行完成后,讲所有的命令执行结果放在一个应答中返回给客户端。
Pipeline 在某些场景下非常有用,比如有多个 command对相应结果没有互相依赖,对结果响应也无需立即获得,那么 pipeline 就可以充当这种“批处理”的工具;而且在一定程度上,可以较大的提升性能,性能提升的原因主要是 TCP 连接中减少了“交互往返”的时间。本文图示的例子,三个正常的command一般数据量较小,放在一个pipeline请求,一般一个tcp报文就发送给服务器端了,而非pipeline模式需要发送三次,每次都需要等待应答回来后才能继续发送,在传输与处理效率上,pipeline机制明显要高效很多。下面我们做一个实验来验证下具体效率会高多少。
- 命令大小为100字节,不使用pipeline传输
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 100 -q SET: 52394.43 requests per second
- 命令大小为100字节,使用pipeline传输,每个请求携带8个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 8 SET: 495662.97 requests per second
- 命令包大小为100字节,使用pipeline传输,每个请求携带10个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 10 SET: 558659.25 requests per second
- 命令包大小为100字节,使用pipeline传输,每个请求携带11个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 11 SET: 312940.09 requests per second
- 命令包大小为100字节,使用pipeline传输,每个请求携带15个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 15 SET: 452386.34 requests per second
- 请求包大小为100字节,使用pipeline传输,每个请求携带40个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 5000000 -t set -d 100 -q -P 40 SET: 487519.53 requests per second
- 请求包大小为200字节,不使用pipeline传输
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q SET: 51167.91 requests per second
- 请求包大小为200字节,使用pipeline传输,每个请求携带4个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q -P 4 SET: 220288.56 requests per second
- 请求包大小为200字节,使用pipeline传输,每个请求携带6个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q -P 6 SET: 161147.36 requests per second
- 请求包大小为200字节,使用pipeline传输,每个请求携带8个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q -P 8 SET: 221361.38 requests per second
- 请求包大小为3000字节,不使用pipeline传输
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 3000 -q SET: 21104.17 requests per second
- 请求包大小为3000字节,使用pipeline传输,每个请求携带8个命令
./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 3000 -q -P 8 SET: 21713.17 requests per second
请求包体大小(字节) | 请求携带命令数 | 整体RPS |
---|---|---|
100 | 1 | 52474.16 |
100 | 8 | 495662.97 |
100 | 10 | 558659.25 |
100 | 11 | 312940.09 |
100 | 15 | 452386.34 |
100 | 40 | 487519.53 |
200 | 1 | 51167.91 |
200 | 4 | 220288.56 |
200 | 6 | 161147.36 |
200 | 8 | 221361.38 |
3000 | 1 | 21104.17 |
3000 | 8 | 21713.17 |
从上面一系列实验数据我们可以得出以下结论:
- 命令包体越小时,pipeline机制会对Redis的整体性能帮助越大,命令包体100字节时,redis整体性能有一个数量级左右的提升;
- 一个pineline请求的整体大小(命令包大小乘以命令个数)如果和TCPIP网络的MTU设置比较匹配,不容易产生碎片请求时,性能最好,这个不好强求;
- 命令包体较大时,pipeline机制对Redis的整体性能没有任何帮助。
在命令传输内容较小,且命令之间无依赖关系时,我们使用pipeline机制可以大幅提供Redis的整体吞吐量。有些系统可能对可靠性要求很高,每次操作都需要立马知道这次操作是否成功,是否数据已经写进redis了,那这种场景就不适合。还有命令传输的内容较大时(比如3k及以上),pipeline对性能也没有优化能力,也不建议使用pipeline机制。
3. 数据持久化
Redis是一个支持持久化的内存数据库,也就是说redis支持将内存中的数据同步到磁盘来保证持久化。redis支持两种持久化方式,第一种是定期讲包含全量数据的内存快照保存到磁盘也是默认方式;第二种是记录所有数据写操作的日志,使用这些日志恢复数据,这种模式我们又称之为AOF模式。两种模式各有优劣,下面我们分别了解下两种模式的运行机制。
3.1 内存快照模式持久化
某个瞬间Redis服务器内存中的所有内容我们称之为内存快照,定期将内存快照异步保存到磁盘进行持久化,在数据出现问题,或者服务器宕机等情况出现是,将内存快照加载到内存,这个定期备份、恢复的机制对数据的安全性有重要的意义。
Redis会自动将内存快照保存成一个RDB类型的文件到Redis的根目录,调用BGSAVE命令能手动触发快照保存,保存快照的动作是后台进程完成的,保存快照期间其他客户端仍然和可以读写REDIS服务器。后台保存快照到磁盘时会占用大量内存。
如果调用SAVE命令保存内存中的数据到磁盘,将阻塞客户端请求,直到保存完毕。调用SHUTDOWN命令,Redis服务器会先调用SAVE,所有数据持久化到磁盘之后才会真正退出。在Redis的配置文件中可以配置定期保存内存快照的触发条件:
# save <时间> <变更次数> # 两个条件同时满足,发生一次保存内存快照的动作,如果配置多条规则,规则之间是或的关系 # 如果不想落地内存中的数据,直接注释掉下面三个配置即可 # 如果配置成save "",之前落地的数据都可能被删除 # 下面这条配置的语义是距上次保存快照时间超过60秒,并且数据变更次数达到1000次,则保存一次内存快照 # 内存快照文件格式为dump.rdb save 60 1000
stop-writes-on-bgsave-error yes 这个配置也是非常重要的一项配置,这是当备份进程出错时,主进程就停止接受新的写入操作,是为了保护持久化的数据一致性问题。如果自己的业务对数据一致性有较高的要求,需要打开这个配置。
Redis启动的时候会判断是否存在RDS文件,如果存在就从RDS文件中加载所有数据到内存。
3.2 AOF模式持久化
默认情况下Redis会异步落地内存快照数据到磁盘,这种模式对于很多场景是够用的。但这种模式有个缺点就是对于突发情况,比如突然停电,落地的文件数据会丢失几分钟数据,极端情况丢数据这事对于普通应用程序可能可以接收,但对于类似银行这种机构是苟能容忍的。因此Redis提供一种更可靠的模式来保证数据的安全,AOF是一种可选的更安全的持久化模式,能很好地解决上面说的数据丢失的问题。默认配置下,AOF模式在意外故障发生时最多丢失一秒钟的数据。
AOF文件是可识别的纯文本,它的内容就是一个个的Redis标准命令,有比较好的可读性。AOF日志也不是完全按客户端的请求来生成日志的,比如命令 INCRBYFLOAT 在记AOF日志时就被记成一条SET记录, 因为浮点数操作可能在不同的系统上会不同,所以为了避免同一份日志在不同的系统上生成不同的数据集,所以这里只将操作后的结果通过SET来记录。每一条写命令都生成一条日志,所以AOF文件会很大。
Redis在落地AOF文件的时候,有三种模式
- appendfsync always : 每次有客户端发送写操作,都需要落地到磁盘,性能最差,但最安全。
- appendfsync everysec : 顾名思义,每秒写一次,均衡模式。
- appendfsync no : 操作系统在需要的时候才落地数据到磁盘,性能最好,但可能有数据丢失风险。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。
Redis实用的默认模式是everysec,这是一种均衡的模式。
在AOF同步文件同步模式设置为always或者everysec的时候,会有一个后台线程去做这个事,同时产生大量磁盘IO。这些IO操作经常会阻塞后台内存快照落地线程和AOF日志重写线程,甚至导致整个Redis被阻塞,目前没有很好的解决方案。
为了缓解这个问题,Redis增加了AOF阻塞机制,生成AOF文件之前会先检查BGSAVE或者BGREWRITEAOF是否在运行,如果是,那么就先阻止AOF操作。这就意味这在BGSAVE或者BGREWRITEAOF时,Redis不会去写AOF,可能会因此丢掉30秒以内的数据。如果你因为AOF写入产生延迟问题,可以将AOF阻塞机制的相关配置no-appendfsync-on-rewrite设置为yes。该配置设置为no为最安全,最不可能丢失数据的方式。
AOF和内存快照两种持久化模式能同时启动,不会互相影响。如果AOF模式生效了,那么Redis启动的时候会首先载入AOF文件来保证数据的可靠性。
3.3 AOF重写
在AOF文件增长到足够大超过配置的百分比的时候,Redis提供AOF重写功能,AOF重写会聚合Key的所有操作,目的是让一个KEY只有一条记录留在AOF文件中,从而大大缩小AOF文件的尺寸。
AOF重写是重新生成一份AOF文件,新的AOF文件中一条记录的操作只会有一次,而不像一份老文件那样,可能记录了对同一个值的多次操作。其生成过程和RDB类似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。 在写入新文件的过程中,所有的写操作日志还是会写到原来老的 AOF文件中,同时还会记录在内存缓冲区中。当重完操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中。然后调用原子性的rename命令用新的 AOF文件取代老的AOF文件。重写后,AOF文件变成一个非常小的全量文件。
命令:BGREWRITEAOF, 我们应该经常调用这个命令来来重写。
auto-aof-rewrite-percentage 100
当前的AOF文件大小超过上一次重写的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的AOF大小为依据。
auto-aof-rewrite-min-size 64mb
限制了允许重写的最小AOF文件尺寸。
3.4 从持久化文件中恢复数据
Redis重启的时候会自动从RDS文件或者AOF文件中加载数据到内存。Redis在启动的时候会优先判断AOF持久化文件是否存在,如果存在优先加载AOF持久化文件。如果AOF持久化文件不存在再去检查RDS持久化文件是否存在,存在的话,加载之。为什么优先加载AOF文件呢,因为AOF在持久化上能够做到更加安全。具体流程如下图所示:
3.5 持久化策略选择
不同的业务场景选择不同的持久化策略,具体业务场景分为以下几种:
- 我们仅仅把Redis当做缓存来使用,Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,比如排行榜数据,用户登录信息等,可以关闭持久化,如果丢失数据可以通过其它途径补回,这种情况最高效,也不会引起各种数据不一致问题;
- Redis中的数据独此一份,并没有数据库打底,但Redis中数据并不特别重要,可以忍受丢失一段时间,比如一些统计计算的中间数据,丢失的数据可以根据原始数据重新计算出来,这个时候我们可以选择RDS或者appendfsync always模式以外的所有模式的AOF。这是一种讨巧的持久化方案,既不消耗很多性能,又能在出现意外情况时恢复大部分数据;
- Redis中的数据独此一份,除了没有数据库打底,还非常重要,不允许丢失任何数据。这个时候我们必须使用appendfsync always模式的AOF持久化策略,并且把no-appendfsync-on-rewrite配置打开,保证AOF吃花花过程出现问题的时候,Redis拒绝服务,不会丢失任何数据。
还有一种做法就是将Redis的主从配置打开,利用一台从服务器去做持久化,其他服务器快速应答业务请求。
4. Redis数据结构详解
Redis并不是简单的key-value存储,实际上他是一个数据结构服务器,支持不同类型的值。也就是说,你不必仅仅把字符串当作键所指向的值。下列这些数据类型都可作为值类型。
二进制安全的 字符串 string
二进制安全的 字符串列表 list of string
二进制安全的 字符串集合 set of string,换言之:它是一组无重复未排序的element。可以把它看成JAVA中的HashSet。
有序集合sorted set of string,类似于集合set,但其中每个元素都和一个浮点数score(评分)关联。element根据score排序。可以把它看成JAVA的HashMap–其key等于element,value等于score,但元素总是按score的顺序排列,无需额外的排序操作。
4.1 Key
Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key值,比如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值。
关于key的几条规则:
太长的键值不是个好主意,例如1024字节的键值就不是个好主意,不仅因为消耗内存,而且在数据中查找这类键值的计算成本很高。
太短的键值通常也不是好主意,如果你要用”u:1000:pwd”来代替”user:1000:password”,这没有什么问题,但后者更易阅读,并且由此增加的空间消耗相对于key object和value object本身来说很小。当然,没人阻止您一定要用更短的键值节省一丁点儿空间。
最好坚持一种模式。例如:”object-type
这篇关于如何正确使用redis的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-18Redis安装入门:新手必读指南
- 2024-11-08阿里云Redis项目实战入门教程
- 2024-11-08阿里云Redis资料:新手入门与初级使用指南
- 2024-11-08阿里云Redis教程:新手入门及实用指南
- 2024-11-07阿里云Redis学习入门:新手必读指南
- 2024-11-07阿里云Redis学习入门:从零开始的操作指南
- 2024-11-07阿里云Redis学习:初学者指南
- 2024-11-06阿里云Redis入门教程:轻松搭建与使用指南
- 2024-11-02Redis项目实战:新手入门教程
- 2024-10-22Redis入门教程:轻松掌握数据存储与操作