解决mysql导入数据文件过慢的问题

目前遇到一个问题,mysql 使用 source 命令导入  *.sql  数据文件时,运行的很慢,大概一秒钟插入一两百条左右的样子,对于大的文件来说这个太慢了。

1.登入mysql

$ mysql -uroot -p***

2.查看mysql中对于参数 innodb_flush_log_at_trx_commit 的配置

show global variables where variable_name = 'innodb_flush_log_at_trx_commit';

3.修改

SET GLOBAL innodb_flush_log_at_trx_commit = 0;

修改完成后在次执行相同的文件,200M大约200w+条的数据在1分钟左右。

对于该参数的不同值的说明:

1.innodb_flush_log_at_trx_commit参数为 0
        binlog_group_flush && thd_flush_log_at_trx_commit(NULL) == 0 条件成立,因此直接return了,那么这种情况下log_buffer_flush_to_disk函数不会调用,因此不会做redo刷盘。依赖master线程。
    2.innodb_flush_log_at_trx_commit参数为 1
        !binlog_group_flush || thd_flush_log_at_trx_commit(NULL) == 1 返回为1即为True,因此调用log_buffer_flush_to_disk(True),因此需要做redo刷盘,也要做sync。
    3.innodb_flush_log_at_trx_commit参数为 2
        !binlog_group_flush || thd_flush_log_at_trx_commit(NULL) == 1 返回为0即为Flase,因此调用log_buffer_flush_to_disk(Flase),因此需要做redo刷盘,不做sync。依赖OS的刷盘机制。

参考例子如下:

mysql -u root -p -h 127.0.0.1
 
 
mysql> use test;
Database changed
 
mysql> set global innodb_flush_log_at_trx_commit=0;
Query OK, 0 rows affected (0.03 sec)

mysql> set global sync_binlog=0;
Query OK, 0 rows affected (0.03 sec)
 
mysql> set global max_allowed_packet=1024*1024*512;
Query OK, 0 rows affected (0.00 sec)
 
mysql> set global bulk_insert_buffer_size=512*1024*1024;
Query OK, 0 rows affected (0.00 sec)
 
mysql> set global innodb_buffer_pool_size=512*1024*1024;
Query OK, 0 rows affected, 1 warning (0.09 sec)
 
mysql> source /root/test.sql

实际测试结果感觉还是不够快。

参考链接


PHP 7.x连接MySQL 8.x报错“The server requested authentication method unknown to the client [caching_sha2_password]”

PHP 7.x使用如下代码连接MySQL 8.x

<?php
mysqli_connect('localhost:3306','user','password','database') or die('Error connecting to MySQL server.')
?>

结果出现如下错误信息

Warning: mysqli_connect(): The server requested authentication method unknown to the client [caching_sha2_password] in xx.php

原因为 MySQL 支持 caching_sha2_password  / 用户名密码 两种验证方式,但是从 MySQL 8 开始默认使用caching_sha2_password 的验证方式,导致以前版本的客户端不能成功连接。更详细的解释如下:

The server validates the user and returns the connection status. MySQL uses caching_sha2_password and auth_socket plugins for validation.

The caching_sha2_password plugin uses an SHA-2 algorithm with 256-bit password encryption. MySQL 8 prefers this auth method.

Whereas the auth_socket plugin checks if the socket username matches with the MySQL username. If the names don’t match, it checks for the socket username of the mysql.user. If a match is found, the plugin permits the connection.

But to serve the pre 8.0 clients and avoid compatibility errors, it is preferred to revert back the auth method. Older versions of MySQL use mysql_native_password plugin for validation.

解决方案为修改用户默认的认证方式,如下:

ALTER USER 'user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

参考链接


[RESOLVED] MySQL the server requested authentication method unknown to the client

Error 'Character set '#255' is not a compiled ch aracter set and is not specified in the '/usr/local/mariadb10/share/mysql/charse ts/Index.xml' file' on query. Default database: 'wordpress'. Query: 'BEGIN'

以前通过 家里ADSL上网无固定外网IP的群晖NAS安全实现与公网MySQL服务器主从同步 配置之后,群晖自带的 MariaDB 10.3.21 可以非常流畅的与服务器上的 MySQL 7.x 版本进行主从同步。

前两天系统从 ubuntu 18.04 升级到 ubuntu 20.04 (MySQL 8.x)之后,发现已经无法进行主从同步。

报告如下错误:

$ mysql -u root -p -e "show slave status\G;"
Enter password:
*************************** 1. row ***************************
                Slave_IO_State:
                   Master_Host: 10.8.0.1
                   Master_User: repl1
                   Master_Port: 3306
                 Connect_Retry: 60
               Master_Log_File: mysql-bin.001242
           Read_Master_Log_Pos: 8350259
                Relay_Log_File: mysqld10-relay-bin.000510
                 Relay_Log_Pos: 424
         Relay_Master_Log_File: mysql-bin.001222
              Slave_IO_Running: No
             Slave_SQL_Running: No
               Replicate_Do_DB:
           Replicate_Ignore_DB:
            Replicate_Do_Table:
        Replicate_Ignore_Table:
       Replicate_Wild_Do_Table:
   Replicate_Wild_Ignore_Table:
                    Last_Errno: 22
                    Last_Error: Error 'Character set '#255' is not a compiled character set and is not specified in the '/usr/local/mariadb10/share/mysql/charsets/Index.xml' file' on query. Default database: 'wordpress'. Query: 'BEGIN'
                  Skip_Counter: 0
           Exec_Master_Log_Pos: 125
               Relay_Log_Space: 137841279
               Until_Condition: None
                Until_Log_File:
                 Until_Log_Pos: 0
            Master_SSL_Allowed: Yes
            Master_SSL_CA_File: /var/packages/MariaDB10/etc/ssl/ca.pem
            Master_SSL_CA_Path: /var/packages/MariaDB10/etc/ssl
               Master_SSL_Cert: /var/packages/MariaDB10/etc/ssl/client-cert.pem
             Master_SSL_Cipher:
                Master_SSL_Key: /var/packages/MariaDB10/etc/ssl/client-key.pem
         Seconds_Behind_Master: NULL
 Master_SSL_Verify_Server_Cert: No
                 Last_IO_Errno: 0
                 Last_IO_Error:
                Last_SQL_Errno: 22
                Last_SQL_Error: Error 'Character set '#255' is not a compiled character set and is not specified in the '/usr/local/mariadb10/share/mysql/charsets/Index.xml' file' on query. Default database: 'wordpress'. Query: 'BEGIN'
   Replicate_Ignore_Server_Ids:
              Master_Server_Id: 1
                Master_SSL_Crl: /var/packages/MariaDB10/etc/ssl/ca.pem
            Master_SSL_Crlpath: /var/packages/MariaDB10/etc/ssl
                    Using_Gtid: No
                   Gtid_IO_Pos:
       Replicate_Do_Domain_Ids:
   Replicate_Ignore_Domain_Ids:
                 Parallel_Mode: conservative
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State:
              Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 0

网上查询很久,找到原因 MySQL 8.016 master with MariaDB 10.2 slave on AWS RDS, Character set '#255' is not a compiled character set

This could be a serious problem when replicating between MySQL 8.0 and MariaDB 10.x.

The default (for good technical reasons) `COLLATION` for the 8.0 is `utf8mb4_0900_ai_ci`. Note the "255" associated with it. MariaDB has not yet adopted the Unicode 9.0 collations.

Furthermore, Oracle (MySQL 8.0) did a major rewrite of the collation code, thereby possibly making collation tables incompatible.

probable fix is to switch to the next best general-purpose collation, `utf8mb4_unicode_520_ci` (246) (based on Unicode 5.20). This would require `ALTERing` all the columns' collations. `ALTER TABLE .. CONVERT TO ..` might be the fastest way. Those could be generated via a `SELECT .. information_schema.tables ...`.

大致原因就是MySql 8.0默认使用了最新的utf8mb4_0900_ai_ci字符集,然而MariaDB 10.3.x版本不支持这个字符集,导致无法同步。

