NoSQL ,泛指非关系型的数据库, NoSQL Not Only SQL,它可以作为关系型数据库的良好补充。 NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。NoSQL与SQL数据库区别对比如下:
SQL | NoSQL | |
---|---|---|
数据结构 | 结构化 | 非结构化 |
数据关联 | 关联的 | 无关联的 |
查询方式 | SQL查询 | 非SQL查询 |
事务特征 | ACID | BASE |
存储方式 | 磁盘 | 内存 |
扩展性 | 垂直 | 水平 |
使用场景 | 数据结构固定;相关业务对数据安全性、一致性要求较高 | 数据结构不固定;对一致性,安全性要求不高,对性能要求高 |
常见NoSQL数据库:
Reids (键值对)
MongoDB(文档类型)
HBase (列类型)
Neo4j(Graph类型)
Redis诞生于2009年全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。其主要特征如下:
键值(key-value)型,value支持多种不同数据结构,功能丰富;
单线程,每个命令具备原子性;
低延迟,速度快(基于内存、IO多路复用、良好的编码);
支持数据持久化;
支持主从集群、分片集群;
支持多语言客户端;
需要的文件
链接:https://pan.baidu.com/s/10o9t5OOX2A4s98vnFnJwmA?pwd=rxtc
提取码:rxtc
基于Linux服务器来部署项目
【1】依赖库,Redis是基于C语言编写的,因此首先需要安装Redis所需要的gcc依赖:
yum install -y gcc tcl
【2】上传安装包并解压,然后将资料提供的Redis安装包上传到虚拟机的/usr/local/目录
【3】解压缩:
tar -xzf redis-6.2.6.tar.gz
【4】进入redis目录:
cd redis-6.2.6
【5】运行编译命令:
make && make install
默认的安装路径是在 /usr/local/bin
目录下:该目录已经默认配置到环境变量,因此可以在任意目录下运行这些命令。其中:
redis-cli:是redis提供的命令行客户端
redis-server:是redis的服务端启动脚本
redis-sentinel:是redis的哨兵启动脚本
redis的启动方式有很多种,例如:
默认启动
指定配置启动
开机自启
安装完成后,在任意目录输入redis-server命令即可启动Redis:
redis-server
这种启动属于前台启动
,会阻塞整个会话窗口,窗口关闭或者按下CTRL + C
则Redis停止。不推荐使用。
如果要让Redis以后台
方式启动,则必须修改Redis配置文件,就在我们之前解压的redis安装包下(/usr/local/redis-6.2.6
),名字叫redis.conf:
我们先将这个配置文件备份一份:
cp redis.conf redis.conf.bck
修改redis.conf文件中的一些配置:
# 允许访问的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0
bind 0.0.0.0
# 守护进程,修改为yes后即可后台运行
daemonize yes
# 密码,设置后访问Redis必须输入密码
# requirepass 123456
Redis的其它常见配置:
# 监听的端口
port 6379
# 工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir .
# 数据库数量,设置为1,代表只使用1个库,默认有16个库,编号0~15
databases 16
# 设置redis能够使用的最大内存
maxmemory 512mb
# 日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"
启动Redis:
# 进入redis安装目录
cd /usr/local/redis-6.2.6
# 启动
redis-server redis.conf
停止服务:
# 利用redis-cli来执行 shutdown 命令,即可停止 Redis 服务,
# 因为之前配置了密码,因此需要通过 -u 来指定密码
redis-cli -u 123456 shutdown
我们也可以通过配置来实现开机自启。
首先,新建一个系统服务文件:
vi /etc/systemd/system/redis.service
内容如下:
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/redis-6.2.6/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
然后重载系统服务:
systemctl daemon-reload
现在,我们可以用下面这组命令来操作redis了:
# 启动
systemctl start redis
# 停止
systemctl stop redis
# 重启
systemctl restart redis
# 查看状态
systemctl status redis
执行下面的命令,可以让redis开机自启:
systemctl enable redis
【1】下载镜像
docker pull redis:6.2.6
【2】运行容器
docker run --name redis -d -p 6379:6379 redis:6.2.6
安装完成Redis,我们就可以操作Redis,实现数据的CRUD了。这需要用到Redis客户端,包括:
命令行客户端
图形化桌面客户端
编程客户端
Redis安装完成后就自带了命令行客户端:redis-cli,
redis-cli [options] [commonds]
其中常见的options有:
-h 127.0.0.1
:指定要连接的redis节点的IP地址,默认是127.0.0.1
-p 6379
:指定要连接的redis节点的端口,默认是6379
-a 123456
:指定redis的访问密码
其中的commonds就是Redis的操作命令,例如:
ping
:与redis服务端做心跳测试,服务端正常会返回pong
不指定commond时,会进入redis-cli
的交互控制台
【1】在资料中可以找到Redis的图形化桌面客户端:
【2】解压缩后,运行安装程序即可安装:
【3】安装完成后,在安装目录下找到rdm.exe文件:
【4】建立连接,点击左上角的连接到Redis服务器
按钮:
【5】在弹出的窗口中填写Redis服务信息:
Redis默认有16个仓库,编号从0至15. 通过配置文件可以设置仓库数量,但是不超过16,并且不能自定义仓库名称。如果是基于redis-cli连接Redis服务,可以通过select命令来选择数据库
# 选择 0号库
select 0
在Redis6/7中,非常受关注的第一个新特性就是多线程。
这是因为,Redis一直被大家熟知的就是它的单线程架构(Redis6以前是单线程架构),虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF重写)。但是,从网络IO处理到实际的读写命令处理,都是由单个线程完成的。
随着网络硬件的性能提升,Redis的性能瓶颈有时会出现在网络IO的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度,
为了应对这个问题:
采用多个IO线程来处理网络请求,提高网络请求处理的并行度,Redis6/7就是采用的这种方法。
但是,Redis的多线程只是用来处理网络请求的,对于读写操作命令Redis仍然使用单线程来处理。这是因为,Redis处理请求时,网络处理经常是瓶颈,通过多个IO线程并行处理网络操作,可以提升实例的整体处理性能。而继续使用单线程执行命令操作,就不用为了保证Lua脚本、事务的原子性,额外开发多线程互斥加锁机制了(不管加锁操作处理),这样一来,Redis线程模型实现就简单了
Redis是一个字典结构的存储服务器,一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。 这与在一个关系数据库实例中可以创建多个数据库类似(如下图所示),所以可以将其中的每个字典都理解成一个独立的数据库。
Redis默认支持16个数据库,可以通过调整Redis的配置文件redis/redis.conf中的databases来修改这一个值,设置完毕后重启Redis便完成配置。
语法结构:
select number
示例:
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
# 切换数据库,获取不到k1数据
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> get k1
(nil)
Redis Flushdb 命令用于清空当前数据库中的所有 key。
语法结构:
127.0.0.1:6379> FLUSHDB
Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。
语法结构:
redis 127.0.0.1:6379> FLUSHALL
查看当前库中所有的key 。
语法结构:
keys *
有3个通配符 *, ? ,[]
*: 通配任意多个字符
?: 通配单个字符
[]: 通配括号内的某1个字符
注意: 生产已经禁止。因为长时间阻塞redis而导致其他客户端的命令请求一直处于阻塞状态。 更安全的做法是采用scan。
新版本也进行了替代:
redis-cli -h 127.0.0.1 -p 6379 -a 123456 --scan "*"
判断某个key是否存在,返回1表示存在,0不存在。
语法结构:
exists key
#查看k1是否存在,如果存在返回1
exists k1
# 查看k1 k2 k3是否存在,如果k1 k2存在,k3不存在,则返回2
exists k1 k2 k3
注意: 可以设置多个key,只返回存在的个数,但不返回哪一个存在/不存在。
查看当前key 所储存的值的类型。返回当前key所储存的值的类型, 如string 、list等。
语法结构:
type key
删除已存在的key,不存在的 key 会被忽略。
语法结构:
del key
示例:
可以设置多个key,返回删除成功的个数。
# 删除k1,如果成功返回1,失败返回0
del k1
# 删除k1 k2 k3,如果k1 k2存在,k3不存在,则返回2
del k1 k2 k3
给key设置time秒的过期时间。设置成功返回 1 。 当 key 不存在返回 0。
语法结构:
expire key time
示例:
# 给k1设置10秒后过期
expire k1 10
以秒为单位返回 key 的剩余过期时间。
语法结构:
ttl key
注意: 当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。
移除给定 key 的过期时间,使得 key 永不过期。
语法结构:
persist key
注意: 当过期时间移除成功时,返回 1 。 如果 key 不存在或 key 没有设置过期时间,返回 0 。
String是Redis最基本的类型,一个key对应一个value。String是二进制安全的,意味着String可以包含任何数据,比如序列化对象或者一张图片。String最多可以放512M的数据。
【1】set
用于设置给定 key 的值。如果 key 已经存储其他值, set 就重写旧值,且无视类型。
语法格式:
set key value
【2】get
用于获取指定 key 的值。如果 key 不存在,返回 nil 。
语法格式:
get key
【3】append
将给定的value追加到key的值的末尾。
语法格式:
append key value
注意: 如果 key 已经存在并且是一个字符串, append 命令将 value 追加到 key 原来的值的末尾。 如果 key 不存在, append 就简单地将给定 key 设为 value ,就像执行 set key value 一样。
【4】strlen
获取指定 key 所储存的字符串值的长度。当 key 储存的不是字符串值时,返回一个错误。
语法格式:
strlen key
【5】setex
给指定的 key 设置值及time 秒的过期时间。如果 key 已经存在, setex命令将会替换旧的值,并设置过期时间。
语法格式:
setex key time value
【6】setnx
只有在key不存在时设置key的值
语法格式:
setnx key value
【7】getrange
获取指定区间范围内的值,类似between........and 的关系,下标的起始与结束
语法格式:
getrange key start end
【8】setrange
设置指定区间范围内的值,类似between........and 的关系,下标的起始与结束
语法结构:
setrange key offset value
【9】incr
将 key 中储存的数字值增一。
语法格式:
incr key
注意: 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 incr 操作。 如字符串类型的值不能表示为数字、或者是其他类型,那么返回一个错误。
【10】decr
将 key 中储存的数字值减一。
语法格式:
decr key
注意:
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 decr 操作。 如字符串类型的值不能表示为数字、或者是其他类型,那么返回一个错误。
【11】incrby/decrby key step
将key存储的数字值按照step进行增减。
语法格式:
incrby/decrby key 步长
注意:
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 incrby/decrby 命令。 如字符串类型的值不能表示为数字、或者是其他类型,那么返回一个错误。
【12】mset
同时设置一个或多个 key-value
语法格式:
mset key1 value1 key2 value2
【13】mget
返回所有(一个或多个)给定 key 的值。
语法格式:
mget key1 key2
【14】getset
将给定key值设为value,并返回key的旧值(old value),简单一句话(先get然后立即set)。
语法格式:
getset key value
value 除了是字符串以外还可以是数字。
计数器
统计多单位的数量
粉丝数
对象缓存存储
分布式锁
List是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。底层是一个双向链表, 对两端操作性能极高,通过索引操作中间的节点性能较差。
一个List最多可以包含 $2^{32}-1$个元素 ( 每个列表超过40亿个元素)。
【1】lpush/rpush
从左边(头部)/右边(尾部)插入一个或多个值。
语法结构:
lpush/rpush key1 value1 value2 value3……
示例:
#从左边放入v1 v2 v3
127.0.0.1:6379> lpush k1 v1 v2 v3
(integer) 3
【2】lrange
返回key列表中的start和end之间的元素(包含start和end)。 其中 0 表示列表的第一个元素,-1表示最后一个元素。
语法结构:
lrange key start end
示例:
#取出列表里前3个值,结果为v3 v2 v1
127.0.0.1:6379> lrange k1 0 2
#取出列表里全部值,结果为v3 v2 v1 v4 v5 v6
127.0.0.1:6379> lrange k1 0 -1
【3】lpop/rpop
移除并返回第一个值或最后一个值。
语法格式:
lpop/rpop key
示例:
# lpop k1 从列表中删除v3,并返回,当前列表全部值v2 v1 v4 v5 v6
127.0.0.1:6379> lpop k1
"v3"
# rpop k1 从列表中删除v6,并返回,当前列表全部值v2 v1 v4 v5
127.0.0.1:6379> rpop k1
"v6"
注意:
值在键在,值光键亡
【4】lindex
获取列表index位置的值(从左开始)。
语法结构:
lindex key index
【5】llen
获取列表长度。
语法结构:
llen key
【6】lrem
从左边开始删除与value相同的count个元素。
语法结构:
lrem key count value
示例:
#从左边开始删除k1列表中2个v1元素
lrem k1 2 v1
【7】linsert
在列表中value值的前边/后边插入一个new value值(从左开始)。
语法结构:
linsert key before/after value newvalue
示例:
# 在v1前面插入一个v5
linsert k1 before v1 v5
【8】lset
将索引为index的值设置为value
语法结构:
lset key index value
消息队列
排行榜
最新列表
与List类似是一个列表功能,但Set是自动去重的,当需要存储一个列表数据,又不希望出现重复数据时,Set是一个很好的选择。 Set是String类型的无序集合,它底层其实是一个value为null的hash表,所以添加、删除、查找的时间复杂度都是O(1)。
【1】sadd
将一个或多个元素添加到集合key中,已经存在的元素将被忽略。
语法结构:
sadd key value1 value2……
【2】smembers
取出该集合的所有元素。
语法结构:
smembers key
【3】sismember
判断集合key中是否含有value元素,如有返回1,否则返回0。
语法结构:
sismember key value
【4】scard
返回该集合的元素个数。
语法结构:
scard key
【5】srem
删除集合中的一个或多个成员元素,不存在的成员元素会被忽略。
语法结构:
srem key value1 value2……
【6】spop
随机删除集合中一个元素并返回该元素。
语法结构:
spop key
【7】srandmember
随机取出集合中count个元素,但不会删除。
语法结构:
srandmember key count
【8】smove
将value元素从sourcekey集合移动到destinationkey集合中。
语法结构:
smove sourcekey destinationkey value
注意:
如果 sourcekey集合不存在或不包含指定的 value元素,则smove 命令不执行任何操作,仅返回 0 。
【9】sinter
返回两个集合的交集元素。
语法结构:
sinter key1 key2
【10】sunion
返回两个集合的并集元素。
语法结构:
sunion key1 key2
【11】sdiff
返回两个集合的差集元素(key1中的,不包含key2)
语法结构:
sdiff key1 key2
黑白名单
随机展示
好友
关注人
粉丝
感兴趣的人集合
【1】hset
给key集合中的field赋值value。
语法结构:
hset key field value
注意:
如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作。
如果字段已经存在于哈希表中,旧值将被重写。
【2】hget
从key哈希中,取出field字段的值。
语法结构:
hget key field
【3】hmset
批量设置哈希的字段及值。
语法结构:
hmset key field1 value1 field2 value2……
【4】hexists
判断指定key中是否存在field
语法结构:
hexists key field
【5】hkeys
获取该哈希中所有的field。
语法结构:
hkeys key
【6】hvals key
获取该哈希中所有的value。
语法结构
hvals key
【7】hincrby
为哈希表key中的field字段的值加上增量increment。
语法结构:
hincrby key field increment
注意:
增量也可以为负数,相当于对指定字段进行减法操作。
如果哈希表的 key 不存在,一个新的哈希表被创建并执行 hincrby 命令。
如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 。
对一个储存字符串值的字段执行 hincrby 命令将造成一个错误。
【8】hdel
删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略。
语法结构:
hdel key field1 field2……
【9】hsetnx
给key哈希表中不存在的的字段赋值 。
语法结构:
hsetnx key field value
购物车
存储对象
Zset与Set非常相似,是一个没有重复元素的String集合。不同之处是Zset的每个元素都关联了一个分数(score),这个分数被用来按照从低分到高分的方式排序集合中的元素。集合的元素是唯一的, 但分数可以重复。有顺序的Set
注意:
因为元素是有序的,所以可以根据分数(score)或者次序(position)来获取一个范围内的元素。
【1】zadd
将一个或多个元素(value)及分数(score)加入到有序集key中。
语法结构:
zadd key score1 value1 score2 value2……
示例:
zadd k1 100 java 200 c++ 300 python 400 php
注意:
如果某个元素已经是有序集的元素,那么更新这个元素的分数值,并通过重新插入这个元素,来保证该元素在正确的位置上。
分数值可以是整数值或双精度浮点数。
如果有序集合 key 不存在,则创建一个空的有序集并执行 zadd 操作。
【2】zrange
返回key集合中的索引start和索引end之间的元素(包含start和end)。
语法结构:
zrange key start end [withscores]
示例:
zrange k1 0 -1 返回集合中所有元素 zrange k1 0 -1 withscores 返回集合中所有元素,并携带元素分数
注意:
其中元素的位置按分数值递增(从小到大)来排序。 其中 0 表示列表的第一个元素,-1表示最后一个元素。
withscores是可选参数,是否返回分数。
【3】zrangebyscore
返回key集合中的分数minscore 和分数maxscore 之间的元素(包含minscore 和maxscore )。其中元素的位置按分数值递增(从小到大)来排序。
语法结构:
zrangebyscore key minscore maxscore [withscores]
示例:
zrangebyscore k1 200 400 返回200-400分之间的元素递增排序
【4】zincrby
为元素value的score加上increment的值。
语法结构:
zincrby key increment value
示例:
zincrby k1 50 java 给java元素加上50分
【5】zrem
删除该集合下value的元素。
语法结构
zrem k1 php 删除php
【6】zcount
统计该集合在minscore 到maxscore分数区间中元素的个数。
语法结构:
zcount key minscore maxscore
示例:
zcount k1 100 300 统计100分到300分中间元素的个数
【7】zrank
返回value在集合中的排名,从0开始。
语法结构:
zrank key value
示例:
zrank k1 c++ 返回c++排名
延时队列
排行榜
限流
在计算机中,用二进制(位)作为存储信息的基本单位,1个字节等于8位。
例如 "abc" 字符串是由 3 个字节组成,计算机存储时使用其二进制表示,"abc"分别对应的ASCII码是97、98、99,对应的二进制是01100001、01100010、01100011,在内存中表示如下:
【1】setbit
设置Bitmaps中某个偏移量的值。
语法结构:
setbit key offset value
示例: 记录张三 1月份上班打卡情况
127.0.0.1:6379> setbit zhangsan:1 0 1 (integer) 0 127.0.0.1:6379> setbit zhangsan:1 1 1 (integer) 0 127.0.0.1:6379> setbit zhangsan:1 2 1 (integer) 0 127.0.0.1:6379> setbit zhangsan:1 3 0 (integer) 0 127.0.0.1:6379> setbit zhangsan:1 4 1 (integer) 0 127.0.0.1:6379> setbit zhangsan:1 5 1 (integer) 0 127.0.0.1:6379> setbit zhangsan:1 6 0 (integer) 0
【2】getbit
获取Bitmaps中某个偏移量的值。
语法结构:
getbit key offset
示例:
查看张三1月份 第三天、第四天是否上班
127.0.0.1:6379> getbit zhangsan:1 2 (integer) 1 127.0.0.1:6379> getbit zhangsan:1 3 (integer) 0
【3】bitcount
统计字符串被设置为1的bit数量。一般情况下,给定的整个字符串都会被进行统计,可以选择通过额外的start和end参数,指定字节组范围内进行统计(包括start和end),0表示第一个元素,-1表示最后一个元素。
语法结构:
bitcount key [start end]
示例:统计张三上班天数
127.0.0.1:6379> bitcount zhangsan:1 (integer) 5
【4】bitop
将多个bitmaps通过求交集/并集方式合并成一个新的bitmaps。
语法结构:
bitop and/or destkey sourcekey1 sourcekey2……
示例:
bitop and k3 k1 k2 通过求交集将k1 k2合并成k3 bitop or k3 k1 k2 通过求并集将k1 k2合并成k3
活跃天数
打卡天数
登录天数
用户签到
统计活跃用户
统计用户是否在线
实现布隆过滤器
GEO,Geographic,地理信息的缩写。该类型就是元素的二维坐标,在地图上就是经纬度。Redis基于该类型,提供了经纬度设置、查询、范围查询、距离查询、经纬度Hash等常见操作。
【1】geoadd
用于存储指定的地理空间位置,可以将一个或多个经度 (longitude)、纬度(latitude)、位置名称(member)添加到指定的 key中。
语法结构:
geoadd key longitude latitude member
示例:
# 将北京的经纬度和名称添加到china geoadd china 116.405285 39.904989 beijing # 将成都和上海的经纬度、名称添加到china geoadd china 104.065735 30.659462 chengdu 121.472644 31.231706 shanghai
【2】geopos
从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
语法结构:
geopos key member [member ……]
示例:
返回china中名称为shanghai和beijing的经纬度
geopos china shanghai beijing
【3】geodist
用于返回两个给定位置之间的距离。
语法结构:
geodist key member1 member2 [m|km|ft|mi]
参数说明:
m :米,默认单位。
km :千米。
mi :英里。
ft :英尺。
示例:
# 返回shanghai和beijing之间的距离,结果1067597.9668,单位米 geodist china shanghai beijing # 返回shanghai和chengdu之间的距离,结果1660.0198,单位是千米 geodist china shanghai beijing km 【4】georadius
以给定的经纬度(longitude latitude)为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离(radius )的所有位置元素。
语法结构:
georadius key longitude latitude radius m|km|ft|mi
示例:
#获取经纬度110 30为中心,在china内1200公里范围内的所有元素。 georadius china 110 30 1200 km
附近的电影院
附近的好友
离最近的火锅店
在我们做站点流量统计的时候一般会统计页面UV(独立访客:unique visitor)和PV(即页面浏览量:page view)。redis HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是:在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且使很小的。
什么是基数?
比如数据集{1,3,5,7,5,7,8},那么这个数据集的基数集为{1,3,5,7,8}, 基数(不重复元素)为5.基数估计就是在误差可接受的范围内,快速计算基数。就是不重复的元素的个数
【1】pfadd
将所有元素参数添加到 Hyperloglog 数据结构中。
语法结构:
pfadd key element1 element2……
示例:
如果至少有个元素被添加返回 1, 否则返回 0。
pfadd book1 uid1 uid2 uid3
注意:
添加元素到HyperLogLog中,如果内部有变动返回1,没有返回0。
【2】pfcount
计算Hyperloglog 近似基数,可以计算多个Hyperloglog ,统计基数总数。
语法结构:
pfcount key1 key2……
示例:
pfcount book1 #计算book1的基数,结果为3 pfadd book2 uid3 uid4 #添加两个元素到book2中 pfcount book1 book2 #统计两个key的基数总数,结果为5
【3】pfmerge
将一个或多个Hyperloglog(sourcekey1) 合并成一个 Hyperloglog (destkey )。
语法结构:
pfmerge destkey sourcekey1 sourcekey2……
示例: 比如每月活跃用户可用每天活跃用户合并后计算。
#将book1和book2合并成book,结果为5 pfmerge book book1 book2
基数不大,数据量不大就用不上,会有点大材小用浪费空间,有局限性,就是只能统计基数数量,而没办法去知道具体的内容是什么,和bitmap相比,属于两种特定统计情况,简单来说,HyperLogLog 去重比 bitmaps 方便很多,一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃。
网站PV统计
网站UV统计
统计访问量(IP数)
统计在线用户数
统计每天搜索不同词条的个数
统计文章真实阅读数
在redis的解压目录下有个重要的配置文件redis.conf
配置大小单位,开头定义基本度量单位,只支持bytes,大小写不敏感。
Redis只有一个配置文件,如果多个人进行开发维护,那么就需要多个这样的配置文件,这时候多个配置文件就可以在此通过 include/path/to/local.conf 配置进来,而原本的 redis.conf 配置文件就作为一个总闸。
参数:
bind:绑定redis服务器网卡IP,默认为127.0.0.1,即本地回环地址。这样的话,访问redis服务只能通过本机的客户端连接,而无法通过远程连接。如果bind选项为空的话,那会接受所有来自于可用网络接口的连接。
port:指定redis运行的端口,默认是6379。由于Redis是单线程模型,因此单机开多个Redis进程的时候会修改端口。
timeout:设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接。默认值为0,表示不关闭。
tcp-keepalive :单位是秒,表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器一直阻塞,官方给出的建议值是300s,如果设置为0,则不会周期性的检测。
具体配置详解:
daemonize:设置为yes表示指定Redis以守护进程的方式启动(后台启动)。默认值为 no
pidfile:配置PID文件路径,当redis作为守护进程运行的时候,它会把 pid 默认写到 /var/redis/run/redis_6379.pid 文件里面
loglevel :定义日志级别。默认值为notice,有如下4种取值:
debug(记录大量日志信息,适用于开发、测试阶段)
verbose(较多日志信息)
notice(适量日志信息,使用于生产环境)
warning(仅有部分重要、关键信息才会被记录)
logfile :配置log文件地址,默认打印在命令行终端的窗口上
databases:设置数据库的数目。默认的数据库是DB 0 ,可以在每个连接上使用select 命令选择一个不同的数据库,dbid是一个介于0到databases - 1 之间的数值。默认值是 16,也就是说默认Redis有16个数据库。
这里的配置主要用来做持久化操作。
参数:
save:这里是用来配置触发 Redis的持久化条件,也就是什么时候将内存中的数据保存到硬盘
save 900 1:表示900 秒内如果至少有 1 个 key 的值变化,则保存
save 300 10:表示300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000:表示60 秒内如果至少有 10000 个 key 的值变化,则保存
参数:
slave-serve-stale-data:默认值为yes。当一个 slave 与 master 失去联系,或者复制正在进行的时候,slave 可能会有两种表现:
如果为 yes ,slave 仍然会应答客户端请求,但返回的数据可能是过时,或者数据可能是空的在第一次同步的时候。
如果为 no ,在你执行除了 info he salveof 之外的其他命令时,slave 都将返回一个 "SYNC with master in progress" 的错误
slave-read-only:配置Redis的Slave实例是否接受写操作,即Slave是否为只读Redis。默认值为yes。
repl-diskless-sync:主从数据复制是否使用无硬盘复制功能。默认值为no。
repl-diskless-sync-delay:当启用无硬盘备份,服务器等待一段时间后才会通过套接字向从站传送RDB文件,这个等待时间是可配置的。
repl-disable-tcp-nodelay:同步之后是否禁用从站上的TCP_NODELAY 如果你选择yes,redis会使用较少量的TCP包和带宽向从站发送数据。
requirepass:设置redis连接密码。 比如: requirepass 123 表示redis的连接密码为123。
maxclients :设置客户端最大并发连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件。 描述符数-32(redis server自身会使用一些),如果设置maxclients为0 。表示不作限制。当客户端连接数到达限制时, Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
参数:
maxmemory:设置Redis的最大内存,如果设置为0 。表示不作限制。通常是配合下面介绍的maxmemory-policy参数一起使用。
maxmemory-policy :当内存使用达到maxmemory设置的最大值时,redis使用的内存清除策略。有以下几种可以选择:
volatile-lru 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )
allkeys-lru 利用LRU算法移除任何key
volatile-random 移除设置过过期时间的随机key
allkeys-random 移除随机key
volatile-ttl 移除即将过期的key(minor TTL)
noeviction noeviction 不移除任何key,只是返回一个写错误 ,默认选项
参数:
appendonly:默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化, Append Only File是另一种持久化方式, 可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入appendonly.aof文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。默认值为no。
appendfilename :aof文件名,默认是"appendonly.aof"
appendfsync:aof持久化策略的配置;no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快;always表示每次写入都执行fsync,以保证数据同步到磁盘;everysec表示每秒执行一次fsync,可能会导致丢失这1s数据
参数: lua-time-limit:一个lua脚本执行的最大时间,单位为ms。默认值为5000.
参数:
cluster-enabled:集群开关,默认是不开启集群模式。
cluster-config-file:集群配置文件的名称。
cluster-node-timeout :可以配置值为15000。节点互连超时的阀值,集群节点超时毫秒数
cluster-slave-validity-factor :可以配置值为10。
在Redis官网中提供了各种语言的客户端,地址:https://redis.io/docs/connect/clients/
标记为*的就是推荐使用的java客户端,包括:
Jedis和Lettuce:这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。
Redisson:是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。
Jedis的官网地址: https://github.com/redis/jedis
1)引入依赖:
redis.clients
jedis
3.7.0
org.apache.commons
commons-pool2
2.11.1
org.junit.jupiter
junit-jupiter
5.7.0
test
2)编写入门程序
新建一个单元测试类,内容如下:
/**
* jedis入门程序
*/
public class JedisTest {
Jedis jedis;
@BeforeEach
public void before(){
//建立连接
jedis = new Jedis("192.168.184.129",6379);
//设置密码
jedis.auth("123456");
//选择库
jedis.select(0);
}
@Test
public void stringTest(){
//存数据
jedis.set("name","张三");
//获取数据
System.out.println(jedis.get("name"));
}
@Test
public void hashTest(){
//存数据
jedis.hset("user:1","name","张三");
jedis.hset("user:1","age","22");
//取数据
Map map = jedis.hgetAll("user:1");
System.out.println(map);
}
@AfterEach
public void after(){
if(jedis!=null){
jedis.close();
}
}
}
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。
public class JedisConnectionFactory {
private static JedisPool jedisPool;
static {
//配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
// 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
jedisPool = new JedisPool(poolConfig, "192.168.184.129", 6379, 1000, "123456");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
public static void main(String[] args) {
System.out.println(JedisConnectionFactory.getJedis());
}
}
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:Spring Data Redis
提供了对不同Redis客户端的整合(Lettuce和Jedis)
提供了RedisTemplate统一API来操作Redis
支持Redis的发布订阅模型
支持Redis哨兵和Redis集群
支持基于Lettuce的响应式编程
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
API | 返回值类型 | 说明 |
---|---|---|
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
redisTemplate.opsForZset() | ZSetOperations | 操作SortedSet类型数据 |
redisTemplate | 通用命令 |
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单。
首先,新建一个maven项目,然后按照下面步骤执行:
1)引入依赖
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
com.fasterxml.jackson.core
jackson-databind
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
2)配置Redis
spring:
redis:
host: 192.168.184.129
port: 6379
password: 123456
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100ms
3)注入RedisTemplate,编写测试代码
@SpringBootTest
public class RedisTemplateTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testString() {
// 写入一条String数据
redisTemplate.opsForValue().set("name", "张三");
// 获取string数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}
RedisTemplate可以接收任意Object作为值写入Redis:
只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
我们可以自定义RedisTemplate的序列化方式,代码如下:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory){
// 创建RedisTemplate对象
RedisTemplate template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
// 设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}
这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如图:
整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
省去了我们自定义RedisTemplate的序列化方式的步骤,而是直接使用:
@SpringBootTest
public class StringRedisTemplateTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON序列化工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User();
user.setId(2);
user.setName("李四");
user.setAge(33);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:2", json);
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:2");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
}
但一般还是常用自动序列化
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
看到发布订阅的特性,可用来做一个简单的实时聊天系统。再比如,在一个博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们拉。
订阅
语法格式:
subscribe 频道名字
示例:
127.0.0.1:6379> subscribe channel-1 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "channel-1" 3) (integer) 1
发布命令
语法格式:
publish channel-1 hello
示例: 打开另一个客户端,给channel1发布消息hello
127.0.0.1:6379> publish channel-1 hello (integer) 1
注意: 返回的1是订阅者数量。
打开第一个客户端可以看到发送的消息
127.0.0.1:6379> subscribe channel-1 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "channel-1" 3) (integer) 1 1) "message" 2) "channel-1" 3) "hello"
注意: 发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息。
慢查询,顾名思义就是比较慢的查询,但是究竟是哪里慢呢?
Redis执行命令过程如下:
说明:
慢查询发生在第3阶段
客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
慢查询日志是存放在Redis内存列表中
什么是慢查询日志?
慢查询日志是Redis服务端在命令执行前后计算每条命令的执行时长,当超过某个阈值是记录下来的日志。日志中记录了慢查询发生的时间,还有执行时长、具体什么命令等信息,它可以用来帮助开发和运维人员定位系统中存在的慢查询。
如何获取慢查询日志?
可以使用 slowlog get 命令获取慢查询日志,在 slowlog get 后面还可以加一 个数字,用于指定获取慢查询日志的条数,比如,获取3条慢查询日志:
127.0.0.1:6379> SLOWLOG get 3 1) (integer) 0 2) (integer) 1640056567 3) (integer) 11780 4) 1) "FLUSHALL" 5) "127.0.0.1:43406" 6) ""
参数:
唯一标识ID
命令执行的时间戳
命令执行时长
执行的命名和参数
如何获取慢查询日志的长度?
可以使用 slowlog len 命令获取慢查询日志的长度
> slowlog len (integer) 121
注意: 当前Redis中有121条慢查询日志。
配置慢查询参数如下:
命令执行时长的指定阈值 slowlog-log-slower-than:
slowlog-log-slower-than的作用是指定命令执行时长的阈值,执行命令的时长超过这个阈值时就会被记录下来。
存放慢查询日志的条数 slowlog-max-len。
slowlog-max-len的作用是指定慢查询日志最多存储的条数。实际上,Redis使用了一个列表存放慢查询日志,slowlog-max-len就是这个列表的最大长度。
如何进行配置:
【1】查看慢日志配置
查看redis慢日志配置,登陆redis服务器,使用redis-cli客户端连接redis server
127.0.0.1:6379> config get slow* 1) "slowlog-max-len" 2) "128" 3) "slowlog-log-slower-than" 4) "10000"
慢日志说明:
10000阈值,单位微秒,此处为10毫秒,128慢日志记录保存数量的阈值,此处保存128条。
【2】修改Redis配置文件
比如,把slowlog-log-slower-than设置为1000,slowlog-max-len 设置为1200
slowlog-log-slower-than 1000 slowlog-max-len 1200
【3】也可以使用 config set 命令动态修改。
比如,还是把slowlog-log-slower-than设置为1000,slowlog-max-len设置为1200
> config set slowlog-log-slower-than 1000 OK > config set slowlog-max-len 1200 OK > config rewrite OK
slowlog-max-len配置建议
线上建议调大慢查询列表,记录慢查询时Redis会对长命令做截断操作,并不会占用大量内存。
增大慢查询列表可以减缓慢查询被剔除的可能,例如线上可设置为1000以上。
slowlog-log-slower-than配置建议
默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值。
由于Redis采用单线程响应命令,对于高流量的场景,如果命令执行时间在1毫秒以上,那么Redis最多可支撑OPS不到1000。因此对于高OPS场景的Redis建议设置为1毫秒。
1次网络命令通信模型
经历了1次时间 = 1次网络时间 + 1次命令时间。
批量网络命令通信模型
经历了 n次时间 = n次网络时间 + n次命令时间
经历了 1次pipeline(n条命令) = 1次网络时间 + n次命令时间,这大大减少了网络时间的开销,这就是流水线。
注意:
在执行批量操作而没有使用pipeline功能,会将大量的时间耗费在每一次网络传输的过程上;而使用pipeline后,只需要经过一次网络传输,然后批量在redis端进行命令操作。这会大大提高了效率。
pipeline-Jedis实现
【1】首先,引入jedis依赖包:
org.springframework.boot
spring-boot-starter-data-redis
【2】没有pipeline的命令执行
@Test
void test1(){
redisTemplate.opsForValue().set("k1","v1");
redisTemplate.opsForValue().set("k2","v2");
redisTemplate.opsForValue().set("k3","v3");
}
@Test
void test2() {
long start = System.currentTimeMillis();
Object k1 = redisTemplate.opsForValue().get("k1");
Object k2 = redisTemplate.opsForValue().get("k2");
Object k3 = redisTemplate.opsForValue().get("k3");
long end = System.currentTimeMillis();
System.out.println("话费时间:"+(end-start));
}
注意:
在不使用pipeline的情况下,使用for循环进行每次一条命令的执行操作,耗费的时间可能达到 1w 条插入命令的耗时为50s。
【3】使用pipeline
@Test
void test3() {
redisTemplate.executePipelined((RedisConnection connection)->{
long start = System.currentTimeMillis();
Object k1 = connection.get("k1".getBytes());
Object k2 = connection.get("k2".getBytes());
Object k3 = connection.get("k3".getBytes());
long end = System.currentTimeMillis();
System.out.println("话费时间:"+(end-start));
return null;
});
}
耗时会大大减少
由于Redis的数据都存放在内存中,如果没有配置持久化,Redis重启后数据就全丢失了,于是需要开启Redis的持久化功能,将数据保存到磁盘上,当Redis重启后,可以从磁盘中恢复数据。
对于Redis而言,持久化机制是指把内存中的数据存为硬盘文件, 这样当Redis重启或服务器故障时能根据持久化后的硬盘文件恢复数据
持久化机制的意义
redis持久化的意义,在于故障恢复。比如部署了一个redis,作为cache缓存,同时也可以保存一些比较重要的数据。
Redis提供了两个不同形式的持久化方式
RDB(Redis DataBase)
AOF(Append Only File)
RDB是什么
在指定的时间间隔内将内存的数据集快照写入磁盘,也就是行话讲的快照,它恢复时是将快照文件直接读到内存里。
配置dump.rdb文件
RDB保存的文件,在redis.conf中配置文件名称,默认为dump.rdb。
440 # The filename where to dump the DB
441 dbfilename dump.rdb
rdb文件的保存位置,也可以修改。默认在Redis启动时命令行所在的目录下。
当前路径
dir ./
触发机制-主要三种方式
RDB配置
快照默认配置:
save 3600 1:表示3600秒内(一小时)如果至少有1个key的值变化,则保存。
save 300 100:表示300秒内(五分钟)如果至少有100个 key 的值变化,则保存。
save 60 10000:表示60秒内如果至少有 10000个key的值变化,则保存。
配置新的保存规则
给redis.conf添加新的快照策略,30秒内如果有5次key的变化,则触发快照。配置修改后,需要重启Redis服务。
save 3600 1
save 300 100
save 60 10000
save 30 5
flushall
执行flushall命令,也会触发rdb规则。
save与bgsave
手动触发Redis进行RDB持久化的命令有两种:
save
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止,不建议使用。
bgsave
执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
stop-writes-on-bgsave-error
默认值是yes。当Redis无法写入磁盘的话,直接关闭Redis的写操作。
rdbcompression
默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
rdbchecksum
默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
恢复数据
只需要将rdb文件放在Redis的启动目录,Redis启动时会自动加载dump.rdb并恢复数据。
优势
适合大规模的数据恢复
对数据完整性和一致性要求不高更适合使用
节省磁盘空间
恢复速度快
劣势
在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来。
AOF默认不开启
可以在redis.conf中配置文件名称,默认为appendonly.aof。
注意:
AOF文件的保存路径,同RDB的路径一致,如果AOF和RDB同时启动,Redis默认读取AOF的数据。
AOF同步频率设置
参数:
appendfsync always 始终同步,每次Redis的写入都会立刻记入日志,性能较差但数据完整性比较好。
appendfsync everysec 每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
appendfsync no redis不主动进行同步,把同步时机交给操作系统。
优势
备份机制更稳健,丢失数据概率更低。
可读的日志文本,通过操作AOF稳健,可以处理误操作。
劣势
比起RDB占用更多的磁盘空间。
恢复备份速度要慢。
每次读写都同步的话,有一定的性能压力。
综合使用AOF和RDB两种持久化机制
用AOF来保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。
【1】你都用过哪些Redis数据类型?
答:String、List、Set、Map、ZSet、BitMaps、GEO、HyperLogLog。
常规缓存用String类型 、对象经常修改字段用Map类型 、BitMaps处理打卡上班记录等问题,GEO处理地理坐标类问题,HyperLogLo处理PV,UV等基数统计操作。
List可以实现排行榜,最新列表等功能。Set实现共同联系人,黑白名单功能。
ZSet可以实现特定条件的排行榜。按照人气排行,人气作为存储分数。
【2】说一下发布订阅机制?
答:发布订阅机制可以实现类似你订阅某个频道后,该频带给你发送消息的功能,订阅频道命令
subscribie 频道 # 发送消息 publish 频道名 消息
【3】如何解决慢查询问题?
答:通过慢查询日志解决,slowlog len获取慢查询记录数,在通过slowlog get n获取具体信息去排查。
还可以通过
> config set slowlog-log-slower-than 1000 OK > config set slowlog-max-len 1200 OK > config rewrite OK
慢查询阈值根据QPS设置
【4】说以下说什么是流水线操作 答:一次网络连接 执行n条redis指令。
【5】说以下Redis持久化机制?
答:Redis支持两种持久化机制:RDB和AOF。RDB是快照持久化,读写速度快,占用磁盘空间小。但有丢失数据风险。AOF通过记录命令日志持久化,不易丢失数据,但是占用磁盘空间大,读写速度慢。RDB适合做冷备,如一天一备份的常规备份。AOF适合做REDIS正常重启前的数据备份,不会丢失数据。