《MySQL技术内幕:InnoDB存储引擎(第2版)》书摘

MySQL技术内幕:InnoDB存储引擎(第2版)node

姜承尧python

第1章 MySQL体系结构和存储引擎mysql

 

>> 在上述例子中使用了mysqld_safe命令来启动数据库,固然启动MySQL实例的方法还有不少,在各类平台下的方式可能又会有所不一样。算法

>> 当启动实例时,MySQL数据库会去读取配置文件,根据配置文件的参数来启动数据库实例。这与Oracle的参数文件(spfile)类似,不一样的是,Oracle中若是没有参数文件,在启动实例时会提示找不到该参数文件,数据库启动失败。而在MySQL数据库中,能够没有配置文件,在这种状况下,MySQL会按照编译时的默认参数设置启动实例sql

>> 从概念上来讲,数据库是文件的集合,是依照某种数据模型组织起来并存放于二级存储器中的数据集合;数据库实例是程序,是位于用户与操做系统之间的一层数据管理软件,用户对数据库数据的任何操做,包括数据库定义、数据查询、数据维护、数据库运行控制等都是在数据库实例下进行的,应用程序只有经过数据库实例才能和数据库打交道。数据库

>> 须要特别注意的是,存储引擎是基于表的,而不是数据库。编程

>> 关于NDB存储引擎,有一个问题值得注意,那就是NDB存储引擎的链接操做(JOIN)是在MySQL数据库层完成的,而不是在存储引擎层完成的数组

>> 相信在任何一本关于数据库原理的书中,可能都会提到数据库与传统文件系统的最大区别在于数据库是支持事务的缓存

>> MySQL提供了一个很是好的用来演示MySQL各项功能的示例数据库,如SQL Server提供的AdventureWorks示例数据库和Oracle提供的示例数据库。据我所知,知道MySQL示例数据库的人不多,多是由于这个示例数据库没有在安装的时候提示用户是否安装(如Oracle和SQL Server)以及这个示例数据库的下载居然和文档放在一块儿安全

>> 在Linux和UNIX环境下,还能够使用UNIX域套接字。UNIX域套接字其实不是一个网络协议,因此只能在MySQL客户端和数据库实例在一台服务器上的状况下使用。用户能够在配置文件中指定套接字文件的路径,如--socket=/tmp/mysql.sock。当数据库实例启动后,用户能够经过下列命令来进行UNIX域套接字文件的查找:

 

第2章 InnoDB存储引擎

 

>> 从MySQL数据库的官方手册可得知,著名的Internet新闻站点Slashdot.org运行在InnoDB上。Mytrix、Inc.在InnoDB上存储超过1 TB的数据,还有一些其余站点在InnoDB上处理插入/更新操做的速度平均为800次/秒。这些都证实了InnoDB是一个高性能、高可用、高可扩展的存储引擎。

>> 后台线程的主要做用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。此外将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的状况下InnoDB能恢复到正常运行状态。

>> 在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写IO请求,这样能够极大提升数据库的性能。而IO Thread的工做主要是负责这些IO请求的回调(call back)处理

>> 能够经过命令SHOW ENGINE INNODB STATUS来观察InnoDB中的IO Thread:

>> 具体来看,缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲(insert buffer)、自适应哈希索引(adaptive hash index)、InnoDB存储的锁信息(lock info)、数据字典信息(data dictionary)等

>> 从InnoDB 1.0.x版本开始,容许有多个缓冲池实例。每一个页根据哈希值平均分配到不一样缓冲池实例中。这样作的好处是减小数据库内部的资源竞争,增长数据库的并发处理能力。能够经过参数innodb_buffer_pool_instances来进行配置,该值默认为1。

>> 从MySQL 5.6版本开始,还能够经过information_schema架构下的表INNODB_BUFFER_POOL_STATS来观察缓冲的状态

>> 在InnoDB存储引擎中,缓冲池中页的大小默认为16KB,一样使用LRU算法对缓冲池进行管理

>> 。稍有不一样的是InnoDB存储引擎对传统的LRU算法作了一些优化。在InnoDB的存储引擎中,LRU列表中还加入了midpoint位置。新读取到的页,虽然是最新访问的页,但并非直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。这个算法在InnoDB存储引擎下称为midpoint insertion strategy。

>> 那为何不采用朴素的LRU算法,直接将读取的页放入到LRU列表的首部呢?这是由于若直接将读取到的页放入到LRU的首部,那么某些SQL操做可能会使缓冲池中的页被刷新出,从而影响缓冲池的效率。

>> 常见的这类操做为索引或数据的扫描操做。这类操做须要访问表中的许多页,甚至是所有的页,而这些页一般来讲又仅在此次查询操做中须要,并非活跃的热点数据。若是页被放入LRU列表的首部,那么很是可能将所须要的热点数据页从LRU列表中移除,而在下一次须要读取该页时,InnoDB存储引擎须要再次访问磁盘。

>> Buffer pool hit rate,表示缓冲池的命中率,这个例子中为100%,说明缓冲池运行状态很是良好。一般该值不该该小于95%。若发生Buffer pool hit rate的值小于95%这种状况,用户须要观察是不是因为全表扫描引发的LRU列表被污染的问题。

>> 执行命令SHOW ENGINE INNODB STATUS显示的不是当前的状态,而是过去某个时间范围内InnoDB存储引擎的状态。从上面的例子能够发现,Per second averages calculated from the last 24 seconds表明的信息为过去24秒内的数据库状态。

>> 在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会经过CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表。须要注意的是,脏页既存在于LRU列表中,也存在于Flush列表中

>> 重作日志缓冲通常不须要设置得很大,由于通常状况下每一秒钟会将重作日志缓冲刷新到日志文件,所以用户只须要保证每秒产生的事务量在这个缓冲大小以内便可

>> 当前3TB的MySQL数据库已并很多见,可是3 TB的内存却很是少见。目前Oracle Exadata旗舰数据库一体机也就只有2 TB的内存。

>> 所以Checkpoint(检查点)技术的目的是解决如下几个问题:□ 缩短数据库的恢复时间;□ 缓冲池不够用时,将脏页刷新到磁盘;□ 重作日志不可用时,刷新脏页。

>> 对于InnoDB存储引擎而言,其是经过LSN(Log Sequence Number)来标记版本的。而LSN是8字节的数字,其单位是字节。每一个页有LSN,重作日志中也有LSN,Checkpoint也有LSN。能够经过命令SHOW ENGINE INNODB STATUS来观察:

>> InnoDB存储引擎的关键特性包括:□ 插入缓冲(Insert Buffer)□ 两次写(Double Write)□ 自适应哈希索引(Adaptive Hash Index)□ 异步IO(Async IO)□ 刷新邻接页(Flush Neighbor Page)

>> InnoDB存储引擎开创性地设计了Insert Buffer,对于非汇集索引的插入或更新操做,不是每一次直接插入到索引页中,而是先判断插入的非汇集索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中,好似欺骗。数据库这个非汇集的索引已经插到叶子节点,而实际并无,只是存放在另外一个位置。

>> 而后再以必定的频率和状况进行Insert Buffer和辅助索引页子节点的merge(合并)操做,这时一般能将多个插入合并到一个操做中(由于在一个索引页中),这就大大提升了对于非汇集索引插入的性能。

>> 辅助索引不能是惟一的,由于在插入缓冲时,数据库并不去查找索引页来判断插入的记录的惟一性。若是去查找确定又会有离散读取的状况发生,从而致使Insert Buffer失去了意

>> 正如前面所说的,目前Insert Buffer存在一个问题是:在写密集的状况下,插入缓冲会占用过多的缓冲池内存(innodb_buffer_pool),默认最大能够占用到1/2的缓冲池内存。

>> InnoDB从1.0.x版本开始引入了Change Buffer,可将其视为Insert Buffer的升级。从这个版本开始,InnoDB存储引擎能够对DML操做——INSERT、DELETE、UPDATE都进行缓冲,他们分别是:Insert Buffer、Delete Buffer、Purge buffer。

>> innodb_change_buffer_max_size值默认为25,表示最多使用1/4的缓冲池内存空间。而须要注意的是,该参数的最大有效值为50。

>> 可能令绝大部分用户感到吃惊的是,Insert Buffer的数据结构是一棵B+树。在MySQL 4.1以前的版本中每张表有一棵Insert Buffer B+树。而在如今的版本中,全局只有一棵Insert Buffer B+树,负责对全部的表的辅助索引进行Insert Buffer。

>> 而这棵B+树存放在共享表空间中,默认也就是ibdata1中。所以,试图经过独立表空间ibd文件恢复表中数据时,每每会致使CHECK TABLE失败

>> 。这是由于表的辅助索引中的数据可能还在Insert Buffer中,也就是共享表空间中,因此经过ibd文件进行恢复后,还须要进行REPAIR TABLE操做来重建表上全部的辅助索引。

>> 若是说Insert Buffer带给InnoDB存储引擎的是性能上的提高,那么doublewrite(两次写)带给InnoDB存储引擎的是数据页的可靠性。

