數據庫

法国波尔多酒庄介绍:TPC-C解析系列03_TPC-C基準測試之SQL優化

廣告
廣告

微信掃一掃,分享到朋友圈

TPC-C解析系列03_TPC-C基準測試之SQL優化
0 0

TPC-C是一個非常嚴苛的基準測試模型,考驗的是一個完備的關系數據庫系統全鏈路的能力。這也是為什么在TPC-C的榜單前列,出現的永遠只是大家熟知的那幾家在業界有著幾十年積累、從關系數據庫理論開始發展就差不多同步出現的數據庫公司。接下來我們通過這篇文章為您分析在TPC-C測試中OceanBase數據庫的SQL??榫嚀逵齙攪四男┨粽?、做出了哪些優化。

背景

對TPC-C有所了解人都知道,TPC-C是一個典型的OLTP (On-Line Transaction Processing) 場景測試,考察的是數據庫在高并發壓力場景下的事務處理能力,最終的性能指標以tpmC(transaction per minute,也即每分鐘系統處理TPC-C模型中的new order事務的數量)和平均到每tpmC的系統成本作為衡量標準。在OLTP場景中,每條請求的響應時間都是極短的。因此,各個數據庫廠商在進行TPC-C測試時,都會盡一切可能將每一個操作時間壓縮到最短,不夸張的說,在TPC-C的測試中,一些關鍵操作的優化往往需要細化到CPU指令級。

在進入我們的主題前,我們先來談談TPC-C中的事務模型,主要分為五種事務,訂單創建、訂單支付、訂單查詢、訂單發貨以及庫存查詢,這五種事務按照一定的比例發生,測試最終衡量的是每分鐘訂單創建事務的執行個數。大家知道,每一個數據庫的事務,其實就是由一定邏輯關系關聯的若干條SQL語句組成,他們在一個事務中,要么全部成功,要么全部失敗,這個在數據庫中稱為“原子性”,也就是ACID中的“A”。那么TPC-C中的一個事務的耗時大約是多久呢?看一下報告就很清楚了——只有十幾個毫秒??悸塹揭桓鍪攣裼啥嗵鮏QL構成,那么每一條SQL的平均耗時都不到1毫秒!

在C/S(client-server)模型有中,一條SQL語句從發起到執行完成需要經歷從客戶端輸入、網絡傳輸、SQL優化、執行、結果返回到客戶端這樣一個流程。而具體每一條SQL的執行可能只是做一個字段的更新,所需要的執行時間是非常短暫的,從整個鏈路的角度來看,大量的時間會花費在與客戶端的交互過程中,造成資源的浪費和耗時的增加。那么如何解決這個問題的呢?答案就是使用存儲過程。

存儲過程

所謂“存儲過程”就是數據庫為用戶提供的一種面向過程的編程語言?;謖庵鐘镅?,用戶可以將應用程序的邏輯封裝為一個可調用的過程(procedure)存放在數據庫中并隨時進行調用。通過這種方式,用戶可以將本來需要與數據庫進行多次交互才能完成的工作通過一次交互完成,省去了中間網絡的傳輸和等待時間(參見圖1)。假如一條事務的網絡開銷平均是30%,也就是說30%的CPU都花在了網絡的收發和解析上。那么在6千萬規模tpmC測試中節省下來30%的CPU資源換算成系統處理能力是驚人的。使用存儲過程還可以帶來事務響應時間的下降,導致數據庫內核中事務鎖的臨界區縮短,間接的提升了系統CPU利用率,整個吞吐量也隨之提高。存儲過程在縮短應用端的等待耗時上同樣有很大作用。

在TPC-C中,存儲過程對于整個系統的執行效率提升是至關重要的。OceanBase 的2.2版本不僅全面支持了存儲過程,而且對存儲過程的執行效率做了大量極致的優化。

編譯執行

