MySQL Version Tokens

MySQL 5.7.8或更高版本的发行版包括版本令牌,该特性支持创建和同步服务器令牌,应用程序可以使用这些令牌来防止访问不正确或过时的数据。

版本令牌接口具有这些特征:
.版本令牌是由用作键或标识符的名称和一个值组成的对
.版本令牌可以被锁定。应用程序可以使用令牌锁向其他协作应用程序表明正在使用令牌,不应该修改它们。
.每个服务器都建立版本令牌列表;例如,指定服务器分配或操作状态。此外,与服务器通信的应用程序可以注册自己的令牌列表,这些令牌表示它需要服务器处于的状态。应用程序发送到不处于所需状态的服务器的SQL语句将产生错误。这是给应用程序的一个信号,它应该寻找另一个处于所需状态的服务器来接收SQL语句。

以下部分描述了版本令牌的组件,讨论了如何安装和使用它,并为其组件提供参考信息。

版本标识组件
名为version_token的服务器端插件持有与服务器关联的版本令牌列表,并订阅语句执行事件的通知。version_token插件使用审计插件API来监视来自客户端的传入语句,并将每个客户端特定于会话的版本令牌列表与服务器版本令牌列表进行匹配。如果存在匹配,插件允许语句通过,服务器继续处理它。否则,插件将向客户端返回一个错误,语句将失败。

一组用户定义函数(udf)提供了一个sql级别的API,用于操作和检查插件维护的服务器版本令牌列表。调用任何的令牌udf版本

系统变量允许客户端指定注册所需服务器状态的版本令牌列表。如果客户端发送语句时服务器处于不同的状态,则客户端接收到一个错误

安装或卸载版本令牌

这里介绍如何安装或卸载版本令牌,这些令牌是在包含插件和用户定义函数的插件库文件中实现的。有关安装或卸载插件和udf的一般信息要使服务器可用,插件库文件必须位于MySQL插件目录中(由plugin_dir系统变量命名的目录)。如果需要,在服务器启动时设置plugin_dir的值,告诉服务器插件目录的位置

插件库的基本名是version_token。文件名后缀因平台而异(例如,对于Unix和类Unix系统,.dll为Windows).

要安装版本令牌插件和udf,请使用install plugin并创建函数语句(根据需要调整.so后缀):

mysql> INSTALL PLUGIN version_tokens SONAME 'version_token.so';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    39
Current database: mysql

Query OK, 0 rows affected (0.07 sec)

mysql> CREATE FUNCTION version_tokens_set RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE FUNCTION version_tokens_show RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE FUNCTION version_tokens_edit RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE FUNCTION version_tokens_delete RETURNS STRING SONAME 'version_token.so';
CREATE FUNCTION version_tokens_lock_shared RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE FUNCTION version_tokens_lock_shared RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_lock_exclusive RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_unlock RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.03 sec)

您必须安装udf来管理服务器的版本令牌列表,但是您还必须安装插件,因为没有它udf将无法正常工作。

如果在主复制服务器上使用插件和udf,也要将它们安装在所有从服务器上,以避免复制问题

如前所述,一旦安装完成,版本令牌插件和udf将一直保持安装状态,直到卸载为止。要删除它们,使用UNINSTALL插件和DROP FUNCTION语句:

mysql> uninstall plugin version_tokens;
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql> drop function version_tokens_set;
drop function version_tokens_show;
Query OK, 0 rows affected (0.02 sec)

mysql> drop function version_tokens_show;
drop function version_tokens_edit;
Query OK, 0 rows affected (0.00 sec)

mysql> drop function version_tokens_edit;
Query OK, 0 rows affected (0.00 sec)

mysql> drop function version_tokens_delete;
Query OK, 0 rows affected (0.01 sec)

mysql> drop function version_tokens_lock_shared;
Query OK, 0 rows affected (0.00 sec)

mysql> drop function version_tokens_lock_exclusive;
Query OK, 0 rows affected (0.00 sec)

mysql> drop function version_tokens_unlock;
Query OK, 0 rows affected (0.02 sec)

使用版本标记
版本令牌可能有用的一个场景是,系统访问MySQL服务器集合,但是需要通过监视它们并根据负载变化调整服务器分配来管理它们,以实现负载平衡。这样一个系统包括这些组件
.要管理的MySQL服务器集合
.与服务器通信并将它们组织成高可用性组的管理或管理应用程序。组有不同的用途,每个组中的服务器可能有不同的分配。某个组内的服务器的分配可以随时更改
.访问服务器以检索和更新数据的客户机应用程序,根据分配给它们的用途选择服务器。例如,客户端不应该向只读服务器发送更新

版本令牌允许根据分配对服务器访问进行管理,而不需要客户端重复查询服务器的分配
.管理应用程序执行服务器分配并在每个服务器上建立版本令牌以反映其分配。应用程序缓存此信息以提供对其的中央访问点。如果在某个时候管理应用程序需要更改服务器分配(例如,将其从允许写改为仅允许读),则它将更改服务器的版本令牌列表并更新其缓存。

.为了提高性能,客户端应用程序从管理应用程序获取缓存信息,使它们不必为每个语句检索关于服务器分配的信息。基于它将发出的语句类型(例如,读与写),客户端选择适当的服务器并连接到它

.此外,客户端向服务器发送自己的客户端特定版本的令牌来注册它需要的服务器分配。对于客户端发送到服务器的每个语句,服务器将自己的令牌列表与客户端令牌列表进行比较。如果服务器令牌列表包含客户端令牌列表中所有具有相同值的令牌,则存在匹配,服务器执行该语句

另一方面,可能管理应用程序更改了服务器分配及其版本令牌列表。在这种情况下,新的服务器分配现在可能与客户端需求不兼容。服务器和客户端令牌列表之间的令牌不匹配,服务器返回一个错误作为对语句的应答。这指示客户机从管理应用程序缓存中刷新其版本令牌信息,并选择要与之通信的新服务器。

检测版本令牌错误和选择新服务器的客户端逻辑可以通过不同的方式实现:
.客户端可以自己处理所有版本令牌注册、不匹配检测和连接切换
.这些操作的逻辑可以在管理客户端和MySQL服务器之间连接的连接器中实现。这样的连接器可以处理错配错误检测和语句重新发送本身,也可以将错误传递给应用程序,并将其留给应用程序重新发送语句。

下面的例子以更具体的形式说明了前面的讨论。
当版本令牌在给定服务器上初始化时,服务器的版本令牌列表为空。通过调用用户定义函数(udf)来执行令牌列表维护。调用任何版本令牌udf都需要超级特权,因此具有该特权的管理或管理应用程序需要修改令牌列表。

假设一个管理应用程序与一组服务器通信,客户端查询这些服务器以访问雇员和产品数据库(分别名为emp和prod)。所有服务器都被允许处理数据检索语句,但只有一部分服务器被允许进行数据库更新。为了在特定于数据库的基础上处理这个问题,管理应用程序在每个服务器上建立一个版本令牌列表。在给定服务器的令牌列表中,令牌名称表示数据库名称,读写令牌值取决于数据库是否必须以只读方式使用,或者是否可以进行读写。

客户端应用程序通过设置系统变量来注册它们需要服务器匹配的版本令牌列表。变量设置是在客户端特定的基础上进行的,因此不同的客户端可以注册不同的需求。默认情况下,客户端令牌列表是空的,它匹配任何服务器令牌列表。当客户端将其令牌列表设置为非空值时,匹配可能成功也可能失败,这取决于服务器版本令牌列表。

为了定义服务器的版本令牌列表,管理应用程序调用version_token_set() UDF。(稍后将介绍用于修改和显示令牌列表的udf。)例如,应用程序可能将这些语句发送到三个服务器组成的组
服务器1:

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

服务器2:

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

服务器3:

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

在每种情况下,令牌列表都被指定为以分号分隔的名称=值对列表。产生的令牌列表值导致这些服务器连接:
.任何服务器都会接受对两个数据库中的任意一个进行读取
.只有服务器2接受对emp数据库的更新
.只有服务器3接受对prod数据库的更新

除了为每个服务器分配一个版本令牌列表外,管理应用程序还维护一个反映服务器分配的缓存。

在与服务器通信之前,客户机应用程序与管理应用程序进行联系,并检索关于服务器分配的信息。然后客户端根据这些分配选择服务器。假设客户机希望同时执行对emp数据库的读写操作。根据前面的分配,只有服务器2合格。客户机连接到服务器2,并通过设置
version_tokens_session系统变量在服务器2上注册服务器需求:

mysql> SET @@session.version_tokens_session = 'emp=write';
Query OK, 0 rows affected (0.00 sec)

对于客户机发送到服务器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.        |
+----------------------------------+
1 row in set (0.00 sec)

服务器2:

mysql> SELECT version_tokens_edit('emp=read');
+---------------------------------+
| version_tokens_edit('emp=read') |
+---------------------------------+
| 1 version tokens updated.       |
+---------------------------------+
1 row in set (0.00 sec)

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值)中命名的每个令牌获取一个共享锁
.执行服务器和客户端令牌列表之间的比较
.根据比较结果执行语句或产生错误
.释放锁

服务器使用共享锁,以便可以在不阻塞的情况下对多个会话进行比较,同时防止对任何试图在操作服务器令牌列表中具有相同名称的令牌之前获取独占锁的会话的令牌进行更改

前面的例子只使用了版本标记插件库中包含的一些用户定义,但是还有其他的。一组udf允许对服务器的版本令牌列表进行操作和检查。另一组udf允许锁定和解锁版本标记

这些udf允许创建、更改、删除和检查服务器的版本标记列表:
.version_tokens_set()完全替换当前列表并分配一个新列表。参数是一个以分号分隔的名称=值对列表。
.version_tokens_edit()支持对当前列表进行部分修改。它可以添加新的令牌或更改现有令牌的值。参数是一个以分号分隔的名称=值对列表
.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.               |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_edit('tok3=c');
+-------------------------------+
| version_tokens_edit('tok3=c') |
+-------------------------------+
| 1 version tokens updated.     |
+-------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_delete('tok2;tok1');
+------------------------------------+
| version_tokens_delete('tok2;tok1') |
+------------------------------------+
| 2 version tokens deleted.          |
+------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_show();
+-----------------------+
| version_tokens_show() |
+-----------------------+
| tok3=c;               |
+-----------------------+
1 row in set (0.00 sec)

如果令牌列表格式不正确,就会出现警告:

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)

如前所述,版本标记是使用一个以分号分隔的名称=值对列表来定义的。考虑一下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.                                         |
+---------------------------------------------------------------+
1 row in set (0.00 sec)

版本标记对参数的解释如下:
.名称和值周围的空白将被忽略。允许在名称和值中使用空格。(对于version_tokens_delete(),它接受一个没有值的名称列表,名称周围的空白将被忽略。)
.没有报价机制
.令牌的顺序并不重要,除非令牌列表包含给定令牌名称的多个实例,否则最后一个值优先于前面的值

根据这些规则,前面的version_tokens_set()调用会产生一个令牌列表,其中包含两个令牌:tok1的值是1’2 3’4,tok2的值是a = b。

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

如果令牌列表包含两个令牌,为什么version_tokens_set()返回设置的值3版本令牌?这是因为原来的令牌列表包含两个tok1定义,而第二个定义替换了第一个定义。

版本标记令牌操作udf将这些约束放在令牌名称和值上:
.令牌名称不能包含=或;字符,最大长度为64个字符
.令牌值不能包含;字符。值的长度受到max_allowed_packet系统变量的值的限制
.版本令牌将令牌名称和值视为二进制字符串,因此比较是区分大小写的

版本令牌还包括一组udf,允许对令牌进行锁定和解锁:
.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)'.

使用版本标记锁定功能的锁定是建议的;申请必须同意合作。

可以锁定不存在的令牌名称。这不会创建令牌。

对于版本令牌锁定函数,令牌名称参数完全按照指定的方式使用。周围的空白不被忽略,并且=和;字符是允许的。这是因为令牌只是像传递
给锁定服务一样传递要锁定的令牌名称。

版本标记引用

版本标记功能
版本令牌插件库包含几个用户定义的函数。一组udf允许对服务器的版本令牌列表进行操作和检查。另一组udf允许锁定和解锁版本标记。调用任何版本令牌UDF都需要超级特权。

下面的udf允许创建、更改、删除和检查服务器的版本令牌列表:
.version_tokens_delete (name_list)
使用name_list参数从服务器的版本令牌列表中删除令牌,并返回指示操作结果的二进制字符串。name_list是要删除的版本令牌名称的分号分隔列表。

mysql> SELECT version_tokens_delete('tok1;tok3');
+------------------------------------+
| version_tokens_delete('tok1;tok3') |
+------------------------------------+
| 2 version tokens deleted.          |
+------------------------------------+
1 row in set (0.00 sec)

从MySQL 5.7.9开始,NULL参数被视为空字符串,这对令牌列表没有影响。
version_tokens_delete()删除其参数中指定的标记(如果它们存在的话)。(删除不存在的令牌不是错误。)要在不知道列表中有哪些令牌的情况下完全清除令牌列表,请将NULL或不包含令牌的字符串传递给version_tokens_set():

mysql> SELECT version_tokens_set(NULL);
+------------------------------+
| version_tokens_set(NULL)     |
+------------------------------+
| Version tokens list cleared. |
+------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_set('');
+------------------------------+
| version_tokens_set('')       |
+------------------------------+
| Version tokens list cleared. |
+------------------------------+
1 row in set (0.00 sec)

.version_tokens_edit (token_list)
使用token_list参数修改服务器的版本令牌列表,并返回指示操作结果的二进制字符串。token_list是一个以分号分隔的名称=值对列表,它指定要定义的每个令牌的名称及其值。如果存在令牌,则使用给定的值更新其值。如果标记不存在,则使用给定的值创建它。如果参数为NULL或字符串不包含令牌,则令牌列表保持不变。

mysql> SELECT version_tokens_set('tok1=value1;tok2=value2');
+-----------------------------------------------+
| version_tokens_set('tok1=value1;tok2=value2') |
+-----------------------------------------------+
| 2 version tokens set.                         |
+-----------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_edit('tok2=new_value2;tok3=new_value3');
+--------------------------------------------------------+
| version_tokens_edit('tok2=new_value2;tok3=new_value3') |
+--------------------------------------------------------+
| 2 version tokens updated.                              |
+--------------------------------------------------------+
1 row in set (0.00 sec)

.version_tokens_set (token_list)
用token_list参数中定义的令牌替换服务器的版本令牌列表,并返回指示操作结果的二进制字符串。token_list是一个用分号分隔的名称=值对列表,指定要定义的每个令牌的名称及其值。如果参数为空或字符串不包含令牌,则清除令牌列表。

mysql> SELECT version_tokens_set('tok1=value1;tok2=value2');
+-----------------------------------------------+
| version_tokens_set('tok1=value1;tok2=value2') |
+-----------------------------------------------+
| 2 version tokens set.                         |
+-----------------------------------------------+
1 row in set (0.00 sec)

.version_tokens_show()
以二进制字符串的形式返回服务器的版本标记列表,其中包含一个以分号分隔的名称=值对列表。

mysql> SELECT version_tokens_show();
+--------------------------+
| version_tokens_show()    |
+--------------------------+
| tok2=value2;tok1=value1; |
+--------------------------+
1 row in set (0.00 sec)

下面的udf允许锁定和解锁版本标记:
.version_tokens_lock_exclusive (token_name [token_name]…超时)
获取一个或多个版本令牌(按名称指定为字符串)上的独占锁,如果未在给定的超时值内获取锁,则超时并报错

mysql> SELECT version_tokens_lock_exclusive('lock1', 'lock2', 10);
+-----------------------------------------------------+
| version_tokens_lock_exclusive('lock1', 'lock2', 10) |
+-----------------------------------------------------+
| 1 |
+-----------------------------------------------------+

这个函数是在MySQL 5.7.8中添加的,名为vtoken_get_write_locks(),在5.7.9中重命名为version_tokens_lock_exclusive()

.version_tokens_lock_shared (token_name [token_name]…超时)
获取一个或多个版本令牌(按名称指定为字符串)上的共享锁,如果未在给定的超时值内获取锁,则超时并报错

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

这个函数是在MySQL 5.7.8中添加的,名为vtoken_get_read_locks(),在5.7.9中重命名为version_tokens_lock_shared()

.version_tokens_unlock ()
使用version_tokens_lock_exclusive()和version_tokens_lock_shared()释放在当前会话中获取的所有锁。

mysql> SELECT version_tokens_unlock();
+-------------------------+
| version_tokens_unlock() |
+-------------------------+
| 1 |
+-------------------------+

这个函数是在MySQL 5.7.8中添加的,名为vtoken_release_locks(),在5.7.9中重命名为version_tokens_unlock()。

锁定功能共享这些特性:
.对于成功,返回值为非零。否则,将发生错误
.令牌名称是字符串
.与操作服务器令牌列表的udf的参数处理不同,令牌名称参数周围的空白不会被忽略,并且=和;字符是允许的
.可以锁定不存在的令牌名称。这不会创建令牌
.超时值是非负整数,表示在出现错误超时之前等待获取锁所需的时间(以秒为单位)。如果超时为0,则不存在等待,如果不能立即获取锁,则该函数将产生一个错误
.版本令牌锁定功能基于所描述的锁定服务在Section 28.3.1 锁服务中描述。