>> 当发生数据库宕机时,可能InnoDB存储引擎正在写入某个页到表中,而这个页只写了一部分,好比16KB的页,只写了前4KB,以后就发生了宕机,这种状况被称为部分写失效(partial page write)。在InnoDB存储引擎未使用doublewrite技术前,曾经出现过由于部分写失效而致使数据丢失的状况。有经验的DBA也许会想,若是发生写失效,能够经过重作日志进行恢复。这是一个办法。可是必须清楚地认识到,重作日志中记录的是对页的物理操做,如偏移量800,写'aaaa'记录。若是这个页自己已经发生了损坏,再对其进行重作是没有意义的。这就是说,在应用(apply)重作日志前,用户须要一个页的副本,当写入失效发生时,先经过页的副原本还原该页,再进行重作,这就是doublewrite

>> 在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会经过memcpy函数将脏页先复制到内存中的doublewrite buffer,以后经过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,而后立刻调用fsync函数,同步磁盘,避免缓冲写带来的问题在这个过程当中,由于doublewrite页是连续的,所以这个过程是顺序写的,开销并非很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。

>> InnoDB存储引擎会监控对表上各索引页的查询。若是观察到创建哈希索引能够带来速度提高,则创建哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。

>> 值得注意的是,哈希索引只能用来搜索等值的查询,如SELECT*FROM table WHERE index_col='xxx'。而对于其余查找类型,如范围查找,是不能使用哈希索引的,所以这里出现了non-hash searches/s的状况

>> 用户能够在发出一个IO请求后当即再发出另外一个IO请求,当所有IO请求发送完毕后,等待全部IO操做的完成,这就是AIO。AIO的另外一个优点是能够进行IO Merge操做,也就是将多个IO合并为1个IO,这样能够提升IOPS的性能

>> 须要注意的是,Native AIO须要操做系统提供支持。Windows系统和Linux系统都提供Native AIO支持,而Mac OSX系统则未提供

>> 参数innodb_use_native_aio用来控制是否启用Native AIO,在Linux操做系统下,默认值为ON

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

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

>> 参数innodb_force_recovery还能够设置为6个非零值:1~6。大的数字表示包含了前面全部小数字表示的影响

 

第3章 文件

 

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

>> Oracle数据库存在所谓的隐藏参数(undocumented parameter),以供Oracle“内部人士”使用,SQL Server也有相似的参数。有些DBA曾问我,MySQL中是否也有这类参数。个人回答是:没有,也不须要。即便Oracle和SQL Server中都有些所谓的隐藏参数,在绝大多数的状况下,这些数据库厂商也不建议用户在生产环境中对其进行很大的调整。

>> MySQL数据库中的参数能够分为两类:□ 动态(dynamic)参数□ 静态(static)参数动态参数意味着能够在MySQL实例运行中进行更改,静态参数说明在整个实例生命周期内都不得进行更改,就好像是只读(read only)的

>> 当出现MySQL数据库不能正常启动时,第一个必须查找的文件应该就是错误日志文件,该文件记录了错误信息,能很好地指导用户发现问题。

>> 设置long_query_time这个阈值后,MySQL数据库会记录运行时间超过该值的全部SQL语句,但运行时间正好等于long_query_time的状况并不会被记录下。也就是说,在源代码中判断的是大于long_query_time,而非大于等于

>> 另外一个和慢查询日志有关的参数是log_queries_not_using_indexes,若是运行的SQL语句没有使用索引,则MySQL数据库一样会将这条SQL语句记录到慢查询日志文件。

>> MySQL 5.6.5版本开始新增了一个参数log_throttle_queries_not_using_indexes,用来表示每分钟容许记录到slow log的且未使用索引的SQL语句次数。该值默认为0,表示没有限制。在生产环境下,若没有使用索引,此类SQL语句会频繁地被记录到slow log,从而致使slow log文件的大小不断增长,故DBA可经过此参数进行配置。

>> MySQL 5.1开始能够将慢查询的日志记录放入一张表中,这使得用户的查询更加方便和直观。慢查询表在mysql架构下,名为slow_log

>> 查看slow_log表的定义会发现该表使用的是CSV引擎,对大数据量下的查询效率可能不高。用户能够把slow_log表的引擎转换到MyISAM,并在start_time列上添加索引以进一步提升查询的效率。

>> 不能忽视的是,将slow_log表的存储引擎更改成MyISAM后,仍是会对数据库形成额外的开销。

>> 用户能够经过额外的参数long_query_io将超过指定逻辑IO次数的SQL语句记录到slow log中。该值默认为100,即表示对于逻辑读取次数大于100的SQL语句,记录到slow log中。而为了兼容原MySQL数据库的运行方式,还添加了参数slow_query_type,用来表示启用slow log的方式

>> 查询日志记录了全部对MySQL数据库请求的信息,不管这些请求是否获得了正确的执行。默认文件名为:主机名.log

>> 。一样地,从MySQL 5.1开始,能够将查询日志的记录放入mysql架构下的general_log表中,该表的使用方法和前面小节提到的slow_log基本同样

>> 二进制日志(binary log)记录了对MySQL数据库执行更改的全部操做,可是不包括SELECT和SHOW这类操做,由于这类操做对数据自己并无修改。

>> 使用事务的表存储引擎(如InnoDB存储引擎)时,全部未提交(uncommitted)的二进制日志会被记录到一个缓存中去,等该事务提交(committed)时直接将缓冲中的二进制日志写入二进制日志文件,而该缓冲的大小由binlog_cache_size决定,默认大小为32K。

>> 此外,binlog_cache_size是基于会话(session)的,也就是说,当一个线程开始一个事务时,MySQL会自动分配一个大小为binlog_cache_size的缓存,所以该值的设置须要至关当心,不能设置过大。当一个事务的记录大于设定的binlog_cache_size时,MySQL会把缓冲中的日志写入一个临时文件中,所以该值又不能设得过小

>> Binlog_cache_use记录了使用缓冲写二进制日志的次数,binlog_cache_disk_use记录了使用临时文件写二进制日志的次数

>> 默认状况下,二进制日志并非在每次写的时候同步到磁盘(用户能够理解为缓冲写)。所以,当数据库所在操做系统发生宕机时,可能会有最后一部分数据没有写入二进制日志文件中,这会给恢复和复制带来问题

>> 即便将sync_binlog设为1,仍是会有一种状况致使问题的发生。当使用InnoDB存储引擎时,在一个事务发出COMMIT动做以前,因为sync_binlog为1,所以会将二进制日志当即写入磁盘。若是这时已经写入了二进制日志,可是提交尚未发生,而且此时发生了宕机,那么在MySQL数据库下次启动时,因为COMMIT操做并无发生,这个事务会被回滚掉。可是二进制日志已经记录了该事务信息,不能被回滚。

>> 若是当前数据库是复制中的slave角色,则它不会将从master取得并执行的二进制日志写入本身的二进制日志文件中去。若是须要写入,要设置log-slave-update。若是须要搭建master=>slave=>slave架构的复制,则必须设置该参数。

>> MySQL 5.1开始引入了binlog_format参数,该参数可设的值有STATEMENT、ROW和MIXED

>> 上面的这个例子告诉咱们,将参数binlog_format设置为ROW,会对磁盘空间要求有必定的增长。而因为复制是采用传输二进制日志方式实现的,所以复制的网络开销也有所增长。

>> 要查看二进制日志文件的内容,必须经过MySQL提供的工具mysqlbinlog。对于STATEMENT格式的二进制日志文件,在使用mysqlbinlog后,看到的就是执行的逻辑SQL语句

>> 但不论表采用何种存储引擎,MySQL都有一个以frm为后缀名的文件,这个文件记录了该表的表结构定义。

>> frm还用来存放视图的定义,如用户建立了一个v_a视图,那么对应地会产生一个v_a.frm文件,用来记录视图的定义,该文件是文本文件,能够直接使用cat命令进行查看

>> 设置innodb_data_file_path参数后,全部基于InnoDB存储引擎的表的数据都会记录到该共享表空间中。若设置了参数innodb_file_per_table,则用户能够将每一个基于InnoDB存储引擎的表产生一个独立表空间

>> 。独立表空间的命名规则为:表名.ibd。经过这样的方式,用户不用将全部数据都存放于默认的表空间

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

>> 在默认状况下,在InnoDB存储引擎的数据目录下会有两个名为ib_logfile0和ib_logfile1的文件。在MySQL官方手册中将其称为InnoDB存储引擎的日志文件,不过更准确的定义应该是重作日志文件(redo log file)。为何强调是重作日志文件呢?由于重作日志文件对于InnoDB存储引擎相当重要,它们记录了对于InnoDB存储引擎的事务日志。

>> 每一个InnoDB存储引擎至少有1个重作日志文件组(group),每一个文件组下至少有2个重作日志文件,如默认的ib_logfile0和ib_logfile1。为了获得更高的可靠性,用户能够设置多个的镜像日志组(mirrored log groups),将不一样的文件组放在不一样的磁盘上,以此提升重作日志的高可用性。在日志组中每一个重作日志文件的大小一致,并以循环写入的方式运行。

>> InnoDB存储引擎先写重作日志文件1,当达到文件的最后时,会切换至重作日志文件2,再当重作日志文件2也被写满时,会再切换到重作日志文件1中。

>> 若磁盘自己已经作了高可用的方案,如磁盘阵列,那么能够不开启重作日志镜像的功能

>> 二进制日志会记录全部与MySQL数据库有关的日志记录,包括InnoDB、MyISAM、Heap等其余存储引擎的日志。而InnoDB存储引擎的重作日志只记录有关该存储引擎自己的事务日志。

