数据字段保证唯一性

数据字段保证唯一性

我们日常开发中,常见这么一个需求,要求一个code,一个name,需要保证code不重复,而code是用户输入的,常见的就比如一些字典等。

这个的我们常见的几种做法的话。

唯一键

要么就是直接以code作为主键,这样的话,伪代码基本就是

java 复制代码
  @Transactional(rollbackFor = Exception.class)
       public void add(String code,String name){
              try{
                     mydao.insert(code,name);
              }catch (Exception e){
                     throw myException("code不能重复")
              }
       }

基本就是以code为唯一键,当出入出现重复性错误的话,捕获后抛出自己封装的异常。

这个当然可以,但是如果不让你加唯一键的话,你怎么做。

先查后插

经常见的做法的话,就是

复制代码
 @Transactional(rollbackFor = Exception.class)
       public void add(String code,String name){
             //操作1,先查询数据库有没有
                 int count= mydao.select(code);
             if(count >0){
                    throw myException("code不能重复");
             }
             //操作2,插入数据库
             mydao.insert(code,name);
       }

看似逻辑没问题,但是如果并发高点,就会导致数据库中存在两条以上code一样的数据。

可是为什么呢。

我们来模拟下,事务1和事务2同时执行,code是一致的。

这个时候,事务1的操作1执行,发现满足条件。

事务2的操作1也执行,因为事务1还没提交,自然也没查到数据,满足插入条件。

这个时候,事务1和事务2的操作2都可以插入,自然就插入两条code一致的数据,就满足不了唯一性了。

数据库锁

我们可以用数据库的锁来解决。

复制代码
  @Transactional(rollbackFor = Exception.class)
       public void add(String code,String name){
             //操作1,先查询数据库有没有 
              //select code from mytable where code =code for update
                 int count= mydao.selectforupdate(code);
             if(count >0){
                    throw myException("code不能重复");
             }
             //操作2,插入数据库
             mydao.insert(code,name);
       }

for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过"for update"语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。

这个的话,就需要注意的是,查询走的是索引还是全表扫描,要将深入的话,基本要讲到索引加锁机制了,我们就简单理解为,如果code走的索引,就行锁,不走就全表。

这样事务1和事务2同时执行,事务1执行操作1,发现加锁成功,事务2执行操作1的时候,发现加锁失败,那事务2就挂起了,只能等事务1执行提交事务后才能成功。

这样就保证唯一性。

但是这个缺点比较大的是,因为code如果是数据库中没有的code,往往会导致加锁的范围比较大。导致接口并发比较低。

分布式锁

我们可以用分布式锁来代替数据库锁。

复制代码
     @Transactional(rollbackFor = Exception.class)
       public void add(String code,String name){
              //操作1,用code加锁,用阻塞式加锁,5s后加锁还失败就返回失败
             boolean success = myLock.trylock(code,5);
             if(!success){
                    throw myException("请重试");
             }
             try{
                    //数据库查询code
                    int count =mydao.selectBycode(code);
                    if(count >0){
                           throw myException("code不能重复");
                    }
                    //操作2,插入数据库
                    mydao.insert(code,name);
             }finally {
                    myLock.unLock(code);
             }
           
       }

用分布式锁通过code加锁,这样的话,不同code的插入不影响,同一个code同时插入的时候,只会有一个插入code执行。

注意还是要数据库查一下,加锁只是保证没有多个同一个code同时插入。不能保证数据库就没有code。

还有就是分布式锁的基本操作,try{}包一下后最终解锁。

总结

简答的话,数据库的唯一索引就好。但是唯一的业务逻辑比较烦的时候,考虑一下分布式锁。

相关推荐
阿丰资源9 分钟前
基于Spring Boot的电影城管理系统(直接运行)
java·spring boot·后端
呱牛do it18 分钟前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 8)
java
虹科网络安全33 分钟前
艾体宝产品|深度解读 Redis 8.4 新增功能:原子化 Slot 迁移(下)
数据库·redis·bootstrap
消失的旧时光-19431 小时前
Spring Boot 工程化进阶:统一返回 + 全局异常 + AOP 通用工具包
java·spring boot·后端·aop·自定义注解
NE_STOP1 小时前
Redis--发布订阅命令和Redis事务
java
PAC_3Dame1 小时前
记一次真实的线上OOM
java
有味道的男人2 小时前
对接亚马逊平台接口,商品全量信息一键抓取
数据库
SunnyDays10112 小时前
如何在Java中将Word文档转换为图像(JPEG、PNG或SVG)
java
Web极客码2 小时前
2026年Linux VPS安全加固清单:SSH、防火墙与审计就绪配置
运维·服务器·数据库
Lumos_7772 小时前
Linux -- 线程
java·jvm·算法