在使用版本令牌之前,請依照第 7.6.6.2 節,「安裝或解除安裝版本令牌」中提供的指示進行安裝。
版本令牌可能有用的一種情況是,系統會存取 MySQL 伺服器集合,但需要透過監控它們並根據負載變更調整伺服器指派來管理它們以達到負載平衡的目的。此類系統包含以下元素:
要管理的 MySQL 伺服器集合。
一個管理或管理應用程式,該應用程式與伺服器通訊並將它們組織成高可用性群組。群組服務於不同的目的,並且每個群組中的伺服器可能具有不同的指派。特定群組內伺服器的指派可能隨時變更。
存取伺服器以擷取和更新資料的用戶端應用程式,根據指派給它們的目的選擇伺服器。例如,用戶端不應將更新傳送至唯讀伺服器。
版本令牌允許根據指派管理伺服器存取,而無需用戶端重複查詢伺服器有關其指派的資訊。
管理應用程式執行伺服器指派,並在每個伺服器上建立版本令牌以反映其指派。應用程式會快取此資訊,以便提供對其的中央存取點。
如果管理應用程式在某些時候需要變更伺服器指派(例如,將其從允許寫入變更為唯讀),它會變更伺服器的版本令牌清單並更新其快取。
為了提高效能,用戶端應用程式會從管理應用程式取得快取資訊,使其能夠避免為每個陳述式擷取有關伺服器指派的資訊。根據它發出的陳述式類型(例如,讀取與寫入),用戶端會選擇適當的伺服器並連線到它。
此外,用戶端會將其自己的用戶端特定版本令牌傳送至伺服器,以註冊它對伺服器的要求指派。對於用戶端傳送至伺服器的每個陳述式,伺服器會將其自己的令牌清單與用戶端令牌清單進行比較。如果伺服器令牌清單包含用戶端令牌清單中存在的所有具有相同值的令牌,則表示相符,伺服器會執行陳述式。
另一方面,管理應用程式可能已變更伺服器指派及其版本令牌清單。在這種情況下,新的伺服器指派現在可能與用戶端要求不相容。伺服器和用戶端令牌清單之間會發生令牌不相符,並且伺服器會傳回錯誤以回覆陳述式。這表示用戶端需要從管理應用程式快取中重新整理其版本令牌資訊,並選擇一個新的伺服器進行通訊。
偵測版本令牌錯誤並選擇新伺服器的用戶端邏輯可以使用不同的方式來實作。
用戶端可以自行處理所有版本令牌註冊、不相符偵測和連線切換。
這些動作的邏輯可以在管理用戶端和 MySQL 伺服器之間連線的連接器中實作。此類連接器可能會自行處理不相符錯誤偵測和陳述式重新傳送,或者可能會將錯誤傳遞給應用程式,並將重新傳送陳述式留給應用程式。
以下範例以更具體的形式說明了先前的討論。
當版本令牌在給定的伺服器上初始化時,伺服器的版本令牌清單為空。令牌清單維護是透過呼叫函數來執行。呼叫任何版本令牌函數需要VERSION_TOKEN_ADMIN
權限(或已棄用的SUPER
權限),因此預期令牌清單修改是由具有該權限的管理或管理應用程式完成。
假設一個管理應用程式與一組伺服器通訊,這些伺服器由客戶端查詢以存取員工和產品資料庫(分別命名為 emp
和 prod
)。所有伺服器都允許處理資料檢索語句,但只有部分伺服器允許進行資料庫更新。為了以資料庫特定的方式處理此問題,管理應用程式會在每個伺服器上建立版本令牌列表。在給定伺服器的令牌列表中,令牌名稱表示資料庫名稱,而令牌值則為 read
或 write
,具體取決於資料庫是否必須以唯讀方式使用,或者是否可以進行讀取和寫入。
客戶端應用程式透過設定系統變數來註冊它們要求伺服器匹配的版本令牌列表。變數設定是以客戶端特定的方式進行的,因此不同的客戶端可以註冊不同的要求。預設情況下,客戶端令牌列表為空,這表示匹配任何伺服器令牌列表。當客戶端將其令牌列表設定為非空值時,匹配可能會成功或失敗,具體取決於伺服器版本令牌列表。
要定義伺服器的版本令牌列表,管理應用程式會呼叫 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)
伺服器和客戶端版本令牌列表之間的差異可能會以兩種方式發生
version_tokens_session
值中的令牌名稱不存在於伺服器令牌列表中。在這種情況下,會發生ER_VTOKEN_PLUGIN_TOKEN_NOT_FOUND
錯誤。version_tokens_session
值中的令牌值與伺服器令牌列表中對應令牌的值不同。在這種情況下,會發生ER_VTOKEN_PLUGIN_TOKEN_MISMATCH
錯誤。
只要伺服器 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
系統變數的值限制。版本令牌將令牌名稱和值視為二進位字串,因此比較會區分大小寫。
版本令牌還包含一組函數,可讓您鎖定和解除鎖定令牌
version_tokens_lock_exclusive()
會取得獨佔版本令牌鎖定。它會採用一個或多個鎖定名稱列表和逾時值。version_tokens_lock_shared()
會取得共用版本令牌鎖定。它會採用一個或多個鎖定名稱列表和逾時值。version_tokens_unlock()
會釋放版本令牌鎖定(獨佔和共用)。它不帶任何引數。
每個鎖定函數都會為成功傳回非零值。否則,會發生錯誤
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
的鎖定服務命名空間。可以使用效能結構描述來監控鎖定服務鎖定,因此版本令牌鎖定也是如此。如需詳細資訊,請參閱 鎖定服務監控。
對於版本令牌(Version Tokens)的鎖定功能,令牌名稱的參數會完全依照指定的方式使用。周圍的空白字元不會被忽略,且允許使用 =
和 ;
字元。這是因為版本令牌會直接將要鎖定的令牌名稱傳遞給鎖定服務。