Mysql技术内幕innodb引擎笔记

第2章 InnoDB存储引擎

2.3 InnoDB体系架构

InnoDB存储引擎有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作:

  • 维护所有进程/线程需要访问的多个内部数据结构。
  • 缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存。
  • 重做日志(redo log)缓冲。
  • ……

2.3.1 后台线程

1.Master Thread

Master Thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入

缓冲(INSERT BUFFER)、UNDO页的回收等。

2.IO Thread

在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写IO请 求,这样可以极大提高数据库的性能。而IO Thread的工作主要是负责这

些IO请求的回调(call back)处理。

通过命令SHOW ENGINE INNODB STATUS来观察InnoDB中 的IO Thread

3.Purge Thread

事务被提交后,其所使用的undo log可能不再需要,因此需要PurgeThread来回收已经使用并分配的undo页

4.Page Cleaner Thread

是将之前版本中脏页的刷新操作都放入到单独的线程中来完成。而其目的是为了减轻原Master Thread的工作及对于用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能。

2.3.2 内存

1.缓冲池

在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页“FIX”在缓冲池中。 下一次再读相同的页时,首先判断该页是否在缓冲池中。若在缓冲池 中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。

对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。这里需要注意的是,页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为 Checkpoint的机制刷新回磁盘。同样,这也是为了提高数据库的整体性能。

对于InnoDB存储引擎而言,其缓冲池的配置通过参数 innodb_buffer_pool_size来设置。

InnoDB 允许有多个缓冲池实例。每个页根据哈希值平均分配到不同缓冲池实例中。可以通过参innodb_buffer_pool_instances来进行配置,该值默认为1。

通过命令SHOW ENGINE INNODB STATUS可以观察到每个缓冲池实例对象运行的状态,还可以通过information_schema架构下的表INNODB_BUFFER_POOL_STATS来观察缓冲的状态。

2.LRU List、Free List和Flush List

通常来说,数据库中的缓冲池是通过LRU(Latest Recent Used,最近最少使用)算法来进行管理的。即最频繁使用的页在LRU列表的前 端,而最少使用的页在LRU列表的尾端。当缓冲池不能存放新读取到的 页时,将首先释放LRU列表中尾端的页。

InnoDB存储引擎对传统的LRU算法做了一些优化。在InnoDB的存储引擎中,LRU列表中还加入 了midpoint位置。新读取到的页,虽然是最新访问的页,但并不是直接 放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。这个算 法在InnoDB存储引擎下称为midpoint insertion strategy。在默认配置下, 该位置在LRU列表长度的5/8处。midpoint位置可由参数 innodb_old_blocks_pct控制

在InnoDB存储引擎中,把midpoint之后的列表称为old列表,之前的列表称为**new列表。可以简单地理解为new列表中的页都是最为活跃 的热点数据。InnoDB存储引擎引入了另一个参数来进一步管理LRU列表,这个参数是innodb_old_blocks_time,用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端**

可以通过命令SHOW ENGINE INNODB STATUS来观察LRU列表及Free列表的使用情况和运行状态。还可以通过表INNODB_BUFFER_POOL_STATS来观察缓冲池的运行状态。还可以通过表INNODB_BUFFER_PAGE_LRU来观察每个LRU列表中每个页的具体信息。

在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过 CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表。需要注意的是,脏页既存在于LRU列表中,也存在于Flush列表 中。LRU列表用来管理缓冲池中页的可用性,Flush列表用来管理将页刷新回磁盘,二者互不影响。

同LRU列表一样,Flush列表也可以通过命令SHOW ENGINE INNODB STATUS来查看,information_schema架构下并没有类似 INNODB_BUFFER_PAGE_LRU的表来显示脏页的数量及脏页的类型, 但正如前面所述的那样,脏页同样存在于LRU列表中,故用户可以通过 元数据表INNODB_BUFFER_PAGE_LRU来查看,唯一不同的是需要加 入OLDEST_MODIFICATION大于0的SQL查询条件。

3.重做日志缓冲

InnoDB存储引擎首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。重做日志缓冲一般不需要设置得很大,因为一般情况下每一秒钟会将重做日志缓冲刷新到日志文件,因此用户只需要保证每秒产生的事务量在这个缓冲大小之内即可。该值可由配置参数innodb_log_buffer_size 控制,默认为8MB

重做日志在下列三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。

  • Master Thread每一秒将重做日志缓冲刷新到重做日志文件;
  • 每个事务提交时会将重做日志缓冲刷新到重做日志文件;
  • 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件。
4.额外的内存池

2.4 Checkpoint技术

为了避免发生数据丢失的问题,当前事务数据库系统普遍都采用了Write Ahead Log策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。

Checkpoint(检查点)技术的目的是解决以下几个问题:

  • 缩短数据库的恢复时间;
  • 缓冲池不够用时,将脏页刷新到磁盘;
  • 重做日志不可用时,刷新脏页。

当数据库发生宕机时,数据库不需要重做所有的日志,因为Checkpoint前的页都已经刷新回磁盘。故数据库只需对Checkpoint后的重做日志进行恢复。这样就大大缩短了恢复的时间。

在InnoDB存储引擎内部,有两种Checkpoint,分别为:

  • Sharp Checkpoint :发生在数据库关闭时将所有的脏页都刷新回磁盘, 这是默认的工作方式,即参数innodb_fast_shutdown=1。
  • Fuzzy Checkpoint :只刷新一部分脏页

2.5 Master Thread工作方式

2.6 InnoDB关键特性

InnoDB存储引擎的关键特性包括:

  • 插入缓冲(Insert Buffer)
  • 两次写(Double Write)
  • 自适应哈希索引(Adaptive Hash Index)
  • 异步IO(Async IO)
  • 刷新邻接页(Flush Neighbor Page)

2.7 启动、关闭与恢复

在关闭时,参数innodb_fast_shutdown影响着表的存储引擎为InnoDB的行为。该参数可取值为0、1、2,默认值为1。

  • 0表示在MySQL数据库关闭时,InnoDB需要完成所有的full purgemerge insert buffer,并且将所有的脏页刷新回磁盘。这需要一些时间,有时甚至需要几个小时来完成。如果在进行InnoDB升级时,必须将这个参数调为0,然后再关闭数据库。
  • 1是参数innodb_fast_shutdown的默认值,表示不需要完成上述的 full purgemerge insert buffer操作,但是在缓冲池中的一些数据脏页还 是会刷新回磁盘。
  • 2表示不完成full purge和merge insert buffer操作,也不将缓冲池中的数据脏页写回磁盘,而是将日志都写入日志文件。这样不会有任何事务的丢失,但是下次MySQL数据库启动时,会进行恢复操作 (recovery)。

参数innodb_force_recovery影响了整个InnoDB存储引擎恢复的状况。该参数值默认为0,代表当发生需要恢复时,进行所有的恢复操作,当不能进行有效恢复时,如数据页发生了corruption,MySQL数据 库可能发生宕机(crash),并把错误写入错误日志中去。

第3章 文件

构成MySQL数据库和InnoDB存储引擎表的各种类型文件。包括:

  • 参数文件:告诉MySQL实例启动时在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内存结构的大小等设置,还会介绍各种参数的类型。
  • 日志文件:用来记录MySQL实例对某种条件做出响应时写入的文件,如错误日志文件、二进制日志文件、慢查询日志文件、查询日志文件等。
  • socket文件:当用UNIX域套接字方式进行连接时需要的文件。
  • pid文件:MySQL实例的进程ID文件。
  • MySQL表结构文件:用来存放MySQL表结构定义文件。
  • 存储引擎文件:因为MySQL表存储引擎的关系,每个存储引擎都会有自己的文件来保存各种数据。这些存储引擎真正存储了记录和索引等数据。

3.1 参数文件

在默认情况下,MySQL实例会按照一定的顺序在指定的位置进行读取,用户只需通过命令mysql --help | grep my.cnf来寻找即可。

mysql --help | grep -C 5 my.cnf

Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf 

MySQL数据库的参数文件是以文本方式进行存储的。用户可以直接通过一些常用的文本编辑软件(如vim)进行参数的修改。

3.1.1 什么是参数

可以通过命令SHOW VARIABLES查看数据库中的所有参数,也可以通过LIKE来过滤参数名。从MySQL 5.1版本开始,还可以通过information_schema架构下的GLOBAL_VARIABLES视图来进行查找。

mysql> SHOW VARIABLES like 'wait_timeout' \G;
*************************** 1. row ***************************
Variable_name: wait_timeout
        Value: 28800
1 row in set (0.00 sec)

SELECT * FROM  GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE'wait_timeout%'  limit 5 ;

#The 'INFORMATION_SCHEMA.GLOBAL_VARIABLES' feature is disabled

3.1.2 参数类型

MySQL数据库中的参数可以分为两类:

  • 动态(dynamic)参数
  • 静态(static)参数

动态参数意味着可以在MySQL实例运行中进行更改,静态参数说明在整个实例生命周期内都不得进行更改,就好像是只读(read only) 的。可以通过SET命令对动态的参数值进行修改,SET的语法如下:

SET  [global|session] system_var_name=expr 
SET  [@@global.|@@session.|@@]system_var_name=expr

-- global和session关键字,它们表明该参数的修改是基于当前会话还是整个实例的生命周期。

3.2 日志文件

日志文件记录了影响MySQL数据库的各种类型活动。MySQL数据 库中常见的日志文件有:

  • 错误日志(error log)
  • 二进制日志(binlog)
  • 慢查询日志(slow query log)
  • 查询日志(log)