>> 其次,记录的内容不一样,不管用户将二进制日志文件记录的格式设为STATEMENT仍是ROW,又或者是MIXED,其记录的都是关于一个事务的具体操做内容,即该日志是逻辑日志。而InnoDB存储引擎的重作日志文件记录的是关于每一个页(Page)的更改的物理状况。

>> 此外,写入的时间也不一样,二进制日志文件仅在事务提交前进行提交,即只写磁盘一次,不论这时该事务多大。而在事务进行的过程当中,却不断有重作日志条目(redo entry)被写入到重作日志文件中。

>> 在InnoDB存储引擎中,对于各类不一样的操做有着不一样的重作日志格式。到InnoDB 1.2.x版本为止,总共定义了51种重作日志类型。

>> 在第2章中已经提到,写入重作日志文件的操做不是直接写,而是先写入一个重作日志缓冲(redo log buffer)中,而后按照必定的条件顺序地写入日志文件

>> 从重作日志缓冲往磁盘写入时,是按512个字节,也就是一个扇区的大小进行写入。由于扇区是写入的最小单位,所以能够保证写入一定是成功的。所以在重作日志的写入过程当中不须要有doublewrite。

>> 所以为了保证事务的ACID中的持久性,必须将innodb_flush_log_at_trx_commit设置为1,也就是每当有事务提交时,就必须确保事务都已经写入重作日志文件。那么当数据库由于意外发生宕机时,能够经过重作日志文件恢复,并保证能够恢复已经提交的事务。

 

第4章 表

 

>> 在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table)。

>> 在InnoDB存储引擎表中,每张表都有个主键(Primary Key),若是在建立表时没有显式地定义主键,则InnoDB存储引擎会按以下方式选择或建立主键:□ 首先判断表中是否有非空的惟一索引(Unique NOT NULL),若是有,则该列即为主键。□ 如果不符合上述条件,InnoDB存储引擎自动建立一个6字节大小的指针

>> 当表中有多个非空惟一索引时,InnoDB存储引擎将选择建表时第一个定义的非空惟一索引为主键

>> 。这里须要很是注意的是,主键的选择根据的是定义索引的顺序,而不是建表时列的顺序。

>> 能够经过下面的SQL语句判断表的主键值:

>> _rowid能够显示表的主键,所以经过上述查询能够找到表z的主键

>> 另外须要注意的是,_rowid只能用于查看单个列为主键的状况,对于多列组成的主键就显得无能为力了

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

>> InnoDB存储引擎的逻辑存储结构大体如图4-1所示。

>> 第3章中已经介绍了在默认状况下InnoDB存储引擎有一个共享表空间ibdata1,即全部数据都存放在这个表空间内。若是用户启用了参数innodb_file_per_table,则每张表内的数据能够单独放到一个表空间内。

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

>> InnoDB存储引擎不会在执行rollback时去收缩这个表空间。虽然InnoDB不会回收这些空间,可是会自动判断这些undo信息是否还须要,若是不须要,则会将这些空间标记为可用空间,供下次undo使用。

>> 我用python写了一个py_innodb_page_info小工具,用来查看表空间中各页的类型和信息,用户能够在code.google.com上搜索david-mysql-tools进行查找

>> 由于前面已经介绍过了InnoDB存储引擎表是索引组织的(index organized),所以数据即索引,索引即数据。那么数据段即为B+树的叶子节点(图4-1的Leaf node segment),索引段即为B+树的非索引节点(图4-1的Non-leaf node segment)。

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

>> InnoDB 1.0.x版本开始引入压缩页,即每一个页的大小能够经过参数KEY_BLOCK_SIZE设置为2K、4K、8K,所以每一个区对应页的数量就应该为5十二、25六、128。

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

>> 在用户启用了参数innodb_file_per_talbe后,建立的表默认大小是96KB。区中是64个连续的页,建立的表的大小至少是1MB才对啊?其实这是由于在每一个段开始时,先用32个页大小的碎片页(fragment page)来存放数据,在使用完这些页以后才是64个连续页的申请。这样作的目的是,对于一些小表,或者是undo这类的段,能够在开始时申请较少的空间,节省磁盘容量的开销

>> 由于已经用完了32个碎片页,新的页会采用区的方式进行空间的申请,若是此时用户再经过py_innodb_page_info工具来看表空间文件t1.ibd,应该能够看到不少类型为Freshly Allocated Page的页:

>> 从InnoDB 1.2.x版本开始,能够经过参数innodb_page_size将页的大小设置为4K、8K、16K。若设置完成,则全部表中页的大小都为innodb_page_size,不能够对其再次进行修改。除非经过mysqldump导入和导出操做来产生新的库

>> 每一个页存放的行记录也是有硬性定义的,最多容许存放16KB / 2-200行的记录,即7992行记录

>> InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数据,

>> 在MySQL 5.1版本中,默认设置为Compact行格式。用户能够经过命令SHOW TABLE STATUS LIKE 'table_name'来查看当前表使用的行格式,其中row_format属性表示当前所使用的行记录结构类型。

>> 每行数据除了用户定义的列外,还有两个隐藏列,事务ID列和回滚指针列,分别为6字节和7字节的大小。

>> 若InnoDB表没有定义主键,每行还会增长一个6字节的rowid列。

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

>> 从错误消息能够看到InnoDB存储引擎并不支持65535长度的VARCHAR。这是由于还有别的开销,经过实际测试发现能存放VARCHAR类型的最大长度为65532。

>> 所以从这个例子中用户也应该理解VARCHAR(N)中的N指的是字符的长度。而文档中说明VARCHAR类型最大支持65535,单位是字节。

>> 此外须要注意的是,MySQL官方手册中定义的65535长度是指全部VARCHAR列的长度总和,若是列的长度总和超出这个长度,依然没法建立

>> 即便能存放65532个字节,可是有没有想过,InnoDB存储引擎的页为16KB,即16384字节,怎么能存放65532字节呢?所以,在通常状况下,InnoDB存储引擎的数据都是存放在页类型为B-tree node中。可是当发生行溢出时,数据存放在页类型为Uncompress BLOB页中。

>> InnoDB存储引擎表是索引组织的,即B+Tree的结构,这样每一个页中至少应该有两条行记录(不然失去了B+Tree的意义,变成链表了)。所以,若是页中只能存放下一条记录,那么InnoDB存储引擎会自动将行数据存放到溢出页中

>> 通过屡次试验测试,发现这个阈值的长度为8098

>> 对于TEXT或BLOB的数据类型,用户老是觉得它们是存放在Uncompressed BLOB Page中的,其实这也是不许确的。是放在数据页中仍是BLOB页中,和前面讨论的VARCHAR同样,至少保证一个页能存放两条记录

>> InnoDB 1.0.x版本开始引入了新的文件格式(file format,用户能够理解为新的页格式),之前支持的Compact和Redundant格式称为Antelope文件格式,新的文件格式称为Barracuda文件格式。Barracuda文件格式下拥有两种新的行记录格式:Compressed和Dynamic。

>> 从MySQL 4.1版本开始,CHR(N)中的N指的是字符的长度,而不是以前版本的字节长度。也就说在不一样的字符集下,CHAR类型列内部存储的可能不是定长的数据

>> SELECT a,CHAR_LENGTH(a),LENGTH(a)

>> SELECT a,HEX(a)        -> FROM j\G;

>> SHOW VARIABLES LIKE 'innodb_file_format'\G;

>> 关系型数据库系统和文件系统的一个不一样点是,关系数据库自己能保证存储数据的完整性,不须要应用程序的控制,而文件系统通常须要在程序端进行控制

>> 经过设置参数sql_mode的值为STRICT_TRANS_TABLES,此次MySQL数据库对于输入值的合法性进行了约束,并且针对不一样的错误,提示的错误内容也都不一样。参数sql_mode可设的值有不少,具体可参考MySQL官方手册。

>> 最多能够为一个表创建6个触发器,即分别为INSERT、UPDATE、DELETE的BEFORE和AFTER各定义一个。

>> 假设有张用户消费表,每次用户购买同样物品后其金额都是减的,若这时有“不怀好意”的用户作了相似减去一个负值的操做,这样用户的钱没减小反而会不断增长

>> 通常来讲,称被引用的表为父表,引用的表称为子表。外键定义时的ON DELETE和ON UPDATE表示在对父表进行DELETE和UPDATE操做时,对子表所作的操做

>> CASCADE表示当父表发生DELETE或UPDATE操做时,对相应的子表中的数据也进行DELETE或UPDATE操做。SET NULL表示当父表发生DELETE或UPDATE操做时,相应的子表中的数据被更新为NULL值,可是子表中相对应的列必须容许为NULL值。NO ACTION表示当父表发生DELETE或UPDATE操做时,抛出错误,不容许这类操做发生。RESTRICT表示当父表发生DELETE或UPDATE操做时,抛出错误,不容许这类操做发生。

>> 在其余数据库中,如Oracle数据库,有一种称为延时检查(deferred check)的外键约束,即检查在SQL语句运行完成后再进行。而目前MySQL数据库的外键约束都是即时检查(immediate check)

>> 虽然视图是基于基表的一个虚拟表,可是用户能够对某些视图进行更新操做,其本质就是经过视图的定义来更新基本表。通常称能够进行更新操做的视图为可更新视图(updatable view)。视图定义中的WITH CHECK OPTION就是针对于可更新的视图的,即更新的值是否须要检查

