解决多线程调用sql存储过程问题
-
场景:
我们程序现在改成多线程了,我现在需要把临时表中的数据给插入到TABLE_M中,但这时候可能其他的线程也在插入,我就不能用之前我们的方案了(select max(oid) from Tuning.TABLE_M。。。,之后去维护主子表关系,改掉TABLE_M的OID的自增属性,插入,之后再添加上自增属性。。。。。。)
我现在是想这样操作:
1,)先插入把临时数据插入到TABLE_M(OID是自增的)中(Insert Into ….select …),我之后获取 select Scope_Identity() as @MaxTABLE_MOID,之后根据这个@MaxTABLE_MOID去修改临时表中的TABLE_MOID.
1.1,)(1,)这种方案会不会出现以下问题:
A线程, 1-10 共10条记录,
B 线程,1-10 共10条记录
A线程插入的时候,
B线程也在再插入
A线程 与 B线程是交互的插入,A线程插入一条,B线程插入一条
运行起来像这样:
比如 此时TABLE_M的OID为 100
A线程插入第一条 TABLE_M的OID变为 101
B线程插入第一条 TABLE_M的OID变为 102
当我A线程插入完的时候,我A线程最后一条记录插入的OID为 120
这时候我的Temp 要到这个120 去修改字表关系
TABLE_M(OID=120) 是A 插入的没问题
TABLE_M(OID=119)是B插入的,这时却关联到了A线程对应的临时表的第9条记录
悲哀!!!
1.2,)我们是不是要在插入的时候使用With(lock)
2,)我还有一种想法,就是先插入完成后,根据条件查询出该批数据插入TABLE_M的生成的OID,之后这些OID去和temp匹配(按照顺序匹配,因为插入temp肯定是有序的插入),之后修改temp中的TABLE_MOID,这样就可以把子数据插入到TABLE_M_LteCell,TABLE_M_GsmCell,TABLE_M_TdsCell中。
-
DBA提供解决方案
我们可以单独做一个表来进行OID自增的维护,可以暂时称为MAX_OID表。最简单的方式是里面仅仅记录:当前最大的OID。
这样我们在做多线程操作的时候遵循以下步骤
1. 取得MAX_OID表中的值,这样就可以获得自增的起始序列号
2. 获得此次插入时候的临时表的最大行数。不要使用select count(1) from table,使用如下语句,可以获得最大的性能。
直接获取表行数sql语句:
select sum(row_count)
from sys.dm_db_partition_stats
where index_id<1 and object_id=object_id('表名')
3. 使用max_oid+临时表的表行数,可以得到将临时表插入到主表之后,主表未来最大的MAXOID号
4. 更新MAX_OID表。将表内数据改为:max_oid+临时表的表行数
特别注意:为了保证事务一致性,上述步骤需要在一个事务中完成,需要以下语句
SET XACT_ABORT ON
begin tran
执行的sql语句(即上面1-4的逻辑操作)
commit
以上四个步骤虽然文字上比较啰嗦,但是实际操作会非常快。使用的全是系统表来读取,肯定是在毫秒级别,不会影响性能。
这样,每个线程读取的都是MAX_OID表中的值,就不会产生冲突了。
如下面的例子:
目前MAX_OID 最大值为100.
线程一的临时表为30行
线程二的临时表为40行
线程一:
1.读取MAX_OID 表。得到100
2.获得临时表行数:30
3.得到预测的maxoid为100+30=130
4.将MAX_OID 更改为130.
线程二:
1.读取MAX_OID 表。得到130
2.获得临时表行数:40
3.得到预测的maxoid为130+40=170
4.将MAX_OID 更改为170.
注意:线程二得到的MAX_OID 一定是130,而不是100.因为线程一已经更新了这个值。且更新的过程中加锁。线程二是读取不到100这个数值的。
这样就可以避免冲突了。
基础才是编程人员应该深入研究的问题,比如:
1)List/Set/Map内部组成原理|区别
2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
3)JVM运行组成与原理及调优
4)Java类加载器运行原理
5)Java中GC过程原理|使用的回收算法原理
6)Redis中hash一致性实现及与hash其他区别
7)Java多线程、线程池开发、管理Lock与Synchroined区别
8)Spring IOC/AOP 原理;加载过程的。。。
【+加关注】。