3.2.1 错误日志

错误日志文件对MySQL的启动、运行、关闭过程进行了记录。 用户可以通过命令SHOW VARIABLES LIKE 'log_error' 来定位该文件。

3.2.2 慢查询日志

慢查询日志(slow log)可帮助DBA定位可能存在问题的SQL语句,从而进行SQL语句层面的优化。例如,可以在MySQL启动时设一个阈 值,将运行时间超过该值的所有SQL语句都记录到慢查询日志文件中。该阈值可以通过参数long_query_time来设置,默认值为10,代表 10秒。

在默认情况下,MySQL数据库并不启动慢查询日志,用户需要手工将这个参数设为ON:

SHOW VARIABLES LIKE 'long_query_time'\G; 

设置long_query_time后, MySQL会记录运行时间超过该值的所有SQL语句,但等于long_query_time的情况并不会被记录下。

log_queries_not_using_indexes:打开时,记录未使用索引语句。

log_throttle_queries_not_using_indexes:每分钟允许记录到slow log的且未使用索引的SQL语句次数。默认为0,表示没有限制

mysqldumpslow 命令用于分析慢查询日志。

mysqldumpslow   nh122-190-slow.log

也可以设置把日志存在在数据表(需要先创建)中。参数log_output指定了慢查询输出的格式,默认为FILE,可以将它设为TABLE,然后就可以查询mysql架构下的slow_log表了。

CREATE TABLE'slow_log'
( 
'start_time' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
, 'user_host' mediumtext NOT NULL
, 'query_time' time NOT NULL
, 'lock_time' time NOT NULL
, 'rows_sent' int(11)NOT NULL
, 'rows_examined' int(11)NOT NULL
, 'db' varchar(512)NOT NULL
, 'last_insert_id' int(11)NOT NULL
, 'insert_id' int(11)NOT NULL
, 'server_id' int(11)NOT NULL 
, 'sql_text' mediumtext NOT NULL 
)ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log';

参数log_output动态的,并且是全局的,因此用户可以在线进行修改。

SHOW VARIABLES LIKE'log_output'\G;
SET GLOBAL log_output='TABLE';
SELECT * FROM mysql.slow_log\G;

3.2.3 查询日志

查询日志记录了所有对MySQL数据库请求的信息,无论这些请求是否得到了正确的执行。默认文件名为:主机名.log。

从MySQL 5.1开始,可以将查询日志的记录放入mysql架构下的general_log表中

3.2.4 二进制日志

二进制日志(binary log)记录了对MySQL数据库执行更改的所有操作,但是不包括SELECT和SHOW这类操作,因为这类操作对数据本 身并没有修改。然而,若操作本身并没有导致数据库发生变化,那么该操作可能也会写入二进制日志。

通过命令SHOW BINLOG EVENT可以查看二进制日志。

show binlog events [IN ‘log_name’] [FROM pos] [LIMIT [offset,] row_count];

二进制日志主要有以下几种作用:

  • 恢复(recovery):某些数据的恢复需要二进制日志,例如,在一个数据库全备文件恢复后,用户可以通过二进制日志进行point-in-time的恢复。
  • 复制(replication):其原理与恢复类似,通过复制和执行二进制日志使一台远程的MySQL数据库(一般称为slave或standby)与一台MySQL数据库(一般称为master或primary)进行实时同步。
  • 审计(audit):用户可以通过二进制日志中的信息来进行审计,判断是否有对数据库进行注入的攻击。

通过配置参数log-bin[=name]可以启动二进制日志。如果不指定name,则默认二进制日志文件名为主机名,后缀名为二进制日志的序列 号,所在路径为数据库所在目录(datadir)。

show variables like'datadir';

bin_log.index为二进制的索引文 件,用来存储过往产生的二进制日志序号

以下配置文件的参数影响着二进制日志记录的信息和行为:

  • max_binlog_size :单个二进制日志文件的最大值。如果超过该值,则产生新的二进制日志文件,后缀名+1,并记录到.index

    件。默认值为 1 073 741 824(1G)

  • binlog_cache_size

  • sync_binlog

  • binlog-do-db

  • binlog-ignore-db

  • log-slave-update

  • binlog_format

3.3 套接字文件

在UNIX系统下本地连接MySQL可以采用UNIX域套接字方式,这种方式需要一个套接字(socket)文件。套接字文件可由 参数socket控制。一般在/tmp目录下,名为mysql.sock:

SHOW VARIABLES LIKE'socket'\G; 
Variable_name:socket 
Value:/tmp/mysql.sock

3.4 pid文件

当MySQL实例启动时,会将自己的进程ID写入一个文件中——该文件即为pid文件。该文件可由参数pid_file控制,默认位于数据库目录 下,文件名为主机名.pid

show variables like'pid_file'\G; 
Variable_name:pid_file 
Value:/usr/local/mysql/data/xxxx.pid

3.5 表结构定义文件

因为MySQL插件式存储引擎的体系结构的关系,MySQL数据的存储是根据表进行的,每个表都会有与之对应的文件。但不论表采用何种 存储引擎,MySQL都有一个以frm为后缀名的文件,这个文件记录了该表的表结构定义。

3.6 InnoDB存储引擎文件

本节将具体介绍与**InnoDB存储引擎**密切相关的文件,这些文件包括重做日志文件、表空间文件。

3.6.1 表空间文件

InnoDB采用将存储的数据按表空间(tablespace进行存放的设计。在默认配置下会有一个初始大小为10MB,名为ibdata1的文件。该 文件就是默认的表空间文件(tablespace file),用户可以通过参数innodb_data_file_path对其进行设置,格式如下:

innodb_data_file_path=datafle_spec1[;datafle_spec2]...

设置innodb_data_file_path参数后,所有基于InnoDB存储引擎的表的数据都会记录到该共享表空间中。若设置了参数innodb_file_per_table,则用户可以将每个基于InnoDB存储引擎的表产生一个独立表空间。独立表空间的命名规则为:表名.ibd

单独的表空间文件仅存储该表的数据、索引和插入缓冲BITMAP等信息,其余信息还是存放在默认的表空间中

3.6.2 重做日志文件

在默认情况下,在InnoDB存储引擎的数据目录下会有两个名为ib_logfile0ib_logfile1的文件。在MySQL官方手册中将其称为InnoDB存储引擎的日志文件,不过更准确的定义应该是重做日志文件redo log file)。为什么强调是重做日志文件呢?因为重做日志文件对于 InnoDB存储引擎至关重要,它们记录了对于InnoDB存储引擎的事务日志

每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组下至少有2个重做日志文件,如默认的ib_logfile0ib_logfile1。为了得到更高的可靠性,用户可以设置多个的镜像日志组mirrored log groups),将不同的文件组放在不同的磁盘上,以此提高重做日志的高可用性。在日志组中每个重做日志文件的大小一致,并以循环写入的方式运行。InnoDB存储引擎先写重做日志文件1,当达到文件的最后时,会切换至重做日志文件2,再当重做日志文件2也被写满时,会再切换到重做日志文件1中。

下列参数影响着重做日志文件的属性:

  • innodb_log_file_size
  • innodb_log_files_in_group
  • innodb_mirrored_log_groups
  • innodb_log_group_home_dir

第4章 表

4.1 索引组织表

在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table)。在InnoDB存储引 擎表中,每张表都有个主键(Primary Key),如果在创建表时没有显式地定义主键,则InnoDB存储引擎会按如下方式选择或创建主键:

  • 首先判断表中是否有非空的唯一索引(Unique NOT NULL),如果有,则该列即为主键。
  • 如果不符合上述条件,InnoDB存储引擎自动创建一个6字节大小的指针

当表中有多个非空唯一索引时,InnoDB存储引擎将选择建表时第一个定义的非空唯一索引为主键。这里需要非常注意的是,主键的选择

根据的是定义索引的顺序,而不是建表时列的顺序。SELECT _rowid可以显示表的主键的值。但是只能查看单列的唯一索引。

4.2 InnoDB逻辑存储结构

从InnoDB存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block)。

4.2.1 表空间

如果启用了innodb_file_per_table的参数,需要注意的是每张表的表空间内存放的只是数据、索引和插入缓冲Bitmap页,其他类的数据,如 回滚(undo)信息,插入缓冲索引页、系统事务信息,二次写缓冲(Double write buffer)等还是存放在原来的共享表空间内。这同时也说 明了另一个问题:即使在启用了参数innodb_file_per_table之后,共享表空间还是会不断地增加其大小。

4.2.2 段

图4-1中显示了表空间是由各个段组成的,常见的段有数据段索引段回滚段等。因为前面已经介绍过了InnoDB存储引擎表是索引组 织的(index organized),因此数据即索引,索引即数据。那么数据段即为B+树的叶子节点索引段即为B+树的非叶子节点

4.2.3 区

区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。 为了保证区中页的连续性,InnoDB存储引擎一次从磁盘申请4~5个 区。在默认情况下,InnoDB存储引擎页的大小为16KB,即一个区中一共有64个连续的页。

InnoDB 1.0.x版本开始引入压缩页,即每个页的大小可以通过参数KEY_BLOCK_SIZE设置为2K、4K、8K,因此每个区对应页的数量就

应该为512、256、128。

InnoDB 1.2.x版本新增了参数innodb_page_size,通过该参数可以将默认页的大小设置为4K、8K,但是页中的数据库不是压缩。这时区中 页的数量同样也为256、128。总之,不论页的大小怎么变化,区的大小总是为1M

