MySQL8-中文参考-四十四-

MySQL8 中文参考(四十四)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-nodes.html

25.6.16.47 ndbinfo nodes 表

该表包含数据节点状态的信息。对于在集群中运行的每个数据节点,该表中的相应行提供节点的节点 ID、状态和运行时间。对于正在启动的节点,还显示当前的启动阶段。

nodes表包含以下列:

  • node_id

    集群中数据节点的唯一节点 ID。

  • uptime

    节点自上次启动以来的时间,以秒为单位。

  • status

    数据节点的当前状态;请参阅文本以获取可能的值。

  • start_phase

    如果数据节点正在启动,当前的启动阶段。

  • config_generation

    在此数据节点上使用的集群配置文件的版本。

注意

uptime列显示自上次启动或重新启动以来该节点运行的时间(以秒为单位)。这是一个BIGINT - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT")值。这个数字包括实际启动节点所需的时间;换句话说,这个计数器从第一次调用ndbdndbmtd")开始运行;因此,即使对于尚未完成启动的节点,uptime可能显示一个非零值。

status列显示节点的当前状态。这是其中之一:NOTHINGCMVMISTARTINGSTARTEDSINGLEUSERSTOPPING_1STOPPING_2STOPPING_3STOPPING_4。当状态为STARTING时,您可以在start_phase列中看到当前的启动阶段(请参见本节后面的内容)。当集群处于单用户模式时(请参阅第 25.6.6 节,“NDB 集群单用户模式”),所有数据节点的status列中都显示SINGLEUSER。看到STOPPING状态之一并不一定意味着节点正在关闭,而可能意味着它正在进入新状态。例如,如果将集群置于单用户模式,有时可以看到数据节点在状态短暂报告为STOPPING_2,然后状态更改为SINGLEUSER

start_phase列使用与ndb_mgm客户端*node_id* STATUS命令输出中使用的相同值范围(请参见第 25.6.1 节,“NDB 集群管理客户端中的命令”)。如果节点当前未启动,则此列显示0。有关带有描述的 NDB 集群启动阶段的列表,请参见第 25.6.4 节,“NDB 集群启动阶段摘要”。

config_generation列显示每个数据节点上生效的集群配置版本。在执行集群滚动重启以更改配置参数时,这可能很有用。例如,从以下SELECT语句的输出中,您可以看到节点 3 尚未使用最新版本的集群配置(6),尽管节点 1、2 和 4 正在使用:

mysql> USE ndbinfo;
Database changed
mysql> SELECT * FROM nodes;
+---------+--------+---------+-------------+-------------------+
| node_id | uptime | status  | start_phase | config_generation |
+---------+--------+---------+-------------+-------------------+
|       1 |  10462 | STARTED |           0 |                 6 |
|       2 |  10460 | STARTED |           0 |                 6 |
|       3 |  10457 | STARTED |           0 |                 5 |
|       4 |  10455 | STARTED |           0 |                 6 |
+---------+--------+---------+-------------+-------------------+
2 rows in set (0.04 sec)

因此,对于刚刚显示的情况,您应重新启动节点 3 以完成集群的滚动重启。

停止的节点不在此表中列出。假设您有一个具有 4 个数据节点(节点 ID 为 1、2、3 和 4)的 NDB 集群,并且所有节点正常运行,则此表包含 4 行,每个数据节点一行:

mysql> USE ndbinfo;
Database changed
mysql> SELECT * FROM nodes;
+---------+--------+---------+-------------+-------------------+
| node_id | uptime | status  | start_phase | config_generation |
+---------+--------+---------+-------------+-------------------+
|       1 |  11776 | STARTED |           0 |                 6 |
|       2 |  11774 | STARTED |           0 |                 6 |
|       3 |  11771 | STARTED |           0 |                 6 |
|       4 |  11769 | STARTED |           0 |                 6 |
+---------+--------+---------+-------------+-------------------+
4 rows in set (0.04 sec)

如果关闭其中一个节点,则此SELECT语句的输出中仅表示仍在运行的节点,如下所示:

ndb_mgm> 2 STOP
Node 2: Node shutdown initiated
Node 2: Node shutdown completed.
Node 2 has shutdown.
mysql> SELECT * FROM nodes;
+---------+--------+---------+-------------+-------------------+
| node_id | uptime | status  | start_phase | config_generation |
+---------+--------+---------+-------------+-------------------+
|       1 |  11807 | STARTED |           0 |                 6 |
|       3 |  11802 | STARTED |           0 |                 6 |
|       4 |  11800 | STARTED |           0 |                 6 |
+---------+--------+---------+-------------+-------------------+
3 rows in set (0.02 sec)

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-operations-per-fragment.html

25.6.16.48 ndbinfo operations_per_fragment 表

operations_per_fragment表提供有关对各个片段和片段副本执行的操作以及一些操作结果的信息。

operations_per_fragment表包含以下列:

  • fq_name

    此片段的名称

  • parent_fq_name

    此片段的父级名称

  • type

    对象类型;请参阅文本以获取可能的值

  • table_id

    此表的表 ID

  • node_id

    此节点的节点 ID

  • block_instance

    内核块实例 ID

  • fragment_num

    片段 ID(数字)

  • tot_key_reads

    此片段副本的总键读取次数

  • tot_key_inserts

    此片段副本的总键插入次数

  • tot_key_updates

    此片段副本的总键更新次数

  • tot_key_writes

    此片段副本的总键写入次数

  • tot_key_deletes

    此片段副本的总键删除次数

  • tot_key_refs

    拒绝的键操作次数

  • tot_key_attrinfo_bytes

    所有attrinfo属性的总大小

  • tot_key_keyinfo_bytes

    所有keyinfo属性的总大小

  • tot_key_prog_bytes

    所有attrinfo属性携带的解释程序的总大小

  • tot_key_inst_exec

    由键操作的解释程序执行的指令总数

  • tot_key_bytes_returned

    从键读取操作返回的所有数据和元数据的总大小

  • tot_frag_scans

    此片段副本执行的扫描总数

  • tot_scan_rows_examined

    扫描检查的总行数

  • tot_scan_rows_returned

    返回给客户端的总行数

  • tot_scan_bytes_returned

    返回给客户端的数据和元数据的总大小

  • tot_scan_prog_bytes

    扫描操作的解释程序的总大小

  • tot_scan_bound_bytes

    有序索引扫描中使用的所有边界的总大小

  • tot_scan_inst_exec

    扫描执行的指令总数

  • tot_qd_frag_scans

    此片段副本扫描排队的次数

  • conc_frag_scans

    当前在此片段副本上活动的扫描数(不包括排队扫描)

  • conc_qd_frag_scans

    当前为此片段副本排队的扫描数

  • 提交总数

    提交给此片段副本的行更改总数

注意

fq_name包含此片段副本所属的模式对象的完全限定名称。目前具有以下格式:

  • 基表:*DbName*/def/*TblName*

  • BLOB表:*DbName*/def/NDB$BLOB_*BaseTblId*_*ColNo*

  • 有序索引:sys/def/*BaseTblId*/*IndexName*

  • 唯一索引:sys/def/*BaseTblId*/*IndexName*$unique

唯一索引显示的$unique后缀是由mysqld添加的;对于由不同 NDB API 客户端应用程序创建的索引,可能会有所不同,或者不存在。

刚刚显示的完全限定对象名称的语法是一个内部接口,可能会在未来版本中更改。

考虑由以下 SQL 语句创建和修改的表t1

CREATE DATABASE mydb;

USE mydb;

CREATE TABLE t1 (
  a INT NOT NULL,
  b INT NOT NULL,
  t TEXT NOT NULL,
  PRIMARY KEY (b)
) ENGINE=ndbcluster;

CREATE UNIQUE INDEX ix1 ON t1(b) USING HASH;

如果t1被分配表 ID 11,则这里显示的fq_name值为:

  • 基本表:mydb/def/t1

  • BLOB表:mydb/def/NDB$BLOB_11_2

  • 有序索引(主键):sys/def/11/PRIMARY

  • 唯一索引:sys/def/11/ix1$unique

对于索引或BLOB表,parent_fq_name列包含相应基本表的fq_name。对于基本表,此列始终为NULL

type列显示了用于此片段的模式对象类型,可以取以下任一值:System tableUser tableUnique hash indexOrdered indexBLOB表显示为User table

table_id列值在任何给定时间都是唯一的,但如果相应对象已被删除,则可以重新使用。可以使用ndb_show_tables实用程序查看相同的 ID。

block_instance列显示此片段副本属于哪个 LDM 实例。您可以使用此信息从threadblocks表中获取有关特定线程的信息。第一个这样的实例始终编号为 0。

由于通常有两个片段副本,并假设是这样,每个fragment_num值应在表中出现两次,分别在来自同一节点组的两个不同数据节点上。

由于NDB不使用单键访问有序索引,因此有序索引操作不会增加tot_key_readstot_key_insertstot_key_updatestot_key_writestot_key_deletes的计数。

注意

在使用tot_key_writes时,应注意在此上下文中,写操作会更新行(如果键存在),否则会插入新行。(这在NDB实现的REPLACE SQL 语句中有用途。)

tot_key_refs列显示 LDM 拒绝的键操作数量。通常,这种拒绝是由于重复键(插入)、未找到键错误(更新、删除和读取)或操作被用作谓词的解释程序拒绝的。

tot_key_attrinfo_bytestot_key_keyinfo_bytes 列计算的 attrinfokeyinfo 属性是 LQHKEYREQ 信号的属性(参见 NDB 通信协议),用于由 LDM 发起的键操作。attrinfo 通常包含元组字段值(插入和更新)或投影规范(用于读取);keyinfo 包含在此模式对象中定位给定元组所需的主键或唯一键。

tot_frag_scans 显示的值包括全扫描(检查每一行)和子集扫描。唯一索引和 BLOB 表永远不会被扫描,因此对于这些片段副本,该值以及其他与扫描相关的计数都为 0。

tot_scan_rows_examined 可能显示的行数少于给定片段副本中的总行数,因为有序索引扫描可能受到边界限制。此外,客户端可能选择在检查所有潜在匹配行之前结束扫描;例如,在使用包含 LIMITEXISTS 子句的 SQL 语句时会发生这种情况。tot_scan_rows_returned 总是小于或等于 tot_scan_rows_examined

tot_scan_bytes_returned 包括在推送连接中返回给 NDB 内核中的 DBSPJ 块的投影。

tot_qd_frag_scans 可受到 MaxParallelScansPerFragment 数据节点配置参数的设置影响,该参数限制了在单个片段副本上可以同时执行的扫描数量。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-pgman-time-track-stats.html

25.6.16.49 ndbinfo pgman_time_track_stats 表

该表提供有关 NDB Cluster Disk Data 表空间磁盘操作延迟的信息。

pgman_time_track_stats 表包含以下列:

  • node_id

    集群中此节点的唯一节点 ID

  • block_number

    块编号(来自 blocks 表)

  • block_instance

    块实例编号

  • upper_bound

    上限

  • page_reads

    页面读取延迟(毫秒)

  • page_writes

    页面写入延迟(毫秒)

  • log_waits

    日志等待延迟(毫秒)

  • get_page

    get_page() 调用的延迟(毫秒)

注意

读取延迟(page_reads 列)衡量了从发送读取请求到文件系统线程直到读取完成并报告给执行线程的时间。写入延迟(page_writes)以类似的方式计算。从磁盘数据表空间读取或写入的页面大小始终为 32 KB。

日志等待延迟(log_waits 列)是页面写入必须等待撤销日志刷新的时间长度,这必须在每次页面写入之前完成。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-processes.html

25.6.16.50 ndbinfo 进程表

此表包含有关 NDB Cluster 节点进程的信息;每个节点在表中由一行表示。仅显示连接到集群的节点在此表中。您可以从nodesconfig_nodes表中获取有关已配置但未连接到集群的节点的信息。

processes表包含以下列:

  • node_id

    集群中节点的唯一节点 ID

  • node_type

    节点类型(管理、数据或 API 节点;参见文本)

  • node_version

    NDB软件程序在此节点上运行的版本。

  • process_id

    此节点的进程 ID

  • angel_process_id

    此节点的 angel 进程的进程 ID

  • process_name

    可执行文件的名称

  • service_URI

    此节点的服务 URI(参见文本)

备注

node_id是分配给此节点的 ID。

node_type列显示以下三个值之一:

  • MGM:管理节点。

  • NDB:数据节点。

  • API:API 或 SQL 节点。

对于 NDB Cluster 发行版附带的可执行文件,node_version显示软件 Cluster 版本字符串,例如8.0.35-ndb-8.0.35

process_id是节点可执行文件的进程 ID,可以通过主机操作系统使用像 Linux 上的top或 Windows 平台上的任务管理器这样的进程显示应用程序来显示。

angel_process_id是节点的 angel 进程的系统进程 ID,它确保在发生故障时数据节点或 SQL 自动重新启动。对于管理节点和 SQL 节点以外的其他类型的 API 节点,此列的值为NULL

process_name列显示正在运行的可执行文件的名称。对于管理节点,这是ndb_mgmd。对于数据节点,这是ndbd(单线程)或ndbmtd(多线程)。对于 SQL 节点,这是mysqld。对于其他类型的 API 节点,它是连接到集群的可执行程序的名称;NDB API 应用程序可以使用Ndb_cluster_connection::set_name()设置此列的自定义值。

service_URI显示了服务网络地址。对于管理节点和数据节点,使用的方案是ndb://。对于 SQL 节点,使用的是mysql://。默认情况下,除 SQL 节点外的 API 节点使用ndb://作为方案;NDB API 应用程序可以使用Ndb_cluster_connection::set_service_uri()将其设置为自定义值。无论节点类型如何,方案后面都是 NDB 传输器用于该节点的 IP 地址。对于管理节点和 SQL 节点,此地址包括端口号(通常为管理节点的 1186 和 SQL 节点的 3306)。如果 SQL 节点是使用bind_address系统变量启动的,则使用此地址而不是传输器地址,除非绑定地址设置为*0.0.0.0::

附加路径信息可以包含在service_URI值中,反映了 SQL 节点的各种配置选项。例如,mysql://198.51.100.3/tmp/mysql.sock表示 SQL 节点是使用skip_networking系统变量启动的,而mysql://198.51.100.3:3306/?server-id=1表明该 SQL 节点启用了复制功能。

dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-resources.html

25.6.16.51 ndbinfo 资源表

此表提供有关数据节点资源可用性和使用情况的信息。

这些资源有时被称为超级池。

resources表包含以下列:

  • node_id

    此数据节点的唯一节点 ID。

  • resource_name

    资源的名称;参见文本。

  • reserved

    为此资源保留的 32KB 页面数。

  • used

    实际由此资源使用的 32KB 页面数。

  • max

    自节点上次启动以来使用的此资源的最大数量(32KB 页面数)。

注意事项

resource_name可以是以下表中显示的任何一个名称:

  • RESERVED:系统保留;不能被覆盖。

  • TRANSACTION_MEMORY:为此数据节点上的事务分配的内存。在 NDB 8.0.19 及更高版本中,可以使用TransactionMemory配置参数来控制。

  • DISK_OPERATIONS:如果分配了日志文件组,则使用撤销日志缓冲区的大小来设置此资源的大小。此资源仅用于为撤销日志文件组分配撤销日志缓冲区;只能有一个这样的组。根据需要通过CREATE LOGFILE GROUP进行过度分配。

  • DISK_RECORDS:为磁盘数据操作分配的记录。

  • DATA_MEMORY:用于主内存元组、索引和哈希索引。DataMemory 和 IndexMemory 的总和,如果 IndexMemory 已设置,则再加上 8 个 32KB 的页面。不能过度分配。

  • JOBBUFFER:由 NDB 调度程序用于分配作业缓冲区;不能过度分配。每个线程大约为 2MB,以及所有可以通信的线程两个方向各有 1MB 的缓冲区。对于大型配置,这将消耗数 GB。

  • FILE_BUFFERS:由DBLQH内核块中的重做日志处理程序使用;不能过度分配。大小为NoOfFragmentLogParts * RedoBuffer,再加上每个日志文件部分 1MB。

  • TRANSPORTER_BUFFERS: 由ndbmtd")使用的发送缓冲区;TotalSendBufferMemoryExtraSendBufferMemory之和。这个资源可以过度分配多达 25%。TotalSendBufferMemory通过对每个节点的发送缓冲区内存求和来计算,其默认值为 2 MB。因此,在一个有四个数据节点和八个 API 节点的系统中,数据节点有 12 * 2 MB 的发送缓冲区内存。ExtraSendBufferMemoryndbmtd")使用,每个线程额外使用 2 MB 内存。因此,对于 4 个 LDM 线程、2 个 TC 线程、1 个主线程、1 个复制线程和 2 个接收线程,ExtraSendBufferMemory为 10 * 2 MB。可以通过设置SharedGlobalMemory数据节点配置参数来过度分配这个资源。

  • DISK_PAGE_BUFFER: 用于磁盘页缓冲区;由DiskPageBufferMemory配置参数确定。不能过度分配。

  • QUERY_MEMORY: 被DBSPJ内核块使用。

  • SCHEMA_TRANS_MEMORY: 最小为 2 MB;可以过度分配以使用任何剩余的可用内存。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-restart-info.html

25.6.16.52 ndbinfo restart_info 表

restart_info表包含有关节点重新启动操作的信息。表中的每个条目对应于具有给定节点 ID 的数据节点实时的节点重新启动状态报告。仅显示任何给定节点的最新报告。

restart_info表包含以下列:

  • node_id

    集群中的节点 ID

  • node_restart_status

    节点状态;请参阅文本以获取值。每个值对应于node_restart_status_int的可能值。

  • node_restart_status_int

    节点状态代码;请参阅文本以获取值。

  • secs_to_complete_node_failure

    完成节点故障处理所需的秒数

  • secs_to_allocate_node_id

    从节点故障完成到分配节点 ID 所需的秒数

  • secs_to_include_in_heartbeat_protocol

    从分配节点 ID 到包含在心跳协议中所花费的秒数

  • secs_until_wait_for_ndbcntr_master

    从包含在心跳协议中到等待NDBCNTR主节点开始等待的秒数

  • secs_wait_for_ndbcntr_master

    等待被NDBCNTR主节点接受开始的秒数

  • secs_to_get_start_permitted

    从主节点接收启动许可到所有节点接受此节点启动所花费的秒数

  • secs_to_wait_for_lcp_for_copy_meta_data

    等待复制元数据之前等待 LCP 完成所花费的秒数

  • secs_to_copy_meta_data

    从主节点复制元数据到新开始节点所需的秒数

  • secs_to_include_node

    等待 GCP 和所有节点加入协议的秒数

  • secs_starting_node_to_request_local_recovery

    刚开始的节点等待请求本地恢复所花费的秒数

  • secs_for_local_recovery

    刚开始的节点本地恢复所需的秒数

  • secs_restore_fragments

    从 LCP 文件中恢复片段所需的秒数

  • secs_undo_disk_data

    执行在磁盘数据部分上的撤销日志所需的秒数

  • secs_exec_redo_log

    执行重做日志在所有恢复片段上所需的秒数

  • secs_index_rebuild

    重建恢复片段上的索引所需的秒数

  • secs_to_synchronize_starting_node

    从活动节点同步开始节点所需的秒数

  • secs_wait_lcp_for_restart

    完成重新启动之前开始和完成 LCP 所需的秒数

  • secs_wait_subscription_handover

    等待复制元数据的秒数

  • total_restart_secs

    从节点故障到节点再次启动的总秒数

备注

以下列表包含了node_restart_status_int列中定义的值及其内部状态名称(括号中),以及在node_restart_status列中显示的相应消息:

  • 0 (ALLOCATED_NODE_ID)

    分配的节点 ID

  • 1 (INCLUDED_IN_HB_PROTOCOL)

    包含在心跳协议中

  • 2 (NDBCNTR_START_WAIT)

    等待 NDBCNTR 主节点允许我们启动

  • 3 (NDBCNTR_STARTED)

    NDBCNTR 主节点允许我们启动

  • 4 (START_PERMITTED)

    所有节点允许我们启动

  • 5 (WAIT_LCP_TO_COPY_DICT)

    等待 LCP 完成以开始复制元数据

  • 6 (COPY_DICT_TO_STARTING_NODE)

    正在将元数据复制到起始节点

  • 7 (INCLUDE_NODE_IN_LCP_AND_GCP)

    将节点包含在 LCP 和 GCP 协议中

  • 8 (LOCAL_RECOVERY_STARTED)

    正在恢复片段

  • 9 (COPY_FRAGMENTS_STARTED)

    正在将起始节点与活动节点同步

  • 10 (WAIT_LCP_FOR_RESTART)

    等待 LCP 以确保持久性

  • 11 (WAIT_SUMA_HANDOVER)

    等待订阅切换

  • 12 (RESTART_COMPLETED)

    重启已完成

  • 13 (NODE_FAILED)

    节点失败,故障处理正在进行中

  • 14 (NODE_FAILURE_COMPLETED)

    节点故障处理已完成

  • 15 (NODE_GETTING_PERMIT)

    所有节点允许我们启动

  • 16 (NODE_GETTING_INCLUDED)

    将节点包含在 LCP 和 GCP 协议中

  • 17 (NODE_GETTING_SYNCHED)

    正在将起始节点与活动节点同步

  • 18 (NODE_GETTING_LCP_WAITED)

    [无]

  • 19 (NODE_ACTIVE)

    重启已完成

  • 20 (NOT_DEFINED_IN_CLUSTER)

    [无]

  • 21 (NODE_NOT_RESTARTED_YET)

    初始状态

状态编号 0 到 12 仅适用于主节点;表中显示的其余状态编号适用于所有重新启动的数据节点。状态编号 13 和 14 定义了节点故障状态;20 和 21 发生在没有关于给定节点重新启动的信息时。

另请参阅 Section 25.6.4, “NDB Cluster 启动阶段总结”。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-server-locks.html

25.6.16.53 ndbinfo server_locks 表

server_locks表的结构类似于cluster_locks表,并提供后者中找到的信息的子集,但是特定于 SQL 节点(MySQL 服务器)的信息(cluster_locks表提供有关集群中所有锁的信息)。更确切地说,server_locks包含由属于当前mysqld实例的线程请求的锁的信息,并作为server_operations的伴随表。这可能有助于将锁定模式与特定的 MySQL 用户会话、查询或用例相关联。

server_locks表包含以下列:

  • mysql_connection_id

    MySQL 连接 ID

  • node_id

    报告节点的 ID

  • block_instance

    报告 LDM 实例的 ID

  • tableid

    包含此行的表的 ID

  • fragmentid

    包含被锁定行的片段的 ID

  • rowid

    被锁定行的 ID

  • transid

    事务 ID

  • mode

    锁请求模式

  • state

    锁状态

  • detail

    是否是行锁队列中的第一个持有锁

  • op

    操作类型

  • duration_millis

    等待或持有锁的毫秒数

  • lock_num

    锁对象的 ID

  • waiting_for

    等待具有此 ID 的锁

备注

mysql_connection_id列显示由SHOW PROCESSLIST显示的 MySQL 连接或线程 ID。

block_instance指的是内核块的一个实例。与块名称一起,此数字可用于在threadblocks表中查找给定实例。

tableidNDB分配给表;在其他ndbinfo表中以及ndb_show_tables的输出中使用相同的 ID 来表示此表。

transid列中显示的事务 ID 是由 NDB API 为请求或持有当前锁的事务生成的标识符。

mode列显示锁模式,始终为S(共享锁)或X(排他锁)。如果事务对给定行有排他锁,则该行上的所有其他锁都具有相同的事务 ID。

state列显示锁状态。其值始终为H(持有)或W(等待)。等待锁请求等待由不同事务持有的锁。

detail列指示此锁是否是受影响行的锁队列中持有锁的第一个锁,如果是,则包含一个*(星号字符);否则,此列为空。此信息可用于帮助识别锁请求列表中的唯一条目。

op列显示请求锁的操作类型。这始终是READINSERTUPDATEDELETESCANREFRESH中的一个值。

duration_millis列显示此锁请求等待或持有锁的毫秒数。当为等待请求授予锁时,此值将重置为 0。

锁 ID(lockid列)对于此节点和块实例是唯一的。

如果lock_state列的值为W,则此锁正在等待授予,并且waiting_for列显示此请求正在等待的锁对象的锁 ID。否则,waiting_for为空。waiting_for只能引用相同行上的锁(由node_idblock_instancetableidfragmentidrowid标识)。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-server-operations.html

25.6.16.54 ndbinfo server_operations 表

server_operations表包含当前 SQL 节点(MySQL 服务器)当前参与的所有进行中的NDB操作的条目。它实际上是cluster_operations表的一个子集,其中不显示其他 SQL 和 API 节点的操作。

server_operations表包含以下列:

  • mysql_connection_id

    MySQL 服务器连接 ID

  • node_id

    节点 ID

  • block_instance

    块实例

  • transid

    事务 ID

  • operation_type

    操作类型(请参阅文本以获取可能的值)

  • state

    操作状态(请参阅文本以获取可能的值)

  • tableid

    表 ID

  • fragmentid

    片段 ID

  • client_node_id

    客户端节点 ID

  • client_block_ref

    客户端块引用

  • tc_node_id

    事务协调器节点 ID

  • tc_block_no

    事务协调器块编号

  • tc_block_instance

    事务协调器块实例

备注

mysql_connection_idSHOW PROCESSLIST输出中显示的连接或会话 ID 相同。它从INFORMATION_SCHEMANDB_TRANSID_MYSQL_CONNECTION_MAP中获取。

block_instance指的是内核块的一个实例。与块名称一起,此数字可用于在threadblocks表中查找给定实例。

事务 ID(transid)是一个唯一的 64 位数字,可以使用 NDB API 的getTransactionId()方法获取。(目前,MySQL 服务器不公开正在进行的事务的 NDB API 事务 ID。)

operation_type列可以取以下任一值:READREAD-SHREAD-EXINSERTUPDATEDELETEWRITEUNLOCKREFRESHSCANSCAN-SHSCAN-EX<unknown>

state列可以具有以下任一值:ABORT_QUEUEDABORT_STOPPEDCOMMITTEDCOMMIT_QUEUEDCOMMIT_STOPPEDCOPY_CLOSE_STOPPEDCOPY_FIRST_STOPPEDCOPY_STOPPEDCOPY_TUPKEYIDLELOG_ABORT_QUEUEDLOG_COMMIT_QUEUEDLOG_COMMIT_QUEUED_WAIT_SIGNALLOG_COMMIT_WRITTENLOG_COMMIT_WRITTEN_WAIT_SIGNALLOG_QUEUEDPREPAREDPREPARED_RECEIVED_COMMITSCAN_CHECK_STOPPEDSCAN_CLOSE_STOPPEDSCAN_FIRST_STOPPEDSCAN_RELEASE_STOPPEDSCAN_STATE_USEDSCAN_STOPPEDSCAN_TUPKEYSTOPPEDTC_NOT_CONNECTEDWAIT_ACCWAIT_ACC_ABORTWAIT_AI_AFTER_ABORTWAIT_ATTRWAIT_SCAN_AIWAIT_TUPWAIT_TUPKEYINFOWAIT_TUP_COMMITWAIT_TUP_TO_ABORT。(如果 MySQL 服务器使用ndbinfo_show_hidden启用运行,则可以通过从通常隐藏的ndb$dblqh_tcconnect_state表中选择来查看此状态列表。)

通过检查ndb_show_tables的输出,您可以通过表 ID 获取NDB表的名称。

fragidndb_desc的输出中看到的分区号相同,--extra-partition-info(简写为-p)。

client_node_idclient_block_ref中,client指的是 NDB Cluster API 或 SQL 节点(即,NDB API 客户端或连接到集群的 MySQL 服务器)。

block_instancetc_block_instance列提供 NDB 内核块实例号。您可以使用这些信息从threadblocks表中获取有关特定线程的信息。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-server-transactions.html

25.6.16.55 ndbinfo server_transactions 表

server_transactions表是cluster_transactions表的子集,但仅包括当前 SQL 节点(MySQL 服务器)参与的事务,并包括相关的连接 ID。

server_transactions表包含以下列:

  • mysql_connection_id

    MySQL 服务器连接 ID

  • node_id

    事务协调器节点 ID

  • block_instance

    事务协调器块实例

  • transid

    事务 ID

  • state

    操作状态(请参阅可能的值)

  • count_operations

    事务中有状态的操作数

  • outstanding_operations

    仍由本地数据管理层(LQH 块)执行的操作

  • inactive_seconds

    等待 API 的时间

  • client_node_id

    客户端节点 ID

  • client_block_ref

    客户端块引用

注意事项

mysql_connection_idSHOW PROCESSLIST输出中显示的连接或会话 ID 相同。它从INFORMATION_SCHEMANDB_TRANSID_MYSQL_CONNECTION_MAP中获取。

block_instance指的是内核块的一个实例。与块名称一起,此数字可用于在threadblocks表中查找给定实例。

事务 ID(transid)是一个唯一的 64 位数字,可以使用 NDB API 的getTransactionId()方法获取。(目前,MySQL 服务器不公开正在进行的事务的 NDB API 事务 ID。)

state列可以具有以下任一值:CS_ABORTINGCS_COMMITTINGCS_COMMIT_SENTCS_COMPLETE_SENTCS_COMPLETINGCS_CONNECTEDCS_DISCONNECTEDCS_FAIL_ABORTEDCS_FAIL_ABORTINGCS_FAIL_COMMITTEDCS_FAIL_COMMITTINGCS_FAIL_COMPLETEDCS_FAIL_PREPAREDCS_PREPARE_TO_COMMITCS_RECEIVINGCS_REC_COMMITTINGCS_RESTARTCS_SEND_FIRE_TRIG_REQCS_STARTEDCS_START_COMMITTINGCS_START_SCANCS_WAIT_ABORT_CONFCS_WAIT_COMMIT_CONFCS_WAIT_COMPLETE_CONFCS_WAIT_FIRE_TRIG_REQ。(如果 MySQL 服务器使用ndbinfo_show_hidden启用运行,则可以通过从通常隐藏的ndb$dbtc_apiconnect_state表中选择来查看此状态列表。)

client_node_idclient_block_ref中,client指的是 NDB 集群 API 或 SQL 节点(即,NDB API 客户端或连接到集群的 MySQL 服务器)。

block_instance列提供了DBTC内核块实例编号。您可以使用此信息从threadblocks表中获取有关特定线程的信息。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-table-distribution-status.html

25.6.16.56 ndbinfo 表table_distribution_status

table_distribution_status表提供了关于NDB表分布进度的信息。

