MySQL Shell 模組提供了一套便捷的工具,用於管理和執行資料函式庫工作負載。其核心功能包含載入測試資料、執行預定義的工作負載以及提供效能監控和分析工具。使用者可以透過 load() 方法載入 employeessakilaworld 等測試資料函式庫,並使用 run() 方法執行預先定義的工作負載。此外,模組還內建了多個 Python 函式庫,例如 innodb_buffer_poolinnodb_monitorinnodb_mutex,用於監控 InnoDB 引擎的關鍵指標,例如緩衝池狀態、mutex 等待事件等,方便開發者進行效能分析和調校。透過 sys.metrics 檢視,使用者可以收集更全面的效能指標,並利用提供的工具將資料輸出為 CSV 格式,以便進一步分析。

MySQL Shell 模組安裝與使用

MySQL Shell 提供了一個強大的模組系統,允許使用者擴充套件其功能。本文將指導您如何安裝並使用 concurrency_book 模組。

安裝步驟

  1. 下載模組檔案:首先,您需要從本文的 GitHub 儲存函式庫下載 concurrency_book 目錄下的所有檔案。您可以透過克隆儲存函式庫或下載 ZIP 檔案來完成此操作。

    • 克隆儲存函式庫:使用 Git 軟體克隆儲存函式庫,網址可透過本文的首頁(www.apress.com/gp/book/9781484266519)找到。
    • 下載 ZIP 檔案:直接下載包含所有檔案的 ZIP 檔案。
  2. 選擇安裝路徑:您可以自由選擇存放這些檔案的路徑,但需保持 concurrency_book 目錄以下的結構不變。本文假設您將檔案存放於 C:\Book\mysql-concurrency

  3. 組態 MySQL Shell:為了能夠在 MySQL Shell 中匯入模組,您需要編輯或建立 mysqlshrc.py 檔案。MySQL Shell 會在四個預設位置搜尋此檔案(Windows 系統):

    • %PROGRAMDATA%\MySQL\mysqlsh\
    • %MYSQLSH_HOME%\shared\mysqlsh\
    • <mysqlsh binary path>\
    • %APPDATA%\MySQL\mysqlsh\

    mysqlshrc.py 檔案中,您需要新增以下程式碼,以將模組所在的目錄加入 Python 的搜尋路徑中:

    import sys
    sys.path.append('C:\\Book\\mysql-concurrency')
    import concurrency_book.generate
    

    請根據您的實際路徑調整上述程式碼中的路徑。

使用模組

concurrency_book.generate 模組提供了多個方法來簡化您的操作,主要包括 help()load()show()run()

help() 方法

此方法提供關於如何使用該模組的資訊。

mysql-py> concurrency_book.generate.help()

輸出結果將顯示支援的操作,包括:

  • help():顯示幫助資訊。
  • load(schema_name=None):載入一個 schema,可選地接受 schema 名稱。
  • show():列出可用的工作負載。
  • run(workload_name=None):執行一個工作負載,可選地接受工作負載名稱。

show() 方法

此方法列出可用的工作負載和可載入的 schema。

mysql-py> concurrency_book.generate.show()

輸出結果將顯示可用的工作負載列表,包括名稱和描述。

程式碼解說

組態 MySQL Shell 的 Python 程式碼

import sys
sys.path.append('C:\\Book\\mysql-concurrency')
import concurrency_book.generate

內容解密:

  1. import sys:匯入 Python 的 sys 模組,該模組提供了對 Python 直譯器相關變數和函式的存取。
  2. sys.path.append('C:\\Book\\mysql-concurrency'):將指定的路徑新增到 Python 的模組搜尋路徑中,使得 MySQL Shell 可以找到 concurrency_book 模組。
  3. import concurrency_book.generate:匯入 concurrency_book 模組中的 generate 部分,使其功能可在 MySQL Shell 中使用。

使用 help() 方法

mysql-py> concurrency_book.generate.help()

內容解密:

  1. concurrency_book.generate.help():呼叫 generate 模組中的 help() 方法,顯示如何使用該模組的資訊。

使用 show() 方法

mysql-py> concurrency_book.generate.show()

內容解密:

  1. concurrency_book.generate.show():呼叫 generate 模組中的 show() 方法,列出可用的工作負載和可載入的 schema。