版本令牌系统变量
版本标记支持以下系统变量。这些变量不可用,除非安装版本令牌插件

系统变量:
.version_tokens_session
命令行格式:–version-tokens-session=value
变量范围:Global,Session
动态:Yes
允许的值:类型为字符串,默认值为NULL
此变量的会话值指定客户端版本令牌列表,并指示客户端会话要求服务器版本令牌列表具有的令牌。

如果version_tokens_session变量为NULL(默认值)或值为空,则任何服务器版本令牌列表都是匹配的。(实际上,空值会禁用匹配需求。)

如果version_tokens_session变量有一个非空值,那么它的值与服务器版本令牌列表之间的任何不匹配都会导致会话发送给服务器的任何语句出错。在这种情况下会发生失配:
.version_tokens_session值中的令牌名称在服务器令牌列表中不存在。在本例中,发生了ER_VTOKEN_PLUGIN_TOKEN_NOT_FOUND错误
.version_tokens_session值中的令牌值与服务器令牌列表中相应令牌的值不同。在本例中,将出现ER_VTOKEN_PLUGIN_TOKEN_MISMATCH错误

服务器版本令牌列表中包含没有在version_tokens_session值中命名的令牌不是不匹配的

假设一个管理应用程序将服务器令牌列表设置为如下所示:

mysql> SELECT version_tokens_set('tok1=a;tok2=b;tok3=c');
+--------------------------------------------+
| version_tokens_set('tok1=a;tok2=b;tok3=c') |
+--------------------------------------------+
| 3 version tokens set. |
+--------------------------------------------+

客户端通过设置其version_tokens_session值来注册它需要服务器匹配的令牌。然后,对于客户端发送的每个后续语句,服务器将根据客户
端version_tokens_session值检查其令牌列表,如果存在不匹配,则产生一个错误:

mysql> SET @@session.version_tokens_session = 'tok1=a;tok2=b';
mysql> SELECT 1;
+---+
| 1 |
+---+
| 1 |
+---+
mysql> SET @@session.version_tokens_session = 'tok1=b';
mysql> SELECT 1;
ERROR 3136 (42000): Version token mismatch for tok1. Correct value a

第一个选择成功是因为客户端令牌tok1和tok2出现在服务器令牌列表中,并且每个令牌在服务器列表中具有相同的值。第二个SELECT失败是因为,虽然tok1出现在服务器令牌列表中,但它的值与客户机指定的值不同

此时,客户端发送的任何语句都将失败,除非服务器令牌列表发生更改,使其再次匹配。假设管理应用程序按如下方式更改服务器令牌列表:

mysql> SELECT version_tokens_edit('tok1=b');
+-------------------------------+
| version_tokens_edit('tok1=b') |
+-------------------------------+
| 1 version tokens updated. |
+-------------------------------+
mysql> SELECT version_tokens_show();
+-----------------------+
| version_tokens_show() |
+-----------------------+
| tok3=c;tok1=b;tok2=b; |
+-----------------------+

现在,客户机version_tokens_session值与服务器令牌列表匹配,客户机可以再次成功执行语句:

mysql> SELECT 1;
+---+
| 1 |
+---+
| 1 |
+---+

这个变量是在MySQL 5.7.8中添加的
.version_tokens_session_number
命令行格式:–version-tokens-session-number=N
变量范围:Global,Session
动态:NO
允许的值:类型为整型,默认值为0
此变量供内部使用。
这个变量是在MySQL 5.7.8中添加的。

DMRAC缓存交换

缓存交换
根据目前的硬件发展状况来看,网络的传输速度比磁盘的读、写速度更快,因此,DMRAC集群引入了缓存交换(Buffer Swap)技术,节点间的数据页尽可能通过网络传递,避免通过磁盘的写入、再读出方式在节点间传递数据,从而减少数据库的IO等待时间,提升系统的响应速度。

缓存交换的实现基础是GBS/LBS服务,在GBS/LBS中维护了Buffer数据页的相关信息。包括:1. 闩的封锁权限(LATCH);2. 哪些站点访问过此数据页(Access MAP);3. 最新数据保存在哪一个节点(Fresh EP)中;4. 以及最新数据页的LSN值(Fresh LSN)等信息。这些信息作为LBS封锁、GBS授权和GBS权限回收请求的附加信息进行传递,因此并不会带来额外的通讯开销。

下面,以两节点DMRAC集群(EP0/EP1)访问数据页P1为例子。初始页P1位于共享存储上,P1的GBS控制结构位于节点EP1上。初始页P1还没有被任何一个节点访问过,初始页P1的LSN为10000。通过几种常见场景分析,逐步深入,解析缓存交换的原理。
场景1
节点EP0访问数据页P1。
1. 节点EP0的本地LBS向EP1的GBS请求数据页P1的S LATCH权限
2. 节点EP1的GBS修改P1控制结构,记录访问节点EP0的封锁模式为S LATCH(数据分布节点为EP0),并响应EP0的LBS请求
3. 节点EP0的LBS获得GBS授权后,记录获得的授权模式是S_LATCH,P1数据不在其他节点的Buffer中,发起本地IO请求,从磁盘读取数据。
IO完成后,修改LBS控制结构,记录数据页上的LSN信息

场景2
节点EP1访问数据页P1。
1. 节点EP1本地LBS向EP1的GBS请求数据页P1的S LATCH权限
2. 节点EP1的GBS修改控制结构,记录访问节点EP1的封锁模式为S LATCH(数据分布节点为EP0/EP1),并响应EP1的LBS请求
3. 节点EP1的LBS获得GBS授权后,记录获得的授权模式是S LATCH,根据数据分布情况,EP1向EP0发起P1的读请求,通过内部网络从EP0获取
数据,而不是重新从磁盘读取P1数据

场景3
节点EP0修改数据页P1。
1. 节点EP0本地LBS向EP1的GBS请求数据页P1的X LATCH权限(附加LSN信息)
2. 节点EP1的GBS修改控制结构的LSN值,从EP1的LBS回收P1的权限
3. 修改访问节点EP0的封锁模式为S + X LATCH,并响应EP0的LBS请求
4. 节点EP0的LBS获得GBS授权后,记录获得的授权模式是S + X LATCH
5. 节点EP0修改数据页P1,LSN修改为11000
这个过程中,只有全局Latch请求,数据页并没有在节点间传递。

修改之后,数据页P1的LSN修改为11000。如下所示:

场景4
节点EP1修改数据页P1。
1.节点EP1本地LBS向EP1的GBS请求数据页P1的X LATCH权限
2.节点EP1的GBS发现P1被EP0以S + X方式封锁,向EP0发起回收P1权限的请求
3.节点EP0释放P1的全局LATCH,响应GBS,并且在响应消息中附加了最新的PAGE LSN值
4.节点EP1的GBS收到EP0的响应后,修改GBS控制结构,记录最新数据保存在EP0,最新的LSN值信息,记录EP0获得的授权模式是S + X LATCH
(此时,数据分布节点仍然是EP0/EP1),并授权EP1的LBS
5.节点EP1的LBS收到授权信息后,记录获得的授权模式是S + X LATCH,并根据数据分布情况,向节点EP0发起数据页P1的读请求
6.节点EP1修改数据页P1,LSN修改为12000

修改之后,数据页P1的LSN修改为12000。如下所示:

这个过程中,数据页P1的最新数据从EP0传递到了EP1,但并没有产生磁盘IO。

达梦7异构(DM-Oracle) DBLINK

使用DM数据库,创建一个连接到IP地址为10.10.10.180机器上的oracle数据库的外部链接。可以通过三种方式创建:一网络服务名tsn_name;二连接描述符description;三/< 服务名>。

(一) 通过网络服务名创建
首先介绍Oracle网络服务名的配置方法。网络服务名配置成功才能创建DBLINK
安装Oracle客户端挺费时间的,而且大部分功能都用不到,Oracle官方给出了简易客户端,直接解压就可以使用,下载地址:http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html
下面来看安装步骤:
1、下载安装包,我这里把所有的都下载下来了

 [root@dmks soft]# ls -lrt