4.2.4 页

同大多数数据库一样,InnoDB有页(Page)的概念(也可以称为块),页是InnoDB磁盘管理的最小单位。在InnoDB存储引擎中,默认 每个页的大小为16KB。

在InnoDB存储引擎中,常见的页类型有:

  • 数据页(B-tree Node)
  • undo页(undo Log Page)
  • 系统页(System Page)
  • 事务数据页(Transaction system Page)
  • 插入缓冲位图页(Insert Buffer Bitmap)
  • 插入缓冲空闲列表页(Insert Buffer Free List)
  • 未压缩的二进制大对象页(Uncompressed BLOB Page)
  • 压缩的二进制大对象页(compressed BLOB Page)

4.2.5 行

InnoDB存储引擎是面向行的(row-oriented),也就说数据是按行进行存放的。每个页存放的行记录也是有硬性定义的,最多允许存放 16KB/2-200行的记录,即7992行记录。

4.3 InnoDB行记录格式

4.3.3 行溢出数据

InnoDB存储引擎可以将一条记录中的某些数据存储在真正的数据页面之外。一般认为BLOB、LOB这类的大对象列类型的存储会把数据 存放在数据页面之外。但是,这个理解有点偏差,BLOB可以不将数据放在溢出页面,而且即便是VARCHAR列数据类型,依然有可能被存放 为行溢出数据。

VARCHAR(N)中的N指的是字符的长度。而文档中说明VARCHAR 类型最大支持65535,单位是字节

此外需要注意的是,MySQL官方手册中定义的65535长度是指所有VARCHAR列的长度总和,如果列的长度总和超出这个长度,依然无法创建

4.3.4 Compressed和Dynamic行记录格式

​ nnoDB 1.0.x版本开始引入了新的文件格式(file format,用户可以理解为新的页格式),以前支持的Compact和Redundant格式称为 Antelope文件格式,新的文件格式称为Barracuda文件格式。Barracuda文 件格式下拥有两种新的行记录格式:Compressed和Dynamic。 新的两种记录格式对于存放在BLOB中的数据采用了完全的行溢出的方式,如图4-5所示,在数据页中只存放20个字节的指针,实际的数 据都存放在Off Page中,而之前的Compact和Redundant两种格式会存放768个前缀字节。

4.3.5 CHAR的行结构存储

CHR(N)中的N指的是字符的长度,而不是之前版本的字节长度。也就说在不同的字符集下,CHAR类型列内部存储的可能不是定长的数据。

4.4 InnoDB数据页结构

4.5 Named File Formats机制

随着InnoDB存储引擎的发展,新的页数据结构有时用来支持新的功能特性。比如前面提到的InnoDB 1.0.x版本提供了新的页数据结构来 支持表压缩功能,完全的溢出(Off page)大变长字符类型字段的存储。这些新的页数据结构和之前版本的页并不兼容,因此从InnoDB 1.0.x版本开始,InnoDB存储引通过Named File Formats机制来解决不同版本下页结构兼容性的问题。

4.8 分区表

4.8.1 分区概述

分区功能并不是在存储引擎层完成的。

MySQL数据库支持的分区类型为水平分区,并不支持垂直分区 。此外,MySQL数据库的分区是局部分区索引,一个分区中既存放了数据又存放了索引。而全局分区是指,数据存放在各个分区中,但是所有数据的索引放在一个对象中。目前,MySQL数据库还不支持全局分区。

SHOW VARIABLES LIKE'%partition%'\G;

当前MySQL数据库支持以下几种类型的分区:

  • RANGE分区:行数据基于属于一个给定连续区间的列值被放入分区。MySQL 5.5开始支持RANGE COLUMNS的分区。
  • LIST分区:和RANGE分区类型,只是LIST分区面向的是离散的值。MySQL 5.5开始支持LIST COLUMNS的分区。
  • HASH分区:根据用户自定义的表达式的返回值来进行分区,返回值不能为负数。
  • KEY分区:根据MySQL数据库提供的哈希函数来进行分区。

无论哪种类型的分区,如果表中存在主键或唯一索引时,分区列必须是唯一索引的一个组成部分

4.8.2 分区类型

RANGE分区
CREATE TABLE t
( id INT )
ENGINE=INNDB 
PARTITION BY RANGE(id)  -- range ()的值,可以是列名,也可以是个表达式。
 (
 		PARTITION p0 VALUES LESS THAN(10), 
		PARTITION p1 VALUES LESS THAN(20),
    partition p2 values less than maxvalue  -- 不加这个,有些值是不能插入的。
 );

查看表在磁盘上的物理文件,启用分区之后,表不再由一个ibd文件组成了,而是由建立分区时的各个分区ibd文件组成,如下面的

t#P#p0.ibd,t#P#p1.ibd:

image-20220503115301448

LIST分区

LIST分区和RANGE分区非常相似,只是分区列的值是离散的。

CREATE TABLE t
( a INT
, b INT)
ENGINE=INNODB
PARTITION BY LIST(b)
( 
	PARTITION p0 VALUES IN(1,3,5,7,9), 
	PARTITION p1 VALUES IN(0,2,4,6,8) 
);

如果插入的值不在分区的定义中,MySQL数据库同样会抛出异常

HASH分区

HASH分区的目的是将数据均匀地分布到预先定义的各个分区中, 保证各分区的数据数量大致都是一样的。

CREATE TABLE t_hash
( a INT
 , b DATETIME 
)
ENGINE=InnoDB 
PARTITION BY HASH(YEAR(b))  -- HASH 参数是个返回整数的表达式。
PARTITIONS 4;  -- 分区个数。



CREATE TABLE t_hash
( a INT
 , b DATETIME 
)
ENGINE=InnoDB 
PARTITION BY LINEAR HASH(YEAR(b))  -- LINEAR 
PARTITIONS 4;  -- 分区个数。

LINEAR 分区选择步骤:

  1. 取大于分区数量4的下一个2的幂值V,V=POWER(2, CEILING(LOG(2,num)))=4;
  2. 所在分区N=YEAR(‘2010-04-01’)&(V-1)=2。
KEY分区

KEY分区和HASH分区相似,不同之处在于HASH分区使用用户定义的函数进行分区,KEY分区使用MySQL数据库提供的函数进行分区。

CREATE TABLE t_hash
( a INT
 , b DATETIME 
)
ENGINE=InnoDB 
PARTITION BY KEY(b)  -- 数据库函数
PARTITIONS 4;  -- 分区个数。
COLUMNS分区

前面介绍的RANGE、LIST、HASH和KEY这四种分区中,分区的条件是:数据必须是整型(interger),如果不是整型,那应该需要通过函数将其转化为整型,如YEAR(),TO_DAYS(),MONTH()等函数。

COLUMNS分区,可视为RANGE分区和LIST分区的一种进化。COLUMNS分区可以直接使用非整型的数据进行分区,分区根据类型直接比较而得,不需要转化为整型。

RANGE COLUMNS分区可以对多个列的值进行分区。

COLUMNS分区支持以下的数据类型:

  • 所有的整型类型,如INT、SMALLINT、TINYINT、BIGINT。 FLOAT和DECIMAL则不予支持。
  • 日期类型,如DATE和DATETIME。其余的日期类型不予支持。
  • 字符串类型,如CHAR、VARCHAR、BINARY和 VARBINARY。BLOB和TEXT类型不予支持。
CREATE TABLE t_columns_range
( a INT, b DATETIME )
ENGINE=INNODB 
PARTITION BY RANGE COLUMNS(B)   -- 注意此处语法
( 
	PARTITION p0 VALUES LESS THAN('2009-01-01')
	, PARTITION p1 VALUES LESS THAN('2010-01-01') 
);

CREATE TABLE rcx( 
  a INT
  , b INT
  , c CHAR(3)
  ,d INT 
)Engine=InnoDB 
PARTITION BY RANGE COLUMNS(a,d,c) -- RANGE 支持多列
( 
  	PARTITION p0 VALUES LESS THAN(5,10,'ggg')
 		, PARTITION p1 VALUES LESS THAN(10,20,'mmmm')
  	, PARTITION p2 VALUES LESS THAN(15,30,'sss')
  	, PARTITION p3 VALUES LESS THAN(MAXVALUE,MAXVALUE,MAXVALUE) 
);


CREATE TABLE customers_1
( first_name VARCHAR(25)
 , last_name VARCHAR(25)
 , street_1 VARCHAR(30)
 , street_2 VARCHAR(30)
 , city VARCHAR(15)
 , renewal DATE )
 PARTITION BY LIST COLUMNS(city)  -- 注意此处语法
 (
   PARTITION pRegion_1 VALUES IN('Oskarshamn','Högsby','Mönsterås')
   , PARTITION pRegion_2 VALUES IN('Vimmerby','Hultsfred','Västervik')
   , PARTITION pRegion_3 VALUES IN('Nässjö','Eksjö','Vetlanda')
   , PARTITION pRegion_4 VALUES IN('Uppvidinge','Alvesta','Växjo') 
 );

4.8.3 子分区

子分区(subpartitioning)是在分区的基础上再进行分区,有时也称这种分区为复合分区(composite partitioning)。MySQL数据库允许在 RANGELIST的分区上再进行HASHKEY的子分区。

CREATE TABLE ts(
  a INT,b DATE
) engine=innodb 
PARTITION BY RANGE(YEAR(b)) 
    SUBPARTITION BY HASH(TO_DAYS(b)) 
    SUBPARTITIONS 2    -- 指定每个分区的子分区个数
    ( 
      PARTITION p0 VALUES LESS THAN(1990), 
      PARTITION p1 VALUES LESS THAN(2000), 
      PARTITION p2 VALUES LESS THAN MAXVALUE 
    ); 

