oracle group by用法_从OceanBase TPCC测试报告看ORACLE兼容性进展
概述
2019年10月2日,TPC委員會在官網(tcp.org)發布了?TPC-C榜單的最新測試報告:OceanBase數據庫TPC-C測試披露報告。下載地址:http://www.tpc.org/results/fdr/tpcc/ant_financial~tpcc~alibaba_cloud_elastic_compute_service_cluster~fdr~2019-10-01~v01.pdf?。
此次OceanBase集群使用了207臺云服務器,客戶端壓測程序使用了64臺云服務器。結果跑到6088萬tpmc,并且8小時持續運行性能抖動不超過0.5%。
當然本文不是來介紹OB的TPC-C性能,而是從披露的測試報告里看看OceanBase新增的幾個功能。如視圖、復制表、存儲過程等。
復制表功能
分布式數據庫的痛點
不管是哪種架構的分布式數據庫,當集群規模變大的時候,不同業務表的數據很大概率會在不同的節點上。此時如果這兩個表要做表連接,在分布式數據庫內部就會是一個跨節點的請求。相比節點內部的表連接,跨節點的表連接性能會有明顯下降。尤其是非拆分表和拆分表做表連接時,這種跨節點請求很難避免。
分布式數據庫中間件使用小表廣播方案,將非拆分表冗余到所有分庫內部,這樣把這個分布式表連接下推到各個分庫內部變成本地表連接,性能有明顯提升。小表廣播的原理是中間件自動在各個分庫內部創建這個表,內部組件會解析底層源表所在數據庫增量日志(多是MySQL)并生成sql應用到所有分庫內的表上。這個數據同步是獨立于底層數據庫外部做的邏輯同步。所以理論上它并沒有很好的高可用能力,節點故障時也不能保證數據強一致。盡管理論上不完美,但由于小表通常是配置表,記錄變化少,而其帶來的好處卻非常明顯,導致小表廣播非常受開發歡迎,成為分布式數據庫中間件產品的基本標配。
在OceanBase內部,水平拆分的方案是使用分區。分區是數據遷移、高可用的最小單元。OceanBase為了避免某些分區跨節點連接,使用了表分組(tablegroup)的技術,將業務上聯系緊密的分區約束在一個節點內部。但對于非分區表和分區表的表連接,以前就沒有好的辦法規避跨節點請求了。OceanBase 2.2.x 新增了表的復制表屬性。當表是復制表的時候,OceanBase會保證主副本到其他所有副本的同步為全同步。
復制表功能
復制表的語法就是在普通建表語法通過locality在租戶所有節點創建只讀副本(其中?F就是默認的全功能副本,R就是只讀副本,后面是描述其分布范圍),同時通過duplicate_scope指定復制表屬性應用范圍。如果是復制表,則主副本到所有備副本的Redo同步為全同步,性能上比Paxos協議同步Redo可能會更慢一些。不過由于用于復制表的數據變更通常不多,這點影響可以忽略。
下面創建2個表結構一致的表,一個是復制表,一個是普通表。下面的sql是在tpc-c的測試schema做了一些細微的改動,方便這里演示。
DROP tablegroup tpcc_group;create tablegroup tpcc_group binding true partition by hash partitions 8;
DROP TABLE ordr;
create table ordr (
o_id number not null,
o_d_id number not null,
o_w_id number not null,
o_c_id number,
o_entry_d date,
o_carrier_id number,
o_ol_cnt number,
o_all_local number,
index iordr(o_w_id, o_d_id, o_c_id, o_id) storing (o_entry_d,o_carrier_id,o_ol_cnt ) local BLOCK_SIZE=8192,
primary key ( o_w_id, o_d_id, o_id )
)tablegroup tpcc_group COMPRESS FOR QUERY pctfree=0 BLOCK_SIZE=8192 progressive_merge_num=1 USE_BLOOM_FILTER = TRUE
partition by hash(o_w_id) partitions 8;
drop table ordl;
create table ordl (
ol_o_id number not null,
ol_d_id number not null,
ol_w_id number not null,
ol_number number not null,
ol_i_id number,
ol_supply_w_id number,
ol_delivery_d date,
ol_quantity number,
ol_amount decimal(6,2),
ol_dist_info char(24),
primary key (ol_w_id, ol_d_id, ol_o_id, ol_number )
)tablegroup tpcc_group COMPRESS for QUERY pctfree=0 BLOCK_SIZE=8192
partition by hash(ol_w_id) partitions 8;
create table item (i_id int
, i_name varchar(24)
, i_price decimal(5,2)
, i_data varchar(50)
, i_im_id int
, primary key(i_id)
) COMPRESS FOR QUERY pctfree=0 BLOCK_SIZE=16384
duplicate_scope='cluster' locality='F,R{all_server}@zone1, F,R{all_server}@zone2,F,R{all_server}@zone3' primary_zone='zone1';
create table item2 (i_id int
, i_name varchar(24)
, i_price decimal(5,2)
, i_data varchar(50)
, i_im_id int
, primary key(i_id)
) COMPRESS FOR QUERY pctfree=0 BLOCK_SIZE=16384
確認一下相關幾個表主副本的位置。可以看出表ordr和ordl是分區表,同一個分區表不同分區的主副本分布在不同節點上,兩個分區表在一個表分組里,所以同號分區在一個節點內部。item表只有一個主副本,其他副本是備副本(下圖備副本位置沒有查出)。
SELECT t1.tenant_id,t1.tenant_name,t2.database_name,t3.table_id, t3.table_Name, t3.tablegroup_id, t3.part_num , t4.partition_Id, t4.svr_ip, t4.rolefrom `gv$tenant` t1 join `gv$database` t2 on (t1.tenant_id=t2.tenant_id)
join gv$table t3 on (t2.tenant_id=t3.tenant_id and t2.database_id=t3.database_id and t3.index_type=0)
left join __all_virtual_meta_table t4 on (t3.tenant_id=t4.tenant_id and (t3.tablegroup_id=t4.table_id or t3.table_id=t4.table_id))
where t1.tenant_id=1002 and t4.role in (1) and table_name in ('ordr','ordl','item','item2')
order by tablegroup_id, table_name , partition_id
;
備注:?以前給的常用SQL里查詢分區位置的SQL,在OB 2.2.x版本后要修改一下。當多個表在一個分區組內時,__all_virtual_meta_table的table_id列就是分區組的id;否則就是普通表的table_id。
復制表執行計劃對比
首先看普通表的三表連接,其中item2表是普通非分區表。所以三表連接,很可能就有跨節點的請求。從執行計劃里的?EXCHANGE IN/OUT?可以確認。
再看復制表的三表連接,其中item表是普通非分區表,但是是復制表。三表連接的時候,分區表ordr和ordl會選取所在節點的item的備副本(或只讀副本,這里沒有只讀副本)進行本地連接。從執行計劃里也可以確認,沒有遠程執行計劃。
實踐經驗
不管是小表廣播還是復制表,都有個前提就是非拆分表(或非分區表)的更新不要太頻繁。根據小表廣播的客戶使用經驗看,不少客戶會濫用這個功能,把很多表設置為廣播表。有的甚至為了解決不同拆分維度表之間的連接性能問題而將拆分表設置為廣播表,結果給后臺同步任務增加很多不必要的壓力,導致廣播表的延時更大。
對于OceanBase而言,同樣也不建議把分區表設置為復制表(雖然技術上是可以),那個違背了分區表的設計初衷。
復制表的備副本或只讀副本如果出現故障,OceanBase會臨時將該副本從全同步成員里拉黑。當該節點恢復時或者OceanBase集群補齊了該節點上原有副本時,會重新將該副本贖回到全同步成員列表里。所以復制表的高可用也是自動保證的。
可更新視圖功能
視圖是數據庫常用的功能,通常用來查詢。用視圖再跟其他表做關聯查詢時,執行計劃往往會很復雜。適當的將一些條件下推到視圖內部,可以提升視圖關聯查詢的性能。
視圖還有一個特殊用法就是用于更新,這有一些前提條件,跟ORACLE可更新視圖原理一樣,不同之處在于OceanBase視圖的數據可以是分布式的。即使更新產生了分布式事務,OceanBase也是保證視圖數據強一致的。
下面直接看一個可更新視圖的UPDATE例子。
CREATE OR REPLACE VIEW stock_item ASSELECT /*+ leading(s) use_merge(i) */
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,
s_dist_02,
s_dist_03,
s_dist_04,
s_dist_05,
s_dist_06,
s_dist_07,
s_dist_08,
s_dist_09,
s_dist_10
FROM stok s, item i WHERE s.s_i_id = i.i_id;
explain update stock_item set s_order_cnt = s_order_cnt + 1 where s_w_id=2 ;
看執行計劃
存儲過程
OceanBase從2.0版本就開始支持自定義函數、存儲過程和匿名塊了。對ORACLE存儲過程兼容的能力是逐步完善的。此次TPC-C的核心業務邏輯全部是通過存儲過程實現的。下面就選取其中幾個存儲過程示例看看OceanBase存儲過程最新能力。
支持存儲過程入參和出參
存儲過程的參數可以是普通類型,也可以是表變量。
從上圖還可以看出OceanBase存儲過程支持自定義異常。-6235這個異常是序列化異常,這點也是兼容ORACLE的。主要在隔離級別serializable下才會出現。
支持?bulk collect into和returning子句
可以將更新或者查詢的結果返回到表變量里。
支持隱式游
如?sql%rowcount?表示返回受影響的行數。sql%found?表示是否有行受影響等等。
支持自定義異常的捕獲
異常的定義在存儲過程前面聲明了。
下面是查詢訂單狀態的存儲過程內容。
CREATE OR REPLACE PROCEDURE orderstatus (
ware_id INTEGER,
dist_id INTEGER,
cust_id IN OUT INTEGER,
bylastname BINARY_INTEGER,
cust_last IN OUT VARCHAR2,
cust_first OUT VARCHAR2,
cust_middle OUT VARCHAR2,
cust_balance OUT NUMBER,
ord_id IN OUT INTEGER,
ord_entry_d OUT VARCHAR2,
ord_carrier_id OUT INTEGER,
ord_ol_cnt OUT INTEGER,
oline_supply_w_id OUT intarray,
oline_i_id OUT intarray,
oline_quantity OUT intarray,
oline_amount OUT numarray,
oline_delivery_d OUT datarray
)
IS
TYPE number_array IS TABLE OF number(38);
rowArr number_array := number_array();
read_nothing EXCEPTION;
PRAGMA EXCEPTION_INIT(read_nothing,-4026);
BEGIN
IF bylastname != 0 THEN
SELECT c_id BULK COLLECT INTO rowArr
FROM cust
WHERE c_last = cust_last AND c_d_id = dist_id AND c_w_id = ware_id
ORDER BY c_first;
IF 0 = rowArr.COUNT THEN
RAISE read_nothing;
END IF;
cust_id := rowArr((rowArr.COUNT + 1) / 2);
END IF;
SELECT c_last, c_first, c_middle, c_balance
INTO cust_last, cust_first, cust_middle, cust_balance
FROM cust
WHERE c_w_id = ware_id AND c_d_id = dist_id AND c_id = cust_id;
/* Select the last ORDER for this customer. */
SELECT o_id, o_entry_d_c, o_carrier_id_n, o_ol_cnt
INTO ord_id, ord_entry_d, ord_carrier_id, ord_ol_cnt
FROM (
SELECT o_id, to_char(o_entry_d, 'DD-MM-YYYY.HH24:MI:SS') as o_entry_d_c, nvl(o_carrier_id,0) as o_carrier_id_n, o_ol_cnt
FROM ordr
WHERE o_w_id = ware_id AND o_d_id = dist_id AND o_c_id = cust_id
ORDER BY o_w_id, o_d_id, o_c_id, o_id DESC
) tmp
WHERE rownum <= 1;
SELECT nvl(ol_delivery_d, DATE'1911-09-15') del_date, ol_amount, ol_i_id, ol_supply_w_id, ol_quantity
BULK COLLECT INTO oline_delivery_d, oline_amount, oline_i_id, oline_supply_w_id, oline_quantity
FROM ordl
WHERE ol_w_id = ware_id AND ol_d_id = dist_id AND ol_o_id = ord_id;
COMMIT;
END orderstatus;
/
CREATE OR REPLACE PROCEDURE stocklevel (
ware_id INTEGER,
dist_id INTEGER,
threshold INTEGER,
low_stock OUT INTEGER
)
IS
BEGIN
SELECT count (DISTINCT s_i_id)
INTO low_stock
FROM ordl, stok, dist
WHERE ol_w_id = ware_id AND d_id = dist_id AND d_w_id = ware_id AND
d_id = ol_d_id AND d_w_id = ol_w_id AND
ol_i_id = s_i_id AND ol_w_id = s_w_id AND
s_quantity < threshold AND
ol_o_id BETWEEN (d_next_o_id - 20) AND (d_next_o_id - 1);
COMMIT;
END stocklevel;
/
下面是一個簡單存儲過程的PL/SQL調用示例。
備注:這個PL/SQL的執行用了MySQL的delimiter 關鍵字改變結束符。后記
本文只是從OceanBase TPC-C的報告里窺探OceanBase幾個特殊的功能,尤其是ORACLE兼容性的能力。隨著版本的升級,更多ORACLE的功能會在ORACLE租戶里支持。敬請持續關注!
備注:?目前OceanBase對外輸出的一個重要場景就是數據庫國產化,主要用于替換ORACLE。OceanBase產品策略是,只要是客戶覺得好用的ORACLE功能在OceanBase里還不支持,OceanBase就會排期去支持。有興趣的客戶歡迎聯系我(關注公眾號,發送“加好友”)。
參考
- 分布式數據庫選型——數據水平拆分方案
- OceanBase 2.1 的ORACLE兼容性能力探秘
- 分布式數據庫的快照隔離級別案例對比分析
OceanBase數據庫實踐入門——常用操作SQL
總結
以上是生活随笔為你收集整理的oracle group by用法_从OceanBase TPCC测试报告看ORACLE兼容性进展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python测试udp端口_Linux系
- 下一篇: python 趋势线表达式_python