Redis集群及分布式锁

本文详细介绍了如何搭建Redis无中心化集群,包括配置文件修改、节点组合与故障转移。还讨论了Redis缓存问题如穿透、击穿和雪崩,以及分布式锁的实现与潜在问题。在Java代码操作Redis方面,提到了Jedis的使用,并提出了防止误删锁的解决方案。

1、无中心化集群

在这里插入图片描述

2、redis集群搭建

1、进入/root/myredis文件目录

cd /root/myredis

在这里插入图片描述

2、进入redis6378.conf,并添加一下内容

cluster-enabled yes 打开集群模式
cluster-config-file nodes-6379.conf 设定节点配置文件名
cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换

在这里插入图片描述

3、复制配置文件
在这里插入图片描述

4、修改配置文件

批量修改命令

:%s/被替换的内容/替换后的内容

在这里插入图片描述

5、启动上面配置的六台redis服务
在这里插入图片描述

6、将六个节点合成一个集群

组合之前,请确保所有redis实例启动后,node-xxxx.conf文件都生成正常。
在这里插入图片描述

7、Redis报错:-bash redis-cli command not found

sudo cp src/redis-cli /usr/local/bin/

sudo cp src/redis-cli /usr/local/bin/

8、执行下面的命令

redis-cli --cluster create --cluster-replicas 1 192.168.18.135:6379 192.168.18.135:6380 192.168.18.135:6381 192.168.18.135:6389 192.168.18.135:6390 192.168.18.135:6391

在这里插入图片描述

9、连接redis集群

redis-cli -c -p 6379

10、通过cluster nodes 命令查看集群信息
在这里插入图片描述

11、分配原则

分配原则尽量保证每个主数据库运行在不同的IP地址,每个主库和从库不在一个IP地址里面。

127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.18.135:6381
OK

底层有一个计算插槽的过程,k1 计算后的插槽值为12706,只要落在6381 【10923-16383】节点所在的区间。所有k1存放到6381这个主节点上。set值后自动切换到6381节点。【这里就体现了去中心化的过程

在这里插入图片描述

12、一次添加多个值

分组添加

mset name{user} lucy age{user} 20

13、查看k插槽的值

cluster keyslot k名称

14、杀掉6379服务,再查看节点情况,发现6390(6379的从机)成为主机。

在这里插入图片描述

15、重启6379,再查看节点情况,发现6379从原来的主机变为现在的6390的从机。

在这里插入图片描述

如果某一段插槽的主从节点都宕机了,redis集群服务是否还能继续?

  • 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage为yes,那么整个集群都挂掉

  • 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage为no,那么,该插槽数据全部不能使用,也无法存储。其他插槽依然可以提供服务。

3、Jedis - 通过java代码操作redis

4、应用问题解决

4.1 缓存穿透

在这里插入图片描述

​ (4)进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

4.2 缓存击穿

现象:

在这里插入图片描述

4.3 缓存雪崩

在这里插入图片描述

5、分布式锁

(1)redis中使用命令来设置分布式锁

setnx k v

在这里插入图片描述

第一次使用setnx设置值时是成功的,但是第二次设置就失败了,说明第一次就加锁了。

(2)如何释放锁

使用命令

del k

在这里插入图片描述

使用删除命令后,再设置值成功。

(3)以上的设置锁的方式存在的问题?

当锁一直不释放,那么后面的所有操作将搁置,怎么解决这个问题呢?很简单,就是给该锁加一个过期时间

使用命令【这种方式不是原子操作

setnx users 10
expire users 10

在这里插入图片描述

总结:

(1)使用setnx上锁,通过del释放锁

(2)锁一直没有释放,设置key过期时间,自动释放

(3)上锁之后突然出现异常,无法设置过期时间了

  • 上锁时候同时设置过期时间就可以解决这个问题

    给users设置值为10 ,并设置过期时间为12秒

    127.0.0.1:6379> set users 10 nx ex 12
    OK
    127.0.0.1:6379> ttl users
    (integer) 5
    