总用量 2293896
drwxr-xr-x. 19 1000 1000       4096 4月  20 2010 unixODBC-2.3.0
drwxrwxr-x.  5  502  502       4096 1月  27 2017 client
-rw-r--r--.  1  502  502 1258314437 3月   2 2017 linuxx64_12201_client.zip
-rw-r--r--.  1 root root  493170688 3月  19 2018 dm7_setup_neokylin6_64_20180209.iso
-rw-r--r--.  1 root root  488814959 3月   4 15:19 DM_linux64.zip
drwxr-xr-x.  2 root root       4096 3月   6 19:47 dm_soft
-rw-r--r--.  1 root root    1804749 3月  11 17:20 unixODBC-2.3.0.tar.gz
-rw-r--r--.  1 root root     904309 3月  21 18:38 instantclient-sqlplus-linux.x64-12.2.0.1.0.zip
-rw-r--r--.  1 root root   68965195 3月  21 18:42 instantclient-basic-linux.x64-12.2.0.1.0.zip
-rw-r--r--.  1 root root     674743 3月  21 18:45 instantclient-sdk-linux.x64-12.2.0.1.0.zip
-rw-r--r--.  1 root root    1572942 3月  21 22:51 instantclient-jdbc-linux.x64-12.2.0.1.0.zip
-rw-r--r--.  1 root root     634023 3月  21 22:51 instantclient-odbc-linux.x64-12.2.0.1.0-2.zip
-rw-r--r--.  1 root root   32917466 3月  21 22:51 instantclient-basiclite-linux.x64-12.2.0.1.0.zip
-rw-r--r--.  1 root root    1132671 3月  21 22:52 instantclient-tools-linux.x64-12.2.0.1.0.zip

2、unzip解压
解压出来一个目录instantclient_12_2

[root@dmks soft]# unzip instantclient-sqlplus-linux.x64-12.2.0.1.0.zip
Archive:  instantclient-sqlplus-linux.x64-12.2.0.1.0.zip
  inflating: instantclient_12_2/glogin.sql
  inflating: instantclient_12_2/libsqlplusic.so
  inflating: instantclient_12_2/libsqlplus.so
  inflating: instantclient_12_2/sqlplus
  inflating: instantclient_12_2/SQLPLUS_README
[root@dmks soft]# unzip instantclient-basic-linux.x64-12.2.0.1.0.zip
Archive:  instantclient-basic-linux.x64-12.2.0.1.0.zip
  inflating: instantclient_12_2/adrci
  inflating: instantclient_12_2/BASIC_README
  inflating: instantclient_12_2/genezi
  inflating: instantclient_12_2/libclntshcore.so.12.1
  inflating: instantclient_12_2/libclntsh.so.12.1
  inflating: instantclient_12_2/libipc1.so
  inflating: instantclient_12_2/libmql1.so
  inflating: instantclient_12_2/libnnz12.so
  inflating: instantclient_12_2/libocci.so.12.1
  inflating: instantclient_12_2/libociei.so
  inflating: instantclient_12_2/libocijdbc12.so
  inflating: instantclient_12_2/libons.so
  inflating: instantclient_12_2/liboramysql12.so
  inflating: instantclient_12_2/ojdbc8.jar
  inflating: instantclient_12_2/uidrvci
  inflating: instantclient_12_2/xstreams.jar
[root@dmks soft]# unzip instantclient-sdk-linux.x64-12.2.0.1.0.zip
Archive:  instantclient-sdk-linux.x64-12.2.0.1.0.zip
   creating: instantclient_12_2/sdk/
  inflating: instantclient_12_2/sdk/ott
 extracting: instantclient_12_2/sdk/ottclasses.zip
  inflating: instantclient_12_2/sdk/SDK_README
   creating: instantclient_12_2/sdk/demo/
  inflating: instantclient_12_2/sdk/demo/setuporamysql.sh
  inflating: instantclient_12_2/sdk/demo/occiobj.typ
  inflating: instantclient_12_2/sdk/demo/occidml.cpp
  inflating: instantclient_12_2/sdk/demo/occidemo.sql
  inflating: instantclient_12_2/sdk/demo/occiobj.cpp
  inflating: instantclient_12_2/sdk/demo/occidemod.sql
  inflating: instantclient_12_2/sdk/demo/oraaccess.xml
  inflating: instantclient_12_2/sdk/demo/demo.mk
  inflating: instantclient_12_2/sdk/demo/cdemo81.c
   creating: instantclient_12_2/sdk/include/
  inflating: instantclient_12_2/sdk/include/occiControl.h
  inflating: instantclient_12_2/sdk/include/oro.h
  inflating: instantclient_12_2/sdk/include/ociapr.h
  inflating: instantclient_12_2/sdk/include/occiCommon.h
  inflating: instantclient_12_2/sdk/include/occiData.h
  inflating: instantclient_12_2/sdk/include/oci8dp.h
  inflating: instantclient_12_2/sdk/include/ociextp.h
  inflating: instantclient_12_2/sdk/include/orl.h
  inflating: instantclient_12_2/sdk/include/nzt.h
  inflating: instantclient_12_2/sdk/include/ldap.h
  inflating: instantclient_12_2/sdk/include/occi.h
  inflating: instantclient_12_2/sdk/include/ociap.h
  inflating: instantclient_12_2/sdk/include/odci.h
  inflating: instantclient_12_2/sdk/include/ocixstream.h
  inflating: instantclient_12_2/sdk/include/nzerror.h
  inflating: instantclient_12_2/sdk/include/oci1.h
  inflating: instantclient_12_2/sdk/include/ori.h
  inflating: instantclient_12_2/sdk/include/ocixmldb.h
  inflating: instantclient_12_2/sdk/include/ocidem.h
  inflating: instantclient_12_2/sdk/include/occiAQ.h
  inflating: instantclient_12_2/sdk/include/ocidef.h
  inflating: instantclient_12_2/sdk/include/occiObjects.h
  inflating: instantclient_12_2/sdk/include/oci.h
  inflating: instantclient_12_2/sdk/include/oratypes.h
  inflating: instantclient_12_2/sdk/include/orid.h
  inflating: instantclient_12_2/sdk/include/xa.h
  inflating: instantclient_12_2/sdk/include/ocikpr.h
  inflating: instantclient_12_2/sdk/include/ocidfn.h
  inflating: instantclient_12_2/sdk/include/ort.h
   creating: instantclient_12_2/sdk/admin/
  inflating: instantclient_12_2/sdk/admin/oraaccess.xsd
[root@dmks soft]# unzip instantclient-jdbc-linux.x64-12.2.0.1.0.zip
Archive:  instantclient-jdbc-linux.x64-12.2.0.1.0.zip
  inflating: instantclient_12_2/JDBC_README
  inflating: instantclient_12_2/libheteroxa12.so
  inflating: instantclient_12_2/orai18n.jar
  inflating: instantclient_12_2/orai18n-mapping.jar
[root@dmks soft]# unzip instantclient-odbc-linux.x64-12.2.0.1.0-2.zip
Archive:  instantclient-odbc-linux.x64-12.2.0.1.0-2.zip
   creating: instantclient_12_2/help/
   creating: instantclient_12_2/help/ja/
  inflating: instantclient_12_2/help/ja/blafdoc.css
  inflating: instantclient_12_2/help/ja/oracle.gif
   creating: instantclient_12_2/help/ja/img_text/
  inflating: instantclient_12_2/help/ja/img_text/setup_ssmig.htm
  inflating: instantclient_12_2/help/ja/img_text/setup_work.htm
  inflating: instantclient_12_2/help/ja/img_text/odbcmodel.htm
  inflating: instantclient_12_2/help/ja/img_text/setup_app.htm
  inflating: instantclient_12_2/help/ja/img_text/setup_ora.htm
  inflating: instantclient_12_2/help/ja/img_text/odbcdrvarch.htm
  inflating: instantclient_12_2/help/ja/toc.htm
  inflating: instantclient_12_2/help/ja/map.xml
   creating: instantclient_12_2/help/ja/META-INF/
  inflating: instantclient_12_2/help/ja/META-INF/MANIFEST.MF
   creating: instantclient_12_2/help/ja/img/
  inflating: instantclient_12_2/help/ja/img/setup_app.gif
  inflating: instantclient_12_2/help/ja/img/odbcmodel.gif
  inflating: instantclient_12_2/help/ja/img/setup_ora.gif
  inflating: instantclient_12_2/help/ja/img/odbcdrvarch.gif
  inflating: instantclient_12_2/help/ja/img/setup_ssmig.gif
  inflating: instantclient_12_2/help/ja/img/setup_work.gif
  inflating: instantclient_12_2/help/ja/cpyr.htm
  inflating: instantclient_12_2/help/ja/sqora.htm
   creating: instantclient_12_2/help/us/
  inflating: instantclient_12_2/help/us/blafdoc.css
  inflating: instantclient_12_2/help/us/oracle.gif
   creating: instantclient_12_2/help/us/img_text/
  inflating: instantclient_12_2/help/us/img_text/setup_ssmig.htm
  inflating: instantclient_12_2/help/us/img_text/setup_work.htm
  inflating: instantclient_12_2/help/us/img_text/odbcmodel.htm
  inflating: instantclient_12_2/help/us/img_text/setup_app.htm
  inflating: instantclient_12_2/help/us/img_text/setup_ora.htm
  inflating: instantclient_12_2/help/us/img_text/odbcdrvarch.htm
  inflating: instantclient_12_2/help/us/toc.htm
  inflating: instantclient_12_2/help/us/map.xml
   creating: instantclient_12_2/help/us/META-INF/
  inflating: instantclient_12_2/help/us/META-INF/MANIFEST.MF
   creating: instantclient_12_2/help/us/img/
  inflating: instantclient_12_2/help/us/img/setup_app.gif
  inflating: instantclient_12_2/help/us/img/odbcmodel.gif
  inflating: instantclient_12_2/help/us/img/setup_ora.gif
  inflating: instantclient_12_2/help/us/img/odbcdrvarch.gif
  inflating: instantclient_12_2/help/us/img/setup_ssmig.gif
  inflating: instantclient_12_2/help/us/img/setup_work.gif
  inflating: instantclient_12_2/help/us/cpyr.htm
  inflating: instantclient_12_2/help/us/sqora.htm
  inflating: instantclient_12_2/libsqora.so.12.1
  inflating: instantclient_12_2/ODBC_IC_Readme_Unix.html
  inflating: instantclient_12_2/odbc_update_ini.sh
