Redis6(下)
- Redis 与 Spring Boot 整合
- Redis_事务_锁机制_秒杀
-
- Redis 的事务定义
- Multi、Exec、discard
- 事务的错误处理
- 事务冲突的问题
-
- 悲观锁
- 乐观锁
- WATCH key [key ...]
- unwatch
- Redis_事务_秒杀案例
- Redis 持久化之 RDB
-
- 简介
- 备份是如何执行的
- Fork
- RDB 持久化流程
- dump.rdb 文件
- 如何触发 RDB 快照;保持策略
- rdb 的备份
- 优势
- 劣势
- Redis 持久化之 AOF
-
- 简介AOF(Append Only File)
- AOF 持久化流程
- AOF 同步频率设置
- Redis_主从复制
-
- 配置主从复制
- 主从复制原理
- 薪火相传
- 反客为主
- 哨兵模式(sentinel)
- Redis 集群
-
- 集群搭建
- 什么是 slots
- 在集群中录入值
- 查询集群中的值
- 故障恢复
- 集群的 Jedis 开发
- Redis 集群提供了以下好处
- Redis 集群的不足
- end
Redis 与 Spring Boot 整合
整合步骤
1、创建springboot工程
2、引入redis依赖
<!-- redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <!-- spring2.X 集成 redis 所需 common-pool2-->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.0</version>
</dependency>
3、配置参数
# Redis 服务器地址
spring.redis.host=192.168.140.136
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 数据库索引(默认为 0)
spring.redis.database= 0
# 连接超时时间(毫秒)
spring.redis.timeout=1800000
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
# 最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
4、配置操作类
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}
}
Redis_事务_锁机制_秒杀
Redis 的事务定义
Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事 务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis 事务的主要作用就是串联多个命令防止别的命令插队。
Multi、Exec、discard
从输入 Multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输 入 Exec 后,Redis 会将之前的命令队列中的命令依次执行。
组队的过程中可以通过 discard 来放弃组队。
事务的错误处理
组队中
某个命令出现了报告错误,执行时整个的所有队列都会被取消。
如果执行阶段
某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都 会执行,不会回滚。
事务冲突的问题
例子,三个人同时操作账户10000元,造成了账户金额的错误扣减
悲观锁
悲观锁(Pessimistic Lock),
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会 修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它 拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读 锁,写锁
等,都是在做操作之前先上锁。MySql悲观锁
乐观锁
乐观锁(Optimistic Lock)
, 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不 会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这 个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐 量
。Redis 就是利用这种 check-and-set 机制实现事务的。
WATCH key [key …]
相当于给要事务操作的key加上乐观锁
在执行 multi 之前,先执行 watch key1 [key2],可以监视一个(或多个) key ,如果在事务 执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
unwatch
取消 WATCH 命令对所有 key 的监视。
如果在执行 WATCH 命令之后,EXEC 命令或 DISCARD 命令先被执行了的话,那么就 不需要再执行 UNWATCH 了。
Redis_事务_秒杀案例
Redis 持久化之 RDB
简介
在指定的时间间隔
内将内存中的数据集快照写入磁盘, 也就是行话讲的 Snapshot 快 照,它恢复时是将快照文件直接读到内存里
备份是如何执行的
Redis 会单独创建(fork)一个子进程来进行持久化,会先
将数据写
入到 一个临时文件
中,待持久化过程都结束了,再用这个临时文件替换上次持久化
好的文件。 整个过程 中,主进程是不进行任何 IO 操作的,这就确保了极高的性能 如果需要进行大规模数 据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加 的高效。RDB的缺点是最后一次持久化后的数据可能丢失
。
Fork
- Fork 的作用是复制一个与当前进程
一样的进程
。新进程的所有数据(变量、环境变量、 程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在 Linux 程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多 会 exec 系统调用,出于效率考虑,Linux 中引入了“
写时复制技术
” 复制临时文件相同空间的内容,在这个空间的基础追加数据,叫写时复制 - 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要 发生变化时,才会将父进程的内容复制一份给子进程。
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要 发生变化时,才会将父进程的内容复制一份给子进程。
RDB 持久化流程
dump.rdb 文件
持久化文件
在 redis.conf 中配置文件名称,默认为 dump.rdb
配置位置
rdb 文件的保存路径,也可以修改。默认为 Redis 启动时命令行所在的目录下
如何触发 RDB 快照;保持策略
配置文件中默认的快照配置,意味 3600秒内一个key发生变化,触发,或者30秒内10个key变化,或者60秒内10000个key变化
-
参数
stop-writes-on-bgsave-error
当 Redis 无法写入磁盘的话,直接关掉 Redis 的写操作。推荐 yes. -
参数rdbcompression 压缩文件
对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis 会采用 LZF 算法进行压缩。
如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能。推荐 yes. -
参数rdbchecksum 检查完整性
在存储快照后,还可以让 redis 使用 CRC64 算法来进行数据校验, 但是这样做会增加大约 10%的性能消耗,如果希望获取到最大的性能提升,可以关 闭此功能 推荐 yes.
rdb 的备份
先通过 config get dir 查询 rdb 文件的目录
将*.rdb 的文件拷贝到别的地方
rdb 的恢复
- 关闭 Redis
- 先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb
- 启动 Redis, 备份数据会直接加载
优势
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
节省磁盘空间
恢复速度快
劣势
- Fork 的时候,内存中的数据被克隆了一份,大致 2 倍的膨胀性需要考虑
- 虽然 Redis 在 fork 时使用了写时拷贝技术,但是如果数据庞大时还是比较消 耗性能。
- 在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外 down 掉的 话,就会丢失最后一次快照后的所有修改。
动态停止 RDB:redis-cli config set save “”#save 后给空值,表示禁用保存策略
Redis 持久化之 AOF
简介AOF(Append Only File)
以日志的形式来记录每个写操作(增量保存)
,将 Redis 执行过的所有写指令记录下 来(读操作不记录
), 只许追加文件但不可以改写文件
,redis 启动之初会读取该文件重 新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一 次以完成数据的恢复工作
AOF 持久化流程
(1)客户端的请求写命令会被 append 追加到 AOF 缓冲区内;
(2)AOF 缓冲区根据 AOF 持久化策略[always,everysec,no]将操作 sync 同步到磁盘 的 AOF 文件中;
(3)AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 rewrite 重写,压缩 AOF 文件容量;
(4)Redis 服务重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的;
- AOF 默认不开启
可以在 redis.conf 中配置文件名称,默认为appendonly.aof
AOF 文件的保存路径,同 RDB 的路径一致。 - AOF 和 RDB 同时开启,redis 听谁的?
AOF 和 RDB 同时开启,系统默认取 AOF 的数据(数据不会存在丢失)
AOF 启动/修复/恢复
- AOF 的备份机制和性能虽然和 RDB 不同, 但是备份和恢复的操作同 RDB 一样, 都是拷贝备份文件,需要恢复时再拷贝到 Redis 工作目录下,启动系统即加载。
- 正常恢复
修改默认的 appendonly no,改为 yes
将有数据的 aof 文件复制一份保存到对应目录(查看目录:config get dir)
恢复:重启 redis 然后重新加载 - 异常恢复
修改默认的 appendonly no,改为 yes
如遇到AOF文件损坏
,通过/usr/local/bin/redis-check-aof--fix
appendonly.aof进行恢复
备份被写坏的 AOF 文件
恢复:重启 redis,然后重新加载
AOF 同步频率设置
- appendfsync always
始终同步,每次 Redis 的写入都会立刻记入日志;性能较差但数据完整性比较好 - appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。 - appendfsync no
redis 不主动进行同步,把同步时机交给操作系统。
Redis_主从复制
主机数据更新后根据配置和策略, 自动同步到备机的 master/slaver 机制
,Master以 写为主,Slave 以读为主
能干嘛
- 读写分离,性能扩展
- 容灾快速恢复
配置主从复制
依次启动三台redis
查看系统进程,看看三台服务器是否启动
info replication
打印主从复制的相关信息
在 6380 和 6381 上执行: slaveof 127.0.0.1 6379
配置完成 ,在主机上写,在从机上可以读取数据,在从机上写数据报错。
- 主机挂掉,重启就行,一切如初。
- 从机重启需重设:slaveof 127.0.0.1 6379
主从复制原理
- 当从连接主后,向主发送同步数据请求
- 主接收到从到同步请求,把数据持久化为rdb,把rdb发送给从,从读取接收到的rdb
- 每次主服务器写操作,向从进行数据同步
全量复制:从 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master 继续将新的所有收集到的修改命令依次传给 slave,完成同步。
薪火相传
上一个 Slave 可以是下一个 slave 的 Master,Slave 同样可以接收其他 slaves 的连接和 同步请求,那么该 slave 作为了链条中下一个的 master, 可以有效减轻 master 的写压 力,去中心化降低风险。
用 slaveof
主从之间就形成了树状结构
反客为主
当一个 master 宕机后,后面的 slave 可以立刻升为 master,其后面的 slave 不用做任 何修改。
用 slaveof no one 将从机变为主机。
哨兵模式(sentinel)
反客为主的自动版
,能够后台监控主机是否故障,如果故障了根据投票数自动将从库 转换为主库
使用步骤
- 自定义的/myredis 目录下新建 sentinel.conf 文件,名 字绝不能错
配置哨兵,填写内容
sentinel monitor mymaster 127.0.0.1 6379 1
其中 mymaster 为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。 - 启动哨兵
执行 redis-sentinel /myredis/sentinel.conf
当主机挂掉,从机选举中产生新的主机
哪个从机会被选举为主机呢?根据优先级别:replica-priority
优先级在 redis.conf 中默认:replica-priority 100,值越小优先级越高
在java中配置
private static JedisSentinelPool jedisSentinelPool = null;public static Jedis getJedisFromSentinel() {
if (jedisSentinelPool == null) {
Set<String> sentinelSet = new HashSet<>();sentinelSet.add("192.168.11.103:26379");JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(10); //最大可用连接数jedisPoolConfig.setMaxIdle(5); //最大闲置连接数edisPoolConfig.setMinIdle(5); //最小闲置连接数jedisPoolConfig.setBlockWhenExhausted(true); //连接耗尽是否等待jedisPoolConfig.setMaxWaitMillis(2000); //等待时间jedisPoolConfig.setTestOnBorrow(true); //取连接的时候进行一下测试 ping pong jedisSentinelPool = new JedisSentinelPool("mymaster", sentinelSet, jedisPoolConfig);return jedisSentinelPool.getResource();} else {
return jedisSentinelPool.getResource();}
}
Redis 集群
容量不够,redis 如何进行扩容?
并发写操作, redis 如何分摊?
另外,主从模式,薪火相传模式,主机宕机,导致 ip 地址发生变化,应用程序中配置 需要修改对应的主机地址、端口等信息。
之前通过代理主机来解决,但是 redis3.0
中提供了解决方案。就是无中心化集群
配置。
什么是集群
Redis 集群实现了对 Redis 的水平扩容,即启动 N 个 redis 节点,将整个数据库分布存 储在这 N 个节点中,每个节点存储总数据的 1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群 中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
集群搭建
在一台主机中,通过不同端口开启6个redis搭建集群
-
删除rdb文件
[root@localhost myredis]# rm -rf dum*
-
制作 6 个实例,端口分别为 6379,6380,6381,6389,6390,6391
-
给6个实例创建配置文件,注意对应端口号,关闭appendonly
include /myredis/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
- 通过6个配置文件启动6个redis
- 将六个节点合成一个集群
组合之前,请确保所有 redis 实例启动后,nodes-xxxx.conf 文件都生成正常。
- 合体:进入redis的安装目录中的src目录下执行合体命令,使用真实IP地址
cd /opt/redis-6.2.1/src
redis-cli --cluster create --cluster-replicas 1 192.168.22.10:6379 192.168.22.10:6380 192.168.22.10:6381 192.168.22.10:6389 192.168.22.10:6390 192.168.22.10:6391
–replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。
- 输入yes后,开启成功
- -c 采用集群策略连接,设置数据会自动切换到相应的 写主机
redis-cli -p 6379
通过 cluster nodes 命令查看集群信息
什么是 slots
一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,集群使用公式 CRC16(key) %
16384 来计算键 key 属于哪个槽
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽。
…
集群创建完毕是
在集群中录入值
在 redis-cli 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是 该客户端对应服务器的插槽,redis 会报错,并告知应前往的 redis 实例地址和端口。
redis-cli 客户端提供了–c 参数实现自动重定向
。
如 redis-cli -c –p 6379
登入后,再录入、查询键值对可以自动重定向。
如图,在6379端口的redis设置k1,计算k1的插槽值为12706,该值属于6381端口的redis,而后自动切换至了6381端口redis
不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作。
可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个 slot 中去。
查询集群中的值
CLUSTER GETKEYSINSLOT <slot><count>
返回 count 个 slot 槽中的键。
首先获取键cust的插槽值,然后根据插槽值获取value
故障恢复
如果主节点下线?从节点能否自动升为主节点?注意:15秒超时
主节点恢复后,主从关系会如何?主节点回来变成从机。
如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 yes ,那么 ,整个 集群都挂掉
如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 no ,那么,该插 槽数据全都不能使用,也无法存储。
redis.conf 中的参数 cluster-require-full-coverage
集群的 Jedis 开发
即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据。
HostAndPort hostAndPort = new HostAndPort("192.168.22.10", 6379);
JedisCluster jedisCluster = new JedisCluster(hostAndPort);
jedisCluster.set("k1", "v1");
System.out.println(jedisCluster.get("k1"));
Redis 集群提供了以下好处
实现扩容
分摊压力
无中心配置相对简单
Redis 集群的不足
多键操作是不被支持的
多键的 Redis 事务是不被支持的。lua 脚本不被支持
由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分 片的方案想要迁移至 redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。