分布式锁(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写过一个网络锁 ...
随机推荐
- 分治与递归-Fibonacci数列兔子问题
裴波那契(Fibonacci leonardo,约1170-1250)是意大利著名数学家.在他的著作<算盘书>中许多有趣的问题,最富成功的问题是著名的“兔子繁殖问题”: 如果每对兔子每月繁 ...
- spring boot 异步发送邮件
发送邮件由于是一个耗时的操作,有可能需要一个几十秒的操作,但是呢,接口 是一个瞬间完成的,为了不影响接口的性能,所以需要对发送邮件的操作进行异步操作,我们这里呢,首先我们要引入发送邮件的测试模块. & ...
- DVWA系列精品教程:2、命令注入
文章更新于:2020-04-11 注:如何搭建环境参见:搭建DVWA Web渗透测试靶场 DVWA之命令注入漏洞 一.介绍 1.1.官方说明 1.2.总结 二.命令注入实践 2.1.安全级别:LOW ...
- web系统安全运营之基础- 基于DFA算法的高性能的敏感词,脏词的检测过滤算法类(c#).
[概述]做好一个web系统的安全运维,除了常规的防注入,防入侵等,还有一个检测并过滤敏感词,脏词.. 这件事做得不好,轻则导致一场投诉或纠纷,重则导致产品被勒令关闭停运. 废话少说,先看下代码,可以 ...
- Java 泛型、通配符? 解惑
Java 泛型通配符?解惑 分类: JAVA 2014-05-05 15:53 2799人阅读 评论(4) 收藏 举报 泛型通配符上界下界无界 目录(?)[+] 转自:http://www.linux ...
- 钩子函数 Function类
Function 为 com.google.common.base包下接口类: public interface Function<F, T> { @Nullable T apply(@N ...
- Redis之事务操作
1.Redis事务的概念: Redis 事务的本质是一组命令的集合.事务支持一次执行多个命令,一个事务中所有命令都会被序列化.在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求 ...
- Windows下如何将一个程序设为开机自启
1.放在 开始-启动(C:\Users\Qi\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup)2.修改注册表[HKEY_L ...
- C#中分布式事务的超时处理问题
事务是个很精妙的存在,我们在数据层.服务层.业务逻辑层等多处地方都会使用到. 在这里我只说下TransactionScope这个微软推荐使用的隐式事务.它是从Framework 2.0开始引入的一个事 ...
- 如何将SqlServer配置为django的数据源(2.2以后版本)
django-pyodbc-azure 是一个官方推荐的 第三方django数据库支持backend. 根据官网的介绍django-pyodbc-azure 只能支持到 django 2.1.如果涉及 ...