[root@dmks soft]# unzip instantclient-basiclite-linux.x64-12.2.0.1.0.zip
Archive:  instantclient-basiclite-linux.x64-12.2.0.1.0.zip
replace instantclient_12_2/adrci? [y]es, [n]o, [A]ll, [N]one, [r]ename: A
  inflating: instantclient_12_2/adrci
  inflating: instantclient_12_2/BASIC_LITE_README
  inflating: instantclient_12_2/genezi
  inflating: instantclient_12_2/libclntshcore.so.12.1
  inflating: instantclient_12_2/libclntsh.so.12.1
  inflating: instantclient_12_2/libipc1.so
  inflating: instantclient_12_2/libmql1.so
  inflating: instantclient_12_2/libnnz12.so
  inflating: instantclient_12_2/libocci.so.12.1
  inflating: instantclient_12_2/libociicus.so
  inflating: instantclient_12_2/libocijdbc12.so
  inflating: instantclient_12_2/libons.so
  inflating: instantclient_12_2/liboramysql12.so
  inflating: instantclient_12_2/ojdbc8.jar
  inflating: instantclient_12_2/uidrvci
  inflating: instantclient_12_2/xstreams.jar
[root@dmks soft]# unzip instantclient-tools-linux.x64-12.2.0.1.0.zip
Archive:  instantclient-tools-linux.x64-12.2.0.1.0.zip
  inflating: instantclient_12_2/exp
  inflating: instantclient_12_2/expdp
  inflating: instantclient_12_2/imp
  inflating: instantclient_12_2/impdp
  inflating: instantclient_12_2/libnfsodm12.so
  inflating: instantclient_12_2/sqlldr
  inflating: instantclient_12_2/TOOLS_README
  inflating: instantclient_12_2/wrc

3、配置环境变量

export ORACLE_HOME=/soft/instantclient_12_2
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME
export PATH=$ORACLE_HOME:$PATH

4、配置tns

[root@dmks instantclient_12_2]# mkdir -p network/admin
[root@dmks instantclient_12_2]# cd  network/admin/

[root@dmks admin]# vi tnsnames.ora
shardcat =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 10.10.10.180)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = shardcat)
    )
  )

5、测试sqlplus,成功

[root@dmks admin]# sqlplus jy/xxzx7817600@shardcat

SQL*Plus: Release 12.2.0.1.0 Production on Sat Mar 21 23:07:41 2020

Copyright (c) 1982, 2016, Oracle.  All rights reserved.

Last Successful login time: Sun Mar 22 2020 01:36:51 +08:00

Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production

SQL>

拷贝动态库文件

[root@dmks instantclient_12_2]# cp  *so*  /dm_home/dmdbms/bin/

[root@dmks instantclient_12_2]# cd  /dm_home/dmdbms/bin/
[root@dmks bin]# chown -R dmdba:dinstall *so*  //一定要将动态库文件修改为dmdba用户所有
[root@dmks bin]# chmod -R 777 /dm_home/dmdbms/bin/

创建DBlink
网络服务名配置成功后,就可以使用网络服务名shardcat或网络连接描述符创建DBLINK.

SQL> create or replace public link link3 connect 'oracle' with system identified by "xxzx7817600" using 'shardcat';
executed successfully
used time: 27.431(ms). Execute id is 8.
SQL> select * from v$version@link3;

LINEID     BANNER                                                                       CON_ID
---------- ---------------------------------------------------------------------------- ------
1          Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
2          PL/SQL Release 12.2.0.1.0 - Production                                       0
3          CORE 12.2.0.1.0 Production                                                   0
4          TNS for Linux: Version 12.2.0.1.0 - Production                               0
5          NLSRTL Version 12.2.0.1.0 - Production                                       0

used time: 66.846(ms). Execute id is 9.

(二) 通过连接描述符创建

SQL> create or replace public link link4 connect 'oracle' with system identified by "xxzx7817600" using '(description =
2   (address_list = (address = (protocol = tcp)(host = 10.10.10.180)(port = 1521)))
3   (connect_data = (service_name = shardcat)))';
executed successfully
used time: 19.074(ms). Execute id is 10.
SQL> select * from v$version@link4;

LINEID     BANNER                                                                       CON_ID
---------- ---------------------------------------------------------------------------- ------
1          Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
2          PL/SQL Release 12.2.0.1.0 - Production                                       0
3          CORE 12.2.0.1.0 Production                                                   0
4          TNS for Linux: Version 12.2.0.1.0 - Production                               0
5          NLSRTL Version 12.2.0.1.0 - Production                                       0

used time: 140.930(ms). Execute id is 12.

(三) 通过/< 服务名>创建

SQL> create or replace public link link5 connect 'oracle' with system identified by "xxzx7817600" using '10.10.10.180/shardcat';
executed successfully
used time: 17.347(ms). Execute id is 13.
SQL> select * from v$version@link5;

LINEID     BANNER                                                                       CON_ID
---------- ---------------------------------------------------------------------------- ------
1          Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
2          PL/SQL Release 12.2.0.1.0 - Production                                       0
3          CORE 12.2.0.1.0 Production                                                   0
4          TNS for Linux: Version 12.2.0.1.0 - Production                               0
5          NLSRTL Version 12.2.0.1.0 - Production                                       0


used time: 92.869(ms). Execute id is 14.
SQL> select * from jy.t1@link3;

LINEID     T_ID T_NAME
---------- ---- -------
1          9    m_YYL
2          1    m_A
3          2    m_B
4          3    m_C
5          4    m_D
6          5    m_E
7          6    m_F
8          7    m_JYHY
9          8    m_JYYYL

9 rows got

used time: 10.891(ms). Execute id is 15.
SQL> insert into jy.t1@link3 values(9,'m_wy');
affect rows 1

used time: 33.658(ms). Execute id is 16.
SQL> commit;
executed successfully
used time: 20.563(ms). Execute id is 17.

SQL> select * from jy.t1;

      T_ID T_NAME
---------- ----------------------------------------
         9 m_wy
         9 m_YYL
         1 m_A
         2 m_B
         3 m_C
         4 m_D
         5 m_E
         6 m_F
         7 m_JYHY
         8 m_JYYYL

10 rows selected.

删除外部链接
删除一个外部链接。
语法格式
DROP [PUBLIC] LINK [< 模式名>.]< 外部链接名>;
参数
1.< 模式名> 指明被操作的外部链接属于哪个模式,缺省为当前模式;
2.< 外部链接名> 指明被操作的外部链接的名称。

语句功能
删除一个外部链接。
使用说明
只有链接对象的创建者和DBA拥有该对象的删除权限。

举例说明
删除外部链接LINK1。
DROP LINK LINK1;

使用外部链接通过外部链接,可以对远程服务器的对象进行查询或进行增删改操作,可以调用远程的过程。使用外部链接进行查询或增删改的语法格式与普通格式基本一致,唯一的区别在于指定外部链接表时需要使用如下格式作为表或视图的引用:
[TABLENAME | VIEWNAME] [LINK | @] 链接名

举例说明 使用外部链接查询LINK1上的远程表进行查询
SELECT * FROM SYSOBJECTS LINK LINK1; 或对远程表进行插入数据:
INSERT INTO T1@LINK1 VALUES(1,2,3);
也可以查询本地表或其他链接的表对远程表进行操作,如
UPDATE T1@LINK1 SET C1 = C1+1 WHERE C2 NOT IN (SELECT ID FROM LOCAL_TABLE);
DELETE FROM T1@LINK1 WHERE C1 IN (SELECT ID FROM T2@LINK2);