存儲過程作為一種面向過程的高級語言,需要轉換成機器碼才能夠執行。這個過程一般可以分為“編譯執行”和“解釋執行”兩種,一般來說,編譯執行相比解釋執行有代碼優化充分、執行效率高等特點。OceanBase利用近兩年逐漸成熟的LLVM編譯器框架實現了一個支持存儲過程的編譯器,通過動態編譯(Just-in-Time Compilation)的方式將存儲過程翻譯成高效的二進制可執行代碼,在執行效率上獲得了數量級的提升。同時,過程中LLVM框架將存儲過程轉換為與機器無關的中間代碼,使得存儲過程也自然而然地獲得了跨平臺的編譯執行能力,LLVM內置的優化過程確保我們在各種不同的硬件平臺上都可以獲得正確、高效的可執行代碼。

Array Binding

另外一個在TPC-C測試中發揮了重要作用的功能就是對DML語句進行批量處理的能力,在Oracle中該功能也稱為“Array Binding”。一條SQL在數據庫中的執行過程大致上可以分為“計劃生成”和“執行”兩個階段。盡管我們對SQL的執行計劃做了高速緩存,但找到一個合適的執行計劃在整個執行過程中仍然是比較耗時的一個部分。那有沒有辦法省去這個時間呢?當一組SQL的執行計劃完全一樣而只有執行參數不同時,在存儲過程中我們可以通過特定的語法將他們的執行做成一個批量處理的過程,此時“計劃生成”只需要做一次即可,這就是所謂的“Array Binding”。

在Array Binding中,數據庫會首先找到需要使用的計劃,然后執行該計劃,并在每次執行完畢后,重新執行參數綁定(binding)的過程。打個比方,這就像是在一個C語言的for循環中,反復賦值而不是重新定義一個數據結構。Array Binding的使用受用戶控制,需要在存儲過程中使用FORALL關鍵字來觸發這一功能,在TPC-C的測試過程中,我們多次使用了Array Binding來提升系統的處理能力,效果非常明顯。

Prepared Statement與執行計劃緩存

Prepared Statement是一種二進制的請求交互協議,可以大大降低系統的交互成本。OceanBase不僅支持用戶程序與數據庫間使用Prepared Statement, 也支持在存儲過程引擎調用SQL引擎執行時使用這種交互方式。存儲過程在對SQL進行一次Prepare操作并獲取唯一id后, 后續的每次執行僅需要傳入該id和對應的參數,系統可以通過高速緩存找到對應的存儲過程或SQL計劃開始執行。該過程相比使用SQL文本的交互方式,省去了大量請求文本解析的CPU開銷。


OceanBase內部實現了高速緩存來緩存存儲過程的可執行代碼及SQL執行計劃, 不同參數的存儲過程和SQL可以通過這一高速緩存快速獲取需要的執行對象, 耗時一般在幾十微秒以內, 有效避免了重新編譯帶來的毫秒級的延遲和CPU消耗。

可更新視圖

在OLTP場景中,通過減少應用與數據庫的交互次數來實現性能提升的例子很多,可更新視圖就是其中之一。我們常見的數據庫視圖通常是只讀的,通過定義視圖,用戶可以定義自己感興趣的數據以及其獲取接口,但視圖同時也可以作為更新操作的入口,比如在TPC-C的new order創建場景中,應用需要得到商品信息,更新庫存并得到更新后的值。一般可以通過兩條SQL實現這一過程:

    select i_price, i_name, i_data from item where i_id = ?;

    UPDATE stock

      SET s_order_cnt = s_order_cnt + 1,

          s_ytd = s_ytd + ?,

          s_remote_cnt = s_remote_cnt + ?,

          s_quantity = (CASE WHEN s_quantity < ? + 10 THEN s_quantity + 91 ELSE s_quantity END) – ?

      WHERE s_i_id = ?

          AND s_w_id = ?

      RETURNING s_quantity, s_dist_01,

          CASE WHEN i_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE (CASE WHEN s_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE ‘B’ END) END

      BULK COLLECT INTO …;

