MySQL 的 Binlog 记录着 MySQL 数据库的所有变更信息,了解 Binlog 的结构可以帮助我们解析Binlog,甚至对 Binlog 进行一些修改,或者说是“篡改”,例如实现类似于 Oracle 的 flashback 的功能,恢复误删除的记录,把 update 的记录再还原回去等。本文将带您探讨一下这些神奇功能的实现,您会发现比您想象地要简单得多。本文指的 Binlog 是 ROW 模式的 Binlog,这也是 MySQL 8 里的默认模式,STATEMENT 模式因为使用中有很多限制,现在用得越来越少了。
创新互联主营龙子湖网站建设的网络公司,主营网站建设方案,重庆App定制开发,龙子湖h5成都微信小程序搭建,龙子湖网站营销推广欢迎龙子湖等地区企业咨询
Binlog 由事件(event)组成,请注意是事件(event)不是事务(transaction),一个事务可以包含多个事件。事件描述对数据库的修改内容。
现在我们已经了解了 Binlog 的结构,我们可以试着修改 Binlog 里的数据。例如前面举例的 Binlog 删除了一条记录,我们可以试着把这条记录恢复,Binlog 里面有个删除行(DELETE_ROWS_EVENT)的事件,就是这个事件删除了记录,这个事件和写行(WRITE_ROWS_EVENT)的事件的数据结构是完全一样的,只是删除行事件的类型是 32,写行事件的类型是 30,我们把对应的 Binlog 位置的 32 改成 30 即可把已经删除的记录再插入回去。从前面的 “show binlog events” 里面可看到这个 DELETE_ROWS_EVENT 是从位置 378 开始的,这里的位置就是 Binlog 文件的实际位置(以字节为单位)。从事件(event)的结构里面可以看到 type_code 是在 event 的第 5 个字节,我们写个 Python 小程序把把第383(378+5=383)字节改成 30 即可。当然您也可以用二进制编辑工具来改。
找出 Binlog 中的大事务
由于 ROW 模式的 Binlog 是每一个变更都记录一条日志,因此一个简单的 SQL,在 Binlog 里可能会产生一个巨无霸的事务,例如一个不带 where 的 update 或 delete 语句,修改了全表里面的所有记录,每条记录都在 Binlog 里面记录一次,结果是一个巨大的事务记录。这样的大事务经常是产生麻烦的根源。我的一个客户有一次向我抱怨,一个 Binlog 前滚,滚了两天也没有动静,我把那个 Binlog 解析了一下,发现里面有个事务产生了 1.4G 的记录,修改了 66 万条记录!下面是一个简单的找出 Binlog 中大事务的 Python 小程序,我们知道用 mysqlbinlog 解析的 Binlog,每个事务都是以 BEGIN 开头,以 COMMIT 结束。我们找出 BENGIN 前面的 “# at” 的位置,检查 COMMIT 后面的 “# at” 位置,这两个位置相减即可计算出这个事务的大小,下面是这个 Python 程序的例子。
切割 Binlog 中的大事务
对于大的事务,MySQL 会把它分解成多个事件(注意一个是事务 TRANSACTION,另一个是事件 EVENT),事件的大小由参数 binlog-row-event-max-size 决定,这个参数默认是 8K。因此我们可以把若干个事件切割成一个单独的略小的事务
ROW 模式下,即使我们只更新了一条记录的其中某个字段,也会记录每个字段变更前后的值,这个行为是 binlog_row_image 参数控制的,这个参数有 3 个值,默认为 FULL,也就是记录列的所有修改,即使字段没有发生变更也会记录。这样我们就可以实现类似 Oracle 的 flashback 的功能,我个人估计 MySQL 未来的版本从可能会基于 Binlog 推出这样的功能。
了解了 Binlog 的结构,再加上 Python 这把瑞士军刀,我们还可以实现很多功能,例如我们可以统计哪个表被修改地最多?我们还可以把 Binlog 切割成一段一段的,然后再重组,可以灵活地进行 MySQL 数据库的修改和迁移等工作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
通过 AUTO_INCREMENT设置
SQL INSERT语句的时候,要避免 指定那个自增的字段.否则会发生主键的冲突。
通过 ALTER TABLE语句 可以修改 自增的数值, 但是只能增加,不能减少。
TRUNCATE TABLE 语句,会将自增ID重置为零。
mysql CREATE TABLE test_create_tab2 (
- id INT AUTO_INCREMENT,
- val VARCHAR(10),
- PRIMARY KEY (id)
- );
Query OK, 0 rows affected (0.09 sec)
mysql INSERT INTO test_create_tab2(val) VALUES ('NO id');
Query OK, 1 row affected (0.03 sec)
mysql select last_insert_id() as id;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
mysql INSERT INTO test_create_tab2(val) VALUES ('NO id 2');
Query OK, 1 row affected (0.03 sec)
mysql select last_insert_id() as id;
+----+
| id |
+----+
| 2 |
+----+
1 row in set (0.00 sec)
mysql select * from test_create_tab2;
+----+---------+
| id | val |
+----+---------+
| 1 | NO id |
| 2 | NO id 2 |
+----+---------+
2 rows in set (0.00 sec)
utf8编码可以支持一到4字节的字符编码,在mysql用我们一般使用utf8编码来处理字符类型,通常情况下都没有问题,但遇到4字节编码的字符,在数据存取的时候就会有问题了。
通常我们可能会得到一个错误或者警告:Incorrect string value: '/xF0/x9D/x8C/x86' for column ...
Mysql 从5.5.3版本开始支持4字节的utf8编码,如果你的Mysql数据库是5.5.3+,按照以下步骤就能解决这个问题,如果版本低于5.5.3,是不是可以考虑升级数据库版本呢?
1、在修改数据库编码前先对数据库备份(虽然utf8mb4兼容utf8,但有备无患)
2、修改数据库的编码、表的编码、列的编码为utf8mb4
3、在Mysql数据库配置文件(my.ini)中加入如下设置
[client]default-character-set = utf8mb4[mysql]default-character-set = utf8mb4[mysqld]character-set-client-handshake = FALSEcharacter-set-server = utf8mb4collation-server = utf8mb4_unicode_ci
重新启动Mysql数据库,确认设置生效
mysql show VARIABLES like '%char%';+--------------------------+----------------------------------------+| Variable_name | Value |+--------------------------+----------------------------------------+| character_set_client | utf8 || character_set_connection | utf8 || character_set_database | utf8mb4 || character_set_filesystem | binary || character_set_results | utf8 || character_set_server | utf8mb4 || character_set_system | utf8 || character_sets_dir | /home/app/mysql-5.5.33/share/charsets/ |+--------------------------+----------------------------------------+8 rows in set
4、在获取数据库连接的时候执行sql:set names utf8mb4;我使用的是alibaba的开源数据库连接池程序,在配置文件中增加一行如下配置
property name="connectionInitSqls" value="set names utf8mb4;" /
重新启动应用程序,问题解决。
整理 MySQL 8.0 文档时发现一个变更:
默认字符集由 latin1 变为 utf8mb4。想起以前整理过字符集转换文档,升级到 MySQL 8.0 后大概率会有字符集转换的需求,在此正好分享一下。
当时的需求背景是:
部分系统使用的字符集是 utf8,但 utf8 最多只能存 3 字节长度的字符,不能存放 4 字节的生僻字或者表情符号,因此打算迁移到 utf8mb4。
迁移方案一1. 准备新的数据库实例,修改以下参数:[mysqld]## Character Settingsinit_connect='SET NAMES utf8mb4'#连接建立时执行设置的语句,对super权限用户无效character-set-server = utf8mb4collation-server = utf8mb4_general_ci#设置服务端校验规则,如果字符串需要区分大小写,设置为utf8mb4_binskip-character-set-client-handshake#忽略应用连接自己设置的字符编码,保持与全局设置一致## Innodb Settingsinnodb_file_format = Barracudainnodb_file_format_max = Barracudainnodb_file_per_table = 1innodb_large_prefix = ON#允许索引的最大字节数为3072(不开启则最大为767字节,对于类似varchar(255)字段的索引会有问题,因为255*4大于767)
2. 停止应用,观察,确认不再有数据写入
可通过 show master status 观察 GTID 或者 binlog position,没有变化则没有写入。
3. 导出数据
先导出表结构:mysqldump -u -p --no-data --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=OFF --databases testdb /backup/testdb.sql
后导出数据:mysqldump -u -p --no-create-info --master-data=2 --flush-logs --routines --events --triggers --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=OFF --database testdb /backup/testdata.sql
4. 修改建表语句
修改导出的表结构文件,将表、列定义中的 utf8 改为 utf8mb4
5. 导入数据
先导入表结构:mysql -u -p testdb /backup/testdb.sql
后导入数据:mysql -u -p testdb /backup/testdata.sql
6. 建用户
查出旧环境的数据库用户,在新数据库中创建
7. 修改新数据库端口,启动应用进行测试
关闭旧数据库,修改新数据库端口重启,启动应用
1、授权mysql在远程机器上以登录。
GRANT ALL PRIVILEGES ON *.* TO 'root'@'host' IDENTIFIED BY 'pwd' WITH GRANT OPTION。
2、修改字段名称和类型。
--alter table upload change column old name new name new datatype
alter table filetable change column fdata fdata MediumBlob
3、MySQL BLOB。
MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。BLOB类型实际是个类型系列(TinyBlob、Blob、MediumBlob、LongBlob),除在存储的最大信息量上不同,基本是等同的。
MySQL的四种BLOB类型;类型、大小(单位:字节):
1、TinyBlob 最大 255 。
2、Blob 最大 65K。
3、MediumBlob 最大 16M。
4、LongBlob 最大 4G。
实际使用中根据需要存入的数据大小定义不同的BLOB类型。注意:如存储的文件过大,数据库的性能会下降。