使用外部链接,可以调用远程的存储过程,但是不支持调用远程的函数,使用中有以下约束:
(1)参数数据类型为SQL类型,不允许为DMSQL程序类型;
(2)参数数据类型不允许为复合类型。
其使用方式为:
[CALL] [< 模式名>.][< 包名>.]< 过程名> [@] < 外部链接名>(< 参数列>);

使用限制
外部链接的使用有以下限制:
1. DM-DM的同构外部链接不支持MPP环境,DM与异构数据库的外部链接支持MPP环境;
2. 增删改不支持INTO语句;
3. 不支持使用游标进行增删改操作;
4. DBLINK理论上不支持LOB类型列的操作,但支持简单的增删改语句中使用常量来对LOB类型列进行操作。
另外,DM连接异构数据库的外部链接还有如下使用限制:
1. 数据类型以DM为基础,不支持DM没有的数据类型;
2. 语法以DM的语法为标准,不支持DM不兼容的语法;
3. 主键更新,如果是涉及到多个服务器的语句,不能保证更新操作一定成功。

达梦7同构(DM-DM)DBLINK

外部链接对象(LINK)是DM中的一种特殊的数据库实体对象,它记录了远程数据库的连接和路径信息,用于建立与远程数据的联系。通过多台数据库主库间的相互通讯,用户可以透明地操作远程数据库的数据,使应用程序看起来只有一个大型数据库。用户远程数据库中的数据请求,都被自动转换为网络请求,并在相应结点上实现相应的操作。用户可以建立一个数据库链接,以说明一个对象在远程数据库中的访问路径。这个链接可以是公用的(数据库中所有用户使用),也可以是私有的(只能被某个用户使用)。

用户可以通过外部链接对远程数据库的表进行查询和增删改操作,以及本地调用远程的存储过程。

语法格式
CREATE [OR REPLACE] [PUBLIC] LINK < 外部链接名> CONNECT [‘< 连接库类型>‘] WITH < 登录名> IDENTIFIED BY < 登录口令> USING ‘< 连接串> ‘;
< 连接库类型> ::= DAMENG | ORACLE | ODBC
< 连接串> ::=< 外部链接串>
< 外部链接串>::=< DAMENG外部链接串>| < ORACLE外部链接串> |
< DAMENG外部链接串>::=< 实例IP地址>/< 实例端口号> |
/ |

< ORACLE外部链接串>::= ||/< 服务名>
::=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=)(PORT=< 端口号>)))(CONNECT_DATA=(SERVICE_NAME=< 服务名>)))
::=

参数
1. OR REPLACE 使用OR REPLACE选项的好处是,如果系统中已经有同名的数据库链接名,服务器会自动用新的代码覆盖原来的代码。如果不使用OR REPLACE选项,当创建的新外部链接名称与系统中已有的外部链接名称同名时,服务器会报错。
2. PUBLIC 此链接对象是否能够被创建者之外的用户引用;
3. < 外部链接名> 数据库链接的名称;
4. < 连接库类型> 目前只支持DAMENG、ORACLE或ODBC,默认为DAMENG;
5. < 登录名> 登录用户名称;
6. < 登录口令> 登录用户口令;
7. 支持三种格式,分别对应目标节点在dmmal.ini中的配置项,具体如下:
l mal_inst_host/mal_inst_port
l mal_host/mal_port
l mal_inst_name
8. 可以使用配置的网络服务名tsn_name(网络服务名需要配置),或者连接描述符description(连接描述符是网络连接目标特殊格式的描述,它包括网络协议、主库IP地址、端口号和服务名),或者/< 服务名>;
9. DSN需要用户手动配置。

语句功能
创建一个外部链接。
使用说明
1.要创建到DM数据库的外部链接,必须首先配置dmmal.ini,才能使用LINK。DM的连接串有两种格式:
INSTANCE_NAME:直接使用远程库的实例名(该实例名必须配置到dmmal.ini中);
/< 端口号>:其中端口号为DM外部链接服务器的dmmal.ini配置中的MAL_PORT端口号。
dmmal.ini的详细配置可参考《DM7系统管理员手册》的2.1节,需要注意同时将dm.ini中的MAL_INI参数置为1以开启MAL系统。
2. 要创建到ORACLE的外部链接,可以使用配置的网络服务名;如果没有配置tsn_name,可以使用连接描述符或者/< 服务名>作为连接串。
3.通过LINK对远程服务器所作的修改,由用户在本地服务器通过commit或rollback进行提交或回滚。
4.只支持普通用户,不支持SSL和Kerberos认证。
5.DM7不支持连接自身实例的LINK。
6.支持在CREATE SCHEMA中CREATE LINK,但是不支持CREATE PUBLIC LINK。
7.只有DBA和具有CREATE LINK权限的用户可以创建外部链接。

举例说明 例1 使用DM数据库,创建一个连接到IP地址为10.10.10.186,MAL_PORT端口号为5336的MAL站点的外部链接,登录到此站点使用的用户名为sysdba,密码为dameng123,实例名为:dmks。
先对远程DM数据库设置dmmai.in文件,并设置MAL_INI参数为1,然后重启dmks数据库

[dmdba@dmks dmks]$ vi dmmal.ini
MAL_CHECK_INTERVAL = 5
MAL_CONN_FAIL_INTERVAL = 5

[MAL_dmks]
MAL_INST_NAME = dmks
MAL_HOST = 10.10.10.187
MAL_PORT = 5337
MAL_INST_PORT = 5236
MAL_INST_HOST = 10.10.10.187

[MAL_jydm]
MAL_INST_NAME = jydm
MAL_HOST = 10.10.10.180
MAL_PORT = 5336
MAL_INST_PORT = 5236
MAL_INST_HOST = 10.10.10.180


SQL> select sf_get_para_value(1,'MAL_INI');

LINEID     SF_GET_PARA_VALUE(1,'MAL_INI')
---------- ------------------------------
1          0

used time: 7.852(ms). Execute id is 1609.
SQL> select * from v$dm_ini where para_name='MAL_INI';

LINEID     PARA_NAME PARA_VALUE MIN_VALUE MAX_VALUE MPP_CHK SESS_VALUE FILE_VALUE DESCRIPTION PARA_TYPE
---------- --------- ---------- --------- --------- ------- ---------- ---------- ----------- ---------
1          MAL_INI   0          0         1         N       0          0          dmmal.ini   IN FILE

used time: 8.395(ms). Execute id is 1610.

SQL> alter system set 'MAL_INI'=1 spfile;
DMSQL executed successfully
used time: 8.209(ms). Execute id is 1611.
SQL> select sf_get_para_value(1,'MAL_INI');

LINEID     SF_GET_PARA_VALUE(1,'MAL_INI')
---------- ------------------------------
1          1

used time: 5.533(ms). Execute id is 1612.
SQL>  select * from v$dm_ini where para_name='MAL_INI';

LINEID     PARA_NAME PARA_VALUE MIN_VALUE MAX_VALUE MPP_CHK SESS_VALUE FILE_VALUE DESCRIPTION PARA_TYPE
---------- --------- ---------- --------- --------- ------- ---------- ---------- ----------- ---------
1          MAL_INI   0          0         1         N       0          1          dmmal.ini   IN FILE

used time: 7.583(ms). Execute id is 1613.


