该问题应该可以归纳为并发导致的数据库数据重复

问题描述:在给移动端做一个收藏功能的时候,正常情况下,登录人点击收藏按钮调用收藏接口,再点击调用取消收藏接口,收藏时不能重复收藏(这个开始时是在程序里做的判断,即添加数据的时候根据登录人id和被收藏人的id来判断是否要收藏该人)。但是,当移动端猛戳收藏按钮的时候,就出现数据重复的情况。


解决办法:使用Hibernate 注释 唯一键约束 uniqueConstraints

@Table 注解包含一个schema和一个catelog 属性,使用@UniqueConstraints 可以定义表的唯一约束。

所以采用的是联合约束:

@Table(name="t_collection",
  uniqueConstraints = {@UniqueConstraint(columnNames={"登录人id", "被收藏人id"})})

添加了注解之后,在数据库里会出现索引信息


如果是单一字段约束可以用

@Table(name="tbl_sky",
  uniqueConstraints = {@UniqueConstraint(columnNames="month")})


网上其他分析及解决办法:

1、解决方法无非就是程序端控制和数据库本身来控制,程序端控制可以用同步锁或者自编的线程锁,或者有避免重复提交的拦截器等等,数据库控制就是使用唯一性标识,当sql中包含唯一性主键标识时,数据库执行n条sql还是会有先后的,这样后面的语句执行就会失败。

2、通过数据库表的唯一索引解决,虽然不能保证判断后可以插入(并发的问题)。 
但是肯定不会有重复记录

如果并发量非常小的话,线程就可以解决。

3、问题很简单:查询重复和真正的Insert并非原子操作(我说的是二者加在一起不是原则操作),因此如果两个线程在insert之前都做了同一个Key的查询,结果都是False,这时两个线程都做insert,问题就产生了: 

回避的方法: 
1、数据库字段设置唯一性,这样一定有一个是insert失败的; 
2、程序操作加Synchronized,但是不支持集群; 
3、使用分布式锁,比如Zookeeper(操作之前获取一把锁) 

注意的是这里实际上是无法使用select for update的,因为数据还没有,所以也没法加锁。

你可能感兴趣的:(该问题应该可以归纳为并发导致的数据库数据重复)