6、分布式锁存在的问题

1、问题描述

在这里插入图片描述

解决方案:

在这里插入图片描述

在设置锁的时候,将锁的值设置为一个随机的uuid值,再在需要释放锁的时候,获取该锁的值并进行比较,如果相等,则说明是同一把锁,再执行释放锁的操作,这样就有效的避免了误删其他的锁。
在这里插入图片描述

### 使用 Redis 集群实现分布式锁 #### 1. 分布式锁的基本原理 在分布式环境中,多个节点可能尝试同时访问相同的资源。为了确保这些操作不会相互干扰,需要一种机制来协调这种并发访问。Redis 提供了一种高效的方法来创建这样的锁定机制。 当使用 Redis 实现分布式锁时,主要依赖于 `SETNX` (Set if Not Exists) 命令配合 `EXPIRE` 来设定过期时间以避免死锁的发生[^3]。这种方式能够有效地保证只有一个客户端可以在指定的时间窗口内获得锁。 #### 2. Redis 集群中的挑战 然而,在 Redis 单实例上构建简单的分布式锁相对容易;但在集群环境下,则面临更多复杂性: - **分区容忍度**:即使部分服务器不可达,整个系统仍需正常工作。 - **一致性保障**:所有副本之间保持一致的状态对于正确处理请求至关重要。 - **网络分割情况下的行为定义**:在网络出现问题的情况下决定哪个分片应该继续服务而哪些应停止接受新请求。 #### 3. 最佳实践 针对上述挑战,以下是几种推荐的做法: - **采用 Redlock 算法**:由 Antirez 提出的一种改进方案,旨在解决单点故障问题并通过多数派投票机制提高可靠性。该算法要求至少五个独立的 Redis 节点参与决策过程,并且只有当超过半数以上的节点同意授予锁时才认为成功获取到了全局唯一的锁[^1]。 - **合理配置 TTL(Time To Live)参数**:为每个锁设置合理的生存周期非常重要。太短可能导致频繁重新申请锁从而影响性能;反之则增加了发生冲突的风险。一般建议根据具体应用场景调整此值至最优水平。 - **定期续租策略**:为了避免因意外断电等原因造成持有者无法及时释放而导致其他等待方长期阻塞的情况出现,可以通过定时向 Redis 发送续约指令延长已存在记录的有效期限直至业务逻辑完成为止。 #### 4. 示例代码 下面给出一段 Python 版本的红锁(RedLock)实现片段: ```python import time from redis import StrictRedis, ConnectionError class RedLock(object): def __init__(self, nodes): self.nodes = [StrictRedis(host=node['host'], port=node['port']) for node in nodes] def acquire(self, resource, lock_id, ttl_ms=10000): majority = int(len(self.nodes)/2)+1 locked_count = 0 start_time = int(time.time() * 1000) for r in self.nodes: try: result = r.set(resource, lock_id, nx=True, px=ttl_ms) if result is not None and bool(result): locked_count += 1 except ConnectionError as e: continue elapsed_time = int(time.time()*1000)-start_time validity = ttl_ms - elapsed_time if locked_count >= majority and validity > 0 : return True, validity else: # Release the locks acquired on some nodes. self.release(resource, lock_id) return False, 0 def release(self, resource, lock_id): lua_script = """ if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end""" for r in self.nodes: try: r.eval(lua_script, 1, resource, lock_id) except Exception as ex: pass if __name__ == '__main__': nodes = [ {'host': 'localhost', 'port': 7000}, {'host': 'localhost', 'port': 7001}, {'host': 'localhost', 'port': 7002} ] red_locker = RedLock(nodes) success, valid_for = red_locker.acquire('my_resource_key','unique_client_identifier') print(f'Acquired Lock? {success}, Valid For(ms):{valid_for}') ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值