子分区的建立需要注意以下几个问题:

  • 每个子分区的数量必须相同。
  • 要在一个分区表的任何分区上使用SUBPARTITION来明确定义 任何子分区,就必须定义所有的子分区。
  • 每个SUBPARTITION子句必须包括子分区的一个名字。
  • 子分区的名字必须是唯一的

4.8.4 分区中的NULL值

MYSQL数据库的分区总是NULL值视小于任何的一个非NULL,这和MySQL数据库中处理NULL值的ORDER BY操作是一样的。因此对于不同的分区类型,MySQL数据库对于NULL值的处理也是各不相同。

  • 对于RANGE分区,如果向分区列插入了NULL值,则MySQL数据库会将该值放入最左边的分区。
  • 在LIST分区下要使用NULL值,则必须显式地指出哪个分区中放入NULL值,
  • HASH和KEY分区对于NULL的处理方式和RANGE分区、LIST分区 不一样。任何分区函数都会将含有NULL值的记录返回为0

4.8.5 分区和性能

对于使用InnoDB存储引擎作为OLTP应用的表在使用分区时应该十分小心,设计时确认数据的访问模式,否则在OLTP应用下分区可能不仅不会带来查询速度的提高,反而可能会使你的应用执行得更慢。

4.8.6 在表和分区间交换数据

MySQL 5.6开始支持ALTER TABLE…EXCHANGE PARTITION语法。该语句允许分区或子分区中的数据与另一个非分区的表中的数据进行交换。如果非分区表中的数据为空,那么相当于将分区中的数据移动到非分区表中。若分区表中的数据为空,则相当于将外部表中的数据导入到分区中。

要使用ALTER TABLE…EXCHANGE PARTITION语句,必须满足下面的条件:

  • 要交换的表需和分区表有着相同的表结构,但是表不能含有分区
  • 在非分区表中的数据必须在交换的分区定义内
  • 被交换的表中不能含有外键,或者其他的表含有对该表的外键引用
  • 用户除了需要ALTER、INSERT和CREATE权限外,还需要DROP的权限

此外,有两个小的细节需要注意:

  • 使用该语句时,不会触发交换表和被交换表上的触发器
  • AUTO_INCREMENT列将被重置

第5章 索引与算法

5.1 InnoDB存储引擎索引概述

InnoDB存储引擎支持以下几种常见的索引:

  • B+树索引
  • 全文索引
  • 哈希索引

B+树索引并不能找到一个给定键值的具体行。B+树索引能找到的只是被查找数据行所在的页。然后 数据库通过把页读入到内存,再在内存中进行查找,最后得到要查找的数据

5.2 数据结构与算法

5.2.1 二分查找法

5.2.2 二叉查找树和平衡二叉树

5.3 B+树

5.4 B+树索引

B+索引在数据库中有一个特点是高扇出性,因此在数据库中,B+树的高度一般都在2~4层,这也就是说查找某一键值的行记录时最多只需要2到4次IO。

数据库中的B+树索引可以分为聚集索引(clustered inex)和辅助索引(secondary index),但是不管是聚集还是辅助的索引,其内部都是B+树的,即高度平衡的,叶子节点存放着所有的数据。聚集索引与辅助索引不同的是,叶子节点存放的是否是一整行的信息

5.4.1 聚集索引

之前已经介绍过了,InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。

聚集索引的存储并不是物理上连续的,而是逻辑上连续的。这其中 有两点:一是前面说过的页通过双向链表链接,页按照主键的顺序排序;另一点是每个页中的记录也是通过双向链表进行维护的,物理存储上可以同样不按照主键存储。

5.4.2 辅助索引

对于辅助索引(Secondary Index,也称非聚集索引),叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点

中的索引行中还包含了一个书签(bookmark)。该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。由于InnoDB存 储引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键

辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录。举例来说,如果在一棵高度为3的辅助索引树中查找数据,那需要对这棵辅助索引树遍历3次找到指定主键,如果聚集索引树的高度同样为3,那么还需要对聚集索引树进行3次查找,最终找到一个完整的行数据所在的页,因此一共需要6次逻辑IO访问以得到最终的一个数据页。

5.5 Cardinality值

5.5.1 什么是Cardinality

并不是在所有的查询条件中出现的列都需要添加索引。

怎样查看索引是否是高选择性的呢?可以通过SHOW INDEX结果中的列Cardinality来观察。Cardinality值非常关键,表示索引中不重复记

录数量的预估值。

5.5.2 InnoDB存储引擎的Cardinality统计

因为 MySQL数据库中有各种不同的存储引擎,而每种存储引擎对于B+树索引的实现又各不相同,所以对Cardinality的统计是放在存储引擎层进行 的。数据 库对于Cardinality的统计都是通过采样Sample)的方法来完成的。

在InnoDB存储引擎中,Cardinality统计信息的更新发生在两个操作 中:INSERTUPDATE。根据前面的叙述,不可能在每次发生INSERTUPDATE时就去更新Cardinality信息,这样会增加数据库系统的负荷,同时对于大表的统计,时间上也不允许数据库这样去操作。因此, InnoDB存储引擎内部对更新Cardinality信息的策略为:

  • 表中1/16的数据已发生过变化。
  • stat_modified_counter>2 000 000 000。

5.6 B+树索引的使用

5.6.2 联合索引

联合索引是指对表上的多个列进行索引。

5.6.3 覆盖索引

覆盖索引(covering index,或称索引覆盖), 即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记****录。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息, 故其大小要远小于聚集索引,因此可以减少大量的IO操作。

5.6.4 优化器选择不使用索引的情况

于用户要选取的数据是整行信息,而 OrderID索引不能覆盖到我们要查询的信息,因此在对OrderID索引查询 到指定数据后,还需要一次书签访问来查找整行数据的信息。虽然 OrderID索引中数据是顺序存放的,但是再一次进行书签查找的数据则是无序的,因此变为了磁盘上的离散读操作。如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的蛮大一部分时(一般是20%左右),优化器会选择通过聚集索引来查找数据。因为之前已经提到过,顺序读要远远快于离散读。

5.6.5 索引提示

5.6.6 Multi-Range Read优化

第6章 锁

实际上,只有当实现本身会增加开销时,行级锁才会增加开销。InnoDB存储引擎不需要锁升级,因为一个锁和多个锁的开销是相同的。

6.1 什么是锁

在Microsoft SQL Server下,锁是一种稀有的资源,锁越多开销就越大,因此它会有锁升级。在这种情况下,行锁会升级到表锁,这时并发的性能又回到了以前

InnoDB存储引擎锁的实现和Oracle数据库非常类似,提供一致性的非锁定读、行级锁支持。行级锁没有相关额外的开销,并可以同时得到 并发性和一致性。

6.2 lock与latch

latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,latch又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。

lock的对象是事务,用来锁定的是数据库中的对象,如表、页、 行。并且一般lock的对象仅在事务commitrollback后进行释放(不同事务隔离级别释放的时间可能不同)。此外,lock,正如在大多数数据库中一样,是有死锁机制的。

对于InnoDB存储引擎中的latch,可以通过命令SHOW ENGINE INNODB MUTEX来进行查看

相对于latch的查看,lock信息就显得直观多了。用户可以通过命令 SHOW ENGINE INNODB STATUSinformation_schema架构下的表 INNODB_TRXINNODB_LOCKSINNODB_LOCK_WAITS来观察锁的信息。

6.3 InnoDB存储引擎中的锁

6.3.1 锁的类型

InnoDB存储引擎实现了如下两种标准的行级锁:

  • 共享锁(S Lock),允许事务读一行数据。
  • 排他锁(X Lock),允许事务删除或更新一行数据。

InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁 (Intention Lock)。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁,

InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类

。其支持两种意向锁:

1)意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁

2)意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁

由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫描以外的任何请求

查看锁情况

SHOW FULL PROCESSLIST
SHOW ENGINE INNODB STATUS
-- 系统表
--     查看事务信息
INNODB_TRX
--     查看锁信息
INNODB_LOCKS
--     查看事务的等待信息
INNODB_LOCK_WAITS

6.3.2 一致性非锁定读

一致性的非锁定读(consistent nonlocking read)是指InnoDB存储引 擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行DELETEUPDATE操作,这 时读取操作不会因此去等待行上锁的释放。相反地,InnoDB存储引擎 会去读取行的一个快照数据

快照数据是指该行的之前版本的数据,该实现是通过**undo来完成。而undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。此外,读取快照 数据是不需要上锁的,因为没有事务需要对历史的数据进行修改**操作。