MySQL Shell 模組使用

MySQL Shell 模組是一個強大的工具,用於載入測試資料和執行工作負載。本將介紹如何使用此模組。

載入測試資料

MySQL Shell 模組支援載入 employees、sakila 和 world 範例資料函式庫到您的 MySQL 例項中。對於 employees 資料函式庫,您可以選擇帶有分割槽的版本。world 資料函式庫是本文最重要的資料函式庫,其次是 sakila 資料函式庫。employees 資料函式庫僅用於第 18 章的案例研究。

載入資料函式庫的步驟:

  1. 使用 load() 方法載入資料函式庫。如果您不提供資料函式庫名稱,則會提示您選擇。
  2. 選擇要載入的資料函式庫編號或名稱。
  3. 如果資料函式庫已經存在,它將被刪除並重新載入。
mysql-py> concurrency_book.generate.load()

可用的資料函式庫載入作業:

編號名稱描述
1employees員薪水料函式庫
2employees partitioned帶有分割槽的員薪水料函式庫
3sakilasakila 資料函式庫
4worldworld 資料函式庫

執行工作負載

使用 run() 方法執行工作負載。如果您指定已知工作負載的名稱,則該工作負載將立即執行。否則,將列出可用的工作負載,並提示您選擇。

執行工作負載的步驟:

  1. 使用 run() 方法執行工作負載。
  2. 如果需要,選擇要執行的工作負載編號或名稱。
mysql-py> concurrency_book.generate.run()

小貼士

  • 如果您的網路連線不穩定,下載 employees 資料函式庫可能會失敗。您可以手動下載 https://github.com/datacharmer/test_db/archive/master.zip 並將其儲存在 sample_data 目錄中。
  • 如果載入過程當機並抱怨檔案損壞,請刪除該檔案並重新下載。
內容解密:

本段落詳細介紹了 MySQL Shell 模組的使用方法,包括如何載入測試資料和執行工作負載。透過使用 load()run() 方法,您可以輕鬆地管理和執行各種測試任務。同時,本也提供了一些實用的小貼士,以幫助您避免常見的問題。透過遵循本的指示,您可以更好地利用 MySQL Shell 模組來滿足您的測試需求。

MySQL Shell 模組的實際操作與分析

使用 Prompt 執行工作負載

在 MySQL Shell 中,可以使用 concurrency_book.generate.run() 命令來執行特定的工作負載。此命令會列出可用的工作負載,並允許使用者選擇要執行的專案。

執行工作負載:使用者層級鎖定死鎖範例

在範例中,使用者選擇了編號 15 的工作負載,即「Listing 6-1: A deadlock for user-level locks」。此範例模擬了兩個連線之間的死鎖情況。

程式碼範例

-- Connection 1
SELECT GET_LOCK('my_lock_1', -1);
-- Connection 2
SELECT GET_LOCK('my_lock_2', -1);
-- Connection 2
SELECT GET_LOCK('my_lock_1', -1);
-- Connection 1
SELECT GET_LOCK('my_lock_2', -1);

內容解密:

  1. 第一步驟: 連線 1 取得鎖 my_lock_1,連線 2 取得鎖 my_lock_2

    • 使用 GET_LOCK 函式嘗試取得鎖,若成功則傳回 1。
  2. 第二步驟: 連線 2 嘗試取得鎖 my_lock_1,而連線 1 嘗試取得鎖 my_lock_2

    • 由於雙方都持有對方需要的鎖,因此會發生死鎖。
  3. 錯誤處理: 當發生死鎖時,MySQL 會傳回錯誤程式碼 3058,並提示嘗試回復交易或釋放鎖後重試。

調查與分析

在發生死鎖後,可以使用 performance_schema 中的表格來調查鎖的情況和事件歷史。

可用的調查查詢

  1. 查詢 metadata_locks 表格

SELECT * FROM performance_schema.metadata_locks WHERE object_type = ‘USER LEVEL LOCK’ AND owner_thread_id IN (249, 250);


