日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串

發(fā)布時間:2024/9/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

設(shè)計(jì)背景

由于歷史原因,線上庫環(huán)境數(shù)據(jù)量及其龐大,很多千萬級以上甚至過億的表。目標(biāo)是讓N張互相關(guān)聯(lián)的表 按照一張?jiān)幢頌榛?#xff0c;數(shù)據(jù)搬移歸檔 這里我們舉例N為50 每張表數(shù)據(jù)5000W


最差性能sql進(jìn)化客串

2表KeyName 字段意義 名稱等相同 從bug01 表中取出前500條不在bug02 表中的數(shù)據(jù)

最差性能:

?
SELECT TOP 500 a.KeyName FROM bug01 a LEFT JOIN bug02 b on a.KeyName = b.KeyName WHERE (a.KeyName not in (select distinct b.KeyName From bug02)) ORDER BY a.KeyName asc

進(jìn)化體在篇尾揭曉


詳細(xì)設(shè)計(jì)

問題點(diǎn):性能 安全 容錯

流程篇 為何如此設(shè)計(jì) 在下文中會解釋

step.1 源表數(shù)據(jù)過濾

這部分沒什么好說的 根據(jù)大家自己的業(yè)務(wù)場景設(shè)定不同的過濾規(guī)則

step.2 源表數(shù)據(jù)副本

程序的入口點(diǎn)肯定是源表了,擴(kuò)展表中的內(nèi)容都是以源表為Key來展開。那么這個展開的過程如何來做。

首先確定一些概念,這50表中的層級關(guān)系如何。可能直接和源表key鍵關(guān)聯(lián)的表只有10張。

例如我統(tǒng)計(jì)市內(nèi)所有圖書館詳細(xì)信息,那么我們以圖書館為源表。圖書館關(guān)聯(lián)書架、地址、會員信息。那么這3中信息我們分為一級別表。

書架關(guān)聯(lián)圖書類別,地址關(guān)聯(lián)街道信息,會員關(guān)聯(lián)用戶借閱信息,那么后面3者我們繼續(xù)分為二級表,......按照場景繼續(xù)擴(kuò)展。

方案1:使用游標(biāo) 循環(huán)源表 根據(jù)源表key值 處理和key相關(guān)的數(shù)據(jù)? 假設(shè)我們沒批次處理500跳源表數(shù)據(jù)

    也就是根據(jù)圖書館ID,遍歷所有節(jié)點(diǎn)。假設(shè)我們不分二級三級表,都是一級表 我們的insert操作次數(shù)是500*50。select操作同數(shù)據(jù)量

    這個給誰肯定都不大樂意,而且如果再遍歷2級表3級更難想象。

方案2:對源表key數(shù)據(jù)進(jìn)行集合,存進(jìn)變量,然后用in表達(dá)式。貌似可行。直接減少到1/500的操作次數(shù)。但是這里有個最恐怖的問題。

    變量都有長度,例如varchar 最大長度不能超過65535。

方案3:將源表Key做成一個查詢過濾池(相對于一級表 底層的sql where條件語句 下面會詳細(xì)介紹一下) 相對于第二種方案,我們這種似乎又將操作數(shù)提高了。

    不考慮層級的情況下,insert操作50。select操作50*2可以接受.

