伺服器好慢啊~ 總之來測試一下吧 – I/O 測試

先說好,我不敢保證這個系列會繼續下去喔!(我個人實在相當希望能快點完結)

這個系列紀錄的是我嘗試改善效能的各種嘗試,大概試到一個程度,我感覺滿意了就會停了。

前情提要

目前本站的網站伺服器是跑在一片 Raspberry Pi4 (4GB) 上,但是效能實在是太差了,光載入首頁可能就要讓使用者等 2 秒以上…

總之先上 Spec:

  • 64 位元 ARM 架構四核心 @1.5GHz(Broadcom BCM2711, Quad core Cortex-A72)
  • 4GB LPDDR4-3200 SDRAM
  • Gigabit Ethernet

看到這樣的規格,你會猜測,或許是記憶體不夠吧?

$ free -h
              total        used        free      shared  buff/cache   available
Mem:          3.7Gi       1.3Gi       218Mi       124Mi       2.2Gi       2.2Gi
Swap:          0B          0B          0B

▲ 很可惜的,似乎完全不是這麼一回事。

$ uptime
 04:40:58 up 16 days,  4:35,  1 user,  load average: 0.32, 1.94, 1.79

▲ CPU 的負載也看不出什麼問題…

$ iostat
Linux 5.4.0-1029-raspi (Play-PC-Pi8) 	2021年03月16日 	_aarch64_	(4 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.35    0.01    0.34    1.37    0.00   97.94

%iowait 可能多了一點?!

使用 hdparm 測試讀取效能

安裝:

sudo apt install hdparm

非常多功能的簡易測試工具,名稱來自 Hard Drive Parameters(硬碟參數)的縮寫。

$ sudo hdparm -I /dev/sda

/dev/sda:

ATA device, with non-removable media
	Model Number:       CT500MX500SSD1                          
	Serial Number:      2022E2A74CBE        
	Firmware Revision:  M3CR023
	Transport:          Serial, ATA8-AST, SATA 1.0a, SATA II Extensions, SATA Rev 2.5, SATA Rev 2.6, SATA Rev 3.0
Standards:
	Used: unknown (minor revision code 0x006d)
	Supported: 10 9 8 7 6 5
	Likely used: 10
...略...

可以使用 -I 顯示硬碟資訊,如果是記憶卡的話可能沒辦法顯示

$ sudo hdparm -t /dev/sda

/dev/sda:
 Timing buffered disk reads: 324 MB in  3.01 seconds = 107.75 MB/sec

使用 -t 可以測試硬碟的讀取效能,除此之外,也可以試試底下的幾種參數:

  • -t: 測試硬碟讀取效能,會依據硬碟大小自動決定測試的大小
  • -T: 測試硬碟 快取 讀取效能
  • --direct: 測試 raw I/O 的讀取效能(跳過作業系統的分頁機制)

對記憶卡紀行測試的時候,會顯示 HDIO_DRIVE_CMD 訊息,這是正常的

$ sudo hdparm -t /dev/mmcblk0

/dev/mmcblk0:
 HDIO_DRIVE_CMD(identify) failed: Invalid argument
 Timing buffered disk reads: 130 MB in  3.03 seconds =  42.93 MB/sec

原因是 hdparm 在測試之前會先透過 ATA 界面,試圖判斷目標裝置的容量(-I)的緣故

使用 fio 進行更加詳細(危險)的測試

全名 flexible I/O tester,這個程式可以測試的項目相當廣泛,因為真的太廣泛了,我想先用兩個範例(模仿 CrystalDiskMark)來解釋一下

安裝:

sudo apt install fio

RAND 4K Q1T1:

fio \
--filename=fio_test \
--size=1G \
--rw=randrw \
--bs=4k \
--name=fio_test

RAND 4K Q32T16:

fio \
--filename=fio_test \
--size=1G \
--rw=randrw \
--bs=4k \
--iodepth=32 \
--numjobs=16 \
--thread \
--group_reporting \
--name=fio_test

讓我們解釋一下幾個我個人覺得有趣的選項:

  • --filename: 可以隨意取名,也可以使用 /dev/sda 等區塊裝置名稱(會抹除區塊裝置內的資料
  • --size: 測試檔案的大小
  • --readwrite / --rw: 讀寫模式
    • read: 循序讀取
    • write: 循序寫入
    • trim: 循序 TRIM 命令
    • 加上 rand 可以從循序變成隨機模式(e.g. randread
    • randrw: 隨機讀寫
    • trimwrite: 循序 trim+write 命令
  • --bs: 區塊大小,以 byte 為單位,也可以使用 k 代表 1024(e.g. 4k 代表 4096 byte)
  • --iodepth: 同一時間允許多少存取
  • --numjobs: 同時開啟的工作數量(aka 複製自己)
  • --thread: fio 預設使用 process(fork) 平行處理,我們可以選擇使用 thread
  • --group_reporting: 可以把多個工作的結果整合成一個報告,只有 numjobs > 1 時才有意義
  • --name: 工作(測試)名稱,可以隨意取名,我們使用這個選項只是讓我們進入命令列模式(CLI),跳過編寫 jobfile 的麻煩

記得刪掉測試檔案:

rm fio_test

範例輸出:

$ fio \
--filename=fio_test \
--size=1G \
--rw=randrw \
--bs=4k \
--iodepth=32 \
--numjobs=16 \
--thread \
--group_reporting \
--name=fio_test
fio_test: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=32
...
fio-3.16
Starting 16 threads
Jobs: 1 (f=1): [_(13),m(1),_(2)][99.0%][r=11.6MiB/s,w=11.3MiB/s][r=2973,w=2900 IOPS][eta 00m:14s]                           
fio_test: (groupid=0, jobs=16): err= 0: pid=2817936: Wed Mar 17 05:14:28 2021
  read: IOPS=1470, BW=5882KiB/s (6023kB/s)(8185MiB/1424847msec)
    clat (usec): min=4, max=12828k, avg=1611.91, stdev=50194.59
     lat (usec): min=4, max=12828k, avg=1612.34, stdev=50194.59
    clat percentiles (usec):
     |  1.00th=[      6],  5.00th=[      7], 10.00th=[      7],
     | 20.00th=[      8], 30.00th=[      9], 40.00th=[     10],
     | 50.00th=[     12], 60.00th=[     13], 70.00th=[     14],
     | 80.00th=[     15], 90.00th=[     17], 95.00th=[   3621],
     | 99.00th=[  25822], 99.50th=[  29754], 99.90th=[  62653],
     | 99.95th=[ 534774], 99.99th=[1350566]
   bw (  KiB/s): min=  115, max=148448, per=100.00%, avg=13649.80, stdev=877.21, samples=19580
   iops        : min=   19, max=37112, avg=3411.77, stdev=219.31, samples=19580
  write: IOPS=1473, BW=5892KiB/s (6034kB/s)(8199MiB/1424847msec); 0 zone resets
    clat (usec): min=10, max=13410k, avg=8363.59, stdev=170561.30
     lat (usec): min=10, max=13410k, avg=8364.16, stdev=170561.42
    clat percentiles (usec):
     |  1.00th=[     14],  5.00th=[     16], 10.00th=[     20],
     | 20.00th=[     25], 30.00th=[     30], 40.00th=[     33],
     | 50.00th=[     39], 60.00th=[     44], 70.00th=[     53],
     | 80.00th=[     63], 90.00th=[     76], 95.00th=[     90],
     | 99.00th=[ 139461], 99.50th=[ 252707], 99.90th=[1266680],
     | 99.95th=[5268046], 99.99th=[6341788]
   bw (  KiB/s): min=  116, max=148800, per=100.00%, avg=13699.90, stdev=876.60, samples=19542
   iops        : min=   20, max=37200, avg=3424.29, stdev=219.16, samples=19542
  lat (usec)   : 10=20.25%, 20=31.16%, 50=28.92%, 100=14.83%, 250=0.55%
  lat (usec)   : 500=0.05%, 750=0.02%, 1000=0.01%
  lat (msec)   : 2=0.24%, 4=0.59%, 10=1.28%, 20=0.48%, 50=0.76%
  lat (msec)   : 100=0.14%, 250=0.44%, 500=0.17%, 750=0.03%, 1000=0.02%
  lat (msec)   : 2000=0.01%, >=2000=0.05%
  cpu          : usr=0.11%, sys=0.53%, ctx=416597, majf=0, minf=0
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=2095332,2098972,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32

Run status group 0 (all jobs):
   READ: bw=5882KiB/s (6023kB/s), 5882KiB/s-5882KiB/s (6023kB/s-6023kB/s), io=8185MiB (8582MB), run=1424847-1424847msec
  WRITE: bw=5892KiB/s (6034kB/s), 5892KiB/s-5892KiB/s (6034kB/s-6034kB/s), io=8199MiB (8597MB), run=1424847-1424847msec

Disk stats (read/write):
  mmcblk0: ios=131062/633321, merge=21/8740, ticks=2653177/72797058, in_queue=73915748, util=99.08%

補充:如何決定 bs(block size)數值?

我建議可以先從裝置的原生磁區大小開始,可以使用 fdisk 指令取得資訊

~ » sudo fdisk -l /dev/sda
Disk /dev/sda: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: DA7FC779-1AB7-44B3-AB5D-E68C99836A1C

可以看出這個硬碟是 4K 格式(AF)

使用 sysbench

有鑑於上面的結果,我猜測 DB 的存取速度,應該是一大問題… 不過我們還可以使用這個工具來進行 DB 效能的測試

(咳)總之,sysbench 能夠測試系統的 CPU、記憶體、I/O 等多個面向,可以說是相當方便的工具。

安裝:

sudo apt install sysbench

簡單的磁碟效能測試:

# 建立測試檔案
sysbench fileio --file-test-mode=rndrw prepare
# 進行測試
sysbench fileio --file-test-mode=rndrw run
# 清除測試檔案
sysbench fileio --file-test-mode=rndrw cleanup

重點是 DB(MySQL)效能測試!!

因為 Ubuntu 官方打包的版本有時比較舊,請自行確認測試設定檔的名稱

$ ls /usr/share/sysbench
bulk_insert.lua  oltp_point_select.lua  oltp_update_non_index.lua
oltp_common.lua  oltp_read_only.lua     oltp_write_only.lua
oltp_delete.lua  oltp_read_write.lua    select_random_points.lua
oltp_insert.lua  oltp_update_index.lua  select_random_ranges.lua

我們要找的是 OLTP(線上交易處理)開頭的設定檔。

注:如果真的找不到,也可以至 sysbench 的官方 GitHub 下載編譯,照著指令複製貼上即可: akopytov/sysbench: Scriptable database and system performance benchmark

另外,為了避免更動到資料庫裡面既有的資料,必須先建立一個空資料庫(sbtest) (請依照自己的環境操作,以下指令僅供參考)

安裝 MariaDB 10.5:

# 設定軟體來源
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64] https://ftp.ubuntu-tw.org/mirror/mariadb/repo/10.5/ubuntu focal main'
# 安裝
sudo apt install mariadb-server
# 初始化(unix_socket authentication,允許已登入主機的同名的使用者,直接登入)
sudo mariadb-secure-installation

登入 root 帳戶:

mariadb -u root -p

建立資料庫(sbtest)與使用者(sbtest,不用密碼):

CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'localhost';
GRANT ALL PRIVILEGES ON * . * TO 'sbtest'@'localhost';
FLUSH PRIVILEGES;
quit;

這邊選擇的是 oltp_read_write.lua 這個測試(不須指定副檔名,除非你的測試檔案不在預設路徑 /usr/share/sysbench):

sysbench oltp_read_write --db-driver=mysql prepare
sysbench oltp_read_write --db-driver=mysql run
sysbench oltp_read_write --db-driver=mysql cleanup

刪除測試資料庫(sbtest)與使用者(sbtest):

SELECT User,Host FROM mysql.user;
DROP USER 'sbtest'@'localhost';
SHOW DATABASES;
DROP DATABASE sbtest;
FLUSH PRIVILEGES;
quit;

範例輸出:

$ sysbench oltp_read_write --db-driver=mysql run
sysbench 1.0.18 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Initializing random number generator from current time


Initializing worker threads...

Threads started!

SQL statistics:
    queries performed:
        read:                            7336
        write:                           2096
        other:                           1048
        total:                           10480
    transactions:                        524    (51.09 per sec.)
    queries:                             10480  (1021.75 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          10.2477s
    total number of events:              524

Latency (ms):
         min:                                    7.46
         avg:                                   19.54
         max:                                  727.57
         95th percentile:                       17.32
         sum:                                10238.47

Threads fairness:
    events (avg/stddev):           524.0000/0.00
    execution time (avg/stddev):   10.2385/0.00

似乎沒什麼問題,看來是需要更多的測試,敬請期待(淚)

參考資料

發表迴響