>> MySQL数据库DBA的一个经常使用的命令是SHOW TABLES,该命令会显示出当前数据库下全部的表。但由于视图是虚表,一样被做为表显示出来

>> 用户只想查看当前架构下的基表,能够经过information_schema架构下的TABLE表来查询,并搜索表类型为BASE TABLE的表

>> Oracle数据库支持物化视图——该视图不是基于基表的虚表,而是根据基表实际存在的实表,即物化视图的数据存储在非易失的存储设备上。物化视图能够用于预先计算并保存多表的连接(JOIN)或汇集(GROUP BY)等耗时较多的SQL操做结果。这样,在执行复杂查询时,就能够避免进行这些耗时的操做,从而快速获得结果。物化视图的好处是对于一些复杂的统计类查询能直接查出结果。在Microsoft SQL Server数据库中,称这种视图为索引视图。

>> 分区功能并非在存储引擎层完成的,所以不是只有InnoDB存储引擎支持分区,常见的存储引擎MyISAM、NDB等都支持。但也并非全部的存储引擎都支持,如CSV、FEDORATED、MERGE等就不支持。在使用分区功能前,应该对选择的存储引擎对分区的支持有所了解。

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

>> 不论建立何种类型的分区,若是表中存在主键或惟一索引时,分区列必须是惟一索引的一个组成部分

>> 惟一索引能够是容许NULL值的,而且分区列只要是惟一索引的一个组成部分,不须要整个惟一索引列都是分区列

>> 若是建表时没有指定主键,惟一索引,能够指定任何一个列为分区列

>> 查看表在磁盘上的物理文件,启用分区以后,表再也不由一个ibd文件组成了,而是由创建分区时的各个分区ibd文件组成,以下面的t#P#p0.ibd,t#P#p1.ibd

>> 经过EXPLAIN PARTITION命令咱们能够发现,在上述语句中,SQL优化器只须要去搜索p2008这个分区,而不会去搜索全部的分区——称为Partition Pruning(分区修剪),故查询的速度获得了大幅度的提

>> 在前面介绍的RANGE、LIST、HASH和KEY这四种分区中,分区的条件是:数据必须是整型(interger),若是不是整型,那应该须要经过函数将其转化为整型,如YEAR(),TO_DAYS(),MONTH()等函数。MySQL5.5版本开始支持COLUMNS分区,可视为RANGE分区和LIST分区的一种进化。COLUMNS分区能够直接使用非整型的数据进行分区,分区根据类型直接比较而得,不须要转化为整型。此外,RANGE COLUMNS分区能够对多个列的值进行分区。

>> 子分区(subpartitioning)是在分区的基础上再进行分区,有时也称这种分区为复合分区(composite partitioning)。MySQL数据库容许在RANGE和LIST的分区上再进行HASH或KEY的子分区

>> 子分区的创建须要注意如下几个问题:□ 每一个子分区的数量必须相同。□ 要在一个分区表的任何分区上使用SUBPARTITION来明肯定义任何子分区,就必须定义全部的子分区。

>> 子分区能够用于特别大的表,在多个磁盘间分别分配数据和索引

>> MySQL数据库容许对NULL值作分区,可是处理的方法与其余数据库可能彻底不一样。MYSQL数据库的分区老是视NULL值视小于任何的一个非NULL值,这和MySQL数据库中处理NULL值的ORDER BY操做是同样的

>> 。所以对于不一样的分区类型,MySQL数据库对于NULL值的处理也是各不相同。

>> HASH和KEY分区对于NULL的处理方式和RANGE分区、LIST分区不同。任何分区函数都会将含有NULL值的记录返回为0

>> 对于OLAP的应用,分区的确是能够很好地提升查询的性能,由于OLAP应用大多数查询须要频繁地扫描一张很大的表。假设有一张1亿行的表,其中有一个时间戳属性列。用户的查询须要从这张表中获取一年的数据。若是按时间戳进行分区,则只须要扫描相应的分区便可。这就是前面介绍的Partition Pruning技术。

>> 然而对于OLTP的应用,分区应该很是当心。在这种应用下,一般不可能会获取一张大表中10%的数据,大部分都是经过索引返回几条记录便可。而根据B+树索引的原理可知,对于一张大表,通常的B+树须要2~3次的磁盘IO。所以B+树能够很好地完成操做,不须要分区的帮助,而且设计很差的分区会带来严重的性能问题。

>> 我发现不少开发团队会认为含有1000W行的表是一张很是巨大的表,因此他们每每会选择采用分区,如对主键作10个HASH的分区,这样每一个分区就只有100W的数据了,所以查询应该变得更快了,如SELECT * FROM TABLE WHERE PK=@pk。可是有没有考虑过这样一种状况:100W和1000W行的数据自己构成的B+树的层次都是同样的,可能都是2层。

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

 

第5章 索引与算法

 

>>  B+树中的B不是表明二叉(binary),而是表明平衡(balance),由于B+树是从最先的平衡二叉树演化而来,可是B+树不是一个二叉树。

>> 另外一个经常被DBA忽视的问题是:B+树索引并不能找到一个给定键值的具体行。B+树索引能找到的只是被查找数据行所在的页。而后数据库经过把页读入到内存,再在内存中进行查找,最后获得要查找的数据。

>> 平衡二叉树的查找性能是比较高的,但不是最高的,只是接近最高性能。最好的性能须要创建一棵最优二叉树,可是最优二叉树的创建和维护须要大量的操做,所以,用户通常只需创建一棵平衡二叉树便可。

>> 平衡二叉树的查询速度的确很快,可是维护一棵平衡二叉树的代价是很是大的。一般来讲,须要1次或屡次左旋和右旋来获得插入或更新后树的平衡性。

>> B+树由B树和索引顺序访问方法(ISAM,是否是很熟悉?对,这也是MyISAM引擎最初参考的数据结构)演化而来,可是在现实使用过程当中几乎已经没有使用B树的状况了。

>> B+树是为磁盘或其余直接存取辅助设备设计的一种平衡查找树。在B+树中,全部记录节点都是按键值的大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行链接。

>> 能够看到,无论怎么变化,B+树老是会保持平衡。可是为了保持平衡对于新插入的键值可能须要作大量的拆分页(split)操做。由于B+树结构主要用于磁盘,页的拆分意味着磁盘的操做,因此应该在可能的状况下尽可能减小页的拆分操做。

>> 所以,B+树一样提供了相似于平衡二叉树的旋转(Rotation)功能。

>> B+树索引的本质就是B+树在数据库中的实现。

>> 可是B+索引在数据库中有一个特色是高扇出性,所以在数据库中,B+树的高度通常都在2~4层,这也就是说查找某一键值的行记录时最多只须要2到4次IO,这倒不错。由于当前通常的机械磁盘每秒至少能够作100次IO,2~4次的IO意味着查询时间只需0.02~0.04秒。