8.0.17:

mysql> show collation like 'utf8mb4%';
+----------------------------+---------+-----+---------+----------+---------+---------------+
| Collation                  | Charset | Id  | Default | Compiled | Sortlen | Pad_attribute |
+----------------------------+---------+-----+---------+----------+---------+---------------+
| utf8mb4_0900_ai_ci         | utf8mb4 | 255 | Yes     | Yes      |       0 | NO PAD        |
| utf8mb4_0900_as_ci         | utf8mb4 | 305 |         | Yes      |       0 | NO PAD        |
| utf8mb4_0900_as_cs         | utf8mb4 | 278 |         | Yes      |       0 | NO PAD        |
| utf8mb4_0900_bin           | utf8mb4 | 309 |         | Yes      |       1 | NO PAD        |
| utf8mb4_bin                | utf8mb4 |  46 |         | Yes      |       1 | PAD SPACE     |
| utf8mb4_croatian_ci        | utf8mb4 | 245 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_cs_0900_ai_ci      | utf8mb4 | 266 |         | Yes      |       0 | NO PAD        |
| utf8mb4_cs_0900_as_cs      | utf8mb4 | 289 |         | Yes      |       0 | NO PAD        |
| utf8mb4_czech_ci           | utf8mb4 | 234 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_danish_ci          | utf8mb4 | 235 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_da_0900_ai_ci      | utf8mb4 | 267 |         | Yes      |       0 | NO PAD        |
| utf8mb4_da_0900_as_cs      | utf8mb4 | 290 |         | Yes      |       0 | NO PAD        |
| utf8mb4_de_pb_0900_ai_ci   | utf8mb4 | 256 |         | Yes      |       0 | NO PAD        |
| utf8mb4_de_pb_0900_as_cs   | utf8mb4 | 279 |         | Yes      |       0 | NO PAD        |
| utf8mb4_eo_0900_ai_ci      | utf8mb4 | 273 |         | Yes      |       0 | NO PAD        |
| utf8mb4_eo_0900_as_cs      | utf8mb4 | 296 |         | Yes      |       0 | NO PAD        |
| utf8mb4_esperanto_ci       | utf8mb4 | 241 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_estonian_ci        | utf8mb4 | 230 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_es_0900_ai_ci      | utf8mb4 | 263 |         | Yes      |       0 | NO PAD        |
| utf8mb4_es_0900_as_cs      | utf8mb4 | 286 |         | Yes      |       0 | NO PAD        |
| utf8mb4_es_trad_0900_ai_ci | utf8mb4 | 270 |         | Yes      |       0 | NO PAD        |
| utf8mb4_es_trad_0900_as_cs | utf8mb4 | 293 |         | Yes      |       0 | NO PAD        |
| utf8mb4_et_0900_ai_ci      | utf8mb4 | 262 |         | Yes      |       0 | NO PAD        |
| utf8mb4_et_0900_as_cs      | utf8mb4 | 285 |         | Yes      |       0 | NO PAD        |
| utf8mb4_general_ci         | utf8mb4 |  45 |         | Yes      |       1 | PAD SPACE     |
| utf8mb4_german2_ci         | utf8mb4 | 244 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_hr_0900_ai_ci      | utf8mb4 | 275 |         | Yes      |       0 | NO PAD        |
| utf8mb4_hr_0900_as_cs      | utf8mb4 | 298 |         | Yes      |       0 | NO PAD        |
| utf8mb4_hungarian_ci       | utf8mb4 | 242 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_hu_0900_ai_ci      | utf8mb4 | 274 |         | Yes      |       0 | NO PAD        |
| utf8mb4_hu_0900_as_cs      | utf8mb4 | 297 |         | Yes      |       0 | NO PAD        |
| utf8mb4_icelandic_ci       | utf8mb4 | 225 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_is_0900_ai_ci      | utf8mb4 | 257 |         | Yes      |       0 | NO PAD        |
| utf8mb4_is_0900_as_cs      | utf8mb4 | 280 |         | Yes      |       0 | NO PAD        |
| utf8mb4_ja_0900_as_cs      | utf8mb4 | 303 |         | Yes      |       0 | NO PAD        |
| utf8mb4_ja_0900_as_cs_ks   | utf8mb4 | 304 |         | Yes      |      24 | NO PAD        |
| utf8mb4_latvian_ci         | utf8mb4 | 226 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_la_0900_ai_ci      | utf8mb4 | 271 |         | Yes      |       0 | NO PAD        |
| utf8mb4_la_0900_as_cs      | utf8mb4 | 294 |         | Yes      |       0 | NO PAD        |
| utf8mb4_lithuanian_ci      | utf8mb4 | 236 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_lt_0900_ai_ci      | utf8mb4 | 268 |         | Yes      |       0 | NO PAD        |
| utf8mb4_lt_0900_as_cs      | utf8mb4 | 291 |         | Yes      |       0 | NO PAD        |
| utf8mb4_lv_0900_ai_ci      | utf8mb4 | 258 |         | Yes      |       0 | NO PAD        |
| utf8mb4_lv_0900_as_cs      | utf8mb4 | 281 |         | Yes      |       0 | NO PAD        |
| utf8mb4_persian_ci         | utf8mb4 | 240 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_pl_0900_ai_ci      | utf8mb4 | 261 |         | Yes      |       0 | NO PAD        |
| utf8mb4_pl_0900_as_cs      | utf8mb4 | 284 |         | Yes      |       0 | NO PAD        |
| utf8mb4_polish_ci          | utf8mb4 | 229 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_romanian_ci        | utf8mb4 | 227 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_roman_ci           | utf8mb4 | 239 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_ro_0900_ai_ci      | utf8mb4 | 259 |         | Yes      |       0 | NO PAD        |
| utf8mb4_ro_0900_as_cs      | utf8mb4 | 282 |         | Yes      |       0 | NO PAD        |
| utf8mb4_ru_0900_ai_ci      | utf8mb4 | 306 |         | Yes      |       0 | NO PAD        |
| utf8mb4_ru_0900_as_cs      | utf8mb4 | 307 |         | Yes      |       0 | NO PAD        |
| utf8mb4_sinhala_ci         | utf8mb4 | 243 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_sk_0900_ai_ci      | utf8mb4 | 269 |         | Yes      |       0 | NO PAD        |
| utf8mb4_sk_0900_as_cs      | utf8mb4 | 292 |         | Yes      |       0 | NO PAD        |
| utf8mb4_slovak_ci          | utf8mb4 | 237 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_slovenian_ci       | utf8mb4 | 228 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_sl_0900_ai_ci      | utf8mb4 | 260 |         | Yes      |       0 | NO PAD        |
| utf8mb4_sl_0900_as_cs      | utf8mb4 | 283 |         | Yes      |       0 | NO PAD        |
| utf8mb4_spanish2_ci        | utf8mb4 | 238 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_spanish_ci         | utf8mb4 | 231 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_sv_0900_ai_ci      | utf8mb4 | 264 |         | Yes      |       0 | NO PAD        |
| utf8mb4_sv_0900_as_cs      | utf8mb4 | 287 |         | Yes      |       0 | NO PAD        |
| utf8mb4_swedish_ci         | utf8mb4 | 232 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_tr_0900_ai_ci      | utf8mb4 | 265 |         | Yes      |       0 | NO PAD        |
| utf8mb4_tr_0900_as_cs      | utf8mb4 | 288 |         | Yes      |       0 | NO PAD        |
| utf8mb4_turkish_ci         | utf8mb4 | 233 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_unicode_520_ci     | utf8mb4 | 246 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_unicode_ci         | utf8mb4 | 224 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_vietnamese_ci      | utf8mb4 | 247 |         | Yes      |       8 | PAD SPACE     |
| utf8mb4_vi_0900_ai_ci      | utf8mb4 | 277 |         | Yes      |       0 | NO PAD        |
| utf8mb4_vi_0900_as_cs      | utf8mb4 | 300 |         | Yes      |       0 | NO PAD        |
| utf8mb4_zh_0900_as_cs      | utf8mb4 | 308 |         | Yes      |       0 | NO PAD        |
+----------------------------+---------+-----+---------+----------+---------+---------------+
75 rows in set (0.01 sec)

