redis
- 一、redis理论概述
- 1.1 redis简介
- 1.2 redis优点
- 1.3 redis与memcached区别
- 1.4 关系型数据库与非关系型数据库
- 二、安装部署redis
- 2.1redis服务控制
- 2.2 修改配置参数
- 2.3 redis常用命令工具
- 2.4redis-benchmark测试工具
- 2.5 redis数据库常用命令
- 2.6 redis多数据库常用命令
- 三、redis高可用
- 3.1 redis高可用概述
- 3.2 持久化的功能
- 3.3 持久化RDB和AOF区别
- 3.4 持久化RDB和AOF的优缺点
- 四、redis持久化
- 4.1 RDB
- 4.1.1 触发条件
- 4.1.2 执行流程
- 4.1.3 启动时加载
- 4.2 AOF
- 4.2.1 开启AOF
- 4.2.2 执行流程(命令追加、文件写入、文件重写)
- 4.2.3 文件重写流程
- 4.2.4 AOF缓存区同步文件策略的方式(三种)
- 4.2.5 启动时加载
- 五、redis性能管理
- 5.1 查redis内存使用
- 5.2 内存碎片率
- 5.3 内存使用率
- 5.4 内回收key
- 六、总结
- 6.1 reids
- 6.2 关系与非关系型数据库
一、redis理论概述
1.1 redis简介
1.2 redis优点
- 支持丰富的数据类型:key-value、Strings、Lists、Hashes、Sets、Ordered Sets等等
- Strings:字符串(可以为整形、浮点型和字符串,统称为元素)
- List:列表(实现队列,元素不唯一。先入先出原则)
- set:集合(各不相同的元素)
- hash:hash散列值(hash的key必须是唯一的)
- Set:集合
- Ordered Sets:有序集合
1.3 redis与memcached区别
数据库 | 类型 | 过期策略 | 数据类型 | 持久化 | 主从复制 | 虚拟内存 |
rdis | K-V | 支持 | 五大数据类型 | 支持 | 支持 | 支持 |
memcached | K-V | 支持 | 单一数据类型 | 不支持 | 不支持 | 不支持 |
1.4 关系型数据库与非关系型数据库
- 关系型数据库
- 关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。
- SQL语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作主流的关系型数据库,包括Oracle、MySQL、SQL Server、Microsoft Access、DB2等等。
- 非关系型数据库
- NoSQL(Not Only SQL,不仅仅是SQL,是非关系数据库的总称)
- 除了主流的关系型数据库外的数据库都认为是非关系型数据库
- 主流的NoSQL数据库有Redis、MongDB、Hbase、Memcached等等
- 因为Web2.0纯动态网站类型的会出现三高问题,所以有了非关系数据库
- High performance(对数据库高并发读写需求)
- HugeStorage(对海量数据高效存储与访问需求)
- High Scalability && High Availability(对数据库高可扩展性与高可用性需求)
- 关系型数据库和非关系型数据库区别(3个方向)
- ①数据存储方式不同
- 关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。
- 与其相反,非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。
- 非关系数据通常存储在数据集中,就像文档、键值对或者图结构
- ②扩展方式不同
- SQL和NoSQL数据库最大的差别是在扩展方式上
- 要支持更多并发量,SQL数据库是纵向扩展,也就是说提高处理能力,使用速度更快速的计算机,这样处理相同的数据集就更快了。因为数据存储在关系表中,操作的性能瓶颈可能设计很多个表,这都需要通过提高计算机性能来客服。虽然SQL数据库有很大扩展空间,但最终肯定会达到纵向扩展的上限
- 但是NoSQL数据库是横向扩展的。因为非关系型数据存储本来就是分布式的,NoSQL数据库的扩展可以通过给资源池添加更多普通的数据库服务器(节点)来分担负载
- ③对事务性的支持不同
- 如果数据操作需要高事务性或复杂数据查询需要控制执行计划,那么传统的SQL数据库从性能和稳定性方面考虑是最佳选项。SQL数据库支持对事务原子性细粒度控制,并且易于回滚事务
- 虽然NoSQL数据库也可以使用事务操作,但是稳定性方面没法和关系型数据库比较,所以它们真正闪亮的价值是在操作的扩展性和大数据量处理方面
二、安装部署redis
[root@localhost ~]# cd /opt/
[root@localhost opt]# systemctl stop firewalld.service
[root@localhost opt]# setenforce 0
[root@localhost opt]# systemctl disable firewalld.service
[root@localhost opt]# ls
redis-5.0.7.tar.gz rh
[root@localhost opt]# tar zxvf redis-5.0.7.tar.gz
[root@localhost opt]# cd redis-5.0.7/
[root@localhost redis-5.0.7]# yum -y install gcc gcc-c++ make
[root@localhost redis-5.0.7]# make
[root@localhost redis-5.0.7]# make PREFIX=/usr/local/redis install 【编译并且编译安装目录为/usr/local/redis】
【由于redis源码包中直接提供了Makefile文件,所以在解压完软件包后,不用再执行./configure进行配置,可直接执行make与make install命令进行安装】
[root@localhost redis-5.0.7]# cd utils/
[root@localhost utils]# ls
build-static-symbols.tcl create-cluster hashtable lru redis_init_script.tpl speed-regression.tcl
cluster_fail_time.tcl generate-command-help.rb hyperloglog redis-copy.rb redis-sha1.rb whatisdoing.sh
corrupt_rdb.c graphs install_server.sh redis_init_script releasetools
[root@localhost utils]# ./install_server.sh 【执行软件包提供的./install_server.sh 脚本文件设置redis服务所需要的相关配置文件】
Welcome to the redis service installer
This script will help you easily set up a running redis server
Please select the redis port for this instance: [6379] 【回车确认端口号6379】
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf] 【回车确认配置文件目录】
Selected default - /etc/redis/6379.conf
Please select the redis log file name [/var/log/redis_6379.log] 【回车确认日志文件目录】
Selected default - /var/log/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379] 【回车确认数据文件目录】
Selected default - /var/lib/redis/6379
【回车到这里的时候需要在[]后面手动输入可执行文件路径(需要一次性输入正确)】
Please select the redis executable path [] /usr/local/redis/bin/redis-server
Selected config:
Port : 6379 【默认侦听端口为6379】
Config file : /etc/redis/6379.conf 【配置文件路径】
Log file : /var/log/redis_6379.log 【日志文件路径】
Data dir : /var/lib/redis/6379 【数据文件路径】
Executable : /usr/local/redis/bin/redis-server 【可执行文件路径】
Cli Executable : /usr/local/redis/bin/redis-cli 【客户端命令工具】
Is this ok? Then press ENTER to go on or Ctrl-C to abort.
Copied /tmp/6379.conf => /etc/init.d/redis_6379
Installing service...
Successfully added to chkconfig!
Successfully added to runlevels 345!
Starting Redis server...
Installation successful!
【把redis的可执行程序文件放入路径环境变量的目录中便于系统识别】
[root@localhost utils]# ln -s /usr/local/redis/bin/ * /usr/local/bin/
【当install_server.sh脚本运行完毕,redis服务就已开启,默认监听端口为6379 】
[root@localhost utils]# netstat -tanp | grep 6379 【查看tcp协议6379端口是否打开】
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 9796/redis-server 1
2.1redis服务控制
[root@localhost utils]# /etc/init.d/redis_6379 status 【查看redis服务状态】
Redis is running (9796)
[root@localhost utils]# /etc/init.d/redis_6379 stop 【停止redis服务】
Stopping ...
Waiting for Redis to shutdown ...
Redis stopped
[root@localhost utils]# /etc/init.d/redis_6379 start 【启动redis服务】
Starting Redis server...
[root@localhost utils]# /etc/init.d/redis_6379 restart 【重启redis服务】
Stopping ...
Waiting for Redis to shutdown ...
Redis stopped
Starting Redis server...
[root@localhost utils]# /etc/init.d/redis_6379 status
Redis is running (48085)
2.2 修改配置参数
[root@localhost utils]# cd /etc/redis/
[root@localhost redis]# ls
6379.conf
[root@localhost redis]# vim 6379.conf 【修改配置文件,70行添加监听主机,其他都为默认配置选项】
70 bind 192.168.131.10 【70行,添加监听的主机地址】
93 port 6379 【93行,redis默认的监听端口(6379)】
137 daemonize yes 【137行,启用守护进程】
159 pidfile /var/run/redis_6379.pid 【159行,指定PID文件】
167 loglevel notice 【167行,日志级别】
172 logfile /var/log/redis_6379.log 【172行,指定日志文件】
[root@localhost redis]# /etc/init.d/redis_6379 restart
Stopping ...
Redis stopped
Starting Redis server...
[root@localhost redis]# netstat -tanp | grep 6379
tcp 0 0 192.168.131.10:6379 0.0.0.0:* LISTEN 48182/redis-server
tcp 0 0 127.0.0.1:6379 127.0.0.1:47538 TIME_WAIT -
2.3 redis常用命令工具
- 语法:redis-cli -h host -p port -a password
- -h:指定远程主机
- -p:指定redis服务的端口号
- -a:指定密码,未设置数据库密码则可以省略此选项
- 若不添加任何选项,则使用127.0.0.1:6379连接本机上的redis数据库
[root@localhost init.d]# redis-benchmark 【用于检测redis在本机的运行效率】
[root@localhost init.d]# redis-check-aof 【修复AOF持久化文件】
[root@localhost init.d]# redis-check-rdb 【修复RDB持久化文件】
[root@localhost init.d]# redis-server 【用于启动redis的工具】
[root@localhost init.d]# redis-cli 【redis命令行工具】
[root@localhost init.d]# redis-cli -h 192.168.131.10 -p 6379 【本地登录使用redis-cli】
192.168.131.10:6379>
2.4redis-benchmark测试工具
- 基本测试语法:redis-benchmark [选项] [选项值]
- -h:指定服务器主机名
- -p:指定服务器端口
- -s:指定服务器socket(套接字)
- -c:指定并发连接数
- -n:指定请求数
- -d:以字节的形式指定SET/GET(写/修改、存/获取)值的数据大小
- -k:1=keep alive 0=reconnect
- -r:SET/GET/INCR 使用随机 key, SADD 使用随机值
- -P:通过管道传输请求
- -q:强制退出redis。仅显示query/sec值
- –csv:以csv格式输出
- -l:生成循环,永久执行测试
- -t:仅运行以逗号分隔的测试命令列表
- -I(大写i):idle模式。仅打开N个idle连接并等待
【向IP地址为192.168.131.10,端口6379的redis服务器发送100个并发连接与1万个请求,测试性能】
[root@localhost init.d]# redis-benchmark -h 192.168.131.10 -p 6379 -c 100 -n 10000
【测试存取大小为100字节的数据包的性能】
[root@localhost init.d]# redis-benchmark -h 192.168.131.10 -p 6379 -q -d 100
【测试本机上redis服务在进行set与lpush操作时的性能】
[root@localhost init.d]# redis-benchmark -t set,lpush -n 100000 -q
2.5 redis数据库常用命令
[root@localhost ~]# redis-cli -h 192.168.131.10 -p 6379
192.168.131.10:6379> set boss qz 【键:boss;值:qz】
OK
192.168.131.10:6379> get boss 【获取boss键的值】
"qz"
192.168.131.10:6379> set q1 10
OK
192.168.131.10:6379> set q2 20
OK
192.168.131.10:6379> set q3 30
OK
192.168.131.10:6379> set z4 40
OK
192.168.131.10:6379> set z5 50
OK
192.168.131.10:6379> set z66 50
OK
192.168.131.10:6379> set z6677 501
OK
192.168.131.10:6379> keys * 【查看当前数据库中所有键】
192.168.131.10:6379> keys *
1) "counter:__rand_int__"
2) "z66"
3) "z5"
4) "key:__rand_int__"
5) "boos"
6) "z4"
7) "mylist"
8) "q2"
9) "boss"
10) "q1"
11) "z6677"
12) "myset:__rand_int__"
13) "q3"
192.168.131.10:6379> keys q* 【查看当前数据库中键名以q开头的键】
1) "q2"
2) "q1"
3) "q3"
192.168.131.10:6379> keys z* 【查看当前数据库中键名以z开头且后面包含任意一位的键】
1) "z66"
2) "z5"
3) "z4"
4) "z6677"
192.168.131.10:6379> keys z?? 【查看当前数据库中键名以z开头且后面包含任意两位数的键】
1) "z66"
192.168.131.10:6379> keys z???? 【查看当前数据库中键名以z开头且后面包含任意四位数的键】
1) "z6677"
192.168.131.10:6379> exists q 【判断q键是否存在】
(integer) 0 【返回值0表示不存在】
192.168.131.10:6379> exists q1
(integer) 1 【返回值1表示存在】
192.168.131.10:6379> keys *
1) "counter:__rand_int__"
2) "z66"
3) "z5"
4) "key:__rand_int__"
5) "boos"
6) "z4"
7) "mylist"
8) "q2"
9) "boss"
10) "q1"
11) "z6677"
12) "myset:__rand_int__"
13) "q3"
192.168.131.10:6379> del boos 【删除boos键】
(integer) 1
192.168.131.10:6379> get boos
(nil)
192.168.131.10:6379> keys * 【查看所有键无boos键】
1) "counter:__rand_int__"
2) "z66"
3) "z5"
4) "key:__rand_int__"
5) "z4"
6) "mylist"
7) "q2"
8) "boss"
9) "q1"
10) "z6677"
11) "myset:__rand_int__"
12) "q3"
192.168.131.10:6379> type z6677
string 【string:字符串】
192.168.131.10:6379> type z1
none
192.168.131.10:6379> type q1
string
192.168.131.10:6379> keys q* 【查看所有q开头的键】
1) "q4"
2) "q2"
3) "q3"
192.168.131.10:6379> get q4 【获取q4键的值】
"10"
192.168.131.10:6379> rename q4 q1 【将q4键重命名为q1键】
OK
192.168.131.10:6379> get q1 【查看q1键的值】
"10"
192.168.131.10:6379> get q4
(nil)
192.168.131.10:6379> keys q*
1) "q2"
2) "q1"
3) "q3"
192.168.131.10:6379> keys z* 【查看所有以z开头的键】
1) "z66"
2) "z5"
3) "z4"
4) "z6677"
192.168.131.10:6379> get z66 【获取z66键的值】
"50"
192.168.131.10:6379> renamenx z66 z5 【将z66键重命名为z5】
(integer) 0 【但是z5键本来就存在,所以执行失败,返回0】
192.168.131.10:6379> keys z*
1) "z66"
2) "z5"
3) "z4"
4) "z6677"
192.168.131.10:6379> get z66
"50"
192.168.131.10:6379> renamenx z66 z1 【将z66键重命名为z1】
(integer) 1 【z1键不存在,所以返回1】
192.168.131.10:6379> keys z* 【查看所有z开头的键,发现z66键不存在,多了个z1键】
1) "z1"
2) "z5"
3) "z4"
4) "z6677"
192.168.131.10:6379> get z1 【获取z1键的值就是之前z66键的值】
"50"
192.168.131.10:6379> DBSIZE
(integer) 12
192.168.131.10:6379> CONFIG SET requirepass 5514
OK
192.168.131.10:6379> key *
(error) ERR unknown command `key`, with args beginning with: `*`,
192.168.131.10:6379> config get requirepass
(error) NOAUTH Authentication required.
192.168.131.10:6379> auth 5514
OK
192.168.131.10:6379> config get requirepass
1) "requirepass"
2) "5514"
- 或者通过修改配置文件的方式也可以解决无法重启,详见error集
[root@localhost ~]# /etc/init.d/redis_6379 restart
Stopping ...
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
(error) NOAUTH Authentication required.
Waiting for Redis to shutdown ...
Waiting for Redis to shutdown ...
[root@localhost ~]# redis-cli -h 192.168.131.10 -p 6379
192.168.131.10:6379> auth 5514
OK
192.168.131.10:6379> CONFIG SET requirepass ''
OK
[root@localhost ~]# /etc/init.d/redis_6379 restart
Stopping ...
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Waiting for Redis to shutdown ...
Redis stopped
Starting Redis server...
2.6 redis多数据库常用命令
【使用redis-cli连接redis数据库后,默认使用的是0号数据库】
[root@localhost ~]# redis-cli -h 192.168.131.10 -p 6379
192.168.131.10:6379> select 16 【切换至16号数据库】
(error) ERR DB index is out of range
192.168.131.10:6379> select 15 【切换至15号数据库】
OK
192.168.131.10:6379[5]> keys * 【查看15号数据库无键值数据】
(empty list or set)
192.168.131.10:6379[15]> set q1 100 【在15号库创建键q1,值100】
OK
192.168.131.10:6379[15]> keys * 【查看15号库里所有键】
1) "q1"
192.168.131.10:6379[15]> get q1 【查看15号库里的q1键值】
"100"
192.168.131.10:6379[15]> select 10 【切换到10号库】
OK
192.168.131.10:6379[10]> keys * 【查看10号库所有键】
(empty list or set)
192.168.131.10:6379[10]> select 15
OK
192.168.131.10:6379[15]> move q1 10 【将15号库里的q1键移动到10号库】
(integer) 1
192.168.131.10:6379[15]> keys * 【查看15号库,无键】
(empty list or set)
192.168.131.10:6379[15]> select 10 【切换到10号库】
OK
192.168.131.10:6379[10]> keys * 【查看10号库里所有键】
1) "q1"
192.168.131.10:6379[10]> get q1 【查看10号库里q1键的值】
"100"
192.168.131.10:6379> select 9
OK
192.168.131.10:6379[9]> set q1 100
OK
192.168.131.10:6379[9]> keys *
1) "q1"
192.168.131.10:6379[9]> select 10
OK
192.168.131.10:6379[10]> keys *
1) "q1"
192.168.131.10:6379[10]> flushdb 【清空当前数据库的数据】
OK
192.168.131.10:6379[10]> keys *
(empty list or set)
192.168.131.10:6379[10]> select 9 【因为只是情况了9号库的数据,所以10号库的数据还在】
OK
192.168.131.10:6379[9]> keys *
1) "q1"
192.168.131.10:6379[9]> flushall 【情况所有数据库的数据】
OK
192.168.131.10:6379[9]> keys *
(empty list or set)
192.168.131.10:6379[9]> select 0
OK
192.168.131.10:6379> keys *
(empty list or set)
三、redis高可用
3.1 redis高可用概述
- 在redis中,实现高可用的技术主要包括持久化、主从复制、哨兵、集群
- 持久化:持久化是最简单的高可用方法(有些时候会不被纳入高可用的手段)
作用:数据备份(将数据存储在硬盘,保证数据不会因进程退出而丢失) - 主从复制:主从复制是高可用redis的基础。哨兵和集群都是在主从复制基础上实现高可用的。
作用:主从复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。
缺点:①故障恢复无法自动化;②写操作无法负载均衡;③存储能力受到单机的限制 - 哨兵:在主从复制的基础上,哨兵实现了自动化的故障恢复
缺点:①写操作无法负载均衡;②存储能力受到单机的限制 - 集群:集群解决redis写操作无法负载均衡,以及存储能力受到单机限制的问题。从而实现了较为完善的高可用方案
3.2 持久化的功能
- redis的持久化方式有两种(RDB和AOF)
- RDB持久化:原理是将redis在内存中的数据库记录定时保存到磁盘上
- AOF持久化:原理是将redis的操作日志以追加的方式写入文件,类似于MySQL的binlog
3.3 持久化RDB和AOF区别
RDB | AOF |
对redis中的数据执行周期性的持久化。简而言之就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上。持续的用日志记录写操作,crash(崩溃)后利用日志恢复。 通过在指定的时间间隔内将内存中的数据集快照写入磁盘的实际操作过程是fork一个子进程,然后先将数据集写入临时文件,写入成功后,再替换之前的文件,并用二进制压缩存储 | 换了一个角度来实现持久化,也就是将redis执行过的所有写、删(查询操作不会)指令记录下来,在下次redis重新启动时只要把这些写指令从前到后再重复执行一遍,即可实现数据恢复。平时写操作的时候不会触发写,只有手动提交save命令或者shutdown关闭命令时才会触发备份操作 |
3.4 持久化RDB和AOF的优缺点
- RDB持久化
- 优点:RDB文件紧凑,体积小,网络传输快,适合全量复制。恢复速度比AOF块很多,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小
- 缺点:RDB文件的致命缺点在于其数据快照的持久化方式使他必然做不到实时持久化。而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(老版本的redis不兼容新版本的rdb文件)
- 对于rdb持久化,一方面是bgsave在进行fork操作时redis主进程会阻塞。另一方面,子进程向硬盘写数据也会带来IO压力
- AOF持久化
- 优点:支持秒级持久化、兼容性好
- 缺点:文件大、恢复速度慢、对性能影响大
- 对于AOF持久化,向硬盘写数据的频率大大提高(everysec策略即为秒级别),IO压力更大,甚至可能造成AOF追加阻塞问题
- AOF文件的重写与RDB的bgsave类似,会有fork时的阻塞和子进程的IO压力问题。相对来说,由于AOF向硬盘中写数据的频率更高,因此对redis主进程性能的影响会更大
四、redis持久化
4.1 RDB
4.1.1 触发条件
- 手动触发
- save命令和bgsave命令都可以生成RDB文件
- save命令会阻塞redis服务器进程,直到RDB文件创建完毕为止,在redis服务器阻塞期间,服务器不能处理任何命令请求。
- bgsave命令则会创建一个子进程,由子进程来负责创建RDB文件,父进程(Redis主进程)则继续处理请求
- bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,所以save已基本被废弃,线上环境更要杜绝save的使用
- 自动触发
- 在自动触发RDB持久化时,Redis也会选择bgsave而不是save来进行持久化
- 自动触发最常见的情况是在配置文件中通过save m n,指定当m秒内发生n次变化时,会触发bgsave
【当满足219行-221行的三个save条件的任意一个时,都会引起bgsave的调用】
[root@localhost ~]# vim /etc/redis/6379.conf
219 save 900 1 【当时间到900秒时,如果redis数据发生了至少1次变化,则执行bgsave】
220 save 300 10 【当时间到300秒时,如果redis数据发生了至少10次变化,则执行bgsave】
221 save 60 10000 【当时间到60秒时,如果redis数据发生了至少10000次变化,则执行bgsave】
242 rdbcompression yes 【是否开启RDB文件压缩】
254 dbfilename dump.rdb 【指定RDB文件名】
264 dir /var/lib/redis/6379 【指定RDB文件和AOF文件所在目录】
- 其他自动触发机制
- 除了save m n 以外,还有一些其他情况会触发bgsave
- 在主从复制的场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点
- 执行shutdown命令时,自动执行rdb持久化
4.1.2 执行流程
4.1.3 启动时加载
4.2 AOF
4.2.1 开启AOF
[root@localhost ~]# vim /etc/redis/6379.conf
700 appendonly yes 【改为yes,开启AOF】
704 appendfilename "appendonly.aof" 【指定AOF文件名称】
796 aof-load-truncated yes 【是否忽略最后一条可能存在问题的指令】
--------------------------------------------aof-load-truncated yes------------------------------------------------
aof-load-truncated yes 指的的是当redis在恢复时,会忽略最后一条可能存在问题的指令。默认yes,即在AOF写入时,可能存在指令写错
的问题(突然断电,写了一半),这种情况下,yes则会log并继续,no则会直接恢复失败
------------------------------------------------------------------------------------------------------------------
[root@localhost ~]# /etc/init.d/redis_6379 restart 【如有密码需要先取消密码再进行重启】
[root@localhost ~]# ls /var/lib/redis/6379/
appendonly.aof dump.rdb
4.2.2 执行流程(命令追加、文件写入、文件重写)
- AOF的执行流程包括
- 命令追加(append):将Redis的写命令追加到缓冲区aof_buf
- 文件写入(write)与文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘
- 文件重写(rewrite):定期写AOF文件,达到压缩的目的
- 1.命令追加(append)
- Redis先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈
- 命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、易处理、操作简单、避免二次开销等优点
- 在AOF文件中,除了用于指定数据库的select命令是由Redis添加的,其他都是客户端发送来的写命令
- 2.文件写入(write)与文件同步(sync)
- Redis提供了多种AOF缓存区的同步文件策略,涉及到操作系统的write函数和fsync函数
- 为了提高文件写入效率,在现代操作系统中,当用户调用write函数将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里。
- 这样的操作虽然提高了效率,但也带来了安全问题(如果计算机停机,内存缓冲区中的数据会丢失。因此系统同时提供了fsync、fdatasync等同步函数,用来强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性)
- 3.文件重写(rewrite)
- 随着时间流逝,redis服务器执行的写命令越来越多,AOF文件也会越来越大。而过大的AOF文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长
- 文件重写则是指定期重写AOF文件,减小AOF文件的大小
- 但是需要注意AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件,并不会对旧的AOF文件进行任何读取、写入操作
- 另一点需要注意的是虽然对于AOF持久化来说,文件重写是强烈推荐的,但并不是必须的。即使没有文件重写,数据也可以被持久化并在redis启动的时候导入。所以一些情况中,会关闭自动的文件重写,然后通过定时任务在每天的某一时刻定时执行
- 文件重写能够压缩AOF文件是因为:①.过期的数据不再写入文件;②.无效的命令不再写入文件(例如:有些被重复设置的数据以及被删除的数据等等);③.多条命令可以合并为一个(例如:sadd q a1,sadd q a2可以合并为sadd q a1 a2)
- 所以通过上述内容可以分看出:重写后AOF执行的命令减少了,所以文件重写既可以减少文件占用的空间,也可以加快恢复速度
- 文件重写的触发也分为手动触发和自动触发
- 手动触发:直接调用bgrewriteaof命令(类似于bgsave,都是fork子进程进行具体的工作,且都只有在fork时才会进行阻塞)
- 自动触发:通过设置auto-aof-rewrite-min-size选项和auto-aof-rewrite-percentage选项来自动执行bgrewriteaof。 只有当两个选项同时满足时,才会自动触发AOF重写,即bgrewriteaof操作。
[root@localhost ~]# vim /etc/redis/6379.conf
773 auto-aof-rewrite-percentage 100
【当前AOF文件大小(aof_current_size)是上次日志重写时AOF文件大小(aof_base_size)两倍时,发生bgrewriteaof操作】
774 auto-aof-rewrite-min-size 64mb
【当前AOF文件执行bgrewriteaof命令的最小值,避免刚开始启动redis时由于文件较小导致频繁的进行bgrewriteaof操作】
4.2.3 文件重写流程
- ①Redis父进程首先判断当前是否存在正在执行bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行
- ②父进程执行fork操作创建子进程(这个过程中父进程是阻塞的)
- ③父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程, 并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确
- 由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。但是因为父进程依然在响应命令,所以redis使用AOF重写缓冲区(aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说在bgrewriteaof执行期间,redis的写命令同时追加到aof_buf和aof_rewrite_buf两缓冲区
- ④子进程根据内存快照,按照命令合并规则写入到新的AOF文件
- 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,可以通过info persistence查看
- 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致
- 使用新的AOF文件替换老文件,完成AOF重写
4.2.4 AOF缓存区同步文件策略的方式(三种)
- appendfsync always
- 命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。
- 这种情况下每次有写命令都要同步到AOF文件,硬盘IO将成为性能瓶颈,redis只能支持大约几百TPS的写入,严重降低了redis的性能
- 即使使用的固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的使用寿命
- TPS:每秒钟request/事务 数量
- appendfsync no
- 命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步
- 同步由操作系统负责,同步周期通常为30秒
- 这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性也无法保证
- appendfsync everysec
- 命令写入aof_buf后调用系统write操作,write完成后线程返回
- fsync同步文件操作由专门的线程每秒调用一次
- everysec是前两种策略的折中,是性能和数据安全性的平衡,因此也是redis的默认配置
4.2.5 启动时加载
五、redis性能管理
5.1 查redis内存使用
[root@localhost ~]# redis-cli -h 192.168.131.10 -p 6379
192.168.131.10:6379> info memory
5.2 内存碎片率
- 跟踪内存碎片率对理解Redis实例的资源性能是非常重要的:
- 内存碎片率稍大于1是合理的,这个值表示内存碎片率比较低
- 内存碎片率超过1.5,说明Redis消耗了实际需要物理内存的150%,其中50%是内存碎片率。需要在redis-cli工具上输入shutdown save 命令,并重启 Redis 服务器。
- 内存碎片率低于1的,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换。需要增加可用物理内存或减少 Redis 内存占用。
5.3 内存使用率
- 避免内存交换发生的方法:
- 针对缓存数据大小选择安装 Redis 实例
- 尽可能的使用Hash数据结构存储
- 设置key的过期时间
5.4 内回收key
[root@localhost ~]# vim /etc/redis/6379.conf
598 maxmemory-policy noeviction 【取消注释】
参数 | 说明 |
volatile-lru | 使用lru算法从已设置过期时间的数据集合中淘汰数据 |
volatile-ttl | 从已设置过期时间的数据集合中挑选即将过期的数据淘汰 |
volatile-random | 从已设置过期时间的数据集合中随机挑选数据淘汰 |
allkeys-lru | 使用lru算法从所有数据集合中淘汰数据 |
allkeys-random | 从数据集合中任意选择数据淘汰 |
noenviction | 禁止淘汰数据 |
六、总结
6.1 reids
- redis应用场景
- redis作为基于内存运行的数据库,缓存是其最常应用的场景之一。除此之外,redis常见的应用场景还包括获取最新N个数据的操作、排行榜类应用、计数器应用、存储关系、实时分析系统、日志记录等等
6.2 关系与非关系型数据库
- 关系型数据库
- 实例→数据库→表(table)→记录行(row)、数据字段(column)
- 非关系数据库
- 实例→数据库→集合(collection)→键值对(key-value)