doc: 阅读redis sentinel文档

This commit is contained in:
2025-12-22 16:44:19 +08:00
parent 61578e0ed2
commit f5d24865b5
9 changed files with 2140 additions and 2057 deletions

View File

@@ -1,363 +1,363 @@
- [文件](#文件)
- [参数](#参数)
- [参数查看](#参数查看)
- [参数类型](#参数类型)
- [动态参数修改](#动态参数修改)
- [静态参数修改](#静态参数修改)
- [日志文件](#日志文件)
- [错误日志](#错误日志)
- [慢查询日志](#慢查询日志)
- [log\_queries\_not\_using\_indexes](#log_queries_not_using_indexes)
- [查询日志](#查询日志)
- [二进制日志](#二进制日志)
- [max\_binlog\_size](#max_binlog_size)
- [binlog\_cache\_size](#binlog_cache_size)
- [binlog\_cache\_use](#binlog_cache_use)
- [binlog\_cache\_disk\_use](#binlog_cache_disk_use)
- [sync\_binlog](#sync_binlog)
- [innodb\_flush\_log\_at\_trx\_commit](#innodb_flush_log_at_trx_commit)
- [binlog\_format](#binlog_format)
- [使用statement可能会存在的问题](#使用statement可能会存在的问题)
- [mysqlbinlog](#mysqlbinlog)
- [pid文件](#pid文件)
- [表结构定义文件](#表结构定义文件)
- [表空间文件](#表空间文件)
- [innodb\_data\_file\_path](#innodb_data_file_path)
- [innodb\_file\_per\_table](#innodb_file_per_table)
- [redo log文件](#redo-log文件)
- [循环写入](#循环写入)
- [redo log capacity](#redo-log-capacity)
- [redo log和binlog的区别](#redo-log和binlog的区别)
- [记录内容](#记录内容)
- [写入时机](#写入时机)
- [redo log写入时机](#redo-log写入时机)
# 文件
## 参数
### 参数查看
mysql参数为键值对可以通过`show variables`命令查看所有的数据库参数,并可以通过`like`来过滤参数名称。
除了`show variables`命令之外,还能够在`performance_schema`下的`global_variables`视图来查找数据库参数,示例如下:
```sql
-- 查看innodb_buffer_pool_size参数
show variables like 'innodb_buffer_pool_size'
```
上述`show variables`命令的执行结果为
| Variable\_name | Value |
| :--- | :--- |
| innodb\_buffer\_pool\_size | 4294967296 |
```sql
select * from performance_schema.global_variables where variable_name like 'innodb_buffer_pool%';
```
上述sql的执行结果如下
| VARIABLE\_NAME | VARIABLE\_VALUE |
| :--- | :--- |
| innodb\_buffer\_pool\_chunk\_size | 134217728 |
| innodb\_buffer\_pool\_dump\_at\_shutdown | ON |
| innodb\_buffer\_pool\_dump\_now | OFF |
| innodb\_buffer\_pool\_dump\_pct | 25 |
| innodb\_buffer\_pool\_filename | ib\_buffer\_pool |
| innodb\_buffer\_pool\_in\_core\_file | ON |
| innodb\_buffer\_pool\_instances | 4 |
| innodb\_buffer\_pool\_load\_abort | OFF |
| innodb\_buffer\_pool\_load\_at\_startup | ON |
| innodb\_buffer\_pool\_load\_now | OFF |
| innodb\_buffer\_pool\_size | 4294967296 |
### 参数类型
mysql中的参数可以分为`动态``静态`两种类型,
- 动态动态参数代表可以在mysql运行过程中进行修改
- 静态:代表在整个实例的声明周期内都不得进行修改
#### 动态参数修改
对于动态参数,可以在运行时通过`SET`命令来进行修改,`SET`命令语法如下:
```sql
set
| [global | session] system_var_name=expr
| [@@global. | @@session. | @@] system_var_name = expr
```
在上述语法中,`global``session`关键字代表该动态参数的修改是针对`当前会话`还是针对`整个实例的生命周期`
- 有些动态参数只能在会话范围内进行修改,例如`autocommit`
- 有些参数修改后,实例整个生命周期内都会生效,例如`binglog_cache_size`
- 有些参数既可以在会话范围内进行修改,又可以在实例声明周期范围内进行修改,例如`read_buffer_size`
使用示例如下:
查询read_buffer_size的global和session值
```sql
-- 查询read_buffer_size的global和session值
select @@session.read_buffer_size,@@global.read_buffer_size;
```
返回结果为
| @@session.read\_buffer\_size | @@global.read\_buffer\_size |
| :--- | :--- |
| 131072 | 131072 |
设置@@session.read_buffer_size为524288
```sql
set @@session.read_buffer_size = 1024 * 512;
```
设置后再次查询read_buffer_size的global和session值结果为
| @@session.read\_buffer\_size | @@global.read\_buffer\_size |
| :--- | :--- |
| 524288 | 131072 |
在调用set命令修改session read_buffer_size参数后session参数发生变化但是global参数仍然为旧的值。
> `set session xxx`命令并不会对global参数的值造成影响新会话的参数值仍然为修改前的值。
之后再对global read_buffer_size值进行修改执行如下命令
```sql
set @@global.read_buffer_size = 496 * 1024;
```
执行该命令后sesion和global参数值为
| @@session.read\_buffer\_size | @@global.read\_buffer\_size |
| :--- | :--- |
| 524288 | 507904 |
> `set global xxx`命令只会修改global参数值对session参数值不会造成影响新的session其`session参数值, global参数值`和修改后的global参数值保持一致
> 即使针对参数的global值进行了修改其影响范围是当前实例的整个生命周期`但是其并不会对参数文件中的参数值进行修改故而下次启动mysql实例时仍然会从参数文件中取值新实例的值仍然是修改前的值`。
>
> 如果想要修改下次启动实例的参数值,需要修改参数文件中该参数的值。(参数文件路径通常为`/etc/my.cnf`
#### 静态参数修改
在运行时,如果尝试对静态参数进行修改,那么会发生错误,示例如下:
```sql
> set global datadir='/db/mysql'
[2025-01-30 15:05:17] [HY000][1238] Variable 'datadir' is a read only variable
```
## 日志文件
mysql中常见日志文件如下
- 错误日志(error log)
- 二进制日志binlog
- 慢查询日志slow query log
- 查询日志log
### 错误日志
错误日志针对mysql的启动、运行、关闭过程进行了记录用户可以通过`show variables like 'log_error';`来获取错误日志的路径:
```sql
show variables like 'log_error';
```
其输出值如下:
| Variable\_name | Value |
| :--- | :--- |
| log\_error | /var/log/mysql/mysqld.log |
当mysql数据库无法正常启动时应当首先查看错误日志。
### 慢查询日志
慢查询日志存在一个阈值,通过`long_query_time`参数来进行控制,该参数默认值为`10`代表慢查询的限制为10s。
通过`slow_query_log`参数,可以控制是否日志输出慢查询日志,默认为`OFF`,如果需要开启慢查询日志,需要将该值设置为`ON`
关于慢查询日志的输出地点,可以通过`log_output`参数来进行控制。该参数默认为`FILE`,支持`FILE, TABLE, NONE``log_output`支持制定多个值,多个值之间可以通过`,`分隔,当值中包含`NONE`时,以`NONE`优先。
#### log_queries_not_using_indexes
`log_queryies_not_using_indexes`开启时如果运行的sql语句没有使用索引那么这条sql同样会被输出到慢查询日志。该参数默认关闭。
`log_throttle_queries_not_using_idnexes`用于记录`每分钟允许记录到慢查询日志并且没有使用索引`的sql语句次数该参数值默认为0代表每分钟输出到慢查询日志中的数量没有限制。
该参数主要用于防止大量没有使用索引的sql添加到慢查询日志中造成慢查询日志大小快速增加。
当慢查询日志中的内容越来越多时可以通过mysql提供的工具`mysqldumpslow`命令,示例如下:
```sql
mysqldumpslow -s at -n 10 ${slow_query_log_path}
```
### 查询日志
查询日志记录了对mysql数据库所有的请求信息无论请求是否正确执行。
查询日志通过`general_log`参数来进行控制,默认该参数值为`OFF`.
### 二进制日志
二进制日志binary log记录了针对mysql数据库执行的所有更改操作不包含select以及show这类读操作
对于update操作等即使没有对数据库进行修改affected rows为0也会被写入到binary log中。
二进制日志的主要用途如下:
- 恢复recovery某些数据恢复需要二进制日志例如在数据库全备份文件恢复后用户可以通过二进制日志进行point-in-time的恢复
- 复制replication通过将一台主机master的binlog同步到另一台主机slave并且在另一台主机上执行该binlog可以令slave与master进行实时同步
- 审计audit用户可以对binlog中的信息进行审计判断是否存在对数据库进行的注入攻击
通过参数`log_bin`可以控制是否启用二进制日志。
binlog通常存放在`datadir`参数所指定的目录路径下。在该路径下,还存在`binlog.index`文件该文件为binlog的索引文件文件内容包含所有binlog的文件名称。
#### max_binlog_size
`max_binlog_size`参数控制单个binlog文件的最大大小如果单个文件超过该值会产生新的二进制文件新binlog的后缀会+1并且新文件的文件名会被记录到`.index`文件中。
`max_binlog_size`的默认值大小为`1G`
#### binlog_cache_size
当使用innodb存储引擎时所有未提交事务的binlog会被记录到缓存中等到事务提交后会将缓存中的binlog写入到文件中。缓存大小通过`binlog_cache_size`决定,该值默认为`32768`,即`32KB`
`binlog_cache_size`是基于会话的,`在每个线程开启一个事务时mysql会自动分配一个大小为binlog_cache_size大小的缓存因而该值不能设置过大`
当一个事务的记录大于设定的`binlog_cache_size`mysql会将缓冲中的日志写入到一个临时文件中故而该值无法设置过小。
通过`show global status like 'binlog_cache%`命令可以查看`binlog_cache_use``binlog_cache_disk_use`的状态可以通过上述两个状态判断binlog cache大小是否合适。
##### binlog_cache_use
`binlog_cache_use`记录了使用缓冲写binlog的次数
##### binlog_cache_disk_use
`binlog_cache_disk_use`记录了使用临时文件写二进制日志的次数
#### sync_binlog
`sync_binlog`参数控制mysql server同步binlog到磁盘的频率该值默认为`1`
- 0: 如果参数值为0代表mysql server禁用binary log同步到磁盘。mysql会依赖操作系统将binary log刷新到磁盘中该设置性能最佳但是遇到操作系统崩溃时可能会出现mysql事务提交但是还没有同步到binary log的场景
- 1: 如果参数值设置为1代表在事务提交之前将binary log同步到磁盘中该设置最安全但是会增加disk write次数对性能会带来负面影响。在操作系统崩溃的场景下binlog中缺失的事务还只处于prepared状态从而确保binlog中没有事务丢失
- N当参数值被设置为非`01`的值时每当n个binlog commit groups被收集到后同步binlog到磁盘。在这种情况下可能会发生事务提交但是还没有被刷新到binlog中`当n值越大时性能会越好但是也会增加数据丢失的风险`
为了在使用innodb事务和replciation时获得最好的一致性和持久性请使用如下设置
```cnf
sync_binlog=1
innodb_flush_log_at_trx_commit=1
```
#### innodb_flush_log_at_trx_commit
innodb_flush_log_at_trx_commit用于控制redo log的刷新。
该参数用于平衡`commit操作ACID的合规性`以及`更高性能`。通过修改该参数值,可以实现更佳的性能,但是在崩溃时可能会丢失事务:
- 1: 1为该参数默认值代表完全的ACID合规性日志在每次事务提交后被写入并刷新到磁盘中
- 0: 日志每秒被写入和刷新到磁盘中,如果事务没有被刷新,那么日志将会在崩溃中被丢失
- 2: 每当事务提交后,日志将会被写入,并且每秒钟都会被刷新到磁盘中。如果事务没有被刷新,崩溃同样会造成日志的丢失
如果当前数据库为slave角色那么其不会把`从master同步的binlog`写入到自己的binlog中如果要实现`master=>slave=>slave`的同步架构,必须设置`log_slave_updates`参数。
#### binlog_format
binlog_format用于控制二进制文件的格式可能有如下取值
- statement: 二进制文件记录的是日志的逻辑sql语句
- row记录表的行更改情况默认值为`row`
- mixed: 如果参数被配置为mixedmysql默认会采用`statement`格式进行记录,但是在特定场景能够下会使用`row`格式:
- 使用了uuid, user, current_user,found_rows, row_count等不确定函数
- 使用了insert delay语句
- 使用了用户自定义函数
- 使用了临时表
##### 使用statement可能会存在的问题
在使用statement格式时可能会存在如下问题
- master运行randuuid等不确定函数时或使用触发器操作时会导致主从服务器上的数据不一致
- innodb的默认事务隔离级别为`repetable_read`,如果使用`read_commited`级别时statement格式可能会导致丢失更新的情况从而令master和slave的数据不一致
binlog为动态参数可以在数据库运行时进行修改并且可以针对session和global进行修改。
#### mysqlbinlog
在查看二进制日志时,可以使用`mysqlbinlog`命令,示例如下
```bash
mysqlbinlog --start-position=203 ${binlog_path}
```
## pid文件
mysql实例启动时会将进程id写入到一个文件中该文件被称为pid文件。
pid文件路径通过`pid_file`参数来进行控制fedora中默认路径为`/run/mysqld/mysqld.pid`
## 表结构定义文件
mysql中数据的存储是根据表进行的每个表都有与之对应的文件。无论表采用何种存储引擎都会存在一个以`frm`为后缀的文件,该文件中保存了该表的表结构定义。
> mysql 8中schema对应目录下不再包含frm文件。
## 表空间文件
innodb采用将存储的数据按照表空间tablespace进行存放的设计。在默认配置下将会有一个初始大小为10MB名称为ibdata1的文件该文件为默认的表空间文件。
### innodb_data_file_path
可以通过`innodb_data_file_path`参数对默认表空间文件进行设置,示例如下:
```sql
innodb_data_file_path=datafile_spec1[;datafile_spec2]...
```
用户可以通过多个文件组成一个表空间,示例如下:
```sql
innodb_data_file_path=/db/ibdata1:2000M;/dr2/db/ibdata2:2000M;autoextend
```
在上述配置中,表空间由`/db/ibdata1``/dr2/db/ibdata2`两个文件组成,如果两个文件位于不同的磁盘上,那么磁盘的负载将会被平均,数据库的整体性能将会被提高。
同时,在上述示例中,为两个文件都指定了后续属性,含义如下:
- ibdata1文件大小为2000M
- ibdata2:文件大小为2000M并且当文件大小被用完后文件会自动增长
`innodb_data_file_path`被设置后所有基于innodb存储引擎的表其数据都会记录到该共享表空间中。
### innodb_file_per_table
如果`innodb_file_per_table`被启用后默认启用则每个基于innodb存储引擎的表都可以有一个独立的表空间独立表空间的命名规则为`表名+.ibd`
通过innodb_file_per_table,用户不需要将所有的数据都放置在默认的表空间中。
> `innodb_file_per_table`所产生的独立表空间文件其仅存储该表的数据、索引和插入缓冲BITMAP信息其余信息仍然存放在默认的表空间中。
## redo log文件
redo log是一个基于磁盘的数据结构用于在crash recovery过程中纠正由`未完成事务写入的错误数据`
> 在一般操作中redo log对那些`会造成表数据发生改变的请求`进行encode操作请求通常由sql statement或地级别api发起。
redo log通常代表磁盘上的redo log file。写入重做日志文件的数据通常基于受影响的记录进行编码。在数据被写入到redo log file中时LSN值也会不断增加。
### 循环写入
innodb会按顺序写入redo log文件例如redo log file group中存在两个文件innodb会先写文件1文件1写满后会切换文件2在文件2写满后重新切换到文件1。
### redo log capacity
从mysql 8.0.30开始,`innodb_redo_log_capacity`参数用于控制redo log file占用磁盘空间的大小。该参数可以在实例启动时进行设置也可以通过`set global`来进行设置。
`innodb_redo_log_capacity`默认值为`104857600`,即`100M`
redo log文件默认位于`datadir`路径下的`#innodb_redo`目录下。innodb会尝试维护32个redo log file每个redo log file文件大小都相同`1/32 * innodb_redo_log_capacity`
redo log file将会使用`#ib_redoN`的命名方式,`N`是redo log file number。
innodb redo log file分为如下两种:
- ordinary正在被使用的redo log file
- spare等待被使用的redo log file
> 相比于ordinary redo log filespare redo log file的名称中还包含了`_tmp`后缀
每个oridnary redo log file都关联了一个制定的LSN范围可以通过查询`performance_schema.innodb_redo_log_files`表里获取LSN范围。
示例如下:
```sql
select file_name, start_lsn, end_lsn from performance_schema.innodb_redo_log_files;
```
查询结果示例如下:
| file\_name | start\_lsn | end\_lsn |
| :--- | :--- | :--- |
| ./#innodb\_redo/#ib\_redo6 | 19656704 | 22931456 |
当执行checkpoint时innodb会将checkpoint LSN存储在文件的header中在recovery过程中所有的redo log文件都将被检查并且基于最大的LSN来执行恢复操作。
常用的redo log状态如下
```bash
# resize operation status
Innodb_redo_log_resize_status
# 当前redo log capacity
Innodb_redo_log_capacity_resized
Innodb_redo_log_checkpoint_lsn
Innodb_redo_log_current_lsn
Innodb_redo_log_flushed_to_disk_lsn
Innodb_redo_log_logical_size
Innodb_redo_log_physical_size
Innodb_redo_log_read_only
Innodb_redo_log_uuid
```
> 重做日志大小设置时,如果设置大小过大,那么在执行恢复操作时,可能需要花费很长时间;如果重做日志文件大小设置过小,可能会导致事务的日志需要多次切换重做日志文件。
>
> 此外重做日志太小会频繁发生async checkpoint导致性能抖动。重做日志存在一个capacity代表了最后的checkpoint不能够超过这个阈值如果超过必须将缓冲区中的部分脏页刷新到磁盘中此时可能会造成用户线程的阻塞。
### redo log和binlog的区别
#### 记录内容
binlog记录的是一个事务的具体操作内容该日志为逻辑日志。
而innodb redo log记录的是关于某个页的修改为物理日志。
#### 写入时机
binlog仅当事务提交前才进行提交即只会写磁盘一次。
redo log则是在事务运行过程中不断有重做日志被写入到redo log file中。
### redo log写入时机
- master thread会每秒将redo log从buffer中刷新到redo log ile中不露内事务是否已经提交
- [文件](#文件)
- [参数](#参数)
- [参数查看](#参数查看)
- [参数类型](#参数类型)
- [动态参数修改](#动态参数修改)
- [静态参数修改](#静态参数修改)
- [日志文件](#日志文件)
- [错误日志](#错误日志)
- [慢查询日志](#慢查询日志)
- [log\_queries\_not\_using\_indexes](#log_queries_not_using_indexes)
- [查询日志](#查询日志)
- [二进制日志](#二进制日志)
- [max\_binlog\_size](#max_binlog_size)
- [binlog\_cache\_size](#binlog_cache_size)
- [binlog\_cache\_use](#binlog_cache_use)
- [binlog\_cache\_disk\_use](#binlog_cache_disk_use)
- [sync\_binlog](#sync_binlog)
- [innodb\_flush\_log\_at\_trx\_commit](#innodb_flush_log_at_trx_commit)
- [binlog\_format](#binlog_format)
- [使用statement可能会存在的问题](#使用statement可能会存在的问题)
- [mysqlbinlog](#mysqlbinlog)
- [pid文件](#pid文件)
- [表结构定义文件](#表结构定义文件)
- [表空间文件](#表空间文件)
- [innodb\_data\_file\_path](#innodb_data_file_path)
- [innodb\_file\_per\_table](#innodb_file_per_table)
- [redo log文件](#redo-log文件)
- [循环写入](#循环写入)
- [redo log capacity](#redo-log-capacity)
- [redo log和binlog的区别](#redo-log和binlog的区别)
- [记录内容](#记录内容)
- [写入时机](#写入时机)
- [redo log写入时机](#redo-log写入时机)
# 文件
## 参数
### 参数查看
mysql参数为键值对可以通过`show variables`命令查看所有的数据库参数,并可以通过`like`来过滤参数名称。
除了`show variables`命令之外,还能够在`performance_schema`下的`global_variables`视图来查找数据库参数,示例如下:
```sql
-- 查看innodb_buffer_pool_size参数
show variables like 'innodb_buffer_pool_size'
```
上述`show variables`命令的执行结果为
| Variable\_name | Value |
| :--- | :--- |
| innodb\_buffer\_pool\_size | 4294967296 |
```sql
select * from performance_schema.global_variables where variable_name like 'innodb_buffer_pool%';
```
上述sql的执行结果如下
| VARIABLE\_NAME | VARIABLE\_VALUE |
| :--- | :--- |
| innodb\_buffer\_pool\_chunk\_size | 134217728 |
| innodb\_buffer\_pool\_dump\_at\_shutdown | ON |
| innodb\_buffer\_pool\_dump\_now | OFF |
| innodb\_buffer\_pool\_dump\_pct | 25 |
| innodb\_buffer\_pool\_filename | ib\_buffer\_pool |
| innodb\_buffer\_pool\_in\_core\_file | ON |
| innodb\_buffer\_pool\_instances | 4 |
| innodb\_buffer\_pool\_load\_abort | OFF |
| innodb\_buffer\_pool\_load\_at\_startup | ON |
| innodb\_buffer\_pool\_load\_now | OFF |
| innodb\_buffer\_pool\_size | 4294967296 |
### 参数类型
mysql中的参数可以分为`动态``静态`两种类型,
- 动态动态参数代表可以在mysql运行过程中进行修改
- 静态:代表在整个实例的声明周期内都不得进行修改
#### 动态参数修改
对于动态参数,可以在运行时通过`SET`命令来进行修改,`SET`命令语法如下:
```sql
set
| [global | session] system_var_name=expr
| [@@global. | @@session. | @@] system_var_name = expr
```
在上述语法中,`global``session`关键字代表该动态参数的修改是针对`当前会话`还是针对`整个实例的生命周期`
- 有些动态参数只能在会话范围内进行修改,例如`autocommit`
- 有些参数修改后,实例整个生命周期内都会生效,例如`binglog_cache_size`
- 有些参数既可以在会话范围内进行修改,又可以在实例声明周期范围内进行修改,例如`read_buffer_size`
使用示例如下:
查询read_buffer_size的global和session值
```sql
-- 查询read_buffer_size的global和session值
select @@session.read_buffer_size,@@global.read_buffer_size;
```
返回结果为
| @@session.read\_buffer\_size | @@global.read\_buffer\_size |
| :--- | :--- |
| 131072 | 131072 |
设置@@session.read_buffer_size为524288
```sql
set @@session.read_buffer_size = 1024 * 512;
```
设置后再次查询read_buffer_size的global和session值结果为
| @@session.read\_buffer\_size | @@global.read\_buffer\_size |
| :--- | :--- |
| 524288 | 131072 |
在调用set命令修改session read_buffer_size参数后session参数发生变化但是global参数仍然为旧的值。
> `set session xxx`命令并不会对global参数的值造成影响新会话的参数值仍然为修改前的值。
之后再对global read_buffer_size值进行修改执行如下命令
```sql
set @@global.read_buffer_size = 496 * 1024;
```
执行该命令后sesion和global参数值为
| @@session.read\_buffer\_size | @@global.read\_buffer\_size |
| :--- | :--- |
| 524288 | 507904 |
> `set global xxx`命令只会修改global参数值对session参数值不会造成影响新的session其`session参数值, global参数值`和修改后的global参数值保持一致
> 即使针对参数的global值进行了修改其影响范围是当前实例的整个生命周期`但是其并不会对参数文件中的参数值进行修改故而下次启动mysql实例时仍然会从参数文件中取值新实例的值仍然是修改前的值`。
>
> 如果想要修改下次启动实例的参数值,需要修改参数文件中该参数的值。(参数文件路径通常为`/etc/my.cnf`
#### 静态参数修改
在运行时,如果尝试对静态参数进行修改,那么会发生错误,示例如下:
```sql
> set global datadir='/db/mysql'
[2025-01-30 15:05:17] [HY000][1238] Variable 'datadir' is a read only variable
```
## 日志文件
mysql中常见日志文件如下
- 错误日志(error log)
- 二进制日志binlog
- 慢查询日志slow query log
- 查询日志log
### 错误日志
错误日志针对mysql的启动、运行、关闭过程进行了记录用户可以通过`show variables like 'log_error';`来获取错误日志的路径:
```sql
show variables like 'log_error';
```
其输出值如下:
| Variable\_name | Value |
| :--- | :--- |
| log\_error | /var/log/mysql/mysqld.log |
当mysql数据库无法正常启动时应当首先查看错误日志。
### 慢查询日志
慢查询日志存在一个阈值,通过`long_query_time`参数来进行控制,该参数默认值为`10`代表慢查询的限制为10s。
通过`slow_query_log`参数,可以控制是否日志输出慢查询日志,默认为`OFF`,如果需要开启慢查询日志,需要将该值设置为`ON`
关于慢查询日志的输出地点,可以通过`log_output`参数来进行控制。该参数默认为`FILE`,支持`FILE, TABLE, NONE``log_output`支持制定多个值,多个值之间可以通过`,`分隔,当值中包含`NONE`时,以`NONE`优先。
#### log_queries_not_using_indexes
`log_queryies_not_using_indexes`开启时如果运行的sql语句没有使用索引那么这条sql同样会被输出到慢查询日志。该参数默认关闭。
`log_throttle_queries_not_using_idnexes`用于记录`每分钟允许记录到慢查询日志并且没有使用索引`的sql语句次数该参数值默认为0代表每分钟输出到慢查询日志中的数量没有限制。
该参数主要用于防止大量没有使用索引的sql添加到慢查询日志中造成慢查询日志大小快速增加。
当慢查询日志中的内容越来越多时可以通过mysql提供的工具`mysqldumpslow`命令,示例如下:
```sql
mysqldumpslow -s at -n 10 ${slow_query_log_path}
```
### 查询日志
查询日志记录了对mysql数据库所有的请求信息无论请求是否正确执行。
查询日志通过`general_log`参数来进行控制,默认该参数值为`OFF`.
### 二进制日志
二进制日志binary log记录了针对mysql数据库执行的所有更改操作不包含select以及show这类读操作
对于update操作等即使没有对数据库进行修改affected rows为0也会被写入到binary log中。
二进制日志的主要用途如下:
- 恢复recovery某些数据恢复需要二进制日志例如在数据库全备份文件恢复后用户可以通过二进制日志进行point-in-time的恢复
- 复制replication通过将一台主机master的binlog同步到另一台主机slave并且在另一台主机上执行该binlog可以令slave与master进行实时同步
- 审计audit用户可以对binlog中的信息进行审计判断是否存在对数据库进行的注入攻击
通过参数`log_bin`可以控制是否启用二进制日志。
binlog通常存放在`datadir`参数所指定的目录路径下。在该路径下,还存在`binlog.index`文件该文件为binlog的索引文件文件内容包含所有binlog的文件名称。
#### max_binlog_size
`max_binlog_size`参数控制单个binlog文件的最大大小如果单个文件超过该值会产生新的二进制文件新binlog的后缀会+1并且新文件的文件名会被记录到`.index`文件中。
`max_binlog_size`的默认值大小为`1G`
#### binlog_cache_size
当使用innodb存储引擎时所有未提交事务的binlog会被记录到缓存中等到事务提交后会将缓存中的binlog写入到文件中。缓存大小通过`binlog_cache_size`决定,该值默认为`32768`,即`32KB`
`binlog_cache_size`是基于会话的,`在每个线程开启一个事务时mysql会自动分配一个大小为binlog_cache_size大小的缓存因而该值不能设置过大`
当一个事务的记录大于设定的`binlog_cache_size`mysql会将缓冲中的日志写入到一个临时文件中故而该值无法设置过小。
通过`show global status like 'binlog_cache%`命令可以查看`binlog_cache_use``binlog_cache_disk_use`的状态可以通过上述两个状态判断binlog cache大小是否合适。
##### binlog_cache_use
`binlog_cache_use`记录了使用缓冲写binlog的次数
##### binlog_cache_disk_use
`binlog_cache_disk_use`记录了使用临时文件写二进制日志的次数
#### sync_binlog
`sync_binlog`参数控制mysql server同步binlog到磁盘的频率该值默认为`1`
- 0: 如果参数值为0代表mysql server禁用binary log同步到磁盘。mysql会依赖操作系统将binary log刷新到磁盘中该设置性能最佳但是遇到操作系统崩溃时可能会出现mysql事务提交但是还没有同步到binary log的场景
- 1: 如果参数值设置为1代表在事务提交之前将binary log同步到磁盘中该设置最安全但是会增加disk write次数对性能会带来负面影响。在操作系统崩溃的场景下binlog中缺失的事务还只处于prepared状态从而确保binlog中没有事务丢失
- N当参数值被设置为非`01`的值时每当n个binlog commit groups被收集到后同步binlog到磁盘。在这种情况下可能会发生事务提交但是还没有被刷新到binlog中`当n值越大时性能会越好但是也会增加数据丢失的风险`
为了在使用innodb事务和replciation时获得最好的一致性和持久性请使用如下设置
```cnf
sync_binlog=1
innodb_flush_log_at_trx_commit=1
```
#### innodb_flush_log_at_trx_commit
innodb_flush_log_at_trx_commit用于控制redo log的刷新。
该参数用于平衡`commit操作ACID的合规性`以及`更高性能`。通过修改该参数值,可以实现更佳的性能,但是在崩溃时可能会丢失事务:
- 1: 1为该参数默认值代表完全的ACID合规性日志在每次事务提交后被写入并刷新到磁盘中
- 0: 日志每秒被写入和刷新到磁盘中,如果事务没有被刷新,那么日志将会在崩溃中被丢失
- 2: 每当事务提交后,日志将会被写入,并且每秒钟都会被刷新到磁盘中。如果事务没有被刷新,崩溃同样会造成日志的丢失
如果当前数据库为slave角色那么其不会把`从master同步的binlog`写入到自己的binlog中如果要实现`master=>slave=>slave`的同步架构,必须设置`log_slave_updates`参数。
#### binlog_format
binlog_format用于控制二进制文件的格式可能有如下取值
- statement: 二进制文件记录的是日志的逻辑sql语句
- row记录表的行更改情况默认值为`row`
- mixed: 如果参数被配置为mixedmysql默认会采用`statement`格式进行记录,但是在特定场景能够下会使用`row`格式:
- 使用了uuid, user, current_user,found_rows, row_count等不确定函数
- 使用了insert delay语句
- 使用了用户自定义函数
- 使用了临时表
##### 使用statement可能会存在的问题
在使用statement格式时可能会存在如下问题
- master运行randuuid等不确定函数时或使用触发器操作时会导致主从服务器上的数据不一致
- innodb的默认事务隔离级别为`repetable_read`,如果使用`read_commited`级别时statement格式可能会导致丢失更新的情况从而令master和slave的数据不一致
binlog为动态参数可以在数据库运行时进行修改并且可以针对session和global进行修改。
#### mysqlbinlog
在查看二进制日志时,可以使用`mysqlbinlog`命令,示例如下
```bash
mysqlbinlog --start-position=203 ${binlog_path}
```
## pid文件
mysql实例启动时会将进程id写入到一个文件中该文件被称为pid文件。
pid文件路径通过`pid_file`参数来进行控制fedora中默认路径为`/run/mysqld/mysqld.pid`
## 表结构定义文件
mysql中数据的存储是根据表进行的每个表都有与之对应的文件。无论表采用何种存储引擎都会存在一个以`frm`为后缀的文件,该文件中保存了该表的表结构定义。
> mysql 8中schema对应目录下不再包含frm文件。
## 表空间文件
innodb采用将存储的数据按照表空间tablespace进行存放的设计。在默认配置下将会有一个初始大小为10MB名称为ibdata1的文件该文件为默认的表空间文件。
### innodb_data_file_path
可以通过`innodb_data_file_path`参数对默认表空间文件进行设置,示例如下:
```sql
innodb_data_file_path=datafile_spec1[;datafile_spec2]...
```
用户可以通过多个文件组成一个表空间,示例如下:
```sql
innodb_data_file_path=/db/ibdata1:2000M;/dr2/db/ibdata2:2000M;autoextend
```
在上述配置中,表空间由`/db/ibdata1``/dr2/db/ibdata2`两个文件组成,如果两个文件位于不同的磁盘上,那么磁盘的负载将会被平均,数据库的整体性能将会被提高。
同时,在上述示例中,为两个文件都指定了后续属性,含义如下:
- ibdata1文件大小为2000M
- ibdata2:文件大小为2000M并且当文件大小被用完后文件会自动增长
`innodb_data_file_path`被设置后所有基于innodb存储引擎的表其数据都会记录到该共享表空间中。
### innodb_file_per_table
如果`innodb_file_per_table`被启用后默认启用则每个基于innodb存储引擎的表都可以有一个独立的表空间独立表空间的命名规则为`表名+.ibd`
通过innodb_file_per_table,用户不需要将所有的数据都放置在默认的表空间中。
> `innodb_file_per_table`所产生的独立表空间文件其仅存储该表的数据、索引和插入缓冲BITMAP信息其余信息仍然存放在默认的表空间中。
## redo log文件
redo log是一个基于磁盘的数据结构用于在crash recovery过程中纠正由`未完成事务写入的错误数据`
> 在一般操作中redo log对那些`会造成表数据发生改变的请求`进行encode操作请求通常由sql statement或地级别api发起。
redo log通常代表磁盘上的redo log file。写入重做日志文件的数据通常基于受影响的记录进行编码。在数据被写入到redo log file中时LSN值也会不断增加。
### 循环写入
innodb会按顺序写入redo log文件例如redo log file group中存在两个文件innodb会先写文件1文件1写满后会切换文件2在文件2写满后重新切换到文件1。
### redo log capacity
从mysql 8.0.30开始,`innodb_redo_log_capacity`参数用于控制redo log file占用磁盘空间的大小。该参数可以在实例启动时进行设置也可以通过`set global`来进行设置。
`innodb_redo_log_capacity`默认值为`104857600`,即`100M`
redo log文件默认位于`datadir`路径下的`#innodb_redo`目录下。innodb会尝试维护32个redo log file每个redo log file文件大小都相同`1/32 * innodb_redo_log_capacity`
redo log file将会使用`#ib_redoN`的命名方式,`N`是redo log file number。
innodb redo log file分为如下两种:
- ordinary正在被使用的redo log file
- spare等待被使用的redo log file
> 相比于ordinary redo log filespare redo log file的名称中还包含了`_tmp`后缀
每个oridnary redo log file都关联了一个制定的LSN范围可以通过查询`performance_schema.innodb_redo_log_files`表里获取LSN范围。
示例如下:
```sql
select file_name, start_lsn, end_lsn from performance_schema.innodb_redo_log_files;
```
查询结果示例如下:
| file\_name | start\_lsn | end\_lsn |
| :--- | :--- | :--- |
| ./#innodb\_redo/#ib\_redo6 | 19656704 | 22931456 |
当执行checkpoint时innodb会将checkpoint LSN存储在文件的header中在recovery过程中所有的redo log文件都将被检查并且基于最大的LSN来执行恢复操作。
常用的redo log状态如下
```bash
# resize operation status
Innodb_redo_log_resize_status
# 当前redo log capacity
Innodb_redo_log_capacity_resized
Innodb_redo_log_checkpoint_lsn
Innodb_redo_log_current_lsn
Innodb_redo_log_flushed_to_disk_lsn
Innodb_redo_log_logical_size
Innodb_redo_log_physical_size
Innodb_redo_log_read_only
Innodb_redo_log_uuid
```
> 重做日志大小设置时,如果设置大小过大,那么在执行恢复操作时,可能需要花费很长时间;如果重做日志文件大小设置过小,可能会导致事务的日志需要多次切换重做日志文件。
>
> 此外重做日志太小会频繁发生async checkpoint导致性能抖动。重做日志存在一个capacity代表了最后的checkpoint不能够超过这个阈值如果超过必须将缓冲区中的部分脏页刷新到磁盘中此时可能会造成用户线程的阻塞。
### redo log和binlog的区别
#### 记录内容
binlog记录的是一个事务的具体操作内容该日志为逻辑日志。
而innodb redo log记录的是关于某个页的修改为物理日志。
#### 写入时机
binlog仅当事务提交前才进行提交即只会写磁盘一次。
redo log则是在事务运行过程中不断有重做日志被写入到redo log file中。
### redo log写入时机
- master thread会每秒将redo log从buffer中刷新到redo log ile中不露内事务是否已经提交
- innodb_flush_log_at_trx_commit控制redo log的刷新时机默认情况下在事务提交前会将数据从redo log buffer刷新到redo log file中

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +1,80 @@
- [CAP Theorem in DBMS](#cap-theorem-in-dbms)
- [What is the CAP Theorem](#what-is-the-cap-theorem)
- [Consistency](#consistency)
- [Availability](#availability)
- [Partition Tolerance](#partition-tolerance)
- [The Trade-Offs in the CAP Theorem](#the-trade-offs-in-the-cap-theorem)
- [CAConsistency and Availability](#caconsistency-and-availability)
- [AP(Availability and Partition Tolerance)](#apavailability-and-partition-tolerance)
- [CP(Consistency and Partition Tolerance)](#cpconsistency-and-partition-tolerance)
# CAP Theorem in DBMS
在网络共享数据系统设计中固有的权衡令构建一个可靠且高效的系统十分困难。CAP理论是理解分布式系统中这些权衡的核心基础。CAP理论强调了系统设计者在处理distributed data replication时的局限性。CAP理论指出在分布式系统中只能同时满足`一致性、可用性、分区容错`这三个特性中的两种。
> CAP理论中对分布式系统提出了如下三个特性
> - consistency(一致性)
> - availability可用性
> - partition tolerance分区容错
由于该底层限制,开发者必须根据其应用的需要谨慎的平衡这些属性。设计者必须决定优先考虑哪些特性,从而获取最佳的性能和系统可靠性。
## What is the CAP Theorem
CAP理论是分布式系统的基础概念其指出分布式系统中所有的三个特性无法被同时满足。
### Consistency
`Consistency`代表network中所有的节点都包含相同的replicated data拷贝而这些数据对不同事务可见。其保证分布式居群中所有节点返回相同、最新的数据。其代表所有client对数据的视角都相同。存在许多不同类型的一致性模型而CAP中的一致性代指的是顺序一致性是一种非常强的一致性形式。
`ACID``CAP`中都包含一致性,但是这两种一致性所有不同:
-`ACID`中,其代表事务不能破坏数据库的完整性约束
-`CAP`中,其代表分布式系统中相同数据项在不同副本中的一致性
### Availability
Availability代表每个对数据项的read/write request要么能被成功处理要么能收到一个操作无法被完成的响应。每个non-failing节点都会为所有读写请求在合理的时间范围内生成响应。
其中“每个节点”代表即使发生network partition节点只要不处于failing状态无论位于network partition的哪一侧都应该能在合理的时间范围内返回响应。
### Partition Tolerance
partition tolerance代表在连接节点的网络发生错误、造成两个或多个分区时系统仍然能够继续进行操作。通常在出现network partition时每个partition中的节点只能彼此互相沟通跨分区的节点通信被阻断。
这意味着即使发生network partition系统仍然能持续运行并保证其一致性。network partition是不可避免地在网络分区恢复正常后拥有partition tolerance的分布式系统能够优雅的从分区状态中恢复。
CAP理论指出分布式数据库最多只能兼顾如下三个特性中的两种consistency, availability, partition tolerance。
<img src="https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram.png" alt="CAP - venndiagram" width="390" height="321" srcset="https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram.png 390w,https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram-100.png 100w,https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram-200.png 200w,https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram-300.png 300w" loading="lazy">
## The Trade-Offs in the CAP Theorem
CAP理论表明分布式系统中只能同时满足三种特性中的两种。
### CAConsistency and Availability
> 对于CA类型的系统其总是可以接收来源于用户的查询和修改数据请求并且分布式网络中所有database nodes都会返回相同的响应
然而该种类似的分布式系统在现实世界中不可能存在因为在network failure发生时仅有如下两种选项
- 发送network failure发生前复制的old data
- 不允许用户访问old data
如果我们选择第一个选项那么系统将满足Availibility如果选择第二个选项系统则是Consistent。
`在分布式系统中consistency和availability的组合是不可能的`。为了实现`CA`,系统必须是单体架构,当用户更新系统状态时,所有其他用户都能访问到该更新,这将代表系统满足一致性;而在单体架构中,所有用户都连接到一个节点上,这代表其是可用的。`CA`系统通常不被青睐因为实现分布式计算必须牺牲consistency或availability中的一种并将剩余的和partition tolerance组合`CP/AP`系统。
> CAP中的AAvailability只是要求非failing状态下的节点能够在合理的时间范围内返回响应故而单体架构可以满足`Availability`。
>
> 即使单体架构可能因单点故障导致系统不可用,不满足`Reliable`(可靠性),并不影响其满足`Availability`(可用性)。
<img src="https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap.png" alt="CA (Consistency and Availability)" width="631" height="579" srcset="https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap.png 631w,https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap-100.png 100w,https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap-200.png 200w,https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap-300.png 300w" loading="lazy">
### AP(Availability and Partition Tolerance)
> 这种类型的系统本质上是分布式的确保即使在network partition场景下用户发送的针对database nodes中数据的查看和修改请求不会被丢失
该系统优先考虑了Availability而非Consistency并且可能会返回过期的数据。一些技术failure可能会导致partition故而过期数据则是代表partition产生前被同步的数据。
AP系统通常在构建社交媒体网站如Facebook和在线内容网站如YouTube时使用其并不要求一致性。对于使用AP系统的场景相比于不一致不可用会造成更大的问题。
AP系统是分布式的可以分布于多个节点即使在network partition发生的前提下也能够可靠运行。
### CP(Consistency and Partition Tolerance)
> 该类系统本质上是分布式的确保由用户发起的针对database nodes中数据进行查看或修改的请求在存在network partition的场景下会直接被丢弃而不是返回不一致的数据
`CP`系统优先考虑了Consistency而非Availability如果发生network partition其不允许用户从replica读取`在network partition发生前同步的数据`。对于部分应用程序来说,相比于可用性,其更强调数据的一致性,例如股票交易系统、订票系统、银行系统等)
例如在订票系统中还剩余一个可订购座位。在该CP系统中将会创建数据库的副本并且将副本发送给系统中其他的节点。此时如果发生网络问题那么连接到partitioned node的用户将会从replica获取数据。此时其他连接到unpartitioned部分的用户则可以对剩余的作为进行预定。这样在连接到partitioned node的用户视角中仍然存在一个seat其将导致数据不一致。
- [CAP Theorem in DBMS](#cap-theorem-in-dbms)
- [What is the CAP Theorem](#what-is-the-cap-theorem)
- [Consistency](#consistency)
- [Availability](#availability)
- [Partition Tolerance](#partition-tolerance)
- [The Trade-Offs in the CAP Theorem](#the-trade-offs-in-the-cap-theorem)
- [CAConsistency and Availability](#caconsistency-and-availability)
- [AP(Availability and Partition Tolerance)](#apavailability-and-partition-tolerance)
- [CP(Consistency and Partition Tolerance)](#cpconsistency-and-partition-tolerance)
# CAP Theorem in DBMS
在网络共享数据系统设计中固有的权衡令构建一个可靠且高效的系统十分困难。CAP理论是理解分布式系统中这些权衡的核心基础。CAP理论强调了系统设计者在处理distributed data replication时的局限性。CAP理论指出在分布式系统中只能同时满足`一致性、可用性、分区容错`这三个特性中的两种。
> CAP理论中对分布式系统提出了如下三个特性
> - consistency(一致性)
> - availability可用性
> - partition tolerance分区容错
由于该底层限制,开发者必须根据其应用的需要谨慎的平衡这些属性。设计者必须决定优先考虑哪些特性,从而获取最佳的性能和系统可靠性。
## What is the CAP Theorem
CAP理论是分布式系统的基础概念其指出分布式系统中所有的三个特性无法被同时满足。
### Consistency
`Consistency`代表network中所有的节点都包含相同的replicated data拷贝而这些数据对不同事务可见。其保证分布式居群中所有节点返回相同、最新的数据。其代表所有client对数据的视角都相同。存在许多不同类型的一致性模型而CAP中的一致性代指的是顺序一致性是一种非常强的一致性形式。
`ACID``CAP`中都包含一致性,但是这两种一致性所有不同:
-`ACID`中,其代表事务不能破坏数据库的完整性约束
-`CAP`中,其代表分布式系统中相同数据项在不同副本中的一致性
### Availability
Availability代表每个对数据项的read/write request要么能被成功处理要么能收到一个操作无法被完成的响应。每个non-failing节点都会为所有读写请求在合理的时间范围内生成响应。
其中“每个节点”代表即使发生network partition节点只要不处于failing状态无论位于network partition的哪一侧都应该能在合理的时间范围内返回响应。
### Partition Tolerance
partition tolerance代表在连接节点的网络发生错误、造成两个或多个分区时系统仍然能够继续进行操作。通常在出现network partition时每个partition中的节点只能彼此互相沟通跨分区的节点通信被阻断。
这意味着即使发生network partition系统仍然能持续运行并保证其一致性。network partition是不可避免地在网络分区恢复正常后拥有partition tolerance的分布式系统能够优雅的从分区状态中恢复。
CAP理论指出分布式数据库最多只能兼顾如下三个特性中的两种consistency, availability, partition tolerance。
<img src="https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram.png" alt="CAP - venndiagram" width="390" height="321" srcset="https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram.png 390w,https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram-100.png 100w,https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram-200.png 200w,https://media.geeksforgeeks.org/wp-content/uploads/20240808172250/venndiagram-300.png 300w" loading="lazy">
## The Trade-Offs in the CAP Theorem
CAP理论表明分布式系统中只能同时满足三种特性中的两种。
### CAConsistency and Availability
> 对于CA类型的系统其总是可以接收来源于用户的查询和修改数据请求并且分布式网络中所有database nodes都会返回相同的响应
然而该种类似的分布式系统在现实世界中不可能存在因为在network failure发生时仅有如下两种选项
- 发送network failure发生前复制的old data
- 不允许用户访问old data
如果我们选择第一个选项那么系统将满足Availibility如果选择第二个选项系统则是Consistent。
`在分布式系统中consistency和availability的组合是不可能的`。为了实现`CA`,系统必须是单体架构,当用户更新系统状态时,所有其他用户都能访问到该更新,这将代表系统满足一致性;而在单体架构中,所有用户都连接到一个节点上,这代表其是可用的。`CA`系统通常不被青睐因为实现分布式计算必须牺牲consistency或availability中的一种并将剩余的和partition tolerance组合`CP/AP`系统。
> CAP中的AAvailability只是要求非failing状态下的节点能够在合理的时间范围内返回响应故而单体架构可以满足`Availability`。
>
> 即使单体架构可能因单点故障导致系统不可用,不满足`Reliable`(可靠性),并不影响其满足`Availability`(可用性)。
<img src="https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap.png" alt="CA (Consistency and Availability)" width="631" height="579" srcset="https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap.png 631w,https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap-100.png 100w,https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap-200.png 200w,https://media.geeksforgeeks.org/wp-content/uploads/20240813184051/cap-300.png 300w" loading="lazy">
### AP(Availability and Partition Tolerance)
> 这种类型的系统本质上是分布式的确保即使在network partition场景下用户发送的针对database nodes中数据的查看和修改请求不会被丢失
该系统优先考虑了Availability而非Consistency并且可能会返回过期的数据。一些技术failure可能会导致partition故而过期数据则是代表partition产生前被同步的数据。
AP系统通常在构建社交媒体网站如Facebook和在线内容网站如YouTube时使用其并不要求一致性。对于使用AP系统的场景相比于不一致不可用会造成更大的问题。
AP系统是分布式的可以分布于多个节点即使在network partition发生的前提下也能够可靠运行。
### CP(Consistency and Partition Tolerance)
> 该类系统本质上是分布式的确保由用户发起的针对database nodes中数据进行查看或修改的请求在存在network partition的场景下会直接被丢弃而不是返回不一致的数据
`CP`系统优先考虑了Consistency而非Availability如果发生network partition其不允许用户从replica读取`在network partition发生前同步的数据`。对于部分应用程序来说,相比于可用性,其更强调数据的一致性,例如股票交易系统、订票系统、银行系统等)
例如在订票系统中还剩余一个可订购座位。在该CP系统中将会创建数据库的副本并且将副本发送给系统中其他的节点。此时如果发生网络问题那么连接到partitioned node的用户将会从replica获取数据。此时其他连接到unpartitioned部分的用户则可以对剩余的作为进行预定。这样在连接到partitioned node的用户视角中仍然存在一个seat其将导致数据不一致。
在上述场景下CP系统通常会令其系统对`连接到partitioned node的用户`不可用。

View File

@@ -1,355 +1,355 @@
- [Replication](#replication)
- [Configuration Replication](#configuration-replication)
- [binary log file position based replication configuration overview](#binary-log-file-position-based-replication-configuration-overview)
- [Replication with Global Transaction Identifiers](#replication-with-global-transaction-identifiers)
- [GTID Format and Storage](#gtid-format-and-storage)
- [GTID Sets](#gtid-sets)
- [msyql.gtid\_executed](#msyqlgtid_executed)
- [mysql.gtid\_executed table compression](#mysqlgtid_executed-table-compression)
- [GTID生命周期](#gtid生命周期)
- [What changes are assign a GTID](#what-changes-are-assign-a-gtid)
- [Replication Implementation](#replication-implementation)
- [Replication Formats](#replication-formats)
- [使用statement-based和row-based replication的优缺点对比](#使用statement-based和row-based-replication的优缺点对比)
- [Advantages of statement-based replication](#advantages-of-statement-based-replication)
- [Disadvantages of statement-based replication](#disadvantages-of-statement-based-replication)
- [Advantages of row-based replication](#advantages-of-row-based-replication)
- [disadvantages of row-based replication](#disadvantages-of-row-based-replication)
- [Replay Log and Replication Metadata Repositories](#replay-log-and-replication-metadata-repositories)
- [The Relay Log](#the-relay-log)
# Replication
Replication允许数据从一个database server被复制到一个或多个mysql database serversreplicas。replication默认是异步的replicas并无需永久连接即可接收来自source的更新。基于configuration可以针对所有的databases、选定的databases、甚至database中指定的tables进行replicate。
mysql中replication包含如下优点
- 可拓展方案可以将负载分散到多个replicas从而提升性能。在该环境中所有的writes和updates都发生在source server。但是reads则是可以发生在一个或多个replicas上。通过该模型能够提升writes的性能因为source专门用于更新并可以通过增加replicas的数量来提高read speed
- data security 因为replica可以暂停replication过程故而可以在replica中运行备份服务而不会破坏对应的source data
- analytics实时数据可以在source中生成而对信息的分析则可以发生在replica分析过程不会影响source的性能
- long-distance data distribution可以replication来对remote site创建一个local copy而无需对source的永久访问
在mysql 8.4中支持不同的replication方式。
- 传统方式基于从source的binary log中复制事件并且需要log files和`在source和replica之间进行同步的position`
- 新的replication方式基于global transaction identifiersGTIDs是事务的并且不需要和log files、position进行交互其大大简化了许多通用的replication任务。使用GTIDs的replication保证了source和replica的一致性所有在source提交的事务都会在replica被应用。
mysql中的replication支持不同类型的同步。
- 原始的同步是单向、异步的复制一个server作为source、其他一个或多个servers作为replicas。
- NDB Cluster中支持synchronous replication
- 在mysql 8.4中支持了半同步复制通过半同步复制在source中执行的提交将会被阻塞直到至少一个replica收到transaction并对transaction进行log event且向source发送ack
- mysql 8.4同样支持delayed replication使得replica故意落后于source至少指定的时间
## Configuration Replication
### binary log file position based replication configuration overview
在该section中会描述mysql servers间基于binary log file position的replication方案。source会将database的writes和updates操作以events的形式写入到binary log中。根据database记录的变更binary log中的信息将会以不同的logging format存储。replicas可以被配置从source的binary log读取events并且在replica本地的binary log中执行时间。
每个replica都会接收binary log的完整副本内容并由replica来决定binary log中哪些statements应当被执行。除非你显式指定否则binary log中所有的events都会在replica上被执行。如果需要你可以对replica进行配置仅处理应用于特定databases、tables的events。
每个replica都会记录二进制日志的坐标其已经从source读取并处理的file name和文件中的position。这代表多个replicas可以连接到source并执行相同binary log的不同部分。由于该过程受replicas控制故而独立的replicas可以和source建立连接、取消连接并且不会影响到source的操作。并且每个replica记录了binary log的当前positionreplica可以断开连接、重新连接并重启之前的过程。
source和每个replica都配置了unique ID使用`server_id` system variable除此之外每个replica必须配置如下信息
- source's host name
- source's log file name
- position of source's file
这些可以在replica的mysql session内通过`CHANGE REPLICATION SOURCE TO`语句来管理并且这些信息存储在replica的connection metadata repository中。
### Replication with Global Transaction Identifiers
在该章节中描述了使用global transaction identifiersGTIDs的transaction-based replication。当使用GTIDs时每个在source上提交的事务都可以被标识和追踪并可以被任一replica应用。
在使用GTIDs时启动新replica或failover to a new source时并不需要参照log files或file position。由于GTIDs时完全transaction-based的其更容易判断是否source和replicas一致只要在source中提交的事务全部也都在replica上提交那么source和replica则是保证一致的。
在使用GTIDs时可以选择是statement-based的还是row-based的推荐使用row-based format。
GTIDs在source和replica都是持久化的。总是可以通过检测binary log来判断是否source中的任一事务是否在replica上被应用。此外一旦给定GTID的事务在给定server上被提交那么后续任一事务如果拥有相同的GTID其都会被server忽略。因此在source上被提交的事务可以在replica上应用多次冗余的应用会被忽略其可以保证数据的一致性。
#### GTID Format and Storage
一个global transation identifierGTID是一个唯一标识符该标识符的创建并和`source上提交的每一个事务`进行关联。GTID不仅在创建其的server上是唯一的并且在给定replication拓扑的所有servers间是唯一的。
GTID的分配区分了client trasactions在source上提交的事务以及replicated transactions在replica上reproduced的事务。当client transaction在source上提交时如果transaction被写入到binary log中其会被分配一个新的GTID。client transactions将会被保证单调递增并且生成的编号之间不会存在间隙。`如果client transaction没有被写入到binary log那么其在source中并不会被分配GTID`
Replicated transaction将会使用`事务被source server分配的GTID`。在replicated transaction开始执行前GTID就已经存在并且即使replicated transaction没有写入到replica的binary log中或是被replica过滤该GTID也会被持久化。`mysql.gtid_executed` system table将会被用于保存`应用到给定server的所有事务的GTID但是已经存储到当前active binary log file的除外`
> `active binary log file`中的gtid最终也会被写入到`gtid_executed`表中但是对于不同的存储引擎写入时机可能不同。对于innodb而言在事务提交时就会写入gtid_executed表而对于其他存储引擎而言则会在binary log发生rotation/server shutdown后才会将active binary log中尚未写入的GTIDs写入到`mysql.gtid_executed`表
GTIDs的auto-skip功能代表一个在source上提交的事务最多只能在replica上应用一次这有助于保证一致性。一旦拥有给定GTID的事务在给定server上被提交任何后续执行的事务如果拥有相同的GTID都会被server忽略。并不会抛出异常并且事务中的statements都不会被实际执行。
如果拥有给定GTID的事务在server上已经开始执行但是尚未提交或回滚那么任何尝试开启一个`拥有相同GTID事务的操作都会被阻塞`。该server并不会开始执行拥有相同GTID的事务也不会将控制权交还给client。一旦第一个事务提交或回滚那么被阻塞的事务就可以开始执行。如果第一次执行被回滚那么被阻塞事务可以实际执行如果第一个事务成功提交那么被阻塞事务并不会被实际执行而是会被自动跳过。
GTID的表示形式如下
```
GTID = source_id:transaction_id
```
- `source_id`表示了originating server通常为source的`server_uuid`
- `transaction_id`则是一个由`transaction在source上提交顺序`决定的序列号。例如第一个被提交的事务其transaction_id为1而第十个被提交的事务其transaction_id则是为10.
- 在GTID中transaction_id部分的值不可能为0
例如在UUID为`3E11FA47-71CA-11E1-9E33-C80AA9429562`上的server提交的第23个事务其GTID为
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
```
在GTID中序列号的上限值为`2^63 - 1, or 9223372036854775807`(signed 64-bit integer)。如果server运行时超过GTIDs其将执行`binlog_error_action`指定的行为。当server接近该限制时会发出一个warning message。
在mysql 8.4中也支持tagged GTIDs一个tagged GTID由三部分组成通过`:`进行分隔,示例如下所示:
```
GTID = source_id:tag:transaction_id
```
在该示例中,`source_id``transaction_id`的含义和先前相同,`tag`则是一个用户自定义的字符串,用于表示`specific group of transactions`
例如在UUID为`ed102faf-eb00-11eb-8f20-0c5415bfaa1d`的server上第117个提交的tag为`Domain_1`的事务其GTID为
```
ed102faf-eb00-11eb-8f20-0c5415bfaa1d:Domain_1:117
```
事务的GTID会展示在`mysqlbinlog`的输出中,用于分辨`performance schema replication status tables`中独立的事务,例如`replication_applier_status_by_worker`表。
`gtid_next` system variable的值是单个GTID。
##### GTID Sets
一个GTID set由`一个或多个GTIDs``GTIDs`范围构成。`gtid_executed``gtid_purged` system variables存储的就是GTID sets。`START REPLICA`选项`UNTIL SQL_BEFORE_GTIDS``UNTIL SQL_AFTER_GTIDS`可令replica在处理事务时`最多只处理到GTID set中的第一个GTID``在处理完GTID set中的最后一个GTID后停止`。内置的`GTID_SUBSET()``GTID_SUBTRACT()`函数需要`GTID sets`作为输入。
在相同server上的GTIDs范围可以按照如下形式来表示
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
```
上述示例表示在uuid为`3E11FA47-71CA-11E1-9E33-C80AA9429562`的server上提交的顺序为`1~5`之间的事务。
相同server上的多个single GTIDs或ranges of GTIDs可以通过如下形式来表示
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49
```
GTID set可以包含任意`single GTIDs``GTIDs range`的组合也可以包含来自不同server的GTIDs。如下实例中展示了`gtid_executed` system variable中存储的GTID set代表replica中应用了来自超过一个source的事务
```
2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19
```
当GTID set从server variables中返回时UUID按照字母排序返回而序列号间区间则是合并后按照升序排列。
当构建GTID set时用户自定义tag也会被作为UUID的一部分这代表来自相同server且tag相同的多个GTIDs可以被包含在一个表达式中如下所示
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:11:47-49
```
来源相同server但是tags不同的GTIDs表示方式如下
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:15-21, 3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_2:8-52
```
GTID set的完整语法如下所示
```
gtid_set:
uuid_set [, uuid_set] ...
| ''
uuid_set:
uuid:[tag:]interval[:interval]...
uuid:
hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
h:
[0-9|A-F]
tag:
[a-z_][a-z0-9_]{0,31}
interval:
m[-n]
(m >= 1; n > m)
```
##### msyql.gtid_executed
GTIDs被存储在`mysql.gtid_executed`表中该表中的行用于表示GTID或GTID set行包含信息如下
- source server的uuid
- 用户自定义的tag
- starting transaction IDs of the set
- ending transaction IDs of the set
如果行代表single GTID那么最后两个字段的值相同。
`mysql.gtid_executed`表在mysql server安装或升级时会被自动创建其表结构如下
```sql
CREATE TABLE gtid_executed (
source_uuid CHAR(36) NOT NULL,
interval_start BIGINT NOT NULL,
interval_end BIGINT NOT NULL,
gtid_tag CHAR(32) NOT NULL,
PRIMARY KEY (source_uuid, gtid_tag, interval_start)
);
```
`mysql.gtid_executed`用于mysql server内部使用其允许replica在binary logging被禁用的情况下使用GTIDs其也可以在binary logs丢失时保留GTID的状态。如果执行`RESET BINARY LOGS AND GTIDS`,那么`mysql.gtid_executed`表将会被清空。
只有当`gtid_mode`被设置为`ON``ON_PERMISSIVE`GTIDs才会被存储到mysql.gtid_executed中。如果binary logging被禁用或者`log_replica_updates`被禁用server在事务提交时将会把属于个各事务的GTID和事务一起存储到buffer中并且background threads将会周期性将buffer中的内容以entries的形式添加到`mysql.gtid_executed`表中。
对于innodb存储引擎而言如果binary logging被启用server更新`mysql.gtid_executed`表的方式将会和`binary logging或replica update logging被启用`时一样都会在每个事务提交时存储GTID。对于其他存储引擎则是在binary log rotation发生时或server shut down时更新`mysql.gtid_executed`表的内容。
如果mysql.gtid_executed表无法被写访问并且binary log file因`reaching the maximum file size`之外的任何理由被rotated那么current binary log file将仍被使用。并且当client发起rotation时将会返回错误信息并且server将会输出warning日志。如果`mysql.gtid_executed`无法被写访问并且binary log单个文件大小达到`max_binlog_size`那么server将会根据`binlog_error_action`设置来执行操作。如果`IGNORE_ERROR`被设置那么server将会输出error到日志并且binary logging将会被停止如果`ABORT_SERVER`被设置那么server将会shutdown。
> 因为写入到active binary log的GTIDs最终也要被写入`mysql.gtid_executed`表,但是该表若当前不可写访问,那么此时将无法触发`binary log rotation`。
>
> MySQL.gtid_executed中缺失的GTIDs必须包含在active binary log file中如果log file触发rotation但是无法向mysql.gtid_executed中写入数据那么rotation是不被允许的。
##### mysql.gtid_executed table compression
随着时间的推移mysql.gtid_executed表会填满大量行行数据代表独立的GTID示例如下
```
+--------------------------------------+----------------+--------------+----------+
| source_uuid | interval_start | interval_end | gtid_tag |
|--------------------------------------+----------------+--------------|----------+
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 31 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 32 | 32 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 33 | 33 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 34 | 34 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 35 | 35 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 36 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 44 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 45 | 45 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 46 | 46 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 47 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 48 | 48 | Domain_1 |
```
为了节省空间可以对gtid_executed表周期性的进行压缩将多个`single GTID`替换为单个`GTID set`,压缩后的数据如下;
```
+--------------------------------------+----------------+--------------+----------+
| source_uuid | interval_start | interval_end | gtid_tag |
|--------------------------------------+----------------+--------------|----------+
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 35 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 39 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 43 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 46 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 48 | Domain_1 |
...
```
server可通过名为`thread/sql/compress_gtid_table`的前台线程来执行gtid_executed表的压缩操作该线程并不会在`show processlist`的输出中被列出,但是可以通过查询`threads`表来查看,示例如下:
```sql
mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
THREAD_ID: 26
NAME: thread/sql/compress_gtid_table
TYPE: FOREGROUND
PROCESSLIST_ID: 1
PROCESSLIST_USER: NULL
PROCESSLIST_HOST: NULL
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
PROCESSLIST_TIME: 1509
PROCESSLIST_STATE: Suspending
PROCESSLIST_INFO: NULL
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: NULL
THREAD_OS_ID: 18677
```
当server启用了binary log时上述压缩方式并不会被使用`mysql.gtid_executed`在每次binary log rotation时会被压缩。但是当binary logging被禁用时`thread/sql/compress_gtid`线程处于sleep状态每有一定数量的事务被执行时线程会wake up并执行压缩操作。在table被压缩之前经过的事务数量即压缩率可由system variable `gtid_executed_compression_period`来进行控制。如果将值设置为0代表该thread永远不会wake up。
相比于其他存储引擎innodb事务写入`mysql.gtid_executed`的过程有所不同在innodb存储引擎中过程通过线程`innodb/clone_gtid_thread`来执行。此GTID持久化线程将会分组收集GTIDs并将其刷新到`mysql.gtid_executed`表中并对table进行压缩。如果server中既包含innodb事务又包含non-innodb事务那么由`compress_gtid_table`线程执行的压缩操作将会干扰`clone_gtid_thread`的工作,并对其效率进行显著下降。为此,在此场景下更推荐将`gtid_executed_compression_period`设置为0故而`compress_gtid_table`线程将永远不被激活。
`gtid_executed_compression_period`的默认值为0且所有事务不论其所属存储引擎都会被`clone_gitid_thread`写入到`mysql.gtid_exec uted`中。
当server实例启动后如果`gtid_executed_compression_preiod`被设置为非0值并且`compress_gtid_table`线程也启动在多数server配置中将会对`msyql.gtid_executed`表执行显式压缩。压缩通过线程的启动触发。
#### GTID生命周期
GTID生命周期由如下步骤组成
1. 事务在source上被执行并提交client transaction将会被分配GTIDGTID由`source UUID``smallest nonzero transaction sequence number not yet used on this server`组成。GTID也会被写入到binary log中在log中GTID被写入在事务之前。如果client transaction没有被写入binary log例如事务被过滤或事务为read-only那么该事务不会被分配GTID
2. 如果为事务分配了GTID那么在事务提交时GTID会被原子的持久化。在binary log中GTID会被写入到事务开始的位置。不管何时如果binary log发生rotation或server shutdownserver会将所有写入到binary log file中事务的GTIDs写入到`mysql.gtid_executed`表中
3. 如果为事务分配了GTID那么GTID的外部化是`non-atomically`在事务提交后非常短的时间。外部化过程会将GTID添加到`gtid_executed` system variable所代表的GTID set中`@@GLOBAL.gtid_executed`。该GTID set中包含`representation of the set of all committed GTID transactions`并且在replication中会作为代表server state的token使用。
1. 当binary logging启用时`gtid_executed` system variable代表的GTIDs set是已被应用事务的完整记录。但是`mysql.gtid_executed`表则并记录所有GTIDs可能由部分GTIDs仍存在于active binary log file中尚未被写入到gtid_executed table
4. 在binary log data被转移到replica并存储在replica的relay log中后replica会读取GTID的值并且将其设置到`gtid_next` system variable中。这告知replica下一个事务将会使用`gtid_next`所代表的GTID。replica在session context中设置`gtid_next`
5. replica在处理事务前会验证没有其他线程已经持有了`gtid_next`中GTID的所有权。通过该方法replica可以保证该该GTID关联的事务在replica上没有被应用过并且还能保证没有其他session已经读取该GTID但是尚未提交关联事务。故而如果并发的尝试应用相同的事务那么server只会让其中的一个运行。
1. replica的`gtid_owned` system variable`@@GLOBAL.gtid_owned`代表了当前正在使用的每个GTID和拥有该GTID的线程ID。如果GTID已经被使用过并不会抛出错误`auto-skip`功能会忽略该事务
6. 如果GTID尚未被使用那么replica将会对replicated transaction进行应用。因为`gtid_next`被设置为了`由source分配的GTID`replica将不会尝试为事务分配新的GTID而是直接使用存储在`gtid_next`中存储的GTID
7. 如果replica上启用了binary loggingGTID将会在提交时被原子的持久化写入到binary log中事务开始的位置。无论何时如果binary log发生rotation或server shutdownserver会将`先前写入到binary log中事务的GTIDs`写入到`mysql.gtid_executed`表中
8. 如果replica禁用binary logGTID将会原子的被持久化直接被写入到`mysql.gtid_executed`表中。mysql会向事务中追加一个statement并且将GTID插入到table中该操作是原子的在该场景下`mysql.gtid_executed`表记录了完整的`transactions applied on the replica`
9. 在replicated transaction提交后很短的时间GTID会被非原子的externalizedGTID会被添加到replica的`gtid_executed` system variable代表的GTIDs中。在source中`gtid_executed` system variable包含了所有提交的GTID事务。
在source上被完全过滤的client transactions并不会被分配GTID因此其不会被添加到`gtid_executed` system variable或被添加到`mysql.gtid_executed`表中。然而replicated transaction中的GTIDs即使在replica被完全过滤也会被持久化。
- 如果binary logging在replica开启被过滤的事务将会作为gtid_log_event的形式被写入到binary log后续跟着一个empty transaction事务中仅包含BEGIN和COMMIT语句
- 如果binary logging在replica被禁用给过滤事务的GTID将会被写入到`mysql.gtid_executed`
将filtered-out transactions的GTIDs保留可以确保`mysql.gtid_executed`表和`gtid_executed`system variable中的GTIDs可以被压缩。同时其也能确保replica重新连接到source时filtered-out transactions不会被重新获取
在多线程的replica上`replica_parallel_workers > 0`事务可以被并行的应用故而replicated transactions可以以不同的顺序来提交除非`replica_preserve_commit_order = 1`)。在并行提交时,`gtid_executed`system variable中的GTIDs将包含多个GTID ranges多个范围之间存在空隙。在多线程replicas上只有最近被应用的事务间才会存在空隙并且会随着replication的进行而被填充。当replication threads通过`STOP REPLICA`停止时这些空隙会被填补。当发生shutdown event时server failure导致此时replication 停止,空隙仍可能保留。
##### What changes are assign a GTID
GTID生成的典型场景为`server为提交事务生成新的GTID`。然而GTIDs可以被分配给除事务外的其他修改且在某些场景下一个事务可以被分配多个GTIDs。
每个写入binary log的数据库修改(`DDL/DML`)都会被分配一个GTID。其包含了自动提交的变更、使用`BEGIN...COMMIT`的变更和使用`START TRANSACTION`的变更。一个GTID会被分配给数据库的`creation, alteration, deletion`操作,并且`non-table` database object例如`procedure, function, trigger, event, view, user, role, grant``creation, alteration, deletion`也会被分配GTID。
非事务更新和事务更新都会被分配`GTID`额外的对于非事务更新如果在尝试写入binary log cahche时发生disk write failure导致binary log中出现间隙那么生成的日志事件将会被分配一个GTID。
## Replication Implementation
在replication的设计中source server会追踪其binary log中所有对databases的修改。binary log中记录了自server启动时所有修改数据库结构或内容的事件。通常`SELECT`语句将不会被记录,其并没有对数据库结构或内容造成修改。
每个连接到source的replica都会请求binary log的副本replica会从source处拉取数据而不是source向replica推送数据。replica也会执行其从binary log收到的事件。`发生在source上的修改将会在replica上进行重现`。在重现过程中,会发生表创建、表结构修改、数据的新增/删除/修改等操作。
由于每个replica都是独立的每个连接到source的replica其对`source中binary log`内容的replaying都是独立的。此外因为每个replica只通过请求source来接收binary log的拷贝replica能够以其自身的节奏来读取和更新其数据副本并且其能在不对source和其他replicas造成影响的条件下开启或停止replication过程。
### Replication Formats
在binary log中events根据其事件类型以不同的格式被记录。replication使用的foramts取决于事件被记录到source server的binary log中时所使用的foramt。binary log format和replciation过程中使用到的format其关联关系如下
- 当使用statement-based binary logging时source会将sql statements写入到binary log。从source到replica的replication将会在replica上执行该sql语句。这被称之为statement-based replication关联了mysql statement-based binary logging format
- 当使用row-based loggging时source将会将`table rows如何变更`的事件写入到binary log中。在replica中将会重现events对table rows所做的修改。则会被成为row-based replication
- `row-based logging`是默认的方法
- 可以配置mysql使用`mix-format logging`,但使用`mix-format logging`将默认使用statement-based log。但是对于特定的语句也取决于使用的存储引擎在某些床惊吓log也会被自动切换到row-based。使用mixed format的replication被称之为mix-based replication或mix-format replication
mysql server中的logging format通过`binlog_format` system variable来进行控制。该变量可以在session/global级别进行设置
- 在将variable设置为session级别时将仅会对当前session生效并且仅持续到当前session结束
- 将variable设置为global级别时该设置将会对`clients that connect after the change`生效,`但对于any current client sessions包括.发出该修改请求的session都不生效`
#### 使用statement-based和row-based replication的优缺点对比
每个binary logging format都有优缺点。对大多数usersmixed replication format能够提供性能和数据完整性的最佳组合。
##### Advantages of statement-based replication
- 在使用statement-based格式时会向log files中写入更少的数据。特别是在更新或删除大量行时使用statement-based格式能够导致更少的空间占用。在从备份中获取和恢复时其速度也更快
- log files包含所有造成修改的statements可以被用于审核database
##### Disadvantages of statement-based replication
- 对于Statement-based replication而言statements并不是安全的。并非所有对数据造成修改的statements都可以使用statement-based replication。在使用statement-based replication时任何非确定性的行为都难以复制例如在语句中包含随机函数等非确定性行为
- 对于复杂语句statement必须在实际对目标行执行修改前重新计算。而当使用row-based replication时replica可以直接修改受影响行而无需重新计算
##### Advantages of row-based replication
- 所有的修改可以被replicated其是最安全的replication形式
##### disadvantages of row-based replication
- 相比于statement-based replicationrow-based replication通常会向log中ieur更多数据特别是当statement操作大量行数据是
- 在replica并无法查看从source接收并执行的statements。可以通过`mysqlbinlog``--base64-output=DECODE-ROWS``--verbose`选项来查看数据变更
### Replay Log and Replication Metadata Repositories
replica server会创建一系列仓库其中存储在replication过程中使用的信息
- `relay log`: 该日志被replication I/O线程写入包含从source server的binary log读取的事务。relay log中记录的事务将会被replication SQL thread应用到replica
- `connection metadata repository`: 包含replication receiver thread连接到source server并从binary log获取事务时需要的信息。connection metadata repository会被写入到`mysql.slave_master_info`
- `applier metadata repository`: 包含replication applier thread从relay log读取并应用事务时所需要的信息。applier metadata repository会被写入到`mysql.slave_relay_log_info`表中
#### The Relay Log
relay log和binary log类似由一些`numbered files`构成,文件中包含`描述数据库变更的事件`。relay log还包含一个index file其中记录了所有被使用的relay log files的名称。默认情况下relay log files位于data directory中
relay log files拥有和binary log相同的格式也可以通过mysqlbinlog进行读取。如果使用了binary log transaction compression那么写入relay log的事务payloads也会按照和binary log相同的方式被压缩。
对于默认的replication channelrelay log file命名形式如下`host_name-relay-bin.nnnnnn`:
- `host_name`为replica server host的名称
- `nnnnnn`是序列号序列号从000001开始
对于非默认的replication channels默认的名称为`host_name-relay-bin-channel`
- `channel`为replciation channel的名称
replica会使用index file来追踪当前在使用的relay log files。默认relay log index file的名称为`host_name-relay-bin.index`
当如下条件下replica server将会创建新的relay log file
- 每次replication I/O thread开启时
- 当logs被刷新时例如当执行`FLUSH LOGS`命令时)
- 当当前relay log file的大小太大时大小上限可以通过如下方式决定
- 如果`max_relay_log_size`的大小大于0那么其就是relay log file的最大大小
- 如果`max_relay_log_size`为0那么relay log file的最大大小由`max_binlog_size`决定
replication sql thread在其执行完文件中所有events之后都不再需要该文件被执行完的relay log file都会被自动删除。目前没有显式的机制来删除relay logsrelay log的删除由replciation sql thread来处理。但是`FLUSH LOGS`可以针对relay logs进行rotation。
- [Replication](#replication)
- [Configuration Replication](#configuration-replication)
- [binary log file position based replication configuration overview](#binary-log-file-position-based-replication-configuration-overview)
- [Replication with Global Transaction Identifiers](#replication-with-global-transaction-identifiers)
- [GTID Format and Storage](#gtid-format-and-storage)
- [GTID Sets](#gtid-sets)
- [msyql.gtid\_executed](#msyqlgtid_executed)
- [mysql.gtid\_executed table compression](#mysqlgtid_executed-table-compression)
- [GTID生命周期](#gtid生命周期)
- [What changes are assign a GTID](#what-changes-are-assign-a-gtid)
- [Replication Implementation](#replication-implementation)
- [Replication Formats](#replication-formats)
- [使用statement-based和row-based replication的优缺点对比](#使用statement-based和row-based-replication的优缺点对比)
- [Advantages of statement-based replication](#advantages-of-statement-based-replication)
- [Disadvantages of statement-based replication](#disadvantages-of-statement-based-replication)
- [Advantages of row-based replication](#advantages-of-row-based-replication)
- [disadvantages of row-based replication](#disadvantages-of-row-based-replication)
- [Replay Log and Replication Metadata Repositories](#replay-log-and-replication-metadata-repositories)
- [The Relay Log](#the-relay-log)
# Replication
Replication允许数据从一个database server被复制到一个或多个mysql database serversreplicas。replication默认是异步的replicas并无需永久连接即可接收来自source的更新。基于configuration可以针对所有的databases、选定的databases、甚至database中指定的tables进行replicate。
mysql中replication包含如下优点
- 可拓展方案可以将负载分散到多个replicas从而提升性能。在该环境中所有的writes和updates都发生在source server。但是reads则是可以发生在一个或多个replicas上。通过该模型能够提升writes的性能因为source专门用于更新并可以通过增加replicas的数量来提高read speed
- data security 因为replica可以暂停replication过程故而可以在replica中运行备份服务而不会破坏对应的source data
- analytics实时数据可以在source中生成而对信息的分析则可以发生在replica分析过程不会影响source的性能
- long-distance data distribution可以replication来对remote site创建一个local copy而无需对source的永久访问
在mysql 8.4中支持不同的replication方式。
- 传统方式基于从source的binary log中复制事件并且需要log files和`在source和replica之间进行同步的position`
- 新的replication方式基于global transaction identifiersGTIDs是事务的并且不需要和log files、position进行交互其大大简化了许多通用的replication任务。使用GTIDs的replication保证了source和replica的一致性所有在source提交的事务都会在replica被应用。
mysql中的replication支持不同类型的同步。
- 原始的同步是单向、异步的复制一个server作为source、其他一个或多个servers作为replicas。
- NDB Cluster中支持synchronous replication
- 在mysql 8.4中支持了半同步复制通过半同步复制在source中执行的提交将会被阻塞直到至少一个replica收到transaction并对transaction进行log event且向source发送ack
- mysql 8.4同样支持delayed replication使得replica故意落后于source至少指定的时间
## Configuration Replication
### binary log file position based replication configuration overview
在该section中会描述mysql servers间基于binary log file position的replication方案。source会将database的writes和updates操作以events的形式写入到binary log中。根据database记录的变更binary log中的信息将会以不同的logging format存储。replicas可以被配置从source的binary log读取events并且在replica本地的binary log中执行时间。
每个replica都会接收binary log的完整副本内容并由replica来决定binary log中哪些statements应当被执行。除非你显式指定否则binary log中所有的events都会在replica上被执行。如果需要你可以对replica进行配置仅处理应用于特定databases、tables的events。
每个replica都会记录二进制日志的坐标其已经从source读取并处理的file name和文件中的position。这代表多个replicas可以连接到source并执行相同binary log的不同部分。由于该过程受replicas控制故而独立的replicas可以和source建立连接、取消连接并且不会影响到source的操作。并且每个replica记录了binary log的当前positionreplica可以断开连接、重新连接并重启之前的过程。
source和每个replica都配置了unique ID使用`server_id` system variable除此之外每个replica必须配置如下信息
- source's host name
- source's log file name
- position of source's file
这些可以在replica的mysql session内通过`CHANGE REPLICATION SOURCE TO`语句来管理并且这些信息存储在replica的connection metadata repository中。
### Replication with Global Transaction Identifiers
在该章节中描述了使用global transaction identifiersGTIDs的transaction-based replication。当使用GTIDs时每个在source上提交的事务都可以被标识和追踪并可以被任一replica应用。
在使用GTIDs时启动新replica或failover to a new source时并不需要参照log files或file position。由于GTIDs时完全transaction-based的其更容易判断是否source和replicas一致只要在source中提交的事务全部也都在replica上提交那么source和replica则是保证一致的。
在使用GTIDs时可以选择是statement-based的还是row-based的推荐使用row-based format。
GTIDs在source和replica都是持久化的。总是可以通过检测binary log来判断是否source中的任一事务是否在replica上被应用。此外一旦给定GTID的事务在给定server上被提交那么后续任一事务如果拥有相同的GTID其都会被server忽略。因此在source上被提交的事务可以在replica上应用多次冗余的应用会被忽略其可以保证数据的一致性。
#### GTID Format and Storage
一个global transation identifierGTID是一个唯一标识符该标识符的创建并和`source上提交的每一个事务`进行关联。GTID不仅在创建其的server上是唯一的并且在给定replication拓扑的所有servers间是唯一的。
GTID的分配区分了client trasactions在source上提交的事务以及replicated transactions在replica上reproduced的事务。当client transaction在source上提交时如果transaction被写入到binary log中其会被分配一个新的GTID。client transactions将会被保证单调递增并且生成的编号之间不会存在间隙。`如果client transaction没有被写入到binary log那么其在source中并不会被分配GTID`
Replicated transaction将会使用`事务被source server分配的GTID`。在replicated transaction开始执行前GTID就已经存在并且即使replicated transaction没有写入到replica的binary log中或是被replica过滤该GTID也会被持久化。`mysql.gtid_executed` system table将会被用于保存`应用到给定server的所有事务的GTID但是已经存储到当前active binary log file的除外`
> `active binary log file`中的gtid最终也会被写入到`gtid_executed`表中但是对于不同的存储引擎写入时机可能不同。对于innodb而言在事务提交时就会写入gtid_executed表而对于其他存储引擎而言则会在binary log发生rotation/server shutdown后才会将active binary log中尚未写入的GTIDs写入到`mysql.gtid_executed`表
GTIDs的auto-skip功能代表一个在source上提交的事务最多只能在replica上应用一次这有助于保证一致性。一旦拥有给定GTID的事务在给定server上被提交任何后续执行的事务如果拥有相同的GTID都会被server忽略。并不会抛出异常并且事务中的statements都不会被实际执行。
如果拥有给定GTID的事务在server上已经开始执行但是尚未提交或回滚那么任何尝试开启一个`拥有相同GTID事务的操作都会被阻塞`。该server并不会开始执行拥有相同GTID的事务也不会将控制权交还给client。一旦第一个事务提交或回滚那么被阻塞的事务就可以开始执行。如果第一次执行被回滚那么被阻塞事务可以实际执行如果第一个事务成功提交那么被阻塞事务并不会被实际执行而是会被自动跳过。
GTID的表示形式如下
```
GTID = source_id:transaction_id
```
- `source_id`表示了originating server通常为source的`server_uuid`
- `transaction_id`则是一个由`transaction在source上提交顺序`决定的序列号。例如第一个被提交的事务其transaction_id为1而第十个被提交的事务其transaction_id则是为10.
- 在GTID中transaction_id部分的值不可能为0
例如在UUID为`3E11FA47-71CA-11E1-9E33-C80AA9429562`上的server提交的第23个事务其GTID为
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
```
在GTID中序列号的上限值为`2^63 - 1, or 9223372036854775807`(signed 64-bit integer)。如果server运行时超过GTIDs其将执行`binlog_error_action`指定的行为。当server接近该限制时会发出一个warning message。
在mysql 8.4中也支持tagged GTIDs一个tagged GTID由三部分组成通过`:`进行分隔,示例如下所示:
```
GTID = source_id:tag:transaction_id
```
在该示例中,`source_id``transaction_id`的含义和先前相同,`tag`则是一个用户自定义的字符串,用于表示`specific group of transactions`
例如在UUID为`ed102faf-eb00-11eb-8f20-0c5415bfaa1d`的server上第117个提交的tag为`Domain_1`的事务其GTID为
```
ed102faf-eb00-11eb-8f20-0c5415bfaa1d:Domain_1:117
```
事务的GTID会展示在`mysqlbinlog`的输出中,用于分辨`performance schema replication status tables`中独立的事务,例如`replication_applier_status_by_worker`表。
`gtid_next` system variable的值是单个GTID。
##### GTID Sets
一个GTID set由`一个或多个GTIDs``GTIDs`范围构成。`gtid_executed``gtid_purged` system variables存储的就是GTID sets。`START REPLICA`选项`UNTIL SQL_BEFORE_GTIDS``UNTIL SQL_AFTER_GTIDS`可令replica在处理事务时`最多只处理到GTID set中的第一个GTID``在处理完GTID set中的最后一个GTID后停止`。内置的`GTID_SUBSET()``GTID_SUBTRACT()`函数需要`GTID sets`作为输入。
在相同server上的GTIDs范围可以按照如下形式来表示
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
```
上述示例表示在uuid为`3E11FA47-71CA-11E1-9E33-C80AA9429562`的server上提交的顺序为`1~5`之间的事务。
相同server上的多个single GTIDs或ranges of GTIDs可以通过如下形式来表示
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49
```
GTID set可以包含任意`single GTIDs``GTIDs range`的组合也可以包含来自不同server的GTIDs。如下实例中展示了`gtid_executed` system variable中存储的GTID set代表replica中应用了来自超过一个source的事务
```
2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19
```
当GTID set从server variables中返回时UUID按照字母排序返回而序列号间区间则是合并后按照升序排列。
当构建GTID set时用户自定义tag也会被作为UUID的一部分这代表来自相同server且tag相同的多个GTIDs可以被包含在一个表达式中如下所示
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:11:47-49
```
来源相同server但是tags不同的GTIDs表示方式如下
```
3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:15-21, 3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_2:8-52
```
GTID set的完整语法如下所示
```
gtid_set:
uuid_set [, uuid_set] ...
| ''
uuid_set:
uuid:[tag:]interval[:interval]...
uuid:
hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
h:
[0-9|A-F]
tag:
[a-z_][a-z0-9_]{0,31}
interval:
m[-n]
(m >= 1; n > m)
```
##### msyql.gtid_executed
GTIDs被存储在`mysql.gtid_executed`表中该表中的行用于表示GTID或GTID set行包含信息如下
- source server的uuid
- 用户自定义的tag
- starting transaction IDs of the set
- ending transaction IDs of the set
如果行代表single GTID那么最后两个字段的值相同。
`mysql.gtid_executed`表在mysql server安装或升级时会被自动创建其表结构如下
```sql
CREATE TABLE gtid_executed (
source_uuid CHAR(36) NOT NULL,
interval_start BIGINT NOT NULL,
interval_end BIGINT NOT NULL,
gtid_tag CHAR(32) NOT NULL,
PRIMARY KEY (source_uuid, gtid_tag, interval_start)
);
```
`mysql.gtid_executed`用于mysql server内部使用其允许replica在binary logging被禁用的情况下使用GTIDs其也可以在binary logs丢失时保留GTID的状态。如果执行`RESET BINARY LOGS AND GTIDS`,那么`mysql.gtid_executed`表将会被清空。
只有当`gtid_mode`被设置为`ON``ON_PERMISSIVE`GTIDs才会被存储到mysql.gtid_executed中。如果binary logging被禁用或者`log_replica_updates`被禁用server在事务提交时将会把属于个各事务的GTID和事务一起存储到buffer中并且background threads将会周期性将buffer中的内容以entries的形式添加到`mysql.gtid_executed`表中。
对于innodb存储引擎而言如果binary logging被启用server更新`mysql.gtid_executed`表的方式将会和`binary logging或replica update logging被启用`时一样都会在每个事务提交时存储GTID。对于其他存储引擎则是在binary log rotation发生时或server shut down时更新`mysql.gtid_executed`表的内容。
如果mysql.gtid_executed表无法被写访问并且binary log file因`reaching the maximum file size`之外的任何理由被rotated那么current binary log file将仍被使用。并且当client发起rotation时将会返回错误信息并且server将会输出warning日志。如果`mysql.gtid_executed`无法被写访问并且binary log单个文件大小达到`max_binlog_size`那么server将会根据`binlog_error_action`设置来执行操作。如果`IGNORE_ERROR`被设置那么server将会输出error到日志并且binary logging将会被停止如果`ABORT_SERVER`被设置那么server将会shutdown。
> 因为写入到active binary log的GTIDs最终也要被写入`mysql.gtid_executed`表,但是该表若当前不可写访问,那么此时将无法触发`binary log rotation`。
>
> MySQL.gtid_executed中缺失的GTIDs必须包含在active binary log file中如果log file触发rotation但是无法向mysql.gtid_executed中写入数据那么rotation是不被允许的。
##### mysql.gtid_executed table compression
随着时间的推移mysql.gtid_executed表会填满大量行行数据代表独立的GTID示例如下
```
+--------------------------------------+----------------+--------------+----------+
| source_uuid | interval_start | interval_end | gtid_tag |
|--------------------------------------+----------------+--------------|----------+
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 31 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 32 | 32 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 33 | 33 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 34 | 34 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 35 | 35 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 36 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 44 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 45 | 45 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 46 | 46 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 47 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 48 | 48 | Domain_1 |
```
为了节省空间可以对gtid_executed表周期性的进行压缩将多个`single GTID`替换为单个`GTID set`,压缩后的数据如下;
```
+--------------------------------------+----------------+--------------+----------+
| source_uuid | interval_start | interval_end | gtid_tag |
|--------------------------------------+----------------+--------------|----------+
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 35 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 39 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 43 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 46 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 48 | Domain_1 |
...
```
server可通过名为`thread/sql/compress_gtid_table`的前台线程来执行gtid_executed表的压缩操作该线程并不会在`show processlist`的输出中被列出,但是可以通过查询`threads`表来查看,示例如下:
```sql
mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
THREAD_ID: 26
NAME: thread/sql/compress_gtid_table
TYPE: FOREGROUND
PROCESSLIST_ID: 1
PROCESSLIST_USER: NULL
PROCESSLIST_HOST: NULL
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
PROCESSLIST_TIME: 1509
PROCESSLIST_STATE: Suspending
PROCESSLIST_INFO: NULL
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: NULL
THREAD_OS_ID: 18677
```
当server启用了binary log时上述压缩方式并不会被使用`mysql.gtid_executed`在每次binary log rotation时会被压缩。但是当binary logging被禁用时`thread/sql/compress_gtid`线程处于sleep状态每有一定数量的事务被执行时线程会wake up并执行压缩操作。在table被压缩之前经过的事务数量即压缩率可由system variable `gtid_executed_compression_period`来进行控制。如果将值设置为0代表该thread永远不会wake up。
相比于其他存储引擎innodb事务写入`mysql.gtid_executed`的过程有所不同在innodb存储引擎中过程通过线程`innodb/clone_gtid_thread`来执行。此GTID持久化线程将会分组收集GTIDs并将其刷新到`mysql.gtid_executed`表中并对table进行压缩。如果server中既包含innodb事务又包含non-innodb事务那么由`compress_gtid_table`线程执行的压缩操作将会干扰`clone_gtid_thread`的工作,并对其效率进行显著下降。为此,在此场景下更推荐将`gtid_executed_compression_period`设置为0故而`compress_gtid_table`线程将永远不被激活。
`gtid_executed_compression_period`的默认值为0且所有事务不论其所属存储引擎都会被`clone_gitid_thread`写入到`mysql.gtid_exec uted`中。
当server实例启动后如果`gtid_executed_compression_preiod`被设置为非0值并且`compress_gtid_table`线程也启动在多数server配置中将会对`msyql.gtid_executed`表执行显式压缩。压缩通过线程的启动触发。
#### GTID生命周期
GTID生命周期由如下步骤组成
1. 事务在source上被执行并提交client transaction将会被分配GTIDGTID由`source UUID``smallest nonzero transaction sequence number not yet used on this server`组成。GTID也会被写入到binary log中在log中GTID被写入在事务之前。如果client transaction没有被写入binary log例如事务被过滤或事务为read-only那么该事务不会被分配GTID
2. 如果为事务分配了GTID那么在事务提交时GTID会被原子的持久化。在binary log中GTID会被写入到事务开始的位置。不管何时如果binary log发生rotation或server shutdownserver会将所有写入到binary log file中事务的GTIDs写入到`mysql.gtid_executed`表中
3. 如果为事务分配了GTID那么GTID的外部化是`non-atomically`在事务提交后非常短的时间。外部化过程会将GTID添加到`gtid_executed` system variable所代表的GTID set中`@@GLOBAL.gtid_executed`。该GTID set中包含`representation of the set of all committed GTID transactions`并且在replication中会作为代表server state的token使用。
1. 当binary logging启用时`gtid_executed` system variable代表的GTIDs set是已被应用事务的完整记录。但是`mysql.gtid_executed`表则并记录所有GTIDs可能由部分GTIDs仍存在于active binary log file中尚未被写入到gtid_executed table
4. 在binary log data被转移到replica并存储在replica的relay log中后replica会读取GTID的值并且将其设置到`gtid_next` system variable中。这告知replica下一个事务将会使用`gtid_next`所代表的GTID。replica在session context中设置`gtid_next`
5. replica在处理事务前会验证没有其他线程已经持有了`gtid_next`中GTID的所有权。通过该方法replica可以保证该该GTID关联的事务在replica上没有被应用过并且还能保证没有其他session已经读取该GTID但是尚未提交关联事务。故而如果并发的尝试应用相同的事务那么server只会让其中的一个运行。
1. replica的`gtid_owned` system variable`@@GLOBAL.gtid_owned`代表了当前正在使用的每个GTID和拥有该GTID的线程ID。如果GTID已经被使用过并不会抛出错误`auto-skip`功能会忽略该事务
6. 如果GTID尚未被使用那么replica将会对replicated transaction进行应用。因为`gtid_next`被设置为了`由source分配的GTID`replica将不会尝试为事务分配新的GTID而是直接使用存储在`gtid_next`中存储的GTID
7. 如果replica上启用了binary loggingGTID将会在提交时被原子的持久化写入到binary log中事务开始的位置。无论何时如果binary log发生rotation或server shutdownserver会将`先前写入到binary log中事务的GTIDs`写入到`mysql.gtid_executed`表中
8. 如果replica禁用binary logGTID将会原子的被持久化直接被写入到`mysql.gtid_executed`表中。mysql会向事务中追加一个statement并且将GTID插入到table中该操作是原子的在该场景下`mysql.gtid_executed`表记录了完整的`transactions applied on the replica`
9. 在replicated transaction提交后很短的时间GTID会被非原子的externalizedGTID会被添加到replica的`gtid_executed` system variable代表的GTIDs中。在source中`gtid_executed` system variable包含了所有提交的GTID事务。
在source上被完全过滤的client transactions并不会被分配GTID因此其不会被添加到`gtid_executed` system variable或被添加到`mysql.gtid_executed`表中。然而replicated transaction中的GTIDs即使在replica被完全过滤也会被持久化。
- 如果binary logging在replica开启被过滤的事务将会作为gtid_log_event的形式被写入到binary log后续跟着一个empty transaction事务中仅包含BEGIN和COMMIT语句
- 如果binary logging在replica被禁用给过滤事务的GTID将会被写入到`mysql.gtid_executed`
将filtered-out transactions的GTIDs保留可以确保`mysql.gtid_executed`表和`gtid_executed`system variable中的GTIDs可以被压缩。同时其也能确保replica重新连接到source时filtered-out transactions不会被重新获取
在多线程的replica上`replica_parallel_workers > 0`事务可以被并行的应用故而replicated transactions可以以不同的顺序来提交除非`replica_preserve_commit_order = 1`)。在并行提交时,`gtid_executed`system variable中的GTIDs将包含多个GTID ranges多个范围之间存在空隙。在多线程replicas上只有最近被应用的事务间才会存在空隙并且会随着replication的进行而被填充。当replication threads通过`STOP REPLICA`停止时这些空隙会被填补。当发生shutdown event时server failure导致此时replication 停止,空隙仍可能保留。
##### What changes are assign a GTID
GTID生成的典型场景为`server为提交事务生成新的GTID`。然而GTIDs可以被分配给除事务外的其他修改且在某些场景下一个事务可以被分配多个GTIDs。
每个写入binary log的数据库修改(`DDL/DML`)都会被分配一个GTID。其包含了自动提交的变更、使用`BEGIN...COMMIT`的变更和使用`START TRANSACTION`的变更。一个GTID会被分配给数据库的`creation, alteration, deletion`操作,并且`non-table` database object例如`procedure, function, trigger, event, view, user, role, grant``creation, alteration, deletion`也会被分配GTID。
非事务更新和事务更新都会被分配`GTID`额外的对于非事务更新如果在尝试写入binary log cahche时发生disk write failure导致binary log中出现间隙那么生成的日志事件将会被分配一个GTID。
## Replication Implementation
在replication的设计中source server会追踪其binary log中所有对databases的修改。binary log中记录了自server启动时所有修改数据库结构或内容的事件。通常`SELECT`语句将不会被记录,其并没有对数据库结构或内容造成修改。
每个连接到source的replica都会请求binary log的副本replica会从source处拉取数据而不是source向replica推送数据。replica也会执行其从binary log收到的事件。`发生在source上的修改将会在replica上进行重现`。在重现过程中,会发生表创建、表结构修改、数据的新增/删除/修改等操作。
由于每个replica都是独立的每个连接到source的replica其对`source中binary log`内容的replaying都是独立的。此外因为每个replica只通过请求source来接收binary log的拷贝replica能够以其自身的节奏来读取和更新其数据副本并且其能在不对source和其他replicas造成影响的条件下开启或停止replication过程。
### Replication Formats
在binary log中events根据其事件类型以不同的格式被记录。replication使用的foramts取决于事件被记录到source server的binary log中时所使用的foramt。binary log format和replciation过程中使用到的format其关联关系如下
- 当使用statement-based binary logging时source会将sql statements写入到binary log。从source到replica的replication将会在replica上执行该sql语句。这被称之为statement-based replication关联了mysql statement-based binary logging format
- 当使用row-based loggging时source将会将`table rows如何变更`的事件写入到binary log中。在replica中将会重现events对table rows所做的修改。则会被成为row-based replication
- `row-based logging`是默认的方法
- 可以配置mysql使用`mix-format logging`,但使用`mix-format logging`将默认使用statement-based log。但是对于特定的语句也取决于使用的存储引擎在某些床惊吓log也会被自动切换到row-based。使用mixed format的replication被称之为mix-based replication或mix-format replication
mysql server中的logging format通过`binlog_format` system variable来进行控制。该变量可以在session/global级别进行设置
- 在将variable设置为session级别时将仅会对当前session生效并且仅持续到当前session结束
- 将variable设置为global级别时该设置将会对`clients that connect after the change`生效,`但对于any current client sessions包括.发出该修改请求的session都不生效`
#### 使用statement-based和row-based replication的优缺点对比
每个binary logging format都有优缺点。对大多数usersmixed replication format能够提供性能和数据完整性的最佳组合。
##### Advantages of statement-based replication
- 在使用statement-based格式时会向log files中写入更少的数据。特别是在更新或删除大量行时使用statement-based格式能够导致更少的空间占用。在从备份中获取和恢复时其速度也更快
- log files包含所有造成修改的statements可以被用于审核database
##### Disadvantages of statement-based replication
- 对于Statement-based replication而言statements并不是安全的。并非所有对数据造成修改的statements都可以使用statement-based replication。在使用statement-based replication时任何非确定性的行为都难以复制例如在语句中包含随机函数等非确定性行为
- 对于复杂语句statement必须在实际对目标行执行修改前重新计算。而当使用row-based replication时replica可以直接修改受影响行而无需重新计算
##### Advantages of row-based replication
- 所有的修改可以被replicated其是最安全的replication形式
##### disadvantages of row-based replication
- 相比于statement-based replicationrow-based replication通常会向log中ieur更多数据特别是当statement操作大量行数据是
- 在replica并无法查看从source接收并执行的statements。可以通过`mysqlbinlog``--base64-output=DECODE-ROWS``--verbose`选项来查看数据变更
### Replay Log and Replication Metadata Repositories
replica server会创建一系列仓库其中存储在replication过程中使用的信息
- `relay log`: 该日志被replication I/O线程写入包含从source server的binary log读取的事务。relay log中记录的事务将会被replication SQL thread应用到replica
- `connection metadata repository`: 包含replication receiver thread连接到source server并从binary log获取事务时需要的信息。connection metadata repository会被写入到`mysql.slave_master_info`
- `applier metadata repository`: 包含replication applier thread从relay log读取并应用事务时所需要的信息。applier metadata repository会被写入到`mysql.slave_relay_log_info`表中
#### The Relay Log
relay log和binary log类似由一些`numbered files`构成,文件中包含`描述数据库变更的事件`。relay log还包含一个index file其中记录了所有被使用的relay log files的名称。默认情况下relay log files位于data directory中
relay log files拥有和binary log相同的格式也可以通过mysqlbinlog进行读取。如果使用了binary log transaction compression那么写入relay log的事务payloads也会按照和binary log相同的方式被压缩。
对于默认的replication channelrelay log file命名形式如下`host_name-relay-bin.nnnnnn`:
- `host_name`为replica server host的名称
- `nnnnnn`是序列号序列号从000001开始
对于非默认的replication channels默认的名称为`host_name-relay-bin-channel`
- `channel`为replciation channel的名称
replica会使用index file来追踪当前在使用的relay log files。默认relay log index file的名称为`host_name-relay-bin.index`
当如下条件下replica server将会创建新的relay log file
- 每次replication I/O thread开启时
- 当logs被刷新时例如当执行`FLUSH LOGS`命令时)
- 当当前relay log file的大小太大时大小上限可以通过如下方式决定
- 如果`max_relay_log_size`的大小大于0那么其就是relay log file的最大大小
- 如果`max_relay_log_size`为0那么relay log file的最大大小由`max_binlog_size`决定
replication sql thread在其执行完文件中所有events之后都不再需要该文件被执行完的relay log file都会被自动删除。目前没有显式的机制来删除relay logsrelay log的删除由replciation sql thread来处理。但是`FLUSH LOGS`可以针对relay logs进行rotation。