MySQL8-中文参考-四十四-
MySQL8 中文参考(四十四)
原文:
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")值。这个数字包括实际启动节点所需的时间;换句话说,这个计数器从第一次调用ndbd或ndbmtd")开始运行;因此,即使对于尚未完成启动的节点,uptime
可能显示一个非零值。
status
列显示节点的当前状态。这是其中之一:NOTHING
、CMVMI
、STARTING
、STARTED
、SINGLEUSER
、STOPPING_1
、STOPPING_2
、STOPPING_3
或STOPPING_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 table
、User table
、Unique hash index
或Ordered index
。BLOB
表显示为User table
。
table_id
列值在任何给定时间都是唯一的,但如果相应对象已被删除,则可以重新使用。可以使用ndb_show_tables实用程序查看相同的 ID。
block_instance
列显示此片段副本属于哪个 LDM 实例。您可以使用此信息从threadblocks
表中获取有关特定线程的信息。第一个这样的实例始终编号为 0。
由于通常有两个片段副本,并假设是这样,每个fragment_num
值应在表中出现两次,分别在来自同一节点组的两个不同数据节点上。
由于NDB
不使用单键访问有序索引,因此有序索引操作不会增加tot_key_reads
、tot_key_inserts
、tot_key_updates
、tot_key_writes
和tot_key_deletes
的计数。
注意
在使用tot_key_writes
时,应注意在此上下文中,写操作会更新行(如果键存在),否则会插入新行。(这在NDB
实现的REPLACE
SQL 语句中有用途。)
tot_key_refs
列显示 LDM 拒绝的键操作数量。通常,这种拒绝是由于重复键(插入)、未找到键错误(更新、删除和读取)或操作被用作谓词的解释程序拒绝的。
tot_key_attrinfo_bytes
和 tot_key_keyinfo_bytes
列计算的 attrinfo
和 keyinfo
属性是 LQHKEYREQ
信号的属性(参见 NDB 通信协议),用于由 LDM 发起的键操作。attrinfo
通常包含元组字段值(插入和更新)或投影规范(用于读取);keyinfo
包含在此模式对象中定位给定元组所需的主键或唯一键。
tot_frag_scans
显示的值包括全扫描(检查每一行)和子集扫描。唯一索引和 BLOB
表永远不会被扫描,因此对于这些片段副本,该值以及其他与扫描相关的计数都为 0。
tot_scan_rows_examined
可能显示的行数少于给定片段副本中的总行数,因为有序索引扫描可能受到边界限制。此外,客户端可能选择在检查所有潜在匹配行之前结束扫描;例如,在使用包含 LIMIT
或 EXISTS
子句的 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 节点进程的信息;每个节点在表中由一行表示。仅显示连接到集群的节点在此表中。您可以从nodes
和config_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")使用的发送缓冲区;TotalSendBufferMemory
和ExtraSendBufferMemory
之和。这个资源可以过度分配多达 25%。TotalSendBufferMemory
通过对每个节点的发送缓冲区内存求和来计算,其默认值为 2 MB。因此,在一个有四个数据节点和八个 API 节点的系统中,数据节点有 12 * 2 MB 的发送缓冲区内存。ExtraSendBufferMemory
由ndbmtd")使用,每个线程额外使用 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
表中查找给定实例。
tableid
由NDB
分配给表;在其他ndbinfo
表中以及ndb_show_tables的输出中使用相同的 ID 来表示此表。
transid
列中显示的事务 ID 是由 NDB API 为请求或持有当前锁的事务生成的标识符。
mode
列显示锁模式,始终为S
(共享锁)或X
(排他锁)。如果事务对给定行有排他锁,则该行上的所有其他锁都具有相同的事务 ID。
state
列显示锁状态。其值始终为H
(持有)或W
(等待)。等待锁请求等待由不同事务持有的锁。
detail
列指示此锁是否是受影响行的锁队列中持有锁的第一个锁,如果是,则包含一个*
(星号字符);否则,此列为空。此信息可用于帮助识别锁请求列表中的唯一条目。
op
列显示请求锁的操作类型。这始终是READ
、INSERT
、UPDATE
、DELETE
、SCAN
或REFRESH
中的一个值。
duration_millis
列显示此锁请求等待或持有锁的毫秒数。当为等待请求授予锁时,此值将重置为 0。
锁 ID(lockid
列)对于此节点和块实例是唯一的。
如果lock_state
列的值为W
,则此锁正在等待授予,并且waiting_for
列显示此请求正在等待的锁对象的锁 ID。否则,waiting_for
为空。waiting_for
只能引用相同行上的锁(由node_id
、block_instance
、tableid
、fragmentid
和rowid
标识)。
原文:
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_id
与SHOW PROCESSLIST
输出中显示的连接或会话 ID 相同。它从INFORMATION_SCHEMA
表NDB_TRANSID_MYSQL_CONNECTION_MAP
中获取。
block_instance
指的是内核块的一个实例。与块名称一起,此数字可用于在threadblocks
表中查找给定实例。
事务 ID(transid
)是一个唯一的 64 位数字,可以使用 NDB API 的getTransactionId()
方法获取。(目前,MySQL 服务器不公开正在进行的事务的 NDB API 事务 ID。)
operation_type
列可以取以下任一值:READ
、READ-SH
、READ-EX
、INSERT
、UPDATE
、DELETE
、WRITE
、UNLOCK
、REFRESH
、SCAN
、SCAN-SH
、SCAN-EX
或<unknown>
。
state
列可以具有以下任一值:ABORT_QUEUED
、ABORT_STOPPED
、COMMITTED
、COMMIT_QUEUED
、COMMIT_STOPPED
、COPY_CLOSE_STOPPED
、COPY_FIRST_STOPPED
、COPY_STOPPED
、COPY_TUPKEY
、IDLE
、LOG_ABORT_QUEUED
、LOG_COMMIT_QUEUED
、LOG_COMMIT_QUEUED_WAIT_SIGNAL
、LOG_COMMIT_WRITTEN
、LOG_COMMIT_WRITTEN_WAIT_SIGNAL
、LOG_QUEUED
、PREPARED
、PREPARED_RECEIVED_COMMIT
、SCAN_CHECK_STOPPED
、SCAN_CLOSE_STOPPED
、SCAN_FIRST_STOPPED
、SCAN_RELEASE_STOPPED
、SCAN_STATE_USED
、SCAN_STOPPED
、SCAN_TUPKEY
、STOPPED
、TC_NOT_CONNECTED
、WAIT_ACC
、WAIT_ACC_ABORT
、WAIT_AI_AFTER_ABORT
、WAIT_ATTR
、WAIT_SCAN_AI
、WAIT_TUP
、WAIT_TUPKEYINFO
、WAIT_TUP_COMMIT
或WAIT_TUP_TO_ABORT
。(如果 MySQL 服务器使用ndbinfo_show_hidden
启用运行,则可以通过从通常隐藏的ndb$dblqh_tcconnect_state
表中选择来查看此状态列表。)
通过检查ndb_show_tables的输出,您可以通过表 ID 获取NDB
表的名称。
fragid
与ndb_desc的输出中看到的分区号相同,--extra-partition-info
(简写为-p
)。
在client_node_id
和client_block_ref
中,client
指的是 NDB Cluster API 或 SQL 节点(即,NDB API 客户端或连接到集群的 MySQL 服务器)。
block_instance
和tc_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_id
与SHOW PROCESSLIST
输出中显示的连接或会话 ID 相同。它从INFORMATION_SCHEMA
表NDB_TRANSID_MYSQL_CONNECTION_MAP
中获取。
block_instance
指的是内核块的一个实例。与块名称一起,此数字可用于在threadblocks
表中查找给定实例。
事务 ID(transid
)是一个唯一的 64 位数字,可以使用 NDB API 的getTransactionId()
方法获取。(目前,MySQL 服务器不公开正在进行的事务的 NDB API 事务 ID。)
state
列可以具有以下任一值:CS_ABORTING
、CS_COMMITTING
、CS_COMMIT_SENT
、CS_COMPLETE_SENT
、CS_COMPLETING
、CS_CONNECTED
、CS_DISCONNECTED
、CS_FAIL_ABORTED
、CS_FAIL_ABORTING
、CS_FAIL_COMMITTED
、CS_FAIL_COMMITTING
、CS_FAIL_COMPLETED
、CS_FAIL_PREPARED
、CS_PREPARE_TO_COMMIT
、CS_RECEIVING
、CS_REC_COMMITTING
、CS_RESTART
、CS_SEND_FIRE_TRIG_REQ
、CS_STARTED
、CS_START_COMMITTING
、CS_START_SCAN
、CS_WAIT_ABORT_CONF
、CS_WAIT_COMMIT_CONF
、CS_WAIT_COMPLETE_CONF
、CS_WAIT_FIRE_TRIG_REQ
。(如果 MySQL 服务器使用ndbinfo_show_hidden
启用运行,则可以通过从通常隐藏的ndb$dbtc_apiconnect_state
表中选择来查看此状态列表。)
在client_node_id
和client_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
将表分布数据复制到磁盘的状态;其中之一为
IDLE
、SR_PHASE1_READ_PAGES
、SR_PHASE2_READ_TABLE
、SR_PHASE3_COPY_TABLE
、REMOVE_NODE
、LCP_READ_TABLE
、COPY_TAB_REQ
、COPY_NODE_STATE
、ADD_TABLE_COORDINATOR
(在 NDB 8.0.23 之前:ADD_TABLE_MASTER
)、ADD_TABLE_PARTICIPANT
(在 NDB 8.0.23 之前:ADD_TABLE_SLAVE
)、INVALIDATE_NODE_LCP
、ALTER_TABLE
、COPY_TO_SAVE
或GET_TABINFO
-
tab_update_status
表分布数据更新状态;其中之一为
IDLE
、LOCAL_CHECKPOINT
、LOCAL_CHECKPOINT_QUEUED
、REMOVE_NODE
、COPY_TAB_REQ
、ADD_TABLE_MASTER
、ADD_TABLE_SLAVE
、INVALIDATE_NODE_LCP
或CALLBACK
-
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
表存储类型;其中之一为
MEMORY
或DISK
-
hashmap_id
哈希映射 ID
-
partition_balance
表的分区平衡(片段计数类型);其中之一为
FOR_RP_BY_NODE
、FOR_RA_BY_NODE
、FOR_RP_BY_LDM
或FOR_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
参数main
或rep
设置为 0 的同时保持另一个参数为 1 的可能性,此时线程名称为main_rep
,描述为主和副本线程,模式,分发,代理块和异步复制处理
。从 NDB 8.0.23 开始,还可以将main
和rep
都设置为 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_utime
、os_ru_stime
、os_ru_minflt
、os_ru_majflt
、os_ru_nvcsw
和 os_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列中,该列可以具有以下任一值:CONNECTING
、CONNECTED
、DISCONNECTING
或DISCONNECTED
。
配置但当前未连接到集群的 API 和管理节点的连接显示为DISCONNECTED
状态。在此表中不显示node_id
为当前未连接的数据节点的行(这类似于ndbinfo.nodes
表中省略了断开连接的节点。
remote_address
是显示在remote_node_id
列中的节点的主机名或地址。从此节点发送的bytes_sent
和此节点接收的bytes_received
分别是使用此连接发送和接收的字节数。对于状态为CONNECTING
或DISCONNECTED
的节点,这些列始终显示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_count
、overloaded
、overload_count
、slowdown
和 slowdown_count
计数器在连接时重置,并在远程节点断开连接后保留其值。bytes_sent
和 bytes_received
计数器也在连接时重置,因此在断开连接后保留其值(直到下次连接重置它们)。
overloaded
和 overload_count
列所指的 过载 状态发生在此传输器的发送缓冲区包含超过 OVerloadLimit
字节(默认为 SendBufferMemory
的 80%,即 0.8 * 2097152 = 1677721 字节)时。当给定传输器处于过载状态时,任何尝试使用此传输器的新事务都会因错误 1218(NDB 内核中的发送缓冲区过载)而失败。这影响扫描和主键操作。
此表中 slowdown
和 slowdown_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
表中按名称显示。
线程名称在threads
和setup_threads
表的name
列中以*
prefix*/*
plugin_name*/*
thread_name*
的格式显示。 prefix
是由performance_schema
引擎确定的对象类型,对于插件线程是thread
(参见 Thread Instrument Elements)。 plugin_name
是ndbcluster
。 thread_name
是线程的独立名称(ndb_binlog
、ndb_index_stat
或ndb_metadata
)。
使用threads
或setup_threads
表中给定线程的线程 ID 或 OS 线程 ID,可以从性能模式中获取有关插件执行和资源使用的大量信息。此示例显示了如何通过连接threads
和memory_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 STATUS
,SHOW ENGINE NDBCLUSTER STATUS
此语句的输出包含有关服务器与集群的连接、NDB 集群对象的创建和使用,以及 NDB 集群复制的二进制日志记录的信息。
有关用法示例和更详细信息,请参见第 15.7.7.15 节,“SHOW ENGINE 语句”。
-
SHOW ENGINES
此语句可用于确定 MySQL 服务器中是否启用了集群支持,以及如果是,则是否处于活动状态。
有关更详细信息,请参见第 15.7.7.16 节,“SHOW ENGINES 语句”。
注意
此语句不支持
LIKE
子句。但是,您可以使用LIKE
来过滤针对 Information SchemaENGINES
表的查询,如下一项所述。 -
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 STOP
和SHUTDOWN
等命令。
出于这些原因,有必要在网络层面保护集群。对于集群来说,最安全的网络配置是将集群节点之间的连接与任何其他网络通信隔离开。可以通过以下任一方法实现这一点:
-
将集群节点保持在与任何公共网络物理隔离的网络上。这个选项是最可靠的;然而,实施成本最高。
我们在这里展示了使用这种物理隔离网络的 NDB 集群设置的示例:
图 25.7 带有硬件防火墙的 NDB 集群
这种设置有两个网络,一个私有的(实线框)用于集群管理服务器和数据节点,一个公共的(虚线框)用于 SQL 节点所在。 (我们展示了管理和数据节点使用千兆交换机连接,因为这提供了最佳性能。)两个网络都受到硬件防火墙的保护,有时也称为基于网络的防火墙。
这种网络设置是最安全的,因为没有数据包可以从网络外部到达集群的管理或数据节点,也没有集群的内部通信可以到达外部,除非通过 SQL 节点,只要 SQL 节点不允许任何数据包被转发。当然,这意味着所有 SQL 节点必须受到黑客攻击的保护。
重要
关于潜在的安全漏洞,SQL 节点与任何其他 MySQL 服务器没有区别。请参阅第 8.1.3 节,“使 MySQL 免受攻击者攻击”,了解您可以用来保护 MySQL 服务器的技术描述。
-
使用一个或多个软件防火墙(也称为主机防火墙)来控制从不需要访问集群的网络部分通过的数据包。在这种设置中,必须在集群中的每台主机上安装软件防火墙,否则可能会从本地网络外部访问。
基于主机的选项是实施成本最低的,但纯粹依赖软件提供保护,因此最难保持安全。
这种 NDB 集群的网络设置类型在这里说明:
图 25.8 带有软件防火墙的 NDB 集群
使用这种类型的网络设置意味着 NDB 集群主机有两个区域。每个集群主机必须能够与集群中的所有其他机器通信,但只有托管 SQL 节点(虚线框)的机器可以与外部进行任何联系,而那些位于包含数据节点和管理节点的区域(实线框)的机器必须与不属于集群的任何机器隔离。使用集群的应用程序和这些应用程序的用户不被允许直接访问管理和数据节点主机。
要实现这一点,您必须设置软件防火墙,根据每个集群主机计算机上运行的节点类型,限制流量到以下表中显示的类型或类型:
表 25.68 主机防火墙集群配置中的节点类型
节点类型 允许的流量 SQL 或 API 节点 -
它源自管理或数据节点的 IP 地址(使用任何 TCP 或 UDP 端口)。
-
它源自集群所在网络中,并位于应用程序正在使用的端口上。
|
数据节点或管理节点 -
它源自管理或数据节点的 IP 地址(使用任何 TCP 或 UDP 端口)。
-
它源自 SQL 或 API 节点的 IP 地址。
|
除了给定节点类型表中显示的流量之外的任何流量都应被拒绝。
配置防火墙的具体细节因防火墙应用程序而异,超出了本手册的范围。iptables是一个非常常见和可靠的防火墙应用程序,通常与APF一起使用作为前端,以使配置更加简单。如果您选择实施此类 NDB 集群网络设置或在下一项中讨论的“混合”类型,您可以(也应该)查阅所使用软件防火墙的文档。
-
-
还可以采用前两种方法的组合,即同时使用网络和主机防火墙来保护集群。这在安全级别和成本方面介于前两种方案之间。这种类型的网络设置将集群置于硬件防火墙后面,但允许传入数据包穿过连接所有集群主机的路由器以到达 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 存储引擎的数据库对象的GRANT
和REVOKE
语句相同。对于CREATE USER
和DROP 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 集群相关的最重要的要点如下:
-
在一个 SQL 节点上建立的用户和权限不会自动存在或生效于集群中的其他 SQL 节点。反之,在集群中的一个 SQL 节点上删除用户或权限并不会从任何其他 SQL 节点中删除用户或权限。
-
你可以使用
NDB_STORED_USER
在 SQL 节点之间共享 MySQL 用户和权限。 -
一旦在 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
选项,因为您可能会忘记使用命令行选项,从而无意中以其他用户身份运行 mysqld。mysqld_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_tables、ndb_desc和ndb_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_status
或 global_status
表进行查询的结果中。通过比较执行影响复制的 NDB
表的语句之前和之后这些状态变量的值,您可以观察副本在 NDB API 级别上采取的相应操作,这在监视或故障排除 NDB 集群复制时可能很有用。第 25.6.15 节,“NDB API 统计计数器和变量”提供了额外信息。
从 NDB 复制到非 NDB 表。 可以将 NDB
表从作为复制源的 NDB 集群复制到使用其他 MySQL 存储引擎(如 InnoDB
或 MyISAM
)的表上,这些表位于一个副本 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 节点响应过慢,可能会被集群丢弃(通过调整MaxBufferedEpochs
和TimeBetweenEpochs
配置参数,在一定程度上可以控制这种情况)。如果发生这种情况,完全有可能在源集群中插入新数据而不记录在源 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 集群循环复制,所有源均为副本
在这种情况下,集群 1 中的 SQL 节点 A 复制到集群 2 中的 SQL 节点 C;集群 2 中的 SQL 节点 C 复制到集群 3 中的 SQL 节点 E;SQL 节点 E 复制到 SQL 节点 A。换句话说,复制线路(图表中的曲线箭头表示)直接连接所有用作源和副本的 SQL 节点。
还应该可以设置循环复制,其中并非所有源 SQL 节点也是副本,如下所示:
图 25.12 NDB 集群循环复制,其中并非所有源均为副本
在这种情况下,每个集群中的不同 SQL 节点被用作源和副本。但是,您不应该启用任何 SQL 节点的log_replica_updates
或log_slave_updates
系统变量。对于 NDB Cluster 的这种循环复制方案,其中复制线(在图中由曲线箭头表示)是不连续的,应该是可能的,但必须注意的是,这种方案尚未经过彻底测试,因此仍然被视为实验性。
注意
NDB
存储引擎使用幂等执行模式,可以抑制重复键和其他错误,否则会破坏 NDB Cluster 的循环复制。这相当于将系统变量replica_exec_mode
或slave_exec_mode
的全局值设置为IDEMPOTENT
,尽管在 NDB Cluster 复制中这并不是必需的,因为 NDB Cluster 会自动设置此变量并忽略任何显式设置的尝试。
NDB Cluster 复制和主键。 在节点故障的情况下,复制没有主键的NDB
表可能仍会出现错误,因为在这种情况下可能会插入重复行。因此,强烈建议所有被复制的NDB
表都有明确的主键。
NDB Cluster 复制和唯一键。 在较旧版本的 NDB Cluster 中,更新NDB
表的唯一键列的操作在复制时可能会导致重复键错误。通过将唯一键检查推迟到所有表行更新执行之后,解决了在NDB
表之间的复制中的此问题。
目前,只有NDB
支持以这种方式推迟约束。因此,当从NDB
复制到其他存储引擎(如InnoDB
或MyISAM
)时,仍不支持唯一键的更新。
在没有延迟检查唯一键更新的情况下复制时遇到的问题可以使用 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 及更高版本的副本上启用多线程,需要执行以下步骤:
-
在启动源 mysqld 时,将
--ndb-log-transaction-dependency
设置为ON
。 -
同样在源 mysqld 上,将
binlog_transaction_dependency_tracking
设置为WRITESET
。这可以在 mysqld 进程运行时完成。 -
要确保副本使用多个工作线程,将
replica_parallel_workers
的值设置大于 1。默认值为 4,可以在运行时更改副本上的值。
在 NDB 8.0.26 之前,设置任何与多线程副本相关的系统变量,如 replica_parallel_workers
或 slave_parallel_workers
,以及 replica_checkpoint_group
或 slave_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 MASTER
和RESET REPLICA
(在 NDB 8.0.22 之前,使用RESET SLAVE
)语句清除无效的ndb_binlog_index
和ndb_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 集群之间的复制正常运行是必需的。特别是,您必须牢记以下内容:
-
使用
--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
。 -
使用
--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
被复制。
您还应该记住,每个复制规则都需要以下内容:
-
自己的
--replicate-do-*
或--replicate-ignore-*
选项,并且不能在单个复制过滤选项中表示多个规则。有关这些规则的信息,请参见第 19.1.6 节,“复制和二进制日志选项和变量”。 -
自己的
--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 连接的复制
在 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
,与InnoDB
或MyISAM
不同,不会将对虚拟列的更改写入二进制日志;然而,这对于 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_index
和 ndb_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_index
和ndb_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_id
和 orig_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_id
或 orig_epoch
。
有关使用 next_position
和 next_file
列的更多信息,请参见第 25.7.8 节,“使用 NDB 集群复制实现故障切换”。
以下图显示了 NDB 集群复制源服务器、其二进制日志注入器线程和 mysql.ndb_binlog_index
表之间的关系。
图 25.14 复制源集群
ndb_replication 表
ndb_replication
表用于控制二进制日志记录和冲突解决,并且是基于每个表的基础上操作的。该表中的每一行对应于被复制的表,确定如何记录对表的更改,并且如果指定了冲突解决函数,则确定如何解决该表的冲突。
与 ndb_apply_status
和 ndb_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
列要应用的冲突解决函数之一是 NDBMAX()"),NDBEPOCH()"),NDBEPOCH2()"),NDBMAX_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
表中插入行,可以为不同的表设置不同的二进制日志记录格式,使用适当的 db
、table_name
和 binlog_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
支持在此表的 db
、table_name
和 server_id
列上进行通配符匹配。在 db
和 table_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
设置为0
或OFF
来实现。
将更改的数据记录为更新。 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 集群进行复制包括以下步骤:
-
检查所有 MySQL 服务器的版本兼容性(参见第 25.7.2 节,“NDB 集群复制的一般要求”)。
-
在源集群上创建一个具有适当权限的复制帐户,使用以下两个 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 USER
和GRANT
语句:mysql*S*> CREATE USER 'myreplica'@'replica-host' -> IDENTIFIED BY '53cr37'; mysql*S*> GRANT REPLICATION SLAVE ON *.* -> TO 'myreplica'@'replica-host';
出于安全原因,最好使用一个唯一的用户帐户—不用于任何其他目的—用于复制帐户。
-
设置复制使用源。使用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 节,“复制和二进制日志选项和变量”。 -
如果源集群已在使用中,则可以创建源的备份并将其加载到副本中,以减少副本与源同步所需的时间。如果副本也在运行 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-start
或skip_replica_start
。一旦数据加载完成,请按照下面两节中概述的额外步骤进行操作。 -
确保每个充当复制源的 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 集群复制的过程。
-
通过发出以下命令启动 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 集群复制的一般要求”)。 -
按照以下方式启动 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-start
和skip_replica_start
。 -
必须将复制服务器与源服务器的复制二进制日志同步。如果源上之前没有运行二进制日志记录,请在复制服务器上运行以下语句:
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_FILE
和SOURCE_LOG_POS
|MASTER_LOG_POS
的正确值。 -
最后,通过在副本上的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
语句来启动复制进程。下面显示了需要发出的命令和顺序:
-
启动主复制源:
shell*S*> mysqld --ndbcluster --server-id=1 \ --log-bin &
-
启动次要复制源:
shell*S'*> mysqld --ndbcluster --server-id=2 \ --log-bin &
-
启动主复制品服务器:
shell*R*> mysqld --ndbcluster --server-id=3 \ --skip-slave-start &
-
启动次要复制品服务器:
shell*R'*> mysqld --ndbcluster --server-id=4 \ --skip-slave-start &
-
最后,通过在主复制品上执行
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
如果主要集群复制过程失败,可以切换到次要复制通道。以下过程描述了完成此操作所需的步骤。
-
获取最近全局检查点(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*
可能更简单或更有效(或两者兼有)。 -
使用第 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 错误而停止。 -
现在可以通过在次要副本服务器上运行以下查询来同步次要通道:
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 通常会尝试将字符串转换为数字,但这种情况是一个例外。 -
您现在可以通过在次要副本上发出适当的命令来启动次要通道的复制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 集群准备复制”,以及紧随其后的各节)。完成这些步骤后,进行备份然后从备份中恢复的过程如下:
-
有两种不同的方法可以启动备份。
-
**方法 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_host
和port
是管理服务器的主机名和端口号:shell*S*> ndb_mgm *management_host*:*port* -e "START BACKUP"
在我们之前概述的场景中(请参阅第 25.7.5 节,“为 NDB 集群准备复制”),将执行如下操作:
shell*S*> ndb_mgm rep-source:1186 -e "START BACKUP"
-
-
将集群备份文件复制到正在上线的复制品。运行源集群的每个为ndbd进程的系统上都有集群备份文件,所有这些文件都必须复制到复制品以确保成功恢复。备份文件可以复制到复制品的管理主机所在计算机上的任何目录中,只要 MySQL 和 NDB 二进制文件在该目录中具有读取权限。在这种情况下,我们假设这些文件已复制到目录
/var/BACKUPS/BACKUP-1
。虽然复制集群不必具有与源相同数量的数据节点,但强烈建议此数量相同。当复制服务器启动时,必须防止复制过程启动。您可以通过在命令行上使用
--skip-slave-start
选项启动复制品,通过在复制品的my.cnf
文件中包含skip-slave-start
,或在 NDB 8.0.24 或更高版本中,通过设置skip_slave_start
系统变量来实现这一点。 -
在复制品集群上创建任何在源集群上存在且需要复制的数据库。
重要
必须在复制品集群中的每个 SQL 节点上执行与要复制的每个数据库对应的
CREATE DATABASE
(或CREATE SCHEMA
)语句。 -
在mysql客户端中使用以下语句重置复制集群:
mysql*R*> RESET SLAVE;
在 NDB 8.0.22 或更高版本中,您还可以使用此语句:
mysql*R*> RESET REPLICA;
-
现在,您可以使用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”.) -
现在,您需要从副本的
ndb_apply_status
表中获取最新的时代(如 Section 25.7.8, “Implementing Failover with NDB Cluster Replication” 中所讨论的):mysql*R*> SELECT @latest:=MAX(epoch) FROM mysql.ndb_apply_status;
-
使用前一步骤中获得的
@latest
作为时代值,您可以从源的mysql.ndb_binlog_index
表中获取正确的起始位置@pos
和正确的二进制日志文件@file
。此处显示的查询从逻辑恢复位置之前应用的最后时代的Position
和File
列中获取这些值: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
列中显示的值。在这种情况下,您必须确定这是哪个文件,并在下一步手动提供名称或通过脚本解析输出。 -
使用前一步骤中获得的值,现在可以在副本的 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;
-
现在,副本知道从源的哪个二进制日志文件的哪个点开始读取数据,您可以使用此语句使副本开始复制:
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 集群的时点恢复,需要按照以下步骤进行:
-
使用
START BACKUP
命令在ndb_mgm客户端中备份集群中的所有NDB
数据库(参见第 25.6.8 节,“NDB 集群的在线备份”)。 -
在恢复集群之前的某个时间点,备份
mysql.ndb_binlog_index
表。最简单的方法可能是使用mysqldump来完成此任务。同时备份二进制日志文件。根据您的需求,这个备份应该定期更新,甚至可能每小时更新一次。
-
(发生灾难性故障或错误。)
-
定位最近的备份。
-
清除数据节点文件系统(使用ndbd
--initial
或 ndbmtd")--initial
)。注意
从 NDB 8.0.21 开始,磁盘数据表空间和日志文件将通过
--initial
删除。以前,��要手动删除这些文件。 -
使用
DROP TABLE
或TRUNCATE TABLE
命令处理mysql.ndb_binlog_index
表。 -
执行ndb_restore,恢复所有数据。在运行ndb_restore时,必须包括
--restore-epoch
选项,以便正确填充ndb_apply_status
表。(有关更多信息,请参见 Section 25.5.23, “ndb_restore — 恢复 NDB 集群备份”.) -
从mysqldump的输出中恢复
ndb_binlog_index
表,并根据需要从备份中恢复二进制日志文件。 -
找到最近应用的时代,即
ndb_apply_status
表中的最大epoch
列值,作为用户变量@LATEST_EPOCH
(强调):SELECT *@LATEST_EPOCH*:=MAX(epoch) FROM mysql.ndb_apply_status;
-
找到与
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;
-
使用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 集群循环复制,所有源均为副本
在这种情况下,集群 1 中的 SQL 节点 A 复制到集群 2 中的 SQL 节点 C;SQL 节点 C 复制到集群 3 中的 SQL 节点 E;SQL 节点 E 复制到 SQL 节点 A。换句话说,复制线(图表中的曲线箭头表示)直接连接所有用作复制源和副本的 SQL 节点。
也可以设置循环复制,使得并非所有源 SQL 节点也是副本,如下所示:
图 25.16 NDB 集群循环复制,不是所有源都是副本
在这种情况下,每个集群中的不同 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 集群多主复制
换句话说,数据通过两种不同的路径从集群 1 复制到集群 3:直接和通过集群 2。
并非所有参与多源复制的 MySQL 服务器都必须同时充当源和副本,给定的 NDB 集群可能会为不同的复制通道使用不同的 SQL 节点。这种情况如下所示:
图 25.18 具有 MySQL 服务器的 NDB 集群多源复制
作为副本的 MySQL 服务器必须启用系统变量 log_replica_updates
(从 NDB 8.0.26 开始)或 log_slave_updates
(NDB 8.0.26 及更早版本)。在前面的图表中还显示了哪些 mysqld 进程需要此选项。
注意
使用 log_replica_updates
或 log_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 集群中使用此功能,必须满足以下三个条件:
-
二进制日志事务依赖关系在源端确定。
为了实现这一点,必须在源端将
binlog_transaction_dependency_tracking
服务器系统变量设置为WRITESET
。这在 NDB 8.0.33 及更高版本中受支持。(默认值为COMMIT_ORDER
。)NDB
中的写集维护工作由 MySQL 二进制日志注入线程执行,作为准备和提交每个时代事务到二进制日志的一部分。这需要额外的资源,并可能降低峰值吞吐量。 -
事务依赖关系被编码到二进制日志中。
NDB 8.0.33 及更高版本支持
--ndb-log-transaction-dependency
启动选项用于mysqld; 将此选项设置为ON
以启用将NDB
事务依赖关系写入二进制日志。 -
副本配置为使用多个工作线程。
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_number
和last_committed
(因此事务依赖关系),可以使用mysqlbinlog查看。
在源上生成的ANONYMOUS_GTID
事件与批量BEGIN
、TABLE_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
表的db
、table_name
和server_id
列中使用模式匹配的信息,请参见使用通配符进行匹配。
您还应该记住,确保解析列正确填充相关值是应用程序的责任,以便解析函数在确定是否应用更新时可以做出适当选择。
要求
冲突解决的准备工作必须在源和副本上都进行。这些任务在以下列表中描述:
-
在写入二进制日志的源上,您必须确定要发送哪些列(所有列还是仅已更新的列)。这是通过在整个 MySQL Server 上应用mysqld启动选项
--ndb-log-updated-only
(稍后在本节中描述)来完成的,或者通过在mysql.ndb_replication
表中放置适当的条目来在一个或多个特定表上完成(参见 ndb_replication Table)。注意
如果您正在复制具有非常大列(如
TEXT
或BLOB
列)的表,--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")。 “时间戳”列还应为UNSIGNED
和NOT NULL
。
本节后面讨论的NDB$EPOCH()
和NDB$EPOCH_TRANS()
函数通过比较应用在主要和次要 NDB 集群上的复制时期的相对顺序来工作,并不使用时间戳。
源列控制
我们可以根据“之前”和“之后”图像来看待更新操作——也就是说,在应用更新之前和之后的表状态。通常,当使用主键更新表时,“之前”图像并不是很重要;然而,当我们需要根据每次更新确定是否在副本上使用更新的值时,我们需要确保两个图像都写入源二进制日志。这是通过--ndb-log-update-as-write
选项为mysqld完成的,稍后在本节中描述。
重要
决定是记录完整行还是仅更新列是在启动 MySQL 服务器时完成的,无法在线更改;您必须重新启动mysqld,或者使用不同的日志记录选项启动新的mysqld实例。
冲突解决控制
冲突解决通常在可能发生冲突的服务器上启用。与日志记录方法选择一样,它是通过mysql.ndb_replication
表中的条目启用的。
NBT_UPDATED_ONLY_MINIMAL
和NBT_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()”处理如下:
-
如果没有冲突写操作,则应用此操作(与
NDB$MAX()
相同)。 -
否则,应用“最大时间戳获胜”冲突解决,如下所示:
-
如果传入写操作的时间戳大于冲突写操作的时间戳,则应用传入操作。
-
如果传入写操作的时间戳不更大,则拒绝传入写操作。
-
处理插入操作时,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()
处理如下:
-
如果没有冲突的写入,应用这个(与
NDB$MAX_DELETE_WIN()
相同)。 -
否则,应用“最大时间戳获胜”冲突解决,如下所示:
-
如果传入写入的时间戳大于冲突写入的时间戳,则应用传入操作。
-
如果传入写入的时间戳不更大,则拒绝传入的写入操作。
-
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)应该足够,除非为TimeBetweenGlobalCheckpoints
、TimeBetweenEpochs
或两者都使用了其他值。值太小可能导致误报,而值太大可能导致数据库中浪费的空间过多。
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 集群。 -
具有
BLOB
或TEXT
列的表目前不支持使用NDB$EPOCH()
或NDB$EPOCH_TRANS()
。
NDB$EPOCH_TRANS()
NDB$EPOCH_TRANS()
扩展了NDB$EPOCH()
函数。冲突使用“主要获胜”规则(参见 NDBEPOCH()在次要上重新对齐单个冲突行,而
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_count
和Ndb_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_id
、source_server_id
、source_epoch
和count
列的名称,并且对于与原始表主键匹配的列,使用与原始表中相同的名称。
如果异常表使用本节后面讨论的一个或多个可选列NDB$OP_TYPE
、NDB$CFT_CAUSE
或NDB$ORIG_TRANSID
,则每个必需列也必须使用前缀NDB$
命名。如果需要,即使您不定义任何可选列,也可以使用NDB$
前缀命名必需列,但在这种情况下,所有四个必需列必须使用前缀命名。
在这些列之后,应按照用于定义原始表主键的顺序复制构成原始表主键的列。复制原始表主键列的列的数据类型应与原始列相同(或更大)。可以使用主键列的子集。
异常表必须使用NDB
存储引擎。(本节后面将展示使用带有异常表的NDB$OLD()
的示例。)
可以在复制的主键列后面选择性地定义其他列,但不能在它们之前;任何此类额外列都不能为 NOT NULL
。NDB 集群支持三个额外的预定义可选列 NDB$OP_TYPE
、NDB$CFT_CAUSE
和 NDB$ORIG_TRANSID
,这些列在接下来的几段中描述。
NDB$OP_TYPE
:此列可用于获取导致冲突的操作类型。如果使用此列,请按以下所示定义:
NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW',
'REFRESH_ROW', 'READ_ROW') NOT NULL
WRITE_ROW
、UPDATE_ROW
和 DELETE_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_ROW
和 WRITE_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_epoch2
和Ndb_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
作为“时间戳”。可以通过以下步骤完成:
-
确保你已经使用
--ndb-log-update-as-write=OFF
启动了源 mysqld。 -
在源上执行这个
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
) 具有相同的效果;使用服务器默认值。 -
创建
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;
需要按照以下顺序执行以下步骤:
-
首先——在创建
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
列中。 -
为
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_TYPE
、NDB$CFT_CAUSE
或NDB$ORIG_TRANSID
中的一个列,因此四个必需列需要使用NDB$
前缀。 -
如前所示创建
test.t2
表。
这些步骤必须针对每个要使用NDB$OLD()
执行冲突解决的表进行遵循。对于每个这样的表,必须在mysql.ndb_replication
中有一个相应的行,并且在被复制的表所在的同一数据库中必须有一个异常表。
阅读冲突检测和解决。 NDB Cluster 还支持跟踪读操作,这使得在循环复制设置中可以管理一个集群中给定行的读取与另一个集群中相同行的更新或删除之间的冲突。此示例使用employee
和department
表来模拟这样一种情况:在源集群(以下简称为集群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
中的两个表t1
和t2
,并且希望对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_UPDATE
(7
),这意味着始终记录完整行。有关其他可能值,请参见 ndb_replication Table。
您还可以为每个需要使用冲突解决的NDB
表创建一个异常表。异常表记录由给定表的冲突解决函数拒绝的所有行。可以使用以下两个 SQL 语句为表t1
和t2
创建用于复制冲突检测的异常表:
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
列被用作时间戳列。
一旦在源上创建了t1
和t2
,它们就会被复制并假定存在于源和副本上。在本示例的其余部分中,我们使用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)
在源上插入的行,其时间戳大于副本中冲突行的时间戳,已替换了那些行。在副本上,我们接下来插入两行新行,这些行不与t1
或t2
中的任何现有行冲突,就像这样:
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 发行说明
NDB Cluster 发行版本的更改与本参考手册分开记录;您可以在 NDB 8.0 Release Notes 找到每个 NDB Cluster 8.0 版本更改的发行说明。
您可以从 NDB Cluster Release Notes 获取旧版本 NDB Cluster 的发行说明。
第二十六章 分区
目录
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 中,分区支持由InnoDB
和NDB
存储引擎提供。
MySQL 8.0 目前不支持使用除InnoDB
或NDB
之外的任何存储引擎对表进行分区,例如MyISAM
。尝试使用不提供本机分区支持的存储引擎创建分区表将失败,并显示ER_CHECK_NOT_IMPLEMENTED
。
由 Oracle 提供的 MySQL 8.0 社区二进制文件包括由InnoDB
和NDB
存储引擎提供的分区支持。有关 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 新闻网站,提供与 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 中可以是模数、简单匹配一组范围或值列表、内部哈希函数或线性哈希函数。该函数根据用户指定的分区类型进行选择,并将用户提供的表达式的值作为其参数。该表达式可以是列值、作用于一个或多个列值的函数,或一个或多个列值的集合,具体取决于所使用的分区类型。
对于 RANGE
、LIST
和 [LINEAR
] HASH
分区,分区列的值将传递给分区函数,该函数返回表示应将该特定记录存储在哪个分区中的整数值。该函数必须是非常量且非随机的。它不能包含任何查询,但可以使用在 MySQL 中有效的 SQL 表达式,只要该表达式返回 NULL
或整数 intval
,使得
-MAXVALUE <= *intval* <= MAXVALUE
(MAXVALUE
用于表示所讨论整数类型的最小上界。-MAXVALUE
表示最大下界。)
对于[LINEAR
] KEY
、RANGE COLUMNS
和LIST 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 中,仅支持分区的存储引擎是InnoDB
和NDB
。不能与不支持分区的存储引擎一起使用分区;这些包括MyISAM
、MERGE
、CSV
和FEDERATED
存储引擎。
使用NDB
进行KEY
或LINEAR KEY
分区是可能的,但不支持使用其他类型的用户定义分区的表使用此存储引擎。此外,使用用户定义分区的NDB
表必须具有显式主键,并且表的分区表达式中引用的任何列必须是主键的一部分。但是,如果在用于创建或修改用户分区的NDB
表的PARTITION BY KEY
或PARTITION 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_engine
为InnoDB
。
重要提示
分区适用于表的所有数据和索引;您不能仅对数据进行分区而不对索引进行分区,反之亦然,也不能仅对表的一部分进行分区。
每个分区的数据和索引可以使用DATA DIRECTORY
和INDEX 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)
);
因为pk
和uk
键没有共同的列,所以没有列可用于分区表达式。在这种情况下的可能解决方法包括将name
列添加到表的主键中,将id
列添加到uk
中,或者简单地完全删除唯一键。有关更多信息,请参阅第 26.6.1 节,“分区键、主键和唯一键”。
此外,MAX_ROWS
和MIN_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
条件匹配的p0
和p1
分区中的行。在这种情况下,MySQL 不会检查表t
的其他分区;当您已经知道要检查哪个分区或哪些分区时,这可以极大地加快查询速度。分区选择也支持数据修改语句DELETE
、INSERT
、REPLACE
、UPDATE
和LOAD DATA
、LOAD XML
。有关这些语句的更多信息和示例,请参阅这些语句的描述。
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 值
本节讨论了 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 中,基于DATE
、TIME
或DATETIME
列或利用这些列的表达式创建分区方案并不困难。
当按KEY
或LINEAR KEY
进行分区时,您可以使用DATE
、TIME
或DATETIME
列作为分区列,而无需对列值进行任何修改。例如,在 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 COLUMNS
和LIST COLUMNS
分区使用DATE
或DATETIME
列作为分区列。
其他分区类型需要产生整数值或NULL
的分区表达式。如果您希望通过RANGE
、LIST
、HASH
或LINEAR HASH
进行基于日期的分区,您可以简单地使用一个操作DATE
、TIME
或DATETIME
列并返回这样一个值的函数,如下所示:
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 个分区,这些分区被编号为0
、1
、2
和3
。对于RANGE
和LIST
分区类型,必须确保为每个分区编号定义了一个分区。对于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 在 mypart
和 MyPart
分区名称之间看不到任何区别。
当你为表指定分区数时,这必须表示为一个正的、非零的整数字面值,不能以0.8E+01
或6-2
这样的表达式表示,即使它求值为整数值也不行。不允许使用小数。
在接下来的章节中,我们并不一定提供用于创建每种分区类型的语法的所有可能形式;有关此信息,请参见第 15.1.20 节,“CREATE TABLE 语句”。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下