在事务隔离级别READ COMMITTEDREPEATABLE READ(InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ事务隔离级别 下,对于快照数据,非一致性读总是读取事务(当前事务)开始时的行数据版本,保证可重复读,不会幻读。

6.3.3 一致性锁定读

InnoDB存储引擎对于SELECT语句支持 两种一致性的锁定读(locking read)操作:

  • SELECT…FOR UPDATE ,对读取的行记录加一个X锁,其他事务不能对已锁定的行加上任何锁
  • SELECT…LOCK IN SHARE MODE,对读取的行记录加一个S锁,其他事务可以向被锁定的行加S锁,但是如果加X锁,则会被阻塞。

6.3.4 自增长与锁

在InnoDB存储引擎的内存结构中,对每个含有自 增长值的表都有一个自增长计数器(auto-increment counter)

插入操作会依据这个自增长的计数器值加1赋予自增长列。这个实现方式称做AUTO-INC Locking。这种锁其实是采用一种特殊的表锁机制,为了提高插入的性能,锁不是在一个事务完成后才释放,而是在完成对自增长值插入的SQL语句后立即释放

从MySQL 5.1.22版本开始,InnoDB存储引擎中提供了一种轻量级互斥量的自增长实现机制,这种机制大大提高了自增长值插入的性能。 并且从该版本开始,InnoDB存储引擎提供了一个参数 innodb_autoinc_lock_mode来控制自增长的模式,该参数的默认值为1

参数innodb_autoinc_lock_mode以及各个设置下对自增的 影响,其总共有三个有效值可供设定,即012

在InnoDB存储引擎中,自增长值的列必须是索引,同时必须是索引的第一个列。如果不是第一个列,则MySQL数据库会抛出异常

6.3.5 外键和锁

6.4 锁的算法

6.4.1 行锁的3种算法

InnoDB存储引擎有3种行锁的算法,其分别是:

  • Record Lock:单个行记录上的锁 。总是会去锁住索引记录。
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
  • Next-Key Lock∶Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身

当查询的索引含有唯一属性时,InnoDB存储引擎会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,而不是范围。

用户可以通过以下两种方式来显式地关闭Gap Lock

  • 将事务的隔离级别设置为READ COMMITTED
  • 将参数innodb_locks_unsafe_for_binlog设置为1

6.4.2 解决Phantom Problem

在默认的事务隔离级别下,即REPEATABLE READ下,InnoDB存储引擎采用Next-Key Locking机制来避免Phantom Problem(幻像问题)。

6.5 锁问题

6.5.1 脏读

6.5.2 不可重复读

6.5.3 丢失更新

6.6 阻塞

6.7 死锁

6.8 锁升级

锁升级(Lock Escalation)是指将当前锁的粒度降低。举例来说, 数据库可以把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁。

InnoDB存储引擎不存在锁升级的问题。因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页对锁进行管理的,采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个记录,其开销通常都是一致的。

第7章 事务

ACID是以下4个词的缩写:

  • 原子性(atomicity)
  • 一致性(consistency)
  • 隔离性(isolation)
  • 持久性(durability)

7.1 认识事务

7.1.2 分类

从事务理论的角度来说,可以把事务分为以下几种类型:

  • 扁平事务(Flat Transactions)
  • 带有保存点的扁平事务(Flat Transactions with Savepoints)
  • 链事务(Chained Transactions)
  • 嵌套事务(Nested Transactions)
  • 分布式事务(Distributed Transactions)

扁平事务(Flat Transaction)是事务类型中最简单的一种,但在实际生产环境中,这可能是使用最为频繁的事务。在扁平事务中,所有操作都处于同一层次,其由BEGIN WORK开始,由COMMIT WORK或 ROLLBACK WORK结束,其间的操作是原子的,要么都执行,要么都 回滚。因此扁平事务是应用程序成为原子操作的基本组成模块。

扁平事务的主要限制是不能提交或者回滚事务的某一部分,或分几个步骤提交

带有保存点的扁平事务(Flat Transactions with Savepoint),除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务较早的一个状态。

对于扁平的事务来说,其隐式地设置了一个保存点。然而在整个事务中,只有这一个保存点,因此,回滚只能回滚到事务开始时的状态。

链事务(Chained Transaction)可视为保存点模式的一种变种。

链事务的思想是:在提交一个事务时,释放不需要的数据对象,将 必要的处理上下文隐式地传给下一个要开始的事务。注意,提交事务操 作和开始下一个事务操作将合并为一个原子操作。这意味着下一个事务 将看到上一个事务的结果,就好像在一个事务中进行的一样。

7.2 事务的实现

事务隔离性由第6章讲述的锁来实现。原子性、一致性、持久性通 过数据库的redo logundo log来完成。redo log称为重做日志,用来保证事务的原子性和持久性。undo log用来保证事务的一致性。

有的DBA或许会认为undoredo的逆过程,其实不然。redoundo 的作用都可以视为是一种恢复操作,redo恢复提交事务修改的页操作, 而undo回滚行记录到某个特定版本。因此两者记录的内容不同,redo通常是物理日志,记录的是页的物理修改操作。undo逻辑日志,根据每行记录进行记录。

7.2.1 redo

1.基本概念

重做日志用来实现事务的持久性,即事务ACID中的D。其由两部分组成:一是内存中的重做日志缓冲redo log buffer),其是易失的;二是重做日志文件redo log file),其是持久的。

InnoDB是事务的存储引擎,其通过Force Log at Commit机制实现事务的持久性,即当事务提交(COMMIT)时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT操作完成才算完成。这里的日志是指重做日志,在InnoDB存储引擎中,由两部分组成,即redo logundo logredo log用来保证事务的持久性undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。

为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写入重做日志文件后,InnoDB存储引擎都需要调用一次fsync操作。由于重做日志文件打开并没有使用O_DIRECT选项,因此重做日志缓冲先写入文件系统缓存。为了确保重做日志写入磁盘,必须进行一次fsync操作。由于fsync的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能,也就是数据库的性能。

InnoDB存储引擎允许用户手工设置非持久性的情况发生,以此提高数据库的性能。即当事务提交时,日志不写入重做日志文件,而是等待一个时间周期后再执行fsync操作。由于并非强制在事务提交时进行一次fsync操作,显然这可以显著提高数据库的性能。但是当数据库发生宕机时,由于部分日志未刷新到磁盘,因此会丢失最后一段时间的事务。

参数innodb_flush_log_at_trx_commit用来控制重做日志刷新到磁盘的策略。该参数的默认值为1,表示事务提交时必须调用一次fsync操作。还可以设置该参数的值为020表示事务提交时不进行写入重做日志操作,这个操作仅在master thread中完成,而在master thread中每1秒会进行一次重做日志文件的fsync操作。2表示事务提交时将重做日志写入重做日志文件,但仅写入文件系统的缓存中,不进行fsync操作。在这个设置下,当MySQL数据库发生宕机而操作系统不发生宕机时,并不会导致事务的丢失。而当操作系统宕机时,重启数据库后会丢失未从文件系统缓存刷新到重做日志文件那部分事务。

虽然用户可以通过设置参数innodb_flush_log_at_trx_commit02 来提高事务提交的性能,但是需要牢记的是,这种设置方法丧失了事务 的ACID特性。

在MySQL数据库中还有一种二进制日志binlog),其用来进行POINT-IN-TIMEPIT)的恢复及主从复制(Replication)环境的建立。从表面上看其和重做日志非常相似,都是记录了对于数据库操作的日志。然而,从本质上来看,两者有着非常大的不同。

首先,重做日志是在InnoDB存储引擎层产生,而二进制日志是在MySQL数据库的上层产生的,并且二进制日志不仅仅针对于InnoDB存

储引擎,MySQL数据库中的任何存储引擎对于数据库的更改都会产生二进制日志。

其次,两种日志记录的内容形式不同。MySQL数据库上层的二进制日志是一种逻辑日志,其记录的是对应的SQL语句。而InnoDB存储引擎层面的重做日志物理格式日志,其记录的是对于每个页的修改。

此外,两种日志记录写入磁盘的时间点不同。二进制日志只在事务提交完成后进行一次写入。而InnoDB存储引擎的重做日志在事务进行中不断地被写入,这表现为日志并不是随事务提交的顺序进行写入的。

二进制日志仅在事务提交时记录,并且对于每一个事务,仅包含对应事务的一个日志。而对于InnoDB存储引擎的重做日志,由于其记录的是物理操作日志,因此每个事务对应多个日志条目,并且事务的重做日志写入是并发的,并非在事务提交时写入,故其在文件中记录的顺序并非是事务开始的顺序。

2.log block

在InnoDB存储引擎中,重做日志都是以512字节进行存储的。这意味着重做日志缓存、重做日志文件都是以块(block)的方式进行保存的,称之为重做日志块redo log block),每块的大小为512字节。

若一个页中产生的重做日志数量大于512字节,那么需要分割为多个重做日志块进行存储。此外,由于重做日志块的大小和磁盘扇区大小一样,都是512字节,因此重做日志的写入可以保证原子性,不需要doublewrite技术

