从零开发短视频电商 分布式锁-基于数据库实现

方式一 for update悲观锁方式

  • select * from xxx for update 悲观锁方式。

    • 首先要开启事务
    • 在for update语句中where条件字段上创建索引,因为是通过索引来加锁的,否则会锁整个表。
    • 这个在微服务中,使用不多,因为事务中很可能嵌套了第三方调用,不符合规范。

    缺点:会导致数据库锁占用很长时间,同时会长时间占用数据库连接。

方式二 单独lock表乐观锁方式

锁示例

@Component
public class Example {
    @Autowired
    private Lock lock;

    public void test() {
        // 尝试获取锁
        LLock llock = lock.acquire(key, Duration.ofSeconds(10));
        if (Objects.isNull(llock)) {
            throw new BusinessException("其他人正在处理中,请稍后重试");
        }
        try {
            // 业务代码
        } finally {
            // 解除锁
            lock.release(llock);
        }
    }
}

数据表结构

CREATE TABLE `distribute_lock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `lock_key` varchar(50) NOT NULL COMMENT '锁记录key',
  `token` varchar(50) NOT NULL COMMENT '锁的token,防止误删其他人的锁',
  `thread_id` varchar(50) NOT NULL COMMENT '获取锁的线程id',
  `expire` bigint(20) NOT NULL COMMENT '锁的失效时间,时间戳',
  PRIMARY KEY (`id`),
  UNIQUE KEY `distribute_lock_UN` (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式锁';

tips:表名切勿使用lock,使用关键字语法会报错。在oracle里,resource也是关键字,mysql里不是。

整体

后边会再实现个redis版本的。

public interface Lock {
    /**
     * 尝试获取锁,
     *
     * @param key        锁定的key
     * @param expiration 锁定的时间
     * @return 锁定失败返回null,成功返回LLock锁对象
     */
    LLock acquire(String key, Duration expiration);
    /**
     * 释放锁
     *
     * @param lock   锁对象
     * @return 是否成功
     */
    boolean release(LLock lock);
}

加锁

String token = fastSimpleUUID();
int row = INSERT ignore INTO distribute_lock (lock_key, token, expire, thread_id) VALUES (?, ?, ?, ?);
if (row == 0) {
    return null;
}
// 续约线程
ScheduledFuture scheduledFuture = scheduleLockRefresh(refresh)
return new LLock(key,token,scheduledFuture);

解锁

客户端设置的锁,必须由自己解开。即会check key和token。

cancelSchedule(lock.getScheduledFuture());
DELETE FROM distribute_lock WHERE lock_key = ? AND token = ?;

续租

当客户端发现在锁的租期内无法完成操作时,就需要延长锁的持有时间,进行续租(renew)。同解锁一样,客户端应该只能续租自己持有的锁。续租需要每个锁后台定时任务时间建议是锁的1/3去续租。

UPDATE distribute_lock SET expire = ? WHERE lock_key = ? AND token = ?;

整体代码在:https://gitee.com/lakernote/easy-admin/tree/master/src/main/java/com/laker/admin/framework/lock

总结

优点

  • 实现简单,没有redis的集群和主从问题。

缺点

  • 性能方面来说没有redis好。

参考:

  • https://github.com/alturkovic/distributed-lock
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lakernote

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值