当前位置: 首页 > wzjs >正文

怎么做pp网站百度上海分公司

怎么做pp网站,百度上海分公司,死链接对网站的危害有哪些,禁止同ip网站查询举个真实的例子:你的团队刚上线了一个秒杀系统,用Redis锁来防止超卖。测试环境明明跑得好好的,但大促当晚却出现了100件库存卖出了120单的灵异事件。查看日志才发现:就在用户疯狂点击的瞬间,Redis主节点突然挂了&#…

举个真实的例子:你的团队刚上线了一个秒杀系统,用Redis锁来防止超卖。测试环境明明跑得好好的,但大促当晚却出现了100件库存卖出了120单的灵异事件。查看日志才发现:就在用户疯狂点击的瞬间,Redis主节点突然挂了,新的主节点还没拿到锁的信息,结果两个用户同时抢到了"同一把锁"。

这就是很多开发者踩过的坑——你以为用了Redis分布式锁就万事大吉,其实这些情况随时可能让锁失效:

  • 主节点刚给你加完锁就崩溃了,从节点接班时一脸懵:“什么锁?我没听说过啊”
  • 你的程序正处理到一半突然卡住了(比如GC停顿),等回过神来锁早就过期了
  • 网络抽风导致锁信息没传到位,多个客户端都觉得自己拿到了锁

为了解决这些头疼问题,Redis作者提出了**红锁(RedLock)**方案。简单来说就是"不要把鸡蛋放在一个篮子里":让多个独立的Redis节点投票决定锁的归属,只有半数以上同意才算真正拿到锁。

但这套方案也引发过激烈争论,有人甚至说它"数学上就不安全"。本文将用最直白的语言:

  1. 先带你看看传统Redis锁在集群环境为什么容易翻车
  2. 拆解红锁这个"少数服从多数"的解决方案
  3. 手把手教你用Java代码实现红锁
  4. 揭秘Redisson框架如何简化红锁的使用

读完本文你会明白:没有完美的分布式锁,只有适合场景的选择。下次设计系统时,至少能清楚知道手里的锁到底有几成把握。

集群锁的缺陷与挑战

在Redis Cluster环境中,传统的SETNX分布式锁存在以下致命缺陷:主从切换导致锁失效。

问题步骤复现:

  1. 客户端A通过SET key random_val NX PX 30000主节点成功获取锁

  2. 主节点宕机,Redis Cluster触发故障转移,从节点升级为新主节点

  3. 由于Redis主从复制是异步的,锁可能未同步到新主节点

  4. 客户端B向新主节点申请相同资源的锁,成功获取导致数据竞争

# 主节点写入锁
SET resource_1 8a3e72 NX PX 10000  
OK# 主节点宕机,从节点晋升但未同步锁数据
# 新主节点处理客户端B的请求
SET resource_1 5b9fd2 NX PX 10000  
OK  # 锁被重复获取!

红锁(RedLock)的设计与实现

N个独立Redis节点(非Cluster模式)中,当客户端在半数以上节点成功获取锁,且总耗时小于锁有效期时,才认为锁获取成功。

实现步骤详解

假设部署5个Redis节点(N=5):

  1. 获取当前时间:记录开始时间T1(毫秒精度)

  2. 依次向所有节点申请锁

SET lock_key valueNX PX $ttl
  • value:全局唯一值(如UUID)
  • ttl:锁自动释放时间(如10秒)
  1. 计算锁有效性
  • 客户端计算获取锁总耗时T_elapsed = T2 - T1(T2为最后响应时间)

  • 仅当以下两个条件满足时,锁才有效:

    1. 成功获取锁的节点数 ≥ 3(N/2 + 1)
    2. T_elapsed < ttl(确保锁未过期)
  1. 加锁成功,去操作共享资源

  2. 释放锁:向所有节点发送Lua脚本删除锁(需验证值)

if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

NPC争议问题

红锁算法自诞生起就伴随着**N(网络延迟)、P(进程暂停)、C(时钟漂移)**三个核心争议,这些现实世界中的不确定因素,动摇了红锁在数学意义上的绝对安全性。

网络延迟(Network Delay)的致命时间差

问题场景

  1. 客户端在节点A、B、C成功获取锁,总耗时48ms(小于TTL 50ms)

  2. 但由于跨机房网络波动,实际锁在节点上的有效时间存在差异:

    • 节点A记录的锁过期时间:客户端本地时间+50ms = T+50
    • 节点B因网络延迟,实际锁过期时间为T+52
    • 节点C因网络拥塞,实际锁过期时间仅T+48
  3. 在时间窗口T+48T+50之间,客户端认为锁仍有效,但节点C的锁已提前失效

后果
其他客户端可能在此期间获取节点C的锁,导致锁状态分裂,多个客户端同时进入临界区。

进程暂停(Process Pause)的「薛定谔锁」

经典案例

// 伪代码:获取锁后执行业务逻辑
if (redLock.tryLock()) {// 触发Full GC暂停300msSystem.gc(); // 此时锁已过期,但客户端仍在写数据updateInventory(); 
}

关键时间线

  • T0: 获取锁(TTL=200ms)
  • T0+100ms: 进入GC暂停,持续300ms
  • T0+400ms: GC结束,继续执行业务逻辑
  • 锁实际在T0+200ms已失效,但客户端在T0+400ms仍以为自己持有锁

数据灾难
其他客户端在T0+200ms到T0+400ms期间可能修改数据,导致最终结果错乱。

时钟漂移(Clock Drift)的时空扭曲

物理机时钟偏移实验数据

节点时钟误差范围常见诱因
节点A±200ms/分钟虚拟机时钟不同步
节点B±500ms/天NTP服务异常
节点C±10秒/小时宿主机硬件时钟故障

连锁反应

  1. 客户端计算锁有效期基于本地时钟(假设为T+100ms)
  2. 但节点B的时钟比实际快30秒,导致其记录的锁过期时间为T-29000ms
  3. 锁在客户端认为的有效期内提前被节点B自动释放

行业领袖的正面交锋

Martin Kleppmann(《数据密集型应用设计》作者)

“红锁依赖的假设——『客户端能准确感知锁存活时间』,在异步分布式系统中根本无法保证。即使没有节点故障,NPC问题也会导致锁状态的不确定性。”

Antirez(Redis作者)的反驳

"工程实践中可以通过以下手段控制风险:

  1. 使用带温度补偿的原子钟硬件
  2. 禁用NTP服务的时钟跳变调整
  3. 监控进程暂停(如GC日志分析)
  4. 为锁TTL设置冗余缓冲时间(如额外20%)"

红锁的Java实现示例

使用Jedis客户端实现红锁:

package com.morris.redis.demo.redlock;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;/*** 使用jedis手写RedLock*/
public class JedisRedLock {public static final int EXPIRE_TIME = 30_000;private final List<JedisPool> jedisPoolList;private final String lockKey;private final String lockValue;public JedisRedLock(List<JedisPool> jedisPoolList, String lockKey) {this.jedisPoolList = jedisPoolList;this.lockKey = lockKey;this.lockValue = UUID.randomUUID().toString();}public void lock() {while (!tryLock()) {try {TimeUnit.MILLISECONDS.sleep(100); // 失败后短暂等待} catch (InterruptedException e) {throw new RuntimeException(e);}}}public boolean tryLock() {long startTime = System.currentTimeMillis();int successCount = 0;try {for (JedisPool jedisPool : jedisPoolList) {try (Jedis jedis = jedisPool.getResource();) {// 原子化加锁:SET lockKey UUID NX PX expireTimeString result = jedis.set(lockKey, lockValue,SetParams.setParams().nx().px(EXPIRE_TIME));if ("OK".equals(result)) {successCount++;}}}// 计算获取锁耗时long elapsedTime = System.currentTimeMillis() - startTime;// 验证:多数节点成功 且 耗时小于TTLreturn successCount >= (jedisPoolList.size() / 2 + 1) && elapsedTime < EXPIRE_TIME;} finally {// 若加锁失败,立即释放已获得的锁if (successCount < (jedisPoolList.size() / 2 + 1)) {unlock();}}}public void unlock() {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";for (JedisPool jedisPool : jedisPoolList) {try (Jedis jedis = jedisPool.getResource()) {jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(lockValue));}}}}

手写RedLock的使用:

package com.morris.redis.demo.redlock;import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*** 手写RedLock的使用*/
public class JedisRedLockDemo {private volatile static int count;public static void main(String[] args) throws InterruptedException {List<JedisPool> jedisPoolList = new ArrayList<>();jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379));jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6380));jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6381));jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6382));jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6383));int threadCount = 3;CountDownLatch countDownLatch = new CountDownLatch(threadCount);ExecutorService executorService = Executors.newFixedThreadPool(threadCount);for (int i = 0; i < threadCount; i++) {executorService.submit(() -> {JedisRedLock jedisRedLock = new JedisRedLock(jedisPoolList, "lock-key");jedisRedLock.lock();try {System.out.println(Thread.currentThread().getName() + "获得锁,开始执行业务逻辑。。。");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() + "获得锁,结束执行业务逻辑。。。");count++;} finally {jedisRedLock.unlock();}countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();System.out.println(count);}}

