連續 Update 資料導致 MSSQL 6.5 停止回應 |
缺席
|
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
用程式跑計算, 連續約一萬多筆紀錄計算出來的數值部為零的就去更新其中一格欄位, 無論我用 Edit and Post 的方式或是下 SQL Update 的方式更新, 大約跑個一千筆紀錄後, 所有其他的 Client 就都斷線了. 真不知道是什麼原因? 總會出現與 TempDB 相關的錯誤訊息. 我是雪龍
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
Mickey
版主 發表:77 回覆:1882 積分:1390 註冊:2002-12-11 發送簡訊給我 |
|
Chance36
版主 發表:31 回覆:1033 積分:792 註冊:2002-12-31 發送簡訊給我 |
bestlong 你好
不知你可不可以說明一下,在運算過程中,同時用了多少TDataset的元件(TTable、TQuery、TAdoxxx、...),sql的內容為何,包括運算時會用到或Form上會顯示的,還有每個DataSet的資料量如何?最好將與運算有關程式碼貼出,這樣才好找問題,照你所述,是不是太跨張了吧,除非你的Server中毒。
去年我也為了這類的問題困擾了將近半年,從八千筆跑20分鐘到兩萬筆跑30分再到八萬筆跑了40分鐘,然後10萬筆是26分鐘,最後一次是25萬筆要60分鐘,不是很滿意,也不得不接受了。
在與程式競速的過程當中,試了很多方法,改了很多流程,最後歸納出幾點原則
1.從運算流程著手並在文件上沙盤推演,讓每個動作都不浪費,所有的資料在運算過程中只要讀取一次,不可回頭重跑一次迴圈,。
2.運算開始,把不必要且佔資源的物件予關閉或釋放,完成後再予以還原。
3.運算過程中使用的變數或物件,只要建立一次並重複使用,待完成運算後再予以釋放。
4.若非必要僅量減少函式或程序的使用,真要使用以使用程序為原則,並減少參數的傳遞。
5.DataSet的部份少用直接連結資料庫的元件(如TQuery、TTable),除了更新資料庫的部份,意思就是TQuery只要有一個(更新比較快),最好用SQLUpdate,其他參與運算用的則以可以離線處理的DataSet為宜(如ClientDataSet、TAdoDataSet),但一次取得的資料筆數,你要計算記憶體餘量,批次取得,算完再取下一批
6.避免或減少畫面的更新動件
7.被更新的資料一定要有索引(Primary與更新Key不同時),若需要在記憶體內搜尋的DataSet,也一定要建臨時的索引。
8.若有不足,等想到再說。 以上幾點能做到,應該就可以有好的更新速度了,若還不行,那就表示[該換電腦了],電腦升級的優先順序,1.Ram速度及大小 2.硬碟的存取速度 3.才是cpu的速率及廠牌及型式 4.Server 最好為雙CPU以上(SQL Server 2000 有支援) PS:有沒有覺得好像這些你都知道,對不對?
|
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
MS-SQL6.5 主機的環境 PIII800雙CPU 512M
該 Table 結構如下
/****** Object: Table dbo.pm_saft_qty_analy Script Date: 04/02/2004 上午 09:47:06 ******/
if exists (select * from sysobjects where id = object_id('dbo.pm_saft_qty_analy') and sysstat & 0xf = 3)
drop table dbo.pm_saft_qty_analy
GO /****** Object: Table dbo.pm_saft_qty_analy Script Date: 04/02/2004 上午 09:47:06 ******/
CREATE TABLE dbo.pm_saft_qty_analy (
part_num varchar (20) NOT NULL CONSTRAINT DF_pm_safe_qt_part_num_1__96 DEFAULT (' '),
part_stk varchar (8) NOT NULL CONSTRAINT DF_PM_SAFE_QT_part_stk_2__96 DEFAULT (' '),
saft_qty int NOT NULL CONSTRAINT DF_pm_saft_qt_saft_qty_3__96 DEFAULT (0),
q1 int NOT NULL CONSTRAINT DF_pm_safe_q1_4__96 DEFAULT (0),
q2 int NOT NULL CONSTRAINT DF_pm_safe_q2_5__96 DEFAULT (0),
q3 int NOT NULL CONSTRAINT DF_pm_safe_q3_6__96 DEFAULT (0),
q4 int NOT NULL CONSTRAINT DF_pm_safe_q4_7__96 DEFAULT (0),
m01 int NOT NULL CONSTRAINT DF_pm_safe_qt_m01_8__96 DEFAULT (0),
m02 int NOT NULL CONSTRAINT DF_pm_safe_qt_m02_9__96 DEFAULT (0),
m03 int NOT NULL CONSTRAINT DF_pm_safe_qt_m03_10__96 DEFAULT (0),
m04 int NOT NULL CONSTRAINT DF_pm_safe_qt_m04_11__96 DEFAULT (0),
m05 int NOT NULL CONSTRAINT DF_pm_safe_qt_m05_12__96 DEFAULT (0),
m06 int NOT NULL CONSTRAINT DF_pm_safe_qt_m06_13__96 DEFAULT (0),
m07 int NOT NULL CONSTRAINT DF_pm_safe_qt_m07_14__96 DEFAULT (0),
m08 int NOT NULL CONSTRAINT DF_pm_safe_qt_m08_15__96 DEFAULT (0),
m09 int NOT NULL CONSTRAINT DF_pm_safe_qt_m09_16__96 DEFAULT (0),
m10 int NOT NULL CONSTRAINT DF_pm_safe_qt_m10_17__96 DEFAULT (0),
m11 int NOT NULL CONSTRAINT DF_pm_safe_qt_m11_18__96 DEFAULT (0),
m12 int NOT NULL CONSTRAINT DF_pm_safe_qt_m12_19__96 DEFAULT (0),
year_total int NOT NULL CONSTRAINT DF_pm_safe_qt_year_total20__96 DEFAULT (0),
saft_qty_new int NOT NULL CONSTRAINT DF_pm_safe_qt_new_21__96 DEFAULT (0),
CONSTRAINT PK___1__96 PRIMARY KEY CLUSTERED
(
part_num
)
)
GO 只用到一個 TADODataSet (select * from pm_saft_qty_analy
order by part_num) 連接 Table 我是雪龍 發表人 - bestlong 於 2004/04/02 10:06:32
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
|
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
計算部分的程式碼
procedure TFPM0_0G0000.Button2Click(Sender: TObject);
begin
screen.Cursor := crSQLWait;
data_m.DisableControls;
try
if not data_m.Active then data_m.Active := true;
CalcNewSaftQty;
finally
data_m.EnableControls;
screen.Cursor := crDefault;
end;
end; procedure TFPM0_0G0000.CalcNewSaftQty;
var
ss, si: string;
i, iCnt: integer;
begin
cmd_m.CommandText := 'update pm_saft_qty_analy set saft_qty_new = 0 ';
cmd_m.Execute; ss := inttostr(data_m.RecordCount);
while not data_m.Eof do
begin
iCnt := 0;
for i := 1 to 12 do
begin
si := rightstr('00' inttostr(i), 2);
if data_m.FieldByName('m' si).AsInteger <> 0 then
begin
data_t.Active := false;
data_t.CommandText := 'select * from pm_saft_qty_config '
' where part_stk like ''' data_m.FieldByName('part_stk').AsString '%'' '
' and qty_level <= ' data_m.FieldByName('m' si).AsString
' order by qty_level DESC';
data_t.Prepared;
data_t.Active := true;
if data_t.RecordCount > 1 then
begin
iCnt := iCnt (data_m.FieldValues['m' si] * data_t.FieldValues['m' si]);
end else
begin
iCnt := iCnt 0;
end;
end else
begin
iCnt := iCnt 0;
end;
end;
iCnt := Round(iCnt / 1000) * 1000; //千位數以下無條件捨去
if iCnt <> 0 then
begin
cmd_m.CommandText := 'update pm_saft_qty_analy '
' set saft_qty_new = ' inttostr(iCnt)
' where part_num = ' Quotedstr(data_m.FieldByName('part_num').AsString);
cmd_m.Execute;
end;
FMain.st_main.Caption := ':' inttostr(data_m.RecNo) '/' ss;
application.ProcessMessages;
data_m.Next;
end;
end; 另外查詢的 Table 結構為
/****** Object: Table dbo.pm_saft_qty_config Script Date: 04/02/2004 上午 10:02:34 ******/
if exists (select * from sysobjects where id = object_id('dbo.pm_saft_qty_config') and sysstat & 0xf = 3)
drop table dbo.pm_saft_qty_config
GO /****** Object: Table dbo.pm_saft_qty_config Script Date: 04/02/2004 上午 10:02:34 ******/
CREATE TABLE dbo.pm_saft_qty_config (
part_stk varchar (8) NOT NULL CONSTRAINT DF_PM_SAFE_QT_Part_stk_8__96 DEFAULT (' '),
qty_level int NOT NULL CONSTRAINT DF_PM_SAFE_QT_qty_level_7__96 DEFAULT (0),
m01 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m01_3__96 DEFAULT (0),
m02 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m02_4__96 DEFAULT (0),
m03 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m03_5__96 DEFAULT (0),
m04 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m04_6__96 DEFAULT (0),
m05 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m05_7__96 DEFAULT (0),
m06 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m06_8__96 DEFAULT (0),
m07 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m07_9__96 DEFAULT (0),
m08 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m08_10_96 DEFAULT (0),
m09 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m09_11_96 DEFAULT (0),
m10 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m10_12_96 DEFAULT (0),
m11 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m11_13_96 DEFAULT (0),
m12 float NOT NULL CONSTRAINT DF_PM_SAFE_QT_m12_14_96 DEFAULT (0),
CONSTRAINT PK___2__96 PRIMARY KEY CLUSTERED
(
part_stk,
qty_level
)
)
GO 我是雪龍 發表人 - bestlong 於 2004/04/02 10:07:39 發表人 - bestlong 於 2004/04/02 10:10:03
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
計算時, pm_saft_qty_analy 記錄數有 11420筆
不過計算後有 update saft_qty_new 的紀錄數只有 871筆 (select count(*) from pm_saft_qty_analy where saft_qty_new > 0) 目前無法判定為何計算程式執行後的所有電腦都會出現的錯誤訊息
要將 MS-SQL6.5 重新啟動才行 我是雪龍 發表人 - bestlong 於 2004/04/02 10:25:47
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
經過仔細的推敲目前認定的是計算程式的其中一段造成問題
data_t.CommandText := 'select * from pm_saft_qty_config ' +
' where part_stk like ''' + data_m.FieldByName('part_stk').AsString + '%'' ' +
' and qty_level <= ' + data_m.FieldByName('m' + si).AsString +
' order by qty_level DESC'; 這樣會導致 SQL Server 會在 TempDB 中建立臨時表來做處理, 所以每運算一次就會建立一次所以導致 TempDB 的 Log 清除的速度跟不上 產生的速度導致空間用盡進而造成 MS-SQL 對之後的所有要求停止回應. 我是雪龍
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
Chance36
版主 發表:31 回覆:1033 積分:792 註冊:2002-12-31 發送簡訊給我 |
bestlong 你好
試著改成如下程式碼看看.
procedure TFPM0_0G0000.CalcNewSaftQty; var ss, si: string; i, iCnt: integer; begin cmd_m.CommandText := 'update pm_saft_qty_analy set saft_qty_new = 0 '; cmd_m.Execute; // 搬到這裏,使用參數方式 data_t.Active := false; // data_t.CommandText := 'select qty_level, :mxx from pm_saft_qty_config ' + // 可以改這樣,但還要再試試看 data_t.CommandText := 'select * from pm_saft_qty_config ' + ' where part_stk like :part_stk'+ ' and qty_level <= :qty_level' + ' order by qty_level '; // data_t.Prepared := True; //Prepare只要一次 ss := inttostr(data_m.RecordCount); while not data_m.Eof do begin iCnt := 0; for i := 1 to 12 do begin si := rightstr('00' + inttostr(i), 2); if data_m.FieldByName('m' + si).AsInteger <> 0 then begin data_t.Active := false; data_t.Parameters.ParamByName('part_stk').AsString := data_m.FieldByName('part_stk').AsString + '%' ; data_t.Parameters.ParamByName('qty_level').AsInteger := data_m.FieldByName('m' + si).AsString ; data_t.Active := true; if data_t.RecordCount > 1 then begin data_t.Last; // 加入這行因為沒用DESC排序 iCnt := iCnt + (data_m.FieldValues['m' + si] * data_t.FieldValues['m' + si]); end else begin iCnt := iCnt + 0; end; end else begin iCnt := iCnt + 0; end; end; iCnt := Round(iCnt / 1000) * 1000; //千位數以下無條件捨去 if iCnt <> 0 then begin cmd_m.CommandText := 'update pm_saft_qty_analy ' + ' set saft_qty_new = ' + inttostr(iCnt) + ' where part_num = ' + Quotedstr(data_m.FieldByName('part_num').AsString); cmd_m.Execute; end; FMain.st_main.Caption := ':' + inttostr(data_m.RecNo) + '/' + ss; application.ProcessMessages; // 這行可以的話最好拿掉 data_m.Next; end; data_t.Prepared := False; end;ps:注意+為全形的加號 發表人 - chance36 於 2004/04/03 03:19:41 |
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
Chance36 你好 看到您改的部分, 應該是同時著重於效率的提昇, 目前關於前述會產生問題的程式碼排序的部分, 目前問題的癥結可以算是很肯定了.
而其中您修改的程式碼中改用 Last 的方式來避免排序作業這樣將會有資料庫紀錄內容尚未排序的可能性而導致計算錯誤(這是我很擔心的部分), 目前考量的重點就是避免造成 SQL Server 的停止回應的問題, 解決方案有兩個方式
1. 將 pm_saft_qty_config Table 複製到 Client PC 的暫存資料庫來輔助運算以減低 SQL Server 的負擔.
2. 將 TempDB 由原來的建立在記憶體中, 改成建立在實體的磁碟機並加大容量來處理, 不過需要使用具有高速 I/O 功能的磁碟機以避免效能的降低. 找了很久看到國外的資料才知道, MS-SQL 6.5 的 TempDB 是每分鐘 Truncate Log 一次的, 不知道 MS-SQL 6.5 版之後是否也如此. 我是雪龍
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |