文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


7.6.6.3 使用版本令牌

在使用版本令牌之前,請依照第 7.6.6.2 節「安裝或解除安裝版本令牌」提供的指示進行安裝。

版本令牌可以派上用場的一種情境是,一個系統存取一組 MySQL 伺服器,但需要為了負載平衡目的而管理它們,方法是監控它們並根據負載變化調整伺服器指派。這類系統包含以下元素:

  • 要管理的 MySQL 伺服器集合。

  • 一個管理或管理應用程式,它會與伺服器通訊並將它們組織成高可用性群組。群組服務不同的目的,而且每個群組內的伺服器可能會有不同的指派。在特定群組中伺服器的指派可以隨時變更。

  • 客戶端應用程式會存取伺服器以擷取和更新資料,並根據指派給它們的目的選擇伺服器。例如,客戶端不應將更新傳送至唯讀伺服器。

版本令牌允許根據指派管理伺服器存取,而不需要客戶端重複查詢伺服器關於它們的指派

  • 管理應用程式執行伺服器指派,並在每個伺服器上建立版本令牌以反映其指派。應用程式快取此資訊,以提供對其的中央存取點。

    如果管理應用程式在某些時候需要變更伺服器指派(例如,將其從允許寫入變更為僅限讀取),它會變更伺服器的版本令牌清單並更新其快取。

  • 為了改善效能,客戶端應用程式會從管理應用程式取得快取資訊,使其能夠避免為每個陳述式擷取有關伺服器指派的資訊。根據其發出的陳述式類型(例如,讀取對寫入),客戶端會選擇適當的伺服器並連線至該伺服器。

  • 此外,客戶端會將其自己的客戶端特定版本令牌傳送至伺服器,以註冊它要求伺服器的指派。對於客戶端傳送至伺服器的每個陳述式,伺服器會將其自己的令牌清單與客戶端令牌清單進行比較。如果伺服器令牌清單包含客戶端令牌清單中存在的所有具有相同值的令牌,則會存在相符,並且伺服器會執行陳述式。

    另一方面,管理應用程式可能已變更伺服器指派及其版本令牌清單。在這種情況下,新的伺服器指派現在可能與客戶端需求不相容。伺服器和客戶端令牌清單之間發生令牌不符,並且伺服器會傳回錯誤以回覆陳述式。這是指示客戶端從管理應用程式快取重新整理其版本令牌資訊,並選取新的伺服器以進行通訊。

偵測版本令牌錯誤並選擇新伺服器的用戶端邏輯可以使用不同的方式實作。

  • 用戶端可以自行處理所有版本令牌的註冊、不匹配偵測和連線切換。

  • 這些動作的邏輯可以實作在一個連接器中,該連接器管理用戶端和 MySQL 伺服器之間的連線。這樣的連接器可能會自行處理不匹配錯誤偵測和語句重新傳送,或者它可能會將錯誤傳遞給應用程式,並讓應用程式重新傳送語句。

以下範例以更具體的形式說明了前面的討論。

當版本令牌在給定的伺服器上初始化時,伺服器的版本令牌列表是空的。令牌列表維護是透過呼叫函數來執行的。呼叫任何版本令牌函數需要 VERSION_TOKEN_ADMIN 權限(或已棄用的 SUPER 權限),因此預期令牌列表修改會由具有該權限的管理或管理應用程式完成。

假設一個管理應用程式與一組伺服器進行通訊,這些伺服器由用戶端查詢以存取員工和產品資料庫(分別命名為 empprod)。所有伺服器都允許處理資料擷取語句,但只有其中一些允許進行資料庫更新。為了在特定資料庫的基礎上處理此問題,管理應用程式在每個伺服器上建立版本令牌列表。在給定伺服器的令牌列表中,令牌名稱代表資料庫名稱,而令牌值為 readwrite,具體取決於資料庫是否必須以唯讀方式使用,或者是否可以進行讀取和寫入。

用戶端應用程式透過設定系統變數來註冊它們要求伺服器匹配的版本令牌列表。變數設定是在特定於用戶端的基礎上進行的,因此不同的用戶端可以註冊不同的要求。預設情況下,用戶端令牌列表為空,這與任何伺服器令牌列表都匹配。當用戶端將其令牌列表設定為非空值時,匹配可能會成功或失敗,具體取決於伺服器版本令牌列表。

要定義伺服器的版本令牌列表,管理應用程式會呼叫 version_tokens_set() 函數。(還有用於修改和顯示令牌列表的函數,稍後會說明。)例如,應用程式可能會將這些語句傳送到一組三台伺服器

伺服器 1

mysql> SELECT version_tokens_set('emp=read;prod=read');
+------------------------------------------+
| version_tokens_set('emp=read;prod=read') |
+------------------------------------------+
| 2 version tokens set.                    |
+------------------------------------------+

伺服器 2

mysql> SELECT version_tokens_set('emp=write;prod=read');
+-------------------------------------------+
| version_tokens_set('emp=write;prod=read') |
+-------------------------------------------+
| 2 version tokens set.                     |
+-------------------------------------------+

伺服器 3

mysql> SELECT version_tokens_set('emp=read;prod=write');
+-------------------------------------------+
| version_tokens_set('emp=read;prod=write') |
+-------------------------------------------+
| 2 version tokens set.                     |
+-------------------------------------------+

在每種情況下,令牌列表都指定為以分號分隔的 name=value 對列表。產生的令牌列表值會導致這些伺服器指派

  • 任何伺服器都接受任一資料庫的讀取。

  • 只有伺服器 2 接受 emp 資料庫的更新。

  • 只有伺服器 3 接受 prod 資料庫的更新。

除了為每個伺服器指派版本令牌列表之外,管理應用程式還維護一個快取,以反映伺服器指派。

在與伺服器通訊之前,用戶端應用程式會聯絡管理應用程式並擷取有關伺服器指派的資訊。然後,用戶端會根據這些指派選擇伺服器。假設用戶端想要對 emp 資料庫執行讀取和寫入。根據前面的指派,只有伺服器 2 符合條件。用戶端連線到伺服器 2 並透過設定其 version_tokens_session 系統變數來註冊其伺服器要求

mysql> SET @@SESSION.version_tokens_session = 'emp=write';

對於用戶端傳送到伺服器 2 的後續語句,伺服器會將其自身的版本令牌列表與用戶端列表進行比較,以檢查它們是否匹配。如果匹配,語句會正常執行