重做日志块除了日志本身之外,还由日志块头log block header) 及日志块尾log block tailer)两部分组成。重做日志头一共占用12字节,重做日志尾占用8字节。故每个重做日志块实际可以存储的大小为492字节(512-12-8

3.log group

log group为重做日志组,其中有多个重做日志文件。

在InnoDB存储引擎运行过程中,log buffer根据一定的规则将内存中的log block刷新到磁盘。这个规则具体是:

  • 事务提交时
  • 当log buffer中有一半的内存空间已经被使用时
  • log checkpoint时

对于log block的写入追加(append)在redo log file最后部分,当一个redo log file被写满时,会接着写入下一个redo log file,其使用方式为round-robin

虽然log block总是在redo log file的最后部分进行写入,有的读者可能以为对redo log file的写入都是顺序的。其实不然,因为redo log file除了保存log buffer刷新到磁盘的log block,还保存了一些其他的信息,这些信息一共占用2KB大小,即每个redo log file的前2KB的部分不保存log block的信息。上述信息仅在每个log group的第一个redo logfile中进行存储。log group中的其余redo log file仅保留这些空间,但不保存上述信息。

4.重做日志格式
5.LSN

LSN是Log Sequence Number的缩写,其代表的是日志序列号。在InnoDB存储引擎中,LSN占用8字节,并且单调递增。LSN表示的含义有:

  • 重做日志写入的总量
  • checkpoint的位置
  • 页的版本
6.恢复

由于checkpoint表示已经刷新到磁盘页上的LSN,因此在恢复过程中仅需恢复checkpoint开始的日志部分

有的DBA或开发人员错误地认为只要将二进制日志的格式设置为ROW,那么二进制日志也是幂等的。这显然是错误的,举个简单的例

子,INSERT操作在二进制日志中就不是幂等的,重复执行可能会插入多条重复的记录。而上述INSERT操作的重做日志是幂等的。

7.2.2 undo

1.基本概念

redo存放在重做日志文件中,与redo不同,undo存放在数据库内部的一个特殊段(segment)中,这个段称为**undo段**(undo segment)。 undo段位于共享表空间内。

用户通常对undo有这样的误解:undo用于将数据库物理地恢复到执行语句或事务之前的样子——但事实并非如此。undo逻辑日志,因此 只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了, 但是数据结构和页本身在回滚之后可能大不相同。这是因为在多用户并发系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要任务就是协调对数据记录的并发访问。比如,一个事务在修改当前一个页中某几条记录,同时还有别的事务在对同一个页中另几条记录进行修 改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响其他 事务正在进行的工作。

当InnoDB存储引擎回滚时,它实际上做的是与先前相反的工作。对于每个INSERT,InnoDB存储引擎会完成一个DELETE;对于每个DELETE,InnoDB存储引擎会执行一个INSERT;对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去。

除了回滚操作,undo的另一个作用是MVCC,即InnoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的版本信息, 以此实现非锁定读取。 最后也是最为重要的一点是,undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。

2.undo存储管理

InnoDB存储引擎对undo的管理同样采用的方式。但是这个段和之前介绍的段有所不同。首先InnoDB存储引擎有rollback segment,每个回滚段种记录了1024undo log segment,而在每个undo log segment段中进行undo的申请。共享表空间偏移量为5(0,5)记录了所有rollback segment header所在的页,这个页的类型为FIL_PAGE_TYPE_SYS

在InnoDB1.1版本之前(不包括1.1版本),只有一个rollback segment,因此支持同时在线的事务限制为1024。从1.1版本开始 InnoDB支持最大128rollback segment,故其支持同时在线的事务限制提高到了128*1024。 虽然InnoDB1.1版本支持了128个rollback segment,但是这些rollback segment都存储于共享表空间中。从InnoDB1.2版本开始,可通过参数对 rollback segment做进一步的设置。这些参数包括:

  • innodb_undo_directory
  • innodb_undo_logs
  • innodb_undo_tablespaces

参数innodb_undo_directory用于设置rollback segment文件所在的路径。这意味着rollback segment可以存放在共享表空间以外的位置,即可以设置为独立表空间。该参数的默认值为“.”,表示当前InnoDB存储引擎的目录。

参数innodb_undo_logs用来设置rollback segment个数,默认值为 128。在InnoDB1.2版本中,该参数用来替换之前版本的参数 innodb_rollback_segments

参数innodb_undo_tablespaces用来设置构成rollback segment文件的数量,这样rollback segment可以较为平均地分布在多个文件中。设置该参数后,会在路径innodb_undo_directory看到undo为前缀的文件,该文件就代表rollback segment文件。

需要特别注意的是,事务在undo log segment分配页并写入undo log 的这个过程同样需要写入重做日志。当事务提交时,InnoDB存储引擎

会做以下两件事情:

  • undo log放入列表中,以供之后的purge操作
  • 判断undo log所在的页是否可以重用,若可以分配给下个事务使用

事务提交后并不能马上删除undo logundo log所在的页。这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本。故事务提交时将undo log放入一个链表中,是否可以最终删除undo logundo log所在页由purge线程来判断。

此外,若为每一个事务分配一个单独的undo页会非常浪费存储空 间,特别是对于OLTP的应用类型。因为在事务提交时,可能并不能马上释放页。假设某应用的删除和更新操作的TPS(transaction per second)为1000,为每个事务分配一个undo页,那么一分钟就需要 1000*60个页,大约需要的存储空间为1GB。若每秒的purge页的数量为 20,这样的设计对磁盘空间有着相当高的要求。因此,在InnoDB存储引擎的设计中对undo页可以进行重用。具体来说,当事务提交时,首先 将undo log放入链表中,然后判断undo页的使用空间是否小于3/4,若是 则表示该undo页可以被重用,之后新的undo log记录在当前undo log的后 面。由于存放undo log的列表是以记录进行组织的,而undo页可能存放着不同事务的undo log,因此purge操作需要涉及磁盘的离散读取操作, 是一个比较缓慢的过程。

3.undo log格式

在InnoDB存储引擎中,undo log分为:

  • insert undo log
  • update undo log

insert undo log是指在insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除不需要进行purge操作

update undo log记录的是对deleteupdate操作产生的undo log。该 undo log可能需要提供**MVCC机制,因此不能在事务提交时就进行删 除**。提交时放入undo log链表,等待purge线程进行最后的删除。

4.查看undo信息

INNODB_TRX_ROLLBACK_SEGMENT表查看rollback segment

INNODB_TRX_UNDO记录事务对应的 undo log

delete操作并不直接删除记录,而只是将记录标记为已删除,也就是将记录的delete flag设置为1。而记录最终的删除是在purge操作中完成的。

update主键的操作其实分两步完成。首先将原主键记录标记为已删除,因此需要产生一个类型为 TRX_UNDO_DEL_MARK_REC的undo log,之后插入一条新的记录

7.2.3 purge

deleteupdate操作可能并不直接删除原有的数据。

purge用于最终完成deleteupdate操作。这样设计是因为InnoDB存储引擎支持MVCC,所以记录不能在事务提交时立即进行处理。这时其他事务可能正在引用这行,故InnoDB存储引擎需要保存记录之前的版本。而是否可以删除该条记录通过purge来进行判断。若该行记录已不被任何其他事务引用,那么就可以进行真正的delete操作。可见,purge 操作是清理之前的deleteupdate操作,将上述操作“最终”完成。而实际执行的操作为delete操作,清理之前行记录的版本。

全局动态参数innodb_purge_batch_size用来设置每次purge操作需要清理的undo page数量

当InnoDB存储引擎的压力非常大时,并不能高效地进行purge操 作。那么history list的长度会变得越来越长。全局动态参数 innodb_max_purge_lag用来控制history list的长度,若长度大于该参数 时,其会“延缓”DML的操作。该参数默认值为0,表示不对history list做 任何限制。当大于0时,就会延缓DML的操作,其延缓的算法为: delay=((length(history_list)-innodb_max_purge_lag)*10)-5 ,单位是毫秒

需要特别注意的是,delay的对象是 行,而不是一个DML操作。例如当一个update操作需要更新5行数据 时,每行数据的操作都会被delay,故总的延时时间为5*delay。而delay 的统计会在每一次purge操作完成后,重新进行计算。

InnoDB1.2版本引入了新的全局动态参数 innodb_max_purge_lag_delay,其用来控制delay的最大毫秒数。也就是 当上述计算得到的delay值大于该参数时,将delay设置为 innodb_max_purge_lag_delay,避免由于purge操作缓慢导致其他SQL线程出现无限制的等待。

7.2.4 group commit

为了提高磁盘fsync的效率,当前数据库都提供了 group commit的功能,即一次fsync可以刷新确保多个事务日志被写入文 件。对于InnoDB存储引擎来说,事务提交时会进行两个阶段的操作:

  1. 修改内存中事务对应的信息,并且将日志写入重做日志缓冲
  2. 调用fsync将确保日志都从重做日志缓冲入磁盘

7.3 事务控制语句

在MySQL命令行的默认设置下,事务都是自动提交(auto commit)的,即执行SQL语句后就会马上执行COMMIT操作。因此要显 式地开启一个事务需使用命令BEGIN、START TRANSACTION,或者 执行命令SET AUTOCOMMIT=0,禁用当前会话的自动提交。

事务控制语句:

  • START TRANSACTION | BEGIN:显式地开启一个事务。
  • COMMIT:要想使用这个语句的最简形式,只需发出COMMIT。也可以更详细一些,写为COMMIT WORK,不过这二者几乎是等价的。COMMIT会提交事务,并使得已对数据库做的所有修改成为永久性的。
  • ROLLBACK:要想使用这个语句的最简形式,只需发出ROLLBACK。同样地,也可以写为ROLLBACK WORK,但是二者几乎是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
  • SAVEPOINT identifier∶SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT
  • RELEASE SAVEPOINT identifier:删除一个事务的保存点,当没有一个保存点执行这句语句时,会抛出一个异常。
  • ROLLBACK TO [SAVEPOINT] identifier:这个语句与 SAVEPOINT命令一起使用。可以把事务回滚到标记点,而不回滚在此标记点之前的任何工作。
  • SET TRANSACTION:这个语句用来设置事务的隔离级别。 InnoDB存储引擎提供的事务隔离级别有:READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE

COMMITCOMMIT WORK语句基本是一致的,都是用来提交事务。不同之处在于COMMIT WORK用来控制事务结束后的行为是 CHAIN还是RELEASE的。如果是CHAIN方式,那么事务就变成了链事务。

用户可以通过参数completion_type来进行控制,该参数默认为0, 表示没有任何操作。在这种设置下COMMIT和COMMIT WORK是完全等价的。当参数completion_type的值为1时,COMMIT WORK等同于 COMMIT AND CHAIN,表示马上自动开启一个相同隔离级别的事务, 参数completion_type2时,COMMIT WORK等同于COMMIT AND RELEASE。在事务提交后会自动断开与服务器的连接

ROLLBACK TO SAVEPOINT,虽然有 ROLLBACK,但其并不是真正地结束一个事务,因此即使执行了 ROLLBACK TO SAVEPOINT,之后也需要显式地运行COMMITROLLBACK命令。

7.4 隐式提交的SQL语句

在Microsoft SQL Server数据库 中,即使是DDL也是可以回滚的。这和InnoDB存储引擎、Oracle这些数 据库完全不同。

另外需要注意的是,TRUNCATE TABLE语句是DDL,因此虽然和 对整张表执行DELETE的结果是一样的,但它是不能被回滚的

7.6 事务的隔离级别

SQL标准定义的四个隔离级别为:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

READ UNCOMMITTED称为浏览访问(browse access),仅仅针对 事务而言的。READ COMMITTED称为游标稳定(cursor stability)。 REPEATABLE READ是2.9999°的隔离,没有幻读的保护。 SERIALIZABLE称为隔离,或3°的隔离。SQL和SQL2标准的默认事务 隔离级别是SERIALIZABLE。

InnoDB存储引擎默认支持的隔离级别是REPEATABLE READ,但 是与标准SQL不同的是,InnoDB存储引擎在REPEATABLE READ事务 隔离级别下,使用Next-Key Lock锁的算法,因此避免幻读的产生。这 与其他数据库系统(如Microsoft SQL Server数据库)是不同的。所以说,InnoDB存储引擎在默认的REPEATABLE READ的事务隔离级别下 已经能完全保证事务的隔离性要求,即达到SQL标准的SERIALIZABLE 隔离级别

在InnoDB存 储引擎中选择REPEATABLE READ的事务隔离级别并不会有任何性能 的损失。同样地,即使使用READ COMMITTED的隔离级别,用户也不会得到性能的大幅度提升。

-- 设置
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED |READ COMMITTED |REPEATABLE READ |SERIALIZABLE }
-- 查看
SELECT @@tx_isolation \G;
SELECT @@global.tx_isolation \G;
[mysqld] 
transaction-isolation=READ-COMMITTED