MariaDB 10.2.30:

mysql> SHOW COLLATION LIKE 'utf8mb4%';
+------------------------------+---------+------+---------+----------+---------+
| Collation                    | Charset | Id   | Default | Compiled | Sortlen |
+------------------------------+---------+------+---------+----------+---------+
| utf8mb4_general_ci           | utf8mb4 |   45 | Yes     | Yes      |       1 |
| utf8mb4_bin                  | utf8mb4 |   46 |         | Yes      |       1 |
| utf8mb4_unicode_ci           | utf8mb4 |  224 |         | Yes      |       8 |
| utf8mb4_icelandic_ci         | utf8mb4 |  225 |         | Yes      |       8 |
| utf8mb4_latvian_ci           | utf8mb4 |  226 |         | Yes      |       8 |
| utf8mb4_romanian_ci          | utf8mb4 |  227 |         | Yes      |       8 |
| utf8mb4_slovenian_ci         | utf8mb4 |  228 |         | Yes      |       8 |
| utf8mb4_polish_ci            | utf8mb4 |  229 |         | Yes      |       8 |
| utf8mb4_estonian_ci          | utf8mb4 |  230 |         | Yes      |       8 |
| utf8mb4_spanish_ci           | utf8mb4 |  231 |         | Yes      |       8 |
| utf8mb4_swedish_ci           | utf8mb4 |  232 |         | Yes      |       8 |
| utf8mb4_turkish_ci           | utf8mb4 |  233 |         | Yes      |       8 |
| utf8mb4_czech_ci             | utf8mb4 |  234 |         | Yes      |       8 |
| utf8mb4_danish_ci            | utf8mb4 |  235 |         | Yes      |       8 |
| utf8mb4_lithuanian_ci        | utf8mb4 |  236 |         | Yes      |       8 |
| utf8mb4_slovak_ci            | utf8mb4 |  237 |         | Yes      |       8 |
| utf8mb4_spanish2_ci          | utf8mb4 |  238 |         | Yes      |       8 |
| utf8mb4_roman_ci             | utf8mb4 |  239 |         | Yes      |       8 |
| utf8mb4_persian_ci           | utf8mb4 |  240 |         | Yes      |       8 |
| utf8mb4_esperanto_ci         | utf8mb4 |  241 |         | Yes      |       8 |
| utf8mb4_hungarian_ci         | utf8mb4 |  242 |         | Yes      |       8 |
| utf8mb4_sinhala_ci           | utf8mb4 |  243 |         | Yes      |       8 |
| utf8mb4_german2_ci           | utf8mb4 |  244 |         | Yes      |       8 |
| utf8mb4_croatian_mysql561_ci | utf8mb4 |  245 |         | Yes      |       8 |
| utf8mb4_unicode_520_ci       | utf8mb4 |  246 |         | Yes      |       8 | <--
| utf8mb4_vietnamese_ci        | utf8mb4 |  247 |         | Yes      |       8 |
| utf8mb4_croatian_ci          | utf8mb4 |  608 |         | Yes      |       8 |
| utf8mb4_myanmar_ci           | utf8mb4 |  609 |         | Yes      |       8 |
| utf8mb4_thai_520_w2          | utf8mb4 |  610 |         | Yes      |       4 |
| utf8mb4_general_nopad_ci     | utf8mb4 | 1069 |         | Yes      |       1 |
| utf8mb4_nopad_bin            | utf8mb4 | 1070 |         | Yes      |       1 |
| utf8mb4_unicode_nopad_ci     | utf8mb4 | 1248 |         | Yes      |       8 |
| utf8mb4_unicode_520_nopad_ci | utf8mb4 | 1270 |         | Yes      |       8 |
+------------------------------+---------+------+---------+----------+---------+
33 rows in set (0.00 sec)

目前暂时没打算修改主服务器上的数据库,暂时等 MariaDB 更新吧。

参考链接


MySQL延时复制(Delayed Replication)

延迟复制简介

即使通常MySQL复制很快,但MySQL缺省的复制存在延迟,并且用户无法缩短延迟时间。另一方面,有时却需要特意增加复制的延迟。设想这样一种场景,用户在主库上误删除了一个表,并且该操作很快被复制到从库。当用户发现这个错误时,从库早就完成了该事件重放。此时主库、从库都没有那个被误删的表了,如何恢复?如果有备份,可以幸运地从备份恢复,丢失的数据量取决于备份的新旧和从备份时间点到表被删除时间点之间该表上数据的变化量。如果没有备份呢?这种情况下,延迟复制或许可以帮上忙,作为一种恢复数据的备选方案。如果在发现问题时,从库还没有来得及重放相应的中继日志,那么就有机会在从库获得该表,继而进行恢复。这里忽略一些其它数据恢复方案,例如已经存在类似Oracle闪回技术(Flashback)在MySQL上的实现,实现方式为解析相应的二进制日志事件,生成反向的SQL语句。这些程序多为个人作品,并没有被加入MySQL发行版本中,因此在易用性、适用性、可靠性等方面还不能与原生的功能相提并论。

        MySQL支持延迟复制,以便从库故意执行比主库晚至少在指定时间间隔的事务。在MySQL 8.0中,延迟复制的方法取决于两个时间戳:immediate_commit_timestamp和original_commit_timestamp。如果复制拓扑中的所有服务器都运行MySQL 8.0.1或更高版本,则使用这些时间戳测量延迟复制。如果从库未使用这些时间戳,则执行MySQL 5.7的延迟复制。

        复制延迟默认为0秒。使用CHANGE MASTER TO MASTER_DELAY = N语句将延迟设置为N秒。从主库接收的事务比主库上的提交至少晚N秒才在从库上执行。每个事务发生延迟(不是以前MySQL版本中的事件),实际延迟仅强制在gtid_log_event或anonymous_gtid_log_event事件上。二进制日志中的每个GTID事务始终都以Gtid_log_event开头,匿名事务没有分配GTID,MySQL确保日志中的每个匿名事务都以Anonymous_gtid_log_event开头。对于事务中的其它事件,不会对它们施加任何等待时间,而是立即执行。注意,START SLAVE和STOP SLAVE立即生效并忽略任何延迟,RESET SLAVE将延迟重置为0。       

例如,下面将实验环境中一主两从半同步复制中的一个从库设置为延迟60秒复制:

mysql> change master to master_delay = 60;
ERROR 3085 (HY000): This operation cannot be performed with a running slave sql thread; run STOP SLAVE SQL_THREAD FOR CHANNEL '' first.
mysql> stop slave sql_thread;
Query OK, 0 rows affected (0.00 sec)
 
mysql> change master to master_delay = 60;
Query OK, 0 rows affected (0.01 sec)
 
mysql> start slave sql_thread;
Query OK, 0 rows affected (0.00 sec)

        联机设置延迟复制时,需要先停止sql_thread线程。现在主库执行一个事务,观察从库的变化:

-- 主
mysql> create table test.t3(a int);
Query OK, 0 rows affected (0.01 sec)
 
mysql> 
 
-- 从
mysql> desc test.t3;
ERROR 1146 (42S02): Table 'test.t3' doesn't exist
mysql> desc test.t3;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

        主库上建立了一个表test.t3,DDL语句自成一个事务。60秒后,从库上才出现该表。

        从库上performance_schema模式下的replication_applier_configuration.desired_delay表列显示使用master_delay选项配置的延迟,replication_applier_status.remaining_delay表列显示剩余的延迟秒数。