mysql> UPDATE emp.employee SET salary = salary * 1.1 WHERE id = 4981;
Query OK, 1 row affected (0.07 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT last_name, first_name FROM emp.employee WHERE id = 4981;
+-----------+------------+
| last_name | first_name |
+-----------+------------+
| Smith     | Abe        |
+-----------+------------+
1 row in set (0.01 sec)

伺服器和用戶端版本令牌列表之間的差異可能以兩種方式發生

只要伺服器 2 的指派沒有變更,用戶端就會繼續使用它進行讀取和寫入。但是,假設管理應用程式想要變更伺服器指派,以便 emp 資料庫的寫入必須傳送到伺服器 1 而不是伺服器 2。為此,它使用 version_tokens_edit() 來修改兩個伺服器上的 emp 令牌值(並更新其伺服器指派快取)

伺服器 1

mysql> SELECT version_tokens_edit('emp=write');
+----------------------------------+
| version_tokens_edit('emp=write') |
+----------------------------------+
| 1 version tokens updated.        |
+----------------------------------+

伺服器 2

mysql> SELECT version_tokens_edit('emp=read');
+---------------------------------+
| version_tokens_edit('emp=read') |
+---------------------------------+
| 1 version tokens updated.       |
+---------------------------------+

version_tokens_edit() 會修改伺服器令牌列表中指定的令牌,並保持其他令牌不變。

用戶端下次將語句傳送到伺服器 2 時,它自己的令牌列表不再與伺服器令牌列表匹配,並且會發生錯誤

mysql> UPDATE emp.employee SET salary = salary * 1.1 WHERE id = 4982;
ERROR 3136 (42000): Version token mismatch for emp. Correct value read

在這種情況下,用戶端應聯絡管理應用程式以取得有關伺服器指派的更新資訊、選擇新的伺服器,並將失敗的語句傳送到新的伺服器。

注意

每個用戶端都必須與版本令牌協作,僅根據它向給定伺服器註冊的令牌列表傳送語句。例如,如果用戶端註冊的令牌列表為 'emp=read',則版本令牌中沒有任何內容可以阻止用戶端傳送 emp 資料庫的更新。用戶端本身必須避免這樣做。

對於從用戶端接收的每個語句,伺服器會隱式使用鎖定,如下所示

  • 為用戶端令牌列表中命名的每個令牌(也就是 version_tokens_session 值中)取得共用鎖定

  • 執行伺服器和用戶端令牌列表之間的比較

  • 根據比較結果執行語句或產生錯誤

  • 釋放鎖定

伺服器使用共用鎖定,以便多個工作階段的比較可以同時發生而不會發生封鎖,同時防止對任何嘗試在操作伺服器令牌列表中相同名稱的令牌之前取得獨佔鎖定的工作階段變更令牌。

前面的範例僅使用了版本令牌外掛程式庫中包含的少數函數,但還有其他函數。一組函數允許操作和檢查伺服器的版本令牌列表。另一組函數允許鎖定和解除鎖定版本令牌。

這些函數允許建立、變更、移除和檢查伺服器的版本令牌列表

  • version_tokens_set() 會完全取代目前的列表並指派新的列表。參數是以分號分隔的 name=value 對列表。

  • version_tokens_edit() 可以對目前的列表進行部分修改。它可以新增新的令牌或變更現有令牌的值。參數是以分號分隔的 name=value 對列表。

  • version_tokens_delete() 會從目前的列表中刪除令牌。參數是以分號分隔的令牌名稱列表。

  • version_tokens_show() 會顯示目前的令牌列表。它不接受任何參數。

如果成功,每個函數都會傳回一個二進位字串,指出發生的動作。以下範例會建立伺服器令牌列表、透過新增新的令牌來修改它、刪除一些令牌,並顯示產生的令牌列表

mysql> SELECT version_tokens_set('tok1=a;tok2=b');
+-------------------------------------+
| version_tokens_set('tok1=a;tok2=b') |
+-------------------------------------+
| 2 version tokens set.               |
+-------------------------------------+
mysql> SELECT version_tokens_edit('tok3=c');
+-------------------------------+
| version_tokens_edit('tok3=c') |
+-------------------------------+
| 1 version tokens updated.     |
+-------------------------------+
mysql> SELECT version_tokens_delete('tok2;tok1');
+------------------------------------+
| version_tokens_delete('tok2;tok1') |
+------------------------------------+
| 2 version tokens deleted.          |
+------------------------------------+
mysql> SELECT version_tokens_show();
+-----------------------+
| version_tokens_show() |
+-----------------------+
| tok3=c;               |
+-----------------------+

如果令牌列表格式不正確,則會發生警告

mysql> SELECT version_tokens_set('tok1=a; =c');
+----------------------------------+
| version_tokens_set('tok1=a; =c') |
+----------------------------------+
| 1 version tokens set.            |
+----------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 42000
Message: Invalid version token pair encountered. The list provided
         is only partially updated.
1 row in set (0.00 sec)

如先前所述,版本令牌是使用以分號分隔的 name=value 對列表來定義的。請考慮以下 version_tokens_set() 的呼叫

mysql> SELECT version_tokens_set('tok1=b;;; tok2= a = b ; tok1 = 1\'2 3"4')
+---------------------------------------------------------------+
| version_tokens_set('tok1=b;;; tok2= a = b ; tok1 = 1\'2 3"4') |
+---------------------------------------------------------------+
| 3 version tokens set.                                         |
+---------------------------------------------------------------+

版本令牌會將參數解譯如下

  • 忽略名稱和值周圍的空格。允許名稱和值中的空格。(對於 version_tokens_delete(),它接受不含值的名稱列表,則會忽略名稱周圍的空格。)

  • 沒有引號機制。

  • 令牌的順序並不重要,除非令牌列表包含給定令牌名稱的多個執行個體,則最後一個值會優先於較早的值。

根據這些規則,前面的 version_tokens_set() 呼叫會產生一個包含兩個令牌的令牌列表:tok1 的值為 1'2 3"4,而 tok2 的值為 a = b。若要驗證這一點,請呼叫 version_tokens_show()

mysql> SELECT version_tokens_show();
+--------------------------+
| version_tokens_show()    |
+--------------------------+
| tok2=a = b;tok1=1'2 3"4; |
+--------------------------+

如果令牌列表包含兩個令牌,為什麼 version_tokens_set() 傳回值 3 version tokens set?發生這種情況是因為原始令牌列表包含兩個 tok1 的定義,而第二個定義取代了第一個定義。

版本令牌的令牌操作函數對令牌名稱和值施加了這些限制

  • 令牌名稱不能包含 =; 字元,且最大長度為 64 個字元。

  • 令牌值不能包含 ; 字元。值的長度受 max_allowed_packet 系統變數的值限制。

  • 版本令牌將令牌名稱和值視為二進位字串,因此比較區分大小寫。

版本令牌還包含一組可以鎖定和解除鎖定令牌的函數

每個鎖定函數都會在成功時傳回非零值。否則,會發生錯誤

mysql> SELECT version_tokens_lock_shared('lock1', 'lock2', 0);
+-------------------------------------------------+
| version_tokens_lock_shared('lock1', 'lock2', 0) |
+-------------------------------------------------+
|                                               1 |
+-------------------------------------------------+

mysql> SELECT version_tokens_lock_shared(NULL, 0);
ERROR 3131 (42000): Incorrect locking service lock name '(null)'.

使用版本令牌鎖定函數進行鎖定是建議性的;應用程式必須同意協作。

可以鎖定不存在的令牌名稱。這不會建立令牌。

注意

版本令牌鎖定函數基於 第 7.6.9.1 節,「鎖定服務」 中描述的鎖定服務,因此具有相同的共用和獨佔鎖定語意。(版本令牌使用內建在伺服器中的鎖定服務常式,而不是鎖定服務函數介面,因此不需要安裝這些函數即可使用版本令牌。)版本令牌取得的鎖定使用 version_token_locks 的鎖定服務命名空間。可以使用效能架構來監視鎖定服務鎖定,因此版本令牌鎖定也是如此。如需詳細資訊,請參閱 鎖定服務監視

對於版本令牌鎖定功能,令牌名稱參數會完全依照指定的方式使用。周圍的空白字元不會被忽略,且允許使用 =; 字元。這是因為版本令牌只是將要鎖定的令牌名稱原封不動地傳遞給鎖定服務。