Redis数据库(安装部署及高可用、持久化、性能管理基础理论)

一、redis理论概述

1.1 redis简介

  • redis是一款开源的、使用C语言编写的NoSQL数据库
  • redis是基于内存运行并支持持久化,采用Key-value(键值对)的存储形式,是目前分布式架构中不可或缺的一环
  • redis服务程序时单进程模型,也就是在一台服务器上可以同时启动多个redis进程,redis的实际处理速度则是完全依赖于主进程的执行效率。若在服务器上只运行一个redis进程,当多个客户端同时访问时,服务器的处理能力是会有一定程度的下降。若在同一台服务器上开启多个redis进程,redis在提高并发处理能力的同时还会给服务器的CPU造成很大压力。所以在实际生成环境中,需要根据实际的需求来决定开启多少个redis进程。若对高并发要求更高一些,则会考虑在同一台服务器上开启多个进程,但是如果CPU资源比较紧张,则采用单进程即可
  • redis作为基于内存运行的数据库,缓存是其最常应用的场景之一。除此之外,redis常见的应用场景还包括获取最新N个数据的操作、排行榜类应用、计数器应用、存储关系、实时分析系统、日志记录等等

1.2 redis优点

  • 具有极高的数据读写速度:数据读取的速度最高可达到110000次/s,数据写入速度最高可达到81000次/s
  • 支持丰富的数据类型:key-value、Strings、Lists、Hashes、Sets、Ordered Sets等等
    • Strings:字符串(可以为整形、浮点型和字符串,统称为元素)
    • List:列表(实现队列,元素不唯一。先入先出原则)
    • set:集合(各不相同的元素)
    • hash:hash散列值(hash的key必须是唯一的)
    • Set:集合
    • Ordered Sets:有序集合
  • 支持数据的持久化:可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
  • 原子性:redis所有操作都是原子性的
  • 支持数据备份:master-salve模式的数据备份

1.3 redis与memcached区别

数据库类型过期策略数据类型持久化主从复制虚拟内存
rdisK-V支持五大数据类型支持支持支持
memcachedK-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(对数据库高可扩展性与高可用性需求)
  • 关系型数据库和非关系型数据库都有各自的特点与应用场景,但两者的紧密结合给Web2.0数据库发展带来了新的思路。让关系型数据库更多关注在关系上,非关系型数据库更多关系在存储上。当我们在读写分离的MySQL数据库环境中,可以把进程访问的数据存储在非关系型数据库中,从而提升访问速度
  • 关系型数据库和非关系型数据库区别(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.1070行,添加监听的主机地址】
  93 port 637993行,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性能测试工具,可有效的测试redis服务的性能
  • 基本测试语法: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数据库常用命令

  • set(存放数据),格式:set key value
  • get(获取数据),格式:get key
[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"


  • keys:取符合规则的键值列表,通常情况可以结合*、?等选项来使用。
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"
  • exists:判断键值是否存在。
192.168.131.10:6379> exists q       【判断q键是否存在】
(integer) 0                         【返回值0表示不存在】
192.168.131.10:6379> exists q1
(integer) 1                         【返回值1表示存在】
  • del:删除当前数据库的指定 key。
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"
  • type:获取 key 对应的 value 值类型。
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
  • rename:对已有key进行重命名(覆盖)
  • 格式:rename 源key 目标key
  • 在使用rename命令进行重命名时,无论目标key是否存在都将进行重命名,且源key的值会覆盖目标key的值。所以在实际使用时建议先用exists命令查看目标key是否存在,然后再决定是否执行rename命令,避免覆盖重要数据
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"
  • renamenx:对已有key进行重命名,并检测新名是否存在,如果目标key存在则不进行重命名(不覆盖)
  • 格式:renamenx 源key 目标key
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键本来就存在,所以执行失败,返回0192.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键不存在,所以返回1192.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"
  • dbsize:查看当前数据库中key的数目
192.168.131.10:6379> DBSIZE
(integer) 12
  • 使用config set requi yourpassword命令设置密码(一旦设置密码,必须先验证通过密码,否则所有操作不可用)
  • 使用config get requirepass命令查看密码
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"
  • 删除密码
  • 不删除密码无法重启redis
  • 或者通过修改配置文件的方式也可以解决无法重启,详见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支持多数据库,默认情况下包含16个数据库,数据库的名称是用数字0-15依次来命名。多数据库互相独立,互不干扰
  • 多数据库间切换
  • 格式:select 序号
【使用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)
  • 多数据库间移动数据
  • 格式:move 键值 序号
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高可用概述

  • 在Web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)
  • 但在redis语境中,高可用除了保证提供正常服务(例如主从分离、快速容灾技术),还需要考虑数据容量的扩展,数据安全不会丢失等等
  • 在redis中,实现高可用的技术主要包括持久化、主从复制、哨兵、集群
    • 持久化:持久化是最简单的高可用方法(有些时候会不被纳入高可用的手段)
      作用:数据备份(将数据存储在硬盘,保证数据不会因进程退出而丢失)
    • 主从复制:主从复制是高可用redis的基础。哨兵和集群都是在主从复制基础上实现高可用的。
      作用:主从复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。
      缺点:①故障恢复无法自动化;②写操作无法负载均衡;③存储能力受到单机的限制
    • 哨兵:在主从复制的基础上,哨兵实现了自动化的故障恢复
      缺点:①写操作无法负载均衡;②存储能力受到单机的限制
    • 集群:集群解决redis写操作无法负载均衡,以及存储能力受到单机限制的问题。从而实现了较为完善的高可用方案

