java 数据库并发插入引起的数据重复插入问题,哪位大佬解答下?

最近在做的是一个通话相关的网站。主要功能是对接硬件厂商,我们提供在网页上一键拨号的功能,项目使用的ssm,数据库是sql server2014。

引起问题的场景如下:

在网页上有一个长连接页面,当通话结束的时候页面会发起一个请求到后端服务器插入一条通话记录。通话记录的主要字段有:

keyId,自增主键,无实际业务含义。

id,通话记录的主键。代表了一通电话。每通通话仅有一个。

正常拨打外部电话是没有问题的。问题出现在内部通话中,如:

同事A与同事B打电话,当通话结束的时候两个页面会同时向后端发送请求添加记录,这个时候后端就会有两条记录,但是业务需要只保留一个通话记录就可以了。

第一次采取的措施是先查询再插入,但是运行后发现有两条通话记录的出现。这个时候猜想可能是因为并发的问题,当a线程经过查询判断后还未插入的时候b线程也经过查询数据库发现没有记录,这时候就造成了两条通话记录。

第二次采取的方法是先查询,若查询无结果后插入数据并返回自增主键,插入数据后by 通话记录主键和自增主键查询是否有id(通话记录主键)相同和自增keyId(自增主键)比当前插入记录的keyId小的记录,若有,就删除。

但是实际运行后发现还是有两条通话记录的出现,只不过比之前降低了一部分。

求大佬解答。

注: 所有的查询都是不加锁(NOLOCK)的。

再备注:后端服务器有六台,通过f5和nginx进行负载均衡

并发问题可试试synchronized同步代码块,即加同步锁,主要功能如下:
当a用户线程访问对象的synchronized代码块时候,b用户线程仍然可以访问对象方法中其余非synchronized块的部分,
当a用户线程进入对象的synchronized代码块的时候,
b用户线程如果要访问这段synchronized块,
那么访问将会被阻塞。利用这应该能防止出现一样的记录

系统有用REDIS吗 ? 由于A和B结束通话都会发起请求 , 所以在请求进来的时候把通话记录主键插入到REDIS (setnx expire) , 能插入成功就执行数据库持久化操作 , 不成功就直接返回

为什么不只由通话的发起方发送请求保留记录呢?为什么要两段都发送保存请求呢

可以用唯一索引来解决的