一日之计在于晨,一年之计在于春,一生之计在于勤。
一、各种日志
查询日志:
query log
,记录客户端操作的所有sql语句,包括select查询语句在内。- 由于查询日志纪录了所有数据库的操作,因此对于访问频繁的应用,该日志会对系统性能造成一定影响,通常建议关闭此日志
- 即便是执行不成功的操作也会被记录在该文件中
慢查询日志:
slow query log
,记录了执行时间超过long_query_time参数值的sql语句。- MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。
- 慢查询日志可以有效的帮助我们发现实际应用中sql的性能问题,找出执行效率低下的sql语句
错误日志:error log,它包含一下几种:
- mysqld启动和关闭过程中输出的事件信息
- mysqld运行中产生的错误信息
- event scheduler(事件调度器)运行一个event时产生的日志信息
- 在主从复制架构中的从服务器上启动从服务器线程时产生的信息
二进制日志:binary log-MySQL的bin log三种工作模式:Row level/Statement level/Mixed。
MySQ的bin log是二进制日志归档文件,用于记录MySQL的数据更新操作(增删改),另外在主从复制中也是通过binlog来实现。
二进制日志需要借助mysqlbinlog这个工具进行查看,该日志里面记录的所有的DDL和DML语句,但select语句除外。
binlog的写入逻辑比较简单:事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。
- 一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及到了 binlog cache 的保存问题。
- 系统给 binlog cache 分配了一片内存,每个线程一个,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。
事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache。
每个线程有自己 binlog cache,但是共用同一份 binlog 文件。
write 和 fsync 的时机,是由参数 sync_binlog 控制的:
- sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
- sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
- sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
三种工作模式
- Row level:日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。
- 优点:能清楚的记录每一行数据修改的细节
- 缺点:数据量太大
- Statement level(默认):每一条被修改数据的sql都会记录到master的bin-log中,slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql再次执行
- 优点:解决了 Row level下的缺点,不需要记录每一行的数据变化,减少bin-log日志量,节约磁盘IO,提高新能
- 缺点:容易出现主从复制不一致
- Mixed(混合模式):结合了Row level和Statement level的优点
- Row level:日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。
相关问题:一个事务的 binlog 是有完整格式的,分别如下:
- statement格式的binlog,最后会有COMMIT
- row格式的binlog,最后会有一个XID event
redo log和binlog是怎么关联起来的?
它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:
- 如果碰到既有prepare又有commit的redo log,就直接提交
- 如果碰到只有prepare而没有commit的redo log,就拿着XID去binlog找对应的事务
MySQL在记录 binlog 的时候,不论是 create table 还是 alter table 语句,都是原样记录,甚至于连空格都不变。
扩展
DML(data manipulation language)数据操纵语言:就是我们最经常用到的 SELECT、UPDATE、INSERT、DELETE,主要用来对数据库的数据进行一些操作。
DDL(data definition language)数据库定义语言:就是我们在创建表的时候用到的一些sql,比如说:CREATE、ALTER、DROP等。DDL主要是用在定义或改变表的结构,数据类型,表之间的链接和约束等初始化工作上。
DCL(Data Control Language)数据库控制语言:是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句
redo log
- 为了控制
redo log
的写入策略,InnoDB 提供了innodb_flush_log_at_trx_commit
参数,它有三种可能取值:- 设置为 0 的时候,表示每次事务提交时都只是把
redo log
留在redo logbuffer
中 - 设置为 1 的时候,表示每次事务提交时都将
redo log
直接持久化到磁盘 - 设置为 2 的时候,表示每次事务提交时都只是把
redo log
写到page cache
- 设置为 0 的时候,表示每次事务提交时都只是把
- InnoDB有一个后台线程,每隔1秒就会把
redo log buffer
中的日志调用write
写到文件系统的page cache
,然后调用fsync
持久化到磁盘。 双1
即binary log
中sync_binlog
参数和redo log
中innodb_flush_log_at_trx_commit
同时设置为1
- 为了控制
undo log
中继日志:reley log,主从复制架构中,从服务器用于保存从主服务器的二进制日志中读取到的事件
二、使用
查看日志开启状态
show variables like 'log_%';
开启binlog
- vim my.cnf(通过brew默认安装)
1
2
3log_bin=ON
log_bin_basename=/usr/local/var/mysql/mysql-bin
log_bin_index=/usr/local/var/mysql/mysql-bin.index- 报错:
ERROR] You have enabled the binary log, but you haven't provided the mandatory server-id. Please refer to the proper server start-up parameters documentation
- vim my.cnf,加入并重启
1 | server-id=1 |
- 报错
unknown variable 'log_bin_basename=/usr/local/var/mysql/mysql-bin'
,教程比较老了,直接看官网,然后重启
1 | server-id=1 |
binlog_format有三种格式
Row格式:日志中会记录成每一行数据被修改的形式,然后在 slave 端再对相同的数据进行修改。
优点:在row模式下,bin-log中可以不记录执行的SQL语句的上下文相关的信息,仅仅只需要记录那一条记录被修改了,修改成什么样了。所以row的日志内容会非常清楚的记录下每一行数据修改的细节,非常容易理解。它不会出现某些特定情况下的存储过程、
function
、trigger
的调用和触发无法被正确复制的问题。
缺点:在row模式下,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如有这样一条
update
语句:UPDATE product SET owner_member_id = 'b' WHERE owner_member_id = 'a';
,执行之后日志中记录的不是这条update语句所对应的事件(MySQL以事件的形式来记录bin-log日志),而是这条语句所更新的每一条记录的变化情况,这样就记录成很多条记录被更新的很多个事件,因此bin-log日志的量就会很大。尤其是当执行alter table
之类的语句的时候产生的日志量是惊人的。
Statement格式:每一条会修改数据的SQL都会记录到master的
bin-log
中,slave 在复制的时候SQL进程会解析成和原来master端执行过的相同的SQL再次执行。
优点:在statement模式下,首先就是解决了row模式的缺点,不需要记录每一行数据的变化,减少了bin-log日志量,节省
I/O
以及存储资源,提高性能。因为他只需要记录在master
上所执行的语句的细节,以及执行语句时候的上下文的信息。
缺点:在
statement
模式下,由于他是记录的执行语句,所以为了让这些语句在slave
端也能正确执行,必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在slave
端杯执行的时候能够得到和在master
端执行时候相同的结果。在statement
中目前已经发现的就有不少情况会造成MySQL
的复制出现问题,主要是修改数据的时候使用了某些特定的函数或者功能的时候会出现,比如sleep()
函数在有些版本中就不能被正确复制,在存储过程中使用了last_insert_id()
函数可能会使slave
和master
上得到不一致的id等等。由于row是基于每一行来记录的变化,所以不会出现类似的问题。
Mixed格式:从
5.1.8
版本开始MySQL提供了前两种模式的结合。在Mixed模式下MySQL
会根据执行的每一条具体的SQL
语句来区分对待记录的日志形式,也就是在statement
和row
之间选择一种。
新版本中的statment还是和以前一样仅仅记录执行的语句,新版本的MySQL中对row模式也被做了优化,并不是所有的修改都会以row模式来记录,比如遇到表结构变更的时候就会以statement模式来记录,如果
SQL
语句确实就是update
或者delete
等修改数据的语句,那么还是会记录所有行的变更。
查看所有binlog日志列表
show master logs;
清空所有binlog
reset master;
查看binlog内容
/usr/local/Cellar/mysql@5.7/5.7.32/bin/mysqlbinlog /usr/local/var/mysql/mysql-bin.000001
1 | /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; |
- MySQL以事件event的形式来记录binlog,日志查看binlog event
show binlog events in 'mysql-bin.000001'\G;
1 | *************************** 1. row *************************** |
- 控制binlog落盘参数
- sync_binlog
- innodb_flush_log_at_trx_commit