3.2 持久化的功能

  • redis是内存数据库,数据都是存储在内存中,所以为了避免服务器断电等原因导致的redis进程异常退出后数据的永久丢失,需要定期将redis中的数据以某种形式(数据或命令)从内存保存到硬盘,当下次redis重启时,利用持久化文件实现数据恢复
  • 为了进行灾难备份,也可以将持久化文件拷贝到一个远程位置
  • redis的持久化方式有两种(RDB和AOF)
    • RDB持久化:原理是将redis在内存中的数据库记录定时保存到磁盘上
    • AOF持久化:原理是将redis的操作日志以追加的方式写入文件,类似于MySQL的binlog
  • 由于AOF持久化的实时性更好,所以当进程意外退出时丢失的数据也更少,因此AOF时目前主流的持久化方式。但是RDB持久化也有用武之地

3.3 持久化RDB和AOF区别

RDBAOF
对redis中的数据执行周期性的持久化。简而言之就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上。持续的用日志记录写操作,crash(崩溃)后利用日志恢复。
通过在指定的时间间隔内将内存中的数据集快照写入磁盘的实际操作过程是fork一个子进程,然后先将数据集写入临时文件,写入成功后,再替换之前的文件,并用二进制压缩存储
换了一个角度来实现持久化,也就是将redis执行过的所有写、删(查询操作不会)指令记录下来,在下次redis重新启动时只要把这些写指令从前到后再重复执行一遍,即可实现数据恢复。平时写操作的时候不会触发写,只有手动提交save命令或者shutdown关闭命令时才会触发备份操作
  • RDB和AOF两种方式可以同时使用,但在这种情况下,如果redis重启的话,会优先采用AOF方式来进行数据恢复,因为AOF方式的数据恢复完整度更高

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

  • RDB持久化是指在指定的时间间隔内将内存中当前进程中的数据生成快照保存到硬盘(所以也称作为快照持久化),并用二进制压缩存储,保存的文件后缀是rdb;当redis重新启动时,可以读取快照文件恢复数据

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 执行流程

  • Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof的子进程,如果在执行则bgsave命令直接返回。 bgsave/bgrewriteaof的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
  • 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令
  • 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令
  • 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换
  • 子进程发送信号给父进程表示完成,父进程更新统计信息

4.1.3 启动时加载

  • RDB文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。但是由于AOF的优先级更高,因此当AOF开启时,Redis会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件期间处于阻塞状态,直到载入完成为止
  • 当AOF关闭的时候redis进行RDB文件载入,会对RDB文件进行检验,如果文件损坏,则日志中会打印错误,Redis启动失败

4.2 AOF

  • AOF持久化是将redis执行的每次写、删除命令记录到单独的日志文件中,查询操作不会记录。当redis重启时再次执行AOF文件的命令来恢复数据
  • 与RDB相比,AOF的实时性更好,所以也成为主流的持久化方案

4.2.1 开启AOF

  • Redis服务器默认开启RDB,关闭AOF;所以要开启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缓存区同步文件策略的方式(三种)

  • 在配置文件/etc/redis/6379.conf 的第729行
  • 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 启动时加载

  • 当AOF开启时,redis启动时会优先载入AOF文件来恢复数据。所以只有当AOF关闭时,才会载入RDB文件恢复数据
  • 当AOF开启,但AOF文件不存在时,就算有RDB文件存在也不会进行加载
  • Redis载入AOF文件时,会对AOF文件进行校验,如果文件损坏,则会在日志中打印错误,redis启动失败
  • 但是如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且aof-load-truncated参数开启,则日志中会输出警告,redis忽略掉AOF文件的尾部,启动成功
  • aof-load-truncated参数默认开启

五、redis性能管理

5.1 查redis内存使用

[root@localhost ~]# redis-cli -h 192.168.131.10 -p 6379
192.168.131.10:6379> info memory

5.2 内存碎片率

  • 操系统分配的内存值used_memory_rss除以Redis使用的内存值used_memory计算得出内存碎
  • 是由操作系统低效的分配/回收物理内存导致的(不连续的物理内存分配)
  • 跟踪内存碎片率对理解Redis实例的资源性能是非常重要的:
    • 内存碎片率稍大于1是合理的,这个值表示内存碎片率比较低
    • 内存碎片率超过1.5,说明Redis消耗了实际需要物理内存的150%,其中50%是内存碎片率。需要在redis-cli工具上输入shutdown save 命令,并重启 Redis 服务器。
    • 内存碎片率低于1的,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换。需要增加可用物理内存或减少 Redis 内存占用。

5.3 内存使用率

  • redis实例的内存使用率超过可用最大内存,操作系统将开始进行内存与swap空间交换。
  • 避免内存交换发生的方法:
    • 针对缓存数据大小选择安装 Redis 实例
    • 尽可能的使用Hash数据结构存储
    • 设置key的过期时间

5.4 内回收key

  • 保证合理分配redis有限的内存资源。
  • 当达到设置的最大阀值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除。
  • 配置文件中修改 maxmemory-policy 属性值
[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)
  • 非关系型数据库不需要手动建数据库和集合(表)。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TaKe___Easy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值