系列
MySQL实战45讲阅读笔记-MySQL入门
MySQL实战45讲阅读笔记-日志
MySQL实战45讲阅读笔记-锁
MySQL实战45讲阅读笔记-索引
MySQL实战45讲阅读笔记-MVCC
日志系统在任何系统都是很重要的,MySQL也不例外;MySQL日志系统主要分为下面这几类
- 错误日志(error log)
- 通用日志(general log)
- 慢查询日志(slow query log)
- 重做日志(redo log)
- 二进制日志(bin log)
- 回滚日志(undo log)
其中redolog和undolog是属于innodb引擎的专有日志,其他的都是MySQL级别的日志;下面是各个日志的功能和基本用法
错误日志(Error Log)
错误日志是默认开启的,在配置文件中log-error=/var/log/mysqld.log
可以看到日志所在位置,且是不允许关闭的;
一般是记录在MySQL启动和关闭时一些日志信息和运行过程中比较严重的错误日志,但不光只有错误日志还有一些其他的信息如innodb引擎状态的改变信息等;
可以使用下面的语句
mysqladmin flush-logs -u[username] -p
或登陆到mysql客户端里面flush logs
来刷新日志,在5.5.7之前的版本使用该命令后会自动备份一份错误日志文件名+_old
的新文件后重新创建一个新的错误日志,但是在5.7.25的版本使用这个命令貌似不会刷新日志文件;
通用日志(General Log)
通用日志记录了所有的SQL语句,默认是不开启的,因为线上可能会造成大量的日志记录,可以通过在配置文件添加general_log=1|0
或者在客户端运行SET [GLOBAL] general_log=[ON|OFF]
来开启或者关闭;虽然不推荐一直打开,但是可以通过短期的开启来查找高峰期内哪些SQL语句使用频率最频繁,同时General log也会记录所有的DDL语句;
慢查询日志(Slow Query Log)
顾名思义就是用来记录所有执行时间大于指定时间的SQL语句,可以通过在配置文件添加slow_query_log=on
或者在客户端使用SET GLOBAL slow_query_log = on
来开启,同时参数long_query_time
是用来控制是否记录到日志里面的临界值,比如SET long_query_time = 1
后所有时间大于1s的语句都会记录下来;同样开启慢查询日志会带来一点的性能损耗,但是无伤大雅,可以选择在对数据库或者程序优化期间打开一段时间;
- 相关参数
show global variables like '%log%'
----------------------------------------
slow_query_log ON #是否开启状态
slow_query_log_file /var/lib/mysql/VM_16_3_centos-slow.log #日志位置
log_queries_not_using_indexes ON #记录没有使用索引的SQL语句
long_query_time 1 #大于该时间都需要记录
----------------------------------------
RedoLog
Innodb的WAL机制核心就是日志先行,更新任何数据前必须先在日志记录,这是保证Innodb在宕机后可以恢复没有落盘的数据,实现事务的持久性;redo log默认在磁盘上由两个名为ib_logfile0和ib_logfile1(默认是两个)的文件物理表示,可以通过修改innodb_log_files_in_group
调整redolog文件的个数,innodb_log_file_size
控制着每个file文件的大小;
redolog写文件是从头开始写到末尾然后又回到头部循环写,write pos是当前写的位置,checkpoint是当前需要擦除的位置,两个之间的距离表示可供写入的大小;
为什么需要Redo log
Innodb buffer pool
内部储存的是数据页,当修改innodb表上某行数据时,如果该行不在内存中则需要将该行所在的数据页从磁盘读入到内存去,然后在内存中更新该行,现在内存中的数据页与磁盘中就不一致了,把这种内存数据页与磁盘数据页不一致的数据页称为脏页(dirty page),DB需要把脏页数据写入磁盘,但是如果每一次更新数据就会带来一次磁盘操作的话那么机器肯定撑不住;所以说mysql会把标记为脏页的数据页储存在一个flush list
里面,用一个专门的后台线程定时刷脏;那么如果在mysql还没有刷脏的时候数据库挂了怎么办呢,所以就需要redo log;
Redo Log的意义
事务将数据的变更写到内存页中同时记录到redolog里面,然后提交就算这个事务已经结束,期间只有一次磁盘IO,效率远比比事务将数据直接写到磁盘里面要高的多,并且redolog写到磁盘可以看作是顺序IO,所以redolog能提升性能的一个方面就在于将对磁盘的随机IO写转换成顺序IO;且事务因为宕机可能会丢失,redolog还有一个作用就是保证事务的持久性;
内存页中数据和磁盘的不一致,把这种数据页称为脏页,反之称为干净页,把内存数据写入磁盘的操作称为刷脏页(flush);
-
Innodb的刷脏策略
Innodb的刷脏速度是根据脏页比例和redolog的写盘速度来决定的,innodb_max_dirty_pages_pct
参数表示innodb最大的脏页比例
innodb_max_dirty_pages_pct 75.00 #默认比例为75%
innodb会根据当前的脏页比例和innodb_max_dirty_pages_pct
计算出一个因子A;
innodb每次写入日志都会有一个序号,当前的序号和checkpoint对应的序号(LSN)之间差值记做N,然后通过N计算出因子B;
取A和B之间的最大值,最后根据innodb_io_capacity
来计算出刷脏速度;
innodb_io_capacity
是用来调整刷脏页数据的参数,默认为200单位是页,可以设置为当前机器的IOPS;
刷脏页次数太频繁的话可能会造成原本很快的SQL语句速度降低,还有SQL语句需要等待刷脏结束后才能执行的也是影响的一大因素,比如redolog写满了需要将checkpoint往前推,就必须要进行flush才能执行更新语句;所以需要合理的设置innodb_io_capacity
,可以通过观察脏页比例来调整
select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty';
select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
select @a/@b;
这个脏页比例最好经常接近设置的innodb_max_dirty_pages_pct
值;
redo log的写入机制
redo log
写入前是先写入到redo log buffer
,buffer中的内容是不会持久化到磁盘中,如果写buffer的过程中异常退出了会丢失这部分日志,但是这部分的事务是没有提交的所有丢失的影响也不大;
innodb_flush_log_at_trx_commit
参数控制着redo log的写入策略:
- 0: 表示每次事务提交时都只是把 redo log 留在 redo log buffer中,然后每秒后台线程使用fsync写入磁盘;
- 1:表示每次事务提交时都使用fsync将 redo log 直接持久化到磁盘;
- 2:表示每次事务提交时都只是把 redo log 写到 page cache中,然后每秒后台线程使用fsync写入磁盘;
Innodb中有一个后台线程每隔1s会把redo log buffer中的日志write到文件系统的page cache中,然后使用fsync持久化到磁盘里面;
所有线程都共用一个buffer,这表示其他线程可能会把当前线程没写完的日志一起fsync到磁盘中;如果buffer占用的空间达到innodb_log_buffer_size
的一半时后台线程同样也会把buffer内容写到page cache里面;
innodb_flush_neighbors
当刷一个脏页的时候发现旁边的数据页同样是一个脏页,那么innodb就会顺带把旁边一起刷了,并且还会检测旁边的旁边是不是脏页。。。默认值为1就是启用这种机制,0的话表示只刷自己的;
这种机制的意义在于将随机IO转换成顺序IO,但是对于SSD来说提升性能的意义可能没有机械硬盘大;
BinLog
Binlog是一种二进制日志,储存MySQL的所有DDL和DML语句(除select语句之外),可以说数据备份和主备同步就是依赖binlog来实现的;Redo log属于innodb引擎才有的日志,而binlog是mysql server层持有的二进制日志,属于逻辑日志;
Redolog是循环写,空间是固定的,而binlog则是写完一个文件后可以切换到下一个文件继续写下去,没有大小的限制(受限于磁盘空间),单个binlog文件大小由参数max_binlog_size
控制;
启用binlog需要在配置文件加上
log-bin=mysql-bin
binlog_format=row|statement|mixed
server-id=1
binlog_format共有三种模式:
row - 记录被修改行的所有数据
- 优点:能够保证在复制过程中数据的准确性
- 缺点:会产生大量的日志数据,尤其是alter table的时候会让日志暴涨
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8;
BEGIN;
#Table_map: `Test`.`ss` mapped to number 113
#Write_rows: table id 113 flags: STMT_END_F
BINLOG '
F7UZXRMBAAAALgAAAJgMAAAAAHEAAAAAAAEABFRlc3QAAnNzAAIDAwACngizFg==
F7UZXR4BAAAALAAAAMQMAAAAAHEAAAAAAAEAAgAC//wDAAAAAQAAAMcZh7k='/*!*/;
### INSERT INTO `Test`.`ss`
### SET
### @1=3 /* INT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
#Xid = 159
COMMIT;
statement - 每一条被修改数据的sql都会记录到master的bin-log中,slave在复制的时候sql会解析成和原来master端执行过的相同的sql再次执行;
- 优点:不需要记录每一条SQL语句与每行的数据变化,这样子binlog的日志也会比较少,减少了磁盘IO,提高性能。
- 缺点:在某些情况下会导致master-slave中的数据不一致(触发器,一些函数如sleep()等会出现问题
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
#Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1561965696/*!*/;
BEGIN/;
#Intvar
SET INSERT_ID=1/*!*/;
#Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1561965696/*!*/;
insert into ss(`value`) values(1)/;
#Xid = 129
COMMIT/*!*/;
mixed - 混合模式,即是row和statement模式的混合,对于可能会产生复制数据不一致的SQL会以row模式记录,反之则用statement模式;
binlog的写入机制
事务执行过程中先把日志写到binlog cache
中,事务提交的时候再把binlog cache写到binlog文件中;一个事务的binlog是不可能拆开写的,所以无论事务多大也要保证一次性写入,如果单个事务过大,可能会造成一个binlog文件写不下需要跨文件处理,这是非常影响性能的;
MySQL给每个线程分配一块binlog cache
,大小由参数binlog_cache_size
控制,如果事务大过这块内存,则会存在磁盘的临时文件中;
虽然每个线程都有自己的cache,但是是共用一份binlog文件,当事务commit的时候会把cache/临时文件中的内容写(write)到文件系统的page cache
中,文件系统再通过fsync持久化到磁盘中;
- sync_binlog
0:表示每次提交事务只write不fsync,性能最好;
1:表示每次提交事务都会write和fsync,性能最不好但是安全性最高;
N(>1):表示每次提交事务都write,但是积累到n个事务才fsync;
sync_binlog
和innodb_flush_log_at_trx_commit
都设置为1是保证crash safe的基础要求,俗称双1配置;
当sync_binlog设置为1的时候每次事务提交都会经历redolog和binlog的刷盘,意味着对TPS性能造成极大的影响,MySQL有个叫做组提交
的机制来优化binlog和redolog的提交;
组提交(group commit)
组提交的基本思想是尽量积攒足够多的日志然后一起fsync;
在redolog的prepare
阶段把日志从redolog buffer
中写入到fs page cache
后不是立即使用fsync刷盘,而是等待binlog write
之后再进行redolog的fsync;在并发条件下可能在binlog-write或者redolog prepare-fsync阶段积攒更多的redolog和binlog日志一起fsync,从而提升性能;相关参数
binlog_group_commit_sync_delay
:表示binlog事务提交后等待延迟多少时间才fsync,默认0,单位为微秒;
binlog_group_commit_sync_no_delay_count
:表示binlog事务要积攒多少个才fsync,必须要上面不为0才有效;
两者是或的关系,只要有一条满足就会使用fsync写到磁盘;为什么WAL机制能提升性能
redo log和binlog能提升性能的地方在于把大量的随机写转成顺序写,且还有组提交优化能有效降低磁盘的IOPS消耗;为什么redolog buffer是全局的但是binlog cache是每个线程自己维护的
因为为了保证binlog事务的完整性,一个事务在binlog是连续写的,等整个事务完成以后再写入文件,而redolog buffer里面事务没有这个要求,相反如果并行事务提交时是允许该事务在redolog buffer中只写入一半就可以被其他事务顺带写入磁盘中;
UndoLog
Undo log的存在保证了事务的原子性,MVCC就是依赖它来实现,当对任何行做了修改的时候都会在undo log
里面记录,大量的undo log构成行的历史版本记录,在需要的时候可以回退(rollback)到任何版本;
- 储存空间
innodb_undo_tablespaces
参数控制undo log日志独立表空间的数量,取值范围0-128,0表示不启用独立的undo log表空间,而是储存在ibdata文件中,一旦实例创建后就不能再修改这个配置;启用后每个文件默认大小为10M;