资讯详情

Redis学习04

Redis主从复制

根据配置和策略更新主机数据, 自动同步到备机 master/slaver 机制,Master 以 写为主,Slave 以读为主

  • 读写分离,性能扩展
  • 容灾迅速恢复

image-20220716131229150

怎么玩:主从复制

拷贝多个 redis.conf 文件 include(写绝对路径) 开启 daemonize yes Pid 文件名字 pidfile 指定端口 port Log 文件名字 dump.rdb 名字 dbfilename Appendonly 关掉或更改名称  [root@jinx100 ~]# mkdir /myredis [root@jinx100 ~]# cd /myredis/ [root@jinx100 myredis]# cp /etc/redis.conf /myredis/redis.conf [root@jinx100 myredis]# vim redis.conf [root@jinx100 myredis]# vim redis6379.conf [root@jinx100 myredis]# cp redis6379.conf redis6380.conf [root@jinx100 myredis]# vim redis6380 [root@jinx100 myredis]# vim redis6380.conf  [root@jinx100 myredis]# cp redis6379.conf redis6381.conf [root@jinx100 myredis]# vim redis6381.conf  [root@jinx100 myredis]# redis-server redis6379.conf   

新建 redis6379.conf,填写以下内容

include /myredis/redis.conf pidfile /var/run/redis_6379.pid port 6379 dbfilename dump6379.rdb 

新建 redis6380.conf,填写以下内容

include /myredis/redis.conf pidfile /var/run/redis_6380.pid port 6380 dbfilename dump6380.rdb 

新建 redis6381.conf,填写以下内容

include /myredis/redis.conf pidfile /var/run/redis_6381.pid port 6381 dbfilename dump6381.rdb 

slave-priority 10 选举主机时设置从机优先级,值越小,优先级越高。 100

启动三台 redis 服务器

查看系统进程,看看三台服务器是否启动

检查三台主机的运行情况

info replication 打印主复制的相关信息

redis-cli -p 6381 

(库)不配主(库)

 slaveof  <ip> <port> 

从服务器中成为一个例子

  1. 在 6380 和 6381 上执行: slaveof 127.0.0.1 6379

  2. 写在主机上,从机上读取数据

写下从机上的数据报错

  1. 主机挂了,重启了,一切都像以前一样

  2. 从机重启需重设:slaveof 127.0.0.1 6379 可将配置添加到文件中。永久生效。

常用 3 招

一主二仆

切入点问题?slave1、slave2 是从零开始复制还是从切入点复制?比如从 k4 进来, 那之前的 k1,k2,k3 也可以复制吗? 机器能写吗?set 可否? 主机 shutdown 后情况如何?机器是上位还是原地待命? 主机回来后,主机增加了记录,从机还能顺利复制吗? 其中一台从机 down 情况如何?它能跟上大部队吗? 

从服务器中悬挂,然后重新启动,然后从服务器中设置,在悬挂过程中可以看到主服务器的内容,重新复制主服务器

即使大哥挂了,他也认出哥,主服务器挂了,从服务器到服务器

薪火相传

上一个Slave 可以是下一个slave的Master, Slave 其他的也可以接受slaves 的连接和同 一步一步地要求,那么该slave作为链条中下-一个master,能有效减少master写压力,去 集中降低风险。

用slaveof <ip><port> 中间变化转向:将清除以前的数据,重建最新的副本 一旦有了风险slave停机,后面的slave 都没法备份 主机挂了,从机还是从机,不能写数据 

反客为主

当一个master停机后,后面的slave可立即升为master,其后面的slave不用做任何 修改。 用slaveof no one将从机变成主机。 

原理

  1. 当数据同步消息从服务器连接到主服务器时
  2. 主服务器收到来自服务器的同步消息, 持久化主服务器数据,rdb文件 把rdb从服务器发送文件 到rdb进行读取
  3. 主服务器每次写作后,与服务器数据同步。(主服务器主动启动)