>> 数据库中的B+树索引能够分为汇集索引(clustered inex)和辅助索引(secondary index[插图],可是无论是汇集仍是辅助的索引,其内部都是B+树的,即高度平衡的,叶子节点存放着全部的数据。汇集索引与辅助索引不一样的是,叶子节点存放的是不是一整行的信息。

>> 汇集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将汇集索引的叶子节点称为数据页。

>> 因为实际的数据页只能按照一棵B+树进行排序,所以每张表只能拥有一个汇集索引

>> 许多数据库的文档会这样告诉读者:汇集索引按照顺序物理地存储数据。若是看图5-14,可能也会有这样的感受。可是试想一下,若是汇集索引必须按照特定顺序存放物理记录,则维护成本显得很是之高。因此,汇集索引的存储并非物理上连续的,而是逻辑上连续的。

>> 对于辅助索引(Secondary Index,也称非汇集索引),叶子节点并不包含行记录的所有数据。叶子节点除了包含键值之外,每一个叶子节点中的索引行中还包含了一个书签(bookmark)。该书签用来告诉InnoDB存储引擎哪里能够找到与索引相对应的行数据

>> 。因为InnoDB存储引擎表是索引组织表,所以InnoDB存储引擎的辅助索引的书签就是相应行数据的汇集索引键。

>> 若是在一棵高度为3的辅助索引树中查找数据,那须要对这棵辅助索引树遍历3次找到指定主键,若是汇集索引树的高度一样为3,那么还须要对汇集索引树进行3次查找,最终找到一个完整的行数据所在的页,所以一共须要6次逻辑IO访问以获得最终的一个数据页。

>> 用户能够设置对整个列的数据进行索引,也能够只索引一个列的开头部分数据,如前面建立的表t,列b为varchar(8000),可是用户能够只索引前100个字段

>>  Cardinality:很是关键的值,表示索引中惟一值的数目的估计值。Cardinality表的行数应尽量接近1,若是很是小,那么用户须要考虑是否能够删除此索引。

>> Cardinality值很是关键,优化器会根据这个值来判断是否使用这个索引。可是这个值并非实时更新的,即并不是每次索引的更新都会更新该值,由于这样代价太大了

>> Cardinality为NULL,在某些状况下可能会发生索引创建了却没有用到的状况。或者对两条基本同样的语句执行EXPLAIN,可是最终出来的结果不同:一个使用索引,另一个使用全表扫描。这时最好的解决办法就是作一次ANALYZE TABLE的操做。

>> MySQL 5.5版本以前(不包括5.5)存在的一个广泛被人诟病的问题是MySQL数据库对于索引的添加或者删除的这类DDL操做,MySQL数据库的操做过程为:

>> InnoDB存储引擎从InnoDB 1.0.x版本开始支持一种称为Fast Index Creation(快速索引建立)的索引建立方式——简称FIC。

>> 对于辅助索引的建立,InnoDB存储引擎会对建立索引的表加上一个S锁。在建立的过程当中,不须要重建表,所以速度较以前提升不少,而且数据库的可用性也获得了提升。删除辅助索引操做就更简单了,InnoDB存储引擎只需更新内部视图,并将辅助索引的空间标记为可用,同时删除MySQL数据库内部视图上对该表的索引定义便可。这里须要特别注意的是,临时表的建立路径是经过参数tmpdir进行设置的。用户必须保证tmpdir有足够的空间能够存放临时表,不然会致使建立索引失败。

>> 因为FIC在索引的建立的过程当中对表加上了S锁,所以在建立的过程当中只能对该表进行读操做,如有大量的事务须要对目标表进行写操做,那么数据库的服务一样不可用

>> Facebook采用PHP脚原本现实OSC,而并非经过修改InnoDB存储引擎源码的方式。OSC最初由Facebook的员工Vamsi Ponnekanti开发。此外,OSC借鉴了开源社区以前的工具The openarkkit toolkit oak-online-alter-table。实现OSC步骤以下:

>> MySQL 5.6版本开始支持Online DDL(在线数据定义)操做,其容许辅助索引建立的同时,还容许其余诸如INSERT、UPDATE、DELETE这类DML操做,这极大地提升了MySQL数据库在生产环境中的可用性。

>> LOCK部分为索引建立或删除时对表添加锁的状况,可有的选择为:

>> InnoDB存储引擎实现Online DDL的原理是在执行建立或者删除操做的同时,将INSERT、UPDATE、DELETE这类DML操做日志写入到一个缓存中。待完成索引建立后再将重作应用到表上,以此达到数据的一致性。这个缓存的大小由参数innodb_online_alter_log_max_size控制,默认的大小为128MB。若用户更新的表比较大,而且在建立过程当中伴有大量的写事务,如遇到innodb_online_alter_log_max_size的空间不能存放日志时,会抛出相似以下的错误:

>> 对于这个错误,用户能够调大参数innodb_online_alter_log_max_size,以此得到更大的日志缓存空间。此外,还能够设置ALTER TABLE的模式为SHARE,这样在执行过程当中不会有写事务发生,所以不须要进行DML日志的记录。

>> 若是某个字段的取值范围很广,几乎没有重复,即属于高选择性,则此时使用B+树索引是最适合的。例如,对于姓名字段,基本上在一个应用中不容许重名的出现。

>> 在InnoDB存储引擎中,Cardinality统计信息的更新发生在两个操做中:INSERT和UPDATE。

>> 联合索引的第二个好处是已经对第二个键值进行了排序处理。例如,在不少状况下应用程序都须要查询某个用户的购物状况,并按照时间进行排序,最后取出最近三次的购买记录,这时使用联合索引能够避免多一次的排序操做,由于索引自己在叶子节点已经排序了。

>> 正如前面所介绍的那样,联合索引(a,b)实际上是根据列a、b进行排序,所以下列语句能够直接使用联合索引获得结果:    SELECT ... FROM TABLE WHERE a=xxx ORDER BY b

>> InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就能够获得查询的记录,而不须要查询汇集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的全部信息,故其大小要远小于汇集索引,所以能够减小大量的IO操做。

>> 覆盖索引的另外一个好处是对某些统计问题而言的

>> 表buy_log有(userid,buy_date)的联合索引,这里只根据列b进行条件查询,通常状况下是不能进行该联合索引的,可是这句SQL查询是统计操做,而且能够利用到覆盖索引的信息,所以优化器会选择该联合索引

>> 这是为何呢?缘由在于用户要选取的数据是整行信息,而OrderID索引不能覆盖到咱们要查询的信息,所以在对OrderID索引查询到指定数据后,还须要一次书签访问来查找整行数据的信息。虽然OrderID索引中数据是顺序存放的,可是再一次进行书签查找的数据则是无序的,所以变为了磁盘上的离散读操做。若是要求访问的数据量很小,则优化器仍是会选择辅助索引,可是当访问的数据占整个表中数据的蛮大一部分时(通常是20%左右),优化器会选择经过汇集索引来查找数据。由于以前已经提到过,顺序读要远远快于离散读。

>> 所以对于不能进行索引覆盖的状况,优化器选择辅助索引的状况是,经过辅助索引查找的数据是少许的。这是由当前传统机械硬盘的特性所决定的,即利用顺序读来替换随机读的查找。若用户使用的磁盘是固态硬盘,随机读操做很是快,同时有足够的自信来确认使用辅助索引能够带来更好的性能,那么能够使用关键字FORCE INDEX来强制使用某个索引

>> 所以,USE INDEX只是告诉优化器能够选择该索引,实际上优化器仍是会再根据本身的判断进行选择。而若是使用FORCE INDEX的索引提示,如:

>> MySQL5.6版本开始支持Multi-Range Read(MRR)优化。Multi-Range Read优化的目的就是为了减小磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问,这对于IO-bound类型的SQL查询语句可带来性能极大的提高。

>> MRR的工做方式以下:□ 将查询获得的辅助索引键值存放于一个缓存中,这时缓存中的数据是根据辅助索引键值排序的。□ 将缓存中的键值根据RowID进行排序。□ 根据RowID的排序顺序来访问实际的数据文件

>> 以前的MySQL数据库版本不支持Index Condition Pushdown,当进行索引查询时,首先根据索引来查找记录,而后再根据WHERE条件来过滤记录。在支持Index Condition Pushdown后,MySQL数据库会在取出索引的同时,判断是否能够进行WHERE条件的过滤,也就是将WHERE的部分过滤操做放在了存储引擎层

>> 当优化器选择Index Condition Pushdown优化时,可在执行计划的列Extra看到Using index condition提示

>> 自适应哈希索引采用以前讨论的哈希表的方式实现。不一样的是,这仅是数据库自身建立并使用的,DBA自己并不能对其进行干预。自适应哈希索引经哈希函数映射到一个哈希表中,所以对于字典类型的查找很是快速

>> 全文检索一般使用倒排索引(inverted index)来实现。倒排索引同B+树索引同样,也是一种索引结构。它在辅助表(auxiliary table)中存储了单词与单词自身在一个或多个文档中所在位置之间的映射。这一般利用关联数组实现,其拥有两种表现形式:

>> full inverted index还存储了单词所在的位置信息,如code这个单词出如今(1∶6),即文档1的第6个单词为code。相比之下,full inverted index占用更多的空间,可是能更好地定位数据,并扩充一些其余的搜索特性。

>> InnoDB存储引擎从1.2.x版本开始支持全文检索的技术,其采用full inverted index的方式

>> 。在InnoDB存储引擎中,将(DocumentId,Position)视为一个“ilist”。所以在全文检索的表中,有两个列,一个是word字段,另外一个是ilist字段,而且在word字段上有设有索引

>> 正如以前所说的那样,倒排索引须要将word存放到一张表中,这个表称为Auxiliary Table(辅助表)。在InnoDB存储引擎中,为了提升全文检索的并行性能,共有6张Auxiliary Table,目前每张表根据word的Latin编码进行分区。

>> Auxiliary Table是持久的表,存放于磁盘上。然而在InnoDB存储引擎的全文索引中,还有另一个重要的概念FTS Index Cache(全文检索索引缓存),其用来提升全文检索的性能FTS Index Cache是一个红黑树结构,其根据(word,ilist)进行排序。这意味着插入的数据已经更新了对应的表,可是对全文索引的更新可能在分词操做后还在FTS Index Cache中,Auxiliary Table可能尚未更新。InnoDB存储引擎会批量对Auxiliary Table进行更新,而不是每次插入后更新一次Auxiliary Table

>> FTS Document ID是另一个重要的概念。在InnoDB存储引擎中,为了支持全文检索,必须有一个列与word进行映射,在InnoDB中这个列被命名为FTS_DOC_ID,其类型必须是BIGINT UNSIGNED NOT NULL,而且InnoDB存储引擎自动会在该列上加入一个名为FTS_DOC_ID_INDEX的Unique Index

>> 文档中分词的插入操做是在事务提交时完成,然而对于删除操做,其在事务提交时,不删除磁盘Auxiliary Table中的记录,而只是删除FTS Cache Index中的记录

>> 因为文档的DML操做实际并不删除索引中的数据,相反还会在对应的DELETED表中插入记录,所以随着应用程序的容许,索引会变得很是大,即便索引中的有些数据已经被删除,查询也不会选择这类记录。

>> 经过设置参数innodb_ft_aux_table来查看分词对应的信息:

>> SELECT * FROM information_schema.INNODB_FT_INDEX_TABLE;

>> 当前InnoDB存储引擎的全文检索还存在如下的限制:□ 每张表只能有一个全文检索的索引。□ 由多列组合而成的全文检索的索引列必须使用相同的字符集与排序规则。□ 不支持没有单词界定符(delimiter)的语言,如中文、日语、韩语等。

 

第6章 锁

 

>> 锁是数据库系统区别于文件系统的一个关键特性。锁机制用于管理对共享资源的并发访[插图]

>> 2005版本,Microsoft SQL Server开始支持乐观并发和悲观并发,在乐观并发下开始支持行级锁,可是其实现方式与InnoDB存储引擎的实现方式彻底不一样。用户会发如今Microsoft SQL Server下,锁是一种稀有的资源,锁越多开销就越大,所以它会有锁升级。在这种状况下,行锁会升级到表锁,这时并发的性能又回到了之前。

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

>> 对于InnoDB存储引擎中的latch,能够经过命令SHOW ENGINE INNODB MUTEX来进行查看

>> 相对于latch的查看,lock信息就显得直观多了。用户能够经过命令SHOW ENGINE INNODB STATUS及information_schema架构下的表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS来观察锁的信息

>> 若是一个事务T1已经得到了行r的共享锁,那么另外的事务T2能够当即得到行r的共享锁,由于读取并无改变行r的数据,称这种状况为锁兼容(Lock Compatible)。但如有其余的事务T3想得到行r的排他锁,则其必须等待事务T一、T2释放行r上的共享锁——这种状况称为锁不兼容。

>> S和X锁都是行锁,兼容是指对同一记录(row)锁的兼容性状况。

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

>> 在InnoDB 1.0版本以前,用户只能经过命令SHOW FULL PROCESSLIST,SHOW ENGINE INNODB STATUS等来查看当前数据库中锁的请求,而后再判断事务锁的状况。从InnoDB1.0开始,在INFORMATION_SCHEMA架构下添加了表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS。经过这三张表,用户能够更简单地监控当前事务并分析可能存在的锁问题

>> 在经过表INNODB_LOCKS查看了每张表上锁的状况后,用户就能够来判断由此引起的等待状况了。当事务较小时,用户就能够人为地、直观地进行判断了。可是当事务量很是大,其中锁和等待也时常发生,这个时候就不这么容易判断。可是经过表INNODB_LOCK_WAITS,能够很直观地反映当前事务的等待

>> 一致性的非锁定读(consistent nonlocking read)是指InnoDB存储引擎经过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据。若是读取的行正在执行DELETE或UPDATE操做,这时读取操做不会所以去等待行上锁的释放。相反地,InnoDB存储引擎会去读取行的一个快照数据。

>> 之因此称其为非锁定读,由于不须要等待访问的行上X锁的释放。快照数据是指该行的以前版本的数据,该实现是经过undo段来完成。而undo用来在事务中回滚数据,所以快照数据自己是没有额外的开销

>> 非锁定读机制极大地提升了数据库的并发性。在InnoDB存储引擎的默认设置下,这是默认的读取方式,即读取不会占用和等待表上的锁。可是在不一样事务隔离级别下,读取的方式不一样,并非在每一个事务隔离级别下都是采用非锁定的一致性读。此外,即便都是使用非锁定的一致性读,可是对于快照数据的定义也各不相同。

>> 一个行记录可能有不止一个快照数据,通常称这种技术为行多版本技术。由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control,MVCC)。

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