方案3擴(kuò)展: 對于一張大表來說 操作50次也不是什么可以樂觀的數(shù)字,并且這個50還有可能變成500,5000,50000。

      更有一個問題就是,當(dāng)你操作這500條的時候,可能會有數(shù)據(jù)干擾,你1秒前取得的這500條可不一定是1秒后的內(nèi)容。

      所以采取臨時表策略。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28     ?? CREATE TABLE #p ??? (????? ??????? OrderID varchar(50), ??????? primary key (OrderID)????? ??? ); ??? SET @temp_text = 'INSERT INTO #p '+@KeyText ??? --PRINT @temp_text ??? EXEC (@temp_text)?? ??? SET @KeyText = 'SELECT OrderID FROM #p' ??? --如果一級表關(guān)聯(lián)的操作次數(shù)比較多那么可以訪源表操作 以臨時表取代物理表 ??? SET @SubKeyText = 'select 一級表_A_被關(guān)聯(lián)鍵 From 一級表_A with(nolock) where 一級表_A_關(guān)聯(lián)源表鍵 in (' + @KeyText+')' ??? CREATE TABLE #q ??? (????? ??????? OrderID varchar(50), ??????? primary key (OrderID)????? ??? ); ??? SET @temp_text = 'INSERT INTO #q '+@SubKeyText ??? EXEC (@temp_text)?? ??? SET @SubKeyText ='SELECT OrderID FROM #q' ??? --如果一級表關(guān)聯(lián)的操作次數(shù)不多可以直接生成數(shù)據(jù)過濾池 ??? SET @SubKeyTextforA ='select 一級表_B_被二級關(guān)聯(lián)鍵 From 一級表_B with(nolock) where 一級表_B_關(guān)聯(lián)源表鍵 in (' + @KeyText+')' ??? SET @SubKeyTextforB ='select 一級表_C_被二級關(guān)聯(lián)鍵 From 一級表_C with(nolock) where 一級表_C_關(guān)聯(lián)源表鍵 in (' + @KeyText+')' ??? --如果存在更多層操作在此處可以繼續(xù)關(guān)聯(lián)資源過濾池 Demo只做到三層 SET @THKeyTextforA ='select 二級表_A_被三級關(guān)聯(lián)鍵 From 二級表_A with(nolock) where 二級表_A_關(guān)聯(lián)一級表鍵 in (' + @SubKeyTextforA+')'

--step.3 分表歸檔操作

這個環(huán)節(jié)的問題是安全 事務(wù)如何控制 事務(wù)的大小如何衡量 如何容錯 以及如何將程序做得可擴(kuò)展 可維護(hù)

大家根據(jù)業(yè)務(wù)場景 區(qū)分自己的批次范圍 拿蟲子這篇demo來說 50張千萬級大表 如果是批次5000條以上 事務(wù)要放在內(nèi)層處理 如果是5000條以下 可以放在最外層

事務(wù)的大小直接影響性能的波動

容錯的方案大家也可以自己設(shè)計(jì) 蟲子的程序員采用第三類表 異常表來重置 失敗了就插入 下一個批次直接就過濾

?
--將錯誤的批次訂單號入異常表 ??? Insert into 異常表(@ExTable) SELECT OrderID FROM #p --@ExTable用來存放異常數(shù)據(jù) 如果當(dāng)期批次出錯 則將本次批次訂單信息入庫@ExTable下一批次則過濾這些數(shù)據(jù)再執(zhí)行 ??? SET @KeyText = 'SELECT TOP '+CAST(@SynSize AS VARCHAR(10))+' '+@Base_Key+' FROM +
?
'+@BaseTable+'+ WHERE '+@Base_Key+' not in (select '+@Base_Key+' From '+@ExTable+') '

如何讓程序變的漂亮 可維護(hù)

我們在存儲過程中同樣可以使用面試對象的思想 只不過存儲過程沒有類這樣的概念給我們 那么我們不妨自己設(shè)計(jì)

用什么 還是臨時表

?
--一級 直接關(guān)聯(lián)源表主鍵 或?yàn)槎壉魂P(guān)聯(lián)的主表 ??? INSERT INTO #k VALUES ('一級表_A',@Base_Key,@KeyText,'')?????????????????? --一級表_A ??? INSERT INTO #k VALUES ('一級表_B',@Base_Key,@KeyText,'')?????????????????? --一級表_B ??? INSERT INTO #k VALUES ('一級表_C',@Base_Key,@KeyText,'')?????????????????? --一級表_C --二級 規(guī)則間接關(guān)聯(lián) ??? --@SubKeyText相關(guān) ??? INSERT INTO #k VALUES ('二級表_A','二級表_A_關(guān)聯(lián)一級鍵',@SubKeyText,'')??????????????? --二級表_A ??? INSERT INTO #k VALUES ('二級表_B','二級表_B_關(guān)聯(lián)一級鍵',@SubKeyText,'')??????????????? --二級表_B ??? INSERT INTO #k VALUES ('二級表_C','二級表_C_關(guān)聯(lián)一級鍵',@SubKeyText,'')??????????????? --二級表_C --特殊處理 ??? --自定義操作 ??? INSERT INTO #k VALUES ('特殊表','特殊表關(guān)聯(lián)鍵','自定義數(shù)據(jù)過濾方式','')?????????? ??? --其他 自增列處理 ??? --修改訂單,及其取消修改訂單狀態(tài)歷史表 ??? INSERT INTO #k VALUES ('自增表',@Base_Key,@KeyText,'自定義字段')

