隨著近幾年混合負(fù)載/實(shí)時(shí)報(bào)表的需求推動(dòng),極端事務(wù)處理(XTP)被越來越多地需要。它已在第5部分的一開始即通過OLTP增長的曲線而展示(可配置的DSS查詢優(yōu)化)。
XTP與OLTP是不同的,正如OLTP也與DSS不同。當(dāng)然,一些系統(tǒng)需要支持XTP和OLTP—頗像“混合負(fù)載”系統(tǒng)支持了OLTP和有限的DSS需求。XTP和OLTP的一些不同之處是OLTP主要關(guān)注的是高并發(fā)量的短小查詢和小事務(wù),而XTP則關(guān)注更少的并發(fā)量,但關(guān)注趨近于連續(xù)的數(shù)據(jù)插入和實(shí)時(shí)的查詢結(jié)果,要么是高度并發(fā)的,要么是持續(xù)時(shí)間短的—通常都在非常的數(shù)據(jù)流上。例如,OLTP會(huì)有數(shù)百個(gè)用戶,查詢響應(yīng)時(shí)間為1 s或低于1 s可能是能被輕松接受的訪問的數(shù)據(jù)是數(shù)天、數(shù)月甚至是數(shù)年之久的。在XTP系統(tǒng)中,查詢需要小于10 ms范圍的響應(yīng)時(shí)間通常針對(duì)僅僅數(shù)秒前的數(shù)據(jù)。類似地,數(shù)據(jù)持久化是OLTP中ACID的重要考慮,在需要的數(shù)據(jù)狀態(tài)達(dá)到前,XTP中的數(shù)據(jù)持久化可能不被考慮例如預(yù)訂了的交易訂單。因?yàn)闊o需或推遲了持久化在失敗后的事務(wù)恢復(fù)需求也大大放松了。
因此正如在相同的系統(tǒng)上不同的應(yīng)用程序需要考慮使用不同的應(yīng)用程序情形:allrows_oltp、allrows_mix或allrows_dss,非常高事務(wù)處理的應(yīng)用程序需要考慮啟動(dòng)ASE 15.0中的一些新特性來取得優(yōu)勢。另外,在一些并非如此極端處理需求的應(yīng)用程序中可能也會(huì)受益,如果原先的應(yīng)用程序情形是OLTP主導(dǎo)的。例如,Sybase自己的復(fù)制服務(wù)器產(chǎn)品就因利用了這些特性而表現(xiàn)出吞吐量提升(文字自動(dòng)參數(shù)化語句緩存和延遲提交)。
考慮到極端事務(wù)處理的需求,ASE利用了其中的幾個(gè)特性來專門解決在當(dāng)今的ASE應(yīng)用程序中成為妨礙高容量OLTP的瓶頸。這些瓶頸包括:
(1)與執(zhí)行時(shí)間比較,對(duì)簡單查詢和DML語句的額外優(yōu)化開銷。
(2)等待完成先記錄日志的時(shí)間拖延,以便保證事務(wù)持久性
(3)應(yīng)用程序在臨時(shí)數(shù)據(jù)庫工作表上的額外物理IO。
正如以前提及的,查詢的優(yōu)化時(shí)間可能大大超過其執(zhí)行時(shí)間。雖然可以在復(fù)雜查詢中得以驗(yàn)證但甚至在簡單查詢和大部分DML操作時(shí)都是如此。例如,對(duì)一張包含普通索引的表進(jìn)行單一插入,根據(jù)索引的數(shù)量,通常僅需要25~30的邏輯IO和3~5物理寫入。如果將寫入緩存—如果不包含事務(wù)日志—?jiǎng)t整個(gè)插入將完全在內(nèi)存中。內(nèi)存操作僅需納秒級(jí)別(即使是物理IO的服務(wù)時(shí)間也可在2~6 ms范圍內(nèi)完成),所以單一插入在最壞的情況下也可能在幾毫秒內(nèi)。然而優(yōu)化需求(例如,分區(qū)消除、決定聚集索引或堆等)可能會(huì)超出很多。通過利用文字參數(shù)化的語句緩存,插入SQL命令的處理將會(huì)極大降低分析所帶來的額外開銷。
和存儲(chǔ)過程一樣,必須注意緩存的語句,因?yàn)槿绻淖种蹈淖兞?,緩存的查詢?jì)劃可能并非優(yōu)化的。例如,考慮常見的數(shù)據(jù)區(qū)間不同(1個(gè)星期與1年比較)與存儲(chǔ)過程優(yōu)化的問題…文字參數(shù)化的語句緩存也會(huì)遇到同樣的問題因此,可能需要保證語句緩存進(jìn)針對(duì)更可預(yù)測的OLTP情況啟動(dòng),可使用之前展示的登錄觸發(fā)器方法。像復(fù)制服務(wù)器(Replication Server)這樣的應(yīng)用程序可明確地受益,因?yàn)樗荄ML密集的。
在消除了優(yōu)化的問題后,下一個(gè)主要的瓶頸就是事務(wù)日志。多年前,日志競爭通過用戶日志緩存(User Log Cache)的實(shí)現(xiàn)而緩解了。在大多數(shù)XTP應(yīng)用程序中,一小部分的電子傳輸驅(qū)動(dòng)了常常與大量用戶并發(fā)讀取捆綁的DML需求。因此,事務(wù)日志的競爭并非大問題—實(shí)際上數(shù)據(jù)持久化并非要素,所以整個(gè)記錄事務(wù)的概念就無需額外開銷了。使用先寫入日志的問題是每個(gè)語句必須等待物理IO才能被刷新至磁盤。這對(duì)使用小批量的原子插入來降低網(wǎng)絡(luò)時(shí)間的情況是成立的。考慮以下序列:insert into table (
無論它使用腳本顯式創(chuàng)建的—還是通過java.sql.Statement的addBatch()方法創(chuàng)建的,每個(gè)插入都代表了一個(gè)原子事務(wù)。結(jié)果就是,在下一個(gè)插入發(fā)生前,它必須等待上一個(gè)插入的提交—也就意味著必須等待最后一個(gè)日志頁面被刷新至磁盤。
ASE 15.0中新增了延遲提交數(shù)據(jù)庫/會(huì)話選項(xiàng)以規(guī)避該問題。支持事務(wù)在ULC刷新至日志緩存完成后即可提交,即在最后一個(gè)日志頁被物理刷新至磁盤前。注意它指的僅是最后一個(gè)日志頁面—所以在典型的ASE中,頁面大小為2 KB,ULC為4 KB,充滿的ULC仍必須等待至少一個(gè)日志頁被刷新—而非兩個(gè)。因此,該選項(xiàng)對(duì)完全能被一個(gè)單獨(dú)的日志頁面容納的事務(wù)最為有效—雖然較長的事務(wù)也可能小程度受益。
為了展示影響,請查看下表:
create table batch_inserts (
row_id varchar(45) not null,
logical_server varchar(30) not null,
client_host varchar(30) not null,
thread _num smallint not null,
thread_row int not null,
client _timestamp datetime not null,
column _06 int not null,
column _07 float not null,
column _08 int not null,
column _09 float not null,
column _10 int not null,
column_11 int not null,
column _12 int not null,
column _13 int not null,
column _14 char(10) not null,
test_string varchar(80) null,
constraint batch _inserts _PK primary key (row_id)
)
lock datarows
go
create index host_thread_idx
on batch_inserts (client_host, thread_num, thread_row)
go
create index server_time_idx
on batch_inserts (logical_server,client _timestamp)
go
grant all on batch_inserts to public go
使用該模式,我們將從一個(gè)運(yùn)行了JDBC驅(qū)動(dòng)程序的客戶端機(jī)器上運(yùn)行兩組測試:
原子插入—10個(gè)客戶端線程使用單獨(dú)的查詢語句(非批量)來執(zhí)行1萬次原子插入。
50批量插入—10個(gè)客戶端線程將用50個(gè)查詢?yōu)橐慌鷣韴?zhí)行1萬行插入(例如,客戶端將發(fā)送200批SQL,每批包含50個(gè)插入語句)。
每組測試將包含6個(gè)查詢流格式:
(1)標(biāo)準(zhǔn)插入語句命令
(2)啟動(dòng)了語句緩存
(3)啟動(dòng)了延遲提交
(4)啟動(dòng)了語句緩存和延遲提交
(5)動(dòng)態(tài)SQL (完全預(yù)備語句)
(6)動(dòng)態(tài)SQL且啟動(dòng)了延遲提交
由于語句緩存提供了動(dòng)態(tài)創(chuàng)建的編譯語句,在其上測試動(dòng)態(tài)SQL就沒有意義了。請查看兩組測試的結(jié)果,見圖1。
圖15 語句緩存與延遲提交對(duì)吞吐量的影響(啟動(dòng)了DIRECTIO的設(shè)備)
對(duì)簡單高速插入中,結(jié)果明確顯示將事務(wù)日志刷新至磁盤是最大的瓶頸。該圖中的其他異常可以輕松被解釋。因?yàn)閯?dòng)態(tài)SQL使用TDSRPC接口,而非TDSLANG,它一次僅能提交一個(gè)單獨(dú)的準(zhǔn)備語句。因此,當(dāng)使用諸如JDBC的addBatch()和execute-Batch()之類的批量方法時(shí),批必須被分解并序列化發(fā)送—結(jié)果比使用executeUpdate()單獨(dú)發(fā)送每個(gè)語句還稍慢。另外,當(dāng)執(zhí)行批量SQL語句時(shí),在語句緩存中找到語句的過程比單獨(dú)的語句更為有效。
最明顯的問題就是如何與ASE 12.5.4比較。與上述相同的基準(zhǔn)在緩存文件系統(tǒng)設(shè)備上運(yùn)行(與DIRECTIO相對(duì))—它通常將消除大部分延遲提交的效果,因?yàn)槿罩緦懭氡痪彺媪?但也仍可考慮性能上的差異,見圖16。
圖16 ASE 12.5.4與ASE 15.0.3比較(相同配置-4引擎和6GB內(nèi)存/緩存文件系統(tǒng)設(shè)備)
總體而言,可以看到ASE 15.0.3比12.5快10%左右—ASE 15.0的高性能特性幾乎將使用語言語句的已有應(yīng)用程序吞吐量提升了一倍。注意這個(gè)測試是在一個(gè)4引擎的ASE 15.0.3,運(yùn)行于兩個(gè)4核XEON處理器、SATA-RAID磁盤的Red Hat Enterprise Linux 5.1上較低端的機(jī)器結(jié)果卻能給人更深的印象。
9.2.2 小批量結(jié)果
對(duì)稱為“小批量”的技術(shù)術(shù)語引用頻繁貫穿了本文。小批量依賴于使用批量數(shù)據(jù)接口至ASE中和小批量大小。在ASE 12.0中,Sybase新增了數(shù)組插入特性,它支持一組數(shù)據(jù)使用類似于bcp的批量方法插入。如果有興趣的話,它參照到了服務(wù)器配置“cis bulk insert array size”—先前在積極聚合和重定向連接中的分布式查詢調(diào)優(yōu)部分提及。數(shù)組插入特性的操作與使用-B選項(xiàng)的慢速bcp很像,它要完全記錄日志并完全事務(wù)化。但是,與普通的語句不同,它無需分析、編譯和優(yōu)化—僅是高速加載。
為了保證應(yīng)用程序延遲和響應(yīng)時(shí)間最小,小批量的行在一次插入。通常,批次要么按照計(jì)時(shí)基準(zhǔn)(例如每250 ms)被刷新,或在批次的大小達(dá)到預(yù)定限制后。另外,為了處理單一來源的數(shù)據(jù)量或同時(shí)處理并行來源,多并發(fā)線程被用來在同一表中加載。
當(dāng)使用Java/JDBC時(shí),在滿足以下條件時(shí),數(shù)組插入被啟動(dòng):
(1)連接屬性ENABLE_BULK_LOAD被配置為“true”(需要SDK 15.0 ESD #10及更高版本)。
(2)預(yù)備語句使用java.sql.Connection.prepare-Statement()方法,通過JDBC調(diào)用反復(fù)執(zhí)行的DML操作必須規(guī)范。
(3)preparedStatement.addBatch()方法用來構(gòu)建未決插入的小批次,然后通過preparedStatement.executeBatch()方法來提交。
因?yàn)檎谟懻撌聞?wù)日志的影響,完全記錄日志的批方法也能受益于延遲提交特性。考慮使用剛才的基準(zhǔn)使用10個(gè)并發(fā)小批量線程插入至未分區(qū)表中的測試結(jié)果,見圖17。
圖1710 個(gè)線程每個(gè)插入5萬行數(shù)據(jù)
正如所展示的結(jié)果,甚至在使用延遲提交的動(dòng)態(tài)SQL情況下,小批量提供了巨大的吞吐量提升。但是,看起來延遲提交對(duì)小批量基本沒有幫助,因?yàn)閮蓷l線幾近重合。真正的原因是:
在使用小批量時(shí),瓶頸并非事務(wù)日志—而是網(wǎng)絡(luò)??墒褂肁SE MDA監(jiān)控表來查看單獨(dú)的進(jìn)程在等待什么,見圖18。
圖18 測試中顯示出網(wǎng)絡(luò)為主要瓶頸的MDA樣本
如果加載過程與數(shù)據(jù)服務(wù)器運(yùn)行在同一主機(jī)或快于1 Gbs的網(wǎng)絡(luò),或許網(wǎng)絡(luò)影響可以減少或消除。在該情況下,可能使用延遲提交時(shí)會(huì)受益。通過關(guān)閉了延遲提交的動(dòng)態(tài)SQL運(yùn)行來比較,見圖19。
多一點(diǎn)解釋,因?yàn)槭褂玫氖切屑?jí)鎖定和隔離級(jí)別1,在表級(jí)別上對(duì)嚴(yán)格插入沒有競爭。因此,150號(hào)事件(等待鎖)是等待日志標(biāo)志/自旋鎖。
9.2.3 數(shù)據(jù)丟失風(fēng)險(xiǎn)
開發(fā)人員和DBA在考慮啟動(dòng)該選項(xiàng)時(shí)會(huì)關(guān)心是否對(duì)數(shù)據(jù)庫一致性造成影響。答案是不會(huì)。在最壞的情況下可能發(fā)生的是最后一個(gè)數(shù)據(jù)頁丟失。損失多大呢?答案是一個(gè)單獨(dú)的頁面中能包含多少數(shù)據(jù)可能僅僅是在崩潰前的幾微 秒上插入的2~3行數(shù)據(jù)。對(duì)于大部分運(yùn)行于該速度下的系統(tǒng)來說,因?yàn)橄到y(tǒng)崩潰而損失2~3行數(shù)據(jù)不是問題。如果該應(yīng)用程序被構(gòu)建成處理HA失敗轉(zhuǎn)移的,很有可能最后一批仍舊在緩存,只需簡單地重新提交數(shù)據(jù)而不用擔(dān)心數(shù)據(jù)丟失。通過緩存之前的批次,完整的可恢復(fù)性也可得到保證。Sybase復(fù)制服務(wù)器(Replication Server)通過對(duì)每個(gè)連接的“保存間隔”提供該功能 —根據(jù)ASE失敗后的恢復(fù),可能設(shè)置幾秒就足以保證數(shù)據(jù)零丟失。
ASE 15.0引入了一些tempdb的提升,消除了在ASE 12.5.4中的一些瓶頸和一些細(xì)小的繁瑣,例如:
(1)#臨時(shí)表名現(xiàn)在可用238個(gè)字符來區(qū)別而非12個(gè)-消除了試圖創(chuàng)建含糊的#臨時(shí)表名來避免重復(fù)表錯(cuò)誤的需求。
(2)既寫入到用戶數(shù)據(jù)庫又寫入到tempdb的事務(wù)會(huì)在每次上下文改變時(shí)造成ULC刷新(寫入用戶db;寫入tempdb;寫入用戶db→兩次ULC刷新)。現(xiàn)在它已通過使用單獨(dú)會(huì)話tempdb日志緩存而消除了。
(3)影響OAM頁面的操作—例如創(chuàng)建新表或擴(kuò)展缺省空間都會(huì)導(dǎo)致ULC刷新以記錄OAM頁面的修改?,F(xiàn)在它作為系統(tǒng)日志記錄而不再需要ULC刷新了。
(4)實(shí)現(xiàn)了行級(jí)目錄 - 去除了對(duì)系統(tǒng)表的競爭。
(5)增強(qiáng)了tempdb的消極I/O實(shí)現(xiàn)-尤其對(duì)tempdb日志—在tempdb緩存足夠大的情況下真正解決了物理I/O。
雖然消極I/O肯定對(duì)tempdb的日志操作有幫助,甚至tempdb中最小的記錄日志批操作也受益于tempdb的優(yōu)化??紤]以下查詢的結(jié)果,見圖20。
declare @now datetime
select @now=getdate ()
-- 1,350,255 rows in salesdetail select * into #foo from salesdetail select
ms=datediff (ms, @now,getdate ()) go
圖20 比較批量插入1 350 255行數(shù)據(jù)至tempdb的時(shí)間
正如所展示的,ASE 15.0.3比較ASE 12.5.4來說使用select/into影響超過一百萬行快了近1 s。
對(duì)開發(fā)人員來說增加消極IO的一個(gè)有趣的方面是,它支持創(chuàng)建的臨時(shí)數(shù)據(jù)庫能提供幾近內(nèi)存數(shù)據(jù)庫的功能??赏ㄟ^以下實(shí)現(xiàn):
(1)增加數(shù)個(gè)(例如,5~6個(gè))數(shù)據(jù)緩存UFS設(shè)備和3~4個(gè)日志緩存UFS設(shè)備確保DIRECTIO和DSYNCH都失效。設(shè)備總大小應(yīng)與臨時(shí)數(shù)據(jù)庫指定能使用的總內(nèi)存大小相同。
(2)在設(shè)備上創(chuàng)建臨時(shí)數(shù)據(jù)庫作為用戶tempdb,跨設(shè)備用輪循調(diào)度的方式使用一系列小的分配(例如,66 MB)。
(3)創(chuàng)建與數(shù)據(jù)庫大小一致的單獨(dú)命名緩存。緩存必須是混合的—為數(shù)據(jù)和日志服務(wù)—并將用戶tempdb與緩存綁定。
(4)通過將服務(wù)器日志文件的緩存狀態(tài)修改為“HK Ignore”或通過SQL語句“update master.dbo.sysconfigures set status = status | 16 where parent=19 and name=‘
(5)將tempdb會(huì)話日志緩存大小配置成較大的數(shù)值(如 128 KB)
(6)將用戶CPU和I/O計(jì)數(shù)關(guān)閉
以上幫助創(chuàng)建能提供更高性能的應(yīng)用程序特定tempdb—如果應(yīng)用程序與該tempdb綁定。