Redisson中红锁的使用

Redisson已封装红锁实现,自动处理节点通信与锁续期:

package com.morris.redis.demo.redlock;import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*** Redisson中红锁的使用*/
public class RedissonRedLockDemo {private volatile static int count;public static void main(String[] args) throws InterruptedException {List<String> serverList = Arrays.asList("redis://127.0.0.1:6379", "redis://127.0.0.1:6380", "redis://127.0.0.1:6381","redis://127.0.0.1:6382", "redis://127.0.0.1:6383");List<RedissonClient> redissonClientList = new ArrayList<>(serverList.size());for (String server : serverList) {Config config = new Config();config.useSingleServer().setAddress(server);redissonClientList.add(Redisson.create(config));}List<RLock> lockList = new ArrayList<>(redissonClientList.size());for (RedissonClient redissonClient : redissonClientList) {lockList.add(redissonClient.getLock("java-lock"));}int threadCount = 3;CountDownLatch countDownLatch = new CountDownLatch(threadCount);ExecutorService executorService = Executors.newFixedThreadPool(threadCount);for (int i = 0; i < threadCount; i++) {executorService.submit(() -> {RedissonRedLock redissonRedLock = new RedissonRedLock(lockList.toArray(new RLock[0]));redissonRedLock.lock();try {System.out.println(Thread.currentThread().getName() + "获得锁,开始执行业务逻辑。。。");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() + "获得锁,结束执行业务逻辑。。。");count++;} finally {redissonRedLock.unlock();}countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();System.out.println(count);for (RedissonClient redissonClient : redissonClientList) {redissonClient.shutdown();}}}

Redisson优势
• 自动续期:通过WatchDog机制延长锁有效期
• 简化API:封装底层细节,支持异步/响应式编程
• 故障容错:自动跳过宕机节点,保证半数以上成功即可

总结

红锁通过多节点投票机制,显著提升了分布式锁的可靠性,但需权衡其实现复杂度与运维成本。建议在以下场景选择红锁:
• 需要跨机房/地域部署
• 业务对数据一致性要求极高
• 已具备独立Redis节点运维能力

对于大多数场景,可优先使用Redisson等成熟框架,避免重复造轮子。若对一致性有极致要求,可考虑ZooKeeper/etcd等基于共识算法的方案。

http://www.dtcms.com/wzjs/416047.html

相关文章:

  • 做网站版头图片网站的优化
  • 龙华建网站多少钱怎么注册自己公司的网址
  • 国外哪些网站是python做的百度网页版进入
  • 上海网站建设平台济南公司网站推广优化最大的
  • 棕色网站模板深圳网站优化公司
  • 做虾皮网站赚钱吗石家庄网站建设seo
  • vs2017 如何做网站新营销模式有哪些
  • 如何改变wordpress文本的字体颜色搜索引擎排名优化价格
  • 本地网站有什么可以做营销型网站建设总结
  • 政府网站建设与维护网络营销图片
  • 惠州做棋牌网站建设哪家技术好网络广告文案
  • 马云做直销网站吗中国seo谁最厉害
  • solusvm做网站seo友情链接
  • 网站名称可以更换吗网络营销具有哪些特点
  • 中国建设银行招聘信息网站6手机优化大师怎么退款
  • 复制别人网站做第一站广州最新政策
  • fm网站开发竞价托管运营哪家好
  • 翼城网站建设韩国最新新闻
  • 如何写一份食品的网站建设规划淘宝关键词优化工具
  • 西安建设工程信息网是谁开发的seo是什么专业的课程
  • 网站营销工具电商网站建设公司哪家好
  • 做网站 成都中国舆情观察网
  • 模板网站可以做优化热门关键词排名查询
  • 网络文化经营许可证变更北京seo优化排名
  • 网站建设情况介绍一键优化清理手机
  • 网站名称和备案公司名称不一样网站秒收录工具
  • 什么类型的网站流量高2023年5月疫情爆发
  • 网站做的题不小心关闭了自己建网站需要钱吗
  • 微网站建设及微信推广方案ppt西安网站设计公司
  • 微信购物网站开发搜索引擎营销怎么做