分布式锁(redis/mysql)
单台机器所能承载的量是有限的,用户的量级上万,基本上服务都会做分布式集群部署。很多时候,会遇到对同一资源的方法。这时候就需要锁,如果是单机版的,可以利用java等语言自带的并发同步处理。如果是多台机器部署就得要有个中间代理人来做分布式锁了。
常用的分布式锁的实现有三种方式。
基于redis实现(利用redis的原子性操作setnx来实现)
基于mysql实现(利用mysql的innodb的行锁来实现,有两种方式, 悲观锁与乐观锁)
基于Zookeeper实现(利用zk的临时顺序节点来实现)
目前,我已经是用了redis和mysql实现了锁,并且根据应用场景应用在不同的线上环境中。zk实现比较复杂,又无应用场景,有兴趣的可以参考他山之石中的《Zookeeper实现分布式锁》。
说说心得和体会。
基于redis缓存实现分布式锁
基于redis的锁实现比较简单,由于redis的执行是单线程执行,天然的具备原子性操作,我们可以利用命令setnx和expire来实现,java版代码参考如下:
包名和获取redis操作对象换成自己的就好了。
基本步骤是
每次进来先检测一下这个key是否实现。如果失效了移除失效锁
使用setnx原子命令争抢锁。
抢到锁的设置过期时间。
步骤2为最核心的东西,为啥设置步骤3?可能应为获取到锁的线程出现什么移除请求,而无法释放锁,因此设置一个最长锁时间,避免死锁。为啥设置步骤1?redis可能在设置expire的时候挂掉。设置过期时间不成功,而出现锁永久生效。
线上环境,步骤1、3的问题都出现过。所以要做保底拦截。
redis集群部署
通常redis都是以master-slave解决单点问题,多个master-slave组成大集群,然后通过一致性哈希算法将不同的key路由到不同master-slave节点上。
redis锁的优缺点:
优点:redis本身是内存操作、并且通常是多片部署,因此有这较高的并发控制,可以抗住大量的请求。缺点:redis本身是缓存,有一定概率出现数据不一致请求。
在线上,之前,利用redis做库存计数器,奖品发放理论上只发放10个的,最后发放了14个。出现了数据的一致性问题。
因此在这之后,引入了mysql数据库分布式锁。
基于mysql实现的分布式锁。
实现第一版
在此之前,在网上搜索了大量的文章,基本上都是 插入、删除发的方式或是直接通过"select for update"这种形式获取锁、计数器。具体可以参考他山之石中的《分布式锁的几种实现方式~》关于数据库锁章节。
一开始,我的实现方式伪代码如下:
这样实现出现了很严重的死锁问题,具体原因可以可以参考他山之石中的《select for update引发死锁分析》这个版本中存在如下几个比较严重的问题:
1.通常线上数据是不允许做物理删除的2.通过唯一键重复报错,处理错误形式是不太合理的。3.如果appclient在处理中还没释放锁之前就挂掉了,会出现锁一直存在,出现死锁。4.如果以这种方式,实现redis中的计数器(incr decr),当记录不存在的时候,会出现大量死锁的情况。
因此考虑引入,记录状态字段、中央锁概念。
实现第二版
在第二版中完善了数据库表设计,参考如下:
在这个版本中,考虑到再条锁并发插入存在死锁(间隙锁争抢)情况,引入中央锁概念。
基本方式是:
根据sql创建好数据库
创建一条记录Flock_name="center_lock"的记录。
在对其他锁(如Flock_name="sale_invite_lock")进行操作的时候,先对"center_lock"记录select for update
"sale_invite_lock"记录自己的增删改查。
考虑到不同公司引入的数据库操作包不同,因此提供伪代码,以便于理解伪代码
到此,该方案,能够满足我的分布式锁的需求。
但是该方案,有一个比较致命的问题,就是所有记录共享一个锁,并发并不高。
经过测试,开启50*100个线程并发修改,5次耗时平均为8秒。
实现第三版
由于方案二,存在共享同一把中央锁,并发不高的请求。参考concurrentHashMap实现原理,引入分段锁概念,降低锁粒度。
基本方式是:
根据sql创建好数据库
创建100条记录Flock_name="center_lock_xx"的记录(xx为00-99)。
在对其他锁(如Flock_name="sale_invite_lock")进行操作的时候,根据crc32算法找到对应的center_lock_02,先对"center_lock_02"记录select for update
"sale_invite_lock"记录自己的增删改查。
伪代码如下:
经过测试,开启50*100个线程并发修改,5次耗时平均为5秒。相较于版本二几乎有一倍的提升。
至此,完成redis/mysql分布式锁、计数器的实现与应用。
最后
根据不同应用场景,做出如下选择:
高并发、不保证数据一致性:redis锁/计数器
低并发、保证数据一致性:mysql锁/计数器
低并发、不保证数据一致性:你随意
高并发。保证数据一致性:redis锁/计数器 + mysql锁/计数器。
表数据和记录:
推荐一个交流学习群:753650956 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多:
本文来源于网络
作者:林湾村龙猫
分布式锁(redis/mysql)的更多相关文章
- 分布式锁--Redis小试牛刀
参考文章: Redis分布式锁的正确实现方式 分布式锁看这篇就够了 在这两篇文章的指引下亲测 Redis分布式锁 引言 分布式系统一定会存在CAP权衡问题,所以才会出现分布式锁 什么是CAP理论? 为 ...
- 分布式锁----Redis实现
分布式锁 为什么需要有分布式锁呢,在单点的时候synchronized 就能解决,但是服务拆分之后,每个服务都是单独的机器,无法解决,所以出现了分布式锁,其实也就是用各种手段,实现获取唯一锁,别人无法 ...
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- java-spring基于redis单机版(redisTemplate)实现的分布式锁+redis消息队列,可用于秒杀,定时器,高并发,抢购
此教程不涉及整合spring整合redis,可另行查阅资料教程. 代码: RedisLock package com.cashloan.analytics.utils; import org.slf4 ...
- 分布式锁redis
1. 首先看这篇文章中 https://mp.weixin.qq.com/s/s-ozSjM5WmSUopxttSWYeQ 为什么redis能实现锁功能呢,看下图,redis命令窗口中,setnx ...
- 分布式锁-Redis方案
#!/usr/bin/env python # coding=utf-8 import time import redis class RedisLock(object): def __init__( ...
- 使用redis分布式锁解决并发线程资源共享问题
众所周知, 在多线程中,因为共享全局变量,会导致资源修改结果不一致,所以需要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操作 但是在分布式架构中,我们的服务可能会有n个实例,但线程锁只对同一 ...
- 死磕 java同步系列之mysql分布式锁
问题 (1)什么是分布式锁? (2)为什么需要分布式锁? (3)mysql如何实现分布式锁? (4)mysql分布式锁的优点和缺点? 简介 随着并发量的不断增加,单机的服务迟早要向多节点或者微服务进化 ...
- 【redis】分布式锁实现,与分布式定时任务
如果你还不知道redis的基本命令与基本使用方法,请看 [redis]redis基础命令学习集合 写在前面 redis辣么多数据结构,这么多命令,具体一点,都可以应用在什么场景呢?用来解决什么具体的问 ...
- python使用redis实现协同控制的分布式锁
python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...
随机推荐
- 汇编刷题:屏幕显示 HOW ARE YOU!
DATA SEGMENT INFO DB 'HOW ARE YOU!$' DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DAT ...
- Gradle系列之初识Gradle
原文首发于微信公众号:躬行之(jzman-blog) 学习 Android 有一段时间了,开发中经常使用到 Gradle ,但是不知道 Gradle 构建项目的原理,计划花一点时间学习一下 Gradl ...
- 【Mongodb】视图 && 索引
准备工作 准备2个集合的数据,后面视图和索引都会用到1个订单集合,一个收款信息集合 var orders = new Array(); var shipping = new Array(); var ...
- SIM900A 通过RS232串口进行短信的发送。
一.基本数据 1.SIM900A模块支持RS232串口和LVTTL串口.保留了232口,在学习或者开发时可以监听51低端单片机和模块指令执行情况,能更快的找出原因,节省开发和学习的时间. 2.此模块供 ...
- go中内存泄露的发现与排查
一,什么是内存泄漏 Go 中的并发性是以 goroutine(独立活动)和 channel(用于通信)的形式实现的.处理 goroutine 时,程序员需要小心翼翼地避免泄露.如果最终永远堵塞在 I/ ...
- vue 全局自定义组件
1.vue文件 <template> <div style="position: absolute;bottom: 10px;text-align: center;widt ...
- 011-指针(上)-C语言笔记
011-指针(上)-C语言笔记 学习目标 1.[掌握]字符串常用函数 2.[掌握]指针变量的声明 3.[掌握]指针变量的初始化 4.[掌握]函数与指针 5.[掌握]指针的数据类型 6.[掌握]多级指针 ...
- ES6新特性箭头函数和常用function()对比
// 无参 var fn1 = function() {} var fn1 = () => {} // 单个参数 var fn2 = function(a) {} var fn2 = a =&g ...
- Three.js实现3D地图实例分享
本文主要给大家介绍了关于利用Three.js开发实现3D地图的实践过程,文中通过示例代码介绍的非常详细,对大家学习或者使用three.js具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习 ...
- 小程序—银行、券商们下一代APP的进阶方向
传统金融机构们的App——尤其以手机银行.手机证券为最,发展到今天,已经产生一系列的问题:从用户角度看,体验普遍不好.高度同质化:从业务运营角度看,几乎没有什么“运营”的抓手:从IT角度看,投入产出比 ...