MySQL 5.1参考手册 :: 15. 存储引擎和表类型(2)
时间:2008-05-11 来源:sdccf
15.2.4. InnoDB启动选项
这一节叙述InnoDB相关的服务器选项,所有这些选项可以以--opt_name=value的形式在命令行或在选项文件里被指定。
· innodb_additional_mem_pool_size
InnoDB用来存储数据目录信息&其它内部数据结构的内存池的大小。你应用程序里的表越多,你需要在这里分配越多的内存。如果InnoDB用光了这个池内的内存,InnoDB开始从操作系统分配内存,并且往MySQL错误日志写警告信息。默认值是1MB。
· innodb_autoextend_increment
当自动扩展表空间被填满之时,为扩展而增加的尺寸(MB为单位)。默认值是8。这个选项可以在运行时作为全局系统变量而改变。
· innodb_buffer_pool_awe_mem_mb
如果缓冲池被放在32位Windows的AWE内存里,这个参数就是缓冲池的大小(MB为单位)。(仅在32位Windows上相关)如果你的32位Windows操作系统使用所谓的“地址窗口扩展(AWE)”支持超过4GB内存,你可以用这个参数把InnoDB缓冲池分配进AWE物理内存。这个参数最大的可能值是64000。如果这个参数被指定了,innodb_buffer_pool_size是在32位地址空间的mysqld内的窗口,InnoDB把那个AWE内存映射上去。对innodb_buffer_pool_size参数,一个比较好的值是500MB。
· innodb_buffer_pool_size
InnoDB用来缓存它的数据和索引的内存缓冲区的大小。你把这个值设得越高,访问表中数据需要得磁盘I/O越少。在一个专用的数据库服务器上,你可以设置这个参数达机器物理内存大小的80%。尽管如此,还是不要把它设置得太大,因为对物理内存的竞争可能在操作系统上导致内存调度。
· innodb_checksums
InnoDB在所有对磁盘的页面读取上使用校验和验证以确保额外容错防止硬件损坏或数据文件。尽管如此,在一些少见的情况下(比如运行标准检查之时)这个额外的安全特征是不必要的。在这些情况下,这个选项(默认是允许的)可以用--skip-innodb-checksums来关闭。
· innodb_data_file_path
到单独数据文件和它们尺寸的路径。通过把innodb_data_home_dir连接到这里指定的每个路径,到每个数据文件的完整目录路径可被获得。文件大小通过给尺寸值尾加M或G以MB或者GB(1024MB)为单位被指定。文件尺寸的和至少是10MB。在一些操作系统上,文件必须小于2GB。如果你没有指定innodb_data_file_path,开始的默认行为是创建一个单独的大小10MB名为ibdata1的自扩展数据文件。在那些支持大文件的操作系统上,你可以设置文件大小超过4GB。你也可以使用原始磁盘分区作为数据文件,请参阅15.2.14.2节,“为表空间使用原始设备”。
· innodb_data_home_dir
目录路径对所有InnoDB数据文件的共同部分。如果你不设置这个值,默认是MySQL数据目录。你也可以指定这个值为一个空字符串,在这种情况下,你可以在innodb_data_file_path中使用绝对文件路径。
· innodb_doublewrite
默认地,InnoDB存储所有数据两次,第一次存储到doublewrite缓冲,然后存储到确实的数据文件。这个选项可以被用来禁止这个功能。类似于innodb_checksums,这个选项默认是允许的;因为标准检查或在对顶级性能的需要超过对数据完整性或可能故障的关注之时,这个选项用--skip-innodb-doublewrite来关闭。
· innodb_fast_shutdown
如果你把这个参数设置为0,InnoDB在关闭之前做一个完全净化和一个插入缓冲合并。这些操作要花几分钟时间,设置在极端情况下要几个小时。如果你设置这个参数为1,InnoDB在关闭之时跳过这些操作。默认值为1。如果你设置这个值为2 (在Netware无此值), InnoDB将刷新它的日志然后冷关机,仿佛MySQL崩溃一样。已提交的事务不会被丢失,但在下一次启动之时会做一个崩溃恢复。
· innodb_file_io_threads
InnoDB中文件I/O线程的数量。正常地,这个参数是用默认的,默认值是4,但是大数值对Windows磁盘I/O有益。在Unix上,增加这个数没有效果,InnoDB总是使用默认值。
· innodb_file_per_table
这个选项致使InnoDB用自己的.ibd文件为存储数据和索引创建每一个新表,而不是在共享表空间中创建。请参阅15.2.6.6节,“使用Per-Table表空间”。
· innodb_flush_log_at_trx_commit
当innodb_flush_log_at_trx_commit被 设置为0,日志缓冲每秒一次地被写到日志文件,并且对日志文件做到磁盘操作的刷新,但是在一个事务提交不做任何操作。当这个值为1(默认值)之时,在每个事务提交时,日志缓冲被写到日志文件,对日志文件做到磁盘操作的刷新。当设置为2之时,在每个提交,日志缓冲被写到文件,但不对日志文件做到磁盘操作的刷新。尽管如此,在对日志文件的刷新在值为2的情况也每秒发生一次。我们必须注意到,因为进程安排问题,每秒一次的刷新不是100%保证每秒都发生。你可以通过设置这个值不为1来获得较好的性能,但随之你会在一次崩溃中损失二分之一价值的事务。如果你设置这个值为0,那么任何mysqld进程的崩溃会删除崩溃前最后一秒的事务,如果你设置这个值为2,那么只有操作系统崩溃或掉电才会删除最后一秒的事务。尽管如此,InnoDB的崩溃恢复不受影响,而且因为这样崩溃恢复开始作用而不考虑这个值。注意,许多操作系统和一些磁盘硬件会欺骗刷新到磁盘操作。尽管刷新没有进行,你可以告诉mysqld刷新已经进行。即使设置这个值为1,事务的持久程度不被保证,且在最坏情况下掉电甚至会破坏InnoDB数据库。在SCSI磁盘控制器中,或在磁盘自身中,使用有后备电池的磁盘缓存会加速文件刷新并且使得操作更安全。你也可以试着使用Unix命令hdparm来在硬件缓存中禁止磁盘写缓存,或使用其它一些对硬件提供商专用的命令。这个选项的默认值是1。
· innodb_flush_method
这个选项只在Unix系统上有效。如果这个选项被设置为fdatasync (默认值),InnoDB使用fsync()来刷新数据和日志文件。如果被设置为O_DSYNC,InnoDB使用O_SYNC来打开并刷新日志文件,但使用fsync()来刷新数据文件。如果O_DIRECT被指定了(在一些GNU/Linux版本商可用),InnoDB使用O_DIRECT来打开数据文件,并使用fsync()来刷新数据和日志文件。注意,InnoDB使用fsync()来替代fdatasync(),并且它默认不使用O_DSYNC,因为这个值在许多Unix变种上已经发生问题。
· innodb_force_recovery
警告:这个选项仅在一个紧急情况下被定义,当时你想要从损坏的数据库转储表。可能的值为从1到6。这些值的意思在15.2.8.1节,“强制恢复”中叙述。作为一个安全措施,当这个选项值大于零之时,InnoDB阻止用户修改数据。
· innodb_lock_wait_timeout
InnoDB事务在被回滚之前可以等待一个锁定的超时秒数。InnoDB在它自己的锁定表中自动检测事务死锁并且回滚事务。InnoDB用LOCK TABLES语句注意到锁定设置。默认值是50秒。
为在一个复制建立中最大可能的持久程度和连贯性,你应该在主服务器上的my.cnf文件里使用innodb_flush_log_at_trx_commit=1和sync-binlog=1。
· innodb_locks_unsafe_for_binlog
这个选项在InnoDB搜索和索引扫描中关闭下一键锁定。这个选项的默认值是假(false)。
正常地,InnoDB使用一个被称为next-key locking的算法。当搜索或扫描一个表索引之时,InnoDB以这样一种方式实行行级锁定,它对任何遇到的索引记录设置共享的或独占的锁定。因此,行级锁定实际是索引记录锁定。InnoDB对索引记录设置的锁定也影响被锁定索引记录之前的“gap”。如果一个用户对某一索引内的记录R又共享的或独占的锁定,另一个用户不能立即在R之前以索引的顺序插入一个新的索引记录。这个选项导致InnoDB不在搜索或索引扫描中使用下一键锁定。下一键锁定仍然被用来确保外键强制及重复键核查。注意,使用这个选项可能会导致一些诡异的问题:假设你想要用值大于100的标识符从子表里读取并锁定所有的子记录,同时向随后在选定的行更新一些列:
SELECT * FROM child WHERE id > 100 FOR UPDATE;
假设在id列有一个索引。查询从id大于100的第一个记录开始扫描索引。如果在索引记录上的锁定不把在间隙处生成的插入排除锁定,同时一个新行被插进表中。如果你在同一个事务之内执行同样的SELECT,你会在查询返回的结果包里看到一个新行。这也意味着,如果新条目被加进数据库,InnoDB不保证连续性;尽管如此,对应连续性仍被保证。因此,如果这个选项被使用,InnoDB在大多数孤立级别保证READ COMMITTED。
这个选项甚至更不安全。InnoDB在一个UPDATE或DELETE中只锁定它更新或删除的行。这大大减少了死锁的可能性,但是可以发生死锁。注意,即使在当类似的操作影响不同行时的情况下,这个选项仍然不允许诸如UPDATE这样的操作压倒相似选项(比如另一个UPDATE)。考虑下列例子:
CREATE TABLE A(A INT NOT NULL, B INT);
INSERT INTO A VALUES (1,2),(2,3),(3,2),(4,3),(5,2);
COMMIT;
如果一个连接执行一个查询:
SET AUTOCOMMIT = 0;
UPDATE A SET B = 5 WHERE B = 3;
并且其它连接跟着第一个连接执行其它查询:
SET AUTOCOMMIT = 0;
UPDATE A SET B = 4 WHERE B = 2;
接着查询2要等查询1的提交或回滚,因为查询1对行(2,3)有一个独占的锁定,并且查询2在扫描行的同时也试着对它不能锁定的同一个行(2,3)采取一个独占的锁定。这是因为当innodb_locks_unsafe_for_binlog选项被使用之时,查询2首先对一个行采取一个独占的锁定,然后确定是否这个行属于结果包,并且如果不属于,就释放不必要的锁定。
因此,查询1按如下执行:
x-lock(1,2)
unlock(1,2)
x-lock(2,3)
update(2,3) to (2,5)
x-lock(3,2)
unlock(3,2)
x-lock(4,3)
update(4,3) to (4,5)
x-lock(5,2)
unlock(5,2)
并且查询2按如下执行:
x-lock(1,2)
update(1,2) to (1,4)
x-lock(2,3) - 等待查询1提交或回滚
· innodb_log_arch_dir
如果我们使用日志档案,被完整写入的日志文件所在的目录也被归档。这个参数值如果被使用了,应该被设置得与innodb_log_group_home_dir一样。尽管如此,它不是必需的。
· innodb_log_archive
这个值当前被设为0。因为MySQL使用它自己的日志文件从备份来恢复,所以当前没有必要来归档InnoDB日志文件。这个选项的默认值是0。
· innodb_log_buffer_size
InnoDB用来往磁盘上的日志文件写操作的缓冲区的大小。明智的值是从1MB到8MB。默认的是1MB。一个大的日志缓冲允许大型事务运行而不需要在事务提交之前往磁盘写日志。因此,如果你有大型事务,使日志缓冲区更大以节约磁盘I/O。
· innodb_log_file_size
在日志组里每个日志文件的大小。在32位计算机上日志文件的合并大小必须少于4GB。默认是5MB。明智的值从1MB到N分之一缓冲池大小,其中N是组里日志文件的数目。值越大,在缓冲池越少需要检查点刷新行为,以节约磁盘I/O。但更大的日志文件也意味这在崩溃时恢复得更慢。
· innodb_log_files_in_group
在日志组里日志文件的数目。InnoDB以循环方式写进文件。默认是2(推荐)。
· innodb_log_group_home_dir
到InnoDB日志文件的目录路径。它必须有和innodb_log_arch_dir一样的值。如果你不指定任何InnoDB日志参数,默认的是在MySQL数据目录里创建两个5MB大小名为ib_logfile0和ib_logfile1的文件。
· innodb_max_dirty_pages_pct
这是一个范围从0到100的整数。默认是90。InnoDB中的主线程试着从缓冲池写页面,使得脏页(没有被写的页面)的百分比不超过这个值。如果你有SUPER权限,这个百分比可以在服务器运行时按下面来改变:
SET GLOBAL innodb_max_dirty_pages_pct = value;
· innodb_max_purge_lag
这个选项控制在净化操作被滞后之时,如何延迟INSERT, UPDATE和DELETE操作。(请参阅15.2.12节,“多版本的实施”)。这个参数的默认值是零,意为无延迟。这个选项可以在运行时作为全局系统变量而被改变。
InnoDB事务系统维持一个事务列表,该列表有被UPDATE或DELETE操作标志为删除的索引记录。让这个列表的长度为purge_lag。当purge_lag超过innodb_max_purge_lag之时,每个INSERT, UPDATE和DELETE操作延迟 ((purge_lag/innodb_max_purge_lag)*10)-5毫秒。在净化批处理的开始,延迟每隔10秒计算。如果因为一个旧的可以看到行被净化的一致的读查看,删除操作不被延迟。
对有问题的工作量,典型设置可能是1百万,假设我们的事务很小,只有100字节大小,我们就可以允许在我们的表之中有100MB未净化的行。
· innodb_mirrored_log_groups
我们为数据库保持的日志组内同样拷贝的数量。当前这个值应该被设为1。
· innodb_open_files
在InnoDB中,这个选项仅与你使用多表空间时有关。它指定InnoDB一次可以保持打开的.ibd文件的最大数目。最小值是10。默认值300。
对.ibd文件的文件描述符是仅对InnoDB的。它们独立于那些由--open-files-limit服务器选项指定的描述符,且不影响表缓存的操作。
· innodb_status_file
这个选项让InnoDB为周期的SHOW INNODB STATUS输出创建一个文件<datadir>/innodb_status.<pid>。
· innodb_support_xa
当被设置为ON或者1(默认地),这个变量允许InnoDB支持在XA事务中的双向提交。允许innodb_support_xa导致一个额外的对事务准备的磁盘刷新。如果你对使用XA并不关心,你可以通过设置这个选项为OFF或0来禁止这个变量,以减少磁盘刷新的次数并获得更好的InnoDB性能。
· innodb_table_locks
InnoDB重视LOCK TABLES,直到所有其它线程已经释放他们所有对表的锁定,MySQL才从LOCK TABLE .. WRITE返回。默认值是1,这意为LOCK TABLES让InnoDB内部锁定一个表。在使用AUTOCOMMIT=1的应用里,InnoDB的内部表锁定会导致死锁。你可以在my.cnf文件(Windows上是my.ini文件)里设置innodb_table_locks=0 来消除这个问题。
· innodb_thread_concurrency
InnoDB试着在InnoDB内保持操作系统线程的数量少于或等于这个参数给出的限制。如果有性能问题,并且SHOW INNODB STATUS显示许多线程在等待信号,可以让线程“thrashing” ,并且设置这个参数更小或更大。如果你的计算机有多个处理器和磁盘,你可以试着这个值更大以更好地利用计算机的资源。一个推荐的值是系统上处理器和磁盘的个数之和。值为500或比500大会禁止调用并发检查。默认值是20,并且如果设置大于或等于20,并发检查将被禁止。
· innodb_status_file
这个选项让InnoDB为周期的SHOW INNODB STATUS输出创建一个文件<datadir>/innodb_status.<pid>。
15.2.5. 创建InnoDB表空间
15.2.5.1. 处理InnoDB初始化问题
假设你已经安装了MySQL,并且已经编辑了选项文件,使得它包含必要的InnoDB配置参数。在启动MySQL之前,你应该验证你为InnoDB数据文件和日志文件指定的目录是否存在,并且MySQL有访问这些目录的权限。InnoDB不能创建目录,只能创建文件。也检查你有足够的空间来放数据和日志文件。
当创建InnoDB数据库时,最好从命令提示符运行MySQL服务器mysqld, 而不要从mysqld_safe包装或作为Windows的服务来运行。当你从命令提示符运行,你可看见mysqld打印什么以及发生了什么。在Unix上,只需要调用mysqld。在Windows上,使用--console选项。
当在选项文件里初始地配置InnoDB后,开始启动MySQL服务器之时,InnoDB创建一个数据文件和日志文件。InnoDB打印如下一些东西:
InnoDB: The first specified datafile /home/heikki/data/ibdata1
did not exist:
InnoDB: a new database to be created!
InnoDB: Setting file /home/heikki/data/ibdata1 size to 134217728
InnoDB: Database physically writes the file full: wait...
InnoDB: datafile /home/heikki/data/ibdata2 did not exist:
new to be created
InnoDB: Setting file /home/heikki/data/ibdata2 size to 262144000
InnoDB: Database physically writes the file full: wait...
InnoDB: Log file /home/heikki/data/logs/ib_logfile0 did not exist:
new to be created
InnoDB: Setting log file /home/heikki/data/logs/ib_logfile0 size
to 5242880
InnoDB: Log file /home/heikki/data/logs/ib_logfile1 did not exist:
new to be created
InnoDB: Setting log file /home/heikki/data/logs/ib_logfile1 size
to 5242880
InnoDB: Doublewrite buffer not found: creating new
InnoDB: Doublewrite buffer created
InnoDB: Creating foreign key constraint system tables
InnoDB: Foreign key constraint system tables created
InnoDB: Started
mysqld: ready for connections
一个新的InnoDB数据库被创建了。你可以用mysql这样通常的MySQL客户端程序连接到MySQL服务器。当你用mysqladmin shutdown关闭MySQL服务器之时,输出类似如下:
010321 18:33:34 mysqld: Normal shutdown
010321 18:33:34 mysqld: Shutdown Complete
InnoDB: Starting shutdown...
InnoDB: Shutdown completed
你可以看数据文件和日志文件,并且你可以看见文件被创建。日志目录也包含一个名为ib_arch_log_0000000000的小文件。这个文件是数据库被创建的结果,数据库被创建之后InnoDB切断日志归档。当MySQL再次启动之时,数据文件&日志文件已经被创建,所以输出更简洁:
InnoDB: Started
mysqld: ready for connections
你可以添加innodb_file_per_table选项到my.cnf文件,并且让InnoDB存储每一个表到MySQL数据库目录里自己的.ibd文件。请参阅15.2.6.6节,“使用Per-Table表空间”。
15.2.5.1. 处理InnoDB初始化问题
如果InnoDB在一个文件操作中打印一个操作系统错误,通常问题是如下中的一个:
· 你没有创建一个InnoDB数据文件目录或InnoDB日志目录。
· mysqld没有访问这些目录的权限以创建文件。
· mysqld不能恰当地读取my.cnf或my.ini选项文件,因此不能看到你指定的选项。
· 磁盘已满,或者超出磁盘配额。
· 你已经创建一个子目录,它的名字与你指定的数据文件相同。
· 在innodb_data_home_dir或innodb_data_file_path有一个语法错误。
当InnoDB试着初始化它的表空间或日志文件之时,如果出错了,你应该删除InnoDB创建的所有文件。这意味着是所有ibdata文件和所有ib_logfiles文件。万一你创建了一些InnoDB表,为这些表也从MySQL数据库目录删除相应的.frm文件(如果你使用多重表空间的话,也删除任何.ibd文件)。然后你可以试着再次创建InnoDB数据库。最好是从命令提示符启动MySQL服务器,以便你可以查看发生了什么。
15.2.6. 创建InnoDB表
15.2.6.1. 如何在InnoDB用不同API来使用事务
15.2.6.2. 转换MyISAM表到InnoDB
15.2.6.3. AUTO_INCREMENT列如何在InnoDB中工作
15.2.6.4.外键约束
15.2.6.5. InnoDB和MySQL复制
15.2.6.6. 使用Per-Table表空间
假如你用mysql test命令启动MySQL客户端。要创建一个InnoDB表,你必须在表创建SQL语句中指定ENGINE = InnoDB或者TYPE = InnoDB选项:
CREATE TABLE customers (a INT, b CHAR (20), INDEX (a)) ENGINE=InnoDB;
CREATE TABLE customers (a INT, b CHAR (20), INDEX (a)) TYPE=InnoDB;
SQL语句在表空间的列上创建一个表和索引,表空间包含你在my.cnf指定的数据文件。此外,MySQL在MySQL数据库目录下的test目录里创建一个名为customers.frm的文件。内部地,InnoDB为'test/customers'表往自己的数据目录添加一个条目。这意味这你可以在其它数据库创建一个具有相同名字customers的表,表的名字不会与InnoDB内的冲突。
你可以对任何InnoDB表,通过使用SHOW TABLE STATUS语句,查询在InnoDB表空间内空闲空间的数量。表空间内空闲空间的数量出现在SHOW TABLE STATUS的输出结果内的Comment节里。例如:
SHOW TABLE STATUS FROM test LIKE 'customers'
注意,统计的SHOW只给出关于InnoDB表的大概情况。它们被用于SQL优化。可是,表和索引保留的大小,以字节为单位是准确的。
15.2.6.1. 如何在InnoDB中用不同的API来使用事务
默认地,每个连接到MySQL服务器的客户端开始之时是允许自动提交模式的,这个模式自动提交你运行的每个SQL语句。要使用多语句事务,你可以用SQL语句SET AUTOCOMMIT = 0禁止自动提交,并且用COMMIT和ROLLBACK来提交或回滚你的事务。 如果你想要autocommit保持打开状态,可以在START TRANSACTION与COMMIT或ROLLBACK之间封装你的事务。下列的例子演示两个事务。第一个是被提交的,第二个是被回滚的:
shell> mysql test
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5 to server version: 3.23.50-log
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> CREATE TABLE CUSTOMER (A INT, B CHAR (20), INDEX (A))
-> ENGINE=InnoDB;
Query OK, 0 rows affected (0.00 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO CUSTOMER VALUES (10, 'Heikki');
Query OK, 1 row affected (0.00 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> SET AUTOCOMMIT=0;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO CUSTOMER VALUES (15, 'John');
Query OK, 1 row affected (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM CUSTOMER;
+------+--------+
| A | B |
+------+--------+
| 10 | Heikki |
+------+--------+
1 row in set (0.00 sec)
mysql>
在类似PHP, Perl DBI/DBD, JDBC, ODBC, 或者MySQL的标准C调用接口这样的API上,你能够以字符串形式发送事务控制语句,如COMMIT,到MySQL服务器,就像其它任何的SQL语句那样,诸如SELECT或INSERT。一些API也提供单独的专门的事务提交和回滚函数或者方法。
15.2.6.2. 转换MyISAM表到InnoDB
要点:你不应该在mysql数据库(比如,user或者host)里把MySQL系统表转换为InnoDB类型。系统表总是MyISAM型。
如果你想要所有(非系统)表都被创建成InnoDB表,你可以简单地把default-table-type=innodb行添加到my.cnf或my.ini文件的[mysqld]节里。
InnoDB对MyISAM存储引擎采用的单独索引创建方法没有做专门的优化。因此,它不值得导出或导入表以及随后创建索引。改变一个表为InnoDB型最快的办法就是直接插入进一个InnoDB表。即,使用ALTER TABLE ... ENGINE=INNODB,或用相同的定义创建一个空InnoDB表,并且用INSERT INTO ... SELECT * FROM ...插入行。
如果你对第二个键有UNIQUE约束,你可以在导入阶段设置:SET UNIQUE_CHECKS=0,以临时关掉唯一性检查好加速表的导入。对于大表,这节省了大量的磁盘I/O,因为InnoDB随后可以使用它的插入缓冲区来第二个索引记录作为一批来写入。
为获得对插入进程的更好控制,分段插入大表可能比较好:
INSERT INTO newtable SELECT * FROM oldtable
WHERE yourkey > something AND yourkey <= somethingelse;
所有记录已经本插入之后,你可以重命名表。
在大表的转换中,你应该增加InnoDB缓冲池的大小来减少磁盘I/O。尽管如此,不要使用超过80%的内部内存。你也可以增加InnoDB日志文件和日志文件的大小。
确信你没有填满表空间:InnoDB表比MyISAM表需要大得多的磁盘空间。如果一个ALTER TABLE耗尽了空间,它就开始一个回滚,并且如果它是磁盘绑定的,回滚可能要几个小时。对于插入,InnoDB使用插入缓冲区来以成批地合并第二个索引记录到索引中。那样节省了大量磁盘I/O。在回滚中,没有使用这样的机制,而回滚要花比插入长30倍的时间来完成。
在失控的回滚情况下,如果你在数据库中没有有价值的数据,比较明智的是杀掉数据库进程而不是等几百万个磁盘I/O被完成。 完整的过程,请参阅15.2.8.1节,“强制恢复”。
15.2.6.3. AUTO_INCREMENT列在InnoDB里如何工作
如果你为一个表指定AUTO_INCREMENT列,在数据词典里的InnoDB表句柄包含一个名为自动增长计数器的计数器,它被用在为该列赋新值。自动增长计数器仅被存储在主内存中,而不是存在磁盘上。
InnoDB使用下列算法来为包含一个名为ai_col的AUTO_INCREMENT列的表T初始化自动增长计数器:服务器启动之后,当一个用户对表T做插入之时,InnoDB执行等价如下语句的动作:
SELECT MAX(ai_col) FROM T FOR UPDATE;
语句取回的值逐次加一,并被赋给列和自动增长计数器。如果表是空的,值1被赋予该列。如果自动增长计数器没有被初始化,而且用户调用为表T显示输出的SHOW TABLE STATUS语句,则计数器被初始化(但不是增加计数)并被存储以供随后的插入使用。注意,在这个初始化中,我们对表做一个正常的独占读锁定,这个锁持续到事务的结束。
InnoDB对为新创建表的初始化自动增长计数器允许同样的过程。
注意,如果用户在INSERT中为AUTO_INCREMENT列指定NULL或者0,InnoDB处理行,就仿佛值还没有被指定,且为它生成一个新值。
自动增长计数器被初始化之后,如果用户插入一个明确指定该列值的行,而且该值大于当前计数器值,则计数器被设置为指定列值。如果没有明确指定一个值,InnoDB给计数器增加一,并且赋新值给该列。
当访问自动增长计数器之时,InnoDB使用专用的表级的AUTO-INC锁定,该锁持续到当前SQL语句的结束而不是到业务的结束。引入了专用锁释放策略,来为对一个含AUTO_INCREMENT列的表的插入改善部署。两个事务不能同时对同一表有AUTO-INC锁定。
注意,如果你回滚从计数器获得数的事务,你可能会在赋给AUTO_INCREMENT列的值的序列中发现间隙。
如果用户给列赋一个赋值,或者,如果值大过可被以指定整数格式存储的最大整数,自动增长机制的行为不被定义。
在CREATE TABLE和ALTER TABLE语句中,InnoDB支持AUTO_INCREMENT = n 表选项来设置计数器初始值或变更当前计数器值。因在本节早先讨论的原因,这个选项的影响在服务器重启后就无效了。
15.2.6.4.外键约束
InnoDB也支持外键约束。InnoDB中对外键约束定义的语法看起来如下:
[CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
外键定义服从下列情况:
· 所有tables必须是InnoDB型,它们不能是临时表。
· 在引用表中,必须有一个索引,外键列以同样的顺序被列在其中作为第一列。这样一个索引如果不存在,它必须在引用表里被自动创建。
· 在引用表中,必须有一个索引,被引用的列以同样的顺序被列在其中作为第一列。
· 不支持对外键列的索引前缀。这样的后果之一是BLOB和TEXT列不被包括在一个外键中,这是因为对这些列的索引必须总是包含一个前缀长度。
· 如果CONSTRAINTsymbol被给出,它在数据库里必须是唯一的。如果它没有被给出,InnoDB自动创建这个名字。
InnoDB拒绝任何试着在子表创建一个外键值而不匹配在父表中的候选键值的INSERT或UPDATE操作。一个父表有一些匹配的行的子表,InnoDB对任何试图更新或删除该父表中候选键值的UPDATE或DELETE操作有所动作,这个动作取决于用FOREIGN KEY子句的ON UPDATE和ON DETETE子句指定的referential action。当用户试图从一个父表删除或更新一行之时,且在子表中有一个或多个匹配的行,InnoDB根据要采取的动作有五个选择:
· CASCADE: 从父表删除或更新且自动删除或更新子表中匹配的行。ON DELETE CASCADE和ON UPDATE CASCADE都可用。在两个表之间,你不应定义若干在父表或子表中的同一列采取动作的ON UPDATE CASCADE子句。
· SET NULL: 从父表删除或更新行,并设置子表中的外键列为NULL。如果外键列没有指定NOT NULL限定词,这就是唯一合法的。ON DELETE SET NULL和ON UPDATE SET NULL子句被支持。
· NO ACTION: 在ANSI SQL-92标准中,NO ACTION意味这不采取动作,就是如果有一个相关的外键值在被参考的表里,删除或更新主要键值的企图不被允许进行(Gruber, 掌握SQL, 2000:181)。 InnoDB拒绝对父表的删除或更新操作。
· RESTRICT: 拒绝对父表的删除或更新操作。NO ACTION和RESTRICT都一样,删除ON DELETE或ON UPDATE子句。(一些数据库系统有延期检查,并且NO ACTION是一个延期检查。在MySQL中,外键约束是被立即检查的,所以NO ACTION和RESTRICT是同样的)。
· SET DEFAULT: 这个动作被解析程序识别,但InnoDB拒绝包含ON DELETE SET DEFAULT或ON UPDATE SET DEFAULT子句的表定义。
当父表中的候选键被更新的时候,InnoDB支持同样选择。选择CASCADE,在子表中的外键列被设置为父表中候选键的新值。以同样的方式,如果在子表更新的列参考在另一个表中的外键,更新级联。
注意,InnoDB支持外键在一个表内引用,在这些情况下,子表实际上意味这在表内附属的记录。
InnoDB需要对外键和被引用键的索引以便外键检查可以快速进行且不需要一个表扫描。对外键的索引被自动创建。这是相对于一些老版本,在老版本中索引必须明确创建,否则外键约束的创建会失败。
在InnoDB内,外键里和被引用列里相应的列必须有类似的内部数据类型,以便它们不需类型转换就可被比较。整数类型的大小和符号必须相同。字符串类型的长度不需要相同。如果你指定一个SET NULL动作,请确认你没有在子表中宣告该列为为NOT NULL。
如果MySQL从CREATE TABLE语句报告一个错误号1005,并且错误信息字符串指向errno 150,这意思是因为一个外键约束被不正确形成,表创建失败。类似地,如果ALTER TABLE失败,且它指向errno 150, 那意味着对已变更的表,外键定义会被不正确的形成。你可以使用SHOW INNODB STATUS来显示一个对服务器上最近的InnoDB外键错误的详细解释。
注释:InnoDB不对那些外键或包含NULL列的被引用键值检查外键约束。
对SQL标准的背离:如果在父表内有数个行,其中有相同的被引用键值,然后InnoDB在外键检查中采取动作,就仿佛其它有相同键值的父行不存在一样。例如,如果你已定义一个RESTRICT类型的约束,并且有一个带数个父行的子行,InnoDB不允许任何对这些父行的删除。
居于对应外键约束的索引内的记录,InnoDB通过深度优先选法施行级联操作。
对SQL标准的背离: 如果ON UPDATE CASCADE或ON UPDATE SET NULL递归更新相同的表,之前在级联过程中该表一被更新过,它就象RESTRICT一样动作。这意味着你不能使用自引用ON UPDATE CASCADE或者ON UPDATE SET NULL操作。这将阻止级联更新导致的无限循环。另一方面,一个自引用的ON DELETE SET NULL是有可能的,就像一个自引用ON DELETE CASCADE一样。级联操作不可以被嵌套超过15层深。
对SQL标准的背离: 类似一般的MySQL,在一个插入,删除或更新许多行的SQL语句内,InnoDB逐行检查UNIQUE和FOREIGN KEY约束。按照SQL的标准,默认的行为应被延迟检查,即约束仅在整个SQL语句被处理之后才被检查。直到InnoDB实现延迟的约束检查之前,一些事情是不可能的,比如删除一个通过外键参考到自身的记录。
注释:当前,触发器不被级联外键的动作激活。
一个通过单列外键联系起父表和子表的简单例子如下:
CREATE TABLE parent(id INT NOT NULL,
PRIMARY KEY (id)
) TYPE=INNODB;
CREATE TABLE child(id INT, parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
ON DELETE CASCADE
) TYPE=INNODB;
如下是一个更复杂的例子,其中一个product_order表对其它两个表有外键。一个外键引用一个product表中的双列索引。另一个引用在customer表中的单行索引:
CREATE TABLE product (category INT NOT NULL, id INT NOT NULL,
price DECIMAL,
PRIMARY KEY(category, id)) TYPE=INNODB;
CREATE TABLE customer (id INT NOT NULL,
PRIMARY KEY (id)) TYPE=INNODB;
CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT,
product_category INT NOT NULL,
product_id INT NOT NULL,
customer_id INT NOT NULL,
PRIMARY KEY(no),
INDEX (product_category, product_id),
FOREIGN KEY (product_category, product_id)
REFERENCES product(category, id)
ON UPDATE CASCADE ON DELETE RESTRICT,
INDEX (customer_id),
FOREIGN KEY (customer_id)
REFERENCES customer(id)) TYPE=INNODB;
InnoDB允许你用ALTER TABLE往一个表中添加一个新的外键约束:
ALTER TABLE yourtablename
ADD [CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
记住先创建需要的索引。你也可以用ALTER TABLE往一个表添加一个自引用外键约束。
InnoDB也支持使用ALTER TABLE来移除外键:
ALTER TABLE yourtablename DROP FOREIGN KEY fk_symbol;
当年创建一个外键之时,如果FOREIGN KEY子句包括一个CONSTRAINT名字,你可以引用那个名字来移除外键。另外,当外键被创建之时,fk_symbol值被InnoDB内部保证。当你想要移除一个外键之时,要找出标记,请使用SHOW CREATE TABLE语句。例子如下:
mysql> SHOW CREATE TABLE ibtest11c\G
*************************** 1. row ***************************
Table: ibtest11c
Create Table: CREATE TABLE `ibtest11c` (
`A` int(11) NOT NULL auto_increment,
`D` int(11) NOT NULL default '0',
`B` varchar(200) NOT NULL default '',
`C` varchar(175) default NULL,
PRIMARY KEY (`A`,`D`,`B`),
KEY `B` (`B`,`C`),
KEY `C` (`C`),
CONSTRAINT `0_38775` FOREIGN KEY (`A`, `D`)
REFERENCES `ibtest11a` (`A`, `D`)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `0_38776` FOREIGN KEY (`B`, `C`)
REFERENCES `ibtest11a` (`B`, `C`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB CHARSET=latin1
1 row in set (0.01 sec)
mysql> ALTER TABLE ibtest11c DROP FOREIGN KEY 0_38775;
InnoDB解析程序允许你在FOREIGN KEY ... REFERENCES ...子句中用`(backticks)把表和列名名字围起来。InnoDB解析程序也考虑到lower_case_table_names系统变量的设置。
InnoDB返回一个表的外键定义作为SHOW CREATE TABLE语句输出的一部分:
SHOW CREATE TABLE tbl_name;
从这个版本起,mysqldump也将表的正确定义生成到转储文件中,且并不忘记外键。
你可以如下对一个表显示外键约束:
SHOW TABLE STATUS FROM db_name LIKE 'tbl_name';
外键约束被列在输出的Comment列。
当执行外键检查之时,InnoDB对它照看着的子或父记录设置共享的行级锁。InnoDB立即检查外键约束,检查不对事务提交延迟。
要使得对有外键关系的表重新载入转储文件变得更容易,mysqldump自动在转储输出中包括一个语句设置FOREIGN_KEY_CHECKS为0。这避免在转储被重新装载之时,与不得不被以特别顺序重新装载的表相关的问题。也可以手动设置这个变量:
mysql> SET FOREIGN_KEY_CHECKS = 0;
mysql> SOURCE dump_file_name;
mysql> SET FOREIGN_KEY_CHECKS = 1;
如果转储文件包含对外键是不正确顺序的表,这就以任何顺序导入该表。这样也加快导入操作。设置FOREIGN_KEY_CHECKS为0,对于在LOAD DATA和ALTER TABLE操作中忽略外键限制也是非常有用的。
InnoDB不允许你删除一个被FOREIGN KEY表约束引用的表,除非你做设置SET FOREIGN_KEY_CHECKS=0。当你移除一个表的时候,在它的创建语句里定义的约束也被移除。
如果你重新创建一个被移除的表,它必须有一个遵从于也引用它的外键约束的定义。它必须有正确的列名和类型,并且如前所述,它必须对被引用的键有索引。如果这些不被满足,MySQL返回错误号1005 并在错误信息字符串中指向errno 150。
15.2.6.5. InnoDB和MySQL复制
MySQL复制就像对MyISAM表一样,也对InnoDB表起作用。以某种方式使用复制也是可能的,在这种方式中从服务器上表的类型不同于主服务器上原始表的类型。例如,你可以复制修改到主服务器上一个InnoDB表,到从服务器上一个MyISAM表里。
要为一个主服务器建立一个新服务器,你不得不复制InnoDB表空间和日志文件,以及InnoDB表的.frm文件,并且移动复件到从服务器。关于其恰当步骤请参阅15.2.9节,“移动InnoDB数据库到另一台机器”。
如果你可以关闭主服务器或者一个存在的从服务器。你可以采取InnoDB表空间和日志文件的冷备份,并用它来建立一个从服务器。要不关闭任何服务器来建立一个新的从服务器,你也可以使用非免费(商业的)InnoDB热备份工具。
InnoDB复制里一个小的限制是LOAD TABLE FROM MASTER不对InnoDB类型表起作用。有两种可能的工作区:
· 转储主服务器上的表并导入转储文件到从服务器。
· 在用LOAD TABLE tbl_name FROM MASTER建立复制之前,在主服务器上使用ALTER TABLE tbl_name TYPE=MyISAM,并且随后使用ALTER TABLE把主表转换回InnoDB。
在主服务器失败的事务根本不影响复制。MySQL复制基于二进制日志,MySQ在那里写修改数据的SQL语句。从服务器读主服务器的二进制日志,并执行同样的SQL语句。尽管如此,在事务里发生的语句不被写进二进制日志直到事务提交,在那一刻,事务里所有语句被一次性写进日志。如果语句失败了,比如,因为外键违例,或者,如果事务被回滚,没有SQL语句被写进二进制日志,并且事务根本不在从服务器上执行。