2. **查詢 events_statements_history 表格(針對特定 thread_id)**
   ```sql
SELECT thread_id, event_id, sql_text,
       mysql_errno, returned_sqlstate, message_text,
       errors, warnings
FROM performance_schema.events_statements_history
WHERE thread_id = 249 AND event_id > 6
ORDER BY event_id;

調查結果分析

透過查詢 events_statements_history,可以檢視特定執行緒的事件歷史。例如,對於 thread_id = 249 的查詢結果顯示了以下事件:

  • 事件 7:執行 SELECT GET_LOCK('my_lock_1', -1) 成功。
  • 事件 8:執行 SELECT GET_LOCK('my_lock_2', -1) 發生死鎖錯誤(錯誤程式碼 3058)。
  • 事件 9:執行 SHOW WARNINGS

這些資訊有助於瞭解發生死鎖時的具體情況和錯誤原因。

MySQL Shell 模組內部結構與使用

MySQL Shell 模組是一個強大的工具,用於執行和管理 MySQL 資料函式庫的平行工作負載。本將探討該模組的內部結構、使用方法以及如何擴充套件其功能。

模組結構

該模組的檔案可以分為三類別,如圖 B-2 所示。

  • generate.py 檔案是模組的入口點,實作了四個公開方法:help()load()show()run()
  • libs 目錄包含各種函式庫,用於實作模組的功能。這些函式庫也可以用於實作自定義的工作負載。
  • workloads 目錄包含工作負載的定義,每個工作負載都有一個 YAML 檔案,部分測試案例還包含 Python 檔案。

使用

  1. 執行工作負載

    • 使用 run() 方法執行工作負載,該方法會提示輸入 MySQL 帳戶的密碼。
    • 執行工作負載前,會顯示連線的 Processlist ID、Thread ID 和 Event ID 等資訊。
    • 執行工作負載後,可以執行調查查詢來分析問題。
  2. 調查查詢

    • 調查查詢用於分析工作負載執行的結果。
    • 可以透過指定查詢編號來執行調查查詢。
    • 調查查詢的結果以註解形式顯示,註明執行的查詢編號。
  3. 離開工作負載

    • 提交空答案以離開工作負載。
    • 如果不想執行更多工作負載,再次提交空答案以離開 run() 方法。

函式庫檔案

該模組提供了多個有用的函式庫,用於實作複雜的工作負載。

innodb_buffer_pool.py

  • 該模組實作了監控 InnoDB 緩衝池的介面。
  • 需要提供一個連線到 MySQL 的 MySQL Shell session 物件。
  • 示例用法:
from concurrency_book import libs
import concurrency_book.libs.innodb_buffer_pool

bp_stats = libs.innodb_buffer_pool.Stats(session)
bp_stats.collect()
...
bp_stats.collect()
young = bp_stats.delta('pages_made_young')
print(f'Made young ......: {young.value:6d} pages ' +
      f'({young.rate:8.2f} pages/s)')

innodb_monitor.py

  • 該模組實作了監控 InnoDB 監控輸出的介面。
  • 可以傳回個別區段的內容,並對 SEMAPHORES 區段進行統計。
  • 需要提供一個連線到 MySQL 的 MySQL Shell session 物件。
  • 示例用法:
from concurrency_book import libs
import concurrency_book.libs.innodb_monitor

innodb = libs.innodb_monitor.InnodbMonitor(session)
innodb.fetch()
semaphores = innodb.get_section('SEMAPHORES')
if semaphores.num_waits >= 2:
    print(r'mysql> SHOW ENGINE INNDB STATUS\G')
    print('...')
    print(semaphores.content)
    print('...')

innodb_mutex.py

  • 該模組實作了監控 SHOW ENGINE INNODB MUTEX 輸出的介面。
  • 可以取得總等待次數和與前一次輸出的差異。
  • 需要提供一個連線到 MySQL 的 MySQL Shell session 物件。
  • 示例用法:
from concurrency_book import libs
import concurrency_book.libs.innodb_mutex

mutex = libs.innodb_mutex.InnodbMutexMonitor(session)
mutex.fetch()
...

MySQL 並發測試工具的模組解析

MySQL 並發測試工具是由多個 Python 模組組成,這些模組共同協助完成並發測試、資料載入、效能監控及查詢格式化等任務。以下將詳細介紹這些模組的功能及其實作方式。

mutex.py 模組

mutex.py 模組主要用於分析 InnoDB 的 mutex 等待事件。它透過 delta_by_file()delta_by_file_line() 方法,分別按檔案名稱和檔案名稱:行號來統計等待次數。這些方法可以傳回結果為字典或以 MySQL 表格格式呈現的報告。

mutex.fetch()
delta = mutex.delta_by_file('dict')
if delta['btr0sea.cc'] > 0:
    print(mutex.delta_by_file_line('report'))

內容解密:

  1. mutex.fetch():取得最新的 mutex 等待事件資料。
  2. delta = mutex.delta_by_file('dict'):計算各檔案的等待次數變化,並以字典形式傳回結果。
  3. if delta['btr0sea.cc'] > 0::檢查特設定檔案(如 btr0sea.cc)的等待次數是否大於 0。
  4. print(mutex.delta_by_file_line('report')):若條件滿足,則列印預出按檔案名稱和行號統計的等待次數報告。

load.py 模組

load.py 模組負責載入測試資料。要支援新的 schema,需要在 KNOWN_SCHEMASURLS 常數中新增相關資訊,並實作 _exec_<schema name>() 方法。例如,對於 world schema,需要定義 _exec_world() 方法。

def _exec_world(self):
    """執行載入 world schema 的步驟"""
    file = self._download()
    with zipfile.ZipFile(file) as zip_fs:
        self._delimiter = ';'
        with zip_fs.open('world.sql') as world:
            self._sql_file(world, zip_fs)
    LOG.info('Load of the world schema completed')
    return True

內容解密:

  1. file = self._download():下載包含 SQL 陳述式的壓縮檔案。
  2. with zipfile.ZipFile(file) as zip_fs::使用 zipfile 模組直接存取壓縮檔案中的內容,而無需顯式解壓縮。
  3. self._sql_file(world, zip_fs):執行 SQL 檔案中的陳述式以載入 schema。
  4. LOG.info('Load of the world schema completed'):記錄載入完成的資訊。

log.py 模組

log.py 模組提供日誌記錄功能,支援在多執行緒環境中安全地列印日誌。可以透過設定鎖定物件來確保執行緒安全。

LOG = libs.log.Log(libs.log.INFO)
lock = threading.Lock()
LOG.lock = lock
LOG.debug('Some debug information.')
LOG.info('Some informational content.')

內容解密:

  1. LOG = libs.log.Log(libs.log.INFO):初始化日誌物件,設定預設日誌級別為 INFO。
  2. lock = threading.Lock():建立一個鎖定物件用於同步。
  3. LOG.lock = lock:將鎖定物件賦予日誌物件,以確保執行緒安全的日誌記錄。
  4. LOG.debug()LOG.info():分別記錄除錯資訊和一般資訊。

metrics.py 模組

metrics.py 模組允許監控 sys.metrics 檢視,用於收集各種效能指標。它需要一個連線到 MySQL 的 MySQL Shell session 物件。

metrics = libs.metrics.Metrics(session)
for i in range(10):
    metrics.collect()
    sleep(1)
metrics.collect()
count_metrics = ['innodb_row_lock_current_waits', 'lock_row_lock_current_waits']
delta_metrics = ['innodb_row_lock_time', 'lock_deadlocks', 'lock_timeouts']
metrics.write_csv(count_metrics)
metrics.write_rate_csv(delta_metrics)

內容解密:

  1. metrics = libs.metrics.Metrics(session):初始化 Metrics 物件,需要一個 MySQL Shell session。
  2. metrics.collect():收集效能指標。
  3. count_metricsdelta_metrics:定義了兩類別效能指標,分別用於統計和計算變化率。
  4. write_csv()write_rate_csv():將收集到的指標以 CSV 格式輸出。

query.py 模組

query.py 模組提供了與執行工作負載和調查查詢相關的工具。其中,Formatter() 類別可用於格式化查詢陳述式,包括新增縮排和替換引數佔位符。

sql_formatter = libs.query.Formatter([6, 7], [12, 14], [6, 6])
sql = """SELECT * FROM world.city""".strip()
print('mysql> ' + sql_formatter.indent_sql(sql, 7))

內容解密:

  1. sql_formatter = libs.query.Formatter([6, 7], [12, 14], [6, 6]):初始化 Formatter 物件,需要提供 process list ids、thread ids 和 last event id。
  2. sql_formatter.indent_sql(sql, 7):格式化 SQL 陳述式,新增適當的縮排。