悲观锁,乐观锁,redis+lua分布式锁的数据库案例讲解

悲观锁、乐观锁与分布式锁:核心原理、使用场景与实战技巧 特性悲观锁乐观锁分布式锁 (Redis)核心思想先取锁,再操作先操作,提交时检查分布式互斥实现方式适用范围单机、多线程/进程单机、多线程/进程分布式系统、跨进程实现方式FOR UPDATE / 内置锁版本号、CAS、WATCHSET NX PX + Lua 脚本性能并发性能低并发性能高有网络开销,低于单机锁典型场景秒杀、转账、状态机用户资料、计数器定时任务、缓存击穿悲观锁:牺牲性能换一致性乐观锁:牺牲部分一致性换性能。 阅读详情

超发问题分析

针对抢到红包案例,用户抢到红包后,红包总量应-1,当多个用户同时抢红包,此时多个线程同时读得库存为n,相应的逻辑执行后,最后将均执update T_RED_PACKET set stock = stock - 1 where id = #{id} ,很明显这是错误的。

锁的概念

悲观锁(包括 排它锁,分享锁 for update)

悲观锁是一个统称的概念
它的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。作用就是线程中的同步,同java中的synchronized。

通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select for update时会获取被select中的数据行的行锁,因此其他并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。

这里需要注意的一点是不同的数据库对select for update的实现和支持都是有所区别的,例如oracle支持select for update no wait,表示如果拿不到锁立刻报错,而不是等待,mysql就没有no wait这个选项。另外mysql还有个问题是select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在mysql中用悲观锁务必要确定走了索引(索引查询),而不是全表扫描,全表扫描悲观锁会把整个表锁住,造成其他任何查询级别之上的操作都需要等待,性能影响特别大。

由悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。下面的sql实现的锁主要针对mysql,其他版本是的数据库不一定支持

排他锁(X锁)

排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。

与共享锁类型,在需要执行的语句后面加上for update就可以了

语法:

select * from table for update
共享锁(S锁)

共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁.

对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。要想修改就必须等所有共享锁都释放完之后.

语法:

select * from table lock in share mode ;

使用排他锁,这样流程如下:

线程1在查询红包数时使用排他锁 select id, user_id as userId, amount, send_date as sendDate, total, unit_amount as unitAmount, stock, version, note from T_RED_PACKET where id = #{id} for update
然后进行后续的操作(redPacketDao.decreaseRedPacket 和 userRedPacketDao.grapRedPacket),更新红包数量,最后提交事务。
线程2在查询红包数时,如果线程1还未释放排他锁,它将等待
线程3同线程2,依次类推

乐观锁(依靠表的设计和代码)

在红包表添加version版本字段或者timestamp时间戳字段,这里我们使用version
线程1查询后,执行更新变成了update T_RED_PACKET set stock = stock - 1, version = version + 1 where id = #{id} and version = #{version}
这样,保证了修改的数据是和它查询出来的数据是一致的,而其他线程并未进行修改。当然,如果更新失败,表示在更新操作之前有其他线程已经更新了该红包数,那么就可以尝试重入机制来保证更新成功。

** 总结**
1.悲观锁使用了排他锁,当程序独占锁时,其他程序就连查询都是不允许的,导致吞吐较低。如果在查询较多的情况下,可使用乐观锁。
2.乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入较频繁,对吞吐要求不高,可使用悲观锁。

代码改造
分析
为了不影响上个版本,我们新加个接口方法和Mapper映射。 因为悲观锁是数据库提供的功能,所以仅仅在Dao层修改Sql,Service层无需新增新的接口,只需要切换下调用的Dao层的方法即可。

RedPacketDao新增接口方法
/**
* 获取红包信息. 悲观锁的实现方式
*
* @param id
* --红包id
* @return 红包具体信息
*/
public RedPacket getRedPacketForUpdate(Long id);

