业务要求唯一键应该放在哪里校验
  qbian 15天前 38 0

一 背景

软件开发的很多场景下都有唯一性要求,例如:判断手机号是否已经注册过(需要对手机号做唯一性校验)、判断某个用户是否已经关注过某个话题(需要对用户ID+话题ID做唯一性校验)。

二 设计

针对上述的业务唯一校验,可以有多种不同的设计实现,例如:

  • 在数据库层设计唯一索引限制(针对定制系统的实现方案);
  • 在应用层通过分布式锁做校验(针对通用系统的实现方案,通常用于业务中台);

为什么需要分布式锁:如果只是在应用层做了查询数据库是否存在,不存在就保存。当出现并发时,两个线程同时查询都没查询到,然后两个线程都去做保存操作,数据库层又没有唯一索引约束,就会出现两个都保存成功,也就打破了业务唯一键约束的要求,会在业务层面出现脏数据。

三 实现

3.1 数据库唯一索引

建表时针对有唯一性要求的列(一个或多个),设置:

UNIQUE KEY `uniq_索引名` (`列名0`, `列名1`)

3.2 应用层分布式锁

不同上游业务方对是否唯一性的要求不一样,所以唯一性的校验就需要结合请求来源去做定制,然后在业务层面获取唯一键的分布式锁,获取成功的再去查数据库是否存在,不存在就插入,存在就报唯一键冲突异常。针对判断某个用户是否已经关注过某个话题的业务场景,伪代码如下:


// 分布式锁的键
String lockKey = userId + topicId;

if (!lockService.acquireLock(lockKey, EXPIRE_TIME)) {
    // 抢占锁失败,结合业务自行处理
    return;
}

try {
    UserAttention userAttention = userAttentionService.get(userId, topicId);
    if (userAttention != null) {

	// 抛异常或不处理
	throw new BizExcepetion("用户已关注,重复关注");
    }
    userAttentionService.addAttention(userId, topicId);
} finally {

    // 释放锁
    lockService.releaseLock(lockKey);
}
最后一次编辑于 3天前 2

暂无评论