因为InnoDB存储引擎在REPEATABLE READ隔离级别下就可以达 到的隔离,因此一般不在本地事务中使用SERIALIABLE的隔离级

别。SERIALIABLE的事务隔离级别主要用于InnoDB存储引擎的分布式 事务。

7.7 分布式事务

7.7.1 MySQL数据库分布式事务

InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。在使用分布式事务时,InnoDB存储引擎的事务 隔离级别必须设置为SERIALIZABLE

第8章 备份与恢复

8.1 备份与恢复概述

可以根据不同的类型来划分备份的方法。

根据备份的方法不同可以将备份分为:

  • Hot Backup(热备) :指数据库运行中直接备份。式在MySQL官方手册中称为Online Backup(在线备份)
  • Cold Backup(冷备) :备份操作是在数据库停止的情况下,在MySQL官方手册中称为Offline Backup(离线备份)
  • Warm Backup(温备) :在数据库运行中进行的,但是会对当前数据库的操作有所影响,如加一个全局读锁以保证备份数据的一致性。

按照备份后文件的内容,备份又可以分为:

  • 逻辑备份 :逻辑备份是指备份出的文件内容是可读的, 一般是文本文件。内容一般是由一条条SQL语句,或者是表内实际数据

    组成。如mysqldumpSELECT * INTO OUTFILE的方法

  • 裸文件备份:复制数据库的物理文件,既可以是在数据库运行中的复制(如ibbackupxtrabackup这类工具),也可以是在数据库停止运行时直接的数据文件复制。

若按照备份数据库的内容来分,备份又可以分为:

  • 完全备份 :对数据库进行一个完整的备份
  • 增量备份 :在上次完全备份的基础上,对于更改的数据进行备份
  • 日志备份 :对MySQL数据库二进制日志的备份,通过对一个完全备份进行二进制日志重做(replay)来完成数据库的point-in-time的恢复工作。

MySQL数据库复制(replication)的原理就是异步实时地将二进制日志重做传送并应用到从(slave/standby)数据库。

对于MySQL数据库来说,官方没有提供真正的增量备份的方法, 大部分是通过二进制日志完成增量备份的工作。这种备份较之真正的增量备份来说,效率还是很低的。假设有一个100GB的数据库,要通过二进制日志完成备份,可能同一个页需要执行多次的SQL语句完成重做的 工作。但是对于真正的增量备份来说,只需要记录当前每页最后的检查点的LSN,如果大于之前全备时的LSN,则备份该页,否则不用备份, 这大大加快了备份的速度和恢复的时间,同时这也是xtrabackup工具增量备份的原理。

久 游网开发过一套DAO(Database Admin Online)系统,这套系统完全由 DBA开发完成,整个平台用Python语言编写,Web操作界面采用 Django。通过这个系统DBA可以方便地对几百台MySQL数据库服务器 进行备份,同时查看备份完成后备份文件的状态。

8.2 冷备

对于InnoDB存储引擎的冷备非常简单,只需要备份MySQL数据库 的frm文件,共享表空间文件,独立表空间文件(*.ibd),重做日志文件。另外建议定期备份MySQL数据库的配置文件my.cnf,这样有利于恢复的操作。

冷备的优点是:

  • 备份简单,只要复制相关文件即可。
  • 备份文件易于在不同操作系统,不同MySQL版本上进行恢复。
  • 恢复相当简单,只需要把文件恢复到指定位置即可。
  • 恢复速度快,不需要执行任何SQL语句,也不需要重建索引。

冷备的缺点是:

  • InnoDB存储引擎冷备的文件通常比逻辑文件大很多,因为表空间中存放着很多其他的数据,如undo段,插入缓冲等信息。
  • 冷备也不总是可以轻易地跨平台。操作系统、MySQL的版本、 文件大小写敏感和浮点数格式都会成为问题。

8.3 逻辑备份

8.3.1 mysqldump

8.3.2 SELECT...INTO OUTFILE

8.3.3 逻辑备份的恢复

通过mysqldump可以恢复数据库,但是经常发生的一个问题是, mysqldump可以导出存储过程、导出触发器、导出事件、导出数据,但 是却不能导出视图。因此,如果用户的数据库中还使用了视图,那么在 用mysqldump备份完数据库后还需要导出视图的定义,或者备份视图定 义的frm文件,并在恢复时进行导入,这样才能保证mysqldump数据库的 完全恢复。

8.3.4 LOAD DATA INFILE

若通过mysqldump-tab,或者通过SELECT INTO OUTFILE导出的数据需要恢复,这时可以通过命令LOAD DATA INFILE来进行导入。

要对服务器文件使用LOAD DATA INFILE,必须拥有FILE权。

8.3.5 mysqlimport

mysqlimport是MySQL数据库提供的一个命令行程序,从本质上来说,是LOAD DATA INFILE命令接口,而且大多数的选项都和LOAD DATA INFILE语法相同。

LOAD DATA INFILE不同的是,mysqlimport命令可以用来导入 多张表。并且通过--user-thread参数并发地导入不同的文件。

8.4 二进制日志备份与恢复

二进制日志非常关键,用户可以通过它完成point-in-time的恢复工作。MySQL数据库的replication同样需要二进制日志。在默认情况下并不启用二进制日志,要使用二进制日志首先必须启用它。如在配置文件中进行设置:

[mysqld] 
log-bin=mysql-bin
#只简单启用二进制 日志是不够的,还需要启用一些其他参数来保证最为安全和正确地记录 二进制日志
sync_binlog=1 
innodb_support_xa=1

在备份二进制日志文件前,可以通过FLUSH LOGS命令来生成一个新的二进制日志文件,然后备份之前的二进制日志。

要恢复二进制日志也是非常简单的,通过mysqlbinlog即可。

mysqlbinlog [options] log_fle...
mysqlbinlog binlog.0000001 | mysql-uroot-p test
#如果需要恢复多个二进制日志文件,最正确的做法应该是同时恢复 多个二进制日志文件,而不是一个一个地恢复
mysqlbinlog binlog.[0-10]* | mysql-u root-p test

也可以先通过mysqlbinlog命令导出到一个文件,然后再通过SOURCE命令来导入,这种做法的好处是可以对导出的文件进行修改后再导入,

# 把SQL语句输出到一个文件
mysqlbinlog binlog.000001 > /tmp/statements.sql 
mysqlbinlog binlog.000002>>/tmp/statements.sql 
# 执行导出的SQL
mysql-u root -p -e "source/tmp/statements.sql"


# --start-position和--stop-position选项可以用来指定从二进制日志的某个偏移量来进行恢复,这样可以跳过某些不正确的语句,
mysqlbinlog --start-position=107856 binlog.0000001 | mysql-uroot-p test
#--start-datetime和--stop-datetime选项可以用来指定从二进制日志的 某个时间点来进行恢复,用法和--start-position和--stop-position选项基本 相同。

8.5 热备

8.5.1 ibbackup

ibbackupInnoDB存储引擎官方提供的热备工具,可以同时备份MyISAM存储引擎和InnoDB存储引擎表。对于InnoDB存储引擎表其备

