本文主要分享針對想壓測OceanBase時須要瞭解的一些技術原理。這些建議能夠幫助用戶對OceanBase作一些調優,再結合測試程序快速找到適合業務的最佳性能。因爲OceanBase自身參數不少、部署形態也比較靈活,這裏並無給出具體步驟。node
壓測的本質就是對一個會話的邏輯設計很高的併發。首先須要瞭解單個會話在數據庫內部的讀寫邏輯。好比說,業務會話1對數據庫發起一個DML SQL,第一次修改某筆記錄,數據庫會怎麼作呢?python
爲了便於理解OB的行爲,咱們先看看ORACLE是怎麼作的。後面有對比才能夠加深理解。mysql
ORACLE會話第一次修改一行記錄,若是該記錄所在塊(8K大小)不在內存(Buffer Cache)裏時會先從磁盤文件裏讀入到內存裏。這個稱爲一次物理讀,爲了性能考慮,ORACLE一次會連續讀取相鄰的多個塊。而後就直接在該塊上修改,修改以前會先記錄REDO和UNDO(包括UNDO的REDO)。而後這個數據塊就是髒塊(Dirty Block)。假設事務沒有提交,其餘會話又來讀取這個記錄,因爲隔離級別是讀已提交(READ COMMITTED),ORACLE會在內存裏克隆當前數據塊到新的位置,新塊包含了最新的未提交數據。而後ORACLE在新塊上逆向應用UNDO鏈表中的記錄,將數據塊回滾到讀須要的那個版本(SCN),而後才能讀。這個也稱爲一次一致性讀(Consistency Read),這個新塊也稱爲CR塊。sql
即便是修改一條記錄一個字段的幾個字節,整個塊(8K大小)都會是髒塊。隨着業務持續寫入,大量髒塊會消耗數據庫內存。因此ORACLE會有多重機制刷髒塊到磁盤數據文件上。在事務日誌切換的時候也會觸發刷髒塊操做。若是業務壓力測試ORACLE,大量的寫致使事務日誌切換很頻繁,對應的刷髒操做可能相對慢了,就會阻塞日誌切換,也就阻塞了業務寫入。這就是ORACLE的特色。解決辦法就是加大事務日誌文件,增長事務日誌成員或者用更快的磁盤存放事務日誌和數據文件。數據庫
ORACLE裏一個表就是一個Segment(若是有大對象列還會有獨立的Segment,這個先忽略),Segment由多個不必定連續的extent組成,extent由連續的Block(每一個大小默認8K)組成,extent缺點是可能會在後期因爲頻繁刪除和插入產生空間碎片。緩存
OceanBase會話第一次修改一行記錄,若是該記錄所在塊(64K大小)不在內存(Block Cache)裏時也會先從磁盤文件裏讀入到內存裏。這個稱爲一次物理讀。而後要修改時跟ORACLE作法不一樣的是,OceanBase會新申請一小塊內存用於存放修改的內容,而且連接到前面Block Cache裏該行記錄所在塊的那筆記錄下。若是修改屢次,每次修改都跟前面修改以鏈表形式關聯。一樣在修改以前也要先在內存裏記錄REDO。每次修改都會記錄一個內部版本號,記錄的每一個版本就是一個增量。其餘會話讀取的時候會先從Block Cache中該記錄最先讀入的那個版本(稱爲基線版本)開始讀,而後疊加應用後面的增量版本直到合適的版本(相似ORACLE中SCN概念)。(隨着版本演進,這裏細節邏輯可能會有變化。)服務器
OB的這個讀方式簡單說就是從最先的版本讀起,逐步應用增量(相似REDO,但跟REDO日誌無關)。而ORACLE一致性讀是從最新的版本讀起,逐步回滾(應用UNDO)。在OB裏,沒有UNDO。當版本鏈路很長時,OB的讀性能會略降低,因此OB也有個checkpoint線程定時將記錄的多個版本合併爲少數幾個版本。這個合併稱爲小合併(minor compaction)。此外,OB在內存裏針對行記錄還有緩存,session
從上面過程還能夠看出,每次修改幾個字節,在內存裏的變髒的塊只有增量版本所在的塊(默認寫滿纔會從新申請內存),基線數據塊是一直不變化。因此OB裏髒塊產生的速度很是小,髒塊就能夠在內存裏保存更久的時間。實際上OB的設計就是髒塊默認不刷盤。那若是機器掛了,會不會丟數據呢?併發
OB跟ORACLE同樣,修改數據塊以前會先記錄REDO,在事務提交的時候,REDO要先寫到磁盤上(REDO同時還會發送往其餘兩個副本節點,這個先忽略)。有REDO在,就不怕丟數據。此外,增量部分天天仍是會落盤一次。在落盤以前,內存中的基線數據和相關的增量數據會在內存裏進行一次合併(稱Merge),最終以SSTable的格式寫回到磁盤。若是說內存裏塊內部產生碎片,在合併的那一刻,這個碎片空間基本被消弭掉了。因此說OB的數據文件空間碎片很小,不須要作碎片整理。同時OB的這個設計也極大下降了LSM的寫放大問題。負載均衡
當業務壓測寫OB時,髒塊的量也會增加,最終達到增量內存限制,這時候業務就沒法寫入,須要OB作合併釋放內存。OB的合併比較耗IO、CPU(有參數能夠控制合併力度),而且也不會等到內存用盡才合併,實際會設置一個閾值。同時爲了規避合併,設計了一個轉儲機制。當增量內存使用率超過閾值後,就開啓轉儲。轉儲就是直接把增量內存寫到磁盤上(不合並)。轉儲對性能的影響很小,能夠高峯期發生,而且能夠轉儲屢次(參數配置)。
OB增量內存就相似一個水池,業務寫是進水管在放水, 轉儲和大合併是出水管。水位就是當前增量內存使用率。當進水的速度快於出水,池子可能就會滿。這時候業務寫入就會報內存不足的錯誤。
這就是OB讀寫的特色,解決方法就是加大OB內存、或者容許OB自動對業務寫入速度限流。
OB 在commit的時候redo落盤會寫磁盤。讀數據的時候內存未命中的時候會有物理讀,轉儲和大合併的時候落盤會有密集型寫IO。這些都依賴磁盤讀寫性能。因此建議磁盤都是SSD盤,而且建議日誌盤和數據盤使用獨立的文件系統。若是是NVME接口的閃存卡或者大容量SSD盤,那日誌盤和數據盤放在一塊兒也能夠。不要使用LVM對NVME接口的大容量SSD作劃分,那樣瓶頸可能會在LVM自身。
OB的增量一般都在內存裏,內存不足的時候會有轉儲,能夠轉儲屢次。儘管如此,建議測試機器的內存不要過小,防止頻繁的增量轉儲。一般建議192G內存以上。
OB集羣的節點數至少要有三個。若是是功能瞭解,在單機上起3個OB進程模擬三節點是能夠的,可是若是是性能測試,那建議仍是使用三臺同等規格的物理機比較合適。機器規格不一致時,最小能力的機器可能會制約整個集羣的性能。 OceanBase集羣的手動部署請參考《OceanBase數據庫實踐入門——手動搭建OceanBase集羣》。在部署好OceanBase以後,建議先簡單瞭解一下OceanBase的使用方法,詳情請參考文章《OceanBase數據庫實踐入門——經常使用操做SQL》。
若是要驗證OB的彈性縮容、水平擴展能力,建議至少要6節點(部署形態2-2-2)。而且測試租戶(實例)的每一個Zone裏的資源單元數量至少也要爲2個,才能夠發揮多機能力。這是由於OB是多租戶設計,對資源的管理比較相似雲數據庫思想,因此裏面設計有點精妙,詳情請參見《揭祕OceanBase的彈性伸縮和負載均衡原理》。
下面是一個租戶的測試租戶資源初始化建議
create resource unit S1, max_cpu=2, max_memory='10G', min_memory='10G', max_iops=10000, min_iops=1000, max_session_num=1000000, max_disk_size=536870912; create resource unit S2, max_cpu=4, max_memory='20G', min_memory='20G', max_iops=20000, min_iops=5000, max_session_num=1000000, max_disk_size=1073741824; create resource unit S3, max_cpu=8, max_memory='40G', min_memory='40G', max_iops=50000, min_iops=10000, max_session_num=1000000, max_disk_size=2147483648; select * from __all_unit_config; create resource pool pool_demo unit = 'S2', unit_num = 2; select * from __all_resource_pool order by resource_pool_id desc ; create tenant t_obdemo resource_pool_list=('pool_demo'); alter tenant t_obdemo set variables ob_tcp_invited_nodes='%';
請注意上面的unit_num=2這個很關鍵。若是unit_num=1,OB會認爲這個租戶是個小租戶,後面負載均衡處理時會有個默認規則。
create database sbtest; grant all privileges on sbtest.* to sbuser@'%' identified by 'sbtest';
由於測試場景跟業務有關,這裏就以常見的sysbench場景舉例分析
sysbench工具能夠建幾個結構相同的表,而後執行純讀、純寫、讀寫混合。其中讀又分根據主鍵或者二級索引查詢,等值查詢、IN查詢或範圍查詢幾種。詳細的能夠查看官方介紹。
若是用sysbench壓測OB,建立不少表是一種方法。另一種方法就是建立分區表。OceanBase是分佈式數據庫,數據遷移和高可用的最小粒度是分區,分區是數據表的子集。分區表有多個分區,非分區表只有一個分區。分區表的拆分細節是業務能夠定義的。OceanBase能夠將多個分區分佈到不一樣節點,也可能不會分佈到多個節點。這個取決於OB集羣和租戶的規劃設計。因此對sysbench建立的表(分區),在性能分析時要分析分區具體的位置。
下面是sysbench裏分區表示例,修改 oltp_common.lua:
query = string.format([[ CREATE TABLE sbtest%d( id %s, k INTEGER DEFAULT '0' NOT NULL, c CHAR(120) DEFAULT '' NOT NULL, pad CHAR(60) DEFAULT '' NOT NULL, %s (id,k) ) partition by hash(k) partitions %s %s %s]], table_num, id_def, id_index_def, part_num, engine_def, extra_table_options)
須要注意用分區表後,主鍵列和惟一索引列須要包含分區鍵。分區表的索引有本地(LOCAL)索引和全局索引兩種。本地索引的存儲跟數據是在一塊兒的,全局索引的存儲是獨立的。全局索引是爲了應對查詢條件不是分區鍵的場景,沒有全局索引時,會掃描全部分區的本地索引。這兩種索引的性能優劣沒有定論,以實際業務場景測試爲準。此外,傳統數據庫索引對修改操做會有負面影響,分佈式數據庫的全局索引對修改操做的影響可能更大,由於一個簡單的DML語句都會由於要同步修改全局索引而產生分佈式事務。而本地索引就沒有分佈式事務問題。因此對全局索引的使用場景要謹慎評估。這個特性不是OB特有,只要是分佈數據庫都會面臨這個問題。
本節是說明OB數據分佈背後的原理和方法。
OceanBase是分佈式數據庫,它經過每一個機器上的observer進程將多個機器的資源能力聚合成一個大的資源池,而後再爲每一個業務分配不一樣資源規格的租戶(實例)。因此每一個業務租戶(實例)的資源能力都只是整個集羣能力的子集。業務並不必定能使用到所有機器資源。
OceanBase裏的數據都有三份,細到每一個分區有三個副本,角色上有1個leader副本2個follower副本。默認只有leader副本提供讀寫服務,leader副本所在的節點纔有可能提供服務,有負載。OceanBase調整各個節點負載的方法是經過調整內部leader副本的位置實現的。這個調整可讓OceanBase自動作,也能夠手動控制。
OceanBase自動負載均衡是參數enable_rebalance控制,默認值爲True。能夠查看確認。修改用alter system語句。
alter system set enable_rebalance=True; show parameters like 'enable_rebalance';
查看實際表的分區leader副本位置
##查看分區分佈 SELECT t5.tenant_id,t5.tenant_name,t3.database_name, t4.tablegroup_name, t2.table_name, t1.partition_id, concat(t1.svr_ip,':',t1.svr_port) observer, t1.role, t1.data_size,t1.row_count from `gv$partition` t1 join `__all_table` t2 on (t1.tenant_id=t2.tenant_id and t1.table_id=t2.table_id) join `__all_database` t3 on (t2.tenant_id=t3.tenant_id and t2.database_id=t3.database_id) left join `__all_tablegroup` t4 on (t1.tenant_id=t4.tenant_id and t1.tablegroup_id=t4.tablegroup_id) join `__all_tenant` t5 on (t1.tenant_id=t5.tenant_id) where t5.tenant_id = 1020 and role=1 and database_name in ('sysbenchtest') order by t5.tenant_id,t3.database_id,t1.tablegroup_id,t1.partition_id, t1.role;
sysbench的機制是壓測過程當中若是有錯誤就會報錯退出,因此須要針對一些常見的錯誤進行忽略處理,這樣sysbench會話能夠重試繼續運行。好比說主鍵或者惟一鍵衝突、事務被殺等等。
下面的運行命令供參考
./sysbench --test=./oltp_read_only.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser" --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=32 --time=300 --report-interval=5 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016 prepare
./sysbench --test=./oltp_read_only.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser" --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=96 --time=600 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016 --secondary=on run
./sysbench --test=./oltp_write_only.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser" --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=32 --time=600 --report-interval=5 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 run
./sysbench --test=./oltp_read_write.lua --mysql-host=***.***.82.173 --mysql-port=4001 --mysql-db=test --mysql-user="sbuser" --mysql-password=sbtest --tables=16 --table_size=100000000 --threads=32 --time=600 --report-interval=5 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 run
在純寫或者讀寫測試中,注意觀察增量增量內存使用進度。若是寫入速度比轉儲和合並速度還快,那會碰到內存不足寫入失敗錯誤。這就是OB租戶資源相對不足了。觀察這個內存使用進度能夠經過 dooba腳本。dooba腳本默認在/home/admin/oceanbase/bin/目錄下。
使用示例以下:
python dooba -h11..84.84 -uroot@sys#obdemo -P2883 -p**
OB有個內部視圖gv$sql_audit能夠查看執行過全部成功或失敗的SQL,用來分析具體的SQL性能。用法詳情參見官網(oceanbase.alipay.com) 或 文章《阿里數據庫性能診斷的利器——SQL全量日誌》。
select /*+ read_consistency(weak) query_timeout(1000000000) */ usec_to_time(request_time) req_time, svr_Ip, trace_id, sid, client_ip, tenant_id,tenant_name,user_name,db_name, query_sql, affected_rows,ret_code, event, state, elapsed_time, execute_time, queue_time, decode_time, get_plan_time, block_cache_hit, bloom_filter_cache_Hit, block_index_cache_hit, disk_reads,retry_cnt,table_scan, memstore_read_row_count, ssstore_read_row_count, round(request_memory_used/1024/1024) req_mem_mb from gv$sql_audit where tenant_id=1012 and user_name in ('demouser') order by request_time desc limit 100;
本節是一些測試場景的經驗總結,須要提早了解一些OceanBase的原理特性介紹。
實際測試情形可能會出現因爲是三節點部署,因此測試時壓力都打到一臺服務器上,這個建議用六節點測試。還有個辦法就是禁用自動負載均衡手動調整分區位置。調整是OB給業務的手段,業務上有些表會有JOIN,爲了性能(避免跨節點請求),業務須要這種干預能力。
還有一種情形是寫入壓力很是大,跑了一段時間後報內存不足的提示,這個就是租戶內存資源相對寫入速度和量不足了(OB的轉儲和合並對內存的回收趕不上寫入對內存的消耗),此時須要擴容或者調整測試需求。OB 2.x版本還有自動對應用寫入限速功能(自我保護),這個會影響測試報告裏性能結果。若是數據庫分析sql性能以及分佈式調優都作了,那能夠認爲當前寫入的TPS就是數據庫的寫入峯值了。須要注意的是不一樣的硬件,不一樣的租戶規格,不一樣的測試場景,這個TPS能力都表現不一樣。
sysbench有個batch insert功能,當表是分區表的時候,默認這個batch insert 極可能會產生分佈式事務,性能比單表寫入要慢。須要靠提升客戶端併發數來提高總的吞吐量。此外這個批量的大小不宜太大。因爲OB支持SQL執行計劃緩存,SQL文本過大且併發很高時,會在SQL解析環節面臨內存不足問題。數據庫內存的大部分仍是主要用於存取數據。JAVA的addBatch方法也同理,建議批量大小設置爲100之內。
在查詢驗證數據的時候,可能會碰到超時類錯誤。OB裏超時的可能場景有多個:
以上超時時間均可以在租戶里根據實際狀況修改。
關於使用分佈式數據庫測試,不一樣的作法能夠獲得不一樣的結果。即便是同一個OB租戶(實例),不一樣的人設計的表結構不一樣,或者SQL不一樣都有可能取得不一樣的性能數據。這些不一樣都是能夠從原理上給出解釋。熟知這些原理的更容易發揮OB的分佈式數據庫特色。使用越深刻會發現這個原理越多且更加有趣。固然環境、場景的不一樣,仍是會遇到一些問題。若是有碰到問題,歡迎公衆號留言討論。
原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。