RedPacket.xml配置映射文件

	<!-- 查询红包具体信息  悲观锁的实现方式for update -->
	<select id="getRedPacketForUpdate" parameterType="long"
		resultType="com.artisan.redpacket.pojo.RedPacket">
		select 
			id, user_id as userId, amount, send_date as sendDate, total, unit_amount as unitAmount, stock, version, note
		from 
			T_RED_PACKET 
		where id = #{id} for update
	</select>

悲观锁是一种利用数据库内部机制提供的锁的方法,也就是对更新的数据加锁,这样在并发期间一旦有一个事务持有了数据库记录的锁,其他的线程将不能再对数据进行更新(Mysql中体现在行锁和表锁上).

在 SQL 中加入的 for update 语句,意味着将持有对数据库记录的行更新锁(因为这里使用主键查询,所以只会对行加锁。如果使用的是非主键查询,要考虑是否对全表加锁的问题,加锁后可能引发其他查询的阻塞〉,那就意味着在高并发的场景下 , 当一条事务持有了这个更新锁才能往下操作,其他的线程如果要更新这条记录,都需要等待,这样就不会出现超发现象引发的数据一致性问题了.

Service层调用新的Dao方法

还原数据,部署测试
将T_RED_PACKET和T_USER_RED_PACKET中的数据还原为初始数据后,启动应用,通过FireFox 访问 http://localhost:8080/ssm_redpacket/grap.jsp

统计报告
一致性数据统计:

SELECT
	a.id,
	a.amount,
	a.stock
FROM
	T_RED_PACKET a
WHERE
	a.id = 1
UNION ALL
	SELECT
		max(b.user_id),
		sum(b.amount),
		count(*)
	FROM
		T_USER_RED_PACKET b
	WHERE
		b.red_packet_id = 1;

这里已经解决了超发的问题,所以结果是正确的,最起码逻辑是正确的了。除了结果正确,我们还需要考虑性能问题,统计来看下

性能数据统计:

SELECT
	(
		UNIX_TIMESTAMP(max(a.grab_time)) - UNIX_TIMESTAMP(min(a.grab_time)) 
	)  AS lastTime
FROM
	T_USER_RED_PACKET a;

注意事项
不使用悲观锁时,2万个红包190秒【主机配置很低】抢完(但存在超发现象),现在是275秒。 目前只是对数据库加了一个锁,当加的锁比较多的时候,数据库的性能还会持续下降,所以要区分不同的业务场景,慎重使用。

悲观锁导致性能下降的原因探究

对于悲观锁来说,当一条线程抢占了资源后,其他的线程将得不到资源,那么这个时, CPU 就会将这些得不到资源的线程挂起,挂起的线程也会消耗 CPU 的资源尤其是在高并发的请求中。

!

只能有一个事务占据资源,其他事务被挂起等待持有资源的事务提交并释放资源。当此时就进入了线程 2 , 线程 3……线程n,开始抢夺资源的步骤了,这里假设线程 3 抢到资源

!
一旦线程1 提交了事务,那么锁就会被释放,这个时候被挂起的线程就会开始竞争红包资源,那么竞争到的线程就会被 CPU 恢复到运行状态,继续运行。

于是频繁挂起,等待持有锁线程释放资源, 一旦释放资源后,就开始抢夺,恢复线程,直至所有红包资源抢完。

在高并发的过程中,使用悲观锁就会造成大量的线程被挂起和恢复,这将十分消耗资源,这就是为什么使用悲观锁性能不佳的原因。

有些时候,我们也会把悲观锁称为独占锁,毕竟只有一个线程可以独占这个资源,或者称为阻塞锁,因为它会造成其他线程的阻塞。无论如何它都会造成并发能力的下降,从而导致 CPU频繁切换线程上下文,造成性能低下。

为了克服这个问题,提高并发的能力,避免大量线程因为阻塞导致 CPU 进行大量的上下文切换,目前比较普遍的是乐观锁机制。

redis调用lua脚本

Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处如下:

1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。
2.原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入(java等客户端则会执行多次命令完成一个业务,违反了原子性操作)。
3.复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。

这里着重看第二条,相比客户端调用redis命令,一个业务流程可能要设计到读取,修改两个操作命令,而这个过程中间有可能被其他线程执行了修改命令,那么这个操作就不具有原子性操作。而redis执行lua脚本是在同一个线程中独占方式执行,期间没有其他线程占用(redis本身是单线程的特点),因此具有天然的原子性,同事一次性执行性能开销很小,相比多条命令要好很多。

@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private RedisRedPacketService redisRedPacketService;

	// Lua脚本
	String script = "local listKey = 'red_packet_list_'..KEYS[1] \n" 
			+ "local redPacket = 'red_packet_'..KEYS[1] \n"
			+ "local stock = tonumber(redis.call('hget', redPacket, 'stock')) \n" 
			+ "if stock <= 0 then return 0 end \n"
			+ "stock = stock -1 \n" 
			+ "redis.call('hset', redPacket, 'stock', tostring(stock)) \n"
			+ "redis.call('rpush', listKey, ARGV[1]) \n" 
			+ "if stock == 0 then return 2 end \n" 
			+ "return 1 \n";

	// 在缓存LUA脚本后,使用该变量保存Redis返回的32位的SHA1编码,使用它去执行缓存的LUA脚本[加入这句话]
	String sha1 = null;

	@Override
	public Long grapRedPacketByRedis(Long redPacketId, Long userId) {
		// 当前抢红包用户和日期信息
		String args = userId + "-" + System.currentTimeMillis();
		Long result = null;
		// 获取底层Redis操作对象
		Jedis jedis = (Jedis) redisTemplate.getConnectionFactory().getConnection().getNativeConnection();
		try {
			// 如果脚本没有加载过,那么进行加载,这样就会返回一个sha1编码
			if (sha1 == null) {
				sha1 = jedis.scriptLoad(script);
			}
			// 执行脚本,返回结果
			Object res = jedis.evalsha(sha1, 1, redPacketId + "", args);
			result = (Long) res;
			// 返回2时为最后一个红包,此时将抢红包信息通过异步保存到数据库中
			if (result == 2) {
				// 获取单个小红包金额
				String unitAmountStr = jedis.hget("red_packet_" + redPacketId, "unit_amount");
				// 触发保存数据库操作
				Double unitAmount = Double.parseDouble(unitAmountStr);
             	redisRedPacketService.saveUserRedPacketByRedis(redPacketId, unitAmount);
			}
		} finally {
			// 确保jedis顺利关闭
			if (jedis != null && jedis.isConnected()) {
				jedis.close();
			}
		}
		return result;
	}

这里使用了保存脚本返回 的 SHAl 字符串 ,所以只会发送一次脚本到 Redis 服务器,之后只传输 SHAl 字符串和参数到 Redis 就能执行脚本 了, 当脚本返回为 2 的时候, 表示此时所有的红包都已经被抢光了 ,那么就会触发 redisRedPacketService 的 saveUserRedPacketByRedis 方法。由于在 saveU serRedPacketByRedis 加入注解@Async , 所以 Spring 会创建一条新的线程去运行它 , 这样就不会影响最后抢一个红包用户 的响应时间了 。

lua脚本实现redis分布式锁

setnx 如果key不存在则添加值并返回1,如果已经存在key则返回0

加锁
使用业务setnx(key,业务流水号)当加锁成功返回1时设置过期时间,避免业务异常没有解锁时防止死锁
当同一业务再次申请锁时,如果随机值相同 则认为是重试,则直接设置过期时长;如果随机值不同则直接返回0,获取锁失败

解锁
业务完成直接del(key)完成

lock.lua

-- Set a lock
--  如果获取锁成功,则返回 1
local key     = KEYS[1]
local content = ARGV[1]
local ttl     = tonumber(ARGV[2])
local lockSet = redis.call('setnx', key, content)
if lockSet == 1 then
  redis.call('PEXPIRE', key, ttl)
else
  -- 如果value相同,则认为是同一个线程的请求,则认为重入锁
  local value = redis.call('get', key)
  if(value == content) then
    lockSet = 1;
    redis.call('PEXPIRE', key, ttl)
  end
end
return lockSet

unlock.lua

-- unlock key
local key     = KEYS[1]
local content = ARGV[1]
local value = redis.call('get', key)
if value == content then
  return redis.call('del', key)
else
    return 0
end


测试加锁和解锁

redis-cli  --eval lock.lua lo3 , 2 60000

redis-cli  --eval unlock.lua lo3 , 2

在java代码中我们可以使用AOP获取当前业务的key,业务主键实现加锁,如果一旦业务异常 那么在超时后自动解锁


作者:小小工匠
来源:优快云
原文:https://blog.youkuaiyun.com/yangshangwei/article/details/82980659
版权声明:本文为博主原创文章,转载请附上博文链接!

用Multisim仿真AD630锁定放大器:从2012年电赛A题实战到避坑指南 本文详细介绍了如何使用Multisim仿真AD630锁定放大器,从2012年电赛A题实战出发,提供完整的电路搭建、参数优化及避坑指南。重点解析AD630的开关调制架构和双相位检测的工程实现,帮助读者在强噪声背景下有效提取微弱信号,提升电子设计竞赛和工程实践中的信号处理能力。 阅读详情

相关推荐

生态学家都在用的MixSIAR:用R语言搞定稳定同位素混合模型(从数据整理到结果解读)

本文详细介绍了生态学家常用的MixSIAR工具,通过R语言实现稳定同位素混合模型分析,从数据整理到结果解读全流程。文章涵盖环境准备、模型构建、参数设置、运行诊断及可视化等关键步骤,帮助研究者科学分析河口鱼类食物来源,适用于生态学研究和湿地保护政策制定。

weixin_28683065的博客 437

redis 7.乐观锁、悲观锁,Redis事务,秒杀案例,redisTemplate执行事务操作和LUA脚本

文章目录1.1 超卖问题1.2 超卖解决(1) RedisTemplate 的默认配置不支持事务1.3 解决库存遗留问题 1.1 超卖问题 大量用户并发的,向库存系统下订单: 1.2 超卖解决   利用乐观锁淘汰用户,解决超卖问题: @RestController @RequestMapping("/redisTest") public class RedisTestController { @Autowired private RedisTemplate redisTemplate

不是一枚开发 5万+

SSD盘符漂移导致OSD down场景模拟与恢复

运维团队反馈一个问题,现象是:某个OSD对应的cache分区发生了变化,比如原来cache使用的分区是/dev/sde,后来还是这块盘,但分区信息不知何故,变成了/dev/sdf(产品OSD设备并不是根据分区名称来map,而是根据partlabel),最终的现象是这个OSD down了,没法启动.接到这个现象的反馈后,记录一下这个问题在lab的复现方法。

Gavin Blog 937

Redis悲观锁、乐观锁和调用Lua脚本的优缺点

悲观锁使用了数据库的锁机制,可以消除数据不一致性,对于开发者而言会十分简单,但是,使用悲观锁后,数据库的性能有所下降,因为大量的线程都会被阻塞,而且需要有大量的恢复过程,需要进一步改变算法以提高系统的并发能力。 通过 CAS 原理和 ABA 问题的讨论,我们更加明确了乐观锁的原理,使用乐观锁有助于提高并发性能,但是由于版本号冲突,乐观锁导致多次请求服务失败的概率大大提高,而我们通过重入(按时间戳或...

weixin_45743799的博客 1761

告别手动复制粘贴!用Python脚本+pywinauto实现PC微信消息自动发送(附完整源码)

本文详细介绍了如何利用Python和pywinauto实现PC微信消息的自动化发送,告别繁琐的手动操作。从环境搭建、UI控件定位到构建健壮的发送流程,再到高级功能扩展和项目打包部署,全面解析了自动化办公的实现方案,显著提升工作效率。

weixin_30315723的博客 583

Python画常见分布的概率密度图

因为做概随大作业,了解了一些python画概率分布图的方法。

qq_44807946的博客 5204

Redis悲观锁、乐观锁和调用Lua脚本三种方式的优缺点

但是这样使用的风险在于 Redis 的不稳定性,因为其事务和存储都存在不稳定的因素,所以更多的时候,笔者都建议使用独立 Redis 服务器做高并发业务,一方面可以提高 Redis 的性能,另一方面即使在高并发的场合,Redis 服务器宕机也不会影响现有的其他业务,同时也可以使用备机等设备提高系统的高可用,保证网站的安全稳定。互联网的高并发应用,先谈及了一些常用的系统设计理念,用以搭建高可用的互联网应用系统,着重介绍了抢红包的高并发应用,还讨论了数据不一致的超发问题。并且还论述了乐观锁、悲观锁和。

unber的博客 409

DIY天文摄影:如何用NMP溶剂安全去除佳能CMOS传感器的拜耳滤镜(附详细步骤)

本文为天文摄影爱好者提供了一份详尽的DIY指南,介绍如何使用NMP溶剂安全去除佳能CMOS传感器的拜耳滤镜,将其改造为高灵敏度单色相机。文章深入剖析了NMP溶剂的作用原理与关键风险,并系统性地拆解了从材料准备、分步操作到后期验证的完整实践流程,旨在帮助具备动手能力的爱好者实现深空天体摄影的进阶目标。

w3x4y的博客 306

线上问题--communication link failure the last packet sent successfully

线上bug解决问题产生过程问题产生原因解决办法参考文章 问题产生过程 早上收到报警显示查询数据库无法连接,communication link failure the last packet sent successfully 具体报警如下: 问题产生原因 收到无法连接数据库报警后,先咨询网络网络是否稳定,确认网络质量无问题后,看了数据库连接数也是正常水平。将数据库域名发给DBA,DBA同事反馈数...

你究竟是想一辈子卖糖水,还是希望获得改变世界的机会? 5349

RuyiDify如何创建知识库API密钥:让程序安全访问知识库

知识库 API Key 是程序访问知识库的钥匙。创建它很简单,但真正重要的是知道它能做什么、应该放在哪里、不能暴露给谁。RuyiDify 讲这一步,不是为了让大家多记一个按钮,而是为了把知识库从“人工上传资料”推进到“程序化维护资料”。

未来已来,AI时代,学AI编程,做AI量化,就来大鹏AI教育。 133

【华为OD机试真题 新系统】1009、企业内部门的最大层级 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS)

【华为OD机试真题 新系统】1009、企业内部门的最大层级 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS) 华为od机试,华为od机试真题,华为od机试E卷,华为odE卷,华为od机试真题E卷,华为OD,华为OD机试,华为OD机试真题,华为od机考,华为od机试D卷,华为od机试真题D卷,华为od手撕代码,华为od,华为od机试C卷,华为od e卷,华为2026e卷,od机试,2026E卷,华为OD机试E卷,什么是华为od,华为od介绍,全面了解华为od,2026华为od,如何备战

KJ.JK 1412

Communications link failure:The last packet successfully received from the server was 0 millisecond

出现这种错误的大致情况如下: 1、数据库连接长时间未使用,断开连接后,再去连接出现这种情况。这种情况常见于用连接池连接数据库出现的问题 2、数据库连接的后缀参数问题 针对上述两种情况,解决方案如下 1、修改MySQL的参数,my.cnf位于/etc目录下,修改完成后,重启mysql wait_timeout最大为31536000即1年,在my.cnf中加入: [mysqld] wait_timeout=31536000 interactive_timeout=31536000 2、连接数

LookingTomorrow的博客 1万+

2xmax96717+max96724+agx orin驱动代码

说明:这里主要解决max96717的设备地址和sensor地址映射,vc0->vc1多颗max96717+max96724使用配置思路如下: 1、改变max96717的pipez stream id,然后max96724的pipe绑定使用相应的stream id 2、max96717默认是vc0,把vc0改写成vc1\vc2\vc3,然后max96724再重新映射vc输出

mantouyouyou的博客 112

性能优化 - 案例篇:CAS、乐观锁、分布式锁和无锁

本文探讨了Java并发编程中的性能优化策略,重点分析了AQS、CAS机制、乐观锁与悲观锁的区别,以及分布式锁的实现方法。首先对比了synchronized和基于AQS的Lock性能差异,指出AQS通过CAS实现非阻塞同步的优势。 详细解析了CAS(Compare And Swap)的基本原理,跟踪AtomicInteger的底层实现,说明其依赖CPU指令保证原子性。通过性能测试证明原子类在高并发场景比synchronized更高效。 讨论了乐观锁和悲观锁的适用场景,并以数据库和Redis为例,展示乐观锁实现

小工匠 2033

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

分布式锁_Redis分布式锁+Redisson分布式锁+Zookeeper分布式锁+Mysql分布式锁(原版)

1. 传统锁回顾 1.1. 从减库存聊起 1.2. 环境准备 1.3. 简单实现减库存 1.4. 演示超卖现象 1.5. jvm锁问题演示 1.5.1. 添加jvm锁 1.5.2. 原理 1.6. 多服务问题 1.6.1. 安装配置nginx 1.6.2. Jmeter压力测试 1.7. mysql锁演示 1.7.1. 一个sql 1.7.2. 悲观锁 1.7.3. 乐观锁 1.7.4. mysql锁总结 1.8. redis乐观锁 2. 基于redis实现分布式锁 2.1. 基本实现 2.2. 防死锁 2

无 931

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Redis分布式锁解决方案 set nx px + LUA

Redis分布式锁解决方案 我们知道分布式锁的特性是排他、避免死锁、高可用。分布式锁的实现可以通过数据库的乐观锁(通过版本号)或者悲观锁(通过for update)、Redis的setnx()命令、Zookeeper(在某个持久节点添加临时有序节点,判断当前节点是否是序列中最小的节点,如果不是则监听比当前节点还要小的节点。如果是,获取锁成功。当被监听的节点释放了锁(也就是被删除),会通知当前节点。然后当前节点再尝试获取锁,如此反复) 本篇文章,主要讲如何用Redis的形式实现分布式...

MayMatrix 的博客 4089

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

基于redis的分布式锁的分析与实践

前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了。乐观...

java面试笔试 149

Redis面试-两种方式实现分布式锁

介绍Redis的两种方式实现分布式锁、缓存更新、超卖问题

weixin_49429082的博客 1175

一文掌握分布式锁:Mysql/Redis/Zookeeper实现

一文掌握分布式锁:Mysql/Redis/Zookeeper实现

qq_43745578的博客 2222

加锁了,还有并发问题?Redis分布式锁你真的了解?

新接手的项目,偶尔会出现账不平的问题。之前的技术老大临走时给的解释是:排查了,没找到原因,之后太忙就没再解决,可能是框架的原因…… 既然项目交付到手中,这样的问题是必须要解决的。梳理了所有账务处理逻辑,最终找到了原因:数据库并发操作热点账户导致。就这这个问题,来聊一聊分布式系统下基于Redis的分布式锁。顺便也分解一下问题形成原因及解决方案。 原因分析 系统并发量并不高,存在热点账户,但也不至于那么严重。问题的根源在于系统架构设计,人为的制造了并发。场景是这样的:商户批量导入一批数据,系统会进行前置处理,并

程序新视界 1637

redis和数据库实现分布式锁

锁(Lock)的本质是:保证同一时刻只有一个线程(或一个进程)能够执行某段代码。stock--;用户A购买用户B购买stock --;库存:-1这就是典型的并发问题。stock--;同一时刻只能有一个线程进入。分布式锁的核心目标只有一句话:在多个 JVM、多个服务实例同时访问同一资源时,保证同一时刻只有一个节点执行关键业务逻辑。库存扣减:乐观锁(Version)中小项目:Redis + Lua企业级项目:Redisson强一致性场景:ZooKeeper。

西凉的悲伤博客 422

Redis学习笔记

多个JVM有一个共同的锁监视器,不同JVM的不同线程都用同一把锁,满足分布式系统或者集群模式下进程可见并且互斥的锁,保证:多进程可见、互斥、高可用、高性能、安全性。版本号法:给数据加一个版本号,每次更新数据前判断版本号是否和上次修改后的版本号一致,不一致说明数据被修改,一致则可以修改数据且版本号自增。悲观锁:认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。主从一致性:如果Redis提供了主从集群,主从同步存在延迟,当主宕机,如果从未同步主中的锁数据,则会出现锁实现失效。

2503_91700196的博客 1255

【并发】高并发下库存超卖问题如何解决?

文章目录出现场景解决方案数据库设置字段为无符号型悲观锁乐观锁通过redis队列解决分布式锁+分段缓存(推荐方案)Redis原子操作(Redis incr)+乐观锁LUA脚本保持库存原子性 出现场景 在并发的场景下,比如商城售卖商品中,一件商品的销售数量>库存数量的问题,称为超卖问题。主要原因是在并发场景下,请求几乎同时到达,对库存资源进行竞争,由于没有适当的并发控制策略导致的错误。 例如简单的下单操作,通常我们会按照如下写法 public ServerResponse createOrder(Integ

BookSea的博客 5095

悲观锁与乐观锁详解:从Java并发到数据库实战

并发编程中,共享资源的竞争与临界区管理是核心问题。悲观锁与乐观锁作为两种基本并发控制策略,分别基于“默认冲突”和“默认不冲突”的前提,衍生出synchronized、ReentrantLock、CAS、版本号等多种实现。理解其底层原理,有助于在Java并发场景中做出合理选型。悲观锁通过阻塞保证强一致性,适合写多读少;乐观锁通过CAS自旋或版本校验提升吞吐,适合读多写少。在数据库事务与分布式环境下,锁的实现需结合行锁、版本号或外部组件,并警惕事务提交时机、ABA问题与重试风暴。本文从锁的思想出发,对比实现差异

weixin_33788244的博客 320

Redis在秒杀业务中的应用

本文探讨了Redis在秒杀业务中的应用,重点介绍了全局唯一ID生成方案和分布式锁的实现。首先提出基于Redis的全局ID生成器设计方案,通过时间戳+序列号的组合方式保证ID唯一性。针对秒杀业务中的库存超卖问题,分析了悲观锁和乐观锁的解决方案及各自优缺点。对于一人一单场景,详细说明了synchronized锁的局限性及分布式锁的必要性。最后深入讲解了Redis分布式锁的实现原理,包括误删问题的解决方案和Lua脚本保证原子性的方法,提供了一套完整的分布式锁实现代码。这些技术方案共同构成了高并发秒杀系统的核心保障

TheChosenOnev的博客 699

不用找了,基于 Redis 的分布式锁实战来了!

Java技术栈www.javastack.cn优秀的Java技术公众号作者:菜蚜my.oschina.net/wnjustdoit/blog/1606215前言:在分布式...

Java技术栈,分享最主流的Java技术 206
上一篇: redis在使用lua脚本以及实现redis分布式锁
下一篇: 微信小程序开发资料整理
牧竹子
博客等级 码龄17年 572粉丝 233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值