网站建设项目软件开发招标文件,江门网站建设方案,龙炎电商软件,wordpress通过百度验证前言#xff1a;
相信很多小伙伴对缓存锁都不陌生#xff0c;但是简单的缓存锁想要用好还是需要一些功力。本文总结了笔者多年使用缓存所的一些心得#xff0c;欢迎交流探讨~ 幂等模型#xff1a;
幂等场景一般由查重写入两步操作组成#xff0c;两步操作组成一个最小完…前言
相信很多小伙伴对缓存锁都不陌生但是简单的缓存锁想要用好还是需要一些功力。本文总结了笔者多年使用缓存所的一些心得欢迎交流探讨~ 幂等模型
幂等场景一般由查重写入两步操作组成两步操作组成一个最小完整逻辑再通过缓存锁保证原子性 实现
实现redis分布式锁需要注意两个关键点保证原子性、设置过期时间
保证原子性的目的是为了保证同一时间只有一个请求能获取到锁
设置过期时间的目的是为了防止解锁失败导致死锁
public class MethodLock {private static final Logger logger LoggerFactory.getLogger(MethodLock.class);public static final String KEY_SEPARATOR :;public static final String PARAM_SEPARATOR _;public static final String KEY_PARAM_SEPARATOR #;public static final String SET_SUCCESS_RESULT OK;public static final String METHOD_LOCK doraemon:method:lock;private static Jedis jedis;private static Jedis getJedis() {if (jedis null) {jedis 自行实现;}return jedis;}/*** 获取锁** param methodName 方法名类名加方法名例MethodLock.getLock* param timeout 锁过期时间单位秒* param params 参数通过参数控制锁的粒度* return*/public static boolean getLock(String methodName, long timeout, String... params) {String method getLock|获取锁|;try {String key getKey(methodName, params);String result getJedis().set(key, methodName, nx, ex, timeout 0 ? 5 : timeout);if (Objects.equals(SET_SUCCESS_RESULT, result)) {return true;}} catch (Exception e) {logger.error(method 执行失败,methodName{},timeout{},params{}, methodName, timeout, JSONObject.toJSONString(params), e);}return false;}/*** 释放锁** param methodName 方法名类名加方法名例MethodLock.getLock* param params 参数通过参数控制锁的粒度* return*/public static boolean delLock(String methodName, String... params) {String method delLock|释放锁|;try {String key getKey(methodName, params);long result getJedis().del(key);if (result 0) {return true;}} catch (Exception e) {logger.error(method 执行失败,methodName{},params{}, methodName, JSONObject.toJSONString(params), e);}return false;}/*** 【私有方法】获取redis key** param methodName* param params* return*/protected static String getKey(String methodName, String... params) {if (StringUtils.isBlank(methodName)) {return null;}String key METHOD_LOCK KEY_SEPARATOR methodName;if (params ! null params.length 0) {key KEY_PARAM_SEPARATOR Joiner.on(PARAM_SEPARATOR).useForNull(null).join(params);}return key;}注意事项
1、锁范围内的逻辑需要 完整精简
锁范围内指的是获取锁和释放锁中间的逻辑只有必须保证原子性的关键的逻辑才能放入到锁范围内其他的一些不相关逻辑完全可以放在锁范围外处理
完整加锁的目的是为了解决并发问题所以锁里面的逻辑要求完整不完整的逻辑没加锁
比如幂等场景下一般是 查重写入 两个操作需要保证原子性这两个操作加起来就是一个完整逻辑
精简精简的意思是在保证完整的前提下不要有多余的逻辑以免锁占用时间过长进而影响性能
2、锁粒度选择要恰当
锁粒度指的是锁对请求条件筛选的粗细程度例如用户购买商品下单时可以 1、根据用户id加锁2、也可以根据用户id 商品id加锁1和2就是两种粒度
锁的粒度太大会导致锁的范围过大可能会影响当前逻辑之外的业务
锁的粒度太小会导致锁的范围过小可能会导致锁失效
3、锁过期时间设置要合理
一般建议过期时间 逻辑执行时间 * 150%
过期时间太小可能导致在逻辑执行完成前锁过期失效
过期时间太大解锁失败可能导致在锁过期失效之前重试的请求被拒之门外
4、加锁在try之前锁中的逻辑使用try catch包围解锁在finally里处理
若加锁操作在try catch中并发场景下未获取锁的操作会执行到finally里将锁解除影响正常逻辑
防止逻辑中出现异常阻断流畅导致解锁操作执行不到
5、基于redis的分布式锁并不100%可靠
在一些特殊情况下比如redis宕机数据丢失可能会导致锁失效