table_distribution_status表包含以下列:

  • node_id

    节点 id

  • table_id

    表 ID

  • tab_copy_status

    将表分布数据复制到磁盘的状态;其中之一为IDLESR_PHASE1_READ_PAGESSR_PHASE2_READ_TABLESR_PHASE3_COPY_TABLEREMOVE_NODELCP_READ_TABLECOPY_TAB_REQCOPY_NODE_STATEADD_TABLE_COORDINATOR在 NDB 8.0.23 之前ADD_TABLE_MASTER)、ADD_TABLE_PARTICIPANT在 NDB 8.0.23 之前ADD_TABLE_SLAVE)、INVALIDATE_NODE_LCPALTER_TABLECOPY_TO_SAVEGET_TABINFO

  • tab_update_status

    表分布数据更新状态;其中之一为IDLELOCAL_CHECKPOINTLOCAL_CHECKPOINT_QUEUEDREMOVE_NODECOPY_TAB_REQADD_TABLE_MASTERADD_TABLE_SLAVEINVALIDATE_NODE_LCPCALLBACK

  • tab_lcp_status

    表 LCP 的状态;其中之一为ACTIVE(等待执行本地检查点)、WRITING_TO_FILE(检查点已执行但尚未写入磁盘)或COMPLETED(检查点已执行并持久化到磁盘)

  • tab_status

    表内部状态;其中之一为ACTIVE(表存在)、CREATING(表正在创建)或DROPPING(表正在删除)

  • tab_storage

    表可恢复性;其中之一为NORMAL(通过重做日志和检查点完全可恢复),NOLOGGING(从节点崩溃中恢复,空集群崩溃后恢复),或TEMPORARY(不可恢复)

  • tab_partitions

    表中的分区数

  • tab_fragments

    表中的片段数;通常与tab_partitions相同;对于完全复制的表,等于tab_partitions * [节点组数]

  • current_scan_count

    当前活动扫描数

  • scan_count_wait

    当前等待执行的扫描数,直到ALTER TABLE完成。

  • is_reorg_ongoing

    表当前是否正在重新组织(如果是则为 1)

原文:dev.mysql.com/doc/refman/8.0/zh/mysql-cluster-ndbinfo-table-fragments.html

25.6.16.57 ndbinfo 表 _fragments 表

table_fragments 表提供关于 NDB 表的分片、分区、分布和(内部)复制的信息。

table_fragments 表包含以下列:

  • node_id

    节点 ID(DIH 主节点)

  • table_id

    表 ID

  • partition_id

    分区 ID

  • fragment_id

    片段 ID(除非表完全复制,否则与分区 ID 相同)

  • partition_order

    片段在分区中的顺序

  • log_part_id

    片段的日志部分 ID

  • no_of_replicas

    片段副本数量

  • current_primary

    当前主节点 ID

  • preferred_primary

    首选主节点 ID

  • current_first_backup

    当前第一备份节点 ID

  • current_second_backup

    当前第二备份节点 ID

  • current_third_backup

    当前第三备份节点 ID

  • num_alive_replicas

    当前活片段副本数量

  • num_dead_replicas

    当前死片段副本数量

  • num_lcp_replicas

    剩余待检查点的片段副本数量

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-table-info.html

25.6.16.58 ndbinfo 表 table_info

table_info表提供了关于个别NDB表的日志记录、检查点、分布和存储选项的信息。

table_info表包含以下列:

  • table_id

    表 ID

  • logged_table

    表是否已记录(1)或未记录(0)

  • row_contains_gci

    表行是否包含 GCI(1 为真,0 为假)

  • row_contains_checksum

    表行是否包含校验和(1 为真,0 为假)

  • read_backup

    如果备份片段副本被读取,则为 1,否则为 0

  • fully_replicated

    如果表是完全复制的话,这个值为 1,否则为 0

  • storage_type

    表存储类型;其中之一为MEMORYDISK

  • hashmap_id

    哈希映射 ID

  • partition_balance

    表的分区平衡(片段计数类型);其中之一为FOR_RP_BY_NODEFOR_RA_BY_NODEFOR_RP_BY_LDMFOR_RA_BY_LDM

  • create_gci

    表创建时的 GCI

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-table-replicas.html

25.6.16.59 ndbinfo 表 _replicas 表

table_replicas表提供有关 NDB 表片段和片段副本的复制、分发和检查点的信息。

table_replicas表包含以下列:

  • node_id

    从中提取数据的节点的 ID(DIH主节点)

  • table_id

    表 ID

  • fragment_id

    片段 ID

  • initial_gci

    表的初始 GCI

  • replica_node_id

    存储片段副本的节点的 ID

  • is_lcp_ongoing

    如果此片段上正在进行 LCP,则为 1,否则为 0

  • num_crashed_replicas

    崩溃片段副本实例的数量

  • last_max_gci_started

    最近 LCP 中开始的最高 GCI

  • last_max_gci_completed

    最近 LCP 中完成的最高 GCI

  • last_lcp_id

    最近 LCP 的 ID

  • prev_lcp_id

    上一个 LCP 的 ID

  • prev_max_gci_started

    上一个 LCP 中开始的最高 GCI

  • prev_max_gci_completed

    上一个 LCP 中完成的最高 GCI

  • last_create_gci

    上一个崩溃片段副本实例的最后创建 GCI

  • last_replica_gci

    上一个崩溃片段副本实例的最后 GCI

  • is_replica_alive

    如果此片段副本存活,则为 1,否则为 0

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-tc-time-track-stats.html

25.6.16.60 The ndbinfo tc_time_track_stats Table

tc_time_track_stats 表提供从数据节点中的 DBTC 块(TC)实例中获取的时间跟踪信息,通过 API 节点访问 NDB。每个 TC 实例跟踪它代表 API 节点或其他数据节点执行的一组活动的延迟;这些活动包括事务、事务错误、键读取、键写入、唯一索引操作、任何类型的失败键操作、扫描、失败扫描、片段扫描和失败片段扫描。

为每种活动维护一组计数器,每个计数器覆盖小于或等于上限值的延迟范围。在每个活动结束时,确定其延迟并适当地递增计数器。tc_time_track_stats 将此信息呈现为行,每个实例对应一行:

  • 数据节点,使用其 ID

  • TC 块实例

  • 其他通信数据节点或 API 节点,使用其 ID

  • 上限值

每行包含每种活动类型的值。这是该活动发生的次数,其延迟在行指定的范围内(即,延迟不超过上限值)。

tc_time_track_stats 表包含以下列:

  • node_id

    请求节点 ID

  • block_number

    TC 块编号

  • block_instance

    TC 块实例编号

  • comm_node_id

    通信 API 或数据节点的节点 ID

  • upper_bound

    区间的上限值(以微秒为单位)

  • scans

    基于从打开到关闭的成功扫描持续时间,针对请求它们的 API 或数据节点进行跟踪。

  • scan_errors

    基于从打开到关闭的失败扫描持续时间,针对请求它们的 API 或数据节点进行跟踪。

  • scan_fragments

    基于从打开到关闭的成功片段扫描持续时间,针对执行它们的数据节点进行跟踪。

  • scan_fragment_errors

    基于从打开到关闭的失败片段扫描持续时间,针对执行它们的数据节点进行跟踪。

  • transactions

    基于从开始到发送提交ACK的成功事务持续时间,针对请求它们的 API 或数据节点进行跟踪。无状态事务不包括在内。

  • transaction_errors

    基于从开始到失败点的失败事务持续时间,针对请求它们的 API 或数据节点进行跟踪。

  • read_key_ops

    基于带锁的成功主键读取的持续时间。针对请求它们的 API 或数据节点以及执行它们的数据节点进行跟踪。

  • write_key_ops

    基于成功主键写入的持续时间,针对请求它们的 API 或数据节点以及执行它们的数据节点进行跟踪。

  • index_key_ops

    基于成功唯一索引键操作的持续时间,跟踪 API 或数据节点请求它们以及执行基表读取的数据节点。

  • key_op_errors

    基于所有不成功的键读取或写入操作的持续时间,跟踪 API 或数据节点请求它们以及执行它们的数据节点。

注意

block_instance列提供了DBTC内核块实例编号。您可以将其与块名称一起使用,从threadblocks表中获取有关特定线程的信息。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-threadblocks.html

25.6.16.61 ndbinfo threadblocks

threadblocks 表关联数据节点、线程和 NDB 内核块的实例。

threadblocks 表包含以下列:

  • node_id

    节点 ID

  • thr_no

    线程 ID

  • block_name

    区块名称

  • block_instance

    块实例编号

备注

在这个表中,block_name 的值是从 ndbinfo.blocks 表中选择时在 block_name 列中找到的值之一。虽然在给定的 NDB Cluster 版本中可能值的列表是静态的,但在不同版本之间可能会有所不同。

block_instance 列提供了内核块实例编号。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-threads.html

25.6.16.62 ndbinfo 线程表

threads表提供了关于在NDB内核中运行的线程的信息。

threads表包含以下列:

  • node_id

    线程所在节点的 ID

  • thr_no

    线程 ID(特定于此节点)

  • thread_name

    线程名称(线程类型)

  • thread_description

    线程(类型)描述

笔记

展示了一个包括线程描述的 2 节点示例集群的样本输出:

mysql> SELECT * FROM threads;
+---------+--------+-------------+------------------------------------------------------------------+
| node_id | thr_no | thread_name | thread_description                                               |
+---------+--------+-------------+------------------------------------------------------------------+
|       5 |      0 | main        | main thread, schema and distribution handling                    |
|       5 |      1 | rep         | rep thread, asynch replication and proxy block handling          |
|       5 |      2 | ldm         | ldm thread, handling a set of data partitions                    |
|       5 |      3 | recv        | receive thread, performing receive and polling for new receives  |
|       6 |      0 | main        | main thread, schema and distribution handling                    |
|       6 |      1 | rep         | rep thread, asynch replication and proxy block handling          |
|       6 |      2 | ldm         | ldm thread, handling a set of data partitions                    |
|       6 |      3 | recv        | receive thread, performing receive and polling for new receives  |
+---------+--------+-------------+------------------------------------------------------------------+
8 rows in set (0.01 sec)

NDB 8.0.23 引入了在将ThreadConfig参数mainrep设置为 0 的同时保持另一个参数为 1 的可能性,此时线程名称为main_rep,描述为主和副本线程,模式,分发,代理块和异步复制处理。从 NDB 8.0.23 开始,还可以将mainrep都设置为 0,此时生成线程的名称在此表中显示为main_rep_recv,描述为主,副本和接收线程,模式,分发,代理块和异步复制处理以及处理接收和轮询新接收

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-threadstat.html

25.6.16.63 ndbinfo threadstat

threadstat 表提供了运行在 NDB 内核中的线程统计的大致快照。

threadstat 表包含以下列:

  • node_id

    节点 ID

  • thr_no

    线程 ID

  • thr_nm

    线程名称

  • c_loop

    主循环中的循环次数

  • c_exec

    执行的信号数

  • c_wait

    等待额外输入的次数

  • c_l_sent_prioa

    发送到自身节点的优先级 A 信号数

  • c_l_sent_priob

    发送到自身节点的优先级 B 信号数

  • c_r_sent_prioa

    发送到远程节点的优先级 A 信号数

  • c_r_sent_priob

    发送到远程节点的优先级 B 信号数

  • os_tid

    OS 线程 ID

  • os_now

    OS 时间(毫秒)

  • os_ru_utime

    OS 用户 CPU 时间(微秒)

  • os_ru_stime

    OS 系统 CPU 时间(微秒)

  • os_ru_minflt

    OS 页面回收(软页错误)

  • os_ru_majflt

    OS 页面错误(硬页错误)

  • os_ru_nvcsw

    OS 自愿上下文切换

  • os_ru_nivcsw

    OS 非自愿上下文切换

注意事项

os_time 使用系统 gettimeofday() 调用。

os_ru_utimeos_ru_stimeos_ru_minfltos_ru_majfltos_ru_nvcswos_ru_nivcsw 列的值是使用系统 getrusage() 调用或等效调用获取的。

由于该表包含在特定时间点采取的计数,为了获得最佳结果,需要定期查询该表并将结果存储在一个中间表或多个表中。 MySQL 服务器的事件调度程序可用于自动化此类监控。有关更多信息,请参见 Section 27.4, “Using the Event Scheduler”。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-transporter-details.html

25.6.16.64 ndbinfo transporter_details 表

此表包含有关单个 NDB 传输器的信息,而不是由 transporters 表显示的聚合信息。transporter_details 表在 NDB 8.0.37 中添加。

transporter_details 表包含以下列:

  • node_id

    此数据节点在集群中的唯一节点 ID

  • block_instance

  • trp_id

    传输器 ID

  • remote_node_id

    远程数据节点的节点 ID

  • status

    连接的状态

  • remote_address

    远程主机的名称或 IP 地址

  • bytes_sent

    使用此连接发送的字节数

  • bytes_received

    使用此连接接收的字节数

  • connect_count

    在此传输器上建立连接的次数

  • overloaded

    如果此传输器当前过载,则为 1,否则为 0

  • overload_count

    此传输器自连接以来进入过载状态的次数

  • slowdown

    如果此传输器处于减速状态,则为 1,否则为 0

  • slowdown_count

    此传输器自连接以来进入减速状态的次数

  • encrypted

    如果此传输器使用 TLS 连接,则此列为 1,否则为 0

transporter_details 表显示集群中每个传输器的状态。有关此表中每列的更多信息,请参阅 transporters 表的注释。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ndbinfo-transporters.html

25.6.16.65 ndbinfo transporters 表

此表包含有关 NDB 传输器的聚合信息。在 NDB 8.0.37 及更高版本中,您可以从transporter_details表中获取有关单个传输器的类似信息。

transporters 表包含以下列:

  • node_id

    此数据节点在集群中的唯一节点 ID

  • remote_node_id

    远程数据节点的节点 ID

  • status

    连接的状态

  • remote_address

    远程主机的名称或 IP 地址

  • bytes_sent

    使用此连接发送的字节数

  • bytes_received

    使用此连接接收的字节数

  • connect_count

    自此传输器建立连接以来的连接次数

  • overloaded

    如果此传输器当前超载,则为 1,否则为 0

  • overload_count

    此传输器自连接以来进入超载状态的次数

  • slowdown

    如果此传输器处于减速状态,则为 1,否则为 0

  • slowdown_count

    自连接以来此传输器进入减速状态的次数

注释

对于集群中每个运行的数据节点,transporters 表显示一行,显示该节点与集群中所有节点(包括自身)的每个连接的状态。此信息显示在表的status列中,该列可以具有以下任一值:CONNECTINGCONNECTEDDISCONNECTINGDISCONNECTED

