寫在前面 目前,在系統設計中引入了越來越多的NoSQL產品,例如Redis/ MongoDB/ HBase等,其中性能指標往往會成為權衡不同NoSQL產品的關鍵因素。對這些產品在性能表現和產品選擇上的爭論,Ivan碰到不止一次。雖然通過對系統架構原理方面的分析可以大致判斷出其在不同讀寫場景下的表現, ...
寫在前面
目前,在系統設計中引入了越來越多的NoSQL產品,例如Redis/ MongoDB/ HBase等,其中性能指標往往會成為權衡不同NoSQL產品的關鍵因素。對這些產品在性能表現和產品選擇上的爭論,Ivan碰到不止一次。雖然通過對系統架構原理方面的分析可以大致判斷出其在不同讀寫場景下的表現,但一是對受眾有較高的要求,也來的不那麼直接。這時候,沒有什麼比一次性能測試更有說服力。有什麼好的性能測試工具呢?這就是今天的主角YCSB。YCSB是Yahoo開源的一套分散式性能測試工具,方便易用,拓展性強。Ivan最近研究HBase二級索引時用它來做性能測試,感覺還是非常順手的。雖然網上已經有很多YCSB的介紹文章,但用來指導實際操作還是有些不便。Ivan會用兩三篇文章來介紹一下YCSB的實際使用。本文是官方文章的譯文,選擇這篇文章是因為其與具體操作的關係比較緊密,感興趣的同學可以瞭解一下。
原文地址:https://github.com/brianfrankcooper/YCSB/wiki/Running-a-Workload
正文
運行workload有六個步驟
- 安裝待測試的資料庫系統
- 選擇適當的DB介面層
- 選擇適當的工作負載
- 選擇適當的運行參數(客戶端線程數量,目標吞吐等)
- 載入數據
- 執行工作負載(workload)
這些步驟描述都假定你運行一個單客戶端。這可以滿足中小規模集群(10台左右)的測試需要。對於更大規模的集群,你必須在不同的伺服器上運行多個客戶端來生成足夠的負載。類似地,在某些場景下,多客戶機載入資料庫可能更快。多客戶端並行運行的更多細節,可以查看Running a Workload in Parallel
Setp 1.安裝待測試的資料庫
第一步是安裝你希望測試的資料庫。可能是單機或者集群,依賴於你要測試的配置。
你必須create 或 set up tables/keyspaces/storage buckkets用於存儲記錄。這些細節對於每個資料庫都不同,依賴於希望運行的負載情況。在YCSB客戶端運行前,數據表必須被創建,因為客戶端自身是不會請求創建資料庫表的。這是因為對於某些系統創建表一個手工操作,而對於其他系統,表必須在集群啟動前被創建。
workload所依賴的table必須被創建。對於核心負載,YCSB客戶端將假定存在一個名為'usertable'的table,且具有靈活的schema:運行時可以根據需要增加列。'usertable'可以被映射為適當的存儲容器。例如,在MySQL中,你可以create table,在Cassandra你可以在配置中定義keyspace。資料庫介面層(Step 2描述)會收到讀寫usertable的請求,將其轉換為你所指定的實際存儲的請求。這意味著你必須提供資料庫介面層幫助它理解下層存儲的結構。例如,在Cassandra中,你必須定義在keyspace中定義列族column families。這樣,必須創建一個列族並命名(例如,你可以使用values)。然後,資料庫訪問層需要理解指向values列族,或者將字元串“values”設置為一個屬性,或者在資料庫介面層中硬編碼。
Step 2. 選擇適當的資料庫介面層
資料庫介面層是一個可執行的java類,實現read、write、update、delete和scan調用,它由YCSB客戶端生成,調用你的資料庫API。這個類是com.yahoo.ycsb包下抽象類DB的子類。在運行YCSB客戶端時,你要通過命令行指定類名,客戶端會動態載入你的介面類。命令行中指定的任何屬性或指定的參數文件,將會傳遞給資料庫介面層實例,用於配置該層(例如,告訴它你要測試的資料庫主機名hostname)
YCSB客戶端自帶一個簡單的虛擬介面層,com.yahoo.ycsb.BasicDB。這層會把執行的操作通過System.out列印。這可以用於確認客戶端在正常運行,用於debug 你的workload。
如何使用YCSB客戶端的細節可以查看 Using the Database Libraries。更多實現資料庫介面層的細節,可以查看 Adding a Database
你可以使用YCSB命令,直接運行資料庫命令。客戶端使用DB介面層發送命令給資料庫。你可以使用客戶端確定DB層運行正常,你的資料庫正確安裝,DB層可以連接到資料庫等等。它為大量的資料庫提供了命令行介面,可以用於檢驗資料庫的數據。運行命令行:
$ ./bin/ycsb shell basic
help
Commands:
read key [field1 field2 ...] - Read a record
scan key recordcount [field1 field2 ...] - Scan starting at key
insert key name1=value1 [name2=value2 ...] - Insert a new record
update key name1=value1 [name2=value2 ...] - Update a record
delete key - Delete a record
table [tablename] - Get or [set] the name of the table
quit - Quit
Step 3 選擇適當的工作負載
工作負載定義了在loading階段將被載入進資料庫的數據,在transaction階段在數據集上執行的操作。
典型的工作負載包括以下內容:
Workload java class(com.yahoo.ycsb.Workload的子類)
Parameter file(Java Properties格式)
因為數據集的參數屬性必須在兩個階段被獲得,在loading階段用於構造和插入適當類型的記錄,在transaction階段用於指定正確的記錄id和field,所以參數文件在兩個階段都會使用。workload java class使用這些屬性插入記錄(loading phase)或操作那些記錄(transaction phase)。選擇哪個階段要看你運行YCSB命令行時指定的參數。
在運行YCSB客戶端的命令行時,你可以指定java class和參數文件。客戶端將動態載入你的workload class,從參數文件解析參數(和任何命令行的附加參數)並執行workload。在loading和transaction兩個階段,需要同樣的屬性和workload邏輯應用。例如,如果loading階段創建10個field的記錄,而後在transaction階段必須知道有10個field可以被查詢和修改。
YCSB自帶的CoreWorkload 是標準workload包可以直接使用。CoreWorkload定義了簡單的read/insert/update/scan操作組合。在參數文件中定義了每種操作的相應頻率,以及其他workload屬性。這樣,修改參數文件可以執行不同的workload。更多CoreWorkload的細節,可以查看Core Workloads
如果CoreWorkload不能滿足你的需求,你可以基於com.yahoo.ycsb.Workload定義自己的workload子類。細節可以查看 Implementing New Workloads
Step 4 選擇適當的運行參數
雖然workload class和參數文件定義了具體的workload,還有一些在運行特定測試時,你可能希望指定附加的設置。這些設置可以通過YCSB客戶端命令行實現。設置包括
- -threads: 客戶端線程數量。預設,YCSB客戶端使用一個worker線程,但可以指定。這通常用來根據資料庫增加負載數量。
- -target:每秒操作目標數量。預設,YCSB客戶端會嘗試儘可能多的操作。例如,每個操作平均花費100ms,客戶端將在每個線程中每秒執行10個操作。然而,你可以限流每秒操作的數量。例如,產生一個延遲與吞吐量的曲線,你可以嘗試不同的目標吞吐量,衡量每次產生的延遲。
- -s :status. 對於一個長時間運行的workload,它是有用的,可以獲得客戶端狀態報告,判斷他是否崩潰並給你一些過程中的信息。通過命令行指定"-s",客戶端可以每10秒報告狀態到stderr。
Step 5. 載入數據
Workload有兩個執行階段:loading階段(定義待插入的數據)和transaction階段(定義數據集上的操作)。為了載入數據,你要運行YCSB客戶端並告訴它執行loading階段。
例如,考慮workload A的benchmark(更多標準workloads細節在Core Workloads)。載入標準數據集
$ ./bin/ycsb load basic -P workloads/workloada
這個命令的關註點
load 參數告訴客戶端執行loading 階段
basic 參數告訴客戶端使用BasicDB層。你可以在參數文件中指定這個屬性,使用'db'屬性(例如db=com.yahoo.ycsb.BasicDB)
-P參數用來載入property文件。這個例子中,我們用來載入workload參數文件。
載入HBase數據集:
$ ./bin/ycsb load hbase -P workloads/workloada -p columnfamily=family
這個命令的關註點
- load 參數是告訴Client執行loading階段操作。
- hbase 參數是告訴Client使用HBase layer
- -P 參數是用來載入參數文件,例子中我們用來載入workload參數文件
- -p 參數用於設置參數,在這個HBase例子中,我們用來設置資料庫的列。你資料庫中存在usertable帶有family列,以執行這個命令。而後所有數據被載入到資料庫 usertable 帶有family列。
- 執行命令前,確定你已經啟動了 Hadoop和 HBase
如果你使用BasicDB,你將看到資料庫的insert語句。如果是你用一個實際的DB介面層,記錄會被載入到資料庫中。
標準workload參數文件創建很小的資料庫,例如,workload僅創建1000條記錄。這用於調試你的安裝。然而,運行一個實際的benchmark你需要創建一個更大的資料庫。例如,想象你需要載入100百萬記錄。然後,你需要修改workload文件中預設的“recordcount”屬性。有兩個辦法實現。
指定一個新的屬性文件,包含recordcount的新值。在命令行中,如果這個文件在workload文件後被指定,它會覆蓋workload的任何屬性。例如創建"large.dat"文件,僅有一行內容
recordcount=100000000
然後,client執行以下內容
$ ./bin/ycsb load basic -P workloads/workloada -P large.dat
Client會載入所有的屬性文件,但使用最後載入的一個文件large.dat中的recordcount值,
通過命令行指定recordcount屬性的新值。在命令行指定的任何屬性都會覆蓋配置文件中的屬性。如下執行
$ ./bin/ycsb load basic -P workloads/workloada -p recordcount=100000000
一般來說,好的實踐是在新的參數文件中存儲任何重要的參數,代替通過命令行指定它們。這使得你的benchmark結果可以被覆現。不用必須重建你使用的命令行,你重用參數文件即可。註意,當它開始執行時,YCSB Client會列印處他的命令行,所以如果你將Client的輸出存儲到一個數據文件,你可以很容易重新執行命令行。
因為一個大資料庫載入需要很長時間,你可能希望1.需要Client輸出狀態,2.直接將輸出寫入數據文件。這樣,你可以執行以下命令載入資料庫。
$ ./bin/ycsb load basic -P workloads/workloada -P large.dat -s > load.dat
-s 參數將要求Client向stderr輸出狀態報告。這樣命令行的輸出可能是這樣
$ ./bin/ycsb load basic -P workloads/workloada -P large.dat -s > load.dat
Loading workload... (might take a few minutes in some cases for large data sets)
Starting test.
0 sec: 0 operations
10 sec: 61731 operations; 6170.6317473010795 operations/sec
20 sec: 129054 operations; 6450.76477056883 operations/sec
...
這個狀態輸出會幫助你看到載入操作執行得多快(這樣你可以估計載入的完成時間),確認load正在執行。當load完成時,Client會報告load的性能統計數據。這些統計與transaction階段一樣,所以看後續介紹
Step 6 執行workload
一旦數據被載入,你就可以執行workload。告訴Client執行transaction操作。執行workload,可以使用以下命令
$ ./bin/ycsb run basic -P workloads/workloada -P large.dat -s > transactions.dat
主要差別是我們使用run參數時,告訴Client執行transaction階段而不是loading階段。如果你使用BasicDB,檢查結果文件 transactions.dat,你會看到一個read和update混合的請求,與統計數據一致。
典型情況下,你會希望使用 -threads 和 -target 參數控制負荷量。例如,你可能希望10個線程每秒總數100個操作。平均操作延時不高於100ms,每個線程能夠攜帶每秒10此操作。一般來說,你需要足夠的線程因為沒有線程嘗試每秒更多的操作,否則你達到的吞吐量將小於指定的目標吞吐量。
這個例子,我們可以執行
$ ./bin/ycsb run basic -P workloads/workloada -P large.dat -s -threads 10 -target 100 > transactions.dat
註意這個例子,我們使用 -threads 10 命令參數指定10個線程, -target 100 命令參數指定每秒100次操作。否則,兩個值可以設置在你的參數文件中,使用threadcount 和 target 屬性代替。例如
threadcount=10
target=100
run的結尾,Client會向stdout報告性能統計數據。上面的例子,統計數據會寫入transaction.dat文件。預設包括每個操作類型延時的average,min,max,95th,99th。每次操作返回代碼的統計,每類操作的直方圖。返回值被你的DB介面層定義,允許你看到workload過程中的任何錯誤。上述例子中,我們可以得到輸出:
[OVERALL],RunTime(ms), 10110
[OVERALL],Throughput(ops/sec), 98.91196834817013
[UPDATE], Operations, 491
[UPDATE], AverageLatency(ms), 0.054989816700611
[UPDATE], MinLatency(ms), 0
[UPDATE], MaxLatency(ms), 1
[UPDATE], 95thPercentileLatency(ms), 1
[UPDATE], 99thPercentileLatency(ms), 1
[UPDATE], Return=0, 491
[UPDATE], 0, 464
[UPDATE], 1, 27
[UPDATE], 2, 0
[UPDATE], 3, 0
[UPDATE], 4, 0
...
這個輸出指標
總體執行時間為10.11秒
- 平均吞吐量98.9 ops(所有線程)
- 491次修改操作,附帶average,min,max,95th,99th %延遲情況
- 所有491次修改操作都返回0(成功)
464次操作在1ms內完成,27次在1至2ms內完成。
讀操作有與之接近的統計數值
延時信息的直方圖通常是有用的,時序圖的形式有時更有用。請求一個時序,需要在Client命令行或在屬性文件指定"measureenttype=timeseries"屬性。預設情況下,Client會每間隔1000ms,報告一次平均延時。你可以對報告指定不同的間隔粒度,使用 timeseries.granularity屬性,例如。
$ ./bin/ycsb run basic -P workloads/workloada -P large.dat -s -threads 10 -target 100 -p \measurementtype=timeseries -p timeseries.granularity=2000 > transactions.dat
將會報告一個時序,間隔2000ms讀一次,結果將是。
[OVERALL],RunTime(ms), 10077
[OVERALL],Throughput(ops/sec), 9923.58836955443
[UPDATE], Operations, 50396
[UPDATE], AverageLatency(ms), 0.04339630129375347
[UPDATE], MinLatency(ms), 0
[UPDATE], MaxLatency(ms), 338
[UPDATE], Return=0, 50396
[UPDATE], 0, 0.10264765784114054
[UPDATE], 2000, 0.026989343690867442
[UPDATE], 4000, 0.0352882703777336
[UPDATE], 6000, 0.004238958990536277
[UPDATE], 8000, 0.052813085033008175
[UPDATE], 10000, 0.0
[READ], Operations, 49604
[READ], AverageLatency(ms), 0.038242883638416256
[READ], MinLatency(ms), 0
[READ], MaxLatency(ms), 230
[READ], Return=0, 49604
[READ], 0, 0.08997245741099663
[READ], 2000, 0.02207505518763797
[READ], 4000, 0.03188493260913297
[READ], 6000, 0.004869141813755326
[READ], 8000, 0.04355329949238579
[READ], 10000, 0.005405405405405406
這個輸出分開顯示了update和read操作的時間序列,每2000ms的數據。數據報告的時點是僅包括前一個2000ms的均值。(這個例子,我們做了100,000次操作,目標是每秒10,000次操作)。一個關於延時度量的關註點:Client度量,特定操作對資料庫的端到端的執行延時。那樣,它在調用DB介面層class適當方法前會啟動啟動一個時鐘,方法返回時會停止時鐘。延時包括:執行包括介面層,到資料庫伺服器的網路延遲,資料庫的執行時間。不包括用於控制吞吐量的延遲。就是說,如果你指定目標是每秒10次操作(單線程)Client會在每100ms僅執行1次操作。如果操作耗費了12ms,Client會在下一次操作前額外等待88ms。然而,報告延時不會包括這個等待時間,報告會顯示延遲是12ms而不是100.