架构设计 | 高并发流量削峰,共享资源加锁机制( 二 )


文章插图
 
redis基础命令
SETNX:加锁的思路是,如果key不存在,将key设置为value如果key已存在,则 SETNX 不做任何动作 。并且可以给key设置过期时间,过期后其他线程可以继续尝试锁获取机制 。
借助Redis的该命令模拟锁的获取动作 。
代码实现
这里基于Redis实现的锁获取和释放机制 。
import org.springframework.stereotype.Component;import redis.clients.jedis.Jedis;import JAVAx.annotation.Resource;@Componentpublic class RedisLock {@Resourceprivate Jedis jedis ;/*** 获取锁*/public boolean getLock (String key,String value,long expire){try {String result = jedis.set( key, value, "nx", "ex", expire);return result != null;} catch (Exception e){e.printStackTrace();}finally {if (jedis != null) jedis.close();}return false ;}/*** 释放锁*/public boolean unLock (String key){try {Long result = jedis.del(key);return result > 0 ;} catch (Exception e){e.printStackTrace();}finally {if (jedis != null) jedis.close();}return false ;}}这里基于Jedis的API实现,这里提供一份配置文件 。
@Configurationpublic class RedisConfig {@Beanpublic JedisPoolConfig jedisPoolConfig (){JedisPoolConfig jedisPoolConfig = new JedisPoolConfig() ;jedisPoolConfig.setMaxIdle(8);jedisPoolConfig.setMaxTotal(20);return jedisPoolConfig ;}@Beanpublic JedisPool jedisPool (@Autowired JedisPoolConfig jedisPoolConfig){return new JedisPool(jedisPoolConfig,"127.0.0.1",6379) ;}@Beanpublic Jedis jedis (@Autowired JedisPool jedisPool){return jedisPool.getResource() ;}}问题描述
在实际的系统运行期间可能出现如下情况:线程01获取锁之后,进程被挂起,后续该执行的没有执行,锁失效后,线程02又获取锁,在数据库更新后,线程01恢复,此时在持有锁之后的状态,继续执行后就会容易导致数据错乱问题 。
这时候就需要引入锁版本概念的,假设线程01获取锁版本1,如果没有执行,线程02获取锁版本2,执行之后,通过锁版本的比较,线程01的锁版本过低,数据更新就会失败 。
CREATE TABLE `dl_data_lock` ( `id` INT (11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `inventory` INT (11) DEFAULT '0' COMMENT '库存量', `lock_value` INT (11) NOT NULL DEFAULT '0' COMMENT '锁版本', PRIMARY KEY (`id`)) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '锁机制表';说明:lock_value就是记录锁版本,作为控制数据更新的条件 。
<update id="updateByLock">UPDATE dl_data_lock SET inventory=inventory-1,lock_value=https://www.isolves.com/it/yj/zs/2020-06-23/#{lockVersion}WHERE id=#{id} AND lock_value <#{lockVersion}说明:这里的更新操作,不但要求线程获取锁,还会判断线程锁的版本不能低于当前更新记录中的最新锁版本 。
2、乐观锁机制描述
乐观锁大多是基于数据记录来控制,在更新数据库的时候,基于前置的查询条件判断,如果查询出来的数据没有被修改,则更新操作成功,如果前置的查询结果作为更新的条件不成立,则数据写失败 。
过程图解

架构设计 | 高并发流量削峰,共享资源加锁机制

文章插图
 
代码实现
业务流程,先查询要更新的记录,然后把读取的列,作为更新条件 。
@Overridepublic Boolean updateByInventory(Integer id) {DataLockEntity dataLockEntity = dataLockMApper.getById(id);if (dataLockEntity != null){return dataLockMapper.updateByInventory(id,dataLockEntity.getInventory())>0 ;}return false ;}例如如果要把库存更新,就把读取的库存数据作为更新条件,如果读取库存是100,在更新的时候库存变了,则更新条件自然不能成立 。
<update id="updateByInventory">UPDATE dl_data_lock SET inventory=inventory-1 WHERE id=#{id} AND inventory=#{inventory}</update>五、分布式服务1、服务保护在处理高并发的秒杀场景时,经常出现服务挂掉场景,常见某些APP的营销页面,出现活动火爆页面丢失的提示情况,但是不影响整体应用的运行,这就是服务的隔离和保护机制 。
基于分布式的服务结构可以把高并发的业务服务独立出来,不会因为秒杀服务挂掉影响整体的服务,导致服务雪崩的场景 。
2、数据库保护数据库保护和服务保护是相辅相成的,分布式服务架构下,服务和数据库是对应的,理论上秒杀服务对应的就是秒杀数据库,不会因为秒杀库挂掉,导致整个数据库宕机 。
 

【架构设计 | 高并发流量削峰,共享资源加锁机制】


推荐阅读