Redis6
这个知识重要吗?有什么用?
(一)NoSQL数据简介
(1)什么是NoSQL,它有什么作用
为了解决性能问题产生的问题。
(1.1)解决服务器CPU、内存压力问题
服务器做集群或者分布式部署
客户端发送请求,使用Nginx(负载均衡/反向代理)
将请求平均的分配给服务器,减少每台服务器的压力。
(1.2)Session共享问题
分布式服务器也导致出现了一个问题,即Session的存放问题,即如果在服务器A中是登录状态(保存了Session),第二次访问时,使用的是服务器B,服务器B中是否有Session?有的话是什么方式保存的?
(1.2.1)存放在Cookie(客户端)中
- 不安全
- 网络负担大
(1.2.2)Session 复制
- 每个服务器都复制一份Session,数据冗余,服务器越多,浪费越多
(1.2.3)使用NoSQL数据库
- 第一次使用服务器A访问将Session存放在NoSQL数据库中,第二次使用服务器B进行访问时,在NoSQL数据库中判断是否存在对应的Session。
- 存放在内存中,不需要IO操作。
- 数据结构简单(key:value)
(1.3)解决数据库IO问题
将一些常用的数据存放在NoSQL数据库(缓存数据库)中,减少对SQL数据库的访问。
(1.4)总结
NoSql 是一种存放在内存中的数据库,不需要IO操作,可以作为缓存数据库,减少服务器负担和IO操作。
(1.2)NoSQL概述
NoSQL = not only Sql
泛指非关系型数据库。
NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式
存储。因此大大的增加了数据库的扩展能力。
- 不遵守 SQL 标准。
- 不支持 ACID(事务的四个特性)。
- 远超过SQL的性能。
(二)Redis
http://redis.io
http://redis.cn/
(1.1)Redis概述
- Redis是一个开源的key-value存储系统。
- 它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希表)
- 这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
- 在此基础上,Redis支持各种不同方式的排序。
- 为了保证效率,数据都是缓存在内存中。
- Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。
- 实现了master-slave(主从)同步。
(1.2)Redis的安装
只考虑linux系统下使用Redis
- 下载安装包到
opt
目录 - 安装C语言编译环境
yum install gcc
- 解压文件
tar -zxvf redis-6.2.1.tar.gz
- 进入目录
cd redis-6.2.1
- 执行
make
命令 - 执行
make install
(1.3)安装目录
- redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
- redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲
- redis-check-dump:修复有问题的dump.rdb文件
- redis-sentinel:Redis集群使用
- redis-server:Redis服务器启动命令
- redis-cli:客户端,操作入口
(1.4)启动Redis
(1.4.1)前台启动
redis-server
端口号:6379
此时是在终端前台使用,那么这个终端就不能进行其他操作了
Ctrl + C
退出
(1.4.2)后台启动
(1.4.2.1)备份redis.conf文件到etc文件夹
进入到Redis文件夹
cp redis.conf /etc/redis.conf
(1.4.2.2)修改etc下的redis.conf
后台启动设置daemonize no改成yes
(1.4.2.3)回到bin目录使用etc下的redis.conf进行启动
启动以后使用ps -ef | grep redis
查看redis进程
(1.4.3)关闭Redis
单实例关闭:redis-cli shutdown
也可以进入终端后再关闭 exit
多实例关闭,指定端口关闭:redis-cli -p 6379 shutdown
(1.5)Redis相关知识
端口号:6379
- 默认十六个数据库,下标从零开始,默认使用零号库,
select index
来切换数据库,每个库的密码是相同的。
Redis是单线程+多路IO复用技术
三、Redis中的基本数据类型
(3.1)Redis中的键
-
keys *
查看当前库的所有的 Key
-
exists key
判断某个 Key 是否存在
1 :存在
0:不存在
-
type key
查看 Key 是什么类型 -
del Key
直接删除 Key -
unlink key
根据value选择非阻塞删除- 仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作,即在后续慢慢的进行删除。
-
expire key 10
10秒钟:为给定的key设置过期时间,过期=在数据库无法取得了
-
ttl key
查看还有多少秒过期-1
表示永不过期-2
表示已过期
-
select index
切换库 -
dbsize
查看当前数据库key的数量 -
flushdb
清空当前库 -
flushall
清空全部库
(3.2)字符串(String)
- String 是 Redis 中最基本的联系,一个 key 对应一个 value
- 是二进制安全的,意味着 String 可以包含任何数据,包括图片、视频、序列化对象
- Value最大容量为 512MB
(3.2.1)字符串的操作
-
set <key><value>
:将一个键值对放入数据库中,其中key value 是必要的。设置相同的
key
会把之前的value
进行覆盖 -
get <key>
获取key
对应的值 -
append <key><value>
将指定的value
追加到末尾 -
strlen <key>
获取到当前key对应value的长度 -
setnx <key><value>
当key不存在时设置value的值,返回值代表是否设置成功。 -
incr <key>
将 key 中存储的数字加1,只能对数字进行操作(否则报错config set stop-writes-on-bgsave-error no
解除错误),如果key对应为空,创建为1 -
decr <key>
将 key 中存储的数字减1 -
incrby/decrby <key><步长>
将 key 中存储的数字添加减去步长,步长是自定义的。 -
mset<key1><value1><key2><value2>
同时设一个或多个键值对 -
mget<key1><key2>
同时取出多个值。 -
msetnx <key1><value1><key2><value2>
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在,msetnx
是原子性的,有一个失败都失败。 -
getrange <key><下标起始位置><下标结束位置>
获取value下标区间的值(下标从零开始) -
setrange <key><下标起始位置><value>
用value将起始位置开始的value进行覆盖 -
set <key><过期时间><value>
设置键值的同时,设置过期时间,单位秒。 -
getset <key><value>
获取到旧值,并用新值进行替换。
(3.2.2)原子性操作
incr
是原子性操作
原子操作是指不会被线程调度机制打断的操作,原子操作一旦开始,就会一直运行到结束,中间不会有任何的 context switch
- 在单线程中,能够被单条指令完成的操作都可以认为是
原子操作
,因为中断只能发生在指令之间。 - 在多线程中,不能被其他进程打断的操作就叫
原子操作
。
Redis单命令的原子性主要得益于Redis的单线程。
(3.2.3)String 底层的数据结构
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配.
(3.2)列表(List)
特点:单键多值
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
(3.2.1)列表的操作
lpush / rpush <key><value1><value2><value3>
从左边/右边插入一个或多个值。lpop/rpop <key>
从左边/右边吐出一个值,当没有值时,对应的键就自动删除了rpoplpush <key1><key2>
从<key1>
列表中右边弹出一个元素,在<key2>
列表的左边进行压入。lrange <key><start><stop>
按照索引下标获取元素(0 -1)表示全部的元素lindex <key> <index>
按照索引下标获取元素(从左向右)llen<key>
获取列表的长度linsert <key> before <value><newvalue>
在<value>
的后面插入<newvalue>
插入值lrem <key><n><value>
从左边删除n个value(从左到右)lset<key><index><value>
将列表key下标为index的值替换成value
(3.2.2)列表的数据结构
元素少的时候使用ziplist,元素多的时候将ziplist作为节点形成quicklist。
(4)集合(set)
和列表相似,但是可以自动进行去重操作。
是String类型的无序集合,底层是一个value为null的哈希表,添加、删除、查找时间复杂度都是O(1),即随着数据量的增加,这些操作所耗费的时间不变。
(4.1)集合的操作
-
sadd <key><value1><value2>
… 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略 -
smembers <key>
取出该集合的所有值。 -
sismember <key><value>
判断集合<key>
是否为含有该<value>
值,有1,没有0 -
scard<key>
返回该集合的元素个数。 -
srem <key><value1><value2>
… 删除集合中的某个元素。 -
spop <key>
随机从该集合中弹出一个值(当键不对应任何值时,键将消失)。 -
srandmember <key><n>
随机从该集合中取出n个值。不会从集合中删除 。 -
smove <source><destination>value
把集合中一个值从一个集合移动到另一个集合 -
sinter <key1><key2>
返回两个集合的交集元素。 -
sunion <key1><key2>
返回两个集合的并集元素。 -
sdiff <key1><key2>
返回两个集合的差集元素(key1中的,不包含key2中的)
(4.2)数据结构
Set数据结构是dict字典,字典是用哈希表实现的。
Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
(5)哈希(Hash)
Redis hash 是一个键值对集合。
即值是一个Map<String, object>
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储
通过 key + field 来操作对应的属性数据。
(5.1)使用Hash
-
hset <key><field><value>给<key>
给key
中的field
赋值value
-
hget <key><field>
从key
集合中field
取出value
-
hmset <key1><field1><value1><field2><value2>
批量设置hash的值 -
hexists<key1><field>
查看key
中给定的field
是否存在 -
hkeys <key>
列出当前hash集合中的全部field
-
hvals <key>
列出当前hash集合中全部的value
-
hincrby <key><field><increment>
为哈希表 key 中的域 field 的值加上增量 1 -1 -
hsetnx <key><field><value>
将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在 .
(5.2)数据结构
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
(6)有序集合 Zset
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。
因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
(6.1)常用命令
-
zadd <key><score1><value1><score2><value2>
将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 -
zrange <key><start><stop> [WITHSCORES]
start 是最小值 stop 是最大值,此时的排名根据评分递增的,添加WITHSCORES可以显示评分。 -
zrangebyscore key min max [withscores] [limit offset count]
将评分在[min,max]
输出 -
zrevbyscore
从大到小排序 -
zincrby <key><increment><value>
为元素的score加上增量
zrem <key><value>
删除该集合下,指定值的元素
zcount <key><min><max>
统计该集合,分数区间内的元素个数
zrank <key><value>
返回该值在集合中的排名,从0开始。
(6.2)数据结构
SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
四、Redis配置文件
(4.1)Units单位
Redis 中只支持 bytes 字节进行配置
大小写不敏感
(4.2)INCLUDES包含
类似jsp中的include,多实例的情况可以把公用的配置文件提取出来。
(4.3)网络配置 network(需要修改)
默认情况bind=127.0.0.1
:只能接受本机的访问请求,不写的情况下,无限制接受任何ip地址的访问。
生产环境肯定要写你应用服务器的地址;服务器是需要远程访问的,所以需要将其注释掉
是否开启保护模式,需要改为 no
端口号
设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。
在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。
一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。
对访问客户端的一种心跳检测,每个n秒检测一次。
单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60
(4.4)GENERAL 通用(需要修改)
是否为后台进程,需要设置为 yes
存放pid文件的位置,每个实例会产生一个不同的pid文件(进程号)
指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice
四个级别根据使用阶段来选择,生产环境选择notice 或者warning
日志文件的名称
数据库的数量
(4.4)Security(需要修改)
默认关闭密码,如果需要使用密码取消注释。
在命令中设置的密码是临时的,当重启Redis服务器以后,密码就会进行还原。
如果需要永久设置,需要在配置文件中进行设置。
(4.5)Limites
设置redis同时可以与多少个客户端进行连接。
- 默认情况下为10000个客户端。
- 如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。
- 建议必须设置,否则,将内存占满,造成服务器宕机
- 设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。
= 如果redis无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么redis则会针对那些需要申请内存的指令返回错误信息,比如SET、LPUSH等。 - 但是对于无内存申请的指令,仍然会正常响应,比如GET等。如果你的redis是主redis(说明你的redis有从redis),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素。
- volatile-lru:使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用)
- allkeys-lru:在所有集合key中,使用LRU算法移除key
- volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
- allkeys-random:在所有集合key中,移除随机的key
- volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
- noeviction:不进行移除。针对写操作,只是返回错误信息
五、Redis6的发布和订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
1、客户端可以订阅频道如下图
2、当给这个频道发布消息后,消息就会发送给订阅的客户端
(5.1)使用发布和订阅
- 开启两个客户端,客户端A订阅频道channelTest,客户端B给频道channelTest发送一条消息。
注:发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息。
六、Redis提供的新数据类型
(6.1)Bitmaps
Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:
- 本身不是一种数据类型, 实际上它就是字符串 , 但是它可以对字符串的位进行操作。
- 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量。
(6.1.1)常用操作
setbit<key><offset><value>
对key对应的偏移量进行设置,offset
从零开始。
例如有20个用户,userid=1, 6, 11, 15, 19的用户进行了访问。
在第一次初始化Bitmaps时, 假如偏移量非常大, 那么整个初始化过程执行会比较慢, 可能会造成Redis的阻塞。
getbit<key><offset>
获取Bitmaps中某个偏移量的值
-
bitcount <key> [start end]
统计字符串被设置为1的bit数,不设置start end 表示全部统计,start end 表示从字节的闭区间,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,一个字节八位bit。 -
注意start end 考虑的是字节下标,而不是bit
-
bitop and(or/not/xor) <destkey> [key…]
bitop是一个复合操作, 它可以做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中。
1号访问的用户ID为 1,3,4,5
2号访问的用户ID为 3,4,6
求出两天都进行访问的用户(交集),两天不同访问用户的个数(交集)
(6.1.2)和Set进行比较
当不同的访问用户更多时,使用bitmaps可以有效的节省空间。
当相同的访问用户更多时,使用set更能节省空间,因为bitmaps有大量的bit空闲。
(6.2)HyperLogLog
是为了解决求集合中不重复元素的个数问题(基数问题)。
基数:不重复元素
HyperLogLog计算基数所需的空间总是固定的、很小的。
每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
(6.2.1)常用操作
pfadd <key>< element> [element ...]
添加指定元素到 HyperLogLog 中,将所有元素添加到指定HyperLogLog数据结构中。如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0。pfcount<key> [key ...]
计算HLL的近似基数,可以计算多个HLL,比如用HLL存储每天的UV,计算一周的UV可以使用7天的UV合并计算即可
pfmerge<destkey><sourcekey> [sourcekey ...]
将一个或多个HLL合并后的结果存储在另一个HLL中,比如每月活跃用户可以使用每天的活跃用户来合并计算可得
七、Jeids(通过Java来操作 Redis)
(7.1)添加依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
(7.2)连接Redis注意事项
- 禁用Linux的防火墙:Linux(CentOS7)里执行命令
- systemctl stop/disable firewalld.service
- redis.conf中注释掉bind 127.0.0.1
- 然后 protected-mode no
(7.3)Jdesi-Api
同在linux一样的方法名称。
(7.4)完成手机验证功能
要求:
- 输入手机号,点击发送后随机生成6位数字码,2分钟有效
- 输入验证码,点击验证,返回成功或失败
- 每个手机号每天只能输入3次
在Java中使用random生成一个六位数字,加入到Redis里面,并且设置过期时间为两分钟,每次判断数据库中是否存在,使用incr,当大于2时就不进行发送了
public class PhoneCode {
private final static String HOST = "192.168.19.131";
private final static Integer PORT = 6379;
public static void main(String[] args) {
String phone = "123";
verityPhone(phone);
Scanner in = new Scanner(System.in);
String next = in.next();
verityCode(phone, next);
}
// 1.获取验证码
private static String getRandomCode() {
StringBuilder code = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 6; i++) {
code.append(random.nextInt(10));
}
return code.toString();
}
// 2.判断请求次数,并将验证码加入到数据库中
private static void verityPhone(String phone) {
Jedis jedis = new Jedis(HOST, PORT);
String verityCount = "verityCount:" + phone;
String verityCode = "verityCode:" + phone;
String count = jedis.get(verityCount);
if (count == null) {
jedis.setex(verityCode, 24 * 60 * 60, "1");
}
else if (Integer.parseInt(count) <= 2) {
jedis.incr(verityCount);
}
else if (Integer.parseInt(count) > 2) {
System.out.println("每天只能发送三次请求");
jedis.close();
return;
}
String randomCode = getRandomCode();
jedis.setex(verityCode, 120, randomCode);
jedis.close();
}
// 3.判断用户输入和数据库中验证码是否相同
public static void verityCode(String phone, String code) {
Jedis jedis = new Jedis(HOST, PORT);
String verityCode = "verityCode:" + phone;
String redisCode = jedis.get(verityCode);
if (redisCode == null) {
jedis.close();
return;
}
else if (redisCode.equals(code)) {
System.out.println("成功");
}
else {
System.out.println("失败");
}
jedis.close();
}
}
八、SpringBoot整合Redis
(8.1)添加场景
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- 导入jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
自动配置:
RedisAutoConfiguration 自动配置类。RedisProperties 属性类 --> spring.redis.xxx是对redis的配置
连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration
自动注入了RedisTemplate<Object, Object> : xxxTemplate;
自动注入了StringRedisTemplate;k:v都是String
key:value
底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis
配置application
redis:
#redis://user:password@example.com:6379
九、事务和锁
(9.1)事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
有三个命令
multi
从输入multi
开始以后输入的命令都会依次的进入到命令队列中,但是不会进行执行。Exec
输入Exec
后,Redis会将命令队列中的命令依次执行。- 在输入
multi
后,可以使用discard
放弃组队。
事务的错误处理:
- 当组队阶段发生错误,此时命令队列被取消。
- 当执行时发生错误,报错命令将不会被执行,其他命令继续执行,不会进行回滚。
(9.2)事物冲突
(9.3)使用悲观锁解决事务冲突
悲观锁(Pessimistic Lock) 每次拿到数据时都会对数据进行上锁,防止其他进程修改,其他进程无法再获取数据,只有处理完成以后才释放锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
(9.4)乐观锁
乐观锁(Optimistic Lock) 每次获取数据时,它不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
版本号机制:每个数据都有一个版本号,当修改数据就会更新版本号,在修改数据前会判断版本号是否比当前高,如果高就不进行修改。
乐观锁适用于多读的应用类型,这样可以提高吞吐量。
Redis就是利用这种check-and-set机制实现事务的。
(9.5)使用乐观锁
WATCH key [key ...]
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
unwatch <key>
取消去事务的监视。
(9.6)Redis事务三特性
- 单独的隔离操作,事务中所有的命令都会序列化,按照顺序执行,事务在执行的过程中,不会被其他客户端发送的命令请求打断。
- 没有隔离级别概念,队列中的命令在没有提交前都不会实际执行,因为在提交到命令队列时,命令是不会执行的。
- 不保证原子性,事务中如果一条命令执行失败,其他的命令仍然会执行,不会进行回滚数据。
十、Redis持久化
Redis是运行在内存中的数据库,那么一但服务器进程退出,那么服务器中的数据库状态就会消失,但是它也可以将数据写到内存中,即持久化。
Redis 提供了2个不同形式的持久化方式。
- RDB(Redis DataBase)
- AOF(Append Of File)
(10.1)RDB
默认持久化使用RDB,默认文件名为 dump.rdb
- 使用
save
命令会触发RDB规则 - 执行
flushall
会触发RDB规则 - 关闭redis,也会触发RDB规则
(10.1.1)RDB是什么
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
时间间隔:每隔一段时间进行一个操作,这段时间就是时间间隔。
数据集快照:当前内存中数据的快照。
(10.1.2)持久化是怎么进行的
-
Redis会单独创建一个子进程(fork)来进行持久化,会先将数据写入到一个临时文件中,当持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,可以防止数据丢失。
-
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
-
RDB的缺点是最后一次持久化后的数据可能丢失。
-
Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
-
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”
-
一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
(10.3)持久化相关配置文件
使用RDB持久化默认的名字
持久化存放的路径,表示当前启动路径
/usr/local/bin
当Redis无法写入磁盘时,直接关闭Redis的写操作,推荐yes
持久化的文件是否进行压缩
检查完整性,会增加百分之10的性能消耗,推荐开启。
在多少秒以内,发生了多少次改变就会进行持久化操作。
(10.4)恢复RDB文件
将RDB文件放到Redis启动目录,Redis就会自动加载RDB文件。
在这个目录下存在.RDB文件,Redis启动时会自动去加载.RDB
十一、主从复制(集群)
主从复制将一台Redis服务器的数据,复制到其他的Redis服务器,前者是主节点,后者成为从节点,数据的复制是单向的,只能从主节点到从节点,master以写为主,slave以读为主。
可以实现读写分离,减缓服务器压力。
最少是一主二从,实际项目中最少一主三从,如果单台Redis服务器使用超过20G,就使用主从复制。
默认情况下每个Redis服务器都是主节点,一个主节点可以有零个或多个从节点,一个从节点必须有主节点。
- 数据冗余
- 故障恢复
- 负载均衡
- 高可用(集群)的基石
(11.1)配置环境
只配置从库环境,因为默认是主库。
加入到Redis服务器,使用info replication
查看当前信息
(11.2)开启窗口,进入到窗口
在redsi.conf的所在地,复制多个redis.conf,要创建多少从机就复制多少个Redis.conf
修改配置文件
- 指定端口
port 6379
,(主机为6379,从机根据实际进行修改) - 开启
daemonize yes
(后台运行) - Pid文件名字
pidfile /var/run/redis_6379.pid
, 依次类推 - Log文件名字
logfile "6379.log"
, 依次类推 - Dump.rdb 名字
dbfilename dump6379.rdb
, 依次类推
上面都配置完毕后,3个服务通过3个不同的配置文件开启,我们的准备环境就OK 了!
从机中仅仅可以读,不能执行写操作
层层链路
上一个Slave 可以是下一个slave 和 Master,Slave 同样可以接收其他 slaves 的连接和同步请求,那么
该 slave 作为了链条中下一个的master,可以有效减轻 master 的写压力!
如果主机断了,从机可以使用命令 SLAVEOF NO ONE
将自己改为主机!这个时
候其余的从机链接到这个节点。对一个从属服务器执行命令 SLAVEOF NO ONE 将使得这个从属服务器
关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。