[root@dmks ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 0.0.0.0:5901                0.0.0.0:*                   LISTEN      24268/Xvnc
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      1738/rpcbind
tcp        0      0 0.0.0.0:39792               0.0.0.0:*                   LISTEN      1964/rpc.statd
tcp        0      0 0.0.0.0:6001                0.0.0.0:*                   LISTEN      24268/Xvnc
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      2191/sshd
tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      2027/cupsd
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      2290/master
tcp        0      0 :::38955                    :::*                        LISTEN      1964/rpc.statd
tcp        0      0 :::111                      :::*                        LISTEN      1738/rpcbind
tcp        0      0 :::6001                     :::*                        LISTEN      24268/Xvnc
tcp        0      0 :::5236                     :::*                        LISTEN      21657/dmserver
tcp        0      0 :::22                       :::*                        LISTEN      2191/sshd
tcp        0      0 ::1:631                     :::*                        LISTEN      2027/cupsd
tcp        0      0 ::1:25                      :::*                        LISTEN      2290/master
[root@dmks ~]# service DmServicedmks restart
Stopping DmServicedmks: [ OK ]
Starting DmServicedmks: [ OK ]
[root@dmks ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 0.0.0.0:5901                0.0.0.0:*                   LISTEN      24268/Xvnc
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      1738/rpcbind
tcp        0      0 0.0.0.0:39792               0.0.0.0:*                   LISTEN      1964/rpc.statd
tcp        0      0 0.0.0.0:6001                0.0.0.0:*                   LISTEN      24268/Xvnc
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      2191/sshd
tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      2027/cupsd
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      2290/master
tcp        0      0 :::38955                    :::*                        LISTEN      1964/rpc.statd
tcp        0      0 :::111                      :::*                        LISTEN      1738/rpcbind
tcp        0      0 :::6001                     :::*                        LISTEN      24268/Xvnc
tcp        0      0 :::5236                     :::*                        LISTEN      840/dmserver
tcp        0      0 :::22                       :::*                        LISTEN      2191/sshd
tcp        0      0 ::1:631                     :::*                        LISTEN      2027/cupsd
tcp        0      0 :::5337                     :::*                        LISTEN      840/dmserver
tcp        0      0 ::1:25                      :::*                        LISTEN      2290/master

从上面的输出可以看到端口5336实战启用了

SQL> select * from v$dm_ini where para_name='MAL_INI';

LINEID     PARA_NAME PARA_VALUE MIN_VALUE MAX_VALUE MPP_CHK SESS_VALUE FILE_VALUE DESCRIPTION PARA_TYPE
---------- --------- ---------- --------- --------- ------- ---------- ---------- ----------- ---------
1          MAL_INI   1          0         1         N       1          1          dmmal.ini   IN FILE

used time: 12.648(ms). Execute id is 3.
SQL>  select sf_get_para_value(2,'MAL_INI');

LINEID     SF_GET_PARA_VALUE(2,'MAL_INI')
---------- ------------------------------
1          1

used time: 1.191(ms). Execute id is 4.

对本地DM数据库配置dmmal.ini,注意这里还要添加远程数据库的信息,否则在创建外部link时会提示实例不存在

[dmdba@shard1 jydm]$ cat  dmmal.ini
MAL_CHECK_INTERVAL = 5
MAL_CONN_FAIL_INTERVAL = 5

[MAL_dmks]
MAL_INST_NAME = dmks
MAL_HOST = 10.10.10.187
MAL_PORT = 5337
MAL_INST_PORT = 5236
MAL_INST_HOST = 10.10.10.187

[MAL_jydm]
MAL_INST_NAME = jydm
MAL_HOST = 10.10.10.180
MAL_PORT = 5336
MAL_INST_PORT = 5236
MAL_INST_HOST = 10.10.10.180

SQL> select * from v$dm_ini where para_name='MAL_INI';

LINEID     PARA_NAME PARA_VALUE MIN_VALUE MAX_VALUE MPP_CHK SESS_VALUE FILE_VALUE DESCRIPTION PARA_TYPE
---------- --------- ---------- --------- --------- ------- ---------- ---------- ----------- ---------
1          MAL_INI   1          0         1         N       1          1          dmmal.ini   IN FILE

used time: 8.629(ms). Execute id is 6.
SQL> select sf_get_para_value(2,'MAL_INI');

LINEID     SF_GET_PARA_VALUE(2,'MAL_INI')
---------- ------------------------------
1          1

used time: 1.302(ms). Execute id is 8.
SQL>

重启本地数据库

[root@shard1 tmp]# systemctl start DmServicejydm.service

SQL> create public link link1 connect 'dameng' with sysdba identified by "dameng123" using '10.10.10.180/5336';
executed successfully
used time: 19.417(ms). Execute id is 9.

或者

SQL> create or replace public link link2 connect 'dameng' with sysdba identified by "dameng123" using 'jydm';
executed successfully
used time: 35.346(ms). Execute id is 12.

在远程数据库中创建jy.t1表

SQL> insert into jy.t1 values(1,'JY');
affect rows 1

used time: 1.019(ms). Execute id is 2809.
SQL> commit;
executed successfully
used time: 13.274(ms). Execute id is 2810.

在本地数据库中通过外部link来查询远程数据库的sysdba.t1表

SQL> select * from jy.t1@link1;

LINEID     C1          C2
---------- ----------- --
1          1           JY

used time: 5.955(ms). Execute id is 134.
SQL> select * from jy.t1@link2;

LINEID     C1          C2
---------- ----------- --
1          1           JY

used time: 2.538(ms). Execute id is 135.

在本地数据库中通过外部link向远程数据库的jy.t1表插入数据

SQL> insert into jy.t1@link1 values(2,'HY');
affect rows 1

used time: 2.611(ms). Execute id is 136.
SQL> commit;
executed successfully
used time: 13.105(ms). Execute id is 137.

在远程数据库中使用sysdba来查询t1表的记录来验证记录是否被插入

SQL>  select * from jy.t1;

LINEID     C1          C2
---------- ----------- --
1          1           JY
2          2           HY

used time: 0.906(ms). Execute id is 2819.

MySQL Rewriter Query Rewrite Plugin

从MySQL 5.7.6开始,MySQL服务器支持查询重写插件它可以在服务器执行语句之前可以检查和可能修改接收到的语句。MySQL包含一个名为Rewriter的查询重写插件和安装插件与它相关组件的脚本。这些组件一起工作提供对select的重写能力:
.服务端插件名为Rewriter检查select语句并且可能基于缓存在内存中的重写规则来重写它们。标准select语句和预备语句中的select语句可能经受重写。出现在视图定义中或存储过程中的select语句不会经受重写。

.Rewriter插件使用一个包含rewrite_rules表的query_rewrite数据库。表提供了对规则的永久存储,插件使用它来决赛是否重写语句。通过存储在表中的规则集让用户与插件通信。通过设置表中记录的message列来让用户与插件通信。

.query_rewrite数据库包含一个名为flush_rewrite_rules()的存储过程用来把规则表中的内容加载到插件中。

.用户定义函数load_rewrite_rules()被flush_rewrite_rules()存储过程来调用。

.Rewriter插件显示了系统变量能让插件配置和状态变量来提供运行时操作信息。

下面将描述如何安装与使用Rewriter插件并提供与它相关组件的信息。

安装或卸载Rewriter查询重写插件
为了安装或卸载Rewriter查询重写插件,在MySQL安装目录下的share目录中选择执行合适的脚本:
.install_rewriter.sql: 使用这个脚本来安装Rewriter插件和它相关的组件。
.uninstall_rewriter.sql:使用这个脚本来卸载Rewriter插件和它相关的组件。

运行安装脚本

[mysql@localhost share]$ mysql -uroot -p < install_rewriter.sql
Enter password:

运行安装脚本将会安装与启用插件。为了验证它,连接到数据库执行以下语句:

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| binlog |
| mysql |
| performance_schema |
| query_rewrite |
| sys |
+--------------------+
6 rows in set (0.00 sec)

mysql> show global variables like 'rewriter_enabled';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| rewriter_enabled | ON |
+------------------+-------+
1 row in set (0.00 sec)

使用Rewriter查询重写插件
为了启用或禁用Rewriter查询重写插件可以通过启用或禁用rewriter_enabled系统变量来完成。默认情况是当你安装Rewriter查询重写插件时是启用的。为了显式设置初始化Rewriter查询重写插件的状态,可以在服务器启动时设置rewriter_enabled变量。例如为了在选项文件中启用Rewriter插件可以进行以下设置:
[mysqld]
rewriter_enabled=ON

也可以在运行时启用或禁用Rewriter插件:

mysql> set global rewriter_enabled=OFF;
Query OK, 0 rows affected (0.00 sec)

mysql> show global variables like 'rewriter_enabled';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| rewriter_enabled | OFF |
+------------------+-------+
1 row in set (0.00 sec)

mysql> set global rewriter_enabled=ON;
Query OK, 0 rows affected (0.00 sec)

mysql> show global variables like 'rewriter_enabled';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| rewriter_enabled | ON |
+------------------+-------+
1 row in set (0.01 sec)

mysql> desc query_rewrite.rewrite_rules;
+--------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| pattern | varchar(10000) | NO | | NULL | |
| pattern_database | varchar(20) | YES | | NULL | |
| replacement | varchar(10000) | NO | | NULL | |
| enabled | enum('YES','NO') | NO | | YES | |
| message | varchar(1000) | YES | | NULL | |
| pattern_digest | varchar(32) | YES | | NULL | |
| normalized_pattern | varchar(100) | YES | | NULL | |
+--------------------+------------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)

mysql> select * from query_rewrite.rewrite_rules;
Empty set (0.00 sec)

假设Rewriter插件被启用,它将检查和可能修改由服务器所接收到的每个select语句。插件将基于内存中缓存的重写规则(从query_rewriter数据库的rewrite_rules表中加载的)来决定是否重写语句。

添加重写规则
为了向Rewriter插件添加规则,向rewrite_rules表中添加记录,然后调用flush_rewrite_rules()存储过程来从表中加载规则到插件中。下面的例子来创建一个简单规则来匹配单个文本值的查询语句。

mysql> insert into query_rewrite.rewrite_rules(pattern,replacement) values('select ?','select ?+1');
Query OK, 1 row affected (0.03 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from query_rewrite.rewrite_rules;
+----+----------+------------------+-------------+---------+---------+----------------+--------------------+
| id | pattern | pattern_database | replacement | enabled | message | pattern_digest | normalized_pattern |
+----+----------+------------------+-------------+---------+---------+----------------+--------------------+
| 1 | select ? | NULL | select ?+1 | YES | NULL | NULL | NULL |
+----+----------+------------------+-------------+---------+---------+----------------+--------------------+
1 row in set (0.00 sec)

mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: NULL
normalized_pattern: NULL
1 row in set (0.00 sec)

规则指定一种模式模板指示那种查询语句会被匹配,并且替换模板指示了如何重写匹配的语句。然而添加规则到rewrite_rules表中不足以造成Rewriter插件使用这个规则。我们必须要调用flush_rewrite_rules()过程来将规则表的内容加载到插件内存缓存中:

mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected, 1 warning (0.04 sec)

当插件从规则表中读取每种规则时,它将计算一个标准化(语句摘要)格式的模式和一个摘要哈希值并使用它们来更新normalized_pattern和pattern_digest列:

mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
1 row in set (0.00 sec)

模式使用与预备语句相同的语法。使用模式模板,?字符实际上作为参数标记用来匹配数据值。参数标记只能用于应该出现数据值的地方,而不能用于SQL关键字、标识符等.?字符不应该使用引号括起来。

像模式一样,替换可以包含?字符。对于匹配一种模式模板的语句,重写插件将重写它,通过模式中的相关标记所匹配的数据值来替换?字符标记。替换的结果是一种完整的语句字符。重写插件要求服务器解析它并执行重写之后的语句将结果返回。

在添加和加载重写规则后,检查匹配规则模式的语句是否会被重写:

mysql> select pi();
+----------+
| pi() |
+----------+
| 3.141593 |
+----------+
1 row in set (0.02 sec)

mysql> select 10;
+------+
| 10+1 |
+------+
| 11 |
+------+
1 row in set, 1 warning (0.00 sec)

从上面的执行结果来看,第一个查询语句没有出现重写,但第二个查询被重写了。因为第二个查询语句Rewriter插件重写语句后生成了一个警告信息。为了查看这个警告信息可以使用show warnings:

mysql> show warnings\g
+-------+------+------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+------------------------------------------------------------------------+
| Note | 1105 | Query 'select 10' rewritten to 'select 10+1' by a query rewrite plugin |
+-------+------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

为了启用或禁用现有的规则,可以通过修改enabled列并重新加载规则表到重写插件。

为了禁用规则1

mysql> update query_rewrite.rewrite_rules set enabled='NO' where id=1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: NO
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
1 row in set (0.00 sec)

mysql> select 10;
+----+
| 10 |
+----+
| 10 |
+----+
1 row in set (0.00 sec)

这就可以不同从表中删除重写规则来禁用重写规则。

为了重新启用重写规则1:

mysql> update query_rewrite.rewrite_rules set enabled='YES' where id=1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected (0.02 sec)

mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
1 row in set (0.00 sec)

mysql> select 10;
+------+
| 10+1 |
+------+
| 11 |
+------+
1 row in set, 1 warning (0.00 sec)

rewrite_rules表包含了一个pattern_database列它是Rewriter用来匹配没有使用数据库名限定的表名:
.如果相关的数据库和表名相同,语句中限定表名匹配模式中的限定名。
.只有默认数据库与pattern_database一样并且表名相同时语句中的非限定表名匹配模式中的非限定名

假设表mysql.cs有一个名为id的列并且应用程序从以下形式的查询中选择一个来从表中查询记录,这里第二个查询只能在默认数据库为mysql的情况下被执行:
select * from mysql.cs where id=id_value;
select * from cs where id=id_value;

现在假设id列被重命名为user_id了。这种修改意味着应用程序必须引用user_id而不是id。但是如果旧的应用程序不能进行修改,那么它们将不能工作了。Rewriter重写插件可以解决这个问题。为了匹配和重写那些不管是否有限定名的查询语句,添加以下两个规则并重新加载规则表:

mysql> select * from cs where mysql.id=1;
ERROR 1054 (42S22): Unknown column 'mysql.id' in 'where clause'
mysql> insert into query_rewrite.rewrite_rules(pattern,replacement)
-> values('select * from mysql.cs where id= ?','select * from mysql.cs where user_id= ?');
Query OK, 1 row affected (0.05 sec)

mysql> insert into query_rewrite.rewrite_rules(pattern,replacement,pattern_database)
-> values('select * from cs where id=?','select * from cs where user_id=?','mysql');
Query OK, 1 row affected (0.05 sec)

mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
*************************** 2. row ***************************
id: 2
pattern: select * from mysql.cs where id= ?
pattern_database: NULL
replacement: select * from mysql.cs where user_id= ?
enabled: YES
message: NULL
pattern_digest: 45281da14b71c1357dd053a4fe49dfac
normalized_pattern: select `*` from `mysql`.`cs` where (`id` = ?)
*************************** 3. row ***************************
id: 3
pattern: select * from cs where id=?
pattern_database: mysql
replacement: select * from cs where user_id=?
enabled: YES
message: NULL
pattern_digest: 0da2491bc4c0e1462cc020e4fcfde16b
normalized_pattern: select `*` from `mysql`.`cs` where (`id` = ?)
3 rows in set (0.00 sec)

mysql> select * from mysql.cs where id=1;
+------+------+---------+
| id | name | user_id |
+------+------+---------+
| 1 | jy | 1 |
+------+------+---------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+-------+------+----------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------------------------------------------------------------------------+
| Note | 1105 | Query 'select * from mysql.cs where id=1' rewritten to 'select * from mysql.cs where user_id= 1' by a query rewrite plugin |
+-------+------+----------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from cs where id=1;
+------+------+---------+
| id | name | user_id |
+------+------+---------+
| 1 | jy | 1 |
+------+------+---------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+-------+------+---------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------------------------------------------------+
| Note | 1105 | Query 'select * from cs where id=1' rewritten to 'select * from cs where user_id=1' by a query rewrite plugin |
+-------+------+---------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Rewriter插件使用第一个规则匹配有限定名的查询,使用第二个规则匹配没有限定名,但默认数据库必须为mysql才能进行查询重写。

如何进行模式匹配
Rewriter插件使用语句摘要和摘要哈希值来使用重写规则匹配输入语句。max_digest_length系统变量决定了用于计算语句摘要的buffer大小。值越大计算的摘要越能区分更长的语句。值越小使用的内存越小但增加了更长语句使用相同摘要的可能性。


mysql> show global variables like 'max_digest_length';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| max_digest_length | 1024 |
+-------------------+-------+
1 row in set (0.00 sec)

插件匹配语句进行重写的规则如下:
1.计算语句摘要的哈希值并将它与规则摘要哈希值进行比较。这可能会出现误报,但可以作为快速的拒绝测试

2.如果语句摘要哈希值匹配任何一个模式摘要哈希值,则匹配规范化哈希值(语句摘要)将语句的形式转化为匹配规则模式所规范化的形式。

3.如果规范化语句与规则匹配,请比较语句和模式中的文字值。模式中的一个?号值与语句中的任何文字值匹配。如果语句准备了一个SELECT语句,模式中的?也匹配语句中的?。否则,对应的文字必须相同。

如果多个规则匹配一个语句,则不确定插件使用哪个规则来重写该语句。

如果一个模式包含比替换更多的标记,那么插件将丢弃多余的数据值。如果一个模式包含的标记比替换的少,这就是一个错误。当加载规则表时,插件会注意到这一点,它会向规则行的message列写入一条错误消息来传递问题,并将Rewriter_reload_error状态变量设置为ON。

重写预备语句
预备语句是在解析时被重写,而不是在执行时被重写。预备语句与非预备语句的区别在于它们可能包含?字符作为参数标记。为了匹配预备语句中的?字符,重写模式必须在同一个地方包含?字符。假设重写规则具有这种模式
select ?, 3
下面列出了几种预备语句和是否与它匹配的模式
预备语句                                模式是否匹配语句
prepare s as ‘select 3, 3’     Yes
prepare s as ‘select ?, 3’     Yes
prepare s as ‘select 3, ?’     No
prepare s as ‘select ?, ?’     No

Rewriter插件操作信息
Rewriter插件通过几种状态变量来表示它的操作信息:

mysql> show global status like 'Rewriter%';
+-----------------------------------+-------+
| Variable_name | Value |
+-----------------------------------+-------+
| Rewriter_number_loaded_rules | 3 |
| Rewriter_number_reloads | 5 |
| Rewriter_number_rewritten_queries | 5 |
| Rewriter_reload_error | OFF |
+-----------------------------------+-------+
4 rows in set (0.01 sec)