-- 从
mysql> select desired_delay from performance_schema.replication_applier_configuration;
+---------------+
| desired_delay |
+---------------+
|            60 |
+---------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|            NULL |
+-----------------+
1 row in set (0.00 sec)
mysql>
-- 主
mysql> drop table test.t3;
Query OK, 0 rows affected (0.02 sec)
mysql> 
-- 从
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|              54 |
+-----------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|              23 |
+-----------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|              16 |
+-----------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|            NULL |
+-----------------+
1 row in set (0.00 sec)
mysql>

        延迟复制可用于多种目的:

  • 防止用户在主库上出错。延迟复制时,可以将延迟的从库回滚到错误之前的时间。
  • 测试滞后时系统的行为方式。例如,在应用程序中,延迟可能是由从库设备上的重负载引起的。但是,生成此负载级别可能很困难。延迟复制可以模拟滞后而无需模拟负载。它还可用于调试与从库滞后相关的条件。
  • 检查数据库过去的快照,而不必重新加载备份。例如,通过配置延迟为一周的从库,如果需要看一下最近几天开发前的数据库样子,可以检查延迟的从库。

延迟复制的简单理解就是,客户端始终保持每条数据的同步时间不低于指定的间隔,同步是持续的。跟计划任务不同,想达到跟计划任务一样,每隔一段时间,集中执行一次是做不到的,比如一小时执行同步一次,这个是做不到的。想实现定时同步,还是写计划任务吧。

延迟复制时间戳

        MySQL 8.0提供了一种新方法,用于测量复制拓扑中的延迟,或称复制滞后。该方法取决于与写入二进制日志的每个事务(不是每个事件)的GTID相关联的以下时间戳:

  • original_commit_timestamp:将事务写入(提交)到主库二进制日志之后的自1970年1月1日00:00:00 UTC以来的微秒数。
  • immediate_commit_timestamp:将事务写入(提交)到从库的二进制日志之后的自1970年1月1日00:00:00 UTC以来的微秒数。

        mysqlbinlog的输出以两种格式显示这些时间戳,从epoch开始的微秒和TIMESTAMP格式,后者基于用户定义的时区以获得更好的可读性。例如:

#190516 15:12:18 server id 1125  end_log_pos 239 CRC32 0xc1ebcb7c       Anonymous_GTID  last_committed=0        sequence_number=1       rbr_only=no     original_committed_timestamp=1557990738835397   immediate_commit_timestamp=1557990738838735     transaction_length=192
# original_commit_timestamp=1557990738835397 (2019-05-16 15:12:18.835397 CST)
# immediate_commit_timestamp=1557990738838735 (2019-05-16 15:12:18.838735 CST)
/*!80001 SET @@session.original_commit_timestamp=1557990738835397*//*!*/;
/*!80014 SET @@session.original_server_version=80016*//*!*/;
/*!80014 SET @@session.immediate_server_version=80016*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 239

        通常,original_commit_timestamp在应用事务的所有副本上始终相同。在主从复制中,主库二进制日志中事务的original_commit_timestamp始终与其immediate_commit_timestamp相同。在从库的中继日志中,事务的original_commit_timestamp和immediate_commit_timestamp与主库的二进制日志中的相同,而在其自己的二进制日志中,事务的immediate_commit_timestamp对应于从库提交事务的时间。

        在组复制设置中,当原始主服务器是组的成员时,将在事务准备好提交时生成original_commit_timestamp。再具体说,当事务在原始主服务器上完成执行并且其写入集准备好发送给该组的所有成员以进行认证时,生成original_commit_timestamp。因此,相同的original_commit_timestamp被复制到所有服务器应用事务,并且每个服务器使用immediate_commit_timestamp在其自己的二进制日志中存储本地提交时间。

        组复制中独有的视图更改事件是一种特殊情况。包含该事件的事务由每个服务器生成,但共享相同的GTID。因此,这种事务不是先在主服务器中执行,然后复制到该组其它成员,而是该组的所有成员都执行并应用相同的事务。由于没有原始主服务器,因此这些事务的original_commit_timestamp设置为零。

监控延迟复制

        在MySQL 8之前的老版本中,监控复制的延迟(滞后)最常用的方法之一是依赖于show slave status输出中的seconds_behind_master字段。但是,当使用比传统主从复制更复杂的复制拓扑,例如组复制时,此度量标准不再适用。MySQL 8中添加的immediate_commit_timestamp和original_commit_timestamp可提供有关复制延迟的更精细的信息。监控支持这些时间戳的复制延迟的推荐方法是使用以下performance_schema模式中的表。

  • replication_connection_status:与主服务器连接的当前状态,提供有关连接线程排队到中继日志中的最后和当前事务的信息。
  • replication_applier_status_by_coordinator:协调器线程的当前状态,仅在使用多线程复制时显示该信息,提供有关协调器线程缓冲到工作队列的最后一个事务的信息,以及当前正在缓冲的事务。
  • replication_applier_status_by_worker:应用从主服务器接收事务的线程的当前状态,提供有关应用程序线程或使用多线程复制时每个工作线程应用的事务信息。

        使用这些表,可以监控相应线程处理的最后一个事务以及该线程当前正在处理的事务的信息,包括:

  • 事务的GTID。
  • 从库中继日志中检索的事务的original_commit_timestamp和immediate_commit_timestamp。
  • 线程开始处理事务的时间。
  • 对于上次处理的事务,线程完成处理它的时间。
            除Performance Schema表之外,show slave status的输出还有三个字段与延迟复制有关:
  • SQL_Delay:非负整数,表示使用CHANGE MASTER TO MASTER_DELAY = N配置的复制延迟,以秒为单位。与performance_schema.replication_applier_configuration.desired_delay值相同。
  • SQL_Remaining_Delay:当Slave_SQL_Running_State等待主执行事件后的MASTER_DELAY秒时,该字段包含一个整数,表示延迟剩余的秒数。在它他时候,此字段为NULL。与performance_schema.replication_applier_status.remaining_delay值相同。
  • Slave_SQL_Running_State:一个字符串,指示SQL线程的状态(类似于Slave_IO_State)。该值与SHOW PROCESSLIST显示的SQL线程的State值相同。
            当从库的SQL线程在执行事件之前等待延迟时,SHOW PROCESSLIST将其状态值显示为:Waiting until MASTER_DELAY seconds after master executed event。

参考链接


MySQL 5.7.27创建用户并授权

$ mysql -u root -p

mysql> use mysql;

mysql> select Host,User from mysql.user;

# 创建用户并设置密码
mysql> create user "wordpress" identified by "password";

# MySQL 8使用如下命令
# mysql> create user "wordpress" identified with mysql_native_password by "password";

#更改用户访问是外网访问还是只能本地访问
mysql> update mysql.user set Host="localhost" where User="wordpress";

# 更新密码,5.7的数据库使用'authentication_string'字段替代了'Password'字段
mysql> update user set authentication_string=password("pass") where User="wordpress" and Host="localhost";

# MySQL 8 不能使用上面的命令修改密码,只能在创建的时候设置密码,可以先删除再创建
# drop user "wordpress";

# 如果没这一行可能也会报一个错误,因此需要运行这一行
mysql> update user set plugin="mysql_native_password";

mysql> select Host,User from mysql.user;

# 授予用户访问Wordpress数据库的权限
mysql> grant all privileges on wordpress.* to "wordpress"@"localhost" identified by "pass";

# MySQL 8使用如下命令
# mysql> grant all privileges on wordpress.* to "wordpress";

# 刷新权限
mysql> flush privileges;

参考链接


家里ADSL上网无固定外网IP的群晖NAS安全实现与公网MySQL服务器主从同步

家里 `ADSL` 上网,没有办法分配固定外网 `IP` ,现在想使用群晖自带的`MariaDB` 安全实现与公网 `MySQL` 服务器主从同步。

最大的问题实际上是如果暴漏 `MySQL` 服务器的端口,但是不限制来源 `IP` 地址的话,会造成非常大的安全隐患。

但是, `ADSL` 恰恰不能提供固定的 `IP` 地址,我们需要解决这个问题。如果通过在公网数据库服务器上搭建 `OpenVPN` 服务器的方式,我们恰恰可以把内外网的设备影射到同一个子网中,而且 `OpenVPN` 提供的加密服务支持,刚刚好满足我们的安全需求。

注意一旦配置主从同步,磁盘的休眠会被MySQL的同步写入打断,导致磁盘不能正常休眠,毕竟正常情况下数据库是持续写入的,不存在很长时间的停顿。这样会导致磁盘周期性的咔咔寻道声,尤其是晚上。

噪声问题,参考 群晖(Synology) DS718+希捷酷狼(Seagate IronWolf)12TB空闲发出持续噪声

1. 参考 Ubuntu架设OpenVPN实现内网穿透 搭建整个的 `OpenVPN` 服务器。映射完成后,设备上会新增一个名为 `tun0` 的网卡设备。同时所有连接到 `VPN` 服务器的设备都被被影射到 `10.8.0.X` 的网段。 公网服务器的地址默认是 `10.8.0.1`,本文中, 群晖NAS的地址被设定为 `10.8.0.7`。

2. 配置防火墙规则,许可来自指定网卡指定地址的设备的访问。注意,此处一定要指定网卡为 `OpenVPN` 创建的虚拟网卡,否则造成安全隐患。

$ export slave_ip=10.8.0.7

#删除可能已经存在的配置,避免出现多条重复记录
$ sudo iptables -D INPUT -i tun0 -p tcp -s $slave_ip --dport 3306 -j ACCEPT
 
#增加配置,只允许特定地址访问数据库端口
$ sudo iptables -I INPUT -i tun0 -p tcp -s $slave_ip --dport 3306 -j ACCEPT

$ sudo iptables -L -n -v
 
#保存配置
$ sudo apt-get install iptables-persistent

#注意,iptables-persistent 与 ufw 冲突, 
#现象就是系统重启后执行 sudo ufw status 显示 inactive,
#但是sudo systemctrl ufw status 或sudo service ufw status 显示服务正常,
#实际上ufw并没有正常工作。
#如果两者同时安装,需要参考 https://www.mobibrw.com?p=29330 进行配置
 
$ sudo netfilter-persistent save
 
#配置被保存到/etc/iptables/rules.v4 /etc/iptables/rules.v6这两个文件下面,
#最好确认一下实际保存的内容,尤其是安装了denyhosts等其他安全软件的情况下,
#可能会记录了多余的规则,需要手工删除

3. 参照 ubuntu 16.04配置基于SSL的MySQL主从同步 配置服务器

4. 接下来是群晖NAS的配置

首先是群晖服务器上没有 `MySQL`,需要安装 `MariaDB` ,如下图:
继续阅读家里ADSL上网无固定外网IP的群晖NAS安全实现与公网MySQL服务器主从同步

解决ubuntu 16.04下更改MySQL的数据库位置

ubuntu 16.04下使用APT安装的MySQL的数据库,目录同时接受apparmor的管理,因此在修改数据库目录的时候,需要同步更新apparmor的配置文件。如果只是迁移数据库的话

$ sudo service mysql stop

$ sudo mv /var/lib/mysql /data/

$ sudo ln -s /data/mysql /var/lib/mysql

$ sudo cp /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/usr.sbin.mysqld.bak

# 增加数据库的访问目录权限,增加如下目录的权限
#  /data/mysql/ r,
#  /data/mysql/** rw,

$ sudo sed -i "s/^[ \t]*\/var\/lib\/mysql\/\*\* rw,/  \/var\/lib\/mysql\/** rw,\n  \/data\/mysql r,\n  \/data\/mysql\/** rwk,/g" /etc/apparmor.d/usr.sbin.mysqld

# 检查完成无误之后,需要删除备份的 usr.sbin.mysqld.bak ,老版本的 apparmor 不会解析备份的文件/或者解析顺序变更了,但是新版本的会解析,导致出现两个不同的配置同时存在,引起异常
$ sudo rm -rf /etc/apparmor.d/usr.sbin.mysqld.bak

$ sudo service apparmor restart

$ sudo service mysql start

对于日志文件一起迁移的情况

$ sudo service tomcat7 stop

$ sudo service denyhosts stop

$ sudo service php7.0-fpm stop

$ sudo service apache2 stop

$ sudo service mysql stop

$ sudo service nginx stop

$ sudo service apparmor stop

$ sudo mv /var/log /data/

$ sudo ln -s /data/log /var/log

# 配置日志文件的访问目录权限,增加如下目录的权限
#  /data/log/mysql.err rw,
#  /data/log/mysql.log rw,
#  /data/log/mysql/ r,
#  /data/log/mysql/** rw,

$ sudo sed -i "s/^[ \t]*\/var\/log\/mysql\/\*\* rw,/  \/var\/log\/mysql\/** rw,\n\n  \/data\/log\/mysql.err rw,\n  \/data\/log\/mysql.log rw,\n  \/data\/log\/mysql\/ r,\n  \/data\/log\/mysql\/** rw,/g" /etc/apparmor.d/usr.sbin.mysqld

# 逐个启动服务,不如直接重启系统
$ sudo reboot

如果依旧启动失败,并且 `MySQL` 是从低版本升级上来的,并且目前正在使用的版本大于或者等于 `MySQL 5.7` ,执行 `journalctl -xe` 观察到类似如下内容:

-- Unit mysql.service has begun starting up.
Aug 20 10:16:50 AY130422143404983ad9 audit[8762]: AVC apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/proc/8762/status" pid=8762 comm="mysqld" requested_mask="r" denied_mask=
Aug 20 10:16:50 AY130422143404983ad9 kernel: audit_printk_skb: 21 callbacks suppressed
Aug 20 10:16:50 AY130422143404983ad9 kernel: audit: type=1400 audit(1566267410.233:325): apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/proc/8762/status" pid=8762 comm="mysq
Aug 20 10:16:50 AY130422143404983ad9 audit[8762]: AVC apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/sys/devices/system/node/" pid=8762 comm="mysqld" requested_mask="r" deni
Aug 20 10:16:50 AY130422143404983ad9 audit[8762]: AVC apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/proc/8762/status" pid=8762 comm="mysqld" requested_mask="r" denied_mask=
Aug 20 10:16:50 AY130422143404983ad9 kernel: audit: type=1400 audit(1566267410.237:326): apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/sys/devices/system/node/" pid=8762 co
Aug 20 10:16:50 AY130422143404983ad9 kernel: audit: type=1400 audit(1566267410.237:327): apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/proc/8762/status" pid=8762 comm="mysq
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.244672Z 0 [Warning] Changed limits: max_open_files: 1024 (requested 5000)
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.244721Z 0 [Warning] Changed limits: table_open_cache: 431 (requested 2000)
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.418600Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.418629Z 0 [Warning] 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict 
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.420618Z 0 [Note] /usr/sbin/mysqld (mysqld 5.7.27-0ubuntu0.16.04.1-log) starting as process 8762 ...
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.422604Z 0 [ERROR] Could not open file '/var/log/mysql/error.log' for error logging: Permission denied
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.423358Z 0 [ERROR] Aborting
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.423499Z 0 [Note] Binlog end
Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.423685Z 0 [Note] /usr/sbin/mysqld: Shutdown complete
Aug 20 10:16:50 AY130422143404983ad9 systemd[1]: mysql.service: Main process exited, code=exited, status=1/FAILURE

那么,此时的`MySQL`的 `apparmor` 配置文件可能还没有更新,这个时候,我们需要手工在`/etc/apparmor.d/usr.sbin.mysqld` 增加几个文件目录的权限,如下:

/proc/*/status r,
/sys/devices/system/node/ r,
/sys/devices/system/node/node0/meminfo r,

另外,注意到我这边出现

Aug 20 10:16:50 AY130422143404983ad9 mysqld[8762]: 2019-08-20T02:16:50.422604Z 0 [ERROR] Could not open file '/var/log/mysql/error.log' for error logging: Permission denied

此时,如果检查目录权限,出现如下现象:

$ ls -la  /var/log/mysql/
total 256260
dr--r-s---   2 mysql adm        4096 Aug 20 10:04 .
drwxrwxr-x+ 21 root  syslog     4096 Aug 20 06:25 ..
-rw-r--r--   1 mysql adm           0 Aug 20 10:04 error.log
-r--r-x---+  1 mysql adm        2982 Aug 20 06:25 error.log.1
-r--r-x---+  1 mysql adm        3195 Aug 19 01:52 error.log.2.gz
-r--r-x---+  1 mysql adm          20 Aug 17 06:25 error.log.3.gz
-r--r-x---+  1 mysql adm          20 Aug 16 06:25 error.log.4.gz
-r--r-x---+  1 mysql adm          20 Aug 15 06:25 error.log.5.gz
-r--r-x---+  1 mysql adm          20 Aug 14 06:25 error.log.6.gz
-r--r-x---+  1 mysql adm          20 Aug 13 06:25 error.log.7.gz
-r--r-x---+  1 mysql adm          20 Aug 12 06:25 error.log.8.gz
-r--r-x---+  1 mysql adm    11235835 Aug 10 06:25 mysql-bin.000637
-r--r-x---+  1 mysql adm     9529581 Aug 11 06:25 mysql-bin.000638
-r--r-x---+  1 mysql adm     8299217 Aug 12 06:25 mysql-bin.000639
-r--r-x---+  1 mysql adm    15319155 Aug 13 06:25 mysql-bin.000640
-r--r-x---+  1 mysql adm    15816427 Aug 14 06:25 mysql-bin.000641
-r--r-x---+  1 mysql adm    47746113 Aug 15 06:25 mysql-bin.000642
-r--r-x---+  1 mysql adm    29508705 Aug 16 06:25 mysql-bin.000643
-r--r-x---+  1 mysql adm    29720349 Aug 17 06:25 mysql-bin.000644
-r--r-x---+  1 mysql adm    34429847 Aug 18 06:25 mysql-bin.000645
-r--r-x---+  1 mysql adm    10252663 Aug 18 19:10 mysql-bin.000646
-r--r-x---+  1 mysql adm     3971401 Aug 19 01:51 mysql-bin.000647
-r--r-x---+  1 mysql adm     1799987 Aug 19 06:25 mysql-bin.000648
-r--r-x---+  1 mysql adm    44653600 Aug 20 06:25 mysql-bin.000649
-r--r-x---+  1 mysql adm         416 Aug 19 06:25 mysql-bin.index

那么需要变更用户的所有者,早期版本支持所有者为 `adm` 用户组,新版本需要 `mysql` ,我们执行如下命令:

$ sudo chown -R mysql:mysql /var/log/mysql*

$ sudo service mysql restart

参考链接


ubuntu 16.04配置基于SSL的MySQL主从同步

首先参考 ubuntu 16.04配置MySQL主从同步 实现同步,接下来执行如下操作

主数据库master配置

#生成加密证书,默认在/var/lib/mysql/下生成ca-key.pem,server-key.pem,client-key.pem

$ sudo mysql_ssl_rsa_setup --uid=mysql

