TPC-C too simple?一文詳解 TPC-E:TPC-C 的升級版
從 TPC-C 到 TPC-E
在數據庫評測領域, TPC-C 可能是最出名的OLTP 基準測試(benchmark)之一了。各大數據庫產品為展現其性能強大,紛紛在 TPC-C 性能榜上你方唱罷我登場。Oracle 一度獨占鰲頭,阿里 OceanBase、 騰訊 TD-SQL 也輪番登頂。達夢、TiDB、TBase 等等也紛紛用 TPC-C 作為自身產品的性能衡量標準。不僅如此,TPC-C 也在許多下游任務中頻繁亮相,例如參數調優任務、負載預測任務、索引推薦任務等等。
然而,TPC-C 作為一個1992 年推出的 OLTP benchmark,庫表結構、事務類型、業務場景都顯得“過于簡單”了。為了應對數據庫領域的發展,TPC 委員會在 2007 年推出了下一代 OLTP 基準測試:TPC-E。
在 TPC-E 官網 上,官方開宗明義:「TPC-E 比」 「TPC-C」 「更加復雜,因為它的事務類型更多樣化、庫表結構和執行結構更復雜」:
?
TPC-E is more complex than previous OLTP benchmarks such as TPC-C because of its diverse transaction types, more complex database and overall execution structure.
?
TPC-E 相比 TPC-C 的復雜性是顯而易見的,我們僅列舉一些:
TPC-C | TPC-E | |
模擬場景 | 簡單的批發商系統 | 復雜的證券交易所系統 |
事務類型 | 5 種 | 12 種 |
庫表 | 9 張表 | 33 張表 |
數據生成 | 隨機數,均勻分布 | 真實數據規律,有傾斜(skew) |
復雜 join | 最多 2 表 join | 最多 7 表 join |
讀寫比 | 1.9:1 | 9.7:1(讀負載比例更高) |
相比 TPC-C 威名赫赫,TPC-E 由于其復雜而顯得小眾,在工業界和學術界并沒有被廣泛地用于性能測試。然而在 TPC-C 已經被研究透徹、各大廠商的評測中紛紛“過度優化”的如今,TPC-E 基準測試不失為一種新的、良好的補充。
本文接下來你會看到:
- 「概覽全貌」:對 TPC-E 做一份詳細的講解,展現 TPC-E 的場景、庫表與事務全貌。
- 「實踐挑戰」:借助 “MySQL 索引優化” 這一場景,展現 TPC-E 對現有技術帶來的新的挑戰。
- 「原理解析」:深入SQL 級別,完全拆解 TPC-E 的 12 種事務類型,知其然也知其所以然。
- 「輕松上手」:繞過 Github 暗坑,在 MySQL 上編譯和運行 TPC-E。
概覽全貌
TPC-E 的場景是股票交易,涉及客戶、經紀行和市場三種角色的復雜交互
TPC-E(Transaction Processing Performance Council - E)是一個模擬復雜在線交易處理(OLTP)環境的基準測試。它通過一系列事務來模擬一個股票經紀行的日常業務活動,這些活動涉及客戶賬戶管理、交易執行以及與金融市場的互動。整個業務場景中包含了客戶、經紀商、市場數據和后臺處理等關鍵要素。
這里我們從角色(Role)、事務和關系表三部分來展現 TPC-E 全貌。
3種角色
TPC-E 模擬的是證券交易所,證券交易的買賣過程會涉及到下面三種角色:
- 「Brokerage(經紀行)」 :在 TPC-E 基準測試中,經紀行的角色通常由
Customer Emulator
(客戶模擬器)組件扮演。它模擬了客戶與經紀行的交互,包括提交交易請求、查詢賬戶信息、執行市場分析等。經紀行角色負責處理客戶的交易訂單,管理客戶賬戶,并提供市場數據。
- 注意,事務有一種類別是 Brokerage initiated,但代碼中并沒有單獨的 broker emulator,因為 broker 通常是應答customer 的要求,broker 參與的事務就放到 CE 中模擬
- 「Customer(客戶)」 :客戶角色代表了實際使用經紀行服務的個人或機構投資者。在 TPC-E 中,客戶角色通過
Customer Emulator
組件模擬,執行各種交易活動,如買賣證券、查詢持倉情況、查看市場動態等。客戶角色的目的是評估經紀行提供的服務和交易平臺的性能。 - 「Market(市場)」 :市場角色在 TPC-E 中由
Market Exchange Emulator
(市場交易所模擬器)組件扮演。它模擬了股票市場的實際運作,包括股票價格的變動、交易的執行、市場數據的發布等。市場角色為經紀行和客戶提供了交易的場所和必要的市場信息。
這三個角色在 TPC-E 中的主要區別在于它們在交易過程中的職責和功能。經紀行負責處理交易和客戶賬戶,客戶負責發起交易和查詢,而市場則提供了交易發生的環境和數據。
12 種事務:一個故事
TPC-E 共包含了 12 種類型的事務,為了便于理解,讓我們用一個故事串講一下。
在一個充滿活力的交易日,客戶們忙碌地通過經紀行的交易平臺進行股票買賣。他們首先會檢查自己的賬戶情況,了解自己的資產和持倉(Customer-Position
事務),然后根據市場動態(Market-Feed
事務)和特定證券的詳細信息(Security-Detail
事務)來制定交易策略。在做出決策之前,他們可能會監控市場趨勢(Market-Watch
事務),或者回顧過去的交易記錄(Trade-Lookup
事務),以分析證券的歷史表現。經紀行管理者會生成不同經紀商的交易報告,用于評估各個經紀商的表現(Broker-Volume
事務)。一旦客戶決定買賣某只股票,他們會下達交易指令(Trade-Order
事務)。這些指令會被提交到市場交易所,并在交易完成后收到交易結果(Trade-Result
事務)。這些結果包括了交易的最終確認、成交價格以及可能的稅務影響。客戶可以通過查看交易狀態(Trade-Status
事務)來跟蹤他們的交易是否成功執行。在交易過程中,客戶可能會需要更新或修改他們的交易指令(Trade-Update
事務)。同時,為了保持數據的準確性和最新性,經紀行會定期進行數據維護(Data-Maintenance
事務),包括更新客戶賬戶信息、稅務信息以及市場數據。在交易日結束時,經紀行需要清理數據庫,取消任何未完成或錯誤的交易(Trade-Cleanup
事務),以確保第二天的交易能夠順利進行。這個過程包括從數據庫中移除所有掛起的交易請求,更新交易歷史記錄,并確保所有交易數據都是最新的。
33 張關系表
TPC-E 共涉及 33 張表:
- 「Customer Tables」:9 張表,描述了與客戶相關的表,包括賬戶信息(
CUSTOMER_ACCOUNT
)、稅務信息(CUSTOMER_TAXRATE
)等。 - 「Broker Tables」:9 張表,與經紀商相關的表,如經紀商(
BROKER
)、現金交易(CASH_TRANSACTION
)、費用(CHARGE
)等。 - 「Market Tables」:11 張表,與市場相關的表,如公司(
COMPANY
)、每日市場數據(DAILY_MARKET
)、交易所(EXCHANGE
)等。 - 「Dimension Tables」:維度表,如地址(
ADDRESS
)、狀態類型(STATUS_TYPE
)、稅率(TAXRATE
)等。
TPC 委員會公布的 TPC-E 標準文件(pdf)中事無巨細的講解了 TPC-E 各方面內容,其中2.2.4 ~ 2.2.7 描述庫表設計,感興趣的同學可以深入了解下:
衡量標準:tpsE
TPC-E 衡量的標準是 tpsE(transactions- per-second-E,每秒成交量)。在 TPC-E 對真實場景的模擬中,用戶和經紀商可能經過許多次的觀望、選擇、評估,才會達成一筆交易。因此,TPC-E 的性能取決于 Trade-Result
事務完成的數量。例如,如果一個客戶執行了一項交易,并且該交易被成功處理(即交易請求被接受并執行,Trade-Result +
1),那么這將被視為完成了一個 tpsE。僅僅查看訂單或執行其他非交易類型的操作通常不會計算在內。Trade-Result
事務與全部事務的比例基本穩定(例如 10%),也意味著 tpsE 基本可以反映數據庫執行的事務總量。考慮到TPC-E
的事務通常較為復雜(單個事務會包含數十條 SQL),在我們執行 TPC-E 測試時,盡管最終顯示的 tpsE 只有 100 上下,但實際執行的
SQL 已經超過數十萬條。
原理解析:深入 SQL 看事務
TPC-E 比 TPC-C 的復雜體現在事務的復雜。TPC-C 包含 5 種事務,SQL 模板共 29 條,而 TPC-E 包含 12 種事務,SQL 模板超過 120 條。在一些復雜的 TPC-E 事務中(例如 Trade-Order),包含 6 個階段(稱為 Frame),每個階段中會執行多輪”子事務“。由此,在各種任務(參數調優、規格調優、索引推薦)走到深水區后,對事務細節的了解就很有必要了。
下面我們會逐一分析各個事務的事務邏輯概述和 SQL 細節。必要的地方我們會結合 TPCE 負載發生器的源碼進行解析。
事務分類
TPC-E 的事務可以按照它們的功能和特征進行分類。根據文檔中的描述,這些事務主要可以分為以下幾類:
- 「客戶發起的事務(Customer Initiated)」 :
這些事務模擬了客戶與系統交互的場景,如查詢賬戶信息、執行交易等。
例如:Customer-Position(客戶持倉查詢)、Market-Watch(市場觀察)、一部分 Trade-Lookup(交易查詢)、Security-Detail(證券詳情查詢)、Trade-Order(交易委托)、Trade-Status(交易狀態查詢)、一部分 Trade-Update(交易更新)。
- 「經紀商發起的事務(Brokerage Initiated)」
這些事務模擬了經紀商內部處理的場景,如生成報告、管理賬戶等。
例如:Broker-Volume(經紀商成交量)。一部分 Trade-Lookup;一部分 Trade-Update
- 「市場觸發的事務(Market Triggered)」 :
這些事務模擬了市場活動對系統的影響,如市場數據更新、市場動態跟蹤等。
例如:Market-Feed(市場數據更新)、Trade-Result
- 「其他」:
Trade-Cleanup、Data-Maintenance
我們結合 Github 源碼進行分析。tpce-mysql 中,DBConnection.h
文件包含幾個 enum,可以作為印證,如下:
/*
Customer Emulator System Under Test
由用戶
*/
enum eCESUTStmt
{
// Customer-Position 有2 階段、4 sql。文檔是3 階段(Frame),但第三階段只有 commit ,其他有意義的 sql 是對得上的。
CESUT_STMT_CPF1_1,
// Market-Watch(市場觀察)
CESUT_STMT_MWF1_1a,
// Security-Detail(證券詳情查詢)
CESUT_STMT_SDF1_1,
// Trade-Lookup(交易查詢),非常巨大的事務
CESUT_STMT_TLF1_1,
// Trade-Order(交易委托)
CESUT_STMT_TOF1_1,
//Trade-Status(交易狀態查詢)
CESUT_STMT_TSF1_1,
// Trade-Update(交易更新)
CESUT_STMT_TUF1_1,
}
/*
Market Exchange Emulator SUT
*/
enum eMEESUTStmt
{
// 極其巨大的事務
MEESUT_STMT_TRF1_1,
// Market-Feed(市場數據更新)
MEESUT_STMT_MFF1_1,
};
/*
Data Maintenance SUT
*/
enum eDMSUTStmt
{
// Trade-Cleanup,開測前初始化;
DMSUT_STMT_TCF1_2,
};
// 其他無對應代碼 enum 的:
// Broker-Volume(經紀商成交量):只有一個 frame、一句 sql,無 enum
除了上述分類,事務還可以根據它們的讀寫特性進行區分:
- 「讀事務(Read-Only)」 :這類事務主要涉及數據的讀取,不會導致數據的修改。例如,客戶查詢賬戶信息(Customer-Position)或查看市場數據(Market-Watch)。
- 「讀寫事務(Read-Write)」 :這類事務既涉及數據的讀取也涉及數據的寫入,可能會改變數據庫的狀態。例如,執行交易(Trade-Order)會創建新的交易記錄,更新客戶賬戶(Trade-Update)會改變賬戶的持倉信息。
- 「寫事務(Write-Only)」 :這類事務主要涉及數據的寫入,不涉及數據的讀取。例如,數據維護(Data-Maintenance)事務可能會更新或刪除數據庫中的記錄。
概括來看:
- 「Broker-Volume (BV)」 - 模擬「經紀行」內部業務處理,例如生成關于不同經紀人業績、潛力的報告。
- 「Customer-Position (CP)」 - 模擬「客戶」查詢其賬戶的持倉情況。根據所有資產的當前市場價值總結其賬戶價值。
- 「Market-Feed (MF)」 - 模擬跟蹤當前市場活動,處理來自「市場交易所」的“股票行情”數據。
- 「Market-Watch (MW)」 - 允許「客戶」跟蹤一組證券的當前日常趨勢(上漲或下跌),基于客戶的當前持倉、觀察列表或特定行業。
- 「Security-Detail (SD)」 - 模擬「客戶」訪問特定證券(
Security
)的詳細信息,如進行研究以決定是否執行交易。 - 「Trade-Lookup (TL)」 - 模擬信息檢索,以回答關于一組交易的問題,可能涉及市場分析、交易歷史審查或特定客戶持倉分析。
- 「Trade-Order (TO)」 - 模擬「客戶、經紀人」 或授權第三方購買或出售證券的過程,包括驗證授權、執行市場價買賣、限價買賣以及提供財務影響估計。
- 「Trade-Result (TR)」 - 模擬完成股票市場交易的過程,更新客戶持倉,記錄交易結果和歷史信息。這是由 「market 市場交易所」 負責記錄的
- 「Trade-Status (TS)」 - 提供特定交易集合的狀態更新,模擬「客戶」查看其賬戶的最近交易活動摘要。
- 「Trade-Update (TU)」 - 模擬對一組交易進行輕微修正或更新,類似于「客戶」或「經紀人」審查交易并進行小的編輯修正。
- 「Data-Maintenance (DM)」 - 模擬對主要靜態數據進行定期修改,如更新參考數據。
- 「Trade-Cleanup (TC)」 - 用于取消數據庫中任何待處理或已提交的交易,通常在測試運行前將數據庫恢復到已知狀態。
Broker-Volume
「Broker-Volume 事務邏輯概述」 在 TPC-E 基準測試的第 3.3.1 章節中,Broker-Volume 事務是一個典型的讀操作,它模擬了經紀行內部生成經紀人業績報告的場景。這個事務的核心目標是計算每個經紀人在特定時間段內的交易量,這通常涉及到對掛單限價訂單(TRADE_REQUEST)的匯總分析。
「SQL 細節」 Broker-Volume 事務的 SQL 查詢設計要實現以下目標:
- 「選擇經紀人列表」:確定需要生成報告的經紀人。
- 「檢索掛單限價訂單」:從 TRADE_REQUEST 表中檢索每個經紀人的訂單信息。
- 「計算總交易量」:對每個經紀人的訂單數量和價格進行計算,得出總交易量。
- 「排序結果」:將經紀人按照總交易量降序排列,以便展示業績最好的經紀人。
以下是 Broker-Volume 事務的 SQL 偽代碼:
-- Broker-Volume 事務的 SQL 查詢
SELECT b_name, SUM(tr_qty * tr_bid_price) -- 經紀人的總交易量
FROM trade_request, sector, industry, company, broker, security
WHERE tr_b_id = b_id -- 經紀人表,通過經紀人ID關聯
AND tr_s_symb = s_symb -- 行業表,通過證券符號關聯
AND s_co_id = co_id -- 行業表,通過行業ID關聯
AND co_in_id = in_id -- 確保公司表中的國家ID與行業表中的國家ID匹配
AND sc_id = in_sc_id -- 確保行業表中的公司ID與公司表中的ID匹配
AND b_name IN (%s..) -- 經紀人名稱列表,這里 %s.. 是一個占位符,表示一系列經紀人名稱
AND sc_name = '%s' -- 行業名稱,這里 '%s' 是一個占位符,表示特定的行業名稱
GROUP BY b_name
ORDER BY 2 DESC -- 按總交易量降序排列
在這個查詢中,我們使用了多個 JOIN 操作來關聯不同的表,確保我們能夠獲取每個經紀人的交易請求信息。我們通過 WHERE 子句過濾出特定經紀人和特定行業的交易請求。然后,我們使用 GROUP BY 對經紀人名稱進行分組,并計算每個經紀人的總交易量。最后,我們使用 ORDER BY 對結果進行降序排列,以便展示交易量最高的經紀人。
Customer-Position
客戶位置(Customer-Position)由EGenDriverCE
調用。它由三個 frame 組成(frame 2和3是相互排斥的)。客戶由客戶ID(customer ID
)或客戶稅號(customer tax ID
)指定。如果轉入交易的 customer ID
為0,則使用客戶稅ID來查找客戶ID。檢索有關客戶個人資料的詳細信息。此外,對于每個客戶的賬戶,將退還該賬戶的現金余額和賬戶中所有持有的當前市場總值。如果請求交易活動的歷史記錄,則檢索客戶帳戶中隨機選擇的帳戶的最新十筆交易的信息。
「事務邏輯概述」
Customer-Position 事務模擬了客戶查詢其賬戶持倉情況的場景。這個事務通過檢索客戶資料、賬戶余額、持倉詳情以及最近的交易歷史,為客戶提供了一個全面的賬戶狀態報告。在技術博客中,我們將詳細探討這個事務的每個階段,以及它們在 SQL 中的具體實現。
「Frame/sql 注解」
在 Frame 1 中,我們首先設置了事務的隔離級別為 READ COMMITTED,這確保了事務在讀取數據時的一致性。接著,我們執行了兩個 SQL 查詢來獲取客戶信息。
-- 設置事務隔離級別
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 根據稅號查詢客戶ID
SELECT c_id FROM customer WHERE c_tax_id = _latin1'970AM8516RE955';
-- 獲取客戶詳細信息
SELECT
c_st_id, c_l_name, c_f_name, c_m_name, c_gndr, c_tier,
DATE_FORMAT(c_dob,'%Y-%m-%d'), c_ad_id,
c_ctry_1, c_area_1, c_local_1, c_ext_1,
c_ctry_2, c_area_2, c_local_2, c_ext_2,
c_ctry_3, c_area_3, c_local_3, c_ext_3,
c_email_1, c_email_2
FROM customer
WHERE c_id = 4300001491;
Frame 2 僅在 get_history
參數為 TRUE 時執行。這個 Frame 負責檢索客戶最近的交易歷史。這里我們使用了兩個 SQL 查詢:
-- 查詢客戶賬戶的前10個持倉及其總價值
SELECT
ca_id, ca_bal, COALESCE(SUM(hs_qty * lt_price),0) AS price_sum
FROM
customer_account
LEFT OUTER JOIN holding_summary ON hs_ca_id = ca_id, last_trade
WHERE
ca_c_id = 4300001491 AND lt_s_symb = hs_symb
GROUP BY
ca_id, ca_bal
ORDER BY
price_sum ASC
LIMIT 10;
-- 查詢客戶最近的30條交易歷史記錄
SELECT
t_id, t_s_symb, t_qty, st_name, DATE_FORMAT(th_dts,'%Y-%m-%d %H:%i:%s.%f')
FROM
(SELECT t_id AS id FROM trade WHERE t_ca_id = 43000014904 ORDER BY t_dts DESC LIMIT 10) AS t,
trade, trade_history, status_type
FORCE INDEX(PRIMARY)
WHERE
t_id = id AND th_t_id = t_id AND st_id = th_st_id
ORDER BY
th_dts DESC
LIMIT 30;
Frame 3 包含了一個 COMMIT
語句,用于提交事務,確保之前的所有更改都被保存到數據庫中。
-- 提交事務
COMMIT;
Market-Feed
「事務邏輯概述」
Market-Feed 事務在 TPC-E 基準測試中扮演著模擬市場數據更新的角色。這個事務的目的是處理市場交易所的最新交易信息,這些信息通常包括股票的最后成交價格、成交量和成交時間。包含 1 個 frame
「Frame/sql 注解」 設置事務隔離級別
-- 設置事務隔離級別為可重復讀,確保在事務期間讀取的數據保持一致
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
更新最后成交信息
-- 更新 last_trade 表,模擬市場交易所的最新成交信息
UPDATE last_trade
SET
lt_price = '2.93399999999999999e+01', -- 設置最新的成交價格
lt_vol = lt_vol + '100', -- 增加成交量
lt_dts = '2024-02-27 20:48:17' -- 更新成交時間
WHERE
lt_s_symb = 'CLYS'; -- 指定特定的證券符號
查詢待處理的交易請求
-- 查詢 trade_request 表,找出與最新成交信息相關的待處理交易請求
SELECT
tr_t_id, tr_bid_price, tr_tt_id, tr_qty
FROM
trade_request
WHERE
tr_s_symb = 'CLYS' -- 指定證券符號
AND (
(tr_tt_id = 'TSL' AND tr_bid_price >= '2.93399999999999999e+01') -- 買入限價單,且報價大于等于最新成交價
OR (tr_tt_id = 'TLS' AND tr_bid_price <= '2.93399999999999999e+01') -- 賣出限價單,且報價小于等于最新成交價
OR (tr_tt_id = 'TLB' AND tr_bid_price >= '2.93399999999999999e+01') -- 買入止損單,且報價大于等于最新成交價
);
提交事務
-- 提交事務,確保所有更改都被保存
COMMIT;
Market-Watch
Market-Watch 事務是由客戶執行的,用于監控市場的整體表現。這個事務通過比較選定證券集合在「特定日期的收盤價」與「當前市場價格」的百分比變化來實現。這個集合可能基于客戶的當前持倉、潛在證券觀察列表或特定行業。Market-Watch 事務包含 1 個 Frame,該 Frame 執行一個 SQL 查詢來計算市值變化。
-- 設置事務隔離級別為 READ COMMITTED,確保事務在讀取數據時不會受到其他并發事務的影響
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 執行查詢,計算市值變化
-- 這個查詢涉及到多個表的連接,包括 watch_item, watch_list, last_trade, security, 和 daily_market
-- 它計算了在特定日期('2004-12-31')的收盤價(dm_close)和當前價格(lt_price)的總和
-- 通過比較這兩個總和,可以得到市值的百分比變化
SELECT
COALESCE(SUM(s_num_out * dm_close), 0) AS market_cap_change, -- 計算特定日期的市值
COALESCE(SUM(s_num_out * lt_price), 0) AS current_market_cap -- 計算當前市值
FROM
watch_item,
watch_list,
last_trade,
security,
daily_market
WHERE
wl_c_id = '4300000678' -- 指定客戶ID
AND wi_wl_id = wl_id -- 確保 watch_item 和 watch_list 的關聯ID匹配
AND dm_s_symb = wi_symb -- 確保證券符號匹配
AND dm_date = '2004-12-31' -- 指定比較的日期
AND lt_s_symb = dm_s_symb -- 確保 last_trade 中的證券符號與 daily_market 中的匹配
AND s_symb = dm_s_sym; -- 確保 security 表中的證券符號與 daily_market 中的匹配
-- 關閉語句,結束查詢
Close stmt;
Security-Detail
「事務邏輯概述」
Security-Detail 事務旨在模擬客戶在決定是否執行交易前對特定證券進行詳細研究的過程。這個事務由 EGenDriverCE 觸發,并且只包含 「1個 Frame」。事務會返回關于給定證券的詳細信息,包括公司信息、競爭對手列表、當前和歷史財務數據,以及關于公司的最新新聞條目。
「Frame/sql 注解」
-- 設置事務隔離級別為 READ COMMITTED,確保事務在讀取數據時的一致性
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 查詢證券和公司詳細信息
SELECT
s_name, co_id, co_name, co_sp_rate, co_ceo, co_desc,
DATE_FORMAT(co_open_date,'%Y-%m-%d'), co_st_id,
ca.ad_line1, ca.ad_line2, zca.zc_town, zca.zc_div, ca.ad_zc_code, ca.ad_ctry,
s_num_out, DATE_FORMAT(s_start_date,'%Y-%m-%d'),
DATE_FORMAT(s_exch_date,'%Y-%m-%d'), s_pe, s_52wk_high,
DATE_FORMAT(s_52wk_high_date,'%Y-%m-%d'), s_52wk_low,
DATE_FORMAT(s_52wk_low_date,'%Y-%m-%d'), s_dividend, s_yield,
zea.zc_div, ea.ad_ctry, ea.ad_line1, ea.ad_line2, zea.zc_town,
ea.ad_zc_code, ex_close, ex_desc, ex_name, ex_num_symb, ex_open
FROM
security, company, address ca, address ea, zip_code zca, zip_code zea, exchange
WHERE
s_symb = _latin1'XTRM'
AND co_id = s_co_id
AND ca.ad_id = co_ad_id
AND ea.ad_id = ex_ad_id
AND ex_id = s_ex_id
AND ca.ad_zc_code = zca.zc_code
AND ea.ad_zc_code = zea.zc_code;
-- 查詢公司競爭對手信息
SELECT
co_name, in_name
FROM
company_competitor, company, industry
WHERE
cp_co_id = 4300000566
AND co_id = cp_comp_co_id
AND in_id = cp_in_id
LIMIT 3;
-- 查詢公司財務數據
SELECT
fi_year, fi_qtr, DATE_FORMAT(fi_qtr_start_date,'%Y-%m-%d'),
fi_revenue, fi_net_earn, fi_basic_eps, fi_dilut_eps,
fi_margin, fi_inventory, fi_assets, fi_liability,
fi_out_basic, fi_out_dilut
FROM
financial
WHERE
fi_co_id = 4300000566
ORDER BY
fi_year ASC, fi_qtr
LIMIT 20;
-- 查詢證券市場歷史數據
SELECT
DATE_FORMAT(dm_date,'%Y-%m-%d'), dm_close, dm_high, dm_low, dm_vol
FROM
daily_market
WHERE
dm_s_symb = _latin1'XTRM'
AND dm_date >= _latin1'2000-08-12'
ORDER BY
dm_date ASC
LIMIT 15;
-- 查詢最后一筆交易信息
SELECT
lt_price, lt_open_price, lt_vol
FROM
last_trade
WHERE
lt_s_symb = _latin1'XTRM';
-- 查詢公司最新新聞條目
SELECT
DATE_FORMAT(ni_dts, '%Y-%m-%d %H:%i:%s.%f'), ni_source, ni_author, ni_headline, ni_summary
FROM
news_xref, news_item
WHERE
ni_id = nx_ni_id
AND nx_co_id = 4300000566
LIMIT 2;
-- 提交事務,確保所有查詢結果被正確處理
COMMIT;
Trade-Lookup
Trade-Lookup包含 4 個 frame,實際上包含了多個數據庫意義上的“事務”,broker 和customer 分別執行兩個 frame,這些甚至不在一個進程中執行完畢。因此不在通過 sql 解釋,而是概述其設計邏輯。
Trade-Lookup 事務是 TPC-E 基準測試中的一個「關鍵組成部分」,它模擬了「客戶」或「經紀人」為了回答關于一組交易的問題而進行的信息檢索過程。這個事務涵蓋了多種場景,包括進行市場分析
、回顧賬戶最近的交易記錄
、分析特定證券的過去表現
以及分析特定客戶持倉的歷史
。
Trade-Lookup 事務由 EGenDriverCE 觸發,并且包含四個互斥的 Frame。每個 Frame 都采用不同的技術來查找歷史交易數據。
- 「Frame 1」 :Frame 1 接受一組交易 ID 的列表。對于列表中的每個交易 ID,系統會返回相關的交易信息。這允許用戶查詢特定的交易詳情,可能是為了驗證交易記錄或進行詳細的交易分析。
- 「Frame 2」 :Frame 2 接受客戶賬戶 ID、開始時間戳、結束時間戳以及交易數量(N)作為輸入。它會返回在指定時間范圍內(包括開始和結束時間戳)的前 N 筆交易信息。這個 Frame 適用于用戶想要了解特定賬戶在一定時間窗口內的交易活動。
- 「Frame 3」 :Frame 3 接受證券符號、開始時間戳、結束時間戳以及交易數量(N)作為輸入。它會返回在指定時間范圍內(包括開始和結束時間戳)的前 N 筆特定證券的交易信息。這個 Frame 用于分析特定證券的市場表現和交易活動。
- 「Frame 4」 :Frame 4 接受客戶賬戶 ID 和一個時間戳作為輸入。它會識別出在指定時間戳或之后該客戶賬戶的第一筆交易,并返回最多 20 條與這筆交易 ID 相關的持倉歷史變更記錄。這些歷史變更記錄包括由這筆交易對之前交易創建的持倉所做的更改,以及后續交易對由此交易創建的任何持倉所做的更改。
部分 sql:
-- 3.3.6 Trade-Lookup
Query SET TRANSACTION ISOLATION LEVEL READ COMMITTED
-- F1
Execute SELECT t_bid_price, t_exec_name, t_is_cash, tt_is_mrkt, t_trade_price FROM trade, trade_type WHERE t_id = '200000005238470' AND t_tt_id = tt_id
Execute SELECT se_amt, DATE_FORMAT(se_cash_due_date, '%Y-%m-%d'), se_cash_type FROM settlement WHERE se_t_id = '200000005238470'
Execute SELECT ct_amt, DATE_FORMAT(ct_dts, '%Y-%m-%d %H:%i:%s.%f'), ct_name FROM cash_transaction WHERE ct_t_id = '200000005238470'
Execute SELECT DATE_FORMAT(th_dts, '%Y-%m-%d %H:%i:%s.%f'), th_st_id FROM trade_history WHERE th_t_id = '200000005238470' ORDER BY th_dts LIMIT 3
-- F2
Query SELECT t_bid_price, t_exec_name, t_is_cash, tt_is_mrkt, t_trade_price FROM trade, trade_type WHERE t_id = 200000005236617 AND t_tt_id = tt_id
Query SELECT se_amt, DATE_FORMAT(se_cash_due_date, '%Y-%m-%d'), se_cash_type FROM settlement WHERE se_t_id = 200000005236617
Query SELECT ct_amt, DATE_FORMAT(ct_dts, '%Y-%m-%d %H:%i:%s.%f'), ct_name FROM cash_transaction WHERE ct_t_id = 200000005236617
Query SELECT DATE_FORMAT(th_dts, '%Y-%m-%d %H:%i:%s.%f'), th_st_id FROM trade_history WHERE th_t_id = 200000005236617 ORDER BY th_dts LIMIT 3
Query COMMIT
-- F3
-- F4
Trade-Order
?
代碼中,第 5、6 步是 rollback或 commit,其余四個步驟請參考
TOF1_1 ~ TOF4_2
?
Trade-Order 事務由 EGenDriverCE 執行,它包含「六個 Frame」,是非常巨大的事務。這個事務模擬了客戶、經紀人或授權第三方買賣證券的過程,包括驗證交易執行者的授權、估算交易的財務影響以及提交或取消交易。
- 「獲取客戶信息」:事務首先使用傳入的賬戶 ID 獲取客戶、客戶賬戶和賬戶經紀人的信息。這是為了確保后續操作能夠在正確的賬戶上下文中進行。
- 「驗證執行者」:接下來,事務會驗證執行交易的人是否具有適當的授權。如果執行者未獲授權,事務將回滾。在基準測試執行期間,CE 總是生成授權的執行者。
- 「估算交易影響」:事務的下一步是估算執行交易的總體財務影響。對于限價單,使用請求的價格進行估算;對于市價單,使用當前市場價值。估算過程包括評估交易對現有持倉的影響,計算可能實現的利潤的資本收益稅,以及計算行政費用和經紀人傭金。如果是保證金交易,還會評估客戶賬戶的總資產。
- 「記錄訂單」:使用上述信息記錄訂單。這一步驟確保了交易的詳細信息被正確地保存在系統中,以便后續處理。
- 「提交或回滾」:在完成所有處理后,事務會根據一定的比例選擇提交或回滾。這模擬了實際交易中可能出現的取消訂單或錯誤條件。所有其他事務則被提交。
- 「發送交易到 MEE」:對于成功提交的市價訂單,EGenTxnHarness 會將交易發送到適當的 MEE。這是模擬交易流程的最后一步,確保交易能夠被市場交易所處理。
Trade-Result
Trade-Result 事務由 EGenDriverMEE 執行,它包含「六個 Frame」。這個事務模擬了完成股票市場交易的過程,即經紀行從市場交易所接收到交易的最終確認和價格。客戶的持倉將根據交易的完成情況進行更新,同時生成的估計數據(如經紀人傭金等)將被實際數值替換,并記錄交易的歷史信息以供后續參考。
- 「獲取交易信息」:事務的第一步是使用傳入的交易 ID 獲取交易的相關信息。這包括客戶的賬戶 ID,用于進一步查詢賬戶信息。
- 「更新客戶持倉」:接下來,根據交易的類型(買入或賣出)、涉及的股票數量以及客戶當前的持倉情況(多頭或空頭),更新客戶的持倉。這可能涉及清算現有持倉以覆蓋銷售,或者在購買股票時使用現有空頭持倉。
- 「計算稅款」:如果交易實現利潤且利潤需要繳稅,將計算應繳稅款。
- 「計算經紀人傭金」:計算經紀人的傭金,并將所有與交易相關的信息記錄下來。
- 「提交交易記錄」:最后,為交易創建結算記錄,并在交易不是保證金交易的情況下更新客戶的賬戶余額。
這個事務的設計確保了交易完成后所有必要的更新和記錄都能被正確處理,反映了實際金融系統中交易結算的復雜性。在基準測試中,它有助于評估系統在處理交易結果時的性能和準確性。
下面 sql 是一個例子,但這個例子只走了一部分分支,例如 F2、F3 有一些就沒有走到。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
-- F1
SELECT t_ca_id, t_tt_id, t_s_symb, t_qty, t_chrg, t_lifo, t_is_cash FROM trade WHERE t_id = 200000014584794
SELECT tt_name, tt_is_sell, tt_is_mrkt FROM trade_type WHERE tt_id = _latin1'TMS'
SELECT hs_qty FROM holding_summary WHERE hs_ca_id = 43000012441 AND hs_s_symb = _latin1'BDGPRB'
-- F2
SELECT ca_b_id, ca_c_id, ca_tax_st FROM customer_account WHERE ca_id = 43000012441
-- TRF2_2a INSERT, or TRF2_2b UPDATE
UPDATE holding_summary SET hs_qty = 8700 WHERE hs_ca_id = 43000012441 AND hs_s_symb = _latin1'BDGPRB'
-- MEESUT_STMT_TRF2_3a ASC, or TRF2_3b DESC
SELECT h_t_id, h_qty, h_price FROM holding WHERE h_ca_id = '43000012441' AND h_s_symb = 'BDGPRB' ORDER BY h_dts ASC
-- TRF2_4
INSERT INTO holding_history(hh_h_t_id, hh_t_id, hh_before_qty, hh_after_qty) VALUES('200000013784914', '200000014584794', '700', '600')
-- TRF2_5a or TRF2_5b (DELETE)
UPDATE holding SET h_qty = '600' WHERE h_t_id = '200000013784914'
-- TRF3_1 在這個事務中 miss,如果要尋找,其他事務中可以搜到
-- SELECT sum(tx_rate) FROM taxrate, customer_taxrate WHERE tx_id = cx_tx_id AND cx_c_id = ?
-- TRF4_1
SELECT s_ex_id, s_name FROM security WHERE s_symb = _latin1'BDGPRB'
-- TRF4_2
SELECT c_tier FROM customer WHERE c_id = 4300001245
-- TRF4_3
SELECT cr_rate FROM commission_rate WHERE cr_c_tier = 1 AND cr_tt_id = _latin1'TMS' AND cr_ex_id = _latin1'NASDAQ' AND cr_from_qty <= 100 AND cr_to_qty >= 100
-- TRF5_1
UPDATE trade SET t_comm = 1.14299999999999997e+01, t_dts = _latin1'2024-02-27 20:48:15.000000', t_st_id = _latin1'CMPT', t_trade_price = 2.85799999999999983e+01 WHERE t_id = 200000014584794
-- TRF5_2
INSERT INTO trade_history(th_t_id, th_dts, th_st_id) VALUES(200000014584794, _latin1'2024-02-27 20:48:15.000000', _latin1'CMPT')
-- TRF5_3
UPDATE broker SET b_comm_total = b_comm_total + 1.14299999999999997e+01, b_num_trades = b_num_trades + 1 WHERE b_id = 4300000017
-- TRF6_1
INSERT INTO settlement(se_t_id, se_cash_type, se_cash_due_date, se_amt) VALUES(200000014584794, _latin1'Cash Account', _latin1'2024-02-29', 2.84157000000000016e+03)
-- TRF6_2
UPDATE customer_account SET ca_bal = ca_bal + 2.84157000000000016e+03 WHERE ca_id = 43000012441
-- TRF6_3
INSERT INTO cash_transaction(ct_dts, ct_t_id, ct_amt, ct_name) VALUES(_latin1'2024-02-27 20:48:15.000000', 200000014584794, 2.84157000000000016e+03, _latin1'Market-Sell 100 shared of PREF_B of Bandag, Inc.')
-- TRF6_4
SELECT ca_bal FROM customer_account WHERE ca_id = 43000012441
COMMIT
Trade-Status
Trade-Status 事務由 EGenDriverCE 執行,它包含一個 Frame。這個事務模擬了客戶查看其賬戶最近交易活動摘要的過程,通常是為了回顧最近的交易記錄。
- 「Frame 1」:這個 Frame 負責檢索給定賬戶 ID 的最近 50 筆交易的狀態信息。這包括交易 ID、交易時間、狀態名稱、交易類型名稱、證券符號、交易數量、執行交易的人員名稱、交易費用、證券名稱以及交易所名稱。
-- 設置事務隔離級別為 READ COMMITTED,確保事務在讀取數據時的一致性
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 執行查詢,獲取最近 50 筆交易的狀態信息
SELECT
t_id, DATE_FORMAT(t_dts,'%Y-%m-%d %H:%i:%s.%f'), st_name, tt_name, t_s_symb, t_qty, t_exec_name, t_chrg, s_name, ex_name
FROM
trade, status_type, trade_type, security, exchange
WHERE
t_ca_id = '43000003162' -- 指定客戶賬戶 ID
AND st_id = t_st_id -- 確保交易狀態與交易 ID 匹配
AND tt_id = t_tt_id -- 確保交易類型與交易 ID 匹配
AND s_symb = t_s_symb -- 確保證券符號與交易 ID 匹配
AND ex_id = s_ex_id -- 確保交易所與證券符號匹配
ORDER BY
t_dts DESC -- 按交易時間降序排列
LIMIT 50; -- 限制結果為最近的 50 筆交易
-- 關閉語句
Close stmt;
-- 執行查詢,獲取客戶、賬戶和經紀人的詳細信息
SELECT
c_l_name, c_f_name, b_name
FROM
customer_account, customer, broker
WHERE
ca_id = '43000003162' -- 指定客戶賬戶 ID
AND c_id = ca_c_id -- 確保客戶賬戶與客戶 ID 匹配
AND b_id = ca_b_id; -- 確保經紀人 ID 與客戶賬戶匹配
-- 關閉語句
Close stmt;
-- 提交事務,確保所有查詢結果被正確處理
Query COMMIT;
在這個事務中,首先設置了事務的隔離級別,然后執行了兩個查詢。第一個查詢用于獲取交易狀態信息,第二個查詢用于獲取與交易相關的客戶、賬戶和經紀人的詳細信息。
Trade-Update
Trade-Update 事務由 EGenDriverCE 執行,它包含「三個互斥的 Frame」。每個 Frame 使用不同的技術來查找和更新歷史交易數據。
「Frame 1」
- 接受一組交易 ID 的列表。
- 返回列表中每個交易的信息。
- 對于每個交易,修改執行者的名稱。
-- 查詢特定交易 ID 的執行者名字
SELECT t_exec_name FROM trade WHERE t_id = '200000001949399';
-- 更新執行者名字
UPDATE trade SET t_exec_name = 'Jessica X Lowery' WHERE t_id = '200000001949399';
-- 查詢交易相關信息
SELECT t_bid_price, t_exec_name, t_is_cash, tt_is_mrkt, t_trade_price FROM trade, trade_type WHERE t_id = '200000001949399' AND t_tt_id = tt_id;
-- 查詢結算信息
SELECT se_amt, DATE_FORMAT(se_cash_due_date, '%Y-%m-%d'), se_cash_type FROM settlement WHERE se_t_id = '200000001949399';
-- 查詢現金交易信息
SELECT ct_amt, DATE_FORMAT(ct_dts, '%Y-%m-%d %H:%i:%s.%f'), ct_name FROM cash_transaction WHERE ct_t_id = '200000001949399';
-- 查詢交易歷史記錄
SELECT DATE_FORMAT(th_dts, '%Y-%m-%d %H:%i:%s.%f'), th_st_id FROM trade_history WHERE th_t_id = '200000001949399' ORDER BY th_dts LIMIT 3;
-- 查詢另一個交易 ID 的執行者名字
SELECT t_exec_name FROM trade WHERE t_id = 200000000135883;
-- 更新執行者名字
UPDATE trade SET t_exec_name = _latin1'Roxann Kniffen' WHERE t_id = 200000000135883;
-- 提交事務
COMMIT;
「Frame 2」
- 接受客戶賬戶 ID、開始時間戳、結束時間戳和交易數量(N)作為輸入。
- 返回指定客戶賬戶在指定時間范圍內的前 N 筆交易信息。
- 修改每筆交易的結算現金類型。
-- 設置事務隔離級別為可重復讀
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 查詢特定客戶賬戶 ID 在指定時間范圍內的交易信息
SELECT t_bid_price, t_exec_name, t_is_cash, t_id, t_trade_price FROM trade WHERE t_ca_id = 43000008818 AND t_dts >= _latin1'2005-01-27 13:24:52.109000' AND t_dts <= _latin1'2005-03-14 09:15:00.000000' ORDER BY t_dts ASC LIMIT 20;
-- 對于每筆交易,更新結算類型為 'Cash'
-- 下面的語句會重復多組
SELECT se_cash_type FROM settlement WHERE se_t_id = 200000002704863;
UPDATE settlement SET se_cash_type = 'Cash' WHERE se_t_id = 200000002704863;
-- 查詢并返回與特定交易 ID 相關的結算信息
SELECT se_amt, DATE_FORMAT(se_cash_due_date, '%Y-%m-%d'), se_cash_type FROM settlement WHERE se_t_id = 200000002704863;
-- 查詢并返回與特定交易 ID 相關的現金交易信息
SELECT ct_amt, DATE_FORMAT(ct_dts, '%Y-%m-%d %H:%i:%s.%f'), ct_name FROM cash_transaction WHERE ct_t_id = 200000002704863;
-- 查詢并返回與特定交易 ID 相關的交易歷史記錄
SELECT DATE_FORMAT(th_dts, '%Y-%m-%d %H:%i:%s.%f'), th_st_id FROM trade_history WHERE th_t_id = 200000002704863 ORDER BY th_dts LIMIT 3;
-- 提交事務
Query COMMIT;
「Frame 3」
- 接受證券符號、開始時間戳、結束時間戳和交易數量(N)作為輸入。
- 返回給定證券在指定時間范圍內的前 N 筆交易信息。
- 對于現金交易,修改交易描述。
-- 查詢特定證券符號在指定時間范圍內的交易信息
SELECT t_ca_id, t_exec_name, t_is_cash, t_trade_price, t_qty, s_name, DATE_FORMAT(t_dts, '%Y-%m-%d %H:%i:%s.%f'), t_id, t_tt_id, tt_name FROM trade, trade_type FORCE INDEX(PRIMARY), security WHERE t_s_symb = _latin1'AMGN' AND t_dts >= _latin1'2005-02-09 16:05:31.891000' AND t_dts <= _latin1'2005-03-14 09:15:00.000000' AND tt_id = t_tt_id AND s_symb = t_symb ORDER BY t_dts ASC LIMIT 20;
-- 對于每筆現金交易,更新交易描述
-- 下面的語句會重復多組
SELECT se_amt, DATE_FORMAT(se_cash_due_date, '%Y-%m-%d'), se_cash_type FROM settlement WHERE se_t_id = 200000004055564;
SELECT ct_name FROM cash_transaction WHERE ct_t_id = 200000004055564;
UPDATE cash_transaction SET ct_name = _latin1'Limit-Sell 400 Shares of COMMON of Amgen, Inc.' WHERE ct_t_id = 200000004055564;
-- 查詢并返回與特定交易 ID 相關的結算信息
SELECT ct_amt, DATE_FORMAT(ct_dts, '%Y-%m-%d %H:%i:%s.%f'), ct_name FROM cash_transaction WHERE ct_t_id = 200000004055564;
-- 查詢并返回與特定交易 ID 相關的交易歷史記錄
SELECT DATE_FORMAT(th_dts, '%Y-%m-%d %H:%i:%s.%f'), th_st_id FROM trade_history WHERE th_t_id = 200000004055564 ORDER BY th_dts ASC LIMIT 3;
Data-Maintenance
Data-Maintenance 只有一個 frame,但是這個 frame 非常復雜。可能是由于 time triggered,因此 tpce_50k_sorted_id_time.csv 中并未出現。Data-Maintenance 事務由 EGenDriverDM 執行,它包含一個 Frame。這個事務模擬了對數據庫中主要用作參考的靜態數據進行定期修改的過程。
「Frame 1」
- 這個 Frame 負責執行數據維護操作,這些操作包括更新賬戶權限、地址信息、公司信用評級、客戶電子郵件地址、客戶稅率、市場數據、交易所描述、財務數據、新聞項、證券交易日期、稅率以及觀察列表中的證券符號。
- 每次運行這個事務時,EGenTxnHarness 會提供要修改的表的名稱作為輸入。
- 事務會根據提供的表名選擇下一個要修改的表,這意味著每個表大約每十二分鐘只會被修改一次。
- 對于每個表,事務會執行特定的更新操作,例如更改信用評級、電子郵件地址、稅率等,以保持數據的時效性和準確性。
Trade-Cleanup
Trade-Cleanup 事務由 EGenDriverDM 執行,它包含一個 Frame。這個事務的目的是清理數據庫中的掛起或已提交的交易,以便在測試運行之前將數據庫恢復到已知狀態。
僅在測試開始時執行一次。
「Frame 1」
- 設置事務隔離級別為 READ COMMITTED,確保事務在讀取數據時的一致性。
- 查詢
trade_request
表,獲取所有待處理交易的交易 ID。 - 對于每個待處理的交易,執行以下步驟:
- 在
trade_history
表中插入一條新記錄,表示交易已被提交(SBMT
表示提交)。 - 更新
trade
表,將交易狀態設置為已取消(CNCL
),并記錄當前的日期和時間。 - 再次在
trade_history
表中插入一條新記錄,記錄交易的取消狀態。
這個過程確保了所有未完成的交易都被正確地標記和記錄,以便在測試運行開始時數據庫處于一個干凈的狀態。
-- 設置事務隔離級別為 READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 選擇 trade_request 表中的所有交易 ID 并排序
SELECT tr_t_id FROM trade_request ORDER BY tr_t_id;
-- 為每個交易 ID 插入一條記錄到 trade_history 表,表示交易已提交
INSERT INTO trade_history (th_t_id, th_dts, th_st_id) VALUES ('200000014582105', '2024-02-27 20:48:13', 'SBMT');
-- 重復多次,為每個交易 ID 更新 trade 表,設置狀態為已取消,并記錄時間
UPDATE trade SET t_st_id = 'CNCL', t_dts = '2024-02-27 20:48:13' WHERE t_id = '200000014582105';
-- 為已取消的交易插入一條記錄到 trade_history 表
INSERT INTO trade_history (th_t_id, th_dts, th_st_id) VALUES (200000014582105, _latin1'2024-02-27 20:48:13', _latin1'CNCL');
-- 如果有其他交易 ID,也執行相同的插入和更新操作
-- 例如:
INSERT INTO trade_history (th_t_id, th_dts, th_st_id) VALUES (200000014582119, _latin1'2024-02-27 20:48:13', _latin1'SBMT');
輕松上手:TPCE for MySQL
TPCE 起初只有面向PostgreSQL 的版本,Percona 公司貢獻了針對 MySQL的版本:https://github.com/Percona-Lab/tpce-mysql。
這個版本仍然存在編譯問題,建議通過下面的改版來安裝 tpce-mysql:https://github.com/VincentS/tpcemysql
下面是 Debian 系統的安裝過程。首先安裝 tpcemysql 的依賴項:
# 安裝 unixodbc
sudo apt-get install unixodbc unixodbc-dev
# 安裝 mysql8 的驅動:
wget https://downloads.mysql.com/archives/get/p/10/file/mysql-connector-odbc_8.0.20-1debian9_amd64.deb
sudo dpkg -i mysql-connector-odbc_8.0.20-1debian9_amd64.deb
sudo apt-get install -f
tpcemysql 需要通過 odbc 連接 mysql,因此配置 odbc :
# 設置 odbc 環境變量 /etc/odbcinst.ini
# 若[MySQL ODBC 8.0 Driver]已經存在,則需要先刪除,避免重復
cat /etc/odbcinst.ini
[MySQL ODBC 8.0 Driver]
Description=MySQL ODBC 8.0 Driver
Driver=/usr/lib/x86_64-linux-gnu/odbc/libmyodbc8w.so
Setup=/usr/lib/x86_64-linux-gnu/odbc/libmyodbc8w.so
# 設置 odbc 連接信息
cat /etc/odbc.ini
[MySQLServer_ODBC_NAME]
Description=My MySQL tpce
Driver=MySQL ODBC 8.0 Driver
Server=xxx.xxx.xxx.xxx
Port=3308
User=root
Password=password
Database=tpce
Option=3
接下來編譯 tpce-mysql:
git clone git@github.com:VincentS/tpcemysql.git
cd tpce_mysql
mkdir flat_out
cd prj
make clean
# 修改 makefile
# 將 CCFLAGS=-g -O2 -Wall -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -DHANA_ODBC -DUSE_PREPARE 中的 -DHANA_ODBC 修改為 -DMYSQL_ODBC
cp Makefile.Mysql Makefile
make
生成 + 導入 tpce 數據。
cd ~/tpcemysql
# 生成數據,生成后,數據會寫入 flat_out,等待 LOAD DATA INFILE
./bin/EGenLoader -i flat_in -o flat_out -c 2000 -t 2000 -f 200 -w 50
cd scripts/mysql/
# 首先在 mysql 中創建一個空庫 tpce
# 步驟 1:建表
mysql --local-infile=1 -h 127.0.0.1 -uroot -ppassword -P 3308 -Dtpce < 1_create_table.sql
# 導入數據等等后續操作與步驟 1 類似
mysql --local-infile=1 -h 127.0.0.1 -uroot -ppassword -P 3308 -Dtpce < 2_load_data.sql
mysql --local-infile=1 -h 127.0.0.1 -uroot -ppassword -P 3308 -Dtpce < 3_create_index.sql
mysql --local-infile=1 -h 127.0.0.1 -uroot -ppassword -P 3308 -Dtpce < 4_create_fk.sql
mysql --local-infile=1 -h 127.0.0.1 -uroot -ppassword -P 3308 -Dtpce < 5_create_sequence.sql
運行:
cd ~/tpcemysql
./bin/EGenSimpleTest -c 2000 -a 2000 -f 200 -d 50 -l 200 -e flat_in -j tpce -U root -P password -r 10 -u 10 -t 90 -D MySQLServer_ODBC_NAME
實踐挑戰:給 TPCE 推薦索引
為 MySQL 推薦索引是很常見的優化手段。對于 OLAP 或 OLTP 業務場景都有重要意義。其中,OLAP 業務的難點在于對復雜 join 關系、復雜操作子(子函數、GROUP BY、單值或范圍查詢)的理解,而 OLTP 業務的難點在于【慢 SQL + 基礎 SQL】的綜合理解。
TPCC 和 TPCE benchmark 自身提供了較為合理的普通索引、唯一鍵索引(UK)和外鍵索引(FK),我們將 benchmark 標準索引組合成為 GT(Ground Truth),這是索引推薦算法致力于達到的目標。我們對比了流行的友商開源算法Soar和字節跳動自研算法的SQLBrain的推薦效果。
下面的測試結果展現了 TPC-E 的意義:「TPC-E 顯然是難度更大、挑戰性更高的基準測試。」 由于 TPC-C 過于簡單,Soar 和 SQLBrain 算法都可以達到不錯的效果(超過 GT 性能的 95%),「測試不出差距」。但是 TPC-E 上兩種方法拉開了差距。Soar 推薦的索引僅能達到 14.4 tpsE(GT 性能的16% 左右),而 SQLBrain 仍可以達到 GT 性能的 95% 以上。
為字節跳動ByteBrain團隊自研的MySQL索引推薦系統 「SQLBrain」 打個廣告:「SQLBrain」 在 TPC-E 的推薦效果達到 Ground Truth 的「98%」(對比流行的開源工具 Soar 推薦效果僅達到 「16%」),已經在字節跳動的業務中接入了近x萬個MySQL實例,覆蓋電商、財經、國際支付、直播、廣告等多種業務。相關技術正在準備開源,敬請期待。??
總結
TPC-E 可以被視為 TPC-C 的強化升級版,引入了更復雜的事務、更復雜的關系表和執行邏輯,增大了 OLTP Benchmark 的挑戰性。在 TPC-C 過于簡單、已經被充分優化的今天,TPC-E 作為 一種更復雜的 OLTP Benchmark,可以在索引推薦、性能調參等領域展現作用、挖掘各種算法技術的能力瓶頸。
參考文獻
- TPC-E 官網: https://www.tpc.org/tpce/
- TPC-E pdf 規范:https://www.tpc.org/TPC_Documents_Current_Versions/pdf/TPC-E_v1.14.0.pdf
- Chen, Shimin, et al. "TPC-E vs. TPC-C: Characterizing the new TPC-E benchmark via an I/O comparison study." ACM Sigmod Record 39.3 (2011): 5-10.
- T?zün, P?nar, et al. "From A to E: analyzing TPC's OLTP benchmarks: the obsolete, the ubiquitous, the unexplored." EDBT. 2013.