配置但当前未连接到集群的 API 和管理节点的连接显示为DISCONNECTED状态。在此表中不显示node_id为当前未连接的数据节点的行(这类似于ndbinfo.nodes表中省略了断开连接的节点。

remote_address 是显示在remote_node_id列中的节点的主机名或地址。从此节点发送的bytes_sent和此节点接收的bytes_received分别是使用此连接发送和接收的字节数。对于状态为CONNECTINGDISCONNECTED的节点,这些列始终显示0

假设您有一个由 2 个数据节点、2 个 SQL 节点和 1 个管理节点组成的 5 节点集群,如SHOW命令在ndb_mgm客户端的输出中所示:

ndb_mgm> SHOW
Connected to Management Server at: localhost:1186
Cluster Configuration
---------------------
[ndbd(NDB)]     2 node(s)
id=1    @10.100.10.1  (8.0.35-ndb-8.0.35, Nodegroup: 0, *)
id=2    @10.100.10.2  (8.0.35-ndb-8.0.35, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s)
id=10   @10.100.10.10  (8.0.35-ndb-8.0.35)

[mysqld(API)]   2 node(s)
id=20   @10.100.10.20  (8.0.35-ndb-8.0.35)
id=21   @10.100.10.21  (8.0.35-ndb-8.0.35)

transporters 表中有 10 行——第一个数据节点有 5 行,第二个数据节点也有 5 行——假设所有数据节点都在运行,如下所示:

mysql> SELECT node_id, remote_node_id, status
 ->   FROM ndbinfo.transporters;
+---------+----------------+---------------+
| node_id | remote_node_id | status        |
+---------+----------------+---------------+
|       1 |              1 | DISCONNECTED  |
|       1 |              2 | CONNECTED     |
|       1 |             10 | CONNECTED     |
|       1 |             20 | CONNECTED     |
|       1 |             21 | CONNECTED     |
|       2 |              1 | CONNECTED     |
|       2 |              2 | DISCONNECTED  |
|       2 |             10 | CONNECTED     |
|       2 |             20 | CONNECTED     |
|       2 |             21 | CONNECTED     |
+---------+----------------+---------------+
10 rows in set (0.04 sec)

如果您使用 ndb_mgm 客户端中的 2 STOP 命令关闭此集群中的一个数据节点,然后重复上一个查询(再次使用 mysql 客户端),则此表现在仅显示 5 行—每个连接从剩余管理节点到另一个节点,包括自身和当前离线的数据节点,显示每个剩余连接到当前离线的数据节点的状态为 CONNECTING,如下所示:

mysql> SELECT node_id, remote_node_id, status
 ->   FROM ndbinfo.transporters;
+---------+----------------+---------------+
| node_id | remote_node_id | status        |
+---------+----------------+---------------+
|       1 |              1 | DISCONNECTED  |
|       1 |              2 | CONNECTING    |
|       1 |             10 | CONNECTED     |
|       1 |             20 | CONNECTED     |
|       1 |             21 | CONNECTED     |
+---------+----------------+---------------+
5 rows in set (0.02 sec)

connect_countoverloadedoverload_countslowdownslowdown_count 计数器在连接时重置,并在远程节点断开连接后保留其值。bytes_sentbytes_received 计数器也在连接时重置,因此在断开连接后保留其值(直到下次连接重置它们)。

overloadedoverload_count 列所指的 过载 状态发生在此传输器的发送缓冲区包含超过 OVerloadLimit 字节(默认为 SendBufferMemory 的 80%,即 0.8 * 2097152 = 1677721 字节)时。当给定传输器处于过载状态时,任何尝试使用此传输器的新事务都会因错误 1218(NDB 内核中的发送缓冲区过载)而失败。这影响扫描和主键操作。

此表中 slowdownslowdown_count 列引用的 减速 状态发生在传输器的发送缓冲区包含超过过载限制的 60% 时(默认为 0.6 * 2097152 = 1258291 字节)。在此状态下,使用此传输器的任何新扫描都会将其批量大小减小以减少传输器的负载。

发送缓冲区减速或过载的常见原因包括以下内容:

  • 数据大小,特别是存储在 TEXT 列或 BLOB 列(或两种列类型)中的数据量

  • 在进行二进制日志记录的 SQL 节点上与数据节点(ndbd 或 ndbmtd)位于同一主机上

  • 每个事务或事务批次中的大量行

  • 配置问题,如不足的 SendBufferMemory

  • 硬件问题,如内存不足或网络连接质量差

参见 Section 25.4.3.14, “配置 NDB 集群发送缓冲区参数”。

25.6.17 NDB 集群的 INFORMATION_SCHEMA 表

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-information-schema-tables.html

两个INFORMATION_SCHEMA表提供了在管理 NDB 集群时特别有用的信息。FILES表提供了关于 NDB 集群磁盘数据文件的信息(参见第 25.6.11.1 节,“NDB 集群磁盘数据对象”)。ndb_transid_mysql_connection_map表提供了事务、事务协调器和 API 节点之间的映射。

可以从ndbinfo数据库中的表中获取有关 NDB 集群事务、操作、线程、块以及性能的其他统计数据。有关这些表的信息,请参见第 25.6.16 节,“ndbinfo: NDB 集群信息数据库”。

25.6.18 NDB Cluster and the Performance Schema

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-ps-tables.html

NDB 8.0 提供了关于线程和事务内存使用情况的 MySQL 性能模式信息;NDB 8.0.29 添加了ndbcluster插件线程,NDB 8.0.30 添加了事务批处理内存的仪表化。这些功能在接下来的章节中有更详细的描述。

ndbcluster 插件线程

从 NDB 8.0.29 开始,ndbcluster插件线程在性能模式threads表中可见,如下查询所示:

mysql> SELECT name, type, thread_id, thread_os_id
 -> FROM performance_schema.threads
 -> WHERE name LIKE '%ndbcluster%'\G
+----------------------------------+------------+-----------+--------------+
| name                             | type       | thread_id | thread_os_id |
+----------------------------------+------------+-----------+--------------+
| thread/ndbcluster/ndb_binlog     | BACKGROUND |        30 |        11980 |
| thread/ndbcluster/ndb_index_stat | BACKGROUND |        31 |        11981 |
| thread/ndbcluster/ndb_metadata   | BACKGROUND |        32 |        11982 |
+----------------------------------+------------+-----------+--------------+

threads表显示了这里列出的所有三个线程:

  • ndb_binlog:二进制日志线程

  • ndb_index_stat:索引统计线程

  • ndb_metadata:元数据线程

这些线程也在setup_threads表中按名称显示。

线程名称在threadssetup_threads表的name列中以*prefix*/*plugin_name*/*thread_name*的格式显示。 prefix是由performance_schema引擎确定的对象类型,对于插件线程是thread(参见 Thread Instrument Elements)。 plugin_namendbclusterthread_name是线程的独立名称(ndb_binlogndb_index_statndb_metadata)。

使用threadssetup_threads表中给定线程的线程 ID 或 OS 线程 ID,可以从性能模式中获取有关插件执行和资源使用的大量信息。此示例显示了如何通过连接threadsmemory_summary_by_thread_by_event_name表来获取ndbcluster插件创建的线程在mem_root区域分配的内存量:

mysql> SELECT
 ->   t.name,
 ->   m.sum_number_of_bytes_alloc,
 ->   IF(m.sum_number_of_bytes_alloc > 0, "true", "false") AS 'Has allocated memory'
 -> FROM performance_schema.memory_summary_by_thread_by_event_name m
 -> JOIN performance_schema.threads t
 -> ON m.thread_id = t.thread_id
 -> WHERE t.name LIKE '%ndbcluster%'
 ->   AND event_name LIKE '%THD::main_mem_root%';
+----------------------------------+---------------------------+----------------------+
| name                             | sum_number_of_bytes_alloc | Has allocated memory |
+----------------------------------+---------------------------+----------------------+
| thread/ndbcluster/ndb_binlog     |                     20576 | true                 |
| thread/ndbcluster/ndb_index_stat |                         0 | false                |
| thread/ndbcluster/ndb_metadata   |                      8240 | true                 |
+----------------------------------+---------------------------+----------------------+

事务内存使用情况

从 NDB 8.0.30 开始,您可以通过查询性能模式memory_summary_by_thread_by_event_name表来查看事务批处理使用的内存量,类似于下面显示的内容:

mysql> SELECT EVENT_NAME
 ->   FROM performance_schema.memory_summary_by_thread_by_event_name
 ->   WHERE THREAD_ID = PS_CURRENT_THREAD_ID()
 ->     AND EVENT_NAME LIKE 'memory/ndbcluster/%';
+-------------------------------------------+
| EVENT_NAME                                |
+-------------------------------------------+
| memory/ndbcluster/Thd_ndb::batch_mem_root |
+-------------------------------------------+
1 row in set (0.01 sec)

ndbcluster事务内存仪器也在性能模式setup_instruments表中可见,如下所示:

mysql> SELECT * from performance_schema.setup_instruments 
 ->   WHERE NAME LIKE '%ndb%'\G
*************************** 1\. row ***************************
         NAME: memory/ndbcluster/Thd_ndb::batch_mem_root
      ENABLED: YES
        TIMED: NULL
   PROPERTIES: 
   VOLATILITY: 0
DOCUMENTATION: Memory used for transaction batching 1 row in set (0.01 sec)

25.6.19 快速参考:NDB 集群 SQL 语句

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-sql-statements.html

本节讨论了几个 SQL 语句,这些语句在管理和监控连接到 NDB 集群的 MySQL 服务器时可能会有用,并在某些情况下提供有关集群本身的信息。

  • SHOW ENGINE NDB STATUSSHOW ENGINE NDBCLUSTER STATUS

    此语句的输出包含有关服务器与集群的连接、NDB 集群对象的创建和使用,以及 NDB 集群复制的二进制日志记录的信息。

    有关用法示例和更详细信息,请参见第 15.7.7.15 节,“SHOW ENGINE 语句”。

  • SHOW ENGINES

    此语句可用于确定 MySQL 服务器中是否启用了集群支持,以及如果是,则是否处于活动状态。

    有关更详细信息,请参见第 15.7.7.16 节,“SHOW ENGINES 语句”。

    注意

    此语句不支持LIKE子句。但是,您可以使用LIKE来过滤针对 Information Schema ENGINES表的查询,如下一项所述。

  • SELECT * FROM INFORMATION_SCHEMA.ENGINES [WHERE ENGINE LIKE 'NDB%']

    这相当于SHOW ENGINES,但使用INFORMATION_SCHEMA数据库的ENGINES表。与SHOW ENGINES语句不同,可以使用LIKE子句来过滤结果,并选择特定列以获取在脚本中可能有用的信息。例如,以下查询显示服务器是否构建有NDB支持,并且如果是,则是否已启用:

    mysql> SELECT ENGINE, SUPPORT FROM INFORMATION_SCHEMA.ENGINES
     ->   WHERE ENGINE LIKE 'NDB%';
    +------------+---------+
    | ENGINE     | SUPPORT |
    +------------+---------+
    | ndbcluster | YES     |
    | ndbinfo    | YES     |
    +------------+---------+
    

    如果未启用NDB支持,则上述查询将返回一个空集。有关更多信息,请参见第 28.3.13 节,“INFORMATION_SCHEMA ENGINES 表”。

  • SHOW VARIABLES LIKE 'NDB%'

    此语句提供了与NDB存储引擎相关的大多数服务器系统变量的列表及其值,如下所示:

    mysql> SHOW VARIABLES LIKE 'NDB%';
    +--------------------------------------+---------------------------------------+
    | Variable_name                        | Value                                 |
    +--------------------------------------+---------------------------------------+
    | ndb_allow_copying_alter_table        | ON                                    |
    | ndb_autoincrement_prefetch_sz        | 512                                   |
    | ndb_batch_size                       | 32768                                 |
    | ndb_blob_read_batch_bytes            | 65536                                 |
    | ndb_blob_write_batch_bytes           | 65536                                 |
    | ndb_clear_apply_status               | ON                                    |
    | ndb_cluster_connection_pool          | 1                                     |
    | ndb_cluster_connection_pool_nodeids  |                                       |
    | ndb_connectstring                    | 127.0.0.1                             |
    | ndb_data_node_neighbour              | 0                                     |
    | ndb_default_column_format            | FIXED                                 |
    | ndb_deferred_constraints             | 0                                     |
    | ndb_distribution                     | KEYHASH                               |
    | ndb_eventbuffer_free_percent         | 20                                    |
    | ndb_eventbuffer_max_alloc            | 0                                     |
    | ndb_extra_logging                    | 1                                     |
    | ndb_force_send                       | ON                                    |
    | ndb_fully_replicated                 | OFF                                   |
    | ndb_index_stat_enable                | ON                                    |
    | ndb_index_stat_option                | loop_enable=1000ms,loop_idle=1000ms,
    loop_busy=100ms,update_batch=1,read_batch=4,idle_batch=32,check_batch=8,
    check_delay=10m,delete_batch=8,clean_delay=1m,error_batch=4,error_delay=1m,
    evict_batch=8,evict_delay=1m,cache_limit=32M,cache_lowpct=90,zero_total=0      |
    | ndb_join_pushdown                    | ON                                    |
    | ndb_log_apply_status                 | OFF                                   |
    | ndb_log_bin                          | OFF                                   |
    | ndb_log_binlog_index                 | ON                                    |
    | ndb_log_empty_epochs                 | OFF                                   |
    | ndb_log_empty_update                 | OFF                                   |
    | ndb_log_exclusive_reads              | OFF                                   |
    | ndb_log_orig                         | OFF                                   |
    | ndb_log_transaction_id               | OFF                                   |
    | ndb_log_update_as_write              | ON                                    |
    | ndb_log_update_minimal               | OFF                                   |
    | ndb_log_updated_only                 | ON                                    |
    | ndb_metadata_check                   | ON                                    |
    | ndb_metadata_check_interval          | 60                                    |
    | ndb_metadata_sync                    | OFF                                   |
    | ndb_mgmd_host                        | 127.0.0.1                             |
    | ndb_nodeid                           | 0                                     |
    | ndb_optimization_delay               | 10                                    |
    | ndb_optimized_node_selection         | 3                                     |
    | ndb_read_backup                      | ON                                    |
    | ndb_recv_thread_activation_threshold | 8                                     |
    | ndb_recv_thread_cpu_mask             |                                       |
    | ndb_report_thresh_binlog_epoch_slip  | 10                                    |
    | ndb_report_thresh_binlog_mem_usage   | 10                                    |
    | ndb_row_checksum                     | 1                                     |
    | ndb_schema_dist_lock_wait_timeout    | 30                                    |
    | ndb_schema_dist_timeout              | 120                                   |
    | ndb_schema_dist_upgrade_allowed      | ON                                    |
    | ndb_show_foreign_key_mock_tables     | OFF                                   |
    | ndb_slave_conflict_role              | NONE                                  |
    | ndb_table_no_logging                 | OFF                                   |
    | ndb_table_temporary                  | OFF                                   |
    | ndb_use_copying_alter_table          | OFF                                   |
    | ndb_use_exact_count                  | OFF                                   |
    | ndb_use_transactions                 | ON                                    |
    | ndb_version                          | 524308                                |
    | ndb_version_string                   | ndb-8.0.35                            |
    | ndb_wait_connected                   | 30                                    |
    | ndb_wait_setup                       | 30                                    |
    | ndbinfo_database                     | ndbinfo                               |
    | ndbinfo_max_bytes                    | 0                                     |
    | ndbinfo_max_rows                     | 10                                    |
    | ndbinfo_offline                      | OFF                                   |
    | ndbinfo_show_hidden                  | OFF                                   |
    | ndbinfo_table_prefix                 | ndb$                                  |
    | ndbinfo_version                      | 524308                                |
    +--------------------------------------+---------------------------------------+
    

    查看更多信息,请参见第 7.1.8 节,“服务器系统变量”。

  • SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME LIKE 'NDB%'

    这个语句相当于之前描述的SHOW VARIABLES语句,并提供几乎相同的输出,如下所示:

    mysql> SELECT * FROM performance_schema.global_variables
     ->   WHERE VARIABLE_NAME LIKE 'NDB%';
    +--------------------------------------+---------------------------------------+
    | VARIABLE_NAME                        | VARIABLE_VALUE                        |
    +--------------------------------------+---------------------------------------+
    | ndb_allow_copying_alter_table        | ON                                    |
    | ndb_autoincrement_prefetch_sz        | 512                                   |
    | ndb_batch_size                       | 32768                                 |
    | ndb_blob_read_batch_bytes            | 65536                                 |
    | ndb_blob_write_batch_bytes           | 65536                                 |
    | ndb_clear_apply_status               | ON                                    |
    | ndb_cluster_connection_pool          | 1                                     |
    | ndb_cluster_connection_pool_nodeids  |                                       |
    | ndb_connectstring                    | 127.0.0.1                             |
    | ndb_data_node_neighbour              | 0                                     |
    | ndb_default_column_format            | FIXED                                 |
    | ndb_deferred_constraints             | 0                                     |
    | ndb_distribution                     | KEYHASH                               |
    | ndb_eventbuffer_free_percent         | 20                                    |
    | ndb_eventbuffer_max_alloc            | 0                                     |
    | ndb_extra_logging                    | 1                                     |
    | ndb_force_send                       | ON                                    |
    | ndb_fully_replicated                 | OFF                                   |
    | ndb_index_stat_enable                | ON                                    |
    | ndb_index_stat_option                | loop_enable=1000ms,loop_idle=1000ms,
    loop_busy=100ms,update_batch=1,read_batch=4,idle_batch=32,check_batch=8,
    check_delay=10m,delete_batch=8,clean_delay=1m,error_batch=4,error_delay=1m,
    evict_batch=8,evict_delay=1m,cache_limit=32M,cache_lowpct=90,zero_total=0      |
    | ndb_join_pushdown                    | ON                                    |
    | ndb_log_apply_status                 | OFF                                   |
    | ndb_log_bin                          | OFF                                   |
    | ndb_log_binlog_index                 | ON                                    |
    | ndb_log_empty_epochs                 | OFF                                   |
    | ndb_log_empty_update                 | OFF                                   |
    | ndb_log_exclusive_reads              | OFF                                   |
    | ndb_log_orig                         | OFF                                   |
    | ndb_log_transaction_id               | OFF                                   |
    | ndb_log_update_as_write              | ON                                    |
    | ndb_log_update_minimal               | OFF                                   |
    | ndb_log_updated_only                 | ON                                    |
    | ndb_metadata_check                   | ON                                    |
    | ndb_metadata_check_interval          | 60                                    |
    | ndb_metadata_sync                    | OFF                                   |
    | ndb_mgmd_host                        | 127.0.0.1                             |
    | ndb_nodeid                           | 0                                     |
    | ndb_optimization_delay               | 10                                    |
    | ndb_optimized_node_selection         | 3                                     |
    | ndb_read_backup                      | ON                                    |
    | ndb_recv_thread_activation_threshold | 8                                     |
    | ndb_recv_thread_cpu_mask             |                                       |
    | ndb_report_thresh_binlog_epoch_slip  | 10                                    |
    | ndb_report_thresh_binlog_mem_usage   | 10                                    |
    | ndb_row_checksum                     | 1                                     |
    | ndb_schema_dist_lock_wait_timeout    | 30                                    |
    | ndb_schema_dist_timeout              | 120                                   |
    | ndb_schema_dist_upgrade_allowed      | ON                                    |
    | ndb_show_foreign_key_mock_tables     | OFF                                   |
    | ndb_slave_conflict_role              | NONE                                  |
    | ndb_table_no_logging                 | OFF                                   |
    | ndb_table_temporary                  | OFF                                   |
    | ndb_use_copying_alter_table          | OFF                                   |
    | ndb_use_exact_count                  | OFF                                   |
    | ndb_use_transactions                 | ON                                    |
    | ndb_version                          | 524308                                |
    | ndb_version_string                   | ndb-8.0.35                            |
    | ndb_wait_connected                   | 30                                    |
    | ndb_wait_setup                       | 30                                    |
    | ndbinfo_database                     | ndbinfo                               |
    | ndbinfo_max_bytes                    | 0                                     |
    | ndbinfo_max_rows                     | 10                                    |
    | ndbinfo_offline                      | OFF                                   |
    | ndbinfo_show_hidden                  | OFF                                   |
    | ndbinfo_table_prefix                 | ndb$                                  |
    | ndbinfo_version                      | 524308                                |
    +--------------------------------------+---------------------------------------+
    

    SHOW VARIABLES语句不同,可以选择单独的列。例如:

    mysql> SELECT VARIABLE_VALUE 
     ->   FROM performance_schema.global_variables
     ->   WHERE VARIABLE_NAME = 'ndb_force_send';
    +----------------+
    | VARIABLE_VALUE |
    +----------------+
    | ON             |
    +----------------+
    

    一个更有用的查询如下所示:

    mysql> SELECT VARIABLE_NAME AS Name, VARIABLE_VALUE AS Value
         >   FROM performance_schema.global_variables
         >   WHERE VARIABLE_NAME
         >     IN ('version', 'ndb_version',
         >       'ndb_version_string', 'ndbinfo_version');
    
    +--------------------+----------------+
    | Name               | Value          |
    +--------------------+----------------+
    | ndb_version        | 524317         |
    | ndb_version_string | ndb-8.0.29     |
    | ndbinfo_version    | 524317         |
    | version            | 8.0.29-cluster |
    +--------------------+----------------+
    4 rows in set (0.00 sec)
    

    更多信息,请参见第 29.12.15 节,“性能模式状态变量表”,以及第 7.1.8 节,“服务器系统变量”。

  • SHOW STATUS LIKE 'NDB%'

    这个语句一目了然地显示了 MySQL 服务器是否作为集群 SQL 节点运行,如果是的话,它提供了 MySQL 服务器的集群节点 ID、连接的集群管理服务器的主机名和端口,以及集群中的数据节点数量,如下所示:

    mysql> SHOW STATUS LIKE 'NDB%';
    +----------------------------------------------+-------------------------------+
    | Variable_name                                | Value                         |
    +----------------------------------------------+-------------------------------+
    | Ndb_metadata_detected_count                  | 0                             |
    | Ndb_cluster_node_id                          | 100                           |
    | Ndb_config_from_host                         | 127.0.0.1                     |
    | Ndb_config_from_port                         | 1186                          |
    | Ndb_number_of_data_nodes                     | 2                             |
    | Ndb_number_of_ready_data_nodes               | 2                             |
    | Ndb_connect_count                            | 0                             |
    | Ndb_execute_count                            | 0                             |
    | Ndb_scan_count                               | 0                             |
    | Ndb_pruned_scan_count                        | 0                             |
    | Ndb_schema_locks_count                       | 0                             |
    | Ndb_api_wait_exec_complete_count_session     | 0                             |
    | Ndb_api_wait_scan_result_count_session       | 0                             |
    | Ndb_api_wait_meta_request_count_session      | 1                             |
    | Ndb_api_wait_nanos_count_session             | 163446                        |
    | Ndb_api_bytes_sent_count_session             | 60                            |
    | Ndb_api_bytes_received_count_session         | 28                            |
    | Ndb_api_trans_start_count_session            | 0                             |
    | Ndb_api_trans_commit_count_session           | 0                             |
    | Ndb_api_trans_abort_count_session            | 0                             |
    | Ndb_api_trans_close_count_session            | 0                             |
    | Ndb_api_pk_op_count_session                  | 0                             |
    | Ndb_api_uk_op_count_session                  | 0                             |
    | Ndb_api_table_scan_count_session             | 0                             |
    | Ndb_api_range_scan_count_session             | 0                             |
    | Ndb_api_pruned_scan_count_session            | 0                             |
    | Ndb_api_scan_batch_count_session             | 0                             |
    | Ndb_api_read_row_count_session               | 0                             |
    | Ndb_api_trans_local_read_row_count_session   | 0                             |
    | Ndb_api_adaptive_send_forced_count_session   | 0                             |
    | Ndb_api_adaptive_send_unforced_count_session | 0                             |
    | Ndb_api_adaptive_send_deferred_count_session | 0                             |
    | Ndb_trans_hint_count_session                 | 0                             |
    | Ndb_sorted_scan_count                        | 0                             |
    | Ndb_pushed_queries_defined                   | 0                             |
    | Ndb_pushed_queries_dropped                   | 0                             |
    | Ndb_pushed_queries_executed                  | 0                             |
    | Ndb_pushed_reads                             | 0                             |
    | Ndb_last_commit_epoch_server                 | 37632503447571                |
    | Ndb_last_commit_epoch_session                | 0                             |
    | Ndb_system_name                              | MC_20191126162038             |
    | Ndb_api_event_data_count_injector            | 0                             |
    | Ndb_api_event_nondata_count_injector         | 0                             |
    | Ndb_api_event_bytes_count_injector           | 0                             |
    | Ndb_api_wait_exec_complete_count_slave       | 0                             |
    | Ndb_api_wait_scan_result_count_slave         | 0                             |
    | Ndb_api_wait_meta_request_count_slave        | 0                             |
    | Ndb_api_wait_nanos_count_slave               | 0                             |
    | Ndb_api_bytes_sent_count_slave               | 0                             |
    | Ndb_api_bytes_received_count_slave           | 0                             |
    | Ndb_api_trans_start_count_slave              | 0                             |
    | Ndb_api_trans_commit_count_slave             | 0                             |
    | Ndb_api_trans_abort_count_slave              | 0                             |
    | Ndb_api_trans_close_count_slave              | 0                             |
    | Ndb_api_pk_op_count_slave                    | 0                             |
    | Ndb_api_uk_op_count_slave                    | 0                             |
    | Ndb_api_table_scan_count_slave               | 0                             |
    | Ndb_api_range_scan_count_slave               | 0                             |
    | Ndb_api_pruned_scan_count_slave              | 0                             |
    | Ndb_api_scan_batch_count_slave               | 0                             |
    | Ndb_api_read_row_count_slave                 | 0                             |
    | Ndb_api_trans_local_read_row_count_slave     | 0                             |
    | Ndb_api_adaptive_send_forced_count_slave     | 0                             |
    | Ndb_api_adaptive_send_unforced_count_slave   | 0                             |
    | Ndb_api_adaptive_send_deferred_count_slave   | 0                             |
    | Ndb_slave_max_replicated_epoch               | 0                             |
    | Ndb_api_wait_exec_complete_count             | 4                             |
    | Ndb_api_wait_scan_result_count               | 7                             |
    | Ndb_api_wait_meta_request_count              | 172                           |
    | Ndb_api_wait_nanos_count                     | 1083548094028                 |
    | Ndb_api_bytes_sent_count                     | 4640                          |
    | Ndb_api_bytes_received_count                 | 109356                        |
    | Ndb_api_trans_start_count                    | 4                             |
    | Ndb_api_trans_commit_count                   | 1                             |
    | Ndb_api_trans_abort_count                    | 1                             |
    | Ndb_api_trans_close_count                    | 4                             |
    | Ndb_api_pk_op_count                          | 2                             |
    | Ndb_api_uk_op_count                          | 0                             |
    | Ndb_api_table_scan_count                     | 1                             |
    | Ndb_api_range_scan_count                     | 1                             |
    | Ndb_api_pruned_scan_count                    | 0                             |
    | Ndb_api_scan_batch_count                     | 1                             |
    | Ndb_api_read_row_count                       | 3                             |
    | Ndb_api_trans_local_read_row_count           | 2                             |
    | Ndb_api_adaptive_send_forced_count           | 1                             |
    | Ndb_api_adaptive_send_unforced_count         | 5                             |
    | Ndb_api_adaptive_send_deferred_count         | 0                             |
    | Ndb_api_event_data_count                     | 0                             |
    | Ndb_api_event_nondata_count                  | 0                             |
    | Ndb_api_event_bytes_count                    | 0                             |
    | Ndb_metadata_excluded_count                  | 0                             |
    | Ndb_metadata_synced_count                    | 0                             |
    | Ndb_conflict_fn_max                          | 0                             |
    | Ndb_conflict_fn_old                          | 0                             |
    | Ndb_conflict_fn_max_del_win                  | 0                             |
    | Ndb_conflict_fn_epoch                        | 0                             |
    | Ndb_conflict_fn_epoch_trans                  | 0                             |
    | Ndb_conflict_fn_epoch2                       | 0                             |
    | Ndb_conflict_fn_epoch2_trans                 | 0                             |
    | Ndb_conflict_trans_row_conflict_count        | 0                             |
    | Ndb_conflict_trans_row_reject_count          | 0                             |
    | Ndb_conflict_trans_reject_count              | 0                             |
    | Ndb_conflict_trans_detect_iter_count         | 0                             |
    | Ndb_conflict_trans_conflict_commit_count     | 0                             |
    | Ndb_conflict_epoch_delete_delete_count       | 0                             |
    | Ndb_conflict_reflected_op_prepare_count      | 0                             |
    | Ndb_conflict_reflected_op_discard_count      | 0                             |
    | Ndb_conflict_refresh_op_count                | 0                             |
    | Ndb_conflict_last_conflict_epoch             | 0                             |
    | Ndb_conflict_last_stable_epoch               | 0                             |
    | Ndb_index_stat_status                        | allow:1,enable:1,busy:0,
    loop:1000,list:(new:0,update:0,read:0,idle:0,check:0,delete:0,error:0,total:0),
    analyze:(queue:0,wait:0),stats:(nostats:0,wait:0),total:(analyze:(all:0,error:0),
    query:(all:0,nostats:0,error:0),event:(act:0,skip:0,miss:0),cache:(refresh:0,
    clean:0,pinned:0,drop:0,evict:0)),cache:(query:0,clean:0,drop:0,evict:0,
    usedpct:0.00,highpct:0.00)                                                     |
    | Ndb_index_stat_cache_query                   | 0                             |
    | Ndb_index_stat_cache_clean                   | 0                             |
    +----------------------------------------------+-------------------------------+
    

    如果 MySQL 服务器是使用NDB支持构建的,但当前未连接到集群,那么此语句的输出中的每一行都包含Value列的零或空字符串。

    参见第 15.7.7.37 节,“SHOW STATUS Statement”。

  • SELECT * FROM performance_schema.global_status WHERE VARIABLE_NAME LIKE 'NDB%'

    这个语句提供了类似于之前讨论的SHOW STATUS语句的输出。与SHOW STATUS不同的是,可以使用SELECT语句提取 SQL 中的数值,用于监控和自动化脚本。

    更多信息,请参见第 29.12.15 节,“性能模式状态变量表”。

  • SELECT * FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'NDB%'

    这个语句显示了与 NDB Cluster 相关的插件的信息,如版本、作者和许可证,如下所示:

    mysql> SELECT * FROM INFORMATION_SCHEMA.PLUGINS
         >     WHERE PLUGIN_NAME LIKE 'NDB%'\G
    *************************** 1\. row ***************************
               PLUGIN_NAME: ndbcluster
            PLUGIN_VERSION: 1.0
             PLUGIN_STATUS: ACTIVE
               PLUGIN_TYPE: STORAGE ENGINE
       PLUGIN_TYPE_VERSION: 80036.0
            PLUGIN_LIBRARY: NULL
    PLUGIN_LIBRARY_VERSION: NULL
             PLUGIN_AUTHOR: Oracle Corporation
        PLUGIN_DESCRIPTION: Clustered, fault-tolerant tables
            PLUGIN_LICENSE: GPL
               LOAD_OPTION: ON
    *************************** 2\. row ***************************
               PLUGIN_NAME: ndbinfo
            PLUGIN_VERSION: 0.1
             PLUGIN_STATUS: ACTIVE
               PLUGIN_TYPE: STORAGE ENGINE
       PLUGIN_TYPE_VERSION: 80036.0
            PLUGIN_LIBRARY: NULL
    PLUGIN_LIBRARY_VERSION: NULL
             PLUGIN_AUTHOR: Oracle Corporation
        PLUGIN_DESCRIPTION: MySQL Cluster system information storage engine
            PLUGIN_LICENSE: GPL
               LOAD_OPTION: ON
    *************************** 3\. row ***************************
               PLUGIN_NAME: ndb_transid_mysql_connection_map
            PLUGIN_VERSION: 0.1
             PLUGIN_STATUS: ACTIVE
               PLUGIN_TYPE: INFORMATION SCHEMA
       PLUGIN_TYPE_VERSION: 80036.0
            PLUGIN_LIBRARY: NULL
    PLUGIN_LIBRARY_VERSION: NULL
             PLUGIN_AUTHOR: Oracle Corporation
        PLUGIN_DESCRIPTION: Map between MySQL connection ID and NDB transaction ID
            PLUGIN_LICENSE: GPL
               LOAD_OPTION: ON
    

    你也可以使用SHOW PLUGINS语句来显示这些信息,但该语句的输出不容易过滤。另请参阅 MySQL 插件 API,其中描述了PLUGINS表中信息的获取位置和方式。

你还可以查询ndbinfo信息数据库中的表,获取关于许多 NDB 集群操作的实时数据。参见 Section 25.6.16, “ndbinfo: The NDB Cluster Information Database”。

25.6.20 NDB Cluster 安全性问题

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-security.html

25.6.20.1 NDB Cluster 安全性和网络问题

25.6.20.2 NDB Cluster 和 MySQL 权限

25.6.20.3 NDB Cluster 和 MySQL 安全程序

这一部分讨论了在设置和运行 NDB Cluster 时需要考虑的安全性问题。

本节涵盖的主题包括以下内容:

  • NDB Cluster 和网络安全问题

  • 与安全运行 NDB Cluster 相关的配置问题

  • NDB Cluster 和 MySQL 权限系统

  • 适用于 NDB Cluster 的 MySQL 标准安全程序

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-security-networking-issues.html

25.6.20.1 NDB Cluster 安全性和网络问题

在本节中,我们讨论与 NDB Cluster 相关的基本网络安全问题。非常重要的是要记住,NDB Cluster“开箱即用”并不安全;您或您的网络管理员必须采取适当措施,确保您的集群在网络上不会受到威胁。

集群通信协议本质上是不安全的,在集群节点之间的通信中不使用加密或类似的安全措施。由于网络速度和延迟直接影响集群的效率,因此不建议在节点之间的网络连接中使用 SSL 或其他加密,因为这些方案会导致通信变慢。

同样,对于控制 API 节点访问 NDB Cluster,也不使用任何身份验证。与加密一样,强加身份验证要求的开销会对集群性能产生不利影响。

另外,在访问集群时,以下两者之一的源 IP 地址没有检查:

  • SQL 或 API 节点使用config.ini文件中空的[mysqld][api]部分创建的“空闲插槽”

    这意味着,如果config.ini文件中有任何空的[mysqld][api]部分,那么任何知道管理服务器主机名(或 IP 地址)和端口的 API 节点(包括 SQL 节点)都可以连接到集群并访问其数据而不受限制。(有关此及相关问题的更多信息,请参阅第 25.6.20.2 节,“NDB Cluster 和 MySQL 权限”。)

    注意

    您可以通过为config.ini文件中所有[mysqld][api]部分指定HostName参数来对 SQL 和 API 节点访问集群进行一些控制。然而,这也意味着,如果您希望从以前未使用的主机连接 API 节点到集群,您需要在config.ini文件中添加一个包含其主机名的[api]部分。

    更多信息可在本章的其他地方找到关于HostName参数的信息。还请参阅第 25.4.1 节,“NDB Cluster 的快速测试设置”,以获取使用HostName与 API 节点的配置示例。

  • 任何ndb_mgm客户端

    这意味着任何被给予管理服务器主机名(或 IP 地址)和端口(如果不是标准端口)的集群管理客户端都可以连接到集群并执行任何管理客户端命令。这包括ALL STOPSHUTDOWN等命令。

出于这些原因,有必要在网络层面保护集群。对于集群来说,最安全的网络配置是将集群节点之间的连接与任何其他网络通信隔离开。可以通过以下任一方法实现这一点:

  1. 将集群节点保持在与任何公共网络物理隔离的网络上。这个选项是最可靠的;然而,实施成本最高。

    我们在这里展示了使用这种物理隔离网络的 NDB 集群设置的示例:

    图 25.7 带有硬件防火墙的 NDB 集群

    内容在周围的文本中描述。

    这种设置有两个网络,一个私有的(实线框)用于集群管理服务器和数据节点,一个公共的(虚线框)用于 SQL 节点所在。 (我们展示了管理和数据节点使用千兆交换机连接,因为这提供了最佳性能。)两个网络都受到硬件防火墙的保护,有时也称为基于网络的防火墙。

    这种网络设置是最安全的,因为没有数据包可以从网络外部到达集群的管理或数据节点,也没有集群的内部通信可以到达外部,除非通过 SQL 节点,只要 SQL 节点不允许任何数据包被转发。当然,这意味着所有 SQL 节点必须受到黑客攻击的保护。

    重要

    关于潜在的安全漏洞,SQL 节点与任何其他 MySQL 服务器没有区别。请参阅第 8.1.3 节,“使 MySQL 免受攻击者攻击”,了解您可以用来保护 MySQL 服务器的技术描述。

  2. 使用一个或多个软件防火墙(也称为主机防火墙)来控制从不需要访问集群的网络部分通过的数据包。在这种设置中,必须在集群中的每台主机上安装软件防火墙,否则可能会从本地网络外部访问。

    基于主机的选项是实施成本最低的,但纯粹依赖软件提供保护,因此最难保持安全。

    这种 NDB 集群的网络设置类型在这里说明:

    图 25.8 带有软件防火墙的 NDB 集群

    内容在周围的文本中描述。

    使用这种类型的网络设置意味着 NDB 集群主机有两个区域。每个集群主机必须能够与集群中的所有其他机器通信,但只有托管 SQL 节点(虚线框)的机器可以与外部进行任何联系,而那些位于包含数据节点和管理节点的区域(实线框)的机器必须与不属于集群的任何机器隔离。使用集群的应用程序和这些应用程序的用户被允许直接访问管理和数据节点主机。

    要实现这一点,您必须设置软件防火墙,根据每个集群主机计算机上运行的节点类型,限制流量到以下表中显示的类型或类型:

    表 25.68 主机防火墙集群配置中的节点类型

    节点类型 允许的流量
    SQL 或 API 节点
    • 它源自管理或数据节点的 IP 地址(使用任何 TCP 或 UDP 端口)。

    • 它源自集群所在网络中,并位于应用程序正在使用的端口上。

    |

    数据节点或管理节点
    • 它源自管理或数据节点的 IP 地址(使用任何 TCP 或 UDP 端口)。

    • 它源自 SQL 或 API 节点的 IP 地址。

    |

    除了给定节点类型表中显示的流量之外的任何流量都应被拒绝。

    配置防火墙的具体细节因防火墙应用程序而异,超出了本手册的范围。iptables是一个非常常见和可靠的防火墙应用程序,通常与APF一起使用作为前端,以使配置更加简单。如果您选择实施此类 NDB 集群网络设置或在下一项中讨论的“混合”类型,您可以(也应该)查阅所使用软件防火墙的文档。

  3. 还可以采用前两种方法的组合,即同时使用网络和主机防火墙来保护集群。这在安全级别和成本方面介于前两种方案之间。这种类型的网络设置将集群置于硬件防火墙后面,但允许传入数据包穿过连接所有集群主机的路由器以到达 SQL 节点。

    一个可能的 NDB 集群网络部署示例,使用硬件和软件防火墙的组合如下所示:

    图 25.9 NDB 集群与硬件和软件防火墙的组合

    内容在周围文本中描述。

    在这种情况下,您可以设置硬件防火墙规则,拒绝除 SQL 节点和 API 节点之外的任何外部流量,然后仅允许它们在应用程序所需的端口上通信。

无论您使用何种网络配置,都要记住,从保持集群安全的角度来看,您的目标始终是一样的——防止任何不必要的流量到达集群,同时确保集群中节点之间的最有效通信。

因为 NDB 集群需要大量端口用于节点之间的通信,推荐的选项是使用隔离网络。这是防止不必要流量到达集群的最简单方式。

注意

如果您希望远程管理 NDB 集群(即从本地网络之外),推荐的方法是使用ssh或其他安全登录 shell 访问 SQL 节点主机。从这个主机上,您可以安全地运行管理客户端,从集群自己的本地网络中访问管理服务器。

即使在理论上可以这样做,也不建议使用ndb_mgm直接从集群所在的本地网络之外管理集群。由于管理客户端和管理服务器之间既没有认证也没有加密,这代表了一种极不安全的管理集群方式,几乎肯定会在早晚被破坏。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-security-mysql-privileges.html

25.6.20.2 NDB Cluster 和 MySQL 特权

在本节中,我们讨论了 MySQL 特权系统在与 NDB Cluster 的关系中的工作原理,以及这对保持 NDB Cluster 安全性的影响。

标准的 MySQL 特权适用于 NDB Cluster 表。这包括在数据库、表和列级别授予的所有 MySQL 特权类型(SELECT特权、UPDATE特权、DELETE特权等),与任何其他 MySQL 服务器一样,用户和特权信息存储在mysql系统数据库中。用于在NDB表、包含这些表的数据库以及这些表中的列上授予和撤销特权的 SQL 语句在所有方面与用于涉及任何(其他)MySQL 存储引擎的数据库对象的GRANTREVOKE语句相同。对于CREATE USERDROP USER语句也是如此。

重要的是要记住,默认情况下,MySQL 授权表使用InnoDB存储引擎。因此,这些表通常不会在充当 NDB Cluster 中 SQL 节点的 MySQL 服务器之间复制或共享。换句话说,默认情况下,用户及其特权的更改不会在 SQL 节点之间自动传播。如果需要,您可以启用 NDB Cluster SQL 节点之间的 MySQL 用户和特权同步;有关详细信息,请参见第 25.6.13 节“特权同步和 NDB_STORED_USER”。

相反,因为 MySQL 中没有拒绝特权的方法(特权可以被撤销或者一开始就不授予,但不能被明确拒绝),因此没有办法在一个 SQL 节点上对NDB表进行特殊保护,使其免受在另一个 SQL 节点上具有特权的用户的影响;即使您没有使用用户特权的自动分发,这也是如此。这一点的明显例子是 MySQL 的root账户,它可以对任何数据库对象执行任何操作。结合config.ini文件中空的[mysqld][api]部分,这个账户可能特别危险。要理解原因,请考虑以下情景:

  • config.ini 文件至少包含一个空的 [mysqld][api] 部分。这意味着 NDB 集群管理服务器不会检查 MySQL 服务器(或其他 API 节点)访问 NDB 集群的主机。

  • 没有防火墙,或者防火墙无法阻止外部主机访问 NDB 集群。

  • NDB 集群管理服务器的主机名或 IP 地址已知或可以从网络外部确定。

如果这些条件成立,那么任何人在任何地方都可以启动一个带有 --ndbcluster --ndb-connectstring=*management_host* 的 MySQL 服务器,并访问这个 NDB 集群。使用 MySQL 的 root 账户,这个人可以执行以下操作:

  • 执行元数据语句,比如SHOW DATABASES语句(获取服务器上所有NDB数据库的列表)或SHOW TABLES FROM *some_ndb_database*语句(获取给定数据库中所有NDB表的列表)

  • 在任何发现的表上运行任何合法的 MySQL 语句,比如:

    • SELECT * FROM *some_table*TABLE *some_table* 从任意表中读取所有数据

    • DELETE FROM *some_table* 或 TRUNCATE TABLE 删除表中的所有数据

    • DESCRIBE *some_table*SHOW CREATE TABLE *some_table* 确定表结构

    • UPDATE *some_table* SET *column1* = *some_value* 用“垃圾”数据填充表列;这实际上可能造成比简单删除所有数据更大的损害

      更加隐匿的变种可能包括以下语句:

      UPDATE *some_table* SET *an_int_column* = *an_int_column* + 1
      

      或者

      UPDATE *some_table* SET *a_varchar_column* = REVERSE(*a_varchar_column*)
      

      这样的恶意语句仅受攻击者想象力的限制。

    唯一安全的表将是使用非 NDB 存储引擎创建的表,因此对于“流氓” SQL 节点不可见。

    一个能够以 root 身份登录的用户也可以访问 INFORMATION_SCHEMA 数据库及其表,从而获取关于存储在 INFORMATION_SCHEMA 中的数据库、表、存储过程、定时事件以及其他数据库对象的信息。

    对于不使用共享权限的情况,最好为不同的 NDB 集群 SQL 节点上的 root 账户使用不同的密码。

总的来说,如果直接从本地网络之外可以访问 NDB 集群,那么你无法拥有一个安全的 NDB 集群。

重要提示

永远不要将 MySQL root 账户密码留空。无论是在作为 NDB 集群 SQL 节点运行 MySQL 还是作为独立(非集群)MySQL 服务器运行时,都是正确的,应该在将 MySQL 服务器配置为 NDB 集群中的 SQL 节点之前作为 MySQL 安装过程的一部分来完成。

如果需要在 SQL 节点之间同步mysql系统表,可以使用标准的 MySQL 复制来实现,或者使用脚本在 MySQL 服务器之间复制表条目。用户及其权限可以使用NDB_STORED_USER特权进行共享和保持同步。

摘要。 关于 MySQL 特权系统与 NDB 集群相关的最重要的要点如下:

  1. 在一个 SQL 节点上建立的用户和权限不会自动存在或生效于集群中的其他 SQL 节点。反之,在集群中的一个 SQL 节点上删除用户或权限并不会从任何其他 SQL 节点中删除用户或权限。

  2. 你可以使用NDB_STORED_USER在 SQL 节点之间共享 MySQL 用户和权限。

  3. 一旦在 NDB 集群中的一个 SQL 节点上为一个 MySQL 用户授予了对NDB表的权限,该用户可以“看到”该表中的任何数据,无论数据来自哪个 SQL 节点,即使该用户没有共享。

译文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-security-mysql-security-procedures.html

25.6.20.3 NDB Cluster 和 MySQL 安全程序

在本节中,我们将讨论 MySQL 标准安全程序,因为它们适用于运行 NDB Cluster。

一般来说,运行 MySQL 安全的任何标准程序也适用于作为 NDB Cluster 的一部分运行 MySQL 服务器。首先,您应始终将 MySQL 服务器作为 mysql 操作系统用户运行;这与在标准(非集群)环境中运行 MySQL 没有区别。mysql 系统帐户应该是唯一且明确定义的。幸运的是,这是新 MySQL 安装的默认行为。您可以使用系统命令(例如下面显示的命令)验证 mysqld 进程是否以 mysql 操作系统用户身份运行:

$> ps aux | grep mysql
root     10467  0.0  0.1   3616  1380 pts/3    S    11:53   0:00 \
  /bin/sh ./mysqld_safe --ndbcluster --ndb-connectstring=localhost:1186
mysql    10512  0.2  2.5  58528 26636 pts/3    Sl   11:53   0:00 \
  /usr/local/mysql/libexec/mysqld --basedir=/usr/local/mysql \
  --datadir=/usr/local/mysql/var --user=mysql --ndbcluster \
  --ndb-connectstring=localhost:1186 --pid-file=/usr/local/mysql/var/mothra.pid \
  --log-error=/usr/local/mysql/var/mothra.err
jon      10579  0.0  0.0   2736   688 pts/0    S+   11:54   0:00 grep mysql

如果 mysqld 进程以除 mysql 之外的任何其他用户身份运行,您应立即关闭它,并以 mysql 用户重新启动它。如果此用户在系统上不存在,则应创建 mysql 用户帐户,并使此用户成为 mysql 用户组的一部分;在这种情况下,您还应确保此系统上的 MySQL 数据目录(使用 --datadir 选项设置为 mysqld)由 mysql 用户拥有,并且 SQL 节点的 my.cnf 文件在 [mysqld] 部分包含 user=mysql。或者,您可以在命令行上使用 --user=mysql 启动 MySQL 服务器进程,但最好使用 my.cnf 选项,因为您可能会忘记使用命令行选项,从而无意中以其他用户身份运行 mysqldmysqld_safe 启动脚本会强制 MySQL 以 mysql 用户身份运行。

重要提示

永远不要以系统根用户身份运行 mysqld。这样做意味着 MySQL 可能读取系统上的任何文件,因此——如果 MySQL 受到攻击——攻击者可能读取任何文件。

如前一节所述(请参阅 Section 25.6.20.2, “NDB Cluster and MySQL Privileges”),您应该在 MySQL 服务器运行后立即设置 root 密码。您还应删除默认安装的匿名用户帐户。您可以使用以下语句��行这些任务:

$> mysql -u root

mysql> UPDATE mysql.user
 ->     SET Password=PASSWORD('*secure_password*')
 ->     WHERE User='root';

mysql> DELETE FROM mysql.user
 ->     WHERE User='';

mysql> FLUSH PRIVILEGES;

在执行DELETE语句时要非常小心,不要忽略WHERE子句,否则会风险删除所有MySQL 用户。一旦修改了mysql.user表,请务必立即运行FLUSH PRIVILEGES语句,以便更改立即生效。没有FLUSH PRIVILEGES,更改将在下次服务器重新启动时才生效。

注意

许多 NDB 集群实用程序,如ndb_show_tablesndb_descndb_select_all也可以在没有身份验证的情况下工作,并且可以显示表名、模式和数据。默认情况下,这些程序在 Unix 风格系统上以权限wxr-xr-x(755)安装,这意味着任何可以访问mysql/bin目录的用户都可以执行它们。

有关这些实用程序的更多信息,请参见第 25.5 节,“NDB 集群程序”。

25.7 NDB 集群复制

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication.html

25.7.1 NDB 集群复制:缩写和符号

25.7.2 NDB 集群复制的一般要求

25.7.3 NDB 集群复制中已知问题

25.7.4 NDB 集群复制模式和表

25.7.5 准备 NDB 集群进行复制

25.7.6 启动 NDB 集群复制(单一复制通道)

25.7.7 使用两个复制通道进行 NDB 集群复制

25.7.8 使用 NDB 集群复制实现故障切换

25.7.9 使用 NDB 集群复制进行 NDB 集群备份

25.7.10 NDB 集群复制:双向和循环复制

25.7.11 使用多线程应用程序的 NDB 集群复制

25.7.12 NDB 集群复制冲突解决

NDB 集群支持异步复制,通常简称为“复制”。本节解释了如何设置和管理一个配置,其中一组计算机作为 NDB 集群操作,复制到第二台计算机或一组计算机。我们假定读者在标准 MySQL 复制方面有一定了解,如本手册的其他地方所述。 (参见 第十九章,复制).

注意

NDB 集群不支持使用 GTID 进行复制;半同步复制和组复制也不受 NDB 存储引擎支持。

正常(非集群)复制涉及源服务器和副本服务器,源被命名为源是因为要复制的操作和数据源自它,而副本是这些操作和数据的接收者。在 NDB Cluster 中,复制在概念上非常相似,但在实践中可能更加复杂,因为它可以扩展到涵盖许多不同的配置,包括在两个完整集群之间进行复制。虽然 NDB Cluster 本身依赖于NDB存储引擎进行集群功能,但不需要使用NDB作为副本的复制表的存储引擎(请参见从 NDB 到其他存储引擎的复制)。然而,为了最大的可用性,可以(并且最好)从一个 NDB Cluster 复制到另一个 NDB Cluster,我们将讨论这种情况,如下图所示:

图 25.10 NDB Cluster 到集群复制布局

![大部分内容在周围的文本中有描述。它展示了 MySQL 源是如何复制的。副本的不同之处在于它显示了一个 I/O(接收器)线程指向一个中继二进制日志,该中继二进制日志指向一个 SQL(应用程序)线程。此外,虽然二进制日志在源服务器上指向和从 NDBCLUSTER 引擎,但在副本上直接指向一个 SQL 节点(MySQL 服务器)。

在这种情况下,复制过程是一个连续记录源集群状态并保存到副本集群的过程。这个过程是通过一个特殊的线程完成的,称为 NDB 二进制日志注入器线程,它在每个 MySQL 服务器上运行并生成一个二进制日志(binlog)。该线程确保将产生二进制日志的集群中的所有更改(而不仅仅是通过 MySQL Server 实现的那些更改)以正确的序列化顺序插入到二进制日志中。我们将 MySQL 源和副本服务器称为复制服务器或复制节点,它们之间的数据流或通信线路称为复制通道。

有关使用 NDB Cluster 和 NDB Cluster Replication 执行时点恢复的信息,请参见 Section 25.7.9.2,“使用 NDB Cluster Replication 进行时点恢复”。

NDB API 复制状态变量。 NDB API 计数器可以提供增强的监控能力,用于监视副本集群。这些计数器实现为 NDB 统计 _slave 状态变量,如在 SHOW STATUS 的输出中所见,或者在连接到充当 NDB 集群复制中的副本的 MySQL 服务器的 mysql 客户端会话中对 Performance Schema session_statusglobal_status 表进行查询的结果中。通过比较执行影响复制的 NDB 表的语句之前和之后这些状态变量的值,您可以观察副本在 NDB API 级别上采取的相应操作,这在监视或故障排除 NDB 集群复制时可能很有用。第 25.6.15 节,“NDB API 统计计数器和变量”提供了额外信息。

从 NDB 复制到非 NDB 表。 可以将 NDB 表从作为复制源的 NDB 集群复制到使用其他 MySQL 存储引擎(如 InnoDBMyISAM)的表上,这些表位于一个副本 mysqld 上。这受到一些条件的限制;请参阅从 NDB 复制到其他存储引擎和从 NDB 复制到非事务性存储引擎以获取更多信息。

25.7.1 NDB 集群复制:缩写和符号

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-abbreviations.html

在本节中,我们使用以下缩写或符号来指代源集群和副本集群,以及在集群或集群节点上运行的进程和命令:

表 25.69 本节中使用的缩写,指的是源集群和副本集群,以及在集群节点上运行的进程和命令

符号或缩写 描述(指的是...)
S 充当(主要)复制源的集群
R 充当(主要)副本的集群
shell*S*> 在源集群上发出的 shell 命令
mysql*S*> 在作为 SQL 节点运行的单个 MySQL 服务器上发出的 MySQL 客户端命令
mysql*S**> 在参与复制源集群的所有 SQL 节点上发出的 MySQL 客户端命令
shell*R*> 在副本集群上发出的 shell 命令
mysql*R*> 在副本集群上作为 SQL 节点运行的单个 MySQL 服务器上发出的 MySQL 客户端命令
mysql*R**> 在参与副本集群的所有 SQL 节点上发出的 MySQL 客户端命令
C 主要复制通道
C' 次要复制通道
S' 次要复制源
R' 次要副本
符号或缩写 描述(指的是...)

25.7.2 NDB Cluster Replication 的一般要求

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-general.html

一个复制通道需要两个充当复制服务器的 MySQL 服务器(分别用于源和副本)。例如,这意味着在具有两个复制通道的复制设置中(为提供冗余的额外通道),应该总共有四个复制节点,每个集群两个。

正如本节和后续节中所述的 NDB Cluster 的复制取决于基于行的复制。这意味着复制源 MySQL 服务器必须使用--binlog-format=ROW--binlog-format=MIXED运行,如 Section 25.7.6, “Starting NDB Cluster Replication (Single Replication Channel)”")中所述。有关基于行的复制的一般信息,请参见 Section 19.2.1, “Replication Formats”。

重要提示

如果您尝试使用--binlog-format=STATEMENT与 NDB Cluster Replication,由于源集群上的ndb_binlog_index表和副本集群上的ndb_apply_status表的epoch列未更新,复制将无法正常工作(参见 Section 25.7.4, “NDB Cluster Replication Schema and Tables”)。相反,只有作为复制源的 MySQL 服务器上的更新会传播到副本,源集群中任何其他 SQL 节点的更新都不会被复制。

--binlog-format选项的默认值为MIXED

在任一集群中用于复制的每个 MySQL 服务器必须在所有参与任一集群的 MySQL 复制服务器中具有唯一标识(源和副本集群上不能共享相同 ID 的复制服务器)。这可以通过使用--server-id=*id*选项启动每个 SQL 节点来完成,其中id是唯一的整数。尽管这并非绝对必要,但我们在本讨论中假设所有 NDB Cluster 二进制文件都是相同版本。

在 MySQL 复制中通常是真实的,涉及的两个 MySQL 服务器(mysqld 进程)必须在复制协议版本和它们支持的 SQL 功能集方面彼此兼容(请参见 Section 19.5.2,“MySQL 版本之间的复制兼容性”)。由于 NDB 集群和 MySQL Server 8.0 发行版之间的二进制文件之间的差异,NDB 集群复制有额外的要求,即两个 mysqld 二进制文件必须来自 NDB 集群发行版。确保 mysqld 服务器兼容的最简单和最容易的方法是对所有源和副本 mysqld 二进制文件使用相同的 NDB 集群发行版。

我们假设副本服务器或集群专用于复制源集群,并且没有其他数据存储在其中。

所有被复制的 NDB 表必须使用 MySQL 服务器和客户端创建。使用 NDB API 创建的表和其他数据库对象(例如,使用 Dictionary::createTable())对 MySQL 服务器不可见,因此不会被复制。通过 NDB API 应用程序对使用 MySQL 服务器创建的现有表的更新可以被复制。

注意

可以使用基于语句的复制来复制 NDB 集群。但是,在这种情况下,以下限制适用:

  • 所有对作为源的集群上数据行的更新必须指向单个 MySQL 服务器。

  • 不可能使用多个同时的 MySQL 复制进程来复制集群。

  • 只有在 SQL 级别进行的更改才会被复制。

这些是基于语句的复制相对于基于行的复制的其他限制;有关两种复制格式之间差异的更具体信息,请参见 Section 19.2.1.1,“基于语句和基于行的复制的优缺点”。

25.7.3 NDB 集群复制中已知问题

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-issues.html

本节讨论在使用 NDB 集群复制时可能遇到的已知问题或问题。

源和复制品之间的连接丢失。 连接丢失可能发生在源集群 SQL 节点和复制集群 SQL 节点之间,也可能发生在源 SQL 节点和源集群的数据节点之间。在后一种情况下,这不仅可能是由于物理连接丢失(例如,网络电缆断开),还可能是由于数据节点事件缓冲区溢出;如果 SQL 节点响应过慢,可能会被集群丢弃(通过调整MaxBufferedEpochsTimeBetweenEpochs配置参数,在一定程度上可以控制这种情况)。如果发生这种情况,完全有可能在源集群中插入新数据而不记录在源 SQL 节点的二进制日志中。因此,为了保证高可用性,非常重要的是维护备份复制通道,监视主要通道,并在必要时切换到次要复制通道,以保持复制集群与源的同步。NDB 集群不设计为自行执行此类监控;为此,需要外部应用程序。

当源 SQL 节点连接或重新连接到源集群时,源 SQL 节点会发出“gap”事件。(“gap”事件是一种“事故事件”的一种,表示发生了影响数据库内容但不容易表示为一组更改的事件。事故的例子包括服务器故障、数据库重新同步、某些软件更新和某些硬件更改。)当复制品在复制日志中遇到间隙时,它会停止并显示错误消息。此消息可在SHOW REPLICA STATUS的输出中找到(在 NDB 8.0.22 之前,请使用SHOW SLAVE STATUS),并指示 SQL 线程由于在复制流中注册的事件而停止,并且需要手动干预。有关在这种情况下应该采取的措施的更多信息,请参见第 25.7.8 节,“使用 NDB 集群复制实现故障转移”。

重要

因为 NDB 集群本身并不设计用于监视复制状态或提供故障转移,如果副本服务器或集群需要高可用性,则必须设置多个复制线路,监视主复制线路上的源mysqld,并准备在必要时切换到次要线路。这必须手动完成,或可能通过第三方应用程序完成。有关实施此类设置的信息,请参见第 25.7.7 节,“使用两个复制通道进行 NDB 集群复制”,以及第 25.7.8 节,“使用 NDB 集群复制实现故障转移”。

如果从独立的 MySQL 服务器复制到 NDB 集群,则通常一个通道就足够了。

循环复制。 NDB 集群复制支持循环复制,如下例所示。复制设置涉及三个 NDB 集群,编号为 1、2 和 3,其中集群 1 充当集群 2 的复制源,集群 2 充当集群 3 的源,集群 3 充当集群 1 的源,从而完成循环。每个 NDB 集群都有两个 SQL 节点,SQL 节点 A 和 B 属于集群 1,SQL 节点 C 和 D 属于集群 2,SQL 节点 E 和 F 属于集群 3。

使用这些集群进行循环复制时,需要满足以下条件:

  • 所有源和副本集群上的 SQL 节点都是相同的。

  • 所有充当源和副本的 SQL 节点都启用了系统变量log_replica_updates(NDB 8.0.26 及更高版本)或log_slave_updates(NDB 8.0.26 之前)。

这种循环复制设置类型如下图所示:

图 25.11 NDB 集群循环复制,所有源均为副本

周围文本描述了一些内容。图表显示了三个集群,每个集群有两个节点。连接不同集群中 SQL 节点的箭头说明所有源也是副本。

在这种情况下,集群 1 中的 SQL 节点 A 复制到集群 2 中的 SQL 节点 C;集群 2 中的 SQL 节点 C 复制到集群 3 中的 SQL 节点 E;SQL 节点 E 复制到 SQL 节点 A。换句话说,复制线路(图表中的曲线箭头表示)直接连接所有用作源和副本的 SQL 节点。

还应该可以设置循环复制,其中并非所有源 SQL 节点也是副本,如下所示:

图 25.12 NDB 集群循环复制,其中并非所有源均为副本

周围文本描述了一些内容。图表显示了三个集群,每个集群有两个节点。连接不同集群中的 SQL 节点的箭头说明并非所有源都是副本。

在这种情况下,每个集群中的不同 SQL 节点被用作源和副本。但是,您应该启用任何 SQL 节点的log_replica_updateslog_slave_updates系统变量。对于 NDB Cluster 的这种循环复制方案,其中复制线(在图中由曲线箭头表示)是不连续的,应该是可能的,但必须注意的是,这种方案尚未经过彻底测试,因此仍然被视为实验性。

注意

NDB 存储引擎使用幂等执行模式,可以抑制重复键和其他错误,否则会破坏 NDB Cluster 的循环复制。这相当于将系统变量replica_exec_modeslave_exec_mode的全局值设置为IDEMPOTENT,尽管在 NDB Cluster 复制中这并不是必需的,因为 NDB Cluster 会自动设置此变量并忽略任何显式设置的尝试。

NDB Cluster 复制和主键。 在节点故障的情况下,复制没有主键的NDB表可能仍会出现错误,因为在这种情况下可能会插入重复行。因此,强烈建议所有被复制的NDB表都有明确的主键。

NDB Cluster 复制和唯一键。 在较旧版本的 NDB Cluster 中,更新NDB表的唯一键列的操作在复制时可能会导致重复键错误。通过将唯一键检查推迟到所有表行更新执行之后,解决了在NDB表之间的复制中的此问题。

目前,只有NDB支持以这种方式推迟约束。因此,当从NDB复制到其他存储引擎(如InnoDBMyISAM)时,仍不支持唯一键的更新。

在没有延迟检查唯一键更新的情况下复制时遇到的问题可以使用 NDB 表,例如 t,在源上创建和填充(并传输到不支持延迟唯一键更新的副本)来说明:

CREATE TABLE t (
    p INT PRIMARY KEY,
    c INT,
    UNIQUE KEY u (c)
)   ENGINE NDB;

INSERT INTO t
    VALUES (1,1), (2,2), (3,3), (4,4), (5,5);

下面的 UPDATE 语句在源上成功,因为受影响的行是按照 ORDER BY 选项确定的顺序处理的,作用于整个表:

UPDATE t SET c = c - 1 ORDER BY p;

相同的语句在副本上失败,因为行更新的顺序是逐个分区执行的,而不是整个表。

注意

每个 NDB 表在创建时都会隐式按键进行分区。有关更多信息,请参见 26.2.5 节“KEY 分区”。

不支持 GTIDs。 使用全局事务 ID 进行复制与 NDB 存储引擎不兼容,也不受支持。启用 GTIDs 可能会导致 NDB Cluster 复制失败。

多线程副本。 以前,NDB Cluster 不支持多线程副本。此限制在 NDB 8.0.33 中已移除。

要在 NDB 8.0.33 及更高版本的副本上启用多线程,需要执行以下步骤:

  1. 在启动源 mysqld 时,将 --ndb-log-transaction-dependency 设置为 ON

  2. 同样在源 mysqld 上,将 binlog_transaction_dependency_tracking 设置为 WRITESET。这可以在 mysqld 进程运行时完成。

  3. 要确保副本使用多个工作线程,将 replica_parallel_workers 的值设置大于 1。默认值为 4,可以在运行时更改副本上的值。

在 NDB 8.0.26 之前,设置任何与多线程副本相关的系统变量,如 replica_parallel_workersslave_parallel_workers,以及 replica_checkpoint_groupslave_checkpoint_group(或等效的 mysqld 启动选项)完全被忽略,没有任何效果。

在 NDB 8.0.27 到 NDB 8.0.32 中,replica_parallel_workers必须设置为 0。在这些版本中,如果在启动时将其设置为其他任何值,NDB会将其更改为 0,并在mysqld服务器日志文件中写入一条消息。这个限制在 NDB 8.0.33 中也被解除。

使用--initial 重新启动。 使用--initial选项重新启动集群会导致 GCI 和时代号的序列从0重新开始。(这通常适用于 NDB 集群,不仅限于涉及集群的复制场景。)在这种情况下,涉及复制的 MySQL 服务器应该重新启动。之后,您应该使用RESET MASTERRESET REPLICA(在 NDB 8.0.22 之前,使用RESET SLAVE)语句清除无效的ndb_binlog_indexndb_apply_status表。

从 NDB 复制到其他存储引擎。 可以将源上的NDB表复制到副本上使用不同存储引擎的表,考虑到这里列出的限制:

  • 不支持多源和循环复制(源和副本上的表必须都使用NDB存储引擎才能正常工作)。

  • 在副本上使用不执行表的二进制日志记录的存储引擎需要特殊处理。

  • 在副本上使用非事务性存储引擎处理表格也需要特殊处理。

  • mysqld必须使用--ndb-log-update-as-write=0--ndb-log-update-as-write=OFF启动。

接下来的几段提供了关于刚才描述的每个问题的额外信息。

不支持将 NDB 复制到其他存储引擎的多源。 对于从NDB到不同存储引擎的复制,两个数据库之间的关系必须是一对一。这意味着 NDB 集群和其他存储引擎之间不支持双向或循环复制。

此外,在从 NDB 复制到不同存储引擎的副本时,不可能配置多个复制通道。 (NDB 集群数据库 可以 同时复制到多个 NDB 集群数据库。)如果源使用 NDB 表,则仍然可以让多个 MySQL 服务器维护所有更改的二进制日志,但是对于副本更改源(故障转移),新的源-副本关系必须在副本上明确定义。

将 NDB 表复制到不执行二进制日志记录的存储引擎。 如果尝试从 NDB 集群复制到使用不处理自己二进制日志的存储引擎的副本,则复制过程将因错误 Binary logging not possible ... Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging (Error 1595) 而中止。可以通过以下一种方式解决此问题:

  • 在副本上关闭二进制日志记录。 可以通过设置 sql_log_bin = 0 来实现。

  • 更改用于 mysql.ndb_apply_status 表的存储引擎。 使该表使用一个不处理自己二进制日志的引擎也可以消除冲突。可以通过在副本上发出类似 ALTER TABLE mysql.ndb_apply_status ENGINE=MyISAM 的语句来完成此操作。在副本上使用除 NDB 之外的存储引擎时,这样做是安全的,因为您不需要担心保持多个副本同步。

  • 在副本上过滤掉对 mysql.ndb_apply_status 表的更改。 可以通过使用 --replicate-ignore-table=mysql.ndb_apply_status 来启动副本来完成此操作。如果需要忽略其他表的复制更改,您可能希望使用适当的 --replicate-wild-ignore-table 选项。

重要提示

在从一个 NDB 集群复制到另一个 NDB 集群时,不应禁用 mysql.ndb_apply_status 的复制或二进制日志记录,也不应更改此表使用的存储引擎。有关详细信息,请参阅 NDB 集群之间复制和二进制日志过滤规则。

从 NDB 复制到非事务性存储引擎。 当从NDB复制到非事务性存储引擎(如MyISAM)时,当复制INSERT ... ON DUPLICATE KEY UPDATE语句时,可能会遇到不必要的重复键错误。您可以通过使用--ndb-log-update-as-write=0来抑制这些错误,该选项强制将更新记录为写入,而不是更新。

NDB 集群之间的复制和二进制日志过滤规则。 如果您使用任何--replicate-do-*--replicate-ignore-*--binlog-do-db--binlog-ignore-db选项来过滤正在复制的数据库或表,您必须注意不要阻止mysql.ndb_apply_status的复制或二进制日志记录,这对于 NDB 集群之间的复制正常运行是必需的。特别是,您必须牢记以下内容:

  1. 使用--replicate-do-db=*db_name*(以及没有其他--replicate-do-*--replicate-ignore-*选项)意味着只有数据库db_name中的表会被复制。在这种情况下,您还应该使用--replicate-do-db=mysql--binlog-do-db=mysql,或--replicate-do-table=mysql.ndb_apply_status来确保在副本上填充mysql.ndb_apply_status

    使用--binlog-do-db=*db_name*(以及没有其他--binlog-do-db选项)意味着只有数据库db_name中的表的更改会被写入二进制日志。在这种情况下,您还应该使用--replicate-do-db=mysql--binlog-do-db=mysql,或--replicate-do-table=mysql.ndb_apply_status来确保在副本上填充mysql.ndb_apply_status

  2. 使用--replicate-ignore-db=mysql意味着mysql数据库中的表不会被复制。在这种情况下,您还应该使用--replicate-do-table=mysql.ndb_apply_status来确保mysql.ndb_apply_status被复制。

    使用--binlog-ignore-db=mysql意味着不会将mysql数据库中的表更改写入二进制日志。在这种情况下,您还应该使用--replicate-do-table=mysql.ndb_apply_status来确保mysql.ndb_apply_status被复制。

您还应该记住,每个复制规则都需要以下内容:

  1. 自己的--replicate-do-*--replicate-ignore-*选项,并且不能在单个复制过滤选项中表示多个规则。有关这些规则的信息,请参见第 19.1.6 节,“复制和二进制日志选项和变量”。

  2. 自己的--binlog-do-db--binlog-ignore-db选项,并且不能在单个二进制日志过滤选项中表示多个规则。有关这些规则的信息,请参见第 7.4.4 节,“二进制日志”。

如果您将 NDB 集群复制到使用NDB之外的存储引擎的副本,则前面提到的考虑可能不适用,如本节其他地方所讨论的。

NDB 集群复制和 IPv6。 从 NDB 8.0.22 开始,所有类型的 NDB 集群节点都支持 IPv6;这包括管理节点、数据节点和 API 或 SQL 节点。

在 NDB 8.0.22 之前,NDB API 和 MGM API(因此数据节点和管理节点)不支持 IPv6,尽管 MySQL 服务器(包括在 NDB 集群中充当 SQL 节点的服务器)可以使用 IPv6 与其他 MySQL 服务器联系。在 8.0.22 之前的 NDB 集群版本中,您可以使用 IPv6 在 SQL 节点之间进行复制,连接源和副本的 SQL 节点如下图中的虚线箭头所示:

图 25.13 SQL 节点之间使用 IPv6 连接的复制

大部分内容在周围的文本中描述。代表 MySQL 到 MySQL 的 IPv6 连接的虚线连接在两个节点之间,分别来自源和副本集群。集群内的所有连接,如数据节点到数据节点或数据节点到管理节点,都使用实线连接表示仅为 IPv4 连接。

在 NDB 8.0.22 之前,所有源自 NDB 集群内部的连接——在前面的图表中用实箭头表示——必须使用 IPv4。换句话说,所有 NDB 集群数据节点、管理服务器和管理客户端必须能够使用 IPv4 相互访问。此外,SQL 节点必须使用 IPv4 与集群通信。在 NDB 8.0.22 及更高版本中,这些限制不再适用;此外,任何使用 NDB 和 MGM API 编写的应用程序都可以假定在仅使用 IPv6 的环境中编写和部署。

注意

在版本 8.0.22 至 8.0.33 中,NDB需要系统支持 IPv6 才能运行,无论集群是否实际使用任何 IPv6 地址。在 NDB Cluster 8.0.34 及更高版本中,这不再是问题,如果集群未使用 IPv6 寻址,则可以在 Linux 内核中禁用 IPv6。

属性提升和降级。 NDB 集群复制包括对属性提升和降级的支持。后者的实现区分了有损和无损类型转换,并且可以通过设置系统变量replica_type_conversions(NDB 8.0.26 及更高版本)或slave_type_conversions(NDB 8.0.26 之前)的全局值来控制在副本上的使用。

有关 NDB 集群中属性提升和降级的更多信息,请参阅基于行的复制:属性提升和降级。

NDB,与InnoDBMyISAM不同,不会将对虚拟列的更改写入二进制日志;然而,这对于 NDB 集群复制或NDB与其他存储引擎之间的复制没有不利影响。存储生成列的更改会被记录。

25.7.4 NDB 集群复制模式和表

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-schema.html

NDB 集群中的复制利用了在充当被复制集群和副本中的 SQL 节点的每个 MySQL 服务器实例上的mysql数据库中的一些专用表。无论副本是单个服务器还是集群,这都是正确的。

ndb_binlog_indexndb_apply_status 表在 mysql 数据库中创建。用户不应明确复制这些表。通常不需要用户干预来创建或维护这两个表,因为两者都由NDB二进制日志(binlog)注入器线程维护。这使得源mysqld进程更新到由NDB存储引擎执行的更改。NDB binlog 注入器线程直接从NDB存储引擎接收事件。NDB注入器负责捕获集群中的所有数据事件,并确保所有更改、插入或删除数据的事件都记录在ndb_binlog_index表中。复制 I/O(接收器)线程将事件从源的二进制日志传输到复制的中继日志。

ndb_replication 表必须手动创建。用户可以更新此表以按数据库或表进行过滤。有关更多信息,请参见ndb_replication 表ndb_replication 也用于 NDB 复制冲突检测和解决冲突控制;请参见冲突解决控制

即使ndb_binlog_indexndb_apply_status是自动创建和维护的,建议在为复制准备 NDB 集群时,首先检查这些表的存在和完整性。可以通过直接在源上查询mysql.ndb_binlog_index表来查看二进制日志中记录的事件数据。这也可以通过在源或副本 SQL 节点上使用SHOW BINLOG EVENTS语句来完成。 (参见第 15.7.7.2 节,“SHOW BINLOG EVENTS Statement”.)

您还可以从SHOW ENGINE NDB STATUS的输出中获取有用信息。

注意

在对NDB表执行模式更改时,应用程序应等到发出该语句的 MySQL 客户端连接中返回ALTER TABLE语句后,再尝试使用表的更新定义。

ndb_apply_status 表

ndb_apply_status用于记录已从源复制到副本的操作。如果副本上不存在ndb_apply_status表,ndb_restore会重新创建它。

ndb_binlog_index不同,此表中的数据不针对(副本)集群中的任何一个 SQL 节点,因此ndb_apply_status可以使用NDBCLUSTER存储引擎,如下所示:

CREATE TABLE `ndb_apply_status` (
    `server_id`   INT(10) UNSIGNED NOT NULL,
    `epoch`       BIGINT(20) UNSIGNED NOT NULL,
    `log_name`    VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
    `start_pos`   BIGINT(20) UNSIGNED NOT NULL,
    `end_pos`     BIGINT(20) UNSIGNED NOT NULL,
    PRIMARY KEY (`server_id`) USING HASH
) ENGINE=NDBCLUSTER DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

ndb_apply_status表仅在副本上填充,这意味着在源上,此表永远不包含任何行;因此,在那里不需要为ndb_apply_status分配任何DataMemory

由于此表是从源头数据填充的,因此应允许其进行复制;任何无意中阻止副本更新ndb_apply_status或阻止源写入二进制日志的复制过滤或二进制日志过滤规则可能会阻止集群之间的复制正常运行。有关此类过滤规则可能引起的潜在问题的更多信息,请参阅在 NDB 集群之间进行复制时的复制和二进制日志过滤规则。

可以删除此表,但不建议这样做。删除它会使所有 SQL 节点进入只读模式;在 NDB 8.0.24 及更高版本中,NDB 检测到该表已被删除,并重新创建它,之后再次执行更新是可能的。删除并重新创建 ndb_apply_status 会在二进制日志中创建一个间隙事件;间隙事件会导致副本 SQL 节点停止应用来自源的更改,直到重新启动复制通道。在 NDB 8.0.24 之前,在这种情况下,需要重新启动所有 SQL 节点以使它们退出只读模式,然后手动重新创建 ndb_apply_status

该表的 epoch 列中的 0 表示来自于 NDB 以外的存储引擎的事务。

ndb_apply_status 用于记录从上游源复制并应用到副本集群的哪些时代事务。这些信息在 NDB 在线备份中被捕获,但(按设计)不会被 ndb_restore 恢复。在某些情况下,恢复这些信息以供新设置使用可能会有所帮助;从 NDB 8.0.29 开始,您可以通过使用 --with-apply-status 选项调用 ndb_restore 来实现这一点。有关该选项的更多信息,请参阅选项的描述。

ndb_binlog_index 表

NDB 集群复制使用 ndb_binlog_index 表来存储二进制日志的索引数据。由于该表对每个 MySQL 服务器都是本地的,并且不参与集群化,因此它使用 InnoDB 存储引擎。这意味着必须在参与源集群的每个 mysqld 上单独创建该表。(二进制日志本身包含来自集群中所有 MySQL 服务器的更新。)该表定义如下:

CREATE TABLE `ndb_binlog_index` (
    `Position` BIGINT(20) UNSIGNED NOT NULL,
    `File` VARCHAR(255) NOT NULL,
    `epoch` BIGINT(20) UNSIGNED NOT NULL,
    `inserts` INT(10) UNSIGNED NOT NULL,
    `updates` INT(10) UNSIGNED NOT NULL,
    `deletes` INT(10) UNSIGNED NOT NULL,
    `schemaops` INT(10) UNSIGNED NOT NULL,
    `orig_server_id` INT(10) UNSIGNED NOT NULL,
    `orig_epoch` BIGINT(20) UNSIGNED NOT NULL,
    `gci` INT(10) UNSIGNED NOT NULL,
    `next_position` bigint(20) unsigned NOT NULL,
    `next_file` varchar(255) NOT NULL,
    PRIMARY KEY (`epoch`,`orig_server_id`,`orig_epoch`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

注意

如果您正在从旧版本(NDB 7.5.2 之前)升级,请执行 MySQL 升级过程,并确保通过使用 --upgrade=FORCE 选项启动 MySQL 服务器来升级系统表。系统表升级会导致为该表执行 ALTER TABLE ... ENGINE=INNODB 语句。继续支持使用 MyISAM 存储引擎以实现向后兼容性。

ndb_binlog_index 转换为 InnoDB 后可能需要额外的磁盘空间。如果这成为问题,您可以通过使用 InnoDB 表空间来为这个表节省空间,将其 ROW_FORMAT 更改为 COMPRESSED,或两者兼而有之。有关更多信息,请参见 Section 15.1.21, “CREATE TABLESPACE Statement”,以及 Section 15.1.20, “CREATE TABLE Statement”,以及 Section 17.6.3, “Tablespaces”。

ndb_binlog_index 表的大小取决于每个二进制日志文件的时代数和二进制日志文件的数量。每个二进制日志文件的时代数通常取决于每个时代生成的二进制日志量和二进制日志文件的大小,较小的时代会导致每个文件有更多的时代。需要注意的是,即使 --ndb-log-empty-epochs 选项为 OFF,空时代也会向 ndb_binlog_index 表插入记录,这意味着每个文件的条目数量取决于文件的使用时间;这种关系可以用下面的公式表示:

[number of epochs per file] = [time spent per file] / TimeBetweenEpochs

繁忙的 NDB Cluster 定期写入二进制日志,并且可能比安静的集群更快地轮换二进制日志文件。这意味着一个带有 --ndb-log-empty-epochs=ON 的“安静” NDB Cluster 实际上可能比活动量大的集群每个文件有更多的 ndb_binlog_index 行。

当使用 --ndb-log-orig 选项启动 mysqld 时,orig_server_idorig_epoch 列分别存储事件起源服务器的 ID 和事件发生的时代,这在使用多个源的 NDB Cluster 复制设置中非常有用。用于在多源设置中找到最接近副本上最高应用时代的二进制日志位置的 SELECT 语句(参见 Section 25.7.10, “NDB Cluster Replication: Bidirectional and Circular Replication”)使用这两列,这两列没有索引。当尝试执行故障切换时,这可能导致性能问题,因为查询必须执行表扫描,特别是当源使用 --ndb-log-empty-epochs=ON 运行时。您可以通过为这些列添加索引来提高多源故障切换时间,如下所示:

ALTER TABLE mysql.ndb_binlog_index
    ADD INDEX orig_lookup USING BTREE (orig_server_id, orig_epoch);

在从单个源到单个副本的复制时,添加此索引不会带来任何好处,因为在这种情况下用于获取二进制日志位置的查询不使用 orig_server_idorig_epoch

有关使用 next_positionnext_file 列的更多信息,请参见第 25.7.8 节,“使用 NDB 集群复制实现故障切换”。

以下图显示了 NDB 集群复制源服务器、其二进制日志注入器线程和 mysql.ndb_binlog_index 表之间的关系。

图 25.14 复制源集群

大多数概念在周围的文本中有描述。这个复杂的图像有三个主要区域。左上角的区域分为三个部分:MySQL 服务器(mysqld)、NDBCLUSTER 表处理程序和互斥体。连接线程连接这些部分,接收器和注入器线程连接 NDBCLUSTER 表处理程序和互斥体。底部区域显示四个数据节点(ndbd)。它们都产生由箭头表示的事件,指向接收器线程,接收器线程也指向连接线程和注入器线程。一个节点发送和接收到互斥区域。表示注入器线程的箭头指向二进制日志以及在周围文本中描述的 ndb_binlog_index 表。

ndb_replication 表

ndb_replication 表用于控制二进制日志记录和冲突解决,并且是基于每个表的基础上操作的。该表中的每一行对应于被复制的表,确定如何记录对表的更改,并且如果指定了冲突解决函数,则确定如何解决该表的冲突。

ndb_apply_statusndb_replication 表不同,ndb_replication 表必须手动创建,使用此处显示的 SQL 语句:

CREATE TABLE mysql.ndb_replication  (
    db VARBINARY(63),
    table_name VARBINARY(63),
    server_id INT UNSIGNED,
    binlog_type INT UNSIGNED,
    conflict_fn VARBINARY(128),
    PRIMARY KEY USING HASH (db, table_name, server_id)
)   ENGINE=NDB
PARTITION BY KEY(db,table_name);

此表的列在此处列出,并附有描述:

  • db

    包含要复制表的数据库的名称���

    您可以在数据库名称中使用下划线 _ 和百分号 % 中的一个或两个通配符。(请参见使用通配符匹配,本节后面有更多信息。)

  • table_name

    要复制的表的名称。

    表名可以包含下划线 _ 和百分号 % 中的一个或两个通配符。请参见使用通配符匹配,本节后面有更多信息。

  • server_id

    MySQL 实例(SQL 节点)的唯一服务器 ID,表所在的位置。

    此列中的 0 充当通配符,相当于 %,匹配任何服务器 ID。(请参见使用通配符匹配,本节后面有更多信息。)

  • binlog_type

    要使用的二进制日志记录类型。有关值和描述,请参阅文本。

  • conflict_fn

    要应用的冲突解决函数之一是 NDB\(OLD()"),NDB\)MAX()"),NDB\(MAX_DELETE_WIN()"),NDB\)EPOCH()"),NDB\(EPOCH_TRANS()"),NDB\)EPOCH2()"),NDB\(EPOCH2_TRANS()")中的一个;`NULL`表示此表不使用冲突解决。NDB 8.0.30 及更高版本支持两个额外的冲突解决函数 NDB\)MAX_INS()")和 NDB$MAX_DEL_WIN_INS()")。

    有关这些函数及其在 NDB 复制冲突解决中的用途的更多信息,请参见 Conflict Resolution Functions。

    一些冲突解决函数(NDB$OLD()NDB$EPOCH()NDB$EPOCH_TRANS())需要使用一个或多个用户创建的异常表。请参见 Conflict Resolution Exceptions Table。

要启用与 NDB 复制的冲突解决,需要在 SQL 节点或应解决冲突的节点上创建并填充此表,其中包含有关控制信息。根据要使用的冲突解决类型和方法,这可能是源、副本或两个服务器。在简单的源-副本设置中,数据也可以在副本上本地更改,这通常是副本。在更复杂的复制方案中,例如双向复制,这通常是所有涉及的源。有关更多信息,请参见 Section 25.7.12,“NDB Cluster Replication Conflict Resolution”。

ndb_replication表允许在冲突解决范围之外对二进制日志进行表级控制,在这种情况下,conflict_fn指定为NULL,而其余列值用于控制给定表或与通配符表达式匹配的一组表的二进制日志。通过为binlog_type列设置适当的值,您可以使给定表或表使用所需的二进制日志格式进行记录,或完全禁用二进制日志记录。此列的可能值及其描述如下表所示:

表 25.70 binlog_type 值及其描述

描述
0 使用服务器默认值
1 不在二进制日志中记录此表(与sql_log_bin = 0效果相同,但仅适用于一个或多个指定表)
2 仅记录更新的属性;将其记录为WRITE_ROW事件
3 记录完整行,即使未更新(MySQL 服务器默认行为)
6 使用更新的属性,即使值未更改
7 记录完整行,即使没有值更改;将更新记录为UPDATE_ROW事件
8 将更新记录为UPDATE_ROW;仅记录主键列的前图像,以及更新后图像中的列(与--ndb-log-update-minimal效果相同,但仅适用于一个或多个指定表)
9 将更新记录为UPDATE_ROW;仅记录主键列的前图像,以及除主键列之外的所有列的后图像

注意

binlog_type值 4 和 5 未被使用,因此未包含在刚刚显示的表中,也未包含在下一个表中。

几个binlog_type值等同于各种组合的mysqld日志选项--ndb-log-updated-only--ndb-log-update-as-write,和--ndb-log-update-minimal,如下表所示:

表 25.71 binlog_type 值与 NDB 日志选项等效组合

--ndb-log-updated-only --ndb-log-update-as-write --ndb-log-update-minimal
0 -- -- --
1 -- -- --
2
3
6
7
8
9

通过向 ndb_replication 表中插入行,可以为不同的表设置不同的二进制日志记录格式,使用适当的 dbtable_namebinlog_type 列值。在设置二进制日志记录格式时,应使用前面表中显示的内部整数值。以下两个语句将二进制日志记录设置为对表 test.a 进行完整行记录(值为 3),以及对表 test.b 仅记录更新(值为 2):

# Table test.a: Log full rows
INSERT INTO mysql.ndb_replication VALUES("test", "a", 0, 3, NULL);

# Table test.b: log updates only
INSERT INTO mysql.ndb_replication VALUES("test", "b", 0, 2, NULL);

要禁用一个或多个表的日志记录,请使用 binlog_type 为 1,如下所示:

# Disable binary logging for table test.t1
INSERT INTO mysql.ndb_replication VALUES("test", "t1", 0, 1, NULL);

# Disable binary logging for any table in 'test' whose name begins with 't'
INSERT INTO mysql.ndb_replication VALUES("test", "t%", 0, 1, NULL);

为给定表禁用日志记录相当于设置sql_log_bin = 0,只是它适用于一个或多个表。如果 SQL 节点不为给定表执行二进制日志记录,则不会发送这些表的行更改事件。这意味着它不是接收所有更改并丢弃一些,而是不订阅这些更改。

禁用日志记录可能有多种原因,包括以下列出的原因:

  • 不将更改发送到网络通常可以节省带宽、缓冲和 CPU 资源。

  • 不记录对具有非常频繁更新但价值不大的表的更改非常适合瞬态数据(例如会话数据),在集群完全失败的情况下可能相对不重要。

  • 使用会话变量(或 sql_log_bin)和应用程序代码,还可以记录(或不记录)某些 SQL 语句或类型的 SQL 语句;例如,在某些情况下,可能不希望记录一个或多个表上的 DDL 语句。

  • 将复制流拆分为两个(或更多)二进制日志文件可以出于性能、需要将不同数据库复制到不同位置、使用不同的二进制日志记录类型等原因进行操作。

使用通配符进行匹配。 为了不必为复制设置中每个数据库、表和 SQL 节点的每个组合插入一行到 ndb_replication 表中,NDB 支持在此表的 dbtable_nameserver_id 列上进行通配符匹配。在 dbtable_name 中使用的数据库和表名可以包含以下通配符中的一个或两个:

  • _(下划线字符):匹配零个或多个字符

  • %(百分号):匹配一个字符

(这些是 MySQL LIKE 操作符支持的相同通配符。)

server_id 列支持 0 作为 _ 的通配符等效(匹配任何内容)。这在前面显示的示例中使用。

ndb_replication表中的给定行可以使用通配符来匹配任何数据库名称、表名称和服务器 ID 的任意组合。在表中存在多个潜在匹配项的情况下,根据此处显示的表,选择最佳匹配项,其中W表示通配符匹配,E表示精确匹配,Quality列中的值越大,匹配越好:

表 25.72 不同列的通配符和精确匹配组合的权重

db table_name server_id 质量
W W W 1
W W E 2
W E W 3
W E E 4
E W W 5
E W E 6
E E W 7
E E E 8

因此,数据库名称、表名称和服务器 ID 的精确匹配被认为是最佳(最强),而三个列的通配符匹配被认为是最弱(最差)。在选择应用哪个规则时,只考虑匹配的强度;表中行的顺序对此决定没有影响。

记录完整或部分行。 有两种基本记录行的方法,由--ndb-log-updated-only选项设置为mysqld决定:

  • 记录完整行(选项设置为ON)

  • 仅记录已更新的列数据,即已设置值的列数据,无论该值是否实际更改。这是默认行为(选项设置为OFF)。

通常仅记录已更新的列就足够且更有效;但是,如果需要记录完整行,可以通过将--ndb-log-updated-only设置为0OFF来实现。

将更改的数据记录为更新。 MySQL 服务器的--ndb-log-update-as-write选项的设置确定是否执行记录时是否包含“之前”图像。

因为更新和删除操作的冲突解决是在 MySQL 服务器的更新处理程序中完成的,所以有必要控制复制源执行的日志记录,使更新为更新而不是写入;也就是说,使更新被视为现有行的更改,而不是写入新行,即使这些行替换了现有行。

此选项默认为打开;换句话说,更新被视为写入。也就是说,默认情况下,更新被写入二进制日志中的write_row事件,而不是update_row事件。

要禁用该选项,请使用--ndb-log-update-as-write=0--ndb-log-update-as-write=OFF启动源mysqld。当从 NDB 表复制到使用不同存储引擎的表时,您必须执行此操作;请参阅从 NDB 复制到其他存储引擎和从 NDB 复制到非事务性存储引擎以获取更多信息。

重要提示

(NDB 8.0.30 及更高版本😃 对于使用NDB$MAX_INS()NDB$MAX_DEL_WIN_INS()进行插入冲突解决的情况,一个 SQL 节点(即一个mysqld进程)可以记录源集群上的行更新为WRITE_ROW事件,并启用--ndb-log-update-as-write选项以确保幂等性和最佳大小。这对这些算法有效,因为它们都将WRITE_ROW事件映射到插入或更新,具体取决于行是否已经存在,并且所需的元数据(时间戳列的“后”图像)存在于WRITE_ROW事件中。

25.7.5 准备 NDB 集群进行复制

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-preparation.html

准备 NDB 集群进行复制包括以下步骤:

  1. 检查所有 MySQL 服务器的版本兼容性(参见第 25.7.2 节,“NDB 集群复制的一般要求”)。

  2. 在源集群上创建一个具有适当权限的复制帐户,使用以下两个 SQL 语句:

    mysql*S*> CREATE USER '*replica_user*'@'*replica_host*'
     -> IDENTIFIED BY '*replica_password*';
    
    mysql*S*> GRANT REPLICATION SLAVE ON *.*
     -> TO '*replica_user*'@'*replica_host*';
    

    在上一条语句中,replica_user是复制帐户用户名,replica_host是复制的主机名或 IP 地址,replica_password是要分配给此帐户的密码。

    例如,要创建一个名为myreplica的复制用户帐户,从名为replica-host的主机登录,并使用密码53cr37,请使用以下CREATE USERGRANT语句:

    mysql*S*> CREATE USER 'myreplica'@'replica-host'
     -> IDENTIFIED BY '53cr37';
    
    mysql*S*> GRANT REPLICATION SLAVE ON *.*
     -> TO 'myreplica'@'replica-host';
    

    出于安全原因,最好使用一个唯一的用户帐户—不用于任何其他目的—用于复制帐户。

  3. 设置复制使用源。使用mysql客户端,可以通过CHANGE REPLICATION SOURCE TO语句(从 NDB 8.0.23 开始)或CHANGE MASTER TO语句(在 NDB 8.0.23 之前)来实现:

    mysql*R*> CHANGE MASTER TO
     -> MASTER_HOST='*source_host*',
     -> MASTER_PORT=*source_port*,
     -> MASTER_USER='*replica_user*',
     -> MASTER_PASSWORD='*replica_password*';
    

    从 NDB 8.0.23 开始,您还可以使用以下语句:

    mysql*R*> CHANGE REPLICATION SOURCE TO
     -> SOURCE_HOST='*source_host*',
     -> SOURCE_PORT=*source_port*,
     -> SOURCE_USER='*replica_user*',
     -> SOURCE_PASSWORD='*replica_password*';
    

    在上一条语句中,source_host是复制源的主机名或 IP 地址,source_port是复制连接到源时复制使用的端口,replica_user是在源上为复制设置的用户名,replica_password是在上一步中为该用户帐户设置的密码。

    例如,要告诉复制使用在上一步中创建的具有复制帐户的 MySQL 服务器,其主机名为rep-source,请使用以下语句:

    mysql*R*> CHANGE MASTER TO
     -> MASTER_HOST='rep-source',
     -> MASTER_PORT=3306,
     -> MASTER_USER='myreplica',
     -> MASTER_PASSWORD='53cr37';
    

    从 NDB 8.0.23 开始,您还可以使用以下语句:

    mysql*R*> CHANGE REPLICATION SOURCE TO
     -> SOURCE_HOST='rep-source',
     -> SOURCE_PORT=3306,
     -> SOURCE_USER='myreplica',
     -> SOURCE_PASSWORD='53cr37';
    

    要查看可与此语句一起使用的选项的完整列表,请参见第 15.4.2.1 节,“CHANGE MASTER TO 语句”。

    为了提供复制备份功能,还需要在开始复制过程之前向复制的my.cnf文件中添加一个--ndb-connectstring选项。有关详细信息,请参见第 25.7.9 节,“NDB 集群复制的 NDB 集群备份”。

    有关副本可以在my.cnf中设置的其他选项,请参见第 19.1.6 节,“复制和二进制日志选项和变量”。

  4. 如果源集群已在使用中,则可以创建源的备份并将其加载到副本中,以减少副本与源同步所需的时间。如果副本也在运行 NDB 集群,则可以使用第 25.7.9 节,“NDB 集群复制的 NDB 集群备份”中描述的备份和恢复过程来实现这一点。

    ndb-connectstring=*management_host*[:*port*]
    

    如果在副本上使用 NDB 集群,则可以使用以下命令在源上创建备份:

    shell*S*> mysqldump --master-data=1
    

    然后通过将转储文件复制到副本上导入生成的数据转储。之后,您可以使用mysql客户端将数据从转储文件导入到副本数据库中,如下所示,其中dump_file是使用mysqldump在源上生成的文件的名称,db_name是要复制的数据库的名称:

    shell*R*> mysql -u root -p *db_name* < *dump_file*
    

    要查看与mysqldump一起使用的完整选项列表,请参见第 6.5.4 节,“mysqldump — A Database Backup Program”。

    注意

    如果您以这种方式将数据复制到副本中,请确保在所有数据加载完成之前停止副本尝试连接到源以开始复制。您可以通过在命令行上使用--skip-slave-start选项启动副本,通过在副本的my.cnf文件中包含skip-slave-start,或者从 NDB 8.0.24 开始,通过设置skip_slave_start系统变量来实现这一点。从 NDB 8.0.26 开始,使用--skip-replica-startskip_replica_start。一旦数据加载完成,请按照下面两节中概述的额外步骤进行操作。

  5. 确保每个充当复制源的 MySQL 服务器都分配了唯一的服务器 ID,并启用了二进制日志记录,使用基于行的格式。(参见 Section 19.2.1, “Replication Formats”.)此外,我们强烈建议启用 replica_allow_batching 系统变量(NDB 8.0.26 及更高版本;在 NDB 8.0.26 之前,请使用 slave_allow_batching)。从 NDB 8.0.30 开始,默认情况下已启用此功能。

    如果您使用的是 NDB Cluster 8.0.30 之前的版本,您还应考虑增加与 --ndb-batch-size--ndb-blob-write-batch-bytes 选项一起使用的值。在 NDB 8.0.30 及更高版本中,请使用 --ndb-replica-batch-size 来设置副本上用于写入的批量大小,而不是 --ndb-batch-size,以及使用 --ndb-replica-blob-write-batch-bytes 而不是 --ndb-blob-write-batch-bytes 来确定复制应用程序用于写入 blob 数据的批量大小。所有这些选项都可以在源服务器的 my.cnf 文件中设置,或者在启动源 mysqld 进程时通过命令行设置。有关更多信息,请参见 Section 25.7.6, “Starting NDB Cluster Replication (Single Replication Channel)”")。

25.7.6 启动 NDB 集群复制(单个复制通道)

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-starting.html

本节概述了使用单个复制通道启动 NDB 集群复制的过程。

  1. 通过发出以下命令启动 MySQL 复制源服务器,其中id是该服务器的唯一 ID(参见第 25.7.2 节,“NDB 集群复制的一般要求”):

    shell*S*> mysqld --ndbcluster --server-id=*id* \
            --log-bin --ndb-log-bin &
    

    这将使用正确的日志格式启动服务器的mysqld进程,并启用二进制日志记录。在 NDB 8.0 中,还需要显式启用对NDB表更新的日志记录,使用--ndb-log-bin选项;这是与 NDB 集群以前版本的一个变化,以前版本中此选项默认启用。

    注意

    你也可以使用--binlog-format=MIXED来启动源,这样在集群之间复制时会自动使用基于行的复制。不支持基于语句的二进制日志记录用于 NDB 集群复制(参见第 25.7.2 节,“NDB 集群复制的一般要求”)。

  2. 按照以下方式启动 MySQL 复制服务器:

    shell*R*> mysqld --ndbcluster --server-id=*id* &
    

    在刚刚显示的命令中,id是复制服务器的唯一 ID。在复制上不需要启用日志记录。

    注意

    除非你希望立即开始复制,延迟复制线程的启动直到适当的START REPLICA语句已经发出,如下面的第 4 步所述。你可以通过在命令行上使用--skip-slave-start选项,通过在复制的my.cnf文件中包含skip-slave-start,或在 NDB 8.0.24 及更高版本中,通过设置skip_slave_start系统变量来实现这一点。在 NDB 8.0.26 及更高版本中,使用--skip-replica-startskip_replica_start

  3. 必须将复制服务器与源服务器的复制二进制日志同步。如果源上之前没有运行二进制日志记录,请在复制服务器上运行以下语句:

    mysql*R*> CHANGE MASTER TO
     -> MASTER_LOG_FILE='',
     -> MASTER_LOG_POS=4;
    

    从 NDB 8.0.23 开始,你还可以使用以下语句:

    mysql*R*> CHANGE REPLICATION SOURCE TO
     -> SOURCE_LOG_FILE='',
     -> SOURCE_LOG_POS=4;
    

    这指示副本从日志的起始点开始读取源服务器的二进制日志。否则,即如果您正在使用备份从源加载数据,请参阅 Section 25.7.8,“NDB Cluster 复制实现故障切换”,了解在这种情况下如何获取SOURCE_LOG_FILE | MASTER_LOG_FILESOURCE_LOG_POS | MASTER_LOG_POS的正确值。

  4. 最后,通过在副本上的mysql客户端发出以下命令,指示副本开始应用复制:

    mysql*R*> START SLAVE;
    

    在 NDB 8.0.22 及更高版本中,您还可以使用以下语句:

    mysql*R*> START REPLICA;
    

    这也启动了从源到副本的数据和更改的传输。

也可以使用两个复制通道,类似于下一节描述的过程;这种方法与使用单个复制通道的区别在 Section 25.7.7,“NDB Cluster 复制使用两个复制通道”中有所涵盖。

也可以通过启用批量更新来提高集群复制性能。可以通过在副本的mysqld进程上设置系统变量replica_allow_batching(NDB 8.0.26 及更高版本)或slave_allow_batching(NDB 8.0.26 之前)来实现。通常,更新在接收到时立即应用。但是,使用批处理会导致每个批次应用 32 KB 的更新;这可能会导致更高的吞吐量和更少的 CPU 使用,特别是在单个更新相对较小的情况下。

注意

批处理是基于每个时代的基础进行的;属于多个事务的更新可以作为同一批发送。

当达到时代结束时,所有未完成的更新都会被应用,即使更新总量不到 32 KB。

批处理可以在运行时打开和关闭。要在运行时激活它,您可以使用以下两个语句之一:

SET GLOBAL slave_allow_batching = 1;
SET GLOBAL slave_allow_batching = ON;

从 NDB 8.0.26 开始,您可以(并且应该)改用以下语句之一:

SET GLOBAL replica_allow_batching = 1;
SET GLOBAL replica_allow_batching = ON;

如果特定批次导致问题(例如,效果似乎没有正确复制的语句),可以使用以下两个语句之一来停用批处理:

SET GLOBAL slave_allow_batching = 0;
SET GLOBAL slave_allow_batching = OFF;

从 NDB 8.0.26 开始,您可以(并且应该)改用以下语句之一:

SET GLOBAL replica_allow_batching = 0;
SET GLOBAL replica_allow_batching = OFF;

您可以通过适当的SHOW VARIABLES语句来检查当前是否正在使用批处理,例如:

mysql> SHOW VARIABLES LIKE 'slave%';

在 NDB 8.0.26 及更高版本中,请使用以下语句:

mysql> SHOW VARIABLES LIKE 'replica%';

25.7.7 使用两个复制通道进行 NDB 集群复制

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-two-channels.html

在一个更完整的示例场景中,我们设想使用两个复制通道来提供冗余,从而防范单个复制通道可能的故障。这需要总共四个复制服务器,两个源服务器在源集群上,两个复制品服务器在复制品集群上。在接下来的讨论中,我们假设分配了如下所示的唯一标识符:

表 25.73 文本中描述的 NDB 集群复制服务器

服务器 ID 描述
1 源 - 主要复制通道(S
2 源 - 次要复制通道(S'
3 复制品 - 主要复制通道(R
4 复制品 - 次要复制通道(R'

使用两个通道设置复制与设置单个复制通道并没有根本不同。首先,必须启动主和次要复制源服务器的mysqld进程,然后启动主和次要复制品的进程。可以通过在每个复制品上发出START REPLICA语句来启动复制进程。下面显示了需要发出的命令和顺序:

  1. 启动主复制源:

    shell*S*> mysqld --ndbcluster --server-id=1 \
                   --log-bin &
    
  2. 启动次要复制源:

    shell*S'*> mysqld --ndbcluster --server-id=2 \
                   --log-bin &
    
  3. 启动主复制品服务器:

    shell*R*> mysqld --ndbcluster --server-id=3 \
                   --skip-slave-start &
    
  4. 启动次要复制品服务器:

    shell*R'*> mysqld --ndbcluster --server-id=4 \
                    --skip-slave-start &
    
  5. 最后,通过在主复制品上执行START REPLICA语句来启动主通道上的复制。

    mysql*R*> START SLAVE;
    

    从 NDB 8.0.22 开始,您还可以使用以下语句:

    mysql*R*> START REPLICA;
    

    警告

    此时只需启动主通道。只有在主复制通道失败时才需要启动次要复制通道,如第 25.7.8 节“使用 NDB 集群复制实现故障切换”中所述。同时运行多个复制通道可能导致在复制品上创建不需要的重复记录。

如前所述,在复制品上不需要启用二进制日志记录。

25.7.8 使用 NDB 集群复制实现故障转移

译文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-failover.html

如果主要集群复制过程失败,可以切换到次要复制通道。以下过程描述了完成此操作所需的步骤。

  1. 获取最近全局检查点(GCP)的时间。也就是说,您需要确定副本集群上的ndb_apply_status表中的最新时代,可以使用以下查询找到:

    mysql*R'*> SELECT @latest:=MAX(epoch)
     ->        FROM mysql.ndb_apply_status;
    

    在循环复制拓扑中,每个主机上都运行源和副本时,当您使用ndb_log_apply_status=1时,NDB 集群时代会写入副本的二进制日志中。这意味着ndb_apply_status表包含了此主机上的副本以及任何其他充当此主机上运行的复制源服务器的副本的主机的信息。

    在这种情况下,您需要确定此副本上的最新时代,排除在此副本的二进制日志中未列在CHANGE REPLICATION SOURCE TO | CHANGE MASTER TO语句的IGNORE_SERVER_IDS选项中的任何其他副本的时代。排除这些时代的原因是,mysql.ndb_apply_status表中具有与IGNORE_SERVER_IDS列表中的匹配的服务器 ID 的行,从CHANGE REPLICATION SOURCE TO | CHANGE MASTER TO语句中的本副本的源准备的行也被视为来自本地服务器,除了具有副本自身服务器 ID 的行。您可以从SHOW REPLICA STATUS的输出中检索此列表作为Replicate_Ignore_Server_Ids。我们假设您已经获得了此列表,并将其替换为此处显示的查询中的ignore_server_ids,该查询与上一个查询的版本一样,将最大时代选择到名为@latest的变量中:

    mysql*R'*> SELECT @latest:=MAX(epoch)
     ->        FROM mysql.ndb_apply_status
     ->        WHERE server_id NOT IN (*ignore_server_ids*);
    

    在某些情况下,使用要包含的服务器 ID 列表和在前述查询的WHERE条件中的server_id IN *server_id_list*可能更简单或更有效(或两者兼有)。

  2. 使用第 1 步中显示的查询获取的信息,从源集群的ndb_binlog_index表中获取相应的记录。

    您可以使用以下查询从源集群的ndb_binlog_index表中获取所需的记录:

    mysql*S'*> SELECT
     ->     @file:=SUBSTRING_INDEX(next_file, '/', -1),
     ->     @pos:=next_position
     -> FROM mysql.ndb_binlog_index
     -> WHERE epoch = @latest;
    

    这些是自主要复制通道故障以来在源上保存的记录。我们在这里使用了一个用户变量 @latest 来表示第 1 步中获得的值。当然,一个 mysqld 实例无法直接访问在另一个服务器实例上设置的用户变量。这些值必须手动或通过应用程序“插入”到第二个查询中。

    重要

    在执行START REPLICA之前,您必须确保副本mysqld使用--slave-skip-errors=ddl_exist_errors启动。否则,复制可能因重复的 DDL 错误而停止。

  3. 现在可以通过在次要副本服务器上运行以下查询来同步次要通道:

    mysql*R'*> CHANGE MASTER TO
          ->     MASTER_LOG_FILE='@file',
     ->     MASTER_LOG_POS=@pos;
    

    在 NDB 8.0.23 及更高版本中,您还可以使用此处显示的语句:

    mysql*R'*> CHANGE REPLICATION SOURCE TO
          ->     SOURCE_LOG_FILE='@file',
     ->     SOURCE_LOG_POS=@pos;
    

    再次我们使用了用户变量(在这种情况下是 @file@pos)来表示第 2 步中获得的值,并在第 3 步中应用;在实践中,这些值必须手动插入或使用能够访问涉及的两个服务器的应用程序。

    注意

    @file 是一个字符串值,例如 '/var/log/mysql/replication-source-bin.00001',因此在 SQL 或应用程序代码中使用时必须加引号。然而,由 @pos 表示的值则不应加引号。尽管 MySQL 通常会尝试将字符串转换为数字,但这种情况是一个例外。

  4. 您现在可以通过在次要副本上发出适当的命令来启动次要通道的复制mysqld

    mysql*R'*> START SLAVE;
    

    在 NDB 8.0.22 或更高版本中,您还可以使用以下语句:

    mysql*R'*> START REPLICA;
    

一旦次要复制通道激活,您可以调查主要通道的故障并进行修复。执行这些精确的操作取决于主要通道失败的原因。

警告

只有在主要复制通道失败时才应启动次要复制通道。同时运行多个复制通道可能导致副本上创建不需要的重复记录。

如果故障仅限于单个服务器,则理论上应该可以从 S 复制到 R',或者从 S' 复制到 R

25.7.9 NDB 集群复制的 NDB 集群备份

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-backups.html

25.7.9.1 NDB 集群复制:自动同步副本到源二进制日志

25.7.9.2 使用 NDB 集群复制进行时点恢复

本节讨论使用 NDB 集群复制进行备份和从备份中恢复。我们假设复制服务器已经按照之前的说明进行了配置(请参阅第 25.7.5 节,“为 NDB 集群准备复制”,以及紧随其后的各节)。完成这些步骤后,进行备份然后从备份中恢复的过程如下:

  1. 有两种不同的方法可以启动备份。

    • **方法 A. ** 此方法要求在启动复制过程之前,在源服务器上先启用集群备份过程。可以通过在 my.cnf 文件中的 [mysql_cluster] 部分中包含以下行来完成,其中 management_host 是源集群的 NDB 管理服务器的 IP 地址或主机名,port 是管理服务器的端口号:

      ndb-connectstring=*management_host*[:*port*]
      

      注意

      只有在未使用默认端口(1186)时才需要指定端口号。有关 NDB 集群中端口和端口分配的更多信息,请参见第 25.3.3 节,“NDB 集群的初始配置”。

      在这种情况下,可以通过在复制源上执行此语句来启动备份:

      shell*S*> ndb_mgm -e "START BACKUP"
      
    • **方法 B. ** 如果 my.cnf 文件未指定管理主机的位置,则可以通过将此信息作为 START BACKUP 命令的一部分传递给 NDB 管理客户端来启动备份过程。可以按照以下方式执行此操作,其中 management_hostport 是管理服务器的主机名和端口号:

      shell*S*> ndb_mgm *management_host*:*port* -e "START BACKUP"
      

      在我们之前概述的场景中(请参阅第 25.7.5 节,“为 NDB 集群准备复制”),将执行如下操作:

      shell*S*> ndb_mgm rep-source:1186 -e "START BACKUP"
      
  2. 将集群备份文件复制到正在上线的复制品。运行源集群的每个为ndbd进程的系统上都有集群备份文件,所有这些文件都必须复制到复制品以确保成功恢复。备份文件可以复制到复制品的管理主机所在计算机上的任何目录中,只要 MySQL 和 NDB 二进制文件在该目录中具有读取权限。在这种情况下,我们假设这些文件已复制到目录/var/BACKUPS/BACKUP-1

    虽然复制集群不必具有与源相同数量的数据节点,但强烈建议此数量相同。当复制服务器启动时,必须防止复制过程启动。您可以通过在命令行上使用--skip-slave-start选项启动复制品,通过在复制品的my.cnf文件中包含skip-slave-start,或在 NDB 8.0.24 或更高版本中,通过设置skip_slave_start系统变量来实现这一点。

  3. 在复制品集群上创建任何在源集群上存在且需要复制的数据库。

    重要

    必须在复制品集群中的每个 SQL 节点上执行与要复制的每个数据库对应的CREATE DATABASE(或CREATE SCHEMA)语句。

  4. mysql客户端中使用以下语句重置复制集群:

    mysql*R*> RESET SLAVE;
    

    在 NDB 8.0.22 或更高版本中,您还可以使用此语句:

    mysql*R*> RESET REPLICA;
    
  5. 现在,您可以使用ndb_restore命令依次为每个备份文件启动集群恢复过程。对于其中的第一个,有必要包含-m选项以恢复集群元数据,如下所示:

    shell*R*> ndb_restore -c *replica_host*:*port* -n *node-id* \
            -b *backup-id* -m -r *dir*
    

    dir是备份文件在复制品上放置的目录路径。对于剩余备份文件对应的ndb_restore命令,不应使用-m选项。

    从具有四个数据节点的源集群中恢复(如第 25.7 节“NDB 集群复制”中所示的图表),其中备份文件已复制到目录/var/BACKUPS/BACKUP-1,在复制品上执行的正确命令序列可能如下所示:

    shell*R*> ndb_restore -c replica-host:1186 -n 2 -b 1 -m \
            -r ./var/BACKUPS/BACKUP-1
    shell*R*> ndb_restore -c replica-host:1186 -n 3 -b 1 \
            -r ./var/BACKUPS/BACKUP-1
    shell*R*> ndb_restore -c replica-host:1186 -n 4 -b 1 \
            -r ./var/BACKUPS/BACKUP-1
    shell*R*> ndb_restore -c replica-host:1186 -n 5 -b 1 -e \
            -r ./var/BACKUPS/BACKUP-1
    

    重要

    在这个例子中,ndb_restore 的最后调用中需要 -e(或 --restore-epoch)选项,以确保时代写入副本的 mysql.ndb_apply_status 表。没有这些信息,副本无法与源正确同步。 (参见 Section 25.5.23, “ndb_restore — Restore an NDB Cluster Backup”.)

  6. 现在,您需要从副本的 ndb_apply_status 表中获取最新的时代(如 Section 25.7.8, “Implementing Failover with NDB Cluster Replication” 中所讨论的):

    mysql*R*> SELECT @latest:=MAX(epoch)
            FROM mysql.ndb_apply_status;
    
  7. 使用前一步骤中获得的 @latest 作为时代值,您可以从源的 mysql.ndb_binlog_index 表中获取正确的起始位置 @pos 和正确的二进制日志文件 @file。此处显示的查询从逻辑恢复位置之前应用的最后时代的 PositionFile 列中获取这些值:

    mysql*S*> SELECT
     ->     @file:=SUBSTRING_INDEX(File, '/', -1),
     ->     @pos:=Position
     -> FROM mysql.ndb_binlog_index
     -> WHERE epoch > @latest
     -> ORDER BY epoch ASC LIMIT 1;
    

    如果当前没有复制流量,您可以通过在源上运行 SHOW MASTER STATUS 并使用输出中 Position 列中显示的值来获取类似信息,该值为所有文件中具有最大值后缀的文件的 File 列中显示的值。在这种情况下,您必须确定这是哪个文件,并在下一步手动提供名称或通过脚本解析输出。

  8. 使用前一步骤中获得的值,现在可以在副本的 mysql 客户端中发出适当的命令。在 NDB 8.0.23 及更高版本中,请使用以下 CHANGE REPLICATION SOURCE TO 语句:

    mysql*R*> CHANGE REPLICATION SOURCE TO
     ->     SOURCE_LOG_FILE='@file',
     ->     SOURCE_LOG_POS=@pos;
    

    在 NDB 8.0.23 之前,您必须使用此处显示的 CHANGE MASTER TO 语句:

    mysql*R*> CHANGE MASTER TO
     ->     MASTER_LOG_FILE='@file',
     ->     MASTER_LOG_POS=@pos;
    
  9. 现在,副本知道从源的哪个二进制日志文件的哪个点开始读取数据,您可以使用此语句使副本开始复制:

    mysql*R*> START SLAVE;
    

    从 NDB 8.0.22 开始,您还可以使用以下语句:

    mysql*R*> START REPLICA;
    

要在第二个复制通道上执行备份和恢复,只需重复这些步骤,根据需要用次要源和副本的主机名和 ID 替换主要源和副本服务器的名称,并在它们上运行前面的语句。

有关执行集群备份和从备份中恢复集群的更多信息,请参阅第 25.6.8 节,“NDB 集群的在线备份”。

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-auto-sync.html

25.7.9.1 NDB 集群复制:自动同步副本到源二进制日志

可以自动化前一节描述的大部分过程(参见第 25.7.9 节,“NDB 集群备份与 NDB 集群复制”)。以下 Perl 脚本reset-replica.pl作为您可以执行此操作的示例。

#!/user/bin/perl -w

#  file: reset-replica.pl

#  Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.

#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.

#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to:
#  Free Software Foundation, Inc.
#  59 Temple Place, Suite 330
#  Boston, MA 02111-1307 USA
#
#  Version 1.1

######################## Includes ###############################

use DBI;

######################## Globals ################################

my  $m_host='';
my  $m_port='';
my  $m_user='';
my  $m_pass='';
my  $s_host='';
my  $s_port='';
my  $s_user='';
my  $s_pass='';
my  $dbhM='';
my  $dbhS='';

####################### Sub Prototypes ##########################

sub CollectCommandPromptInfo;
sub ConnectToDatabases;
sub DisconnectFromDatabases;
sub GetReplicaEpoch;
sub GetSourceInfo;
sub UpdateReplica;

######################## Program Main ###########################

CollectCommandPromptInfo;
ConnectToDatabases;
GetReplicaEpoch;
GetSourceInfo;
UpdateReplica;
DisconnectFromDatabases;

################## Collect Command Prompt Info ##################

sub CollectCommandPromptInfo
{
  ### Check that user has supplied correct number of command line args
  die "Usage:\n
       reset-replica >source MySQL host< >source MySQL port< \n
                   >source user< >source pass< >replica MySQL host< \n
                   >replica MySQL port< >replica user< >replica pass< \n
       All 8 arguments must be passed. Use BLANK for NULL passwords\n"
       unless @ARGV == 8;

  $m_host  =  $ARGV[0];
  $m_port  =  $ARGV[1];
  $m_user  =  $ARGV[2];
  $m_pass  =  $ARGV[3];
  $s_host  =  $ARGV[4];
  $s_port  =  $ARGV[5];
  $s_user  =  $ARGV[6];
  $s_pass  =  $ARGV[7];

  if ($m_pass eq "BLANK") { $m_pass = '';}
  if ($s_pass eq "BLANK") { $s_pass = '';}
}

###############  Make connections to both databases #############

sub ConnectToDatabases
{
  ### Connect to both source and replica cluster databases

  ### Connect to source
  $dbhM
    = DBI->connect(
    "dbi:mysql:database=mysql;host=$m_host;port=$m_port",
    "$m_user", "$m_pass")
      or die "Can't connect to source cluster MySQL process!
              Error: $DBI::errstr\n";

  ### Connect to replica
  $dbhS
    = DBI->connect(
          "dbi:mysql:database=mysql;host=$s_host",
          "$s_user", "$s_pass")
    or die "Can't connect to replica cluster MySQL process!
            Error: $DBI::errstr\n";
}

################  Disconnect from both databases ################

sub DisconnectFromDatabases
{
  ### Disconnect from source

  $dbhM->disconnect
  or warn " Disconnection failed: $DBI::errstr\n";

  ### Disconnect from replica

  $dbhS->disconnect
  or warn " Disconnection failed: $DBI::errstr\n";
}

######################  Find the last good GCI ##################

sub GetReplicaEpoch
{
  $sth = $dbhS->prepare("SELECT MAX(epoch)
                         FROM mysql.ndb_apply_status;")
      or die "Error while preparing to select epoch from replica: ",
             $dbhS->errstr;

  $sth->execute
      or die "Selecting epoch from replica error: ", $sth->errstr;

  $sth->bind_col (1, \$epoch);
  $sth->fetch;
  print "\tReplica epoch =  $epoch\n";
  $sth->finish;
}

#######  Find the position of the last GCI in the binary log ########

sub GetSourceInfo
{
  $sth = $dbhM->prepare("SELECT
                           SUBSTRING_INDEX(File, '/', -1), Position
                         FROM mysql.ndb_binlog_index
                         WHERE epoch > $epoch
                         ORDER BY epoch ASC LIMIT 1;")
      or die "Prepare to select from source error: ", $dbhM->errstr;

  $sth->execute
      or die "Selecting from source error: ", $sth->errstr;

  $sth->bind_col (1, \$binlog);
  $sth->bind_col (2, \$binpos);
  $sth->fetch;
  print "\tSource binary log file =  $binlog\n";
  print "\tSource binary log position =  $binpos\n";
  $sth->finish;
}

##########  Set the replica to process from that location #########

sub UpdateReplica
{
  $sth = $dbhS->prepare("CHANGE MASTER TO
                         MASTER_LOG_FILE='$binlog',
                         MASTER_LOG_POS=$binpos;")
      or die "Prepare to CHANGE MASTER error: ", $dbhS->errstr;

  $sth->execute
       or die "CHANGE MASTER on replica error: ", $sth->errstr;
  $sth->finish;
  print "\tReplica has been updated. You may now start the replica.\n";
}

# end reset-replica.pl

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-pitr.html

25.7.9.2 使用 NDB 集群复制进行时点恢复

NDB 集群表的时点恢复,即恢复自某一特定时间点以来所做的数据更改,是在恢复将服务器恢复到备份时的状态的完整备份之后进行的。使用 NDB 集群和 NDB 集群复制进行 NDB 集群表的时点恢复可以通过使用本地NDB数据备份(通过在ndb_mgm客户端中发出CREATE BACKUP命令)和恢复ndb_binlog_index表(从使用mysqldump制作的转储中)来完成。

要执行 NDB 集群的时点恢复,需要按照以下步骤进行:

  1. 使用START BACKUP命令在ndb_mgm客户端中备份集群中的所有NDB数据库(参见第 25.6.8 节,“NDB 集群的在线备份”)。

  2. 在恢复集群之前的某个时间点,备份mysql.ndb_binlog_index表。最简单的方法可能是使用mysqldump来完成此任务。同时备份二进制日志文件。

    根据您的需求,这个备份应该定期更新,甚至可能每小时更新一次。

  3. 发生灾难性故障或错误。)

  4. 定位最近的备份。

  5. 清除数据节点文件系统(使用ndbd --initialndbmtd") --initial)。

    注意

    从 NDB 8.0.21 开始,磁盘数据表空间和日志文件将通过--initial删除。以前,��要手动删除这些文件。

  6. 使用DROP TABLETRUNCATE TABLE命令处理mysql.ndb_binlog_index表。

  7. 执行ndb_restore,恢复所有数据。在运行ndb_restore时,必须包括--restore-epoch选项,以便正确填充ndb_apply_status表。(有关更多信息,请参见 Section 25.5.23, “ndb_restore — 恢复 NDB 集群备份”.)

  8. mysqldump的输出中恢复ndb_binlog_index表,并根据需要从备份中恢复二进制日志文件。

  9. 找到最近应用的时代,即ndb_apply_status表中的最大epoch列值,作为用户变量@LATEST_EPOCH(强调):

    SELECT *@LATEST_EPOCH*:=MAX(epoch)
        FROM mysql.ndb_apply_status;
    
  10. 找到与ndb_binlog_index表中的@LATEST_EPOCH对应的最新二进制日志文件(@FIRST_FILE)和位置(Position列值):

    SELECT Position, *@FIRST_FILE*:=File
        FROM mysql.ndb_binlog_index
        WHERE epoch > *@LATEST_EPOCH* ORDER BY epoch ASC LIMIT 1;
    
  11. 使用mysqlbinlog,重放给定文件和位置的二进制日志事件,直到故障点。(参见 Section 6.6.9, “mysqlbinlog — 用于处理二进制日志文件的实用程序”.)

另请参阅 Section 9.5, “时间点(增量)恢复”,了解有关二进制日志、复制和增量恢复的更多信息。

25.7.10 NDB 集群复制:双向和循环复制

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-multi-source.html

可以使用 NDB 集群在两个集群之间进行双向复制,也可以在任意数量的集群之间进行循环复制。

循环复制示例。 在接下来的几段中,我们考虑涉及三个 NDB 集群(编号为 1、2 和 3)的复制设置示例,其中集群 1 充当集群 2 的复制源,集群 2 充当集群 3 的源,集群 3 充当集群 1 的源。每个集群都有两个 SQL 节点,SQL 节点 A 和 B 属于集群 1,SQL 节点 C 和 D 属于集群 2,SQL 节点 E 和 F 属于集群 3。

只要满足以下条件,就支持使用这些集群进行循环复制:

  • 所有源和副本上的 SQL 节点都是相同的。

  • 所有作为源和副本的 SQL 节点都启用了系统变量log_replica_updates(从 NDB 8.0.26 开始)或log_slave_updates(NDB 8.0.26 及更早版本)。

这种循环复制设置如下图所示:

图 25.15 NDB 集群循环复制,所有源均为副本

周围文本描述了一些内容。图表显示了三个集群,每个集群有两个节点。连接不同集群中的 SQL 节点的箭头说明所有源也是副本。

在这种情况下,集群 1 中的 SQL 节点 A 复制到集群 2 中的 SQL 节点 C;SQL 节点 C 复制到集群 3 中的 SQL 节点 E;SQL 节点 E 复制到 SQL 节点 A。换句话说,复制线(图表中的曲线箭头表示)直接连接所有用作复制源和副本的 SQL 节点。

也可以设置循环复制,使得并非所有源 SQL 节点也是副本,如下所示:

图 25.16 NDB 集群循环复制,不是所有源都是副本

周围文本描述了一些内容。图表显示了三个集群,每个集群有两个节点。连接不同集群中的 SQL 节点的箭头说明并非所有源都是副本。

在这种情况下,每个集群中的不同 SQL 节点被用作复制源和副本。您应该启动任何带有系统变量log_replica_updates(NDB 8.0.26 及更高版本)或log_slave_updates(NDB 8.0.26 之前)的 SQL 节点。NDB 集群的这种循环复制方案,其中复制线路(在图中由曲线箭头表示)是不连续的,应该是可能的,但需要注意的是,这种方案尚未经过彻底测试,因此仍然被视为实验性。

使用 NDB 本地备份和恢复初始化副本集群。 在设置循环复制时,可以通过在一个 NDB 集群上使用管理客户端START BACKUP命令创建备份,然后在另一个 NDB 集群上使用ndb_restore应用此备份来初始化副本集群。这不会自动在充当副本的第二个 NDB 集群的 SQL 节点上创建二进制日志;为了导致创建二进制日志,您必须在该 SQL 节点上发出SHOW TABLES语句;在运行START REPLICA之前应该这样做。这是一个已知问题。

多源故障转移示例。 在本节中,我们讨论了具有服务器 ID 1、2 和 3 的三个 NDB 集群的多源 NDB 集群复制设置中的故障转移。在这种情况下,集群 1 复制到集群 2 和 3;集群 2 也复制到集群 3。这种关系如下所示:

图 25.17 具有 3 个源的 NDB 集群多主复制

具有服务器 ID 1、2 和 3 的三个 NDB 集群的多源 NDB 集群复制设置;集群 1 复制到集群 2 和 3;集群 2 也复制到集群 3。

换句话说,数据通过两种不同的路径从集群 1 复制到集群 3:直接和通过集群 2。

并非所有参与多源复制的 MySQL 服务器都必须同时充当源和副本,给定的 NDB 集群可能会为不同的复制通道使用不同的 SQL 节点。这种情况如下所示:

图 25.18 具有 MySQL 服务器的 NDB 集群多源复制

概念在周围的文本中描述。显示三个节点:Cluster 1 中的 SQL 节点 A 复制到 Cluster 3 中的 SQL 节点 F;Cluster 1 中的 SQL 节点 B 复制到 Cluster 2 中的 SQL 节点 C;Cluster 3 中的 SQL 节点 E 复制到 Cluster 3 中的 SQL 节点 G。Cluster 1 中的 SQL 节点 A 和 B 的 --log-slave-updates=0;Cluster 2 中的 SQL 节点 C,Cluster 3 中的 SQL 节点 F 和 G 的 --log-slave-updates=1;Cluster 2 中的 SQL 节点 D 和 E 的 --log-slave-updates=0。

作为副本的 MySQL 服务器必须启用系统变量 log_replica_updates(从 NDB 8.0.26 开始)或 log_slave_updates(NDB 8.0.26 及更早版本)。在前面的图表中还显示了哪些 mysqld 进程需要此选项。

注意

使用 log_replica_updateslog_slave_updates 系统变量对未作为副本运行的服务器没有影响。

当复制集群之一宕机时,就会出现故障切换的需求。在本例中,我们考虑 Cluster 1 丢失服务的情况,因此 Cluster 3 丢失了来自 Cluster 1 的 2 个更新源。由于 NDB 集群之间的复制是异步的,不能保证 Cluster 3 直接源自 Cluster 1 的更新比通过 Cluster 2 接收的更新更近。您可以通过确保 Cluster 3 追赶 Cluster 2 关于来自 Cluster 1 的更新来处理这个问题。在 MySQL 服务器方面,这意味着您需要将 MySQL 服务器 C 的任何未完成更新复制到服务器 F。

在服务器 C 上执行以下查询:

mysqlC> SELECT @latest:=MAX(epoch)
 ->     FROM mysql.ndb_apply_status
 ->     WHERE server_id=1;

mysqlC> SELECT
 ->     @file:=SUBSTRING_INDEX(File, '/', -1),
 ->     @pos:=Position
 ->     FROM mysql.ndb_binlog_index
 ->     WHERE orig_epoch >= @latest
 ->     AND orig_server_id = 1
 ->     ORDER BY epoch ASC LIMIT 1;

注意

您可以通过向 ndb_binlog_index 表添加适当的索引来提高此查询的性能,从而显着加快故障切换时间。有关更多信息,请参见 第 25.7.4 节,“NDB Cluster Replication Schema and Tables”。

从服务器 C 手动复制 @file@pos 的值到服务器 F(或让您的应用程序执行相应操作)。然后,在服务器 F 上执行以下 CHANGE REPLICATION SOURCE TO 语句(NDB 8.0.23 及更高版本)或 CHANGE MASTER TO 语句(NDB 8.0.23 之前):

mysqlF> CHANGE MASTER TO
 ->     MASTER_HOST = 'serverC'
 ->     MASTER_LOG_FILE='@file',
 ->     MASTER_LOG_POS=@pos;

从 NDB 8.0.23 开始,您还可以使用以下语句:

mysqlF> CHANGE REPLICATION SOURCE TO
 ->     SOURCE_HOST = 'serverC'
 ->     SOURCE_LOG_FILE='@file',
 ->     SOURCE_LOG_POS=@pos;

完成后,在 MySQL 服务器 F 上发出 START REPLICA 语句;这将导致来自服务器 B 的任何丢失更新被复制到服务器 F。

CHANGE REPLICATION SOURCE TO | CHANGE MASTER TO 语句还支持一个 IGNORE_SERVER_IDS 选项,该选项接受一个逗号分隔的服务器 ID 列表,并导致来自相应服务器的事件被忽略。有关更多信息,请参见 第 15.4.2.1 节,“CHANGE MASTER TO 语句”,以及 第 15.7.7.36 节,“SHOW SLAVE | REPLICA STATUS 语句”。有关此选项如何与 ndb_log_apply_status 变量交互的信息,请参见 第 25.7.8 节,“使用 NDB 集群复制实现故障切换”。

25.7.11 NDB 集群使用多线程应用程序的复制

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-mta.html

从 NDB 8.0.33 开始,NDB 复制支持使用通用的 MySQL 服务器多线程应用程序机制(MTA),允许在副本上并行应用独立的二进制日志事务,从而增加了峰值复制吞吐量。

需求

MySQL 服务器 MTA 实现将单独的二进制日志事务的处理委托给一组工作线程(其大小是可配置的),并协调工作线程以确保二进制日志中编码的事务依赖关系得到尊重,并在必要时保持提交顺序(参见第 19.2.3 节,“复制线程”)。要在 NDB 集群中使用此功能,必须满足以下三个条件:

  1. 二进制日志事务依赖关系在源端确定

    为了实现这一点,必须在源端将binlog_transaction_dependency_tracking服务器系统变量设置为WRITESET。这在 NDB 8.0.33 及更高版本中受支持。(默认值为COMMIT_ORDER。)

    NDB中的写集维护工作由 MySQL 二进制日志注入线程执行,作为准备和提交每个时代事务到二进制日志的一部分。这需要额外的资源,并可能降低峰值吞吐量。

  2. 事务依赖关系被编码到二进制日志中

    NDB 8.0.33 及更高版本支持--ndb-log-transaction-dependency启动选项用于mysqld; 将此选项设置为ON以启用将NDB事务依赖关系写入二进制日志。

  3. 副本配置为使用多个工作线程

    NDB 8.0.33 及更高版本支持将replica_parallel_workers设置为非零值,以控制副本上的工作线程数。默认值为 4。

MTA 配置:源

NDB MTA 的源mysqld配置必须包括以下显式设置:

  • binlog_transaction_dependency_tracking必须设置为WRITESET

  • 复制源 mysqld 必须使用--ndb-log-transaction-dependency=ON启动。

如果设置了,replica_parallel_type必须为LOGICAL_CLOCK(默认值)。

注意

NDB不支持replica_parallel_type=DATABASE

此外,建议您将用于跟踪二进制日志事务写入集的内存量设置为源上的binlog_transaction_dependency_history_size*E* * *P*,其中E是平均时代大小(即每个时代的操作数),P是最大预期并行性。有关更多信息,请参见写集跟踪内存使用情况。

MTA 配置:副本

NDB MTA 的副本mysqld配置要求replica_parallel_workers大于 1。首次启用 MTA 时的推荐起始值为 4,这是默认值。

此外,replica_preserve_commit_order必须为ON。这也是默认值。

事务依赖性和写集处理

通过分析每个事务的写集(即事务写入的行(表、键值)集)来检测事务依赖关系。如果两个事务修改相同的行,则它们被视为依赖关系,并且必须按顺序(换句话说,串行)应用,以避免死锁或不正确的结果。如果表具有辅助唯一键,这些值也会添加到事务的写集中,以检测由不同事务影响相同唯一键值而暗示事务依赖关系的情况,因此需要排序。无法有效确定依赖关系的情况下,mysqld会退回到考虑出于安全原因而依赖于事务的情况。

事务依赖关系通过源mysqld在二进制日志中编码。依赖关系通过使用称为“逻辑时钟”的方案在ANONYMOUS_GTID事件中编码。(参见 Section 19.1.4.1, “Replication Mode Concepts”.)

MySQL(和 NDB Cluster)采用的写集实现使用基于哈希的冲突检测,基于匹配的相关表和索引值的 64 位行哈希。这可靠地检测到当相同键被看到两次时,但如果不同的表和索引值哈希到相同的 64 位值,则可能产生误报,这可能导致人为依赖关系,从而降低可用的并行性。

事务依赖关系由以下任一方式强制:

  • DDL 语句

  • 二进制日志轮换或遇到二进制日志文件边界

  • 写集历史大小限制

  • 写入引用目标表中的父外键

    更具体地说,对外键表执行插入、更新和删除的事务相对于所有前后事务进行序列化,而不仅仅是与涉及约束关系的表相关的事务。相反,对外键表(引用)执行插入、更新和删除的事务与彼此之间并没有特别的序列化。

MySQL MTA 实现尝试并行应用独立的二进制日志事务。NDB记录在一个二进制日志事务中发生的所有用户事务在一个时期内提交的所有更改(TimeBetweenEpochs,默认为 100 毫秒)。因此,为了使两个连续的时期事务独立且可能并行应用,需要确保在两个时期中没有任何行被修改。如果任何单行在两个时期中都被修改,则它们是依赖的,并且按顺序应用,这可能限制可利用的并行性。

基于在时期内在源集群上修改的行集,但不包括传达时期元数据的生成的mysql.ndb_apply_status WRITE_ROW事件,时期事务被视为独立的。这避免了每个时期事务都简单地依赖于前一个时期,但需要在保留提交顺序的情况下在副本上应用 binlog。这也意味着具有写集依赖关系的 NDB 二进制日志不适合由使用不同 MySQL 存储引擎的副本数据库使用。

可能或有必要修改应用程序事务行为,以避免在短时间内通过单独的事务重复修改相同行的模式,以增加可利用的应用并行性。

写集跟踪内存使用

用于跟踪二进制日志事务写入集的内存使用量可以使用binlog_transaction_dependency_history_size服务器系统变量进行设置,默认为 25000 行哈希。

如果平均二进制日志事务修改了N行,则为了能够识别独立(可并行化)事务达到并行级别P,我们需要binlog_transaction_dependency_history_size至少为*N* * *P*。(最大值为 1000000。)

历史记录的有限大小导致可靠确定的有限最大依赖长度,从而给出可以表达的有限并行性。在历史记录中找不到的任何行可能依赖于从历史记录中清除的最后一个事务。

写入集历史不像是对最后N个事务的滑动窗口;相反,它是一个允许完全填满的有限缓冲区,当其完全填满时,其内容完全丢弃。这意味着历史大小随时间呈锯齿状变化,因此最大可检测的依赖长度也随时间呈锯齿状变化,这样,如果写入集历史缓冲区在它们被处理之间被重置,独立事务仍可能被标记为依赖。

在这个方案中,二进制日志文件中的每个事务都带有一个sequence_number(1、2、3,...),以及它依赖的最近二进制日志事务的序列号,我们称之为last_committed

在给定的二进制日志文件中,第一个事务的sequence_number为 1,last_committed为 0。

如果一个二进制日志事务依赖于其直接前任,则其应用是串行的。如果依赖于较早的事务,则可能可以与前面的独立事务并行应用该事务。

ANONYMOUS_GTID事件的内容,包括sequence_numberlast_committed(因此事务依赖关系),可以使用mysqlbinlog查看。

在源上生成的ANONYMOUS_GTID事件与批量BEGINTABLE_MAP*WRITE_ROW*UPDATE_ROW*DELETE_ROW*COMMIT事件的压缩事务有效载荷分开处理,允许在解压缩之前确定依赖关系。这意味着副本协调器线程可以将事务有效载荷解压缩委托给工作线程,从而在副本上自动并行解压缩独立事务。

已知限制

次要唯一列。 具有次要唯一列(即主键以外的唯一键)的表将所有列发送到源,以便可以检测到与唯一键相关的冲突。

当当前的二进制日志模式不包括所有列,而只包括更改的列(--ndb-log-updated-only=OFF, --ndb-log-update-minimal=ON, --ndb-log-update-as-write=OFF)时,这可能会增加从数据节点发送到 SQL 节点的数据量。

影响取决于这些表中行的修改(更新或删除)速率以及实际未修改的列中的数据量。

将 NDB 复制到 InnoDB。 NDB 二进制日志注入器事务依赖跟踪有意忽略由生成的 mysql.ndb_apply_status 元数据事件创建的事务间依赖关系,这些事件作为副本应用程序上的时代事务提交的一部分单独处理。对于复制到InnoDB,没有特殊处理;当使用InnoDB多线程应用程序来消耗NDB MTA 二进制日志时,这可能导致性能降低或其他问题。

25.7.12 NDB Cluster 复制冲突解决

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-replication-conflict-resolution.html

  • 要求

  • 源列控制

  • 冲突解决控制

  • 冲突解决函数

  • 冲突解决异常表

  • 冲突检测状态变量

  • 示例

在涉及多个源(包括循环复制)的复制设置中,可能会出现不同源尝试使用不同数据更新副本上相同行的情况。NDB Cluster 复制中的冲突解决提供了一种通过允许使用用户定义的解决列来确定是否应在副本上应用给定源上的更新来解决此类冲突的方法。

NDB Cluster 支持的一些冲突解决类型(NDB$OLD()NDB$MAX()NDB$MAX_DELETE_WIN();此外,在 NDB 8.0.30 及更高版本中,还有NDB$MAX_INS()NDB$MAX_DEL_WIN_INS())将此用户定义列实现为“时间戳”列(尽管其类型不能是TIMESTAMP,如本节后面所述)。这些类型的冲突解决总是基于逐行而不是基于事务的。基于时代的冲突解决函数NDB$EPOCH()NDB$EPOCH_TRANS()比较了复制时代的顺序(因此这些函数是事务性的)。在冲突发生时,可以使用不同的方法来比较副本上的解决列值,如本节后面所述;所使用的方法可以设置为在单个表、数据库或服务器上操作,或者使用模式匹配在一个或多个表上操作。有关在mysql.ndb_replication表的dbtable_nameserver_id列中使用模式匹配的信息,请参见使用通配符进行匹配。

您还应该记住,确保解析列正确填充相关值是应用程序的责任,以便解析函数在确定是否应用更新时可以做出适当选择。

要求

冲突解决的准备工作必须在源和副本上都进行。这些任务在以下列表中描述:

  • 在写入二进制日志的源上,您必须确定要发送哪些列(所有列还是仅已更新的列)。这是通过在整个 MySQL Server 上应用mysqld启动选项--ndb-log-updated-only(稍后在本节中描述)来完成的,或者通过在mysql.ndb_replication表中放置适当的条目来在一个或多个特定表上完成(参见 ndb_replication Table)。

    注意

    如果您正在复制具有非常大列(如TEXTBLOB列)的表,--ndb-log-updated-only也可以用于减小二进制日志的大小,并避免由于超过max_allowed_packet而导致的可能的复制失败。

    有关此问题的更多信息,请参见 Section 19.5.1.20, “Replication and max_allowed_packet”。

  • 在副本上,您必须确定要应用哪种冲突解决方法(“最新时间戳获胜”,“相同时间戳获胜”,“主要获胜”,“主要获胜,完成事务”或无)。这是通过使用mysql.ndb_replication系统表来完成的,并适用于一个或多个特定表(参见 ndb_replication Table)。

  • NDB Cluster 还支持读冲突检测,即检测一个集群中对给定行的读取与另一个集群中对同一行的更新或删除之间的冲突。这需要通过在副本上将ndb_log_exclusive_reads设置为 1 来获得独占读锁。所有被冲突读取的行都将被记录在异常表中。有关更多信息,请参见读冲突检测和解决。

  • 在 NDB 8.0.30 之前,NDB严格将WRITE_ROW事件应用为插入操作,要求不存在这样的行;也就是说,如果行已经存在,则传入的写操作总是被拒绝。(当使用除NDB$MAX_INS()NDB$MAX_DEL_WIN_INS()之外的任何冲突解决函数时,仍然如此。)

    从 NDB 8.0.30 开始,当使用NDB$MAX_INS()NDB$MAX_DEL_WIN_INS()时,NDB可以对WRITE_ROW事件进行幂等应用,将这样的事件映射到插入操作,当传入行不存在时,或者当传入行存在时映射到更新操作。

当使用函数NDB$OLD()NDB$MAX()NDB$MAX_DELETE_WIN()进行基于时间戳的冲突解决(以及从 NDB 8.0.30 开始使用NDB$MAX_INS()NDB$MAX_DEL_WIN_INS()),我们通常将用于确定更新的列称为“时间戳”列。然而,此列的数据类型从不是TIMESTAMP;相反,其数据类型应为INT - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT")(INTEGER - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT"))或BIGINT - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT")。 “时间戳”列还应为UNSIGNEDNOT NULL

本节后面讨论的NDB$EPOCH()NDB$EPOCH_TRANS()函数通过比较应用在主要和次要 NDB 集群上的复制时期的相对顺序来工作,并不使用时间戳。

源列控制

我们可以根据“之前”和“之后”图像来看待更新操作——也就是说,在应用更新之前和之后的表状态。通常,当使用主键更新表时,“之前”图像并不是很重要;然而,当我们需要根据每次更新确定是否在副本上使用更新的值时,我们需要确保两个图像都写入源二进制日志。这是通过--ndb-log-update-as-write选项为mysqld完成的,稍后在本节中描述。

重要

决定是记录完整行还是仅更新列是在启动 MySQL 服务器时完成的,无法在线更改;您必须重新启动mysqld,或者使用不同的日志记录选项启动新的mysqld实例。

冲突解决控制

冲突解决通常在可能发生冲突的服务器上启用。与日志记录方法选择一样,它是通过mysql.ndb_replication表中的条目启用的。

NBT_UPDATED_ONLY_MINIMALNBT_UPDATED_FULL_MINIMAL可与NDB$EPOCH()NDB$EPOCH2()NDB$EPOCH_TRANS()一起使用,因为这些不需要非主键列的“之前”值。需要旧值的冲突解决算法,如NDB$MAX()NDB$OLD(),与这些binlog_type值不正确地配合。

冲突解决函数

本节提供了关于可用于 NDB 复制中冲突检测和解决的函数的详细信息。

  • NDB$OLD()")

  • NDB$MAX()")

  • NDB$MAX_DELETE_WIN()")

  • NDB$MAX_INS()")

  • NDB$MAX_DEL_WIN_INS()")

  • NDB$EPOCH()")

  • NDB$EPOCH_TRANS()")

  • NDB$EPOCH2()")

  • NDB$EPOCH2_TRANS()")

NDB$OLD()

如果源数据和副本上的column_name的值相同,则应用更新;否则,在副本上不应用更新,并将异常写入日志。以下是伪代码示例:

if (*source_old_column_value* == *replica_current_column_value*)
  apply_update();
else
  log_exception();

此函数可用于“相同值获胜”冲突解决。这种冲突解决确保更新不会从错误的源应用于副本。

重要提示

此函数使用源数据的“之前”图像中的列值。

NDB$MAX()

对于更新或删除操作,如果源数据中给定行的“时间戳”列值高于副本中的值,则应用该操作;否则不应用于副本。以下是伪代码示例:

if (*source_new_column_value* > *replica_current_column_value*)
  apply_update();

此函数可用于“最大时间戳获胜”冲突解决。这种冲突解决确保在冲突发生时,最近更新的行版本是持久的版本。

除了拒绝具有与先前写操作相同主键的写操作外,此函数对写操作之间的冲突没有影响;仅当不存在使用相同主键的先前写操作时,才接受并应用该写操作。从 NDB 8.0.30 开始,您可以使用NDB$MAX_INS()")处理写操作之间的冲突解决。

重要

此函数使用源“after”图像的列值。

NDB$MAX_DELETE_WIN()

这是NDB$MAX()的变体。由于删除操作没有时间戳可用,因此使用NDB$MAX()进行的删除实际上被处理为NDB$OLD,但对于某些用例,这并不理想。对于NDB$MAX_DELETE_WIN(),如果来自源的添加或更新现有行的行的“时间戳”列值高于副本上的值,则应用该值。但是,删除操作始终被视为具有更高的值。如下伪代码所示:

if ( (*source_new_column_value* > *replica_current_column_value*)
        ||
      *operation.type* == "delete")
  apply_update();

此函数可用于“最大时间戳,删除获胜”冲突解决。这种冲突解决确保在冲突发生时,被删除或(其他方式)最近更新的行版本是持久的版本。

注意

NDB$MAX()一样,此函数使用源“after”图像的列值。

NDB$MAX_INS()

此函数提供对冲突写操作的支持。这些冲突由“NDB$MAX_INS()”处理如下:

  1. 如果没有冲突写操作,则应用此操作(与NDB$MAX()相同)。

  2. 否则,应用“最大时间戳获胜”冲突解决,如下所示:

    1. 如果传入写操作的时间戳大于冲突写操作的时间戳,则应用传入操作。

    2. 如果传入写操作的时间戳更大,则拒绝传入写操作。

处理插入操作时,NDB$MAX_INS()比较源和副本的时间戳,如下伪代码所示:

if (source_new_column_value > replica_current_column_value)
  apply_insert();
else
  log_exception();

对于更新操作,源的更新时间戳列值与副本的时间戳列值进行比较,如下所示:

if (source_new_column_value > replica_current_column_value)
  apply_update();
else
  log_exception();

这与NDB$MAX()")执行的操作相同。

对于删除操作,处理方式与NDB$MAX()(因此与NDB$OLD())执行的操作相同,如下所示:

if (source_new_column_value == replica_current_column_value)
  apply_delete();
else
  log_exception();

NDB$MAX_INS()添加在 NDB 8.0.30 中。

NDB$MAX_DEL_WIN_INS()

此函数提供对冲突写操作的支持,以及类似于NDB$MAX_DELETE_WIN()")的“删除获胜”解决方案。写冲突由NDB$MAX_DEL_WIN_INS()处理如下:

  1. 如果没有冲突的写入,应用这个(与 NDB$MAX_DELETE_WIN() 相同)。

  2. 否则,应用“最大时间戳获胜”冲突解决,如下所示:

    1. 如果传入写入的时间戳大于冲突写入的时间戳,则应用传入操作。

    2. 如果传入写入的时间戳更大,则拒绝传入的写入操作。

NDB$MAX_DEL_WIN_INS() 执行插入操作的处理可以用伪代码表示如下:

if (source_new_column_value > replica_current_column_value)
  apply_insert();
else
  log_exception();

对于更新操作,源的更新时间戳列值与副本的时间戳列值进行比较,如下所示(再次使用伪代码):

if (source_new_column_value > replica_current_column_value)
  apply_update();
else
  log_exception();

删除使用“删除始终获胜”策略处理(与 NDB$MAX_DELETE_WIN() 相同);DELETE 总是应用,而不考虑任何时间戳值,如下所示的伪代码所示:

if (operation.type == "delete")
  apply_delete();

对于更新和删除操作之间的冲突,此函数的行为与 NDB$MAX_DELETE_WIN() 完全相同。

NDB$MAX_DEL_WIN_INS() 添加在 NDB 8.0.30 中。

NDB$EPOCH()

NDB$EPOCH() 函数跟踪在副本集群上应用的复制时期的顺序,相对于在副本上发起的更改。这种相对顺序用于确定在副本上发起的更改是否与本地发起的任何更改同时发生,因此可能存在冲突。

NDB$EPOCH() 的描述中接下来的大部分内容也适用于 NDB$EPOCH_TRANS()。任何异常情况在文本中有注明。

NDB$EPOCH() 是不对称的,操作在一个 NDB 集群中的双向复制配置(有时称为“主动-主动”复制)。我们在这里将其操作的集群称为主集群,另一个称为从集群。主集群上的副本负责检测和处理冲突,而从集群上的副本不参与任何冲突检测或处理。

当主集群上的副本检测到冲突时,它会向自己的二进制日志中注入事件来补偿这些冲突;这确保了从集群最终重新与主集群对齐,从而使主集群和从集群保持一致。这种补偿和重新对齐机制要求主集群始终在与从集群的冲突中获胜,即主集群的更改始终优先于从集群的更改。这个“主始终获胜”的规则有以下含义:

  • 一旦在主集群上提交更改数据的操作是完全持久的,并且不会被冲突检测和解决所撤消或回滚。

  • 从主集群读取的数据是完全一致的。在主集群上提交的任何更改(本地或来自副本)后来都不会被撤销。

  • 在从集群上更改数据的操作可能会在主集群确定它们存在冲突时稍后被撤销。

  • 在辅助节点上读取的各行始终自洽,每行始终反映出辅助节点提交的状态或主节点提交的状态之一。

  • 在辅助节点上读取的行集在给定的单个时间点上可能不一定一致。对于NDB$EPOCH_TRANS()来说,这是一个瞬时状态;对于NDB$EPOCH()来说,它可以是一个持久状态。

  • 假设在足够长的时间段内没有任何冲突,辅助 NDB 集群上的所有数据(最终)都会与主节点的数据保持一致。

NDB$EPOCH()NDB$EPOCH_TRANS()不需要任何用户模式修改或应用更改来提供冲突检测。然而,必须仔细考虑所使用的模式和访问模式,以验证整个系统是否在指定的限制内运行。

NDB$EPOCH()NDB$EPOCH_TRANS()函数中的每一个都可以接受一个可选参数;这是用来表示时代低 32 位的比特数,应设置为不少于如下计算所示的值:

CEIL( LOG2( TimeBetweenGlobalCheckpoints / TimeBetweenEpochs ), 1)

对于这些配置参数的默认值(分别为 2000 和 100 毫秒),这给出了一个值为 5 位,因此默认值(6)应该足够,除非为TimeBetweenGlobalCheckpointsTimeBetweenEpochs或两者都使用了其他值。值太小可能导致误报,而值太大可能导致数据库中浪费的空间过多。

NDB$EPOCH()NDB$EPOCH_TRANS()都会将冲突行的条目插入相关的异常表中,前提是这些表已根据本节其他地方描述的相同异常表模式规则进行了定义(参见 NDB$OLD()"))。你必须在创建要使用的数据表之前创建任何异常表。

与本节讨论的其他冲突检测函数一样,通过在mysql.ndb_replication表中包含相关条目来激活NDB$EPOCH()NDB$EPOCH_TRANS()(参见 ndb_replication 表)。在这种情况下,主 NDB 集群和辅助 NDB 集群的角色完全由mysql.ndb_replication表中的条目确定。

由于NDB$EPOCH()NDB$EPOCH_TRANS()所采用的冲突检测算法是不对称的,你必须为主节点和辅助节点副本的server_id条目使用不同的值。

仅仅 DELETE 操作之间的冲突不足以触发使用NDB$EPOCH()NDB$EPOCH_TRANS()的冲突,而且时代内的相对位置并不重要。

NDB$EPOCH()的限制

当使用NDB$EPOCH()执行冲突检测时,目前存在以下限制:

  • 使用 NDB 集群时代边界来检测冲突,粒度与TimeBetweenEpochs成比例(默认值:100 毫秒)。最小冲突窗口是同时在两个集群上对同一数据进行并发更新时始终报告冲突的最短时间。这始终是一个非零长度的时间,并且大致与2 *(延迟+排队+TimeBetweenEpochs)成比例。这意味着——假设默认为TimeBetweenEpochs并忽略集群之间的任何延迟(以及任何排队延迟)——最小冲突窗口大小约为 200 毫秒。在查看预期应用程序“竞争”模式时,应考虑这个最小窗口。

  • 使用NDB$EPOCH()NDB$EPOCH_TRANS()函数的表需要额外的存储空间;每行需要额外的 1 到 32 位空间,具体取决于传递给函数的值。

  • 删除操作之间的冲突可能导致主要和次要之间的分歧。当同时在两个集群上删除一行时,冲突可以被检测到,但不会被记录,因为行已被删除。这意味着在任何后续重新对齐操作的传播过程中不会检测到进一步的冲突,这可能导致分歧。

    删除应该外部串行化,或者路由到一个集群。或者,应该在这些删除和随后的任何插入事务中事务更新一个单独的行,以便跟踪冲突。这可能需要应用程序的更改。

  • 当使用NDB$EPOCH()NDB$EPOCH_TRANS()进行冲突检测时,目前仅支持双向“主动-主动”配置中的两个 NDB 集群。

  • 具有BLOBTEXT列的表目前不支持使用NDB$EPOCH()NDB$EPOCH_TRANS()

NDB$EPOCH_TRANS()

NDB$EPOCH_TRANS()扩展了NDB$EPOCH()函数。冲突使用“主要获胜”规则(参见 NDB\(EPOCH()"))进行检测和处理,但额外条件是在发生冲突的同一事务中更新的任何其他行也被视为冲突。换句话说,`NDB\)EPOCH()在次要上重新对齐单个冲突行,而NDB$EPOCH_TRANS()`在冲突事务上重新对齐。

此外,任何可检测依赖于冲突事务的事务也被视为冲突,这些依赖关系由次要集群的二进制日志内容确定。由于二进制日志仅包含数据修改操作(插入、更新和删除),因此只有重叠的数据修改用于确定事务之间的依赖关系。

NDB$EPOCH_TRANS()受到与NDB$EPOCH()相同的条件和限制,并且还要求所有事务 ID 都记录在次要的二进制日志中,使用--ndb-log-transaction-id设置为ON。这会增加可变的开销量(每行最多 13 个字节)。

废弃的log_bin_use_v1_row_events系统变量,默认值为OFF应该与NDB$EPOCH_TRANS()一起设置为ON

参见 NDB$EPOCH()")。

NDB$EPOCH2()

NDB$EPOCH2()函数类似于NDB$EPOCH(),不同之处在于NDB$EPOCH2()提供了双向复制拓扑的删除-删除处理。在这种情况下,通过在每个源上设置ndb_slave_conflict_role系统变量的适当值(通常一个PRIMARY,一个SECONDARY)来为两个源分配主要和次要角色。完成此操作后,次要所做的修改会由主要反映回次要,然后有条件地应用这些修改。

NDB$EPOCH2_TRANS()

NDB$EPOCH2_TRANS()扩展了NDB$EPOCH2()函数。冲突的检测和处理方式相同,并为复制集群分配主要和次要角色,但额外条件是在发生冲突的同一事务中更新的任何其他行也被视为冲突。也就是说,NDB$EPOCH2()在次要上重新调整单个冲突行,而NDB$EPOCH_TRANS()重新调整冲突事务。

NDB$EPOCH()NDB$EPOCH_TRANS()使用每行指定的元数据,每个最后修改的时代,在主键上确定来自辅助的复制行更改是否与本地提交的更改同时发生;同时发生的更改被视为冲突,随后的异常表更新和辅助的重新对齐。当主键上的行被删除时,不再有任何最后修改的时代可用来确定任何复制操作是否冲突,这意味着不会检测到冲突的删除操作。这可能导致分歧,例如一个集群上的删除与另一个集群上的删除和插入同时发生;这就是在使用NDB$EPOCH()NDB$EPOCH_TRANS()时删除操作只能路由到一个集群的原因。

NDB$EPOCH2()通过忽略任何删除-删除冲突的问题—在主键上存储有关已删除行的信息—并避免任何潜在的结果分歧。通过将成功应用并从辅助复制的任何操作反射回辅助,可以在辅助上重新应用由来自主键的操作删除的操作。

当使用NDB$EPOCH2()时,您应该记住,辅助会应用从主键删除的操作,直到通过反射操作恢复新行。理论上,辅助上的后续插入或更新与主键上的删除冲突,但在这种情况下,我们选择忽略这一点,并允许辅助“获胜”,以防止集群之间的分歧。换句话说,在删除后,主键不会检测到冲突,而是立即采纳辅助的后续更改。因此,随着辅助向最终(稳定)状态前进,辅助的状态可以重新访问多个先前提交的状态,并且其中一些可能是可见的。

您还应该意识到,将所有操作从辅助反射回主键会增加主键的 logbinary 日志大小,以及对带宽、CPU 使用率和磁盘 I/O 的需求。

应用在辅助上的反射操作取决于辅助上目标行的状态。可以通过检查Ndb_conflict_reflected_op_prepare_countNdb_conflict_reflected_op_discard_count状态变量来跟踪在辅助上是否应用了反射更改。应用的更改数量简单地是这两个值之间的差异(请注意Ndb_conflict_reflected_op_prepare_count始终大于或等于Ndb_conflict_reflected_op_discard_count)。

事件仅在以下两个条件都为真时应用:

  • 行的存在与否,即它是否存在,与事件类型一致。对于删除和更新操作,行必须已经存在。对于插入操作,行必须不存在

  • 行是由主要操作修改的。可能是通过执行反射操作完成修改的。

如果这两个条件不满足,则次要操作将被次要方丢弃。

冲突解决异常表

要使用NDB$OLD()冲突解决函数,还需要为每个要使用此类冲突解决的NDB表创建一个对应的异常表。当使用NDB$EPOCH()NDB$EPOCH_TRANS()时也是如此。这个表的名称与要应用冲突解决的表的名称相同,只是在末尾添加了字符串$EX。(例如,如果原始表的名称是mytable,则相应的异常表名称应为mytable$EX。)创建异常表的语法如下所示:

CREATE TABLE *original_table*$EX  (
    [NDB$]server_id INT UNSIGNED,
    [NDB$]source_server_id INT UNSIGNED,
    [NDB$]source_epoch BIGINT UNSIGNED,
    [NDB$]count INT UNSIGNED,

    [NDB$OP_TYPE ENUM('WRITE_ROW','UPDATE_ROW', 'DELETE_ROW',
      'REFRESH_ROW', 'READ_ROW') NOT NULL,]
    [NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
      'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,]
    [NDB$ORIG_TRANSID BIGINT UNSIGNED NOT NULL,]

    *original_table_pk_columns*,

    [*orig_table_column*|*orig_table_column*$OLD|*orig_table_column*$NEW,]

    [*additional_columns*,]

    PRIMARY KEY([NDB$]server_id, [NDB$]source_server_id, [NDB$]source_epoch, [NDB$]count)
) ENGINE=NDB;

前四列是必需的。前四列的名称和与原始表主键列匹配的列的名称并不重要;但是,出于清晰和一致性的原因,我们建议您使用此处显示的server_idsource_server_idsource_epochcount列的名称,并且对于与原始表主键匹配的列,使用与原始表中相同的名称。

如果异常表使用本节后面讨论的一个或多个可选列NDB$OP_TYPENDB$CFT_CAUSENDB$ORIG_TRANSID,则每个必需列也必须使用前缀NDB$命名。如果需要,即使您不定义任何可选列,也可以使用NDB$前缀命名必需列,但在这种情况下,所有四个必需列必须使用前缀命名。

在这些列之后,应按照用于定义原始表主键的顺序复制构成原始表主键的列。复制原始表主键列的列的数据类型应与原始列相同(或更大)。可以使用主键列的子集。

异常表必须使用NDB存储引擎。(本节后面将展示使用带有异常表的NDB$OLD()的示例。)

可以在复制的主键列后面选择性地定义其他列,但不能在它们之前;任何此类额外列都不能为 NOT NULL。NDB 集群支持三个额外的预定义可选列 NDB$OP_TYPENDB$CFT_CAUSENDB$ORIG_TRANSID,这些列在接下来的几段中描述。

NDB$OP_TYPE:此列可用于获取导致冲突的操作类型。如果使用此列,请按以下所示定义:

NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW',
    'REFRESH_ROW', 'READ_ROW') NOT NULL

WRITE_ROWUPDATE_ROWDELETE_ROW 操作类型代表用户发起的操作。REFRESH_ROW 操作是由冲突解决在补偿事务中生成的操作,从检测到冲突的集群发送回原始集群。READ_ROW 操作是由用户发起的读跟踪操作,使用独占行锁定义。

NDB$CFT_CAUSE:您可以定义一个可选列 NDB$CFT_CAUSE,提供注册冲突的原因。如果使用此列,则应按以下所示定义该列:

NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
    'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL

ROW_DOES_NOT_EXIST 可以报告为 UPDATE_ROWWRITE_ROW 操作的原因;ROW_ALREADY_EXISTS 可以报告为 WRITE_ROW 事件的原因。当基于行的冲突函数检测到冲突时,报告 DATA_IN_CONFLICT;当事务冲突函数拒绝完整事务的所有操作时,报告 TRANS_IN_CONFLICT

NDB$ORIG_TRANSID:如果使用 NDB$ORIG_TRANSID 列,则该列包含原始事务的 ID。应按以下方式定义此列:

NDB$ORIG_TRANSID BIGINT UNSIGNED NOT NULL

NDB$ORIG_TRANSID 是由 NDB 生成的 64 位值。此值可用于关联属于相同冲突事务的多个异常表条目,这些条目来自相同或不同的异常表。

附加的参考列,不是原始表的主键的一部分,可以命名为 *colname*$OLD*colname*$NEW*colname*$OLD 引用更新和删除操作中的旧值,即包含 DELETE_ROW 事件的操作。 *colname*$NEW 可用于引用插入和更新操作中的新值,换句话说,使用 WRITE_ROW 事件、UPDATE_ROW 事件或两种事件的操作。如果冲突操作未为给定的非主键参考列提供值,则异常表行包含 NULL 或该列的定义默认值。

重要

当数据表设置为复制时,将读取 mysql.ndb_replication 表,因此在创建要复制的表之前,必须将对应于要复制的表的行插入到 mysql.ndb_replication 中。

冲突检测状态变量

几个状态变量可用于监视冲突检测。您可以通过Ndb_conflict_fn_epoch系统状态变量的当前值,查看自此副本上次从头启动以来,由NDB$EPOCH()发现的冲突行数。

Ndb_conflict_fn_epoch_trans提供了由NDB$EPOCH_TRANS()直接发现的冲突行数。Ndb_conflict_fn_epoch2Ndb_conflict_fn_epoch2_trans分别显示了由NDB$EPOCH2()NDB$EPOCH2_TRANS()发现的冲突行数。实际重新对齐的行数,包括由于它们属于或依赖于与其他冲突行相同事务的行受到影响的行数,由Ndb_conflict_trans_row_reject_count给出。

另一个服务器状态变量Ndb_conflict_fn_max提供了自上次mysqld启动以来,由于“最大时间戳获胜”冲突解决而未在当前 SQL 节点上应用的行数。Ndb_conflict_fn_max_del_win提供了基于NDB$MAX_DELETE_WIN()结果的冲突解决已应用的次数。

NDB 8.0.30 及更高版本提供了Ndb_conflict_fn_max_ins,用于跟踪“更大时间戳获胜”处理已应用于写操作的次数(使用NDB$MAX_INS());由状态变量Ndb_conflict_fn_max_del_win_ins提供了“相同时间戳获胜”写操作处理已应用的次数(由NDB$MAX_DEL_WIN_INS()实现)。

自上次mysqld重新启动以来,由于“相同时间戳获胜”冲突解决而未应用的行数由全局状态变量Ndb_conflict_fn_old给出。除了递增Ndb_conflict_fn_old外,未使用的行的主键被插入到异常表中,如本节其他地方所述。

另请参阅 Section 25.4.3.9.3, “NDB Cluster Status Variables”。

示例

以下示例假定您已经有一个正常工作的 NDB Cluster 复制设置,如 Section 25.7.5, “Preparing the NDB Cluster for Replication” 和 Section 25.7.6, “Starting NDB Cluster Replication (Single Replication Channel)”") 中所述。

NDB$MAX() 示例。 假设你希望在表 test.t1 上启用“最大时间戳获胜”冲突解决,使用列 mycol 作为“时间戳”。可以通过以下步骤完成:

  1. 确保你已经使用 --ndb-log-update-as-write=OFF 启动了源 mysqld

  2. 在源上执行这个 INSERT 语句:

    INSERT INTO mysql.ndb_replication
        VALUES ('test', 't1', 0, NULL, 'NDB$MAX(mycol)');
    

    注意

    如果 ndb_replication 表尚不存在,则必须创建它。请参阅 ndb_replication Table。

    将 0 插入到 server_id 列中表示所有访问该表的 SQL 节点应该使用冲突解决。如果你只想在特定的 mysqld 上使用冲突解决,使用实际的服务器 ID。

    NULL 插入到 binlog_type 列中与插入 0 (NBT_DEFAULT) 具有相同的效果;使用服务器默认值。

  3. 创建 test.t1 表:

    CREATE TABLE test.t1 (
        *columns*
        mycol INT UNSIGNED,
        *columns*
    ) ENGINE=NDB;
    

    现在,当对该表执行更新时,将应用冲突解决,并将具有 mycol 最大值的行版本写入副本。

注意

其他 binlog_type 选项,如 NBT_UPDATED_ONLY_USE_UPDATE (6),应该使用 ndb_replication 表来控制源上的日志记录,而不是使用命令行选项。

NDB$OLD() 示例。 假设正在复制一个 NDB 表,如此处定义的表,并且你希望为更新到该表的“相同时间戳获胜”冲突解决启用:

CREATE TABLE test.t2  (
    a INT UNSIGNED NOT NULL,
    b CHAR(25) NOT NULL,
    *columns*,
    mycol INT UNSIGNED NOT NULL,
    *columns*,
    PRIMARY KEY pk (a, b)
)   ENGINE=NDB;

需要按照以下顺序执行以下步骤:

  1. 首先——创建 test.t2 之前——你必须像这样向 mysql.ndb_replication 表中插入一行:

    INSERT INTO mysql.ndb_replication
        VALUES ('test', 't2', 0, 0, 'NDB$OLD(mycol)');
    

    binlog_type 列的可能值在本节中已经显示;在这种情况下,我们使用 0 来指定使用服务器默认的日志记录行为。值 'NDB$OLD(mycol)' 应该插入到 conflict_fn 列中。

  2. test.t2创建一个适当的异常表。此处显示的表创建语句包括所有必需列;任何额外列必须在这些列之后声明,并在表的主键定义之前。

    CREATE TABLE test.t2$EX  (
        server_id INT UNSIGNED,
        source_server_id INT UNSIGNED,
        source_epoch BIGINT UNSIGNED,
        count INT UNSIGNED,
        a INT UNSIGNED NOT NULL,
        b CHAR(25) NOT NULL,
    
        [*additional_columns*,]
    
        PRIMARY KEY(server_id, source_server_id, source_epoch, count)
    )   ENGINE=NDB;
    

    我们可以为给定冲突的类型、原因和起始事务 ID 包含额外列。我们也不需要为原始表中的所有主键列提供匹配列。这意味着您可以像这样创建异常表:

    CREATE TABLE test.t2$EX  (
        NDB$server_id INT UNSIGNED,
        NDB$source_server_id INT UNSIGNED,
        NDB$source_epoch BIGINT UNSIGNED,
        NDB$count INT UNSIGNED,
        a INT UNSIGNED NOT NULL,
    
        NDB$OP_TYPE ENUM('WRITE_ROW','UPDATE_ROW', 'DELETE_ROW',
          'REFRESH_ROW', 'READ_ROW') NOT NULL,
        NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
          'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,
        NDB$ORIG_TRANSID BIGINT UNSIGNED NOT NULL,
    
        [*additional_columns*,]
    
        PRIMARY KEY(NDB$server_id, NDB$source_server_id, NDB$source_epoch, NDB$count)
    )   ENGINE=NDB;
    

    注意

    由于我们在表定义中至少包含了NDB$OP_TYPENDB$CFT_CAUSENDB$ORIG_TRANSID中的一个列,因此四个必需列需要使用NDB$前缀。

  3. 如前所示创建test.t2表。

这些步骤必须针对每个要使用NDB$OLD()执行冲突解决的表进行遵循。对于每个这样的表,必须在mysql.ndb_replication中有一个相应的行,并且在被复制的表所在的同一数据库中必须有一个异常表。

阅读冲突检测和解决。 NDB Cluster 还支持跟踪读操作,这使得在循环复制设置中可以管理一个集群中给定行的读取与另一个集群中相同行的更新或删除之间的冲突。此示例使用employeedepartment表来模拟这样一种情况:在源集群(以下简称为集群A)中将员工从一个部门移动到另一个部门,而副本集群(以下简称为B)在交错事务中更新员工以前部门的员工计数。

数据表已使用以下 SQL 语句创建:

# Employee table
CREATE TABLE employee (
    id INT PRIMARY KEY,
    name VARCHAR(2000),
    dept INT NOT NULL
)   ENGINE=NDB;

# Department table
CREATE TABLE department (
    id INT PRIMARY KEY,
    name VARCHAR(2000),
    members INT
)   ENGINE=NDB;

两个表的内容包括以下SELECT语句的(部分)输出中显示的行:

mysql> SELECT id, name, dept FROM employee;
+---------------+------+
| id   | name   | dept |
+------+--------+------+
...
| 998  |  Mike  | 3    |
| 999  |  Joe   | 3    |
| 1000 |  Mary  | 3    |
...
+------+--------+------+

mysql> SELECT id, name, members FROM department;
+-----+-------------+---------+
| id  | name        | members |
+-----+-------------+---------+
...
| 3   | Old project | 24      |
...
+-----+-------------+---------+

我们假设我们已经在使用包含四个必需列(并且这些列用于此表的主键)的异常表,操作类型和原因的可选列,以及原始表的主键列,使用此处显示的 SQL 语句创建:

CREATE TABLE employee$EX  (
    NDB$server_id INT UNSIGNED,
    NDB$source_server_id INT UNSIGNED,
    NDB$source_epoch BIGINT UNSIGNED,
    NDB$count INT UNSIGNED,

    NDB$OP_TYPE ENUM( 'WRITE_ROW','UPDATE_ROW', 'DELETE_ROW',
                      'REFRESH_ROW','READ_ROW') NOT NULL,
    NDB$CFT_CAUSE ENUM( 'ROW_DOES_NOT_EXIST',
                        'ROW_ALREADY_EXISTS',
                        'DATA_IN_CONFLICT',
                        'TRANS_IN_CONFLICT') NOT NULL,

    id INT NOT NULL,

    PRIMARY KEY(NDB$server_id, NDB$source_server_id, NDB$source_epoch, NDB$count)
)   ENGINE=NDB;

假设在两个集群上发生了两个同时事务。在集群A上,我们创建一个新部门,然后将员工编号 999 移入该部门,使用以下 SQL 语句:

BEGIN;
  INSERT INTO department VALUES (4, "New project", 1);
  *UPDATE employee SET dept = 4 WHERE id = 999;* COMMIT;

与此同时,在集群B上,另一个事务从employee中读取,如下所示:

BEGIN;
  *SELECT name FROM employee WHERE id = 999;* UPDATE department SET members = members - 1  WHERE id = 3;
commit;

冲突解决机制通常不会检测到冲突的事务,因为冲突是在读取(SELECT)和更新操作之间。您可以通过在复制集群上执行SET ndb_log_exclusive_reads = 1来解决此问题。以这种方式获取独占读锁会导致在源上读取的任何行在复制集群上被标记为需要冲突解决。如果在记录这些事务之前以这种方式启用独占读取,则在集群B上的读取将被跟踪并发送到集群A进行解决;随后检测到员工行上的冲突,并且在集群B上的事务被中止。

冲突在异常表(在集群A上)中注册为READ_ROW操作(参见冲突解决异常表,了解操作类型的描述),如下所示:

mysql> SELECT id, NDB$OP_TYPE, NDB$CFT_CAUSE FROM employee$EX;
+-------+-------------+-------------------+
| id    | NDB$OP_TYPE | NDB$CFT_CAUSE     |
+-------+-------------+-------------------+
...
| 999   | READ_ROW    | TRANS_IN_CONFLICT |
+-------+-------------+-------------------+

在读操作中找到的任何现有行都会被标记。这意味着由于同一冲突导致的多行可能会在异常表中记录,如通过检查在同时事务中在集群A上进行更新和在集群B上从相同表读取多行之间的冲突的影响所示。在集群A上执行的事务如下所示:

BEGIN;
  INSERT INTO department VALUES (4, "New project", 0);
  *UPDATE employee SET dept = 4 WHERE dept = 3;* SELECT COUNT(*) INTO @count FROM employee WHERE dept = 4;
  UPDATE department SET members = @count WHERE id = 4;
COMMIT;

同时,在集群B上运行包含以下语句的事务:

SET ndb_log_exclusive_reads = 1;  # Must be set if not already enabled
...
BEGIN;
  *SELECT COUNT(*) INTO @count FROM employee WHERE dept = 3 FOR UPDATE;* UPDATE department SET members = @count WHERE id = 3;
COMMIT;

在这种情况下,第二个事务的SELECT中匹配WHERE条件的所有三行都被读取,并因此在异常表中标记,如下所示:

mysql> SELECT id, NDB$OP_TYPE, NDB$CFT_CAUSE FROM employee$EX;
+-------+-------------+-------------------+
| id    | NDB$OP_TYPE | NDB$CFT_CAUSE     |
+-------+-------------+-------------------+
...
| 998   | READ_ROW    | TRANS_IN_CONFLICT |
| 999   | READ_ROW    | TRANS_IN_CONFLICT |
| 1000  | READ_ROW    | TRANS_IN_CONFLICT |
...
+-------+-------------+-------------------+

读跟踪仅基于现有行执行。基于给定条件的读取仅跟踪找到的任何行的冲突,而不是插入在交错事务中的任何行。这类似于在单个 NDB 集群实例中执行独占行锁定的方式。

插入冲突检测和解决示例(NDB 8.0.30 及更高版本)。 以下示例说明了在 NDB 8.0.30 中添加的插入冲突检测功能的使用。我们假设我们正在复制数据库test中的两个表t1t2,并且希望对t1使用NDB$MAX_INS()进行插入冲突检测,对t2使用NDB$MAX_DEL_WIN_INS()进行插入冲突检测。这两个数据表直到设置过程的后期才会创建。

设置插入冲突解决类似于设置其他冲突检测和解决算法,如前面的示例所示。如果用于配置二进制日志记录和冲突解决的mysql.ndb_replication表尚不存在,则首先需要创建它,如下所示:

CREATE TABLE mysql.ndb_replication (
    db VARBINARY(63),
    table_name VARBINARY(63),
    server_id INT UNSIGNED,
    binlog_type INT UNSIGNED,
    conflict_fn VARBINARY(128),
    PRIMARY KEY USING HASH (db, table_name, server_id)
) ENGINE=NDB 
PARTITION BY KEY(db,table_name);

ndb_replication表是基于每个表的基础操作的;也就是说,我们需要插入一行包含表信息、binlog_type值、要使用的冲突解决函数以及时间戳列(X)的名称,就像这样:

INSERT INTO mysql.ndb_replication VALUES ("test", "t1", 0, 7, "NDB$MAX_INS(X)");
INSERT INTO mysql.ndb_replication VALUES ("test", "t2", 0, 7, "NDB$MAX_DEL_WIN_INS(X)");

在这里,我们将 binlog_type 设置为NBT_FULL_USE_UPDATE7),这意味着始终记录完整行。有关其他可能值,请参见 ndb_replication Table。

您还可以为每个需要使用冲突解决的NDB表创建一个异常表。异常表记录由给定表的冲突解决函数拒绝的所有行。可以使用以下两个 SQL 语句为表t1t2创建用于复制冲突检测的异常表:

CREATE TABLE `t1$EX` (
    NDB$server_id INT UNSIGNED,
    NDB$master_server_id INT UNSIGNED,
    NDB$master_epoch BIGINT UNSIGNED,
    NDB$count INT UNSIGNED,
    NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW', 
                     'REFRESH_ROW', 'READ_ROW') NOT NULL,
    NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
                       'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,
    a INT NOT NULL,
    PRIMARY KEY(NDB$server_id, NDB$master_server_id, 
                NDB$master_epoch, NDB$count)
) ENGINE=NDB;

CREATE TABLE `t2$EX` (
    NDB$server_id INT UNSIGNED,
    NDB$master_server_id INT UNSIGNED,
    NDB$master_epoch BIGINT UNSIGNED,
    NDB$count INT UNSIGNED,
    NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW',
                     'REFRESH_ROW', 'READ_ROW') NOT NULL,
    NDB$CFT_CAUSE ENUM( 'ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
                        'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,
    a INT NOT NULL,
    PRIMARY KEY(NDB$server_id, NDB$master_server_id, 
                NDB$master_epoch, NDB$count)
) ENGINE=NDB;

最后,在刚刚显示的创建异常表之后,您可以创建要复制并受冲突解决控制的数据表,使用以下两个 SQL 语句:

CREATE TABLE t1 (
    a INT PRIMARY KEY, 
    b VARCHAR(32), 
    X INT UNSIGNED
) ENGINE=NDB;

CREATE TABLE t2 (
    a INT PRIMARY KEY, 
    b VARCHAR(32), 
    X INT UNSIGNED
) ENGINE=NDB;

对于每个表,X列被用作时间戳列。

一旦在源上创建了t1t2,它们就会被复制并假定存在于源和副本上。在本示例的其余部分中,我们使用mysqlS>表示连接到源的mysql客户端,使用mysqlR>表示在副本上运行的mysql客户端。

首先,我们在源上分别插入一行到每个表中,就像这样:

mysqlS> INSERT INTO t1 VALUES (1, 'Initial X=1', 1);
Query OK, 1 row affected (0.01 sec)

mysqlS> INSERT INTO t2 VALUES (1, 'Initial X=1', 1);
Query OK, 1 row affected (0.01 sec)

我们可以确定这两行在不引起任何冲突的情况下被复制,因为在在源上发出INSERT语句之前,副本上的表不包含任何行。我们可以通过从副本中的表中选择来验证这一点,就像这样:

mysqlR> TABLE t1 ORDER BY a;
+---+-------------+------+
| a | b           | X    |
+---+-------------+------+
| 1 | Initial X=1 |    1 |
+---+-------------+------+
1 row in set (0.00 sec)

mysqlR> TABLE t2 ORDER BY a;
+---+-------------+------+
| a | b           | X    |
+---+-------------+------+
| 1 | Initial X=1 |    1 |
+---+-------------+------+
1 row in set (0.00 sec)

接下来,我们在副本中插入新行到表中,就像这样:

mysqlR> INSERT INTO t1 VALUES (2, 'Replica X=2', 2);
Query OK, 1 row affected (0.01 sec)

mysqlR> INSERT INTO t2 VALUES (2, 'Replica X=2', 2);
Query OK, 1 row affected (0.01 sec)

现在我们在源上插入具有更大时间戳(X)列值的冲突行,使用下面显示的语句:

mysqlS> INSERT INTO t1 VALUES (2, 'Source X=20', 20);
Query OK, 1 row affected (0.01 sec)

mysqlS> INSERT INTO t2 VALUES (2, 'Source X=20', 20);
Query OK, 1 row affected (0.01 sec)

现在我们通过再次从副本上的两个表中选择来观察结果,就像这样:

mysqlR> TABLE t1 ORDER BY a;
+---+-------------+-------+
| a | b           | X     |
+---+-------------+-------+
| 1 | Initial X=1 |    1  |
+---+-------------+-------+
| 2 | Source X=20 |   20  |
+---+-------------+-------+
2 rows in set (0.00 sec)

mysqlR> TABLE t2 ORDER BY a;
+---+-------------+-------+
| a | b           | X     |
+---+-------------+-------+
| 1 | Initial X=1 |    1  |
+---+-------------+-------+
| 1 | Source X=20 |   20  |
+---+-------------+-------+
2 rows in set (0.00 sec)

在源上插入的行,其时间戳大于副本中冲突行的时间戳,已替换了那些行。在副本上,我们接下来插入两行新行,这些行不与t1t2中的任何现有行冲突,就像这样:

mysqlR> INSERT INTO t1 VALUES (3, 'Slave X=30', 30);
Query OK, 1 row affected (0.01 sec)

mysqlR> INSERT INTO t2 VALUES (3, 'Slave X=30', 30);
Query OK, 1 row affected (0.01 sec)

在源上插入具有相同主键值(3)的更多行会引起冲突,但这次我们使用时间戳列中小于副本中冲突行中相同列中的时间戳的值。

mysqlS> INSERT INTO t1 VALUES (3, 'Source X=3', 3);
Query OK, 1 row affected (0.01 sec)

mysqlS> INSERT INTO t2 VALUES (3, 'Source X=3', 3);
Query OK, 1 row affected (0.01 sec)

通过查询表格,我们可以看到源端插入的两行都被副本端拒绝了,并且副本端之前插入的行也没有被覆盖,就像在副本端的mysql客户端中所展示的那样:

mysqlR> TABLE t1 ORDER BY a;
+---+--------------+-------+
| a | b            | X     |
+---+--------------+-------+
| 1 |  Initial X=1 |    1  |
+---+--------------+-------+
| 2 |  Source X=20 |   20  |
+---+--------------+-------+
| 3 | Replica X=30 |   30  |
+---+--------------+-------+
3 rows in set (0.00 sec)

mysqlR> TABLE t2 ORDER BY a;
+---+--------------+-------+
| a | b            | X     |
+---+--------------+-------+
| 1 |  Initial X=1 |    1  |
+---+--------------+-------+
| 2 |  Source X=20 |   20  |
+---+--------------+-------+
| 3 | Replica X=30 |   30  |
+---+--------------+-------+
3 rows in set (0.00 sec)

您可以在异常表中查看被拒绝的行的信息,如下所示:

mysqlR> SELECT  NDB$server_id, NDB$master_server_id, NDB$count,
      >         NDB$OP_TYPE, NDB$CFT_CAUSE, a
      > FROM t1$EX
      > ORDER BY NDB$count\G
*************************** 1\. row ***************************
NDB$server_id       : 2
NDB$master_server_id: 1
NDB$count           : 1
NDB$OP_TYPE         : WRITE_ROW
NDB$CFT_CAUSE       : DATA_IN_CONFLICT
a                   : 3 1 row in set (0.00 sec)

mysqlR> SELECT  NDB$server_id, NDB$master_server_id, NDB$count,
      >         NDB$OP_TYPE, NDB$CFT_CAUSE, a
      > FROM t2$EX
      > ORDER BY NDB$count\G
*************************** 1\. row ***************************
NDB$server_id       : 2
NDB$master_server_id: 1
NDB$count           : 1
NDB$OP_TYPE         : WRITE_ROW
NDB$CFT_CAUSE       : DATA_IN_CONFLICT
a                   : 3 1 row in set (0.00 sec)

正如我们之前所看到的,源端插入的其他行并没有被副本端拒绝,只有那些时间戳值较小于副本端冲突行的行被拒绝。

25.8 NDB Cluster 发行说明

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-news.html

NDB Cluster 发行版本的更改与本参考手册分开记录;您可以在 NDB 8.0 Release Notes 找到每个 NDB Cluster 8.0 版本更改的发行说明。

您可以从 NDB Cluster Release Notes 获取旧版本 NDB Cluster 的发行说明。

第二十六章 分区

原文:dev.mysql.com/doc/refman/8.0/en/partitioning.html

目录

26.1 MySQL 中分区的概述

26.2 分区类型

26.2.1 范围分区

26.2.2 LIST 分区

26.2.3 列分区

26.2.4 HASH 分区

26.2.5 KEY 分区

26.2.6 子分区

26.2.7 MySQL 分区如何处理 NULL 值

26.3 分区管理

26.3.1 管理范围和列表分区

26.3.2 管理 HASH 和 KEY 分区

26.3.3 与表交换分区和子分区

26.3.4 分区的维护

26.3.5 获取有关分区的信息

26.4 分区修剪

26.5 分区选择

26.6 分区的限制和限制

26.6.1 分区键、主键和唯一键

26.6.2 与存储引擎相关的分区限制

26.6.3 与函数相关的分区限制

本章讨论用户定义的分区。

注意

表分区与窗口函数中使用的分区不同。有关窗口函数的信息,请参见第 14.20 节,“窗口函数”。

在 MySQL 8.0 中,分区支持由InnoDBNDB存储引擎提供。

MySQL 8.0 目前不支持使用除InnoDBNDB之外的任何存储引擎对表进行分区,例如MyISAM。尝试使用不提供本机分区支持的存储引擎创建分区表将失败,并显示ER_CHECK_NOT_IMPLEMENTED

由 Oracle 提供的 MySQL 8.0 社区二进制文件包括由InnoDBNDB存储引擎提供的分区支持。有关 MySQL 企业版二进制文件中提供的分区支持的信息,请参见第三十二章,MySQL 企业版

如果您正在从源代码编译 MySQL 8.0,配置构建以支持InnoDB即可生成具有InnoDB表分区支持的二进制文件。有关更多信息,请参见第 2.8 节,“从源代码安装 MySQL”。

无需执行任何其他操作即可启用InnoDB的分区支持(例如,在my.cnf文件中不需要特殊条目)。

无法禁用InnoDB存储引擎的分区支持。

查看第 26.1 节,“MySQL 中分区概述”,介绍了分区和分区概念。

支持多种类型的分区,以及子分区;参见第 26.2 节,“分区类型”和第 26.2.6 节,“子分区”。

第 26.3 节,“分区管理”,介绍了在现有分区表中添加、删除和修改分区的方法。

第 26.3.4 节,“分区维护”,讨论了用于分区表的表维护命令。

INFORMATION_SCHEMA数据库中的PARTITIONS表提供有关分区和分区表的信息。有关更多信息,请参见第 28.3.21 节,“INFORMATION_SCHEMA PARTITIONS 表”;有关针对此表的一些查询示例,请参见第 26.2.7 节,“MySQL 分区如何处理 NULL”。

有关 MySQL 8.0 中分区的已知问题,请参见第 26.6 节,“分区的限制和限制”。

在处理分区表时,您可能还会发现以下资源很有用。

额外资源。 关于 MySQL 中用户定义分区的其他信息来源包括以下内容:

  • MySQL 分区论坛

    这是官方讨论论坛,供对 MySQL 分区技术感兴趣或正在尝试该技术的人使用。它包括来自 MySQL 开发人员和其他人的公告和更新。由分区开发和文档团队的成员监控。

  • PlanetMySQL

    一个 MySQL 新闻网站,提供与 MySQL 相关的博客,对于任何使用 MySQL 的人都应该感兴趣。我们鼓励您查看这里的链接,这些链接由使用 MySQL 分区的人维护,或者将您自己的博客添加到其中。

26.1 MySQL 中分区的概述

译文:dev.mysql.com/doc/refman/8.0/en/partitioning-overview.html

本节提供了 MySQL 8.0 中分区的概念概述。

有关分区限制和功能限制的信息,请参见 第 26.6 节,“分区的限制和限制”。

SQL 标准在数据存储的物理方面并没有提供太多指导。SQL 语言本身旨在独立于其所使用的模式、表、行或列的任何数据结构或媒体而工作。尽管如此,大多数先进的数据库管理系统已经发展出一些确定特定数据存储的物理位置的方法,涉及文件系统、硬件甚至两者。在 MySQL 中,InnoDB 存储引擎长期以来一直支持表空间的概念(参见 第 17.6.3 节,“表空间”),即使在引入分区之前,MySQL 服务器也可以配置为使用不同的物理目录来存储不同的数据库(参见 第 10.12.2 节,“使用符号链接”,了解如何实现)。

分区通过使您能够根据您基本上可以根据需要设置的规则将个别表的部分分布到文件系统中,进一步发展了这一概念。实际上,表的不同部分以不同位置的单独表的形式存储。用户选择的数据划分规则称为分区函数,在 MySQL 中可以是模数、简单匹配一组范围或值列表、内部哈希函数或线性哈希函数。该函数根据用户指定的分区类型进行选择,并将用户提供的表达式的值作为其参数。该表达式可以是列值、作用于一个或多个列值的函数,或一个或多个列值的集合,具体取决于所使用的分区类型。

对于 RANGELIST 和 [LINEAR] HASH 分区,分区列的值将传递给分区函数,该函数返回表示应将该特定记录存储在哪个分区中的整数值。该函数必须是非常量且非随机的。它不能包含任何查询,但可以使用在 MySQL 中有效的 SQL 表达式,只要该表达式返回 NULL 或整数 intval,使得

-MAXVALUE <= *intval* <= MAXVALUE

MAXVALUE 用于表示所讨论整数类型的最小上界。-MAXVALUE 表示最大下界。)

对于[LINEAR] KEYRANGE COLUMNSLIST COLUMNS分区,分区表达式由一个或多个列的列表组成。

对于[LINEAR] KEY分区,分区函数由 MySQL 提供。

有关允许的分区列类型和分区函数的更多信息,请参见第 26.2 节“分区类型”,以及提供分区语法描述和其他示例的第 15.1.20 节“CREATE TABLE 语句”。有关分区函数的限制信息,请参见第 26.6.3 节“与函数相关的分区限制”。

这被称为水平分区,即表的不同行可以分配给不同的物理分区。MySQL 8.0 不支持垂直分区,即表的不同列分配给不同的物理分区。目前没有计划将垂直分区引入 MySQL 中。

要创建分区表,必须使用支持它们的存储引擎。在 MySQL 8.0 中,同一分区表的所有分区必须使用相同的存储引擎。但是,没有任何阻止您在同一 MySQL 服务器上或甚至在同一数据库中为不同的分区表使用不同的存储引擎。

在 MySQL 8.0 中,仅支持分区的存储引擎是InnoDBNDB。不能与不支持分区的存储引擎一起使用分区;这些包括MyISAMMERGECSVFEDERATED存储引擎。

使用NDB进行KEYLINEAR KEY分区是可能的,但不支持使用其他类型的用户定义分区的表使用此存储引擎。此外,使用用户定义分区的NDB表必须具有显式主键,并且表的分区表达式中引用的任何列必须是主键的一部分。但是,如果在用于创建或修改用户分区的NDB表的PARTITION BY KEYPARTITION BY LINEAR KEY子句中未列出任何列,则不需要该表具有显式主键。有关更多信息,请参见第 25.2.7.1 节“NDB Cluster 中的 SQL 语法不兼容”。

创建分区表时,默认使用与创建任何其他表时相同的存储引擎;要覆盖此行为,只需像为非分区表一样使用[STORAGE] ENGINE选项即可。目标存储引擎必须提供本机分区支持,否则语句将失败。需要记住,在CREATE TABLE语句中使用任何分区选项之前,需要将[STORAGE] ENGINE(以及其他表选项)列在之前。此示例显示了如何创建一个表,该表按哈希分区为 6 个分区,并使用InnoDB存储引擎(不管default_storage_engine的值是什么):

CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)
    ENGINE=INNODB
    PARTITION BY HASH( MONTH(tr_date) )
    PARTITIONS 6;

每个PARTITION子句可以包括一个[STORAGE] ENGINE选项,但在 MySQL 8.0 中,这没有任何效果。

除非另有说明,在本讨论中的其余示例假定default_storage_engineInnoDB

重要提示

分区适用于表的所有数据和索引;您不能仅对数据进行分区而不对索引进行分区,反之亦然,也不能仅对表的一部分进行分区。

每个分区的数据和索引可以使用DATA DIRECTORYINDEX DIRECTORY选项分配到特定目录,这些选项是用于创建分区表的CREATE TABLE语句的PARTITION子句。

仅支持InnoDB表的各个分区和子分区的DATA DIRECTORY选项。截至 MySQL 8.0.21,DATA DIRECTORY子句中指定的目录必须为InnoDB所知。有关更多信息,请参阅使用 DATA DIRECTORY 子句。

表的分区表达式中使用的所有列必须是表可能具有的每个唯一键的一部分,包括任何主键。这意味着通过以下 SQL 语句创建的这样一个表不能进行分区:

CREATE TABLE tnp (
    id INT NOT NULL AUTO_INCREMENT,
    ref BIGINT NOT NULL,
    name VARCHAR(255),
    PRIMARY KEY pk (id),
    UNIQUE KEY uk (name)
);

因为pkuk键没有共同的列,所以没有列可用于分区表达式。在这种情况下的可能解决方法包括将name列添加到表的主键中,将id列添加到uk中,或者简单地完全删除唯一键。有关更多信息,请参阅第 26.6.1 节,“分区键、主键和唯一键”。

此外,MAX_ROWSMIN_ROWS可用于确定每个分区中可以存储的最大和最小行数。有关这些选项的更多信息,请参阅第 26.3 节“分区管理”。

MAX_ROWS选项还可用于创建具有额外分区的 NDB Cluster 表,从而允许更大的哈希索引存储。有关DataMemory数据节点配置参数的文档,以及更多信息,请参阅第 25.2.2 节“NDB Cluster 节点、节点组、片段副本和分区”。

分区的一些优点列在这里:

  • 分区使得可以在一个表中存储比单个磁盘或文件系统分区能够容纳的更多数据。

  • 数据如果失去了其用处,通常可以通过删除仅包含该数据的分区(或分区)来轻松地从分区表中删除。相反,通过为存储特定数据添加一个或多个新分区,在某些情况下可以极大地促进添加新数据的过程。

  • 由于满足给定WHERE子句的数据只能存储在一个或多个分区中,因此某些查询可以在很大程度上进行优化,自动排除搜索中的任何剩余分区。由于分区表创建后可以更改分区,因此您可以重新组织数据以增强在设置分区方案时可能未经常使用的频繁查询。排除非匹配分区(以及它们包含的任何行)的能力通常被称为分区修剪。有关更多信息,请参阅第 26.4 节“分区修剪”。

    此外,MySQL 支持对查询进行显式分区选择。例如,SELECT * FROM t PARTITION (p0,p1) WHERE c < 5 仅选择与WHERE条件匹配的p0p1分区中的行。在这种情况下,MySQL 不会检查表t的其他分区;当您已经知道要检查哪个分区或哪些分区时,这可以极大地加快查询速度。分区选择也支持数据修改语句DELETEINSERTREPLACEUPDATELOAD DATALOAD XML。有关这些语句的更多信息和示例,请参阅这些语句的描述。

26.2 分区类型

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-types.html

26.2.1 范围分区

26.2.2 LIST 分区

26.2.3 列分区

26.2.4 HASH 分区

26.2.5 KEY 分区

26.2.6 子分区

26.2.7 MySQL 分区如何处理 NULL 值

本节讨论了 MySQL 8.0 中可用的分区类型。这些包括以下列出的类型:

  • RANGE 分区。 这种分区类型根据列值落在给定范围内来将行分配到分区。参见 26.2.1 节,“RANGE 分区”。有关此类型的扩展信息,RANGE COLUMNS,请参见 26.2.3.1 节,“RANGE COLUMNS 分区”。

  • LIST 分区。 类似于RANGE分区,不同之处在于根据与一组离散值匹配的列来选择分区。参见 26.2.2 节,“LIST 分区”。有关此类型的扩展信息,LIST COLUMNS,请参见 26.2.3.2 节,“LIST COLUMNS 分区”。

  • HASH 分区。 使用这种分区类型,根据用户定义的表达式返回的值来选择一个分区,该表达式在要插入表中的行的列值上操作。该函数可以由 MySQL 中任何产生整数值的有效表达式组成。参见 26.2.4 节,“HASH 分区”。

    这种类型的扩展,LINEAR HASH,也是可用的,请参见 26.2.4.1 节,“LINEAR HASH 分区”。

  • KEY 分区。 这种分区类型类似于HASH分区,不同之处在于只提供要评估的一个或多个列,并且 MySQL 服务器提供自己的哈希函数。这些列可以包含除整数值以外的其他值,因为 MySQL 提供的哈希函数保证无论列数据类型如何,都会得到一个整数结果。这种类型的扩展,LINEAR KEY,也是可用的。参见 26.2.5 节,“KEY 分区”。

数据库分区的一个非常常见的用途是按日期对数据进行分隔。一些数据库系统支持显式日期分区,MySQL 在 8.0 中没有实现。但是,在 MySQL 中,基于DATETIMEDATETIME列或利用这些列的表达式创建分区方案并不困难。

当按KEYLINEAR KEY进行分区时,您可以使用DATETIMEDATETIME列作为分区列,而无需对列值进行任何修改。例如,在 MySQL 中,以下表创建语句是完全有效的:

CREATE TABLE members (
    firstname VARCHAR(25) NOT NULL,
    lastname VARCHAR(25) NOT NULL,
    username VARCHAR(16) NOT NULL,
    email VARCHAR(35),
    joined DATE NOT NULL
)
PARTITION BY KEY(joined)
PARTITIONS 6;

在 MySQL 8.0 中,还可以使用RANGE COLUMNSLIST COLUMNS分区使用DATEDATETIME列作为分区列。

其他分区类型需要产生整数值或NULL的分区表达式。如果您希望通过RANGELISTHASHLINEAR HASH进行基于日期的分区,您可以简单地使用一个操作DATETIMEDATETIME列并返回这样一个值的函数,如下所示:

CREATE TABLE members (
    firstname VARCHAR(25) NOT NULL,
    lastname VARCHAR(25) NOT NULL,
    username VARCHAR(16) NOT NULL,
    email VARCHAR(35),
    joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
    PARTITION p0 VALUES LESS THAN (1960),
    PARTITION p1 VALUES LESS THAN (1970),
    PARTITION p2 VALUES LESS THAN (1980),
    PARTITION p3 VALUES LESS THAN (1990),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);

本章的以下部分中还可以找到使用日期进行分区的其他示例:

  • 26.2.1 节,“RANGE 分区”

  • 26.2.4 节,“HASH 分区”

  • 26.2.4.1 节,“线性哈希分区”

有关基于日期的分区的更复杂示例,请参阅以下部分:

  • 26.4 节,“分区修剪”

  • 26.2.6 节,“子分区”

MySQL 分区针对与TO_DAYS()YEAR()TO_SECONDS()函数一起使用进行了优化。然而,你可以使用其他返回整数或NULL的日期和时间函数,如WEEKDAY()DAYOFYEAR()MONTH()。有关这些函数的更多信息,请参见第 14.7 节,“日期和时间函数”。

无论你使用何种分区类型,都要记住,分区在创建时总是自动按顺序编号,从0开始。当向分区表插入新行时,使用这些分区编号来识别正确的分区。例如,如果你的表使用了 4 个分区,这些分区被编号为0123。对于RANGELIST分区类型,必须确保为每个分区编号定义了一个分区。对于HASH分区,用户提供的表达式必须求值为整数值。对于KEY分区,这个问题由 MySQL 服务器内部使用的哈希函数自动处理。

分区的名称通常遵循其他 MySQL 标识符(如表和数据库)的规则。然而,你应该注意,分区名称不区分大小写。例如,如下所示的CREATE TABLE语句会失败:

mysql> CREATE TABLE t2 (val INT)
 -> PARTITION BY LIST(val)(
 ->     PARTITION mypart VALUES IN (1,3,5),
 ->     PARTITION MyPart VALUES IN (2,4,6)
 -> );
ERROR 1488 (HY000): Duplicate partition name mypart

失败发生是因为 MySQL 在 mypartMyPart 分区名称之间看不到任何区别。

当你为表指定分区数时,这必须表示为一个正的、非零的整数字面值,不能以0.8E+016-2这样的表达式表示,即使它求值为整数值也不行。不允许使用小数。

在接下来的章节中,我们并不一定提供用于创建每种分区类型的语法的所有可能形式;有关此信息,请参见第 15.1.20 节,“CREATE TABLE 语句”。

posted @ 2024-06-23 16:28  绝不原创的飞龙  阅读(1)  评论(0编辑  收藏  举报