向一张表中插入数据,加了Transaction。在Insert之前先用查询判断数据是否已经在数据库中存在(业务主键)。若数据量大比较耗时,前一次Transaction还未完成就再次插入同样的数据,就会导致业务主键重复。这问题该怎么解决?
1. 业务主键加唯一约束。但逻辑删除的数据可能会违反约束。
2. 加表锁。整张表都锁住,也不太好啊。
求助!!!
貌似可以用lock
你难道没办法先检查所有插入的数据,把重复的排除?
是这样的,插入之前做过重复判断。第一次插入,Transaction还没完成。所以第二次插入前数据库中还没,这样就导致重复了。
@心亦: 还原一下你的场景,
1、多线程。
2、只要是多线程,不管是大数据,还是小数据,理论上都存在第一次插入没完成的情况下,
第二次检测已经发生。就是说不管你执行速度有多快,0.1NS也好,都存在并发考虑。
3、那么为啥会出现一线程和二线程有相同的业务数据,能不能在这个上面思考一下呢?
你是想解决问题呢,还是只想从一个方向解决问题?
@爱编程的大叔: 这个业务数据是用户提供,也没法控制。
最后的问句没读懂啊。想解决问题的
@心亦: 那么再问一句,瓶颈是数据库,还是CPU计算能力?
1、如果是数据库的话,可以考虑不要多线程。
2、业务数据用户提供?包括主键?(比如像我们使用的主键是无任何意义的GUID,与业务无关)
3、延时5分钟,排队(类似MSMQ),只用一个线程(或者多线程也行),这样5分钟内的数据可以先
保证这部分数据是不重复的。(不会出现一线程没完成的业务,和二线程的检测无效的问题)
4、即便你的信息比较不方便透露,但是你可以将一些关键点说下的,
比如数据怎么来:每分钟过来500条数据,每天24小时不间断。
数据怎么组织? 比如说数据是 客户、日期、金额,单据号。单据号可能重复,重复的话使用第一条还是第二条,因为什么原因重复了,能不能在前一道工序上去重?
为啥主键是没法自行控制的?
为啥会出现同样的业务数据?
能不能插入前(我指不通过数据库检测直接去重复)排除?
数据处理的时效性要求?
@爱编程的大叔:
1 数据库单线程的方法请指教一下。
2 是用户提供主键
3 引入队列好像会更加复杂
4 数据是wcf post来的,用户频繁操作两次导致了这个问题。要在代码去重,那肯定要将一定时间内的数据缓存起来,和3的方案相同吧。
@心亦: 好,那么问题可以不可以这样理解。
1、多线程,一线程、二线程。
2、插入数据,假设是一条就行,都存在并发冲突的问题,与事务无关。
3、数据库是可以限制主键重复的,也就是第二次插入直接就ERROR了。
4、那么这个逻辑哪一步是你不能接受的。
Insert into PrimaryID, Title values (1, "测试") 一线程
Insert into PrimaryID, Title values (1,"其他测试") 二线程
这样第二次插入直接就出错了。
5、还是说,你那个所谓的业务主键,根本就不是数据库主键,数据库是允许重复的?
6、如果是第5条所说的情况,那么事后补救是否可行,就是另外运行一个线程,专门检查业务数据重复,发现重复删除。
@爱编程的大叔: 用户输入的业务主键并没有唯一约束,因为允许逻辑删除后再添加一条业务主键相同的数据
@爱编程的大叔: 谢谢,有没有在sql中单线程的方法?类似于c#的lock。C#代码中不能用是因为有负载均衡,多台server同时跑。
@心亦: 逻辑删除就是只做标识,不从数据库删除是吧。
那么延时处理,事后处理方式呢?
@心亦: 负载均衡,这个说到要点了。你必须告诉别人,什么是你不能承受的,或者是不可修改的。
1、多应用服务器-单数据库。
2、如果重复插入了,后期删除会造成什么问题?
3、数据库锁定你可以看这篇文章:
可以锁定表的,但不知道你是否可以承受?
@爱编程的大叔: 是的。
这样也是一种方案。但这个发生频率不是很高,搞个线程一直跑不太好吧。
@爱编程的大叔: 谢谢。嗯,还是利用数据库的锁好些
试试园子里这篇博文中的方法:多线程重复插入数据检测SQL
谢谢dudu解答。但是这篇博文中的方案二应该也建了唯一索引的,否则Try Catch就没有意义了。
@心亦: 那就加唯一索引,逻辑删除怎么会违反唯一索引
@dudu: 一条数据被逻辑删除后,可能会再插入一条相同业务主键的数据,这样的操作应当是被允许的。但这样是违反唯一索引的。
@心亦: 这个就是设计上的考虑了,基于什么样的原因,需要使用相同的业务主键?
理论上来说,就算是同样一张单据(内容相同),删除和新建的单据我们都是采用不同的主键的。
@爱编程的大叔: 这个相当于一个名字之类的东西,用户来输入的,还需要唯一。
@心亦: 好吧,我知道你这是国家一级保护项目,连场景都不能说的。这样真没办法了。
你需要
这两个插入操作时在两个Transaction中的,他们对彼此是不可见的。merge into不是解决这种问题的吧。可否给个例子参考?
1、在查询的表上建相应的索引以提高查询效率
2、在插入的目标表上先禁用索引,插入完毕后再启用。
或者
将查询出的结果集(60W)数据导入到 MDB 文件中。然后再将MDB中的数据导入目标表中。
利用导入导出工具 调用批量插入(bulkinsert)的方式 提高速度。
谢谢,不过现在不是查询速度的问题,是会插入重复数据的问题。
业务主键冲突..你看能不能这样..在Trans insert 方法里操作一个全局变量 ,在方法的最后一条语句,将这个变量设置为true,表示方法已执行完成...当下次再调用这个方法之前,先判断全局变量是否为true,如果不..就等到为true再调用.
1、单独建个只含业务主键的表,使用唯一索引,在插入数据前先在这个表里插入主键
2、写数据时不直接操作数据库,而是把要插入的数据提交到接口,在接口程序中屏蔽重复数据