-
場景:
我們程序現在改成多線程了,我現在需要把臨時表中的數據給插入到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這個數值的。
這樣就可以避免沖突了。