Redis 配置与优化
一、Redis 介绍
1. 关系数据库与非关系型数据库
数据库按照数据库的结构可以分为关系型数据库与其他数据库,而这些其他数据库我们将其统称为非关系型数据库。
1.1 关系型数据库
关系型数据库是一个结构化的数据库,创建在关系模型基础上,一般面向于记录。它借助于集合代数等数学概念和方法来处理数据库中的数据。关系模型就是指二维表格模型,因而一个关系型数据库就是由二维表及其之间的联系组成的一个数据组织。现实世界中,各种实体与实体之间的各种联系都可以用关系模型来表示。SQL 语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作。
主流的关系型数据库包括 Oracle、MySQL、SQL Server、Microsoft Access、DB2 等。
1.2 非关系型数据库
NoSQL(NoSQL = Not Only SQL ),意思是 “不仅仅是 SQL”,是非关系型数据库的总称。主流的 NoSQL 数据库有 Redis、MongoBD、Hbase、CouchDB 等等。以上这些非关系型数据库,他们的存储方式、存储结构以及使用的场景都是完全不同的。所以我们认为它是一个非关系型数据库的集合,而不是像关系型数据库一样,是一个统称。换言之,除了主流的关系型数据库以外的数据系,都可以认为是非关系型的。NoSQL 数据库凭借着其非关系型、分布式、开源及横向扩展等优势,被认为是下一代数据库产品。
1.3 非关系型数据库产生背景
关系型数据库已经诞生很久了,而且一直在使用。面对这样的情况,为什么还会产生 NoSQL?那么,下面就来介绍一下 NoSQL 产生的背景。
随着 Web2.0 网站的兴起,关系型数据库在应对 Web2.0 网站,特别是海量数据和高并发的 SNS(Social Networking Services,即社交网络服务)类型的 Web2.0 纯动态网站时,暴露出很多难以解决的问题,例如三高问题。
类别 | 英文表述 | 需求描述 |
---|---|---|
高性能 | High performance | Web2.0 网站因需根据用户个性化信息实时生成动态页面和提供动态信息,无法用动态页面静态化技术,数据库并发负载高,一般达 10000 次 /s 以上读写请求,关系型数据库难承受上万次写数据请求(硬盘 IO 不堪重负 ),普通 BBS 网站也有高并发写数据请求 |
海量存储 | Huge Storage | 类似 Facebook、Friendfeed 的 SNS 网站每天产生大量用户动态信息(如 Friendfeed 一月产生不少于 2.5 亿条 ),关系型数据库在含 2.5 亿条记录的表中执行 SQL 查询效率极低 |
高扩展性与高可用性 | High Scalability && High Availability | Web 架构中数据库最难横向扩展,应用系统用户量和访问量增加时,难像 Web 服务通过添加硬件和服务器节点扩展性能与负载能力;对 24 小时对外服务的网站,数据库升级扩展常伴随停机维护与数据迁移,工作量大 |
关系型数据库和非关系型数据库都有各自的特点与应用场景,两者的紧密结合将会给 Web2.0 的数据库发展带来新的思路。让关系数据库关注在关系上,非关系型数据库关注在存储上。例如,在读写分离的 MySQL 数据库环境中,可以把经常访问的数据存储在非关系型数据库中,提升访问速度。
2. Redis 基础
2.1 Redis 简介
Redis(RemoteDictionaryServer,远程字典型)是一个开源的、使用 C 语言编写的 NoSQL 数据库。Redis 基于内存运行并支持持久化,采用 key - value(键值对)的存储形式,是目前分布式架构中不可或缺的一环。
Redis 服务器程序是单进程模型,也就是在一台服务器上可以同时启动多个 Redis 进程,而 Redis 的实际处理速度则是完全依靠于主进程的执行效率。若在服务器上只运行一个 Redis 进程,当多个客户端同时访问时,服务器的处理能力是会有一定程度的下降;若在同一台服务器上开启多个 Redis 进程,Redis 在提高并发处理能力的同时会给服务器的 CPU 造成很大压力。即:在实际生产环境中,需要根据实际的需求来决定开启多少个 Redis 进程。若对高并发要求更高一些,可能会考虑在同一台服务器上开启多个进程。若 CPU 资源比较紧张,采用单进程即可
特性 | 详情 |
---|---|
数据读写速度 | 数据读取速度最高可达 110000 次 /s,数据写入速度最高可达 81000 次 /s |
支持数据类型 | 支持简单 key - value 类型,还支持 Strings、Lists、Hashes、Sets 及 Ordered Sets 等数据类型操作 |
数据持久化 | 可将内存中数据保存到磁盘,重启可再次加载使用 |
原子性 | 所有操作都是原子性的 |
数据备份 | 支持 master - salve 模式的数据备份 |
Redis 作为基于内存运行的数据库,缓存是其最常应用的场景之一。除此之外,Redis 常见应用场景还包括获取最新 N 个数据的操作、排行榜类应用、计数器应用、存储关系、实时分析系统、日志记录。
2.2 Redis 安装部署
Redis 的安装相对于其他服务来说比较简单 。首先需要到 Redis 官网(https://www.redis.io)下载相应的源码软件包,然后上传至 Linux 系统的服务器中进行解压、安装。本章中以 redis-4.0.9.tar.gz 为例进行 Redis 服务的安装和配置讲解。
通常情况下,在 Linux 系统中进行源码编译安装,需要先执行./configure 进行环境检查与配置,从而生成 Makefile 文件,再执行 make && make install 命令进行编译安装。而 Redis 源码包中直接提供了 Makefile 文件,所以在解压完软件包后,可直接进入解压后的软件包目录,执行 make 与 make install 命令进行安装
#安装依赖环境
dnf -y install gcc zlib-devel#解压
tar zxf redis-4.0.9.tar.gz#编译安装、并创建软连接
make && make PREFIX=/usr/local/redis install && ln -s /usr/local/redis/bin/* /usr/local/bin/
make install 只是安装了二进制文件到系统中,并没有启动脚本和配置文件。
软件包中默认提供了一个 install_server.sh 脚本文件,通过该脚本文件可以设置 Redis 服务所需要的相关配置文件。当脚本运行完毕,Redis 服务就已经启动,默认侦听端口为 6379
# 端口号,Redis 服务监听的网络端口,客户端通过该端口连接 Redis 服务
Port : 6379 # 配置文件路径,Redis 服务启动时加载的配置文件,里面包含各种服务参数设置
Config file : /etc/redis/6379.conf # 日志文件路径,Redis 运行过程中的日志会记录到这个文件,用于排查问题、查看运行状态等
Log file : /var/log/redis_6379.log # 数据目录,Redis 持久化数据(比如 RDB 快照、AOF 文件等相关数据)存储的目录
Data dir : /var/lib/redis/6379 # Redis 服务可执行文件路径,启动 Redis 服务时会调用这个程序
Executable : /usr/local/bin/redis-server # Redis 命令行客户端可执行文件路径,用于通过命令行连接、操作 Redis 服务
Cli Executable : /usr/local/bin/redis-cli
2.3、查看监听
netstat -anpt | grep redis
2.4、配置参数
Redis 主配置文件为 /etc/redis/6379.conf,由注释行与设置行两部分组成。与大多数 Linux 配置文件一样,注释性的文字以 “#” 开始,包含了对相关配置内容进行的说明和解释。除了注释行与空行以外的内容即为设置行。可根据生产环境的需求调整相关参数,如下:
vim /etc/redis/6379.conf# 将 127.0.0.1 和 192.168.10.101 绑定到 Redis 服务,
# 用于指定 Redis 监听的网络地址,这样 Redis 就会在这些地址上接受客户端连接
bind 127.0.0.1 192.168.10.101 70行# 设置 Redis 服务监听的端口号,客户端通过 6379 端口与 Redis 进行通信
port 6379 93行# 以守护进程(后台进程)方式运行 Redis 服务,
# 这样 Redis 启动后不会占用当前终端,而是在后台持续运行
daemonize yes 137行# 指定 Redis 守护进程模式运行时,保存进程 ID(PID)的文件路径,
# 方便进行进程管理(如查看、停止等操作时通过该文件找到对应的进程)
pidfile /var/run/redis_6379.pid 159行# 设置 Redis 的日志记录级别,notice 级别会记录较为重要的提示性信息,
# 不同级别(如 debug、verbose、notice、warning 等)决定了日志内容的详细程度
loglevel notice 167行# 指定 Redis 日志文件的存储路径,Redis 运行过程中的日志信息会写入该文件,
# 用于排查问题、了解服务运行状态等
logfile /var/log/redis_6379.log 172行
2.5、重启服务查看监听
/etc/init.d/redis_6379 restartnetstat -anpt | grep redis
参数 | 作用 |
---|---|
timeout 300 | 当客户端闲置多长时间后关闭连接,指定为 0 表示关闭该功能 |
dbfilename dump.rdb | 指定本地数据库文件名,默认值为 dump.rdb |
dir /var/lib/redis/6379 | 指定本地数据库存放目录 |
maxclients 10000 | 设置同一时间最大客户端连接数,默认 10000 ;设为 0 不限 制,达限制关闭新连接并返回错误信息 |
rdbcompression yes | 指定存储至本地数据库时是否压缩数据,默认 yes ,为省 CPU 可关 闭,但数据库文件会变大 |
slaveof <masterip><masterport> | 本机为从服务器时,设置主服务 IP 地址及端口,启动时自动从主 服务同步数据 |
masterauth <master-password> | 主服务设密码保护时,从服务连接主服务的密码 |
requirepass foobared | 设置 Redis 连接密码,配置后客户端需用 AUTH <password> 命令 提供密码,默认关闭 |
maxmemory <bytes> | 指定 Redis 最大内存限制;达限制先清过期 Key ,仍超则无法写 入但可读取;新 VM 机制 Key 存内存、Value 存 Swap 分区 |
appendonly no | 指定是否每次更新操作后日志记录,默认异步写磁盘;不开启可 能断电丢数据,默认 no |
appendfilename appendonly.aof | 指定更新日志文件名,默认 appendonly.aof |
appendfsync everysec | 指定更新日志条件,可选 no(等系统缓存同步,快 )、always(每次更新调 fsync() 写盘,慢且安全 )、everysec(每秒同步一次,折衷,默认 ) |
activerehashing yes | 指定是否激活重置哈希,默认为开启 |
include /path/to/local.conf | 指定包含其他配置文件,实现同一主机多 Redis 实例共用基础配 置,又有各自特定配置 |
3、Redis 命令工具
工具名称 | 功能描述 |
---|---|
redis - server | 用于启动 Redis 的工具 |
redis - benchmark | 用于检测 Redis 在本机的运行效率 |
redis - check - aof | 修复 AOF 持久化文件 |
redis - check - rdb | 修复 RDB 持久化文件 |
redis - cli | Redis 命令行工具 |
3.1、redis-cli 命令行工具
Redis 数据库系统是典型 C/S(客户端 / 服务器端)架构应用,访问其数据库需用专门客户端软件,自带的 redis-cli 命令行工具就是 Redis 服务的客户端软件。使用 redis-cli 连接指定数据库,成功后进入提示符为 “远程主机 IP 地址:端口号>”(如 “127.0.0.1:6379>” )的数据库操作环境,用户可输入操作语句管理数据库,执行 ping 命令可检测 Redis 服务是否启动 。
以下是提取的内容: 在进行数据库连接操作时,可以通过选项来指定远程主机上的Redis数据库。命令语法为redis - cli - h host - p port - a password,其中 - h指定远程主机、 - p指定Redis服务的端口号、 - a指定密码。若不添加任何选项表示,连接本机上的Redis数据库;若未设置数据库密码可以省略 - a选项。例如执行以下命令可连接到主机为192.168.10.161,端口为6379的Redis数据库,并查看Redis服务的统计信息。若要退出数据库操作环境,执行“exit”或“quit”命令即可返还原来的Shell环境。
redis-cli -h 192.168.10.101 -p 6379
帮助命令格式 | 功能描述 |
---|---|
help @<group> | 获取<group>中的命令列表 |
help <command> | 获取某个命令的帮助 |
help <tab> | 获取可能帮助的主题列表 |
栗子:
#查看所有与 List 数据类型的相关命令
help @list
3.2 redis-benchmark 测试工具
redis-benchmark 是官方自带的 Redis 性能测试工具,可以有效的测试 Redis 服务的性能。
基本的测试语法为:
redis-benchmark [option] [option value]
选项 | 说明 |
---|---|
-h | 指定服务器主机名 |
-p | 指定服务器端口 |
-s | 指定服务器 socket |
-c | 指定并发连接数 |
-n | 指定请求数 |
-d | 以字节的形式指定 SET/GET 值的数据大小 |
-k | 1=keep alive 0=reconnect |
-r | SET/GET/INCR 使用随机 key,SADD 使用随机值 |
-P | 通过管道传输<numreq>请求 |
-q | 强制退出 redis,仅显示 query/sec 值 |
--csv | 以 CSV 格式输出 |
-l | 生成循环,永久执行测试 |
-t | 仅运行以逗号分隔的测试命令列表 |
-I | Idle 模式,仅打开 N 个 idle 连接并等待 |
结合上述选项,可以针对某台 Redis 服务器进行性能检测,如执行 redis - benchmark - h 192.168.10.161 -p 6379 -c 100 -n 100000 命令即可向 IP 地址为 192.168.10.161、端口为 6379 的 Redis 服务器发送 100 个并发连接与 100000 个请求测试性能 。 主要介绍了利用 redis - benchmark 命令对特定 Redis 服务器(IP 192.168.10.161 、端口 6379 )进行性能检测的操作及参数含义,-h 指定 IP,-p 指定端口,-c 指定并发连接数,-n 指定请求数
redis-benchmark -h 192.168.10.101 -p 6379 -c 100 -n 100000
测试存取大小为 100 字节的数据包的性能
redis-benchmark -h 192.168.10.101 -p 6379 -q -d 100
测试本机上 Redis 服务在进行 set 与 lpush 操作时的性能
4、Redis 数据库常用命令
前面提到 Redis 数据库采用 key - value(键值对)的数据存储形式。所使用的命令是 set 与 get 命令
命令 | 功能描述 | 基本命令格式 |
---|---|---|
set | 存放数据 | set key value |
get | 获取数据 | get key |
# 使用 SET 命令,将键为 "name" 的数据,设置值为 "zhangsan",用于存储字符串类型的键值对数据
set name zhangsan
# 使用 GET 命令,根据键 "name" 获取对应的值,这里会返回之前设置的 "zhangsan"
get name
4.1、key命令
# keys * 命令:
# 作用:在 Redis 中,匹配当前数据库里所有的键(key)
# 说明:* 是通配符,代表任意字符组合,执行后会返回数据库中存在的所有键名列表
keys * # keys a* 命令:
# 作用:匹配 Redis 数据库中,以字母 "a" 开头的所有键(key)
# 说明:a* 里的 a 是固定前缀,* 匹配后续任意字符,比如 aaa、ab、a123 这类以 a 开头的键都会被匹配到
keys a* # keys a? 命令:
# 作用:匹配 Redis 数据库中,以字母 "a" 开头且总长度为 2 个字符的键(key)
# 说明:a? 里的 a 是固定前缀,? 是单个字符通配符,只会匹配像 aa、ab、a1 这类以 a 开头且后面跟着 1 个任意字符的键
keys a?
4.2、exists命令
根据返还值是否唯一判断键是否存在
4.3、type命令
查看键的类型
4.4、rename、renamenx命令
4.5、dbsize命令
查看当前库键的个数
4.6、select 数据库名(0~15)切换数据库
4.7、move移动键到指定的库
4.8、flushdb、flushall清除数据库内数据
Redis 数据库的整库数据删除主要分为两个部分:清空当前数据库数据,使用 FLUSHDB 命令实现;清空所有数据库的数据,使用 FLUSHALL 命令实现。但数据清空操作比较危险,生产环境下一般不建议使用
二.Redis 持久化
Redis 是一种高级 key-value 数据库。它跟 Memcached 类似,不过数据可以持久化,而且支持的数据类型很丰富,有字符串、列表、集合和有序集合。支持在服务器端计算集合(difference)等,还支持多种排序功能。所以 Redis 也可以被看成是一个数据结构服务器。
Redis 的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为 “半持久化模式” );也可以把每一次数据变化都写入到一个 append only file(aof)里面(这称为 “全持久化模式” )。
由于 Redis 的数据都存放在内存中,如果没有配置持久化,Redis 重启后数据就全丢失了。所以,需要开启 Redis 的持久化功能,将数据保存到磁盘上,当 Redis 重启后,可以从磁盘中恢复数据。Redis 提供两种方式进行持久化,一种是 RDB 持久化(原理是将 Reids 在内存中的数据库记录定时 dump 到磁盘上的 RDB 持久化),另外一种是 AOF(append only file)持久化(原理是将 Reids 的操作日志以追加的方式写入文件)。那么这两种持久化方式有什么区别呢?在实际使用的时候该如何选择呢?下面简单介绍一下二者的区别
AOF 持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录
2. RDB 和 AOF 的优缺点
2.1 RDB 优缺点
RDB 优点
一旦采用该方式,那么整个 Redis 数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,计划每个小时归档一次最近 24 小时的数据,同时还要每天归档一次最近 30 天的数据。通过这样的备份策略,一旦系统出现灾难性故障,可以非常容易地进行恢复。
对于灾难恢复而言,RDB 是非常不错的选择。可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
性能最大化,对于 Redis 的服务进程而言,在开始持久化时,它唯一需要做的只是 fork 出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行 IO 操作了。
相比于 AOF 机制,如果数据集很大,RDB 的启动效率会更高。
RDB 缺点
如果想保证数据的高可用性,即最大限度的避免数据丢失,那么 RDB 将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟
2.2 AOF 优缺点
AOF 的优点
AOF 机制可带来更高数据安全性(数据持久性)。Redis 提供每秒同步、每次修改同步、不同步 3 种同步策略。每秒同步异步完成,效率高,但系统宕机时一秒内修改数据可能丢失;每次修改同步是同步持久化,数据变化立即记录到磁盘,效率最低 。
因对日志文件写入用 append 模式,写入中宕机不破坏已有内容。若操作写一半系统崩溃,Redis 下次启动前可用 redis - check - aof 工具解决数据一致性问题 。
日志过大时,Redis 可自动启用 rewrite 机制。Redis 以 append 模式写修改数据到旧磁盘文件,同时创建新文件记录期间修改命令,rewrite 切换时能更好保证数据安全 。
AOF 包含格式清晰、易理解的日志文件记录所有修改操作,可通过该文件重建数据 。
AOF 的缺点
相同数据集下,AOF 文件通常比 RDB 文件大,RDB 恢复大数据集速度比 AOF 快 。
因同步策略不同,AOF 运行效率往往慢于 RDB。每秒同步策略效率较高,同步禁用策略效率和 RDB 一样高效 。
二者选择标准:看系统愿牺牲性能换更高缓存一致性(AOF),还是写操作频繁时不用备份换更高性能,待手动运行 save 再备份(RDB
3. Redis 持久化配置
3.1 RDB 持久化配置
Redis 会将数据集的快照 dump 到 dump.rdb 文件中。此外,也可以通过配置文件来修改 Redis 服务器 dump 快照的频率。在打开 6379.conf 文件之后,搜索 save,可以看到如下所示配置信息。
背景知识:RDB(Redis DataBase)是 Redis 的一种持久化方式,它按照指定时间间隔将内存中的数据集生成快照并保存到磁盘(即 dump 到 dump.rdb 文件 ),重启 Redis 时可通过加载该文件恢复数据,常用于数据备份、灾难恢复等场景 ,通过配置 save 相关规则(如 “save 900 1” 表示 900 秒内有 1 次修改就触发快照生成 )来控制 RDB 持久化的频率
配置格式 | 时间(秒) | 时间(分钟) | key 变化数量条件 | 操作 |
---|---|---|---|---|
save 900 1 | 900 | 15 | 至少 1 个 | dump 内存快照 |
save 300 10 | 300 | 5 | 至少 10 个 | dump 内存快照 |
save 60 10000 | 60 | 1 | 至少 10000 个 | dump 内存快照 |
3.2 AOF 持久化配置
appendonly no 改为yes 开启 AOF
同步方式 | 描述 |
---|---|
appendfsync always | 每次有数据修改发生时都会写入 AOF 文件 |
appendfsync everysec | 每秒钟同步一次,为 AOF 的缺省策略 |
appendfsync no | 从不同步,高效但数据不会被持久化 |
4. AOF 重写
Redis 会不断地将被执行的命令记录到 AOF 文件里,随着 Redis 持续运行,AOF 文件体积会不断增长,极端情况下可能用尽硬盘可用空间。Redis 重启后需通过重新执行 AOF 文件记录的写命令还原数据集,若 AOF 文件体积过大,还原操作时间会很长。
为解决 AOF 文件体积增大问题,用户可向 Redis 发送 BGREWRITEAOF 命令,该命令通过移除 AOF 文件中的冗余命令来重写 AOF 文件,减小其体积 。
BGREWRITEAOF 工作原理和 BGSAVE 创建快照相似:Redis 会创建子进程,由子进程负责重写 AOF 文件。因用到子进程,快照持久化因创建子进程导致的性能和内存占用问题,在 AOF 持久化中也存在 。
与快照持久化通过设置 save 选项自动执行 BGSAVE 类似,AOF 持久化可通过设置 auto - aof - rewrite - percentage 选项和 auto - aof - rewrite - min - size 选项自动执行 BGREWRITEAOF
举例,若用户为 Redis 设置配置选项 auto - aof - rewrite - percentage 100 和 auto - aof - rewrite - min - size 64mb ,且启动 AOF 持久化,当 AOF 文件体积大于 64MB ,且比上一次重写后的体积大至少一倍(100%)时,Redis 将执行 BGREWRITEAOF 命令。若重写执行过频,可考虑将 auto - aof - rewrite - percentage 选项值设为 100 以上,这会让 Redis 在 AOF 文件体积更大后才执行重写,但也会使 Redis 启动时还原数据集所需时间更长
三.性能管理
Redis 性能管理需要关注的数据指标有内存使用率、内存碎片率、回收 key 等。这其中有些数据可以通过进入 info 命令进行查看。需要查看某一项的值就后面跟具体参数,执行以下命令查看 Redis 使用内存值
1、内存碎片率
上述信息中的 mem_fragmentation_ratio 给出了内存碎片率的数据指标,它是由操系统分配的内存值 used_memory_rss 除以 Redis 使用的内存值 used_memory 得出的。内存值 used_memory_rss 中的 rss 是 Resident Set Size 的缩写,表示该进程所占物理内存的大小,即为操作系统分配给 Redis 实例的内存大小。
除了用户定义的数据和内部开销以外,used_memory_rss 指标还包含了内存碎片的开销,内存碎片是由操作系统低效的分配 / 回收物理内存导致的。操作系统负责分配物理内存给各个应用进程,Redis 使用的内存与物理内存的映射是由操作系统上虚拟内存管理分配器完成的。
举例来说:Redis 需要分配连续内存块来存储 1G 的数据集。如果物理内存上没有超过 1G 的连续内存块,那操作系统就不得不使用多个不连续的小内存块来分配并存储这 1G 数据,该操作就会导致内存碎片的产生。
内存分配器另一个复杂的层面是,系统会预先分配一下内存区块给应用程序,这样可以加快应用程序运行速度,但会产生另外一个问题,就是预先分配内存区块大小的问题。比如运行一个 5G 大小应用,需要 5 个 1G 大小的内存区块,结果系统就分配了 5 个 2G 的内存区块,这样就浪费了 5G 的运行内存空间,同样就产生内存碎片率。
内存碎片率对理解 Redis 实例的资源性能是非常重要的。内存碎片率稍大于 1 是合理的,这个值表示内存碎片率比较低,也说明 Redis 没有发生内存交换。但如果内存碎片率超过 1.5,那就说明 Redis 消耗了实际需要物理内存的 150%,其中 50% 是内存碎片率。若是内存碎片率低于 1 的话,说明 Redis 内存分配超出了物理内存,操作系统正在进行内存交换。内存交换会引起非常明显的响应延迟。
若生产环境内存碎片率过高,会导致 Redis 性能降低。解决该情况的常见方案有三种
1.1、内存碎片率超过 1.5 相关内容
重启 Redis 服务器可让额外产生的内存碎片失效并重新作为新内存使用,使操作系统恢复高效内存管理。额外碎片产生源于 Redis 释放内存块,但内存分配器(编译时指定,可为 libc、jemalloc 或 tcmalloc )未返回内存给操作系统。
通过比较 used_memory_peak、used_memory_rss 和 used_memory_metrics 数据指标值,可检查额外内存碎片占用。used_memory_peak 是 Redis 内存使用峰值(非当前值 ),若其与 used_memory_rss 值大致相等且明显超过 used_memory 值,说明有额外内存碎片产生。
重启服务器前,需在 redis - cli 工具输入 shutdown save 命令,强制 Redis 数据库执行保存操作并关闭服务,保证关闭时不丢数据;重启后 Redis 从硬盘加载持久化文件,确保数据集可用
1.2、如果内存碎片率低于 1
Redis 实例可能会把部分数据交换到硬盘上,内存交换严重影响 Redis 性能,应增加可用物理内存或减少 Redis 内存占用。
1.3、修改内存分配相关
Redis 支持 libc、jemalloc11、tcmalloc 三种不同内存分配器,各分配器在内存分配和碎片上实现不同;不建议运维人员修改 Redis 默认内存分配器,因需完全理解差异且要重新编译 Redis,该方法更多是用于了解 Redis 内存分配器工作
2、内存使用率
内存使用率是 Redis 服务最关键的一部分。如果一个 Redis 实例的内存使用率超过可用最大内存,那么操作系统开始进行内存与 swap 空间交换,把内存中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫 swap 分区),以便腾出新的物理内存给新页或活动页 (page) 使用。
used_memory 字段数据表示的是由 Redis 分配器分配的内存总量,以字节为单位。其中 used_memory_human 上的数据和 used_memory 是一样的值,它以 M 为单位显示,目的是为了方便阅读。
used_memory 是 Redis 使用的内存总量,它包含了实际缓存占用的内存和 Redis 自身运行所占用的内存 (如元数据、lua)。它是由 Redis 使用内存分配器分配的内存,所以这个数据并没有把内存碎片浪费掉的内存给统计进去。Redis 默认最大使用内存是可用物理内存剩余的所有内存,0 代表没有限制。
在硬盘上进行读写操作要比在内存上进行读写操作慢很多。如果 Redis 进程上发生内存交换,那么 Redis 和依赖 Redis 上数据的应用会受到严重的性能影响。 通过查看 used_memory 指标可知道 Redis 正在使用的内存情况,如果 used_memory 大于可用最大内存,那就说明 Redis 实例正在进行内存交换或者已经完成内存交换。运维人员应该根据这个情况执行相应的应急措施。
2.1 针对缓存数据大小选择
如果缓存数据小于 4GB,就使用 32 位的 Redis 实例。因为 32 位实例上的指针大小只有 64 位的一半,它的内存空间占用空间会更少些。这样有一个坏处就是,假设物理内存超过 4GB,那么 32 位实例能使用的内存仍然会被限制在 4GB 以下。要是实例同时也共享给其他一些应用使用的话,那可能需要更高效的 64 位 Redis 实例,这种情况下切换到 32 位是不可取的。不管使用哪种方式,Redis 的 dump 文件在 32 位和 64 位之间是互相兼容的,因此倘若有减少占用内存空间的需求,可以尝试先使用 32 位,后面再切换到 64 位上。
2.2 使用 Hash 数据结构
因为 Redis 在储存小于 100 个字段的 Hash 结构上,其存储效率是非常高的。所以在不需要集合 (set) 操作或 list 的 push/pop 操作时,尽可能的使用 Hash 结构。例如在一个 Web 应用程序中,需要存储一个对象表示用户信息,使用单个 key 表示一个用户,其每个属性存储在 Hash 的字段里,这样要比给每个属性单独设置一个 key - value 要高效的多。通常情况下倘若有数据使用 string 结构,用多个 key 存储时,那么应该转换成单 key 多字段的 Hash 结构。如上述例子中介绍的 Hash 结构应包含单个对象的属性或者单个用户各种各样的资料。Hash 结构的操作命令是 HSET (key, fields, value) 和 HGET (key, field),使用它可以存储或从 Hash 中取出指定的字段。
2.3 设置 key 的过期时间
一个减少内存使用率的简单方法就是,每当存储对象时确保设置 key 的过期时间。倘若 key 在明确的时间周期内使用或者旧 key 不大可能被使用时,就可以用 Redis 过期时间命令 (expire, expireat, pexpire, pexpireat) 去设置过期时间,这样 Redis 会在 key 过期时自动删除 key。假如知道每秒钟有多少个新 key - value 被创建,那可以调整 key 的存活时间,并指定阈值去限制 Redis 使用的最大内存。
3. 回收 key
当内存使用达到设置的最大阈值时,需要选择一种 key 的回收策略,可在 redis.conf 配置文件中修改 “maxmemory - policy” 属性值。默认情况下回收策略是禁止删除,若是 Redis 数据集中的 key 都设置了过期时间,那么 “volatile - ttl” 策略是比较好的选择。但如果 key 在达到最大内存限制时没能够迅速过期,或者根本没有设置过期时间。那么设置为 “allkeys - lru” 值比较合适,它允许 Redis 从整个数据集中挑选最近最少使用的 key 进行删除(LRU 淘汰算法)
策略名称 | 策略描述 |
---|---|
volatile-lru | 使用 LRU 算法从已设置过期时间的数据集合中淘汰数据 |
volatile-ttl | 从已设置过期时间的数据集合中挑选即将过期的数据淘汰 |
volatile-random | 从已设置过期时间的数据集合中随机挑选数据淘汰 |
allkeys-lru | 使用 LRU 算法从所有数据集合中淘汰数据 |
allkeys-random | 从数据集合中任意选择数据淘汰 |
no-eviction | 禁止淘汰数据 |
info stats
信息里的evicted_keys
字段,含义是因maxmemory
限制致key
被回收删除的数量 。
Redis 因内存压力回收key
时,不优先回收最旧数据,而是从最近最少使用或即将过期的key
里随机选一个从数据集中删除
通过文字描述和参数对比,可依据 key 回收定位性能问题,回收 key 能合理分配 Redis 有限内存资源,evicted_keys 值常超 0 会使客户端命令响应延迟增加,因 Redis 既要处理请求又要频繁回收 key 。
回收 key 对性能影响远不及内存交换严重,强制内存交换和设置回收策略二选一时,放弃强制内存交换更合理,因内存数据交换到硬盘对性能影响极大。虽频繁回收 key 会致性能问题,需减少回收提升性能,经验而言,开启快照功能时 maxmemory 设为物理内存 45%,不易引发内存交换;未开启则设为系统可用内存 95% 较合理 。
另一种分片技术是将数据分割成合适大小,存于不同 Redis 实例,每个实例含数据集一部分。分片可联合多服务器存储数据,相当于增加总物理内存,无内存交换和回收 key 策略时也能存更多 key。若数据集大,maxmemory 设后实际内存使用超推荐阈值,数据分片可明显减少 key 回收,提升 Redis 性能