但通過建立一個可更新視圖:

    CREATE VIEW stock_item AS

      SELECT i_price, i_name, i_data, s_i_id, s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01

      FROM stock s, item i WHERE s.s_i_id = i.i_id;

我們就可以通過一條語句更新庫存并得到商品和庫存信息:

    UPDATE stock_item

      SET s_order_cnt = s_order_cnt + 1,

          s_ytd = s_ytd + ?,

          s_remote_cnt = s_remote_cnt + ?,

          s_quantity = (CASE WHEN s_quantity < ? + 10 THEN s_quantity + 91 ELSE s_quantity END) – ?

      WHERE s_i_id = ?

          AND s_w_id = ?

      RETURNING i_price, i_name, s_quantity, s_dist_01,

          CASE WHEN i_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE (CASE WHEN s_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE ‘B’ END) END

      BULK COLLECT INTO …;

這樣就省去了一條語句的交互,并且更新邏輯更加直觀??篩率油莢市磧沒Э梢韻衿脹ū硪謊僮魘油?,但不是所有視圖都可以定義為可更新視圖。比如帶distinct, group by的視圖,具體更新哪些行語義是不明確的,因此不能允許更新。具體到上面的stock_item兩表join的視圖,需要滿足所更新表的unique key在join之后保持unique (key-preserved table),即item.i_id必須是唯一的這個前提。

需要強調,TPC-C規范禁止使用物化視圖,而可更新視圖并沒有改變底層數據表格的存儲形式,是符合規范的。

總結

因為TPC-C的設計原則是盡可能的“真實”反應一個OLTP系統的運行場景,我們所做的很多優化都具有廣泛的適用性。例如,對于一個高并發的OLTP系統來說,大部分的SQL請求的耗時是非常短的,采用純粹的C/S交互模型的后果必然使系統的時間浪費在應用與數據庫的頻繁交互中,而使用存儲過程可以大大緩解這種交互的耗時,并且增強系統對于網絡抖動的免疫力,這種核心能力對于一個分布式OLTP數據庫是不可或缺的。

OceanBase從創立伊始就堅持走自主研發的道路,這個選擇確保了我們對數據庫內核有著完全的掌控能力,讓我們有在任何場景下追求極致性能的底氣和實力的同時,也對產品形態的發展方向有更清晰的規劃和目標。在這次的TPC-C測試中,我們采用了OceanBase 2.0版本開始支持的Oracle兼容模式,存儲過程和SQL全部使用了兼容Oracle的數據類型和語法,這樣做也是為了在追求極致優化的同時,確保產品迭代可以沿著通用和正規的方向發展。從OceanBase 2.0版本開始,OceanBase就不斷朝著Oracle兼容這個大的目標前進,隨著2.2版本支持的存儲過程(PL/SQL)功能的完善,我們的產品功能也完成了一輪新的迭代。我們堅信這次的TPC-C測試結果不僅僅見證了OceanBase的極致性能,也將成為OceanBase數據庫走向成熟產品的一個新起點。

作者介紹:

  • 陳萌萌:現任螞蟻金服OceanBase團隊資深技術專家,負責OceanBase SQL方向的研發工作。2006年畢業于清華大學,2006年到2008年在歐洲核子研究中心(CERN)負責網格計算調度器的開發工作,2009年在美國威斯康星大學麥迪遜分?;竦眉撲慊妒墾?,先后在Oracle、華為美國研究所從事數據庫的開發和研究。
  • 潘毅:現任螞蟻金服OceanBase團隊資深技術專家,負責OceanBase的并行查詢和新一代OLAP引擎。曾就職于美國Oracle公司,負責Oracle數據庫并行查詢研發工作并有多項專利申請。
我還沒有學會寫個人說明!

TPC-C解析系列02_OceanBase如何做TPC-C測試

上一篇

TPC-C解析系列04_TPC-C基準測試之數據庫事務引擎的挑戰

下一篇

你也可能喜歡

TPC-C解析系列03_TPC-C基準測試之SQL優化

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