Slave成功启动连接mater以后会发送一个syne命令 Master收到命令启动后台存盘过程,同时收集所有收到的修改数据集命令,后台过程执行后, master 将整个数据文件传输到slave,完成完全同步 全量复制:slave接收数据库文件数据后,服务将其存盘并加载到内存中。 增量复制: Master 继续依次传递所有新收集的修改命令dave完成同步 但只要是重新连接master,一 次完全同步(自动执行全复制) 

哨兵模式(sentinel)

是什么

以反客为主的自动版本可以在后台监控主机是否故障。如果出现故障,将根据投票数自动从库 转换为主库

使用步骤

6379调整为一主二仆模式 带着 6380、6381  自定义的/myredis 目录下新建 sentinel.conf 文件,名字 绝不能错  配置哨兵,填写内容  sentinel monitor mymaster 127.0.0.1 6379 1 其中 mymaster 服务器名称为监控对象, 1 至少有多少哨兵同意迁移。  启动哨兵  /usr/local/bin redis 可以自带做压测 redis-benchmark 工具 执行 redis-sentinel /myredis/sentinel.conf  当主机挂断时,新机器选举中产生新的主机 (大概 10 哨兵窗口日志可以在几秒钟内看到,切换新主机) 哪个机会被选为主机?优先级:slave-priority 原主机重启后会变成从机。 复制延时 因为所有的写作操作都是先做的 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统非常繁忙时,延迟问题会更严重,Slave 机器数量的增加也会使这个问题更加严重。 复制延时 因为所有的写作操作都是先做的 Master 上操作,同步更新 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统非常繁忙时,延迟问题会更严重,Slave 机器数量的增加也会使这个问题更加严重。 

故障恢复

优先级在 redis.conf 中默认:slave-priority 值越小,优先级越高 偏移是指获取最完整的原始数据 每个 redis 实例启动后,将随机生成 40 位的 runid 

主从复制