--step.4 處理細(xì)節(jié)

游標(biāo)循環(huán)臨時表 針對每一張表操作一次

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 DECLARE CUR_ORDERHEDER INSENSITIVE CURSOR FOR SELECT TableName,KeyName,temptext,colname FROM #k ??? OPEN CUR_ORDERHEDER ??? FETCH CUR_ORDERHEDER INTO @Cur_Table,@Cur_Key,@Cur_W,@Cur_K ??????? WHILE @@FETCH_STATUS = 0 ??????????? BEGIN????????????? ???????????????? EXECUTE P_Task_Sub_Synchronization ???????????????? @OutParam? = @OutParam OUT, @OutMessage = @OutMessage OUT, ??????????? @KeyText =? @Cur_W,@Table= @Cur_Table,@Extension=@Extension,@IsDelSource=@IsDelSource,@KeyName=@Cur_Key,@ColName=@Cur_K ???????????????? --SET @OutMessage = @OutMessage+@OutMessage ???????????????? --PRINT @OutMessage ???????????????? IF @OutParam <> 0? ???????????????????? BEGIN ??????????????????????? SET @OutMessage = @OutMessage + @Cur_Table +'操作失敗'???????????????????? ??????????????????????? ROLLBACK TRAN ??????????????????????? --將錯誤的批次訂單號入異常表 ??????????????????????? Insert into 異常表(@ExTable) SELECT OrderID FROM #p ??????????????????????? DROP TABLE #k ??????????????????????? DROP TABLE #p ??????????????????????? DROP TABLE #q ??????????????????????? RETURN ???????????????????? END?? ???????????????? FETCH CUR_ORDERHEDER INTO @Cur_Table,@Cur_Key,@Cur_W,@Cur_K ??????????? END ??? ClOSE CUR_ORDERHEDER ??? DEALLOCATE CUR_ORDERHEDER??????

--step.5 資源釋放

--step.6 流程處理

這2個部分就不詳細(xì)說了?


最差性能sql進(jìn)化過程

step.1 not in了 就別再distinc了 distinc和not in都是臭名昭著的角色 not in后+dinstinc畫蛇添足而已

改后sql:

SELECT TOP 500 a.KeyName FROM bug01 a LEFT JOIN bug02 b on a.KeyName = b.KeyName WHERE (a.KeyName not in (select? b.KeyName From bug02)) ORDER BY a.KeyName asc

step.2 別名 別小看別名 用圖來說話 原sql計(jì)劃

改后sql:

SELECT TOP 500 a.KeyName FROM bug01 a LEFT JOIN bug02 b on a.KeyName = b.KeyName WHERE (a.KeyName not in (select? c.KeyName From bug02 c)) ORDER BY a.KeyName asc

step.3 何必要用外聯(lián) 直接過濾不就得了 嘿嘿

改后sql:

SELECT TOP 500 a.KeyName FROM bug01 a WHERE (a.KeyName not in (select? c.KeyName From bug02 c)) ORDER BY a.KeyName asc

step.4 根據(jù)luofer同學(xué)的建議 再進(jìn)化一次 直接EXCEPT

SELECT TOP 500 a.KeyName FROM bug01 a except SELECT b.KeyName from bug02 b

熬夜的蟲子:http://www.cnblogs.com/dubing/archive/2011/11/11/2245836.html

總結(jié)

以上是生活随笔為你收集整理的针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。