在上一章中,我们学习了如何优化 MySQL 8。我们还了解了实现优化需要进行哪些配置,以及如何利用缓存和缓冲进行优化。为了在以下组件中实现优化,我们一步一步地进行了用例研究:
在本章中,我们将学习如何扩展 MySQL 8。我们将检查允许扩展哪些 MySQL 8 组件,并了解如何根据特定的业务需求定制 MySQL 8。您将了解扩展 MySQL 8 之前的基本组件,以及用于扩展 MySQL 8 的 MySQL 插件 API 的特性。以下是本章涵盖的主题列表:
在本节中,您将了解如何根据需要扩展 MySQL 8 的最激动人心的主题之一。在尝试扩展 MySQL 8 之前,您应该了解 MySQL 8 的几个组件。下面列出了对扩展 MySQL 8 非常重要的组件:
在开始使用 MySQL 代码之前,您应该知道一些事情。为了帮助或跟踪 MySQL 开发,您应该按照系统或操作系统平台的说明安装源代码分发版。源代码包括内部文档,这对于从开发人员的角度理解 MySQL 内部如何工作非常重要。您也可以从订阅内部邮件列表 https://lists.mysql.com/internals ,其中包括从事 MySQL 代码工作的人员,您还可以讨论与 MySQL 开发相关的主题或发布补丁:
InnoDB
存储引擎创建连接管理器线程、信号线程、读写线程、调度线程处理连接、复制和事件处理等线程。MySQL 8 支持插件 API,通过插件 API 可以创建服务器组件本身。插件可以在服务器启动时加载,也可以在运行时加载和卸载;无需重新启动服务器。API 非常通用,因为它没有指定插件在限制方面可以做什么,而是允许它们做的不仅仅是内置组件。API 支持存储引擎插件、全文解析器插件、服务器扩展等组件的接口。
插件接口利用 MySQL 8 数据库中的plugin
表,通过INSTALL PLUGIN
语句永久存储已安装插件的信息。在 MySQL 8 安装过程中会创建plugin
表。对于单服务器调用,也可以使用--plugin--load
选项安装插件,但使用此选项不会将安装的插件记录到plugin
表中。
MySQL 8 还为用于特定目的的客户端插件提供支持 API,例如通过不同的身份验证方法启用客户端的服务器连接。
MySQL 8 服务器插件可以访问和启动服务器插件服务;类似地,服务器组件也可以访问和请求组件服务。MySQL 8 插件服务接口通过公开服务器功能来补充 API 插件,这些功能可以由插件调用。以下是插件服务的特点:
MySQL 为插件和组件提供两种服务类型,如下所示:
您可以将自己的函数添加到 MySQL 8 中,这可以通过支持的三种函数类型中的任意一种来完成。调用新函数的方式与调用内置函数(如ABS()
)的方式相同,无论您新添加了哪种函数类型,都是如此。以下是 MySQL 8 中支持的三种新函数:
将 MySQL 8 移植到其他操作系统目前受到许多操作系统的支持;支持的操作系统列表见http://www.mysql.com/support/supportedplatforms/database.html 。如果您添加了一个新端口,并且在使用新端口时遇到问题,您可以使用 MySQL 8 的调试。
根据您在 MySQL 服务器或 MySQL 客户端中遇到的问题,有不同的启动调试的方法。根据问题的位置,您可以分别在 MySQL 服务器或客户端启动调试,也可以从DBUG
包获得帮助来跟踪程序的活动。
在本节中,您将了解插件 API、其接口和 MySQL 服务如何相互作用并在 MySQL 8 中提供扩展。这些插件也被认为是 MySQL 8 体系结构中的组件,因此您可以使用它们来提供可插入的特性。插件 API 和插件服务接口有以下区别:
要创建插件库,必须提供所需的描述符信息,因为它指定库文件包含哪些插件。为每个指定的插件编写接口函数也是必要的。
每个服务器插件都必须有一个向插件 API 提供信息的通用描述符,以及一个为指定插件类型提供接口信息的类型特定描述符。用于指定通用描述符的结构对于所有插件类型都是相同的,并且特定于类型的描述符可以根据插件行为或功能的要求而变化。服务器插件接口允许插件公开系统变量和状态。
客户端插件的体系结构与服务器端插件略有不同。例如,每个插件都必须有描述符信息,但在通用描述符和特定类型描述符之间没有单独的划分
插件可以用 C 或 C++或任何其他可以使用 C 调用约定的语言编写。插件是动态加载和卸载的,因此操作系统必须动态支持动态编译调用应用程序的位置。具体来说,对于服务器插件,这意味着mysqld
必须动态链接。
As we cannot be sure of what application will use the plugin, the dependencies on the symbols of the calling application should be avoided by the client plugin writers.
以下是可实现多种功能的受支持插件创建类型:
INFORMATION_SCHEMA
表格您可以通过查看 MySQL 8 源发行版的include/mysql/components
和相应的services
目录来识别 MySQL 提供的组件服务和功能。
同样,您可以通过查看 MySQL 8 源代码发行版的include/mysql
目录和相关文件来识别 MySQL 提供的插件服务和功能,如下所示:
plugin.h
文件包括services.h
文件,该services.h
文件包含其中所有可用的特定于服务的头文件service_xxx.h
以下是 MySQL 8 中可用组件服务的列表:
component_sys_variable_register
、component_sys_variable_unregister
:用于注册和注销系统变量log_builtins
、log_builtins_string
:用于日志组件服务mysql_service_udf_registration
、mysql_service_udf_registration_aggregate
:用于对组件和插件中的标量和聚合自定义函数进行注册和注销mysql_string
:用于字符串服务 APIpfs_plugin_table
:用于动态性能模式表操作以下是 MySQL 8 中可用的插件服务列表:
get_sysvar_source
:用于检索系统变量设置locking_service
:用于 C 语言和 SQL 级接口的锁实现,具有名称空间、名称和模式属性my_plugin_log_service
:用于将错误消息写入日志my_snprintf
:用于字符串格式设置,以保持跨平台输出的一致性status_variable_registration
:用于注册状态变量my_thd_scheduler
:用于线程调度程序选择mysql_keyring
:用于钥匙圈存储服务mysql_password_policy
:用于密码强度和验证检查plugin_registry_service
:用于访问组件注册表及相关服务security_context
:用于管理线程安全上下文thd_alloc
:用于内存分配thd_wait
:用于报告睡眠或暂停现在,您已经清楚地了解了插件服务和组件服务。MySQL 8 提供以下类型的服务来支持插件和组件服务:
以下各节提供了这两种服务的详细信息。
锁定服务接口分为两个级别:C 级别和 SQL 级别。该接口处理锁名称空间、锁名称和锁模式属性。C 语言接口可以作为插件服务从用户定义的函数或服务器插件中调用,SQL 级接口用作用户定义的函数集,映射为调用服务例程。
以下是锁定接口的特征:
keyring 服务提供了一个接口,用于安全地存储敏感信息,供内部服务器组件和插件稍后检索。在 keyring 服务中,来自密钥库本身的记录由密钥数据和可访问密钥的唯一标识符组成。标识符由以下两部分组成:
key_id
:名称。key_id
或以mysql_
开头的密钥 ID 值由 MySQL 服务器保留。user_id
:user_id
表示每次会话有效user_id
。如果没有用户上下文,则可以是NULL
,并且该值不一定需要是实际的user
,而是取决于应用程序。以下是钥匙环服务功能的常见特征:
user_id
和key_id
参数构成了一个唯一的组合,指示要在钥匙圈中使用的钥匙key_type
参数值一起提供,作为其预期用途、其加密方法或其他此类信息以下是可用的钥匙扣服务功能列表:
my_key_generate()
:顾名思义,它生成一个给定类型和长度的新随机密钥,并存储在密钥环中。函数由参数key_id
、user_id
、key_type
和key_len
组成,函数语法如下: bool my_key_generate(const char *key_id, const char*key_type,
const char *user_id, size_t key_len)
my_key_fetch()
:deobfoused 参数值并从 keyring 及其类型中检索密钥。该函数由参数key_id
、user_id
、key_type
、key
和key_len
组成,以及以下函数语法: bool my_key_fetch(const char *key_id, const char **key_type,
const char* user_id, void **key, size_t *key_len)
my_key_remove()
:从钥匙圈中移除相关钥匙。该函数由参数key_id
和user_id
以及以下函数语法组成: bool my_key_remove(const char *key_id, const char* user_id)
my_key_store()
:混淆参数值,并将一个键存储在 keyring 中。该函数由参数key_id
、user_id
、key_type
、key
和key_len
组成,以及以下函数语法: bool my_key_store(const char *key_id, const char *key_type,
const char* user_id, void *key, size_t key_len)
MySQL 8 中支持的三种类型中的任何一种都可以添加新函数。每种类型都有各自的优缺点。在何处以及应添加或实现哪种类型的功能取决于功能的要求。
以下是 MySQL 8 中支持的三种新函数的列表,我们将在下一节中介绍:
用户定义的功能接口为用户用途的功能提供独立的功能。
MySQL 接口为用户定义的函数提供了以下特性和功能:
NULL
则可以进行指示UDF 函数必须用 C 或 C++编写,底层操作系统必须支持动态加载行为。有一个文件sql/udf_example.cc
,它定义了五个 UDF 函数,它包含在 MySQL 源代码发行版中。分析该文件将让您了解 UDF 的调用约定是如何工作的。include/mysql_com.h
文件中定义了用户定义的功能相关符号和数据结构,该文件包含在mysql.h
头文件中。
UDF 中包含的典型代码在运行的服务器中执行,因此在编写 UDF 代码服务器代码时,所有约束都适用。当服务器升级时,当前适用的约束可能会被修改,这可能导致需要重写 UDF 代码,因此在为 UDF 编写代码时必须小心。
为了使用 UDF,必须动态链接mysqld
。对于 SQL 语句中使用的任何函数,必须有底层 C 或 C++函数。遵循分离 SQL 和 C/C++代码的约定,其中大写的xxx()
表示 SQL 函数调用,而小写的xxx()
表示 C/C++函数调用
Encapsulate your C function as shown in following sentence when you are using C++: extern "C"
{ ... }
This way it is ensured that your C++ function names are readable in the completed user-defined function.
要编写和实现接口功能名称XXX()
,必须使用主功能xxx()
,另外还需要从以下位置实现一个或多个功能:
xxx()
:生成函数结果的主函数xxx_init()
:主功能xxx()
的初始化功能,可用于以下任一目的:XXX()
的参数数NULL
xxx_deinit()
:表示主函数的去初始化,如果初始化函数为主函数分配了内存,则释放内存在 MySQL 8 中,聚合 UDF 的处理顺序如下:
xxx_init()
以便分配存储结果信息所需的内存。GROUP BY
功能的规定对表格/结果进行排序。xxx_clear()
以便重置每个新组中第一行的当前聚合值。xxx_add()
,将参数添加到当前聚合值。xxx()
通过更改或在处理最后一行后获取分组数据聚合结果。xxx_deinit()
为 UDF 释放任何分配的内存。所有函数都必须是线程安全的,包括主函数以及所需的其他附加函数,以及初始化和反初始化函数
与上述顺序类似,在添加新的用户定义函数时,需要注意以下重要方面:
要添加新的本机函数,需要源代码分发,以便使用包含新本机函数的修改源代码进行编译。当您迁移到另一个 MySQL 版本时,还需要重复此操作。
如果要在语句中引用新的本机函数并将其复制到从属服务器,请确保每个从属服务器都有新的本机函数可用,否则,在尝试调用新的本机函数时,从属服务器上的复制将失败。
以下是在sql
目录的源分发文件中添加新本机函数的步骤:
item_create.cc
中添加该函数的子类:Create_func_arg0
、Create_func_arg1
、Create_func_arg2
或Create_func_arg3
创建子类。您可以参考Create_func_abs
、Create_func_uuid
和Create_func_pow
类。Create_native_func
创建子类。您可以参考Creat_func_concat
类。item_create.cc
中注册,方法是在数组中添加以下行:static Native_func_registry func_array[]
:LOWER
和LCASE
的行,它们是代表Create_func_lcase
的别名。Item_str_func
或Item_num_func
继承的类是必要的,这取决于您的函数返回类型是item_func.h
文件中的字符串还是数字。item_func.cc
文件中定义为字符串还是数值函数,需要添加以下声明之一: double Item_func_newname::val()
longlong Item_func_newname::val_int()
String *Item_func_newname::Str(String *str)
current_thd->lex->safe_to_cache_query=0;
。void Item_func_newname::fix_length_and_dec()
max_length
计算NULL
值,也应设置maybenull = 0
Item_func_mod::fix_length_and_dec
来了解相同的内容线程安全是所有功能的必备条件。在没有互斥锁保护的情况下,不应在函数中使用任何静态或全局变量。
目前许多操作系统都支持将 MySQL 8 移植到其他操作系统。最新支持的操作系统列表见http://www.mysql.com/support/supportedplatforms/database.html 。如果您已经添加或试图添加新端口(受支持的平台)并且遇到问题,您可以使用 MySQL 8 的调试来查找和修复问题。
First, you should get the test program mysys/thr_lock
to work before debugging mysqld
. This makes sure that your thread installation can have a remote chance to work!
根据您遇到问题的位置,启动调试有不同的可能性——可能在 MySQL 服务器中,也可能在 MySQL 客户端中。根据问题的位置,您可以分别在 MySQL 服务器或 MySQL 客户端中开始调试,并且为了跟踪程序的活动,您将从DEBUG
包获得帮助。
The MySQL source code includes internal documentation written using Doxygen
, which is very helpful in understanding the developer perspective on how MySQL works.
在本节中,您将看到有关以下主题的详细信息:
DBUG
包如果您正在使用 MySQL 中的一些非常新的功能,并且面临一些问题,比如说服务器正在崩溃,那么您可以尝试使用--skip-new
选项运行mysqld
。此选项告诉 MySQL 服务器禁用所有新的和可能不安全的功能
如果mysqld
未启动,请验证my.cnf
文件,因为它们可能会干扰设置!您可以使用mysqld --print-defaults
选项检查my.cnf
中的参数,然后使用--no-defaults
选项启动mysqld
以避免使用它们。
如果mysqld
开始消耗内存或 CPU 或挂起,您可以检查mysqladmin processlist status
并确定某人执行的查询是否花费了很长时间。当您面临性能问题或新客户端无法连接时,您可以使用mysqladmin -i10
进程列表状态。
您还可以使用 debug 命令mysqladmin
,该命令将查询使用情况、内存使用情况和正在使用的锁的信息转储到 MySQL 日志文件中,可以为您解决一些问题。如果您还没有编译 MySQL 进行调试,这个命令也可以使用,提供了一些有用的信息
如果您遇到表速度变慢的问题,您应该尝试使用myisamchk
或OPTIMIZE_TABLE
优化表。您可能应该检查慢速查询(如果有),使用EXPLAIN
查找并修复查询问题。
以下是在 MySQL 8 中调试时要考虑的重要领域:
-DWITH_DEBUG=1
选项配置 MySQL。调试配置自动启用许多额外的安全检查功能,以监控mysqld
的运行状况。mysqld
。然后您可以使用--debug
选项,该选项将在 Unix 上的/tmp/mysqld.trace
和 Windows 上的\mysqld.trace
中添加跟踪日志。mysqld
。mysqld
服务器在ready for connections
之前挂起时,可以使用此选项。mysqld
意外死亡时使用此选项,并找出问题所在。mysqld
中的错误原因:您可以通过启用常规查询日志来使用此选项-在此之前,您应该使用myisamchk
工具检查所有表,并验证日志中是否存在任何问题。MyISAM
表。在 MySQL 客户机中遇到问题的情况下,您也可以在 MySQL 客户机中进行调试,但要做到这一点,您必须拥有集成的调试包。您需要使用-DWITH_DEBUG=1
配置 MySQL,以便在 MySQL 客户端中进行调试。
在运行 MySQL 客户端之前,需要对环境变量MYSQL_DEBUG
进行如下设置:
shell> MYSQL_DEBUG=d:t:O,/tmp/client.trace
shell> export MYSQL_DEBUG
这使得 MySQL 客户端在 Unix 的/tmp/client.trace
或 Windows 的\client.trace
中生成跟踪文件。
如果您自己的客户机代码有问题,您可以尝试通过使用已知可用的客户机运行查询来连接到服务器。为此,您应在调试模式下运行mysqld
:
shell> mysql --debug=d:t:O,/tmp/client.trace
如果您想邮寄问题的 bug 报告,此跟踪将提供有用的信息。
如果您的客户机在某些看起来像legal
的代码中崩溃,您可以检查您的mysql.h
头文件是否包含与 MySQL 库文件匹配的文件。这是一个非常常见的错误,使用旧 MySQL 安装中的旧mysql.h
文件和新的 MySQL 库,导致了这个问题。
Fred Fish 最初使用 MySQL 服务器和大多数 MySQL 客户端创建了DBUG
包。如果将 MySQL 配置为调试,那么这个包可以生成一个跟踪文件,其中包含有关程序正在执行的操作的信息。
可以指定调试选项,以便使用DBUG
包获取跟踪文件的特定信息。它可以在程序调用中使用-# [debug_options]
选项或--debug[=debug_options]
选项。
如果指定了--debug
或-#
选项而未指定debug_options
值,则大多数 MySQL 程序将使用默认值。Windows 上的服务器默认值为d:t:i:O,\mysqld.trace
,Unix 上的服务器默认值为d:t:i:o,/tmp/mysqld.trace
。此默认值的影响如下所示:
d
:为所有调试宏启用输出t
:跟踪函数调用和退出i
:在跟踪文件的输出行中增加PID
o,/tmp/mysqld.trace
、 O,\mysqld.trace
:分别在 Unix 和 Windows 中设置调试输出文件在大多数情况下,无论平台工作情况如何,对大多数客户端程序都使用默认的debug_options
值d:t:o,/tmp/myprogram_name.trace
。对于 Windows,请使用\myprogram_name.trace
。
以下是要在 shell 命令行上指定的调试控制字符串的一些示例:
--debug=d:t
--debug=d:f,main,subr1:F:L:t,20
--debug=d,input,output,files:n
--debug=d:t:i:O,\\mysqld.trace
在本章中,您学习了如何通过自定义函数和 API 扩展 MySQL 8。您还了解了编写函数以及插件服务和 API 的相关特性。现在,您可以创建自己的函数或插件,以满足特定的业务需求,还可以在函数不能按预期工作时进行调试,并测试它是否能正常工作。
在下一章中,您将了解 MySQL 8 的最佳实践和 MySQL 8 中的基准测试。您将了解基准测试和用于基准测试的工具。您还将学习 MySQL 8 的一些非常重要的功能的最佳实践,如 memcached、复制、数据分区和索引。