#有时候需要手工修改一下证书所有者
$ sudo chown mysql:mysql /var/lib/mysql/*.pem

$ sudo ls -l /var/lib/mysql/ | grep .pem
-rw------- 1 mysql mysql 1679 Apr  2 22:53 ca-key.pem         #CA私钥
-rw-r--r-- 1 mysql mysql 1107 Apr  2 22:53 ca.pem             #自签的CA证书,客户端连接也需要提供
-rw-r--r-- 1 mysql mysql 1107 Apr  2 22:53 client-cert.pem    #客户端连接服务器端需要提供的证书文件
-rw------- 1 mysql mysql 1679 Apr  2 22:53 client-key.pem     #客户端连接服务器端需要提供的私钥文件
-rw------- 1 mysql mysql 1675 Apr  2 22:53 private_key.pem    #私钥/公钥对的私有成员
-rw-r--r-- 1 mysql mysql  451 Apr  2 22:53 public_key.pem     #私钥/公钥对的共有成员
-rw-r--r-- 1 mysql mysql 1107 Apr  2 22:53 server-cert.pem    #服务器端证书文件
-rw------- 1 mysql mysql 1679 Apr  2 22:53 server-key.pem     #服务器端私钥文件

#配置服务器
$ sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf.bak

$ sudo sed -i "s/# ssl-ca=\/etc\/mysql\/cacert.pem/ssl-ca=\/var\/lib\/mysql\/ca.pem/g" /etc/mysql/mysql.conf.d/mysqld.cnf

$ sudo sed -i "s/# ssl-cert=\/etc\/mysql\/server-cert.pem/ssl-cert=\/var\/lib\/mysql\/server-cert.pem/g" /etc/mysql/mysql.conf.d/mysqld.cnf

$ sudo sed -i "s/# ssl-key=\/etc\/mysql\/server-key.pem/ssl-key=\/var\/lib\/mysql\/server-key.pem/g" /etc/mysql/mysql.conf.d/mysqld.cnf

#重启MySQL服务
$ sudo service mysql restart

#查看服务器状态,是否已经启用SSL
$ mysql -u root -p -e "show global variables like '%ssl%'"
Enter password: 
+---------------+--------------------------------+
| Variable_name | Value                          |
+---------------+--------------------------------+
| have_openssl  | YES                            |
| have_ssl      | YES                            |
| ssl_ca        | /var/lib/mysql/ca.pem          |
| ssl_capath    |                                |
| ssl_cert      | /var/lib/mysql/server-cert.pem |
| ssl_cipher    |                                |
| ssl_crl       |                                |
| ssl_crlpath   |                                |
| ssl_key       | /var/lib/mysql/server-key.pem  |
+---------------+--------------------------------+

#修改已存在用户,要求必须通过SSL才能同步,完成主从同步之后,从库可能会无法正常同步这个修改,需要手工跳过一个错误 "stop slave;SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;start slave;"
$ mysql -u root -p -e "ALTER USER 'repl'@'182.254.149.39' REQUIRE SSL;"

# 阻止数据库记录写入,避免后期我们备份数据库的时候数据发生变动
# 该命令对于普通账号的只读模式,root 账号无效,因此访问数据库的账号
# 尽量不要使用root账号,如果是root 账号,只能暂时停止所有访问数据库的服务了
$ mysql -u root -p -e "set global read_only=1;"  

#查询并记录主库的同步位置
$ mysql -u root -p -e "SHOW MASTER STATUS;"
Enter password: 
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000021 |   12144639 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

# 执行 ubuntu 16.04配置MySQL主从同步 http://www.mobibrw.com/?p=10541 里的备份脚本 
$ sudo bash backup_wordpress.sh

# 取消普通账号的只读模式
$ mysql -u root -p -e "set global read_only=0;"

从服务器slave配置

$ sudo mkdir /etc/mysql/ssl
#从服务端获取证书

$ sudo scp -P 22 -r root@www.mobibrw.com:/var/lib/mysql/ca.pem /etc/mysql/ssl/

$ sudo chown mysql:mysql /etc/mysql/ssl/ca.pem

$ sudo scp -P 22 -r root@www.mobibrw.com:/var/lib/mysql/client-cert.pem /etc/mysql/ssl/

$ sudo chown mysql:mysql /etc/mysql/ssl/client-cert.pem

$ sudo scp -P 22 -r root@www.mobibrw.com:/var/lib/mysql/client-key.pem /etc/mysql/ssl/

$ sudo chown mysql:mysql /etc/mysql/ssl/client-key.pem

$ mysql -u root -p -e "stop slave;"

#获取最后同步的位置,为后续恢复进行准备
$ mysql -u root -p -e "show slave status\G;" | grep Exec_Master_Log_Pos:
Enter password:
          Exec_Master_Log_Pos: 12178842

$ mysql -u root -p -e "show slave status\G;" | grep Master_Log_File
Enter password:
              Master_Log_File: mysql-bin.000021
        Relay_Master_Log_File: mysql-bin.000021

#修改同步信息
$ mysql -u root -p -e "CHANGE MASTER TO MASTER_HOST='www.mobibrw.com', MASTER_USER='repl', MASTER_PASSWORD='slavepass', MASTER_LOG_FILE='mysql-bin.000021',MASTER_LOG_POS=12178842 ,master_ssl=1,master_ssl_ca='/etc/mysql/ssl/ca.pem', master_ssl_capath='/etc/mysql/ssl', master_ssl_cert='/etc/mysql/ssl/client-cert.pem', master_ssl_key='/etc/mysql/ssl/client-key.pem';"

$ mysql -u root -p -e "start slave;"

#查看从库状态
$ mysql -u root -p -e "show slave status\G;"
Enter password:
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: www.mobibrw.com
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000021
          Read_Master_Log_Pos: 12701244
               Relay_Log_File: VM-xxx-xxx-xxxxx-relay-bin.000003
                Relay_Log_Pos: 195033
        Relay_Master_Log_File: mysql-bin.000021
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 12701244
              Relay_Log_Space: 523107
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: Yes
           Master_SSL_CA_File: /etc/mysql/ssl/ca.pem
           Master_SSL_CA_Path: /etc/mysql/ssl
              Master_SSL_Cert: /etc/mysql/ssl/client-cert.pem
            Master_SSL_Cipher:
               Master_SSL_Key: /etc/mysql/ssl/client-key.pem
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
                  Master_UUID: xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:

注意,偶尔在系统升级的时候,从库可能会丢失同步状态配置。这时候,我们需要重新同步,此时我们从从设备`/var/lib/mysql/master.info`中找到被中断的同步点。

里面的内容一般如下:

25
mysql-bin.000582
4765083
www.mobibrw.com
xxxx
xxxx
3306
60
1
/etc/mysql/ssl/ca.pem
/etc/mysql/ssl
/etc/mysql/ssl/client-cert.pem

/etc/mysql/ssl/client-key.pem
0
30.000

0
0b674082-f01d-11e9-8f8c-00163e0a4ffe
86400


0

注意`mysql-bin.000582`下面的`4765083`就是同步位置,在恢复的时候,就是这两个关键数据。

参考链接


ubuntu 16.04配置MySQL主从同步

准备工作

1.主从数据库版本最好一致

2.主从数据库内数据保持一致

主数据库:121.199.27.227 /ubuntu 16.04 MySQL 5.7.21 (阿里云

从数据库:182.254.149.39 /ubuntu 16.04 MySQL 5.7.21 (腾讯云

防火墙配置

配置主服务器只允许特定IP访问数据库的端口,避免不必要的攻击。

主库防火墙配置
# iptables -A INPUT -p tcp -s slave_ip --dport 3306 -j ACCEPT

#删除可能已经存在的配置,避免出现多条重复记录
$ sudo iptables -D INPUT -p tcp -s 182.254.149.39 --dport 3306 -j ACCEPT 
$ sudo iptables -D INPUT -p tcp -s 127.0.0.1 --dport 3306 -j ACCEPT 
$ sudo iptables -D INPUT -p tcp --dport 3306 -j DROP 
$ sudo iptables -D INPUT -p udp --dport 3306 -j DROP 
$ sudo iptables -D INPUT -p sctp --dport 3306 -j DROP

#增加配置,只允许特定地址访问数据库端口
$ sudo iptables -A INPUT -p tcp -s 182.254.149.39 --dport 3306 -j ACCEPT
$ sudo iptables -A INPUT -p tcp -s 127.0.0.1 --dport 3306 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 3306 -j DROP
$ sudo iptables -A INPUT -p udp --dport 3306 -j DROP
$ sudo iptables -A INPUT -p sctp --dport 3306 -j DROP

$ sudo iptables -L -n -v

#保存配置
$ sudo apt-get install iptables-persistent

#注意,iptables-persistent 与 ufw 冲突, 
#现象就是系统重启后执行 sudo ufw status 显示 inactive,
#但是sudo systemctrl ufw status 或sudo service ufw status 显示服务正常,
#实际上ufw并没有正常工作。
#如果两者同时安装,需要参考 https://www.mobibrw.com?p=29330 进行配置

$ sudo netfilter-persistent save

#配置被保存到/etc/iptables/rules.v4 /etc/iptables/rules.v6这两个文件下面,
#最好确认一下实际保存的内容,尤其是安装了denyhosts等其他安全软件的情况下,
#可能会记录了多余的规则,需要手工删除
从库防火墙配置
# iptables -A OUTPUT -p tcp -d master_ip --dport 3306 -j ACCEPT

#删除可能已经存在的配置,避免出现多条重复记录
$ sudo iptables -D OUTPUT -p tcp -d 121.199.27.227 --dport 3306 -j ACCEPT

#增加配置
$ sudo iptables -A OUTPUT -p tcp -d 121.199.27.227 --dport 3306 -j ACCEPT

$ sudo iptables -L -n

#保存配置
$ sudo apt-get install iptables-persistent

#注意,iptables-persistent 与 ufw 冲突, 
#现象就是系统重启后执行 sudo ufw status 显示 inactive,
#但是sudo systemctrl ufw status 或sudo service ufw status 显示服务正常,
#实际上ufw并没有正常工作。
#如果两者同时安装,需要参考 https://www.mobibrw.com?p=29330 进行配置

$ sudo netfilter-persistent save

#配置被保存到/etc/iptables/rules.v4 /etc/iptables/rules.v6这两个文件下面,
#最好确认一下实际保存的内容,尤其是安装了denyhosts等其他安全软件的情况下,
#可能会记录了多余的规则,需要手工删除

主数据库master配置

1.修改mysql配置

$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

在[mysqld]部分进行如下修改:

[mysqld]
#开启二进制日志,默认是注释掉的,我们去掉注释
log-bin = /var/log/mysql/mysql-bin.log 

#设置server-id
server-id = 1 

#默认是127.0.0.1,此处我们设置为任意地址,放开远程访问,这么操作之前一定要确保防火墙配置正确,否则会产生安全风险
bind-address = 0.0.0.0 

#如果数据库是从5.7版本之前升级的,并且是wordpress那么会遇到无法更改数据库的情况,
#NO_ZERO_IN_DATE,NO_ZERO_DATE这两个参数限制的,我们需要去掉这个限制,原因在于
#wordpress创建的表中存在
#`comment_date` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00',
#这样的定义是没办法进行后续的操作的,因此我们需要重新定义sql_mode来解除这个限制
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

2.修改需要同步的表的引擎为INNODB,只有INNODB支持主从,MyISAM不支持

# 此处以wordpress为例,默认情况下wordpress中的wp_options表为MyISAM引擎,
# 这会导致数据同步失败,可能出现的错误信息如下:
# Last_Errno: 1032
# Last_Error: Could not execute Delete_rows event on table wordpress.wp_options; Can't find record in 'wp_options', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000007, end_log_pos 3678486
# 使用“show create table wp_options\G;”查看表的引擎信息
# 单个表修改的命令如下
#mysql -u root -p -e "use wordpress; ALTER TABLE wp_options ENGINE=INNODB;"

#整个数据库修改的命令如下,下面的语句只是生成执行语句,具体执行,还需要把结果拷贝出来执行
$ mysql -u root -p -e "USE wordpress; SET @DATABASE_NAME = 'wordpress'; SELECT GROUP_CONCAT(CONCAT( 'ALTER TABLE ' ,TABLE_NAME ,' ENGINE=InnoDB; ') SEPARATOR '' ) FROM information_schema.TABLES AS t WHERE TABLE_SCHEMA = @DATABASE_NAME AND TABLE_TYPE = 'BASE TABLE' AND ENGINE = 'MyISAM';"

#我这边的例子如下
$ mysql -u root -p -e "USE wordpress; ALTER TABLE wp_IPBLC_blacklist ENGINE=InnoDB; ALTER TABLE wp_IPBLC_login_failed ENGINE=InnoDB; ALTER TABLE wp_IPBLC_usernames ENGINE=InnoDB; ALTER TABLE wp_commentmeta ENGINE=InnoDB; ALTER TABLE wp_comments ENGINE=InnoDB; ALTER TABLE wp_links ENGINE=InnoDB; ALTER TABLE wp_options ENGINE=InnoDB; ALTER TABLE wp_postmeta ENGINE=InnoDB; ALTER TABLE wp_posts ENGINE=InnoDB; ALTER TABLE wp_term_relationships ENGINE=InnoDB; ALTER TABLE wp_term_taxonomy ENGINE=InnoDB; ALTER TABLE wp_terms ENGINE=InnoDB; ALTER TABLE wp_usermeta ENGINE=InnoDB; ALTER TABLE wp_users ENGINE=InnoDB;"

3.重启mysql,创建用于同步的用户账号

创建用户并授权:用户:repl 密码:slavepass

$ sudo service mysql restart

$ mysql -u root -p -e "CREATE USER 'repl'@'182.254.149.39' IDENTIFIED BY 'slavepass';" #创建用户

$ mysql -u root -p -e "GRANT REPLICATION SLAVE ON *.* TO 'repl'@'182.254.149.39';" #分配权限

$ mysql -u root -p -e "flush privileges;"  #刷新权限

$ mysql -u root -p -e "SELECT User, Host FROM mysql.user;"  #查看用户

4.查看master状态,记录二进制文件名(mysql-bin.000001)和位置(333802):

# 阻止数据库记录写入,避免后期我们备份数据库的时候数据发生变动
# 该命令对于普通账号的只读模式,root 账号无效,因此访问数据库的账号
# 尽量不要使用root账号,如果是root 账号,只能暂时停止所有访问数据库的服务了
$ mysql -u root -p -e "set global read_only=1;"  

$ mysql -u root -p -e "SHOW MASTER STATUS;"
Enter password: 
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |   333802 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

5.主库备份,为从库的第一次数据同步准备数据

使用如下脚本产生数据库备份文件

#此处以备份wordpress数据库为例子

datadump=`which mysqldump`

mysqluser="root"

userpass="password" 

wordpressdb="wordpress"

backupwordpress_sql=$wordpressdb.`date +%Y%m%d`.sql

# 注意,如果是MySQL 8.x那么 --master-data 需要修改成 --source-data 否则在备份的第一句会出现如下警告 
#“WARNING: --master-data is deprecated and will be removed in a future version. Use --source-data instead.”

ver=`mysqldump -V | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p'`

args='--master-data'

if [ "${ver}"\>="8.0.0" ]; then
  args='--source-data'
fi

if $datadump $args --single-transaction -u $mysqluser --password=$userpass -h localhost --opt $wordpressdb > $backupwordpress_sql 2>&1
then
  echo " backup $wordpressdb success"
else
  echo " backup $wordpressdb error"
  exit 1
fi

#检验文件尾部是否存在 “-- Dump completed on”,如果存在不存在,则说明备份出错了。
if [ 0 -eq "$(sed '/^$/!h;$!d;g' $backupwordpress_sql | grep -c "Dump completed on")" ]; 
then
  echo " backup $wordpressdb error"
  exit 1	
else
  echo " backup $wordpressdb success"
fi

执行脚本,确保最后输出备份成功

$ cd ~

$ sudo bash backup_wordpress.sh

# 取消普通账号的只读模式
$ mysql -u root -p -e "set global read_only=0;"

从服务器slave配置

1.修改mysql配置

$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

修改server-id,每个数据库的server-id要求是唯一的,不能相互冲突

[mysqld]
#设置server-id,必须唯一
server-id = 2

# 根据业务需要配置数据库是否只读
#read_only = on
#super_read_only = on
#tx_read_only = on

#日志也最好打开
log_bin                 = /var/log/mysql/mysql-bin.log

#如果日志开启了,最好把日志格式设置为row格式,这样如果主从数据不一致,可以尝试mysql flashback功能
binlog-format    = row 

#如果数据库是从5.7版本之前升级的,并且是wordpress那么会遇到无法更改数据库的情况,
#NO_ZERO_IN_DATE,NO_ZERO_DATE这两个参数限制的,我们需要去掉这个限制,原因在于
#wordpress创建的表中存在
#`comment_date` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00',
#这样的定义是没办法进行后续的操作的,因此我们需要重新定义sql_mode来解除这个限制
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

2.首次还原数据库:

#停止可能访问数据库的应用
$ sudo service apache2 stop

$ sudo service php7.0-fpm stop

$ sudo service mysql restart

# 确保无应用访问数据库的情况下,记录主库日志位置,并执行备份脚本
 mysql -u root -p -e "SHOW MASTER STATUS;"
Enter password: 
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |   333802 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

$ sudo bash backup_wordpress.sh

# 此处已经可以启动刚刚关闭的服务了
$ sudo service php7.0-fpm start

$ sudo service apache2 start

$ scp -P 22 -r root@121.199.27.227:~/wordpress.*.sql ./

#删除可能存在的一行警告信息,这行警告信息可能导致我们无法恢复数据
$ sed -i "/^mysqldump: \[Warning\] Using a password on the command line interface can be insecure\./d" wordpress.*.sql

$ mysql -u root -p -e "stop slave;"

$ mysql -u root -p -e "drop database wordpress;"

$ mysql -u root -p -e "create database wordpress;"

$ mysql -u root -p wordpress < wordpress.*.sql

还原完成后,把数据库设置成只读模式,如果从库可写会出现冲突导致同步失败

$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

添加如下语句:

[mysqld]
#数据库只读 
#read_only = on 

super_read_only = on 

#tx_read_only = on

3.重启mysql,执行同步SQL语句(需要主服务器主机名,登陆凭据,二进制文件的名称和位置):

$ sudo service mysql restart

#只读模式
$ mysql -u root -p -e "set global read_only=1;"

#最好使用如下命令获得主库的起始位置
$ grep 'CHANGE MASTER TO MASTER_LOG_FILE'  wordpress.*.sql | more

$ mysql -u root -p -e "CHANGE MASTER TO MASTER_HOST='121.199.27.227', MASTER_USER='repl', MASTER_PASSWORD='slavepass', MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=333802;"

4.启动slave同步进程:

$ mysql -u root -p -e "start slave;"

5.查看slave状态:

$ mysql -u root -p -e "show slave status\G;"
Enter password:
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 121.199.27.227
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 9448236
               Relay_Log_File: VM-114-251-ubuntu-relay-bin.000002
                Relay_Log_Pos: 17780
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
        ...

当Slave_IO_Running和Slave_SQL_Running都为YES的时候就表示主从同步设置成功了。接下来就可以进行一些验证了,比如在主master数据库的test数据库的一张表中插入一条数据,在slave的test库的相同数据表中查看是否有新增的数据即可验证主从复制功能是否有效,还可以关闭slave(mysql>stop slave;),然后再修改master,看slave是否也相应修改(停止slave后,master的修改不会同步到slave),就可以完成主从复制功能的验证了。

还可以用到的其他相关参数:

master开启二进制日志后默认记录所有库所有表的操作,可以通过配置来指定只记录指定的数据库甚至指定的表的操作,具体在mysql配置文件的[mysqld]可添加修改如下选项:

# 不同步哪些数据库  
binlog-ignore-db = mysql  
binlog-ignore-db = test  
binlog-ignore-db = information_schema  
  
# 只同步哪些数据库,除此之外,其他不同步  
binlog-do-db = game

如之前查看master状态时就可以看到只记录了test库,忽略了manual和mysql库。

注意,偶尔在系统升级的时候,从库可能会丢失同步状态配置。这时候,我们需要重新同步,此时我们从从设备`/var/lib/mysql/master.info`中找到被中断的同步点。

里面的内容一般如下:

25
mysql-bin.000582
4765083
121.199.27.227
xxxx
xxxx
3306
60
1
/etc/mysql/ssl/ca.pem
/etc/mysql/ssl
/etc/mysql/ssl/client-cert.pem

/etc/mysql/ssl/client-key.pem
0
30.000

0
0b674082-f01d-11e9-8f8c-00163e0a4ffe
86400


0

注意`mysql-bin.000582`下面的`4765083`就是同步位置,在恢复的时候,就是这两个关键数据。

参考链接