private static JedisSentinelPool jedisSentinelPool=null; public static Jedis getJedisFromSentinel(){ if(jedisSentinelPool==null){  Set<String> sentinelSet=new HashSet<>();  sentinelSet.add("192.168.1.103:26379");
 JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
 jedisPoolConfig.setMaxTotal(10); //最大可用连接数
jedisPoolConfig.setMaxIdle(5); //最大闲置连接数
jedisPoolConfig.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

并发写操作, redis 如何分摊?

集群

另外,主从模式,薪火相传模式,主机宕机,导致 ip 地址发生变化,应用程序中配置 需要修改对应的主机地址、端口等信息。

之前通过代理主机来解决,但是 redis3.0 中提供了解决方案。就是无中心化集群配置。

什么是集群

Redis 集群实现了对 Redis 的水平扩容,即启动 N 个 redis 节点,将整个数据库分布存
储在这 N 个节点中,每个节点存储总数据的 1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群
中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

删除持久化数据

将 rdb,aof 文件都删除掉。

制作 6 个实例,6379,6380,6381,6389,6390,6391

配置基本信息

开启 daemonize yes
Pid 文件名字
指定端口
Log 文件名字
Dump.rdb 名字
Appendonly 关掉或者换名字

redis cluster 配置修改

cluster-enabled yes 打开集群模式
cluster-config-file nodes-6379.conf 设定节点配置文件名
cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进
行主从切换。
include /home/bigdata/redis.conf
port 6379
pidfile "/var/run/redis_6379.pid"
dbfilename "dump6379.rdb"
dir "/home/bigdata/redis_cluster"
logfile "/home/bigdata/redis_cluster/redis_err_6379.log"
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000

:%s/6379/6389把6379改成6389

启动 6 个 redis 服务

redis-server redis6379.conf

将六个节点合成一个集群

组合之前,请确保所有 redis 实例启动后,nodes-xxxx.conf 文件都生成正常。

cd /opt/redis-6.2.1/src
合体
redis-cli --cluster create --cluster-replicas 1 192.168.11.101:6379
192.168.11.101:6380 192.168.11.101:6381 192.168.11.101:6389
192.168.11.101:6390 192.168.11.101:6391

此处不要用 127.0.0.1, 请用真实 IP 地址 --replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。

普通方式登录
可能直接进入读主机,存储数据时,会出现 MOVED 重定向操作。所以,应该以集群
方式登录。
redis-cli -p 6379
-c 采用集群策略连接,设置数据会自动切换到相应的
写主机 
redis-cli -c -p 6379

通过 cluster nodes 命令查看集群信息

cluster nodes

redis cluster 如何分配这六个节点?

一个集群至少要有三个主节点。
选项 --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
分配原则尽量保证每个主数据库运行在不同的 IP 地址,每个从库和主库不在一个 IP 地
址上。

什么是 slots

[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384
个插槽的其中一个,
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语
句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点,
其中:
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽。

在集群中录入值

在 redis-cli 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是
该客户端对应服务器的插槽,redis 会报错,并告知应前往的 redis 实例地址和端口。
redis-cli 客户端提供了 –c 参数实现自动重定向。
如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。
不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作。
可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个 slot 中去。
mset kl{cust} v1 k2{cust} v2 k3{cust} v3 

查询集群中的值

CLUSTER GETKEYSINSLOT <slot><count> 返回 count 个 slot 槽中的键。
cluster keyslot k1 插槽值
cluster countkeysinslot 12706 查询插槽12706有几个key
cluster getkeysinslot 449 

故障恢复

如果主节点下线?从节点能否自动升为主节点?注意:15 秒超时

主节点恢复后,主从关系会如何?主节点回来变成从机。

如果所有某一段插槽的主从节点都宕掉,redis 服务是否还能继续?

如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 yes ,那么 ,整个
集群都挂掉
如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 no ,那么,该插槽
数据全都不能使用,也无法存储。
redis.conf 中的参数 cluster-require-full-coverage

集群的 Jedis 开发

即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。 无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据

public class JedisClusterTest {
 public static void main(String[] args) {
 Set<HostAndPort>set =new HashSet<HostAndPort>();
 set.add(new HostAndPort("192.168.31.211",6379));
 JedisCluster jedisCluster=new JedisCluster(set);
 jedisCluster.set("k1", "v1");
 System.out.println(jedisCluster.get("k1"));
 }

Redis 集群提供了以下好处

实现扩容

分摊压力

无中心配置相对简单

Redis 集群的不足

多键操作是不被支持的
多键的 Redis 事务是不被支持的。lua 脚本不被支持
由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分
片的方案想要迁移至 redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

Redis 应用问题解决

缓存穿透

问题描述

key 对应的数据在数据源并不存在,每次针对此 key 的请求从缓存获取不到,请求
都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户 id 获取用户信息,
不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

1. 应用服务器压力变大了
2. redis命中率降低
3. 一直查数据库
redis查询不到数据
出现很多非正常访问

解决方案

一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出
于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每
次请求都要到存储层去查询,失去了缓存的意义。

解决方案:

  1. 对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我 们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最 长不超过五分钟
  2. 设置可访问的名单(白名单): 使用 bitmaps 类型定义一个可以访问的名单,名单 id 作为 bitmaps 的偏移量, 每次访问和 bitmap 里面的 id 进行比较,如果访问 id 不在 bitmaps 里面,进行拦截,不允许访问。
  3. 采用布隆过滤器:(布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。 它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函 数)。 布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。) 将所有可能存在的数据哈希到一个足够大的 bitmaps 中,一个一定不存在的数据会被 这个 bitmaps 拦截掉,从而避免了对底层存储系统的查询压力。
  4. 进行实时监控:当发现 Redis 的命中率开始急速降低,需要排查访问对象和 访问的数据,和运维人员配合,可以设置黑名单限制服务

缓存击穿

问题描述

key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求 发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能 会瞬间把后端 DB 压垮。

数据库访问压力瞬时增加
redis里面没有出现大量key过期
redis正常运行
redis某个key过期了,大量访问使用这个key

解决方案

key 可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时
候,需要考虑一个问题:缓存被“击穿”的问题。
解决问题:
(1)预先设置热门数据:在 redis 高峰访问之前,把一些热门数据提前存入到
redis 里面,加大这些热门数据 key 的时长
(2)实时调整:现场监控哪些数据热门,实时调整 key 的过期时长
(3)使用:
(1) 就是在缓存失效的时候(判断拿出来的值为空),不是立即去 load db。
(2) 先使用缓存工具的某些带成功操作返回值的操作(比如 Redis 的 SETNX)
去 set 一个 mutex key
(3) 当操作返回成功时,再进行 load db 的操作,并回设缓存,最后删除 mutex
key;
(4) 当操作返回失败,证明有线程在 load db,当前线程睡眠一段时间再重试
整个 get 缓存的方法。

缓存雪崩

问题描述

key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求
发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能
会瞬间把后端 DB 压垮。
缓存雪崩与缓存击穿的区别在于这里针对很多 key 缓存,前者则是某一个 key
正常访问

解决方案

缓存失效时的雪崩效应对底层系统的冲击非常可怕!
解决方案:
(1) 构建多级缓存架构:nginx 缓存 + redis 缓存 +其他缓存(ehcache 等)
(2) 使用锁或队列:
用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行
读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并
发情况
(3) 设置过期标志更新缓存:
记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程
在后台去更新实际 key 的缓存。
(4) 将缓存失效时间分散开:
比如我们可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随
机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效
的事件。

分布式锁

问题描述

随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于
分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发
控制锁策略失效,单纯的 Java API 并不能提供分布式锁的能力。为了解决这个问题就
需要一种跨 JVM 的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁主流的实现方案:

  1. 基于数据库实现分布式锁
  2. 基于缓存(Redis 等)
  3. 基于 Zookeeper 每一种分布式锁解决方案都有各自的优缺点:
  4. 性能:redis 最高
  5. 可靠性:zookeeper 最高 这里,我们就基于 redis 实现分布式锁。

解决方案:使用 redis 实现分布式锁

redis:命令
# set sku:1:info “OK” NX PX 10000
EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于
SETEX key second value 。
PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX
millisecond 效果等同于 PSETEX key millisecond value 。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX
key value 。
XX :只在键已经存在时,才对键进行设置操作。

  1. 多个客户端同时获取锁(setnx)
  2. 获取成功,执行业务逻辑{从 db 获取数据,放入缓存},执行完成释放锁(del)
  3. 其他客户端等待重试

编写代码

Redis: set num

@GetMapping("testLock")
public void testLock(){
 //1 获取锁,setne
 Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");
 //2 获取锁成功、查询 num 的值
 if(lock){
 Object value = redisTemplate.opsForValue().get("num");
 //2.1 判断 num 为空 return
 if(StringUtils.isEmpty(value)){
 return;
 }
 //2.2 有值就转成成 int
 int num = Integer.parseInt(value+"");
 //2.3 把 redis 的 num 加 1
 redisTemplate.opsForValue().set("num", ++num);
 //2.4 释放锁,del
 redisTemplate.delete("lock");
 }else{
 //3 获取锁失败、每隔 0.1 秒再获取
 try {
 Thread.sleep(100);
 testLock();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
}
重启,服务集群,通过网关压力测试:
ab -n 1000 -c 100 http://192.168.140.1:8080/test/testLock

查看 redis 中 num 的值

基本实现。

问题:setnx 刚好获取到锁,业务逻辑出现异常,导致锁无法释放 解决:设置过期时间,自动释放锁。

优化之设置锁的过期时间

设置过期时间有两种方式:
1. 首先想到通过 expire 设置过期时间(缺乏原子性:如果在 setnx 和 expire 之
间出现异常,锁也无法释放)
2. 在 set 时指定过期时间(推荐)
set users 10 nx ex 12

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GCHfuwH3-1658114916793)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220717112546007.png)]

设置过期时间:

压力测试肯定也没有问题。自行测试
问题:可能会释放其他服务器的锁。
场景:如果业务逻辑的执行时间是 7s。执行流程如下
1. index1 业务逻辑没执行完,3 秒后锁被自动释放。
2. index2 获取到锁,执行业务逻辑,3 秒后锁被自动释放。
3. index3 获取到锁,执行业务逻辑
4. index1 业务逻辑执行完成,开始调用 del 释放锁,这时释放的是 index3 的锁,
导致 index3 的业务只执行 1s 就被别人释放。
最终等于没锁的情况。
解决:setnx 获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这
个值,判断是否自己的锁

优化之 UUID 防误删

问题:删除操作缺乏原子性。
场景:
1. index1 执行删除时,查询到的 lock 值确实和 uuid 相等
uuid=v1
set(lock,uuid);
2. index1 执行删除前,lock 刚好过期时间已到,被 redis 自动释放
在 redis 中没有了 lock,没有了锁。
3. index2 获取了 lock
index2 线程获取到了 cpu 的资源,开始执行方法
uuid=v2
set(lock,uuid);
4. index1 执行删除,此时会把 index2 的 lock 删除
index1 因为已经在方法中了,所以不需要重新上锁。index1 有执行的权
限。index1 已经比较完成了,这个时候,开始执行
删除的 index2 的锁!

优化之 LUA 脚本保证删除的原子性

@GetMapping("testLockLua")
public void testLockLua() {
 //1 声明一个 uuid ,将做为一个 value 放入我们的 key 所对应的值中
 String uuid = UUID.randomUUID().toString();
 //2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!
 String skuId = "25"; // 访问 skuId 为 25 号的商品 100008348542
 String locKey = "lock:" + skuId; // 锁住的是每个商品的数据
 // 3 获取锁
 Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3,
TimeUnit.SECONDS);
 // 第一种: lock 与过期时间中间不写任何的代码。
 // redisTemplate.expire("lock",10, TimeUnit.SECONDS);//设置过期时间
 // 如果 true
 if (lock) {
 // 执行的业务逻辑开始
 // 获取缓存中的 num 数据
 Object value = redisTemplate.opsForValue().get("num");
 // 如果是空直接返回
 if (StringUtils.isEmpty(value)) {
 return;
 }
 // 不是空 如果说在这出现了异常! 那么 delete 就删除失败! 也就是说锁永
远存在!
 int num = Integer.parseInt(value + "");
 // 使 num 每次+1 放入缓存
 redisTemplate.opsForValue().set("num", String.valueOf(++num));
 /*使用 lua 脚本来锁*/
 // 定义 lua 脚本
 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return
redis.call('del', KEYS[1]) else return 0 end";
 // 使用 redis 执行 lua 执行
 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
 redisScript.setScriptText(script);
 // 设置一下返回值类型 为 Long
 // 因为删除判断的时候,返回的 0,给其封装为数据类型。如果不封装那么默认
返回 String 类型,
 // 那么返回字符串与 0 会有发生错误。
 redisScript.setResultType(Long.class);
 // 第一个要是 script 脚本 ,第二个需要判断的 key,第三个就是 key 所对应的
值。
 redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid);
 } else {
 // 其他线程等待
 try {
 // 睡眠
 Thread.sleep(1000);
 // 睡醒了之后,调用方法。
 testLockLua();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
}

项目中正确使用:

1. 定义 key,key 应该是为每个 sku 定义的,也就是每个 sku有一把锁。
String locKey ="lock:"+skuId; // 锁住的是每个商品的数据
Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey,
uuid,3,TimeUnit.SECONDS);

总结

1、加锁
// 1. 从 redis 中获取锁,set k1 v1 px 20000 nx
String uuid = UUID.randomUUID().toString();
Boolean lock = this.redisTemplate.opsForValue()
 .setIfAbsent("lock", uuid, 2, TimeUnit.SECONDS);
2、使用 lua 释放锁
// 2. 释放锁 del
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return
redis.call('del', KEYS[1]) else return 0 end";
// 设置 lua 脚本返回的数据类型
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
// 设置 lua 脚本返回类型为 Long
redisScript.setResultType(Long.class);
redisScript.setScriptText(script);
redisTemplate.execute(redisScript, Arrays.asList("lock"),uuid);
3、重试
Thread.sleep(500);
testLock();
为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
- 互斥性。在任意时刻,只有一个客户端能持有锁。
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也
能保证后续其他客户端能加锁。
- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人
加的锁给解了。
- 加锁和解锁必须具有原子性。

Redis6.0 新功能

ACL

简介

Redis ACL 是 Access Control List(访问控制列表)的缩写,该功能允许根据可
以执行的命令和可以访问的键来限制某些连接。
在 Redis 5 版本之前,Redis 安全规则只有密码控制 还有通过 rename 来调整
高危命令比如 flushdb , KEYS* , shutdown 等。Redis 6 则提供 ACL 的功能对用户
进行更细粒度的权限控制 :
(1)接入权限:用户名和密码
(2)可以执行的命令
(3)可以操作的 KEY
参考官网:https://redis.io/topics/acl

命令

1、使用 acl list 命令展现用户权限列表

(1)数据说明

2、使用 acl cat 命令

(1)查看添加权限指令类别

(2)加参数类型名可以查看类型下具体命令

3、使用 acl whoami 命令查看当前用户

4、使用 aclsetuser 命令创建和编辑用户 ACL

(1)ACL 规则 下面是有效 ACL 规则的列表。某些规则只是用于激活或删除标志,或对用户 ACL 执行给定更改的单个单词。其他规则是字符前缀,它们与命令或类别名称、键模式等连 接在一起。

(2)通过命令创建新用户默认权限 acl setuser user1

在上面的示例中,我根本没有指定任何规则。如果用户不存在,这将使用 just created 的默认属性来创建用户。如果用户已经存在,则上面的命令将不执行任何操作。

(3)设置有用户名、密码、ACL 权限、并启用的用户 acl setuser user2 on >password ~cached:* +get

(4)切换用户,验证权限

IO 多线程

简介

Redis6 终于支撑多线程了,告别单线程了吗? IO 多线程其实指客户端交互部分的网络 IO 交互处理模块多线程,而非执行命令多线 程。Redis6 执行命令依然是单线程。

原理架构

Redis 6 加入多线程,但跟 Memcached 这种从 IO 处理到数据访问多线程的实现模式 有些差异。Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然 是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务, LPUSH/LPOP 等等的并发问题。整体的设计大体如下:

另外,多线程 IO 默认也是不开启的,需要再配置文件中配置 io-threads-do-reads yes io-threads 4

工具支持 Cluster

之前老版 Redis 想要搭集群需要单独安装 ruby 环境,Redis 5 将 redis-trib.rb 的 功能集成到 redis-cli 。另外官方 redis-benchmark 工具开始支持 cluster 模式了,通过 多线程的方式对多个分片进行压测。

Redis 新功能持续关注

Redis6 新功能还有:
1、RESP3 新的 Redis 通信协议:优化服务端与客户端之间通信
2、Client side caching 客户端缓存:基于 RESP3 协议实现的客户端缓存功能。为
了进一步提升缓存的性能,将客户端经常访问的数据 cache 到客户端。减少 TCP 网络
交互。
3、Proxy 集群代理模式:Proxy 功能,让 Cluster 拥有像单实例一样的接入方式,
降低大家使用 cluster 的门槛。不过需要注意的是代理不改变 Cluster 的功能限制,不
支持的命令还是不会支持,比如跨 slot 的多 Key 操作。
4、Modules API
Redis 6 中模块 API 开发进展非常大,因为 Redis Labs 为了开发复杂的功能,从一
开始就用上 Redis 模块。Redis 可以变成一个框架,利用 Modules 来构建不同系统,而
不需要从头开始写然后还要 BSD 许可。Redis 一开始就是一个向编写各种系统开放的
平台。

标签: cp114差压变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台