>> 须要特别注意的是,对于READ COMMITTED的事务隔离级别而言,从数据库理论的角度来看,其违反了事务ACID中的I的特性,即隔离性

>> 可是在某些状况下,用户须要显式地对数据库读取操做进行加锁以保证数据逻辑的一致性。而这要求数据库支持加锁语句,即便是对于SELECT的只读操做。InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操做:□ SELECT…FOR UPDATE□ SELECT…LOCK IN SHARE MODE

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

>> 对于一致性非锁定读,即便读取的行已被执行了SELECT…FOR UPDATE,也是能够进行读取的,这和以前讨论的状况同样。此外,SELECT…FOR UPDATE,SELECT…LOCK IN SHARE MODE必须在一个事务中,当事务提交了,锁也就释放了。

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

>> 虽然AUTO-INC Locking从必定程度上提升了并发插入的效率,但仍是存在一些性能上的问题。首先,对于有自增加值的列的并发插入性能较差,事务必须等待前一个插入的完成(虽然不用等待事务的完成)。其次,对于INSERT…SELECT的大数据量的插入会影响插入的性能,由于另外一个事务中的插入会被阻塞。

>> 从MySQL 5.1.22版本开始,InnoDB存储引擎中提供了一种轻量级互斥量的自增加实现机制,这种机制大大提升了自增加值插入的性能。

>> 还须要特别注意的是InnoDB存储引擎中自增加的实现和MyISAM不一样,MyISAM存储引擎是表锁设计,自增加不用考虑并发插入的问题。所以在master上用InnoDB存储引擎,在slave上用MyISAM存储引擎的replication架构下,用户必须考虑这种状况。

>> InnoDB存储引擎有3种行锁的算法,其分别是:□ Record Lock:单个行记录上的锁□ Gap Lock:间隙锁,锁定一个范围,但不包含记录自己□ Next-Key Lock∶Gap Lock+Record Lock,锁定一个范围,而且锁定记录自己

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

>> InnoDB存储引擎默认的事务隔离级别是REPEATABLE READ,在该隔离级别下,其采用Next-Key Locking的方式来加锁。而在事务隔离级别READ COMMITTED下,其仅采用Record Lock,所以在上述的示例中,会话A须要将事务的隔离级别设置为READ COMMITTED。

>> 不可重复读是指在一个事务内屡次读取同一数据集合。在这个事务尚未结束时,另一个事务也访问该同一数据集合,并作了一些DML操做。所以,在第一个事务中的两次读数据之间,因为第二个事务的修改,那么第一个事务两次读到的数据多是不同的。这样就发生了在一个事务内两次读到的数据是不同的状况,这种状况称为不可重复读。

>> 不可重复读和脏读的区别是:脏读是读到未提交的数据,而不可重复读读到的倒是已经提交的数据,可是其违反了数据库事务一致性的要求。

>> 在MySQL官方文档中将不可重复读的问题定义为Phantom Problem,即幻像问题。

>> 丢失更新是另外一个锁致使的问题,简单来讲其就是一个事务的更新操做会被另外一个事务的更新操做所覆盖,从而致使数据的不一致。

>> 由于不一样锁之间的兼容性关系,在有些时刻一个事务中的锁须要等待另外一个事务中的锁释放它所占用的资源,这就是阻塞。阻塞并非一件坏事,其是为了确保事务能够并发且正常地运行。

>> 在InnoDB存储引擎中,参数innodb_lock_wait_timeout用来控制等待的时间(默认是50秒),innodb_rollback_on_timeout用来设定是否在等待超时时对进行中的事务进行回滚操做(默认是OFF,表明不回滚)。

Spring应该是会帮咱们处理回滚的。本身用JDBC写咱们捕获异常后,通常也会手动回滚。

>须要牢记的是,在默认状况下InnoDB存储引擎不会回滚超时引起的错误异常。其实InnoDB存储引擎在大部分状况下都不会对异常进行回滚。

>> 解决死锁问题最简单的方式是不要有等待,将任何的等待都转化为回滚,而且事务从新开始。

>> 毫无疑问,这的确能够避免死锁问题的产生。然而在线上环境中,这可能致使并发性能的降低,甚至任何一个事务都不能进行。而这所带来的问题远比死锁问题更为严重,由于这很难被发现而且浪费资源。

>> 解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另外一个等待的事务就能继续进行。在InnoDB存储引擎中,参数innodb_lock_wait_timeout用来设置超时的时间。

>> 所以,除了超时机制,当前数据库还都广泛采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式

>> 经过上述的介绍,能够发现wait-for graph是一种较为主动的死锁检测机制,在每一个事务请求锁并发生等待时都会判断是否存在回路,若存在则有死锁,一般来讲InnoDB存储引擎选择回滚undo量最小的事务。

有并发就有可能发生,跟开发的实现也有关系,可是几率也比较小,出现具体问题的时候再具体分析!

>若是程序是串行的,那么不可能发生死锁。死锁只存在于并发的状况,而数据库自己就是一个并发运行的程序,所以可能会发生死锁

>> 若是程序是串行的,那么不可能发生死锁。死锁只存在于并发的状况,而数据库自己就是一个并发运行的程序,所以可能会发生死锁。

理论上Spring应该也是作了这个判断的…本身用JDBC实现的时候就要注意…

>还记得6.6节中所说的内容吗?InnoDB存储引擎并不会回滚大部分的错误异常,可是死锁除外。发现死锁后,InnoDB存储引擎会立刻回滚一个事务,这点是须要注意的。所以若是在应用程序中捕获了1213这个错误,其实并不须要对其进行回滚。

>> Oracle数据库中产生死锁的常见缘由是没有对外键添加索引,而InnoDB存储引擎会自动对其进行添加,于是可以很好地避免了这种状况的发生

>> 此外还存在另外一种死锁,即当前事务持有了待插入记录的下一个记录的X锁,可是在等待队列中存在一个S锁的请求,则可能会发生死锁

>> 锁升级(Lock Escalation)是指将当前锁的粒度下降。举例来讲,数据库能够把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁。若是在数据库的设计中认为锁是一种稀有资源,并且想避免锁的开销,那数据库中会频繁出现锁升级现象。Microsoft SQL Server数据库的设计认为锁是一种稀有的资源,在适合的时候会自动地将行、键或分页锁升级为更粗粒度的表级锁。这种升级保护了系统资源,防止系统使用太多的内存来维护锁,在必定程度上提升了效率。

