30.6. MySQL并发控制,加锁和事务,隔离级别,日志等

点点圈 提交于 2020-04-02 12:12:18

并发控制

锁粒度:
表级锁
行级锁
锁:
读锁:共享锁,只读不可写(包括自己当前用户和当前事务) ,多个读互不阻塞
写锁:独占锁,排它锁,写锁会阻塞其它事务(不包括当前事务)的读和它锁
实现
存储引擎:自行实现其锁策略和锁粒度
服务器级:实现了锁,表级锁,用户可显式请求
分类:
隐式锁:由存储引擎自动施加锁
显式锁:用户手动请求

锁策略:在锁粒度及数据安全性寻求的平衡机制
显式使用锁

  1. LOCK TABLES 加锁
    lock tables tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...
    lock_type: READ ,WRITE
  2. UNLOCK TABLES 解锁
  3. FLUSH TABLES [tb_name[,...]] [WITH READ LOCK]
    关闭所有正在打开的表,同时清除掉查询缓存以及准备好的语句缓存,
    如果加上with read lock 选项的话,它代表关闭所有正在打开的表并加上全局锁(不清除缓存了),通常在备份前加全局读锁
  4. SELECT clause [FOR UPDATE | LOCK IN SHARE MODE]
    查询时加写或读锁

注意点1(加锁):

  1. 注意,读锁加到表上之后,此表将只能读,不能进行其他任何操作。而且加了读锁的这个session终端也不能对它进行读read以外其他操作了,必须要unlock table命令之后才可以进行其他操作。
    • 但是如果在此session上加了一个read锁之后,再在此session上对其他的一个或多个表再继续加read锁(没有unlock tables),则经过测试以及查看lock的帮助得知,之前加的这个锁将会被清除掉,只有最后一个锁会生效。
    • 对于其他的session终端来说,只有最后一个表相当于被加了read锁,之前在原来的session被加锁的表的锁都会消失掉,和没加锁效果一样。而对于原来的session来说也是这样,只不过经过测试,如果想要访问之前被加过锁但是掉锁的表的时候,它会报错(不论是select命令还是其他命令都会报错,其他终端session上不会报错)
    • 此时在原session上只有unlock table 之后才可继续访问之前的被加锁但是掉锁的表而不报错。
    • 经过测试 lock table TABNAME write 的结果和上面read的效果相同,只不过它俩限制的内容不一样而已,上面说的现象都是lock tables这个命令的效果,和加读锁read还是写锁write无关;
    • 还有一点要注意的是,如果在当前session中对表加了锁之后,当前session终端上退出了mysql的连接(注意不是关闭了终端,就是当前终端上退出数据库的连接,当然关闭终端更加暴力),则这个锁就立即失效了,其他的终端session上就可以继续进行正常访问数据库了。(同时加锁也是立即生效的,而且这个锁都是全局立即生效的,注意这个效果和之前的global变量不同:global变量虽然也是全局的,但它必须重连session才能生效,这个加锁解锁都是命令一执行则所有连接的session不用重新连接就全局直接立即生效了。
  2. flush tables with read lock:这个命令和lock table还是有区别的,而且有一点非常重要
    • 当在一个session中,先用了lock table命令锁了自己想要锁定的表,此时如果在再当前session开启事务,则注意,这些用命令lock table命令被锁的表上面的锁全部都会被释放,就相当于执行了unlock tables,同时,其他session之前不能执行的操作或者正在执行但是阻塞的操作(比如这边session开了读锁,另外的session要插入数据但是卡住,而此时这边的session又开了事务,则另外的session中的这个插入命令就直接执行并成功了,而且能够瞬间被这边的事务看到,事务级别为可重复读的级别。估计是因为测试用的插入命令就一条执行很快,如果插入修改等命令占用时间过多的话可能在这边事务中就看不到那么多的内容了)
    • 而flush tables with read lock命令则不会受到事务开启的影响,因为它不含table lock。开启事务之后,用这个命令执行的全局锁仍然会生效。不过当然,如果当前连接到数据库的session退出,这个全局锁也会失效。也可以手动unlock tables取消这个全局锁。
    • 更详细的说明查看官方文档
      https://dev.mysql.com/doc/refman/5.7/en/flush.html#flush-tables-with-read-lock
    • 下面三段话的解释说明,(结合示例):
FLUSH TABLES WITH READ LOCK acquires a global read lock rather than table locks, so it is not subject to the same behavior as LOCK TABLES and UNLOCK TABLES with respect to table locking and implicit commits:

UNLOCK TABLES implicitly commits any active transaction only if any tables currently have been locked with LOCK TABLES. The commit does not occur for UNLOCK TABLES following FLUSH TABLES WITH READ LOCK because the latter statement does not acquire table locks.

Beginning a transaction causes table locks acquired with LOCK TABLES to be released, as though you had executed UNLOCK TABLES. Beginning a transaction does not release a global read lock acquired with FLUSH TABLES WITH READ LOCK.

注意:unlock tables 命令只能解当前session加的锁

  1. flush不受到lock命令的影响,前者得到的锁和后者得到的锁不一样:
  2. unlock tables命令会把当前session中的事务给提交commit了,因为它隐性的执行了commit命令,注意不过有个前提就是数据库中有表被lock tables 命令锁住了,此时执行unlock tables才会commit,如果没有表被锁,执行unlock tables命令相当于无效,没有执行任何操作。
    • 注意上面已经说过了,开启事务会把开始事务之前的table lock给解开,因此1中的情况指的是开启事务之后,再次进行了lock table的命令进行了加锁(不论是给哪个表加锁都一样),此时如果事务没有完成但是进行了unlock tables命令操作,则它相当于即进行了解锁,但会也把当前事务给提交commit了,(注意unlock提交之后再进行的操作就不在之前的事务内了)这点及其要注意。
    • 附加:如果对表加了写锁,若没有对表进行更改,其他的session还是能用select查看此表的,因为用了缓存,缓存验证表灭有改变,其他session还能查看。但是如果当前session对表进行修改了,则缓存验证不通过,其他session就会卡住无法对表进行包括查看之内的任何命令了。
  3. 对于flush tables加的锁,则unlock tables 虽然能够解锁,但是不会把事务给提交了。
    • 注意 lock tables和flush tables加锁经过测试不能够同时存在
    • 比如先flush tables(不论是事务前还是事务后),因为它会加了全局读锁之后就无法再用lock table命令给表加锁了。
    • 再比如,先lock tables之后,然后再flush tables加全局锁,它会提示Can't execute the given command because you have active locked tables or an active transaction。
  4. 一些结果的例子:
    • 先lock锁再开启事务,则lock锁直接无效
    • 先事务,再开启lock锁,锁有效,但是如果在当前的客户端中解锁,则会一并把事务给提交commit了(注意不是回滚),事务就结束了。
    • flush的加锁全局有效,此时开启事务没有意义(只能读),它的功能最好之久就是仅仅用于备份
    • 根据上面操作得知,要对表进行独占操作,先开启事务,再开启写锁,顺序不能反
  5. 还有一点就是,在session的事务中,一旦session断开(quit数据库或直接关闭窗口),如果加了lock锁 则相当于此session的执行了unlock table命令,但因为unlock tables它会隐性的执行commit命令 ,会将锁全部解开并自动提交事务,因此会让这个session断开之间的事务的已经进行的操作给commit了,这点要注意小心
    但是如果此session并未加锁,则session断开则相当于当前事务执行了rollback操作。

事务

事务Transactions:一组原子性的SQL语句,或一个独立工作单元
事务日志:记录事务信息,实现undo,redo等故障恢复功能

  • ACID特性:
    1. A:atomicity原子性;整个事务中的所有操作要么全部成功执行,要么全部失败后回滚
    2. C:consistency一致性;数据库总是从一个一致性状态转换为另一个一致性状态
    3. I:Isolation隔离性;一个事务所做出的操作在提交之前,是不能为其它事务所见;隔离有多种隔离级别,实现并发
    4. D:durability持久性;一旦事务提交,其所做的修改会永久保存于数据库中

事务的生命周期

事务隔离级别

  • 事务隔离级别:从上至下严格程度递增
    1. READ UNCOMMITTED 可读取到未提交数据,产生脏读
    2. READ COMMITTED 可读取到提交数据,但未提交数据不可读,产生不可重复读,即可读取到多个提交数据,导致每次读取数据不一致
    3. REPEATABLE READ 可重复读,多次读取数据都一致,产生幻读,即读取过程中,即使有其它提交的事务修改数据,仍只能读取到未修改前的旧数据。此为MySQL默认设置
    4. SERIALIZABILE 可串行化,未提交的读事务阻塞修改事务,或者未提交的修改事务阻塞读事务。导致并发性能差
  • MVCC: 多版本并发控制,和事务级别相关

事务的相关操作

  1. 启动事务:
    BEGIN;
    BEGIN WORK;
    START TRANSACTION;
  2. 结束事务:
    COMMIT:提交
    ROLLBACK: 回滚
    注意:只有事务型存储引擎中的DML语句方能支持此类操作,比如innodb。(myisam不支持)
  3. 自动提交:set autocommit={1|0}
    默认为1,为0时设为非自动提交
    建议:显式请求和提交事务,而不要使用“自动提交”功能
  4. 事务支持保存点:savepoint
    SAVEPOINT identifier;
    ROLLBACK [WORK] TO [SAVEPOINT] identifier;
    RELEASE SAVEPOINT identifier;

指定事务隔离级别:

服务器变量tx_isolation指定,默认为REPEATABLE-READ,可在GLOBAL和
SESSION级进行设置

SET tx_isolation=''
READ-UNCOMMITTED
READ-COMMITTED
REPEATABLE-READ
SERIALIZABLE
也可在服务器选项中指定,重启服务自动生效
vim /etc/my.cnf
[mysqld]
transaction-isolation=SERIALIZABLE

  • 注:mysql默认的事务级别(可重复读,也叫幻读)很符合生产要求,一般不需要修改

并发控制

  1. 死锁:
    两个或多个事务在同一资源相互占用,并请求锁定对方占用的资源的状态
  2. 事务日志:
    事务日志的写入类型为“追加”,因此其操作为“顺序IO” ;通常也被称为:预写式日志 write ahead logging
    事务日志文件: ib_logfile0, ib_logfile1

注意点2(事务):

  1. 事务对于数据库中的数据操作过程:开启事务之后,首先将事务读入内存之中,然后对它进行的各种操作会同步写入事务日志中,此时因为并未写入真正的数据存放文件位置内,因此可以进行回滚等操作。等到事务内的所有操作全部完成并提交之后,数据此时才真正的修改了。
    • 如果在这个过程中出现故障或者其他问题,则服务器下次正常重启(也有可能是客户端断开了未提交事务)的时候,事务日志发现上一次进行的事务并未完成,则它就会对这个事务已经进行的操作undo,进行rollback回滚操作。
    • 而如果重启之后事务日志发现上一次的事务的操作已经在内存中处理完毕了(事务完整并且提交结束了),但是对于数据的修改还未真正进行或者说仅仅执行了一部分,则事务日志就会对这个事务进行redo操作,重新执行事务。
  2. 事务的原子性和一致性:一个或者多个事务如果在执行过程中中断,则下次机器或者服务正常启动的时候,会先对事务日志进行查询处于执行一半过程中的事务的操作将会回滚被撤销掉,而那些之前完成的事务(并未写入磁盘)则将会写入到磁盘上,这样就能保证事务操作的事件的原子性,要么做完事务内的这一系列操作,要么这些操作一个都不做。
  3. 注意,Mysql数据库默认就自动提交,每一条单独命令都是一个独立的事务,每执行一条语句,就相当于执行了 ‘语句 ;commit;’ 的隐性2个操作(DDL语句更特殊,它不是相当于执行,它就是隐性跟着commit语句并执行它,下面有介绍)。
    • 由此原因,在之前进行的各种测试中,因为没有使用显式事务(手动开启事务),因此对数据库进行的何种操作都相当于单条命令事务,只要一执行就相当于提交了会立即生效就能够在其他的session中看到结果(比如创建数据库,创建表这些DDL语言,以及DML语言增加删除记录等等,都会立即生效被其他session看到)
    • 可以通过修改配置或变量autocommit为off来关闭自动提交事务
    • 但如果手动开启了显式事务,则在这个显式事务内的命令就不会自动提交了,必须手动commit提交(除了DDL语句之外)。
    • 但是注意了DDL语言(create,drop,alter),就算是在事务中,比如说手动begin开启事务或者说把autocommit设置为off之后,它的结果也会立即生效,感觉就像直接就commit了DDL语句,即使当前是在事务内;而且感觉真的没错,经过测试,只要进行了DDL语句的命令,它后面就隐性地真的执行了commit命令并结束了当前事务,后面的命令就不在当前事务内了(如果mysql按照默认的配置自动提交,则此时后面执行DML的操作也就是单命令事务了,也就会立即生效并在其他session可见了),
    • 由上面DDL的特殊性可知,在事务中前面执行的DML语句就算没有commit或者rollback,只要后面再跟上一个DDL语句就会把这个整个事务给提交了,很容易造成前面不想提交的DML语句被提交并且永久生效了。因此在事务中不能混用DML和DDL。
    • 还有一点就是手动开启事务begin会把之前加的table锁解开,但是autocommit模式下,如果不手动开启事务,虽然执行一条命令自动提交了(看起来像执行了'begin 命令 commit'),但是这个隐性的begin事务不像显式的手动begin开启事务,它并不会把lock table的锁给解开,所以放心使用
  4. 一个事务的结束用commit或者rollback命令,不论是这两个的哪一个命令被执行了,都会结束当前事务;
    • 注意执行了上面两个命令之后,当前事务就结束了,在mysql的默认配置下(自动提交),如果之后没有再手动开启事务的话,则再进行的操作就相当于自动提交的单条事务了(相当于重新连接到mysql数据库时的初始状态,没有手动开启任何显式事务),而这样就会在其他session中可见到立即生效的结果。
    • 还有要注意,如果在事务中用了savepoint 保存点, 利用rollback to savepoint_name命令,并没有结束当前事务,后面的命令仍然在事务中执行,只有最后遇到commit或者rollback命令才会结束事务并提交或回滚。注意它和rollback的区别:后者直接结束当前事务。
  5. 注意事务的隔离级别的作用范围是两个不同的事务之间,比如两个新开的session都手动开启了begin显式事务,这两个事务处于隔离级别中;而如果没有开启begin显式事务的话,比如新开两个session但没有输入begin,则不属于隔离级别作用范围。
    • 注意事务隔离级别的变量和服务器选项不相同。
    • 注意在事务之内的查询select命令不会被缓存到查询缓存中,只会增加not_cache中的数量(如果能存进去则其他session和事务就可以查询到了,就起不到隔离级别的效果了)
  6. 在可重复读的隔离级别中:一个session的一个事务提交commit并结束之后,然后利用了select 命令查看了一下。此时如果在另外的事务中用相同的select命令查看,则这一次查看经过对查询缓存的status变量分析得知:
    - 第一点:首先我们要知道,查询缓存的缓存内容,在连接到数据库的任何一个session中,不论是在开启的显性事务内还是没有开启事务,只要对表进行了DML语句(经测试DDL语句不影响),则此mysql数据库之前的所有缓存过的SQL语句queries_in_cache都会被清空为0(不修改不会清空)(不过那些统计的变量不会被清空)。
    • 还有一点就是,在一个事务内的进行的select语句查询操作,如果在所有的session开启事务之后,所有的session都没有进行过DML语句,这在这个事务内的select的SQL语句也会被记录到查询缓存中,不过这个查询缓存是相对于总的数据库来说的,和这个事务无关。可以这样说,标准状况下,一个事务内的select查询语句本来是不会被记录到查询缓存中的,事务内的select语句只会增加not-cached的数量,但在这里会被记录到查询缓存queries_in_cache中是因为这个事务虽然begin手动开启了,但是并没有执行修改数据的DML操作,也就相当于没有开启事务。执行的查询操作被系统认为在事务外,所以会被记录到查询缓存中
    • 在以上两点的前提下,回到之前的问题,在开启的显性事务中第一个的select查看命令(不论在事务之前是否有完全一样的SQL语句),即使在可重复读隔离级别下,这整个事务的第一次的select查看语句都能够查看到当前表的真实数据状态。
    • 可以这样理解,也就是说,这一个事务,虽然begin开启的时间很早,但只要你没有使用selec命令进行查看,则这个事务就相当于没有开启,只有在事务内使用了查看select命令的时候,这个可重复读的事务才相当于刚刚开启,之前的其他事务即使是在这个事务的begin命令之后的操作,也能够被这第一个select命令查看到。
    • 根据上面的原因,当可重复读事务内执行select命令之后,才能把它看做是真正的开启了事务,之后的select才有幻读的隔离效果,此时如果其他的session再开启事务,然后进行操作,即使它提交并进行了查看命令,也不会再影响这个事务的查看了。这里绝不能把begin看做开启事务的起点时间。经过了各种测试,确实是这样的结果。
    • 注意可重复读级别中,上面这里的事务中的第一次select语句虽然看到当前数据库表中真正的数据,但这里真正的数据指的是已经被其他事务commit修改后的永久保存后的结果,如果其他事务还没有提交并修改数据为永久的(也就是说其他事务能够回滚),则这个select是看不到的,一定要分清楚哪些能看到,哪些不能看到。还要区分可重复读和不可重复读以及上面提到的这一点坑的区别
    • 上面是经多次测试总结下来,目前就是这样结果,更多内容更多以后再研究。
    • 如果利用事务进行备份而又不想影响到其他客户进行修改或者查询操作等,不能用flush tables的方式(当然在这里其他客户不能进行DDL操作),则就用幻读的隔离级别事务来进行这种备份操作,进去之后一定要先执行一次select命令真正开启事务再进行备份。
  7. Innodb存储引擎,事务开启之后,各个事务则会对表中正在修改(DML)的行record进行加锁操作,此时如果有两个事务想要修改已经被对方事务加锁的row记录的话(也就是对方事务之前已经对这些行进行了DML操作了,加了锁,注意这些行可以是数据库中同一个表的不同行,也可是不同表的不同行),则就会产生死锁现象(如果仅仅是一个事务想要修改被另外的事务中加锁的表中的行row的话,只是会卡住而已)。
    • 此时如果服务器发现死锁现象存在,就会将其中的一个事务进行rollback操作,仅让一个事务生效。
  8. 如果出现了一个表长期被锁导致其他用户无法操作,可用show processlist以及和kill命令来剔除锁表的用户和命令(主要是sleep的用户要踢掉,他可能就是锁表的用户,没有进行操作还连着数据库)
  9. 事务日志是比数据写入磁盘中要先写的,也被称为预写式日志。它是按照追加的方式写入默认的两个事务日志文件中的,这两个事务日志文件默认就在存放数据库数据的目录中(可修改)。这两个(或多个)事务日志文件一个文件写满了就会写另外一个,另外一个也写满了就会重新返回来写这个文件,把之前的日志给覆盖掉。

日志

事务日志 transaction log
错误日志 error log
通用日志 general log
慢查询日志 slow query log
二进制日志 binary log
中继日志 reley log

事务日志

事务日志:transaction log
事务型存储引擎自行管理和使用,建议和数据文件分开存放
redo log
undo log
Innodb事务日志相关配置:
show variables like '%innodb_log%';
innodb_log_file_size 5242880 每个日志文件大小
innodb_log_files_in_group 2 日志组成员个数
innodb_log_group_home_dir ./ 事务文件路径(可以写绝对路径)
innodb_flush_log_at_trx_commit 默认为1

  1. innodb_flush_log_at_trx_commit
    说明:设置为1,同时sync_binlog = 1表示最高级别的容错
    innodb_use_global_flush_log_at_trx_commit的值确定是否可以使用SET语句重置此变量
    • 1默认情况下,日志缓冲区将写入日志文件,并在每次事务后执行刷新到磁盘。这是完全遵守ACID特性
    • 0提交时没有任何操作; 而是每秒执行一次日志缓冲区写入和刷新。 这样可以提供更好的性能,但服务器崩溃可以清除最后一秒的事务
    • 2每次提交后都会写入日志缓冲区,但每秒都会进行一次刷新。 性能比0略好一些,但操作系统或停电可能导致最后一秒的交易丢失
    • 3模拟MariaDB 5.5组提交(每组提交3个同步),此项MariaDB 10.0支持

事务日志优化:

错误日志

  1. 错误日志
    mysqld启动和关闭过程中输出的事件信息
    mysqld运行中产生的错误信息
    event scheduler运行一个event时产生的日志信息
    在主从复制架构中的从服务器上启动从服务器线程时产生的信息
  2. 错误日志相关配置
    SHOW GLOBAL VARIABLES LIKE 'log_error'
    错误文件路径
    log_error=/PATH/TO/LOG_ERROR_FILE
    是否记录警告信息至错误日志文件
    log_warnings=1|0 默认值1

通用日志

  1. 通用日志:记录对数据库的通用操作,包括错误的SQL语句
    文件:file,默认值
    表:table
  2. 通用日志相关设置
    general_log=ON|OFF
    general_log_file=HOSTNAME.log
    log_output=TABLE|FILE|NONE

慢查询日志

慢查询日志:记录执行查询时长超出指定时长的操作
slow_query_log=ON|OFF 开启或关闭慢查询
long_query_time=N 慢查询的阀值,单位秒
slow_query_log_file=HOSTNAME-slow.log 慢查询日志文件
log_slow_filter = admin,filesort,filesort_on_disk,full_join,full_scan,
query_cache,query_cache_miss,tmp_table,tmp_table_on_disk
上述查询类型且查询时长超过long_query_time,则记录日志
log_queries_not_using_indexes=ON 不使用索引或使用全索引扫描,不论
是否达到慢查询阀值的语句是否记录日志,默认OFF,即不记录
log_slow_rate_limit = 1 多少次查询才记录,mariadb特有
log_slow_verbosity= Query_plan,explain 记录内容
log_slow_queries = OFF 同slow_query_log 新版已废弃

二进制日志

  1. 二进制日志
    记录导致数据改变或潜在导致数据改变的SQL语句
    记录已提交的日志
    不依赖于存储引擎类型
    功能:通过“重放”日志文件中的事件来生成数据副本
    注意:建议二进制日志和数据文件分开存放
  2. 中继日志:relay log
    主从复制架构中,从服务器用于保存从主服务器的二进制日志中读取的事件

  3. 二进制日志记录格式
    • 二进制日志记录三种格式
      基于“语句”记录:statement,记录语句,默认模式
      基于“行”记录:row,记录数据,日志量较大
      混合模式:mixed, 让系统自行判定该基于哪种方式进行
  4. 格式配置
    show variables like ‘binlog_format';
  5. 二进制日志文件的构成
    有两类文件
    日志文件:mysql|mariadb-bin.文件名后缀,二进制格式
    如: mariadb-bin.000001
    索引文件:mysql|mariadb-bin.index,文本格式
  6. 二进制日志相关的服务器变量:
    • sql_log_bin=ON|OFF:是否记录二进制日志,默认ON
    • log_bin=/PATH/BIN_LOG_FILE:指定文件位置;默认OFF,表示不启用二进制日志功能,上述两项都开启才可
    • binlog_format=STATEMENT|ROW|MIXED:二进制日志记录的格式,默认STATEMENT
    • max_binlog_size=1073741824:单个二进制日志文件的最大体积,到达最大值会自动滚动,默认为1G
      说明:文件达到上限时的大小未必为指定的精确值
    • sync_binlog=1|0:设定是否启动二进制日志即时同步磁盘功能,默认0,由操作系统负责同步日志到磁盘,1的话是立即写入磁盘,虽然更加安全但是IO效率占用多,影响服务器效率
    • expire_logs_days=N:二进制日志可以自动删除的天数。 默认为0,即不自动删除
  7. 二进制日志相关配置
    • 查看mariadb自行管理使用中的二进制日志文件列表,及大小
      SHOW {BINARY | MASTER} LOGS
    • 查看使用中的二进制日志文件
      SHOW MASTER STATUS
    • 查看二进制文件中的指定内容
      SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
      show binlog events in ‘mysql-bin.000001' from 6516 limit 2,3

二进制日志工具

mysqlbinlog:二进制日志的客户端命令工具

  1. 命令格式:
    mysqlbinlog [OPTIONS] log_file…
    --start-position=# 指定开始位置
    --stop-position=#
    --start-datetime=
    --stop-datetime=
    时间格式:YYYY-MM-DD hh:mm:ss
    --base64-output[=name]
    -v -vvv :查看详细信息
    示例:
    mysqlbinlog --start-position=6787 --stop-position=7527 /var/lib/mysql/mariadb-bin.000003 -v
    mysqlbinlog --start-datetime="2018-01-30 20:30:10" --stop-datetime="2018-01-30 20:35:22" mariadb-bin.000003 -vvv

  2. 二进制日志事件的格式:
    #at 328
    #151105 16:31:40 server id 1 end_log_pos 431 Query thread_id=1 exec_time=0 error_code=0
    use mydb/!/;
    SET TIMESTAMP=1446712300/!/;
    CREATE TABLE tb1 (id int, name char(30))
    /!/;

事件发生的日期和时间:151105 16:31:40
事件发生的服务器标识:server id 1
事件的结束位置:end_log_pos 431
事件的类型:Query
事件发生时所在服务器执行此事件的线程的ID:thread_id=1
语句的时间戳与将其写入二进制文件中的时间差:exec_time=0
错误代码:error_code=0
事件内容:
GTID:Global Transaction ID,mysql5.6以mariadb10以上版本专属属性:GTID

  1. 清除指定二进制日志:
    PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }
    示例:
    PURGE BINARY LOGS TO ‘mariadb-bin.000003’;删除3之前的日志
    PURGE BINARY LOGS BEFORE '2017-01-23';
    PURGE BINARY LOGS BEFORE '2017-03-22 09:25:30';
    • 删除所有二进制日志,index文件重新记数:
      RESET MASTER [TO #];
      删除所有二进制日志文件,并重新生成日志文件,文件名从#开始记数,默认从1开始,一般是master主机第一次启动时执行,
      MariaDB10.1.6开始支持TO #
    • 切换日志文件:
      FLUSH LOGS;
      注意它会Closes and reopens any log file to which the server is writing.
      因此虽然它也能生成新的二级制文件,不过最好还是单独用命令flush binary logs;

注意点3(日志)

  1. 各种日志的位置,大小,启用的功能等等参数都有相应的变量以及服务器选项配置用于修改。
  2. 事务日志可用innodb_log_group_home_dir 选项在配置文件中修改其存放的位置,最好是把它和数据库目录分开存放,这样可以提高数据库的读写IO速度,减少IO占用。
    • 事务日志有多种把最终的修改后的事务数据写入磁盘的方式,可以用变量(选项)innodb_flush_log_at_trx_commit的值来控制。如果为提高效率,可以把它修改为2.不过注意可能有丢失事务操作的数据的风险(不过一般这种风险比较小,因为断电都有额外供电电源,只有CPU或者mysql崩了才会出现问题)
  3. 通用日志默认不启用,可以直接global变量修改general_log=on启动,默认位置也和数据库放在一起,也可以修改默认位置和名字 general_log_file
    • 通用日志除了上面的以文件方式存放,也可以以表的形式存放,修改选项log_output的值即可.如果以表的形式存放,则这个表的位置就在mysql的database下,名字就叫general_log;
  4. 慢查询日志可以查看哪些任务的查询速度很慢,再用explain重新执行这些命令,查看是否没有利用索引等,以及再根据其他的操作来判断为何这个操作速度慢(命令不合理还是数据库的表设计不合理等,或者说没有创建索引,用于甩锅,慢查询因此很重要)
    • 除了打开慢查询,调整一下时间长度,设置文件位置,还有一个选项log_queries_not_using_indexes打开,则可以让查询只要没有用索引的记录都用慢查询记录下来。以上几项比较常用。
  5. 二进制的日志只记录已经提交的事务或者说是已经完成的数据操作,而事务日志不论提交与否都会记录下来事务中进行的各种操作。
    • 二进制日志只记录数据的增删改操作,至于查询以及对服务器的配置操作(修改变量和选项等,目前所知这种操作它不记录的,因为没有对数据进行操作,可以查看官方文档来详细分析,因为这个修改配置可能会影响后面的主从服务器的同步问题,但这里暂时当做全部都不记录)都不记录。注意它会记录授权以及创建用户的操作等等,因为这些操作相当于是对information和mysql数据库的内容进行了修改。
    • 二进制日志主要用于恢复上次全备份之后到服务器崩溃之间这一段时间内的数据操作,也用来进行主从备份。因此二进制日志很重要,同时它不要和原来的数据库文件放在一起,要分开硬盘存放,也要把它进行远程主机的备份。
  6. 二级制日志基于语句形式的话会造成根据时间状况的命令执行的错误(比如now())函数等等),因此最好用基于row的方式记录二进制日志,不过这样的话也会造成二进制日志所占空间的增长速度比数据库还要快,它要比数据库的文件还要大。也可以用mixd模式来进行记录减少一点影响。不过最好是用row的格式来记录。
  7. 二进制日志有两项控制变量必须都开启才可,其中sql_log_bin是为了在mysql连接之后的session命令行中直接控制二进制是否记录用的,在某些情况下(比如主从备份,还原数据的时候等等,因为数据变了但是不想让它记录到二进制日志中)会把它关闭来进行一些设置之后再开启。
    • 另外一个log_bin选项,如果在配置文件中直接写入,它会生成二进制文件在数据库中,也可以用它自己指定二进制文件的路径位置。生产中最好就是用这个选项把二进制日志文件放到其他目录中,但是注意别忘了二进制文件目录的属主(其他日志改位置也是)。
    • 只要重启mysql服务,就会生成一个新的二进制文件(重启的时候相当于执行了flush binary logs命令)
  8. 利用mysqlbinlog查看的二进制文件的标准输出(加上-v或者不加都行,重定向导出的时候最好不加),它的结果也可以直接导出到文件中,比如起个名字叫bin.sql 这个文件也可以直接导入到数据库中进行数据的恢复操作。
    • 用这种方式的好处就是可以选取二进制文件中记录的部分操作(利用start,stop等选开始结束点,不写结束点的话就是到最后),然后进行恢复。当然也可以直接把这整个二进制文件进行导入数据库的操作,和sql的导入方式是一样的,mysql数据库也支持。
    • 到这里我们发现已经有4种文件可以直接导入了,一个是我们自己写的sql文件,里面就是各个sql语句;一个是二进制文件直接导入;一个是二进制文件部分导入,先利用mysqlbinlog查看的结果生成一个文件然后把它重定向导入;还有一个是后面的mysqldump命令生成的sql文件,它类似于自己写的sql文件,不过它是数据库的逻辑全盘备份文件,后面博客详细介绍。
  9. 二进制文件的清理操作:
    • 在进行清理操作之前可用flush binary logs新建二进制文件,然后再用purge命令清除之前的二进制文件(可以指定日期等)
    • 利用reset master [to #] 可以清除所有的binarylogs并且从数字#开始,不写默认为1,它和flush的不同之处就是它会删除掉所有的二进制文件,慎用。
    • 在不连接数据库的情况下用mysqladmin flush-binary-logs也可以切换新的binlog.

下面是配置文件中,可能要配置的设置,先放在这里以后完善详细设置:

[服务器端]
注意各个日志文件夹的属主

datadir
socket
port
safe update 开启
per table 开启
sql_mode 严格模式
服务器字符集设置utf8mb4
慢查询,时间阈值,位置,利用索引记录打开
通用日志,位置
事务日志,大小,数量,位置,开启2模式等
查询缓存开启,大小,每个字节设置等等,优化
pid-log
log-err
二进制日志,位置

[客户端]
字符集utf8mb4
socket
datadir
prompt="Date:\D Count:\c \n\U[\d] > "


下面是测试用sql语句文件,可写入文件后直接导入数据库。

create table testlog (id int auto_increment primary key,name char(10),age int default 20);

delimiter $$

create procedure  pro_testlog() 
begin  
declare i int;
set i = 1; 
while i < 100000 
do  insert into testlog(name,age) values (concat('wang',i),i); 
set i = i +1; 
end while; 
end$$

delimiter ;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!