份工作原理如下:

  1. 记录备份开始时,InnoDB存储引擎重做日志文件检查点的LSN。
  2. 复制共享表空间文件以及独立表空间文件。
  3. 记录复制完表空间文件,InnoDB存储引擎重做日志文件检查点的LSN。
  4. 复制在备份时产生的重做日志

ibbackup的优点如下:

  • 在线备份,不阻塞任何的SQL语句。
  • 备份性能好,备份的实质是复制数据库文件和重做日志文件。
  • 支持压缩备份,通过选项,可以支持不同级别的压缩。
  • 平台支持,ibbackup可以运行在Linux、Windows以及主流的UNIX系统平台上。

ibbackup对InnoDB存储引擎表的恢复步骤为:

  1. 恢复表空间文件。
  2. 应用重做日志文件

8.5.2 XtraBackup

在开始备份时,xtrabackup首先记录了重做日志的位置(position)。然后对备份的InnoDB存储引擎表的物理文件,即共享表空间和独立表空间进行copy操作,最后记录备份完成后的重做日志位置

8.5.3 XtraBackup实现增量备份

MySQL数据库本身提供的工具并不支持真正的增量备份,更准确地说,二进制日志的恢复应该是point-in-time的恢复而不是增量备份。

而XtraBackup工具支持对于InnoDB存储引擎的增量备份,其工作原理如下:

  1. 首选完成一个全备,并记录下此时检查点的LSN
  2. 在进行增量备份时,比较表空间中每个页的LSN是否大于上次备份时的LSN,如果是,则备份该页,同时记录当前检查点的LSN。

因此XtraBackup的备份和恢复的过程大致如下:

首先将全部文件备份到/backup/base目录下,增量备份产生的文件备份到/backup/delta。在恢复过程中,首先指定全备的路径,然后将增量的备份应用于该完全备份。

8.7 复制

8.7.1 复制的工作原理

复制(replication)是MySQL数据库提供的一种高可用高性能的解决方案,一般用来建立大型的应用。总体来说,replication的工作原理分为以下3个步骤:

  1. 主服务器(master)把数据更改记录到二进制日志(binlog) 中。
  2. 从服务器(slave)把主服务器的二进制日志复制到自己的中继日志(relay log)中。
  3. 从服务器重做中继日志中的日志,把更改应用到自己的数据库上,以达到数据的最终一致性。

复制的工作原理并不复杂,其实就是一个完全备份加上二进制日志备份的还原。不同的是这个二进制日志的还原操作基本上实时在进行中。这里特别需要注意的是,复制不是完全实时地进行同步,而是异步实时。这中间存在主从服务器之间的执行延时,如果主服务器的压力很大,则可能导致主从服务器延时较大。

image-20220504122411396

从服务器有2个线程,一个是I/O线程,负责读取主服务器的二进制日志,并将其保存为中继日志;另一个是SQL线程,复制执行中继日志。

用户要想得知当前的延迟,可以通过命令SHOW SLAVE STATUSSHOW MASTER STATUS得知

通过SHOW SLAVE STATUS命令可以观察当前复制的运行状态

命令SHOW MASTER STATUS可以用来查看主服务器中二进制日志的状态。

8.7.2 快照+复制的备份架构

第9章 性能调优

9.1 选择合适的CPU

9.2 内存的重要性

到InnoDB存储引擎既缓存数据,又缓存索引,并且将它们缓存于一个很大的缓冲池中,即InnoDB Buffer Pool。因此,内存的大小直接影响了数据库的性能。

随着缓冲池 的增大,测试结果TPS(Transaction Per Second)会线性增长。所以,应该在开发应用前预估“活跃”数据库的大小是多少,并以此确定数据库服务器内存的大小。

如何判断当前数据库的内存是否已经达到瓶颈了呢?可以通过查看当前服务器的状态,比较物理磁盘的读取内存读取比例来判断缓冲池的命中率,通常InnoDB存储引擎的缓冲池的命中率不应该小于99%

SHOW GLOBAL STAUTS LIKE 'innodb%read%'\G; 
***************************1.row*************************** 
Variable_name:Innodb_buffer_pool_read_ahead   -- 预读的次数
Value:0 
***************************2.row*************************** 
Variable_name:Innodb_buffer_pool_read_ahead_evicted  -- 预读的页,但是没有被读取,就从缓冲池中被替换的页的数量,一般用来判断预读的效率。
Value:0 
***************************3.row*************************** 
Variable_name:Innodb_buffer_pool_read_requests -- 从缓冲池中读取页的次数。
Value:167051313 
***************************4.row*************************** 
Variable_name:Innodb_buffer_pool_reads    -- 从物理磁盘 读取 页 的次数。
Value:129236 
***************************5.row*************************** 
Variable_name:Innodb_data_pending_reads 
Value:0 
***************************6.row*************************** 
Variable_name:Innodb_data_read            -- 总共读入的字节数
Value:2135642112 
***************************7.row*************************** 
Variable_name:Innodb_data_reads           -- 发起读取请求的次数,每次读取可能需要读取多个页。
Value:130309 
***************************8.row*************************** 
Variable_name:Innodb_pages_read 
Value:130215 
***************************9.row*************************** 
Variable_name:Innodb_rows_read 
Value:17651085 
9 rows in set(0.00 sec)
缓冲池命中率 = 从缓冲池中读取页的次数 / ( 从缓冲池中读取页的次数 + 预读的次数 + 从物理磁盘 读取 页 的次数)
缓冲池命中率 = Innodb_buffer_pool_read_requests / ( Innodb_buffer_pool_read_requests + Innodb_buffer_pool_read_ahead + Innodb_buffer_pool_reads)

平均每次读取的字节数 = Innodb_data_read / Innodb_data_reads 

9.3 硬盘对数据库性能的影响

9.4 合理地设置RAID

9.4.1 RAID类型

常见的RAID组合方式可分为

  • RAID 0:不冗余,并行IO,速度最快
  • RAID 1:镜像,磁盘利用率最低。
  • RAID 5: 有冗余,磁盘利用率= (N-1) / N
  • RAID 10
  • RAID 50

9.4.2 RAID Write Back功能

9.7 选择合适的基准测试工具

9.7.1 sysbench

sysbench是一个模块化的、跨平台的多线程基准测试工具,主要用于测试各种不同系统参数下的数据库负载情况。它主要包括以下几种测试方式:

  • CPU性能
  • 磁盘IO性能
  • 调度程序性能
  • 内存分配及传输速度
  • POSIX线程性能
  • 数据库OLTP基准测试

sysbench可以通过不同的参数设置来进行不同项目的测试。

9.7.2 mysql-tpcc

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《MySQL 技术内幕 InnoDB 存储引擎》是一本探讨 MySQLInnoDB 存储引擎技术书籍,该书主要讲解了 InnoDB 存储引擎的原理、架构和实现细节。 InnoDBMySQL 数据库的默认存储引擎,具有事务处理和行级锁定等特性,广泛应用于企业级数据库系统。因此,了解 InnoDB 存储引擎的内部工作原理和性能优化技巧对于 MySQL 数据库的使用和优化非常重要。 这本书首先介绍了数据库系统和存储引擎的基本概念,然后详细讲解了 InnoDB 存储引擎的体系结构和核心组件。读者可以了解到 InnoDB 存储引擎是如何将数据存储在磁盘上,以及如何通过索引加快查询速度。 在介绍完基础知识之后,书中继续深入讲解了 InnoDB 存储引擎的事务处理机制、并发控制和崩溃恢复等关键技术。读者可以学习到如何正确使用事务,并了解到 InnoDB 是如何通过行级锁定来实现并发访问的。 此外,书中还涵盖了 InnoDB 存储引擎的性能优化技巧和调试方法。读者可以学习到如何通过合理的配置和索引设计来提升查询性能,以及如何通过监控和分析工具来定位和解决性能问题。 总之,《MySQL 技术内幕 InnoDB 存储引擎》是一本深入剖析 InnoDB 存储引擎的权威技术书籍,对于想要深入理解和优化 MySQL 数据库的开发人员和数据库管理员来说是一本非常有价的参考资料。 ### 回答2: 《MySQL技术内幕InnoDB存储引擎》是一本深入讲解MySQL InnoDB存储引擎技术书籍。InnoDBMySQL中的一种存储引擎,相比其他存储引擎,如MyISAM,它拥有更好的事务支持和并发控制能力,可以提供更高的数据一致性和可靠性。 这本书主要从架构、存储、索引、事务、锁定等方面对InnoDB存储引擎进行了详细的介绍和分析。首先,作者介绍了InnoDB的整体架构,包括缓冲池、日志系统、刷新机制等,帮助读者理解InnoDB的内部工作原理。 然后,书中详细解释了InnoDB的物理存储机制,包括页结构、数据页、索引页等。通过了解这些概念,读者可以更好地理解数据在磁盘上的存储方式,进而优化查询性能和空间利用。 接着,书中介绍了InnoDB的索引机制,包括B+树索引和自适应哈希索引。通过学习这些索引类型的原理和使用方法,读者可以更好地选择和创建适合自己业务需求的索引,提高查询效率。 此外,该书还详细说明了InnoDB的事务处理机制,包括事务的隔离级别、锁定机制、行锁和表锁等。通过学习这些内容,读者可以更好地理解和掌握InnoDB的并发控制技术,避免数据冲突和锁定问题。 总而言之,《MySQL技术内幕InnoDB存储引擎》通过深入讲解InnoDB的各个方面,帮助读者更好地理解和应用该存储引擎。无论是MySQL开发人员还是DBA,都可以从这本书中获得对InnoDB的深入了解,提高数据库性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值