>> InnoDB存储引擎不存在锁升级的问题。由于其不是根据每一个记录来产生行锁的,相反,其根据每一个事务访问的每一个页对锁进行管理的,采用的是位图的方式。所以无论一个事务锁住页中一个记录仍是多个记录,其开销一般都是一致的。

>> 假设一张表有3000 000个数据页,每一个页大约有100条记录,那么总共有300000 000条记录。如有一个事务执行全表更新的SQL语句,则须要对全部记录加X锁。若根据每行记录产生锁对象进行加锁,而且每一个锁占用10字节,则仅对锁管理就须要差很少须要3GB的内存。而InnoDB存储引擎根据页进行加锁,并采用位图方式,假设每一个页存储的锁信息占用30个字节,则锁对象仅需90MB的内存。

 

第7章 事务

 

>> 事务(Transaction)是数据库区别于文件系统的重要特性之一。在文件系统中,若是正在写文件,可是操做系统忽然崩溃了,这个文件就颇有可能被破坏。固然,有一些机制能够把文件恢复到某个时间点。不过,若是须要保证两个文件同步,这些文件系统可能就显得无能为力了。例如,在须要更新两个文件时,更新完一个文件后,在更新完第二个文件以前系统重启了,就会有两个不一样步的文件。

>> 这正是数据库系统引入事务的主要目的:事务会把数据库从一种一致状态转换为另外一种一致状态。

>> 理论上说,事务有着极其严格的定义,它必须同时知足四个特性,即一般所说的事务的ACID特性。值得注意的是,虽然理论上定义了严格的事务要求,可是数据库厂商出于各类目的,并无严格去知足事务的ACID标准。例如,对于MySQL的NDB Cluster引擎来讲,虽然其支持事务,可是不知足D的要求,即持久性的要求。对于Oracle数据库来讲,其默认的事务隔离级别为READ COMMITTED,不知足I的要求,即隔离性的要求。虽然在大多数的状况下,这并不会致使严重的结果,甚至可能还会带来性能的提高,可是用户首先须要知道严谨的事务标准,并在实际的生产应用中避免可能存在的潜在问题。对于InnoDB存储引擎而言,其默认的事务隔离级别为READ REPEATABLE,彻底遵循和知足事务的ACID特性。

原子性就是说,事务中的两个SQL确定都应该是一块儿执行或者一块儿不执行,就是一个组合体,最小单元。

>A(Atomicity),原子性

>> 若是事务中的操做都是只读的,要保持原子性是很简单的。一旦发生任何错误,要么重试,要么返回错误代码。由于只读操做不会改变系统中的任何相关部分。

事务提交之后,要么成功,要么失败,失败了就必定要回滚。

>C(consistency),一致性。一致性指事务将数据库从一种状态转变为下一种一致的状态

隔离性主要是处理和避免并发状况下出现的一些异常的问题。

>I(isolation),隔离性。隔离性还有其余的称呼,如并发控制(concurrency control)、可串行化(serializability)、锁(locking)等。

>> D(durability),持久性。事务一旦提交,其结果就是永久性的。即便发生宕机等故障,数据库也能将数据恢复。须要注意的是,只能从事务自己的角度来保证结果的永久性。例如,在事务提交后,全部的变化都是永久的。即便当数据库由于崩溃而须要恢复时,也能保证恢复后提交的数据都不会丢失。但若不是数据库自己发生故障,而是一些外部的缘由,如RAID卡损坏、天然灾害等缘由致使数据库发生问题,那么全部提交的数据可能都会丢失。所以持久性保证事务系统的高可靠性(High Reliability),而不是高可用性(High Availability)。

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

>> 带有保存点的扁平事务(Flat Transactions with Savepoint),除了支持扁平事务支持的操做外,容许在事务执行过程当中回滚到同一事务中较早的一个状态。这是由于某些事务可能在执行过程当中出现的错误并不会致使全部的操做都无效,放弃整个事务不合乎要求,开销也太大

>> 链事务(Chained Transaction)可视为保存点模式的一种变种。带有保存点的扁平事务,当发生系统崩溃时,全部的保存点都将消失,由于其保存点是易失的(volatile),而非持久的(persistent)。这意味着当进行恢复时,事务须要从开始处从新执行,而不能从最近的一个保存点继续执行。

>> 链事务的思想是:在提交一个事务时,释放不须要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务

>> 。注意,提交事务操做和开始下一个事务操做将合并为一个原子操做

>> 子事务既能够提交也能够回滚。可是它的提交操做并不立刻生效,除非其父事务已经提交。所以能够推论出,任何子事物都在顶层事务提交后才真正的提交。

>> 树中的任意一个事务的回滚会引发它的全部子事务一同回滚,故子事务仅保留A、C、I特性,不具备D的特性。

>> redo log称为重作日志,用来保证事务的原子性和持久性。undo log用来保证事务的一致性。

>> 有的DBA或许会认为undo是redo的逆过程,其实否则。redo和undo的做用均可以视为是一种恢复操做,redo恢复提交事务修改的页操做,而undo回滚行记录到某个特定版本。所以二者记录的内容不一样,redo一般是物理日志,记录的是页的物理修改操做。undo是逻辑日志,根据每行记录进行记录。

>> redo log基本上都是顺序写的,在数据库运行时不须要对redo log的文件进行读取操做。而undo log是须要进行随机读写的。

>> 二进制日志只在事务提交完成后进行一次写入。而InnoDB存储引擎的重作日志在事务进行中不断地被写入,这表现为日志并非随事务提交的顺序进行写入的。

>> InnoDB存储引擎在启动时无论上次数据库运行时是否正常关闭,都会尝试进行恢复操做。由于重作日志记录的是物理日志,所以恢复的速度比逻辑日志,如二进制日志,要快不少。与此同时,InnoDB存储引擎自身也对恢复进行了必定程度的优化,如顺序读取及并行应用重作日志,这样能够进一步地提升数据库恢复的速度。

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

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

>> 除了回滚操做,undo的另外一个做用是MVCC,即在InnoDB存储引擎中MVCC的实现是经过undo来完成。当用户读取一行记录时,若该记录已经被其余事务占用,当前事务能够经过undo读取以前的行版本信息,以此实现非锁定读取。

>> 事务提交后并不能立刻删除undo log及undo log所在的页。这是由于可能还有其余事务须要经过undo log来获得行记录以前的版本。故事务提交时将undo log放入一个链表中,是否能够最终删除undo log及undo log所在页由purge线程来判断。

>> 若为每个事务分配一个单独的undo页会很是浪费存储空间,特别是对于OLTP的应用类型

>> 所以,在InnoDB存储引擎的设计中对undo页能够进行重用。具体来讲,当事务提交时,首先将undo log放入链表中,而后判断undo页的使用空间是否小于3/4,如果则表示该undo页能够被重用,以后新的undo log记录在当前undo log的后面

>> History list length就表明了undo log的数量,这里为12。purge操做会减小该值。然而因为undo log所在的页能够被重用,所以即便操做发生,History list length的值也能够不为0。

>> update undo log记录的是对delete和update操做产生的undo log。该undo log可能须要提供MVCC机制,所以不能在事务提交时就进行删除

>> InnoSQL对information_schema进行了扩展,添加了两张数据字典表,这样用户能够很是方便和快捷地查看undo的信息。首先增长的数据字典表为INNODB_TRX_ROLLBACK_SEGMENT。

>> 另外一张数据字典表为INNODB_TRX_UNDO,用来记录事务对应的undo log,方便DBA和开发人员详细了解每一个事务产生的undo量

>> 经过上面的例子能够看到,delete操做并不直接删除记录,而只是将记录标记为已删除,也就是将记录的delete flag设置为1。而记录最终的删除是在purge操做中完成的。

>> 全局动态参数innodb_purge_batch_size用来设置每次purge操做须要清理的undo page数量。在InnoDB1.2以前,该参数的默认值为20。而从1.2版本开始,该参数的默认值为300。一般来讲,该参数设置得越大,每次回收的undo page也就越多,这样可供重用的undo page就越多,减小了磁盘存储空间与分配的开销。不过,若该参数设置得太大,则每次须要purge处理更多的undo page,从而致使CPU和磁盘IO过于集中于对undo log的处理,使性能降低。所以对该参数的调整须要由有经验的DBA来操做,而且须要长期观察数据库的运行的状态。正如官方的MySQL数据库手册所说的,普通用户不须要调整该参数。

>> 为了提升磁盘fsync的效率,当前数据库都提供了group commit的功能,即一次fsync能够刷新确保多个事务日志被写入文件。对于InnoDB存储引擎来讲,事务提交时会进行两个阶段的操做:1)修改内存中事务对应的信息,而且将日志写入重作日志缓冲。2)调用fsync将确保日志都从重作日志缓冲写入磁盘。

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

>> 用户能够经过参数completion_type来进行控制,该参数默认为0,表示没有任何操做。在这种设置下COMMIT和COMMIT WORK是彻底等价的。当参数completion_type的值为1时,COMMIT WORK等同于COMMIT AND CHAIN,表示立刻自动开启一个相同隔离级别的事务,

>> 参数completion_type为2时,COMMIT WORK等同于COMMIT AND RELEASE。在事务提交后会自动断开与服务器的链接

>> TRUNCATE TABLE语句是DDL,所以虽然和对整张表执行DELETE的结果是同样的,但它是不能被回滚的(这又是和Microsoft SQL Server数据不一样的地方)。

>> 计算TPS的方法是(com_commit+com_rollback)/time。可是利用这种方法进行计算的前提是:全部的事务必须都是显式提交的,若是存在隐式地提交和回滚(默认autocommit=1),不会计算到com_commit和com_rollback变量中。

>> 隔离级别越低,事务请求的锁越少或保持锁的时间就越短。这也是为何大多数数据库系统默认的事务隔离级别是READ COMMITTED。

>> 据了解,大部分的用户质疑SERIALIZABLE隔离级别带来的性能问题,可是根据Jim Gray在《Transaction Processing》一书中指出,二者的开销几乎是同样的,甚至SERIALIZABLE可能更优!!!所以在InnoDB存储引擎中选择REPEATABLE READ的事务隔离级别并不会有任何性能的损失

>> 由于InnoDB存储引擎在REPEATABLE READ隔离级别下就能够达到3°的隔离,所以通常不在本地事务中使用SERIALIABLE的隔离级别。SERIALIABLE的事务隔离级别主要用于InnoDB存储引擎的分布式事务。

>> XA事务容许不一样数据库之间的分布式事务,如一台服务器是MySQL数据库的,另外一台是Oracle数据库的,又可能还有一台服务器是SQL Server数据库的,只要参与在全局事务中的每一个节点都支持XA事务

>> 在单个节点上运行分布式事务没有太大的实际意义,可是要在MySQL数据库的命令下演示多个节点参与的分布式事务也是行不通的。一般来讲,都是经过编程语言来完成分布式事务的操做的。当前Java的JTA(Java Transaction API)能够很好地支持MySQL的分布式事务,须要使用分布式事务应该认真参考其API

>> 最为常见的内部XA事务存在于binlog与InnoDB存储引擎之间

>> 对于不一样语言的API,自动提交是不一样的。MySQL C API默认的提交方式是自动提交,而MySQL Python API则会自动执行SET AUTOCOMMIT=0,以禁用自动提交。所以在选用不一样的语言来编写数据库应用程序前,应该对链接MySQL的API作好研究。

>> 就像以前小节中所讲到的,对事务的BEGIN、COMMIT和ROLLBACK操做应该交给程序端来完成,存储过程须要完成的只是一个逻辑的操做,即对逻辑进行封装。

>> 长事务(Long-Lived Transactions),顾名思义,就是执行时间较长的事务。好比,对于银行系统的数据库,每过一个阶段可能须要更新对应帐户的利息。若是对应帐号的数量很是大,例如对有1亿用户的表account,须要执行下列语句

>> 在执行过程当中,当数据库或操做系统、硬件等发生问题时,从新开始事务的代价变得不可接受。数据库须要回滚全部已经发生的变化,而这个过程可能比产生这些变化的时间还要长。所以,对于长事务的问题,有时能够经过转化为小批量(mini batch)的事务来进行处理。当事务发生错误时,只须要回滚一部分数据,而后接着上次已完成的事务继续进行

>> 上述代码将一个须要处理1亿用户的大事务分解为每次处理10万用户的小事务,经过批量处理小事务来完成大事务的逻辑。每完成一个小事务,将完成的结果存放在batchcontext表中,表示已完成批量事务的最大帐号ID。

 

第8章 备份与恢复

 

>> 按照备份后文件的内容,备份又能够分为:□ 逻辑备份□ 裸文件备份在MySQL数据库中,逻辑备份是指备份出的文件内容是可读的,通常是文本文件。内容通常是由一条条SQL语句,或者是表内实际数据组成。如mysqldump和SELECT*INTO OUTFILE的方法。这类方法的好处是能够观察导出文件的内容,通常适用于数据库的升级、迁移等工做。但其缺点是恢复所须要的时间每每较长。裸文件备份是指复制数据库的物理文件,既能够是在数据库运行中的复制(如ibbackup、xtrabackup这类工具),也能够是在数据库中止运行时直接的数据文件复制。这类备份的恢复时间每每较逻辑备份短不少。

>> 对于MySQL数据库来讲,官方没有提供真正的增量备份的方法,大部分是经过二进制日志完成增量备份的工做。这种备份较之真正的增量备份来讲,效率仍是很低的

>> 最后,任什么时候候都须要作好远程异地备份,也就是容灾的防范。只是同一机房的两台服务器的备份是远远不够的。我曾经遇到的状况是,公司在2008年的汶川地震中发生一个机房可能被淹的的状况,这时远程异地备份显得就相当重要了。

>> replication的工做原理分为如下3个步骤:1)主服务器(master)把数据更改记录到二进制日志(binlog)中。2)从服务器(slave)把主服务器的二进制日志复制到本身的中继日志(relay log)中。3)从服务器重作中继日志中的日志,把更改应用到本身的数据库上,以达到数据的最终一致性。

>> 以前已经说过MySQL的复制是异步实时的,并不是彻底的主从同步。若用户要想得知当前的延迟,能够经过命令SHOW SLAVE STATUS和SHOW MASTER STATUS得知,如:

>> 假设当前应用采用了主从的复制架构,从服务器做为备份。这时,一个初级DBA执行了误操做,如DROP DATABASE或DROP TABLE,这时从服务器也跟着运行了。这时用户怎样从服务器进行恢复呢?所以,一个比较好的方法是经过对从服务器上的数据库所在分区作快照,以此来避免误操做对复制形成影响。当发生主服务器上的误操做时,只须要将从服务器上的快照进行恢复,而后再根据二进制日志进行point-in-time的恢复便可。

>> 还有一些其余的方法来调整复制,好比采用延时复制,即间歇性地开启从服务器上的同步,保证大约一小时的延时。这的确也是一个方法,只是数据库在高峰和非高峰期间每小时产生的二进制日志量是不一样的,用户很难精准地控制。另外,这种方法也不能彻底起到对误操做的防范做用。

>> 建议在从服务上启用read-only选项,这样能保证从服务器上的数据仅与主服务器进行同步,避免其余线程修改数据

 

第9章 性能调优

 

>> 另外一方面,闪存中的数据是不能够更新的,只能经过扇区(sector)的覆盖重写,而在覆盖重写以前,须要执行很是耗时的擦除(erase)操做。擦除操做不能在所含数据的扇区上完成,而须要在删除整个被称为擦除块的基础上完成,这个擦除块的尺寸大于扇区的大小,一般为128KB或者256KB。此外,每一个擦除块有擦写次数的限制。已经有一些算法来解决这个问题

>> 由于存在上述写入方面的问题,闪存提供的读写速度是非对称的。读取速度要远快于写入的速度,所以对于固态硬盘在数据库中的应用,应该好好利用其读取的性能,避免过多的写入操做。

>> 因为将多个硬盘组合成为一个逻辑扇区,RAID看起来就像一个单独的硬盘或逻辑存储单元,所以操做系统只会把它看成一个硬盘。

>> RAID 5具备和RAID 0相近似的数据读取速度,只是多了一个奇偶校验信息,写入数据的速度至关慢,若使用Write Back可让性能改善很多。

>> RAID 01比RAID 10有着更快的读写速度,不过也多了一些会让整个硬盘组中止运转的概率,由于只要同一组的硬盘所有损毁,RAID 01就会中止运做,而RAID 10能够在牺牲RAID 0的优点下正常运做。RAID 10巧妙地利用了RAID 0的速度及RAID 1的安全(保护)两种特性,它的缺点是须要较多的硬盘,由于至少必须拥有四个以上的偶数硬盘才能使用。

>> RAID Write Back功能是指RAID控制器可以将写入的数据放入自身的缓存中,并把它们安排到后面再执行。这样作的好处是,不用等待物理磁盘实际写入的完成,所以写入变得更快了

>> 对RAID卡进行配置能够在服务器启动时进入一个相似于BIOS的配置界面,而后再对其进行各类设置。此外,不少厂商都开发了各类操做系统下的软件对RAID进行配置,若是用户使用的是LSI公司生产提供的RAID卡,则能够使用MegaCLI工具来进行配置。

>> 特别须要注意地是,当RAID卡的写入策略从Write Back切换为Write Through时,该更改当即生效。然而从Write Through切换为Write Back时,必须重启服务器才能使其生效。

>> 基准测试工具能够用来对数据库或操做系统调优后的性能进行对比。MySQL数据库自己提供了一些比较优秀的工具,这里将介绍另外两款更为优秀和经常使用的基准测试工具:sysbench和mysql-tpcc。

>> 对于MySQL数据库的OLTP测试,和fileio同样须要经历prepare、run和cleanup阶段。prepare阶段会根据选项产生一张指定行数的表,默认表在sbtest架构下,表名为sbtest(sysbench默认生成表的存储引擎为InnoDB)。例如建立一张8000W的表:

>> TPC(Transaction Processing Performance Council,事务处理性能协会)是一个用来评价大型数据库系统软硬件性能的非盈利组织。TPC-C是TPC协会制定的,用来测试典型的复杂OLTP(在线事务处理)系统的性能。目前在学术界和工业界广泛采用TPC-C来评价OLTP应用的性能。

相关文章
相关标签/搜索