MySQL中 InnoDB 和 MyISAM 小结

614 阅读20分钟

InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定。基本的差别为:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持已经外部键等高级数据库功能。 以下是一些细节和具体实现的差别:

  1. InnoDB不支持FULLTEXT类型的索引。
  2. InnoDB 中不保存表的具体行数,也就是说,执行select count() from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含 where条件时,两种表的操作是一样的。
  3. 对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
  4. DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
  5. LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。
  6. InnoDB表的行锁也不是绝对的,假如在执行一个SQL语句时mysql不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”   两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁.而MyISAM不支持.所以MyISAM往往就容易被人认为只适合在小项目中使用。   我作为使用MySQL的用户角度出发,Innodb和MyISAM都是比较喜欢的,但是从我目前运维的数据库平台要达到需求:99.9%的稳定性,方便的扩展性和高可用性来说的话,MyISAM绝对是我的首选。

原因如下:   1、首先我目前平台上承载的大部分项目是读多写少的项目,而MyISAM的读性能是比Innodb强不少的。   2、MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而Innodb是索引和数据是紧密捆绑的,没有使用压缩从而会造成Innodb比MyISAM体积庞大不小。   3、从平台角度来说,经常隔1,2个月就会发生应用开发人员不小心update一个表where写的范围不对,导致这个表没法正常用了,这个时候MyISAM的优越性就体现出来了,随便从当天拷贝的压缩包取出对应表的文件,随便放到一个数据库目录下,然后dump成sql再导回到主库,并把对应的binlog补上。如果是Innodb,恐怕不可能有这么快速度,别和我说让Innodb定期用导出xxx.sql机制备份,因为我平台上最小的一个数据库实例的数据量基本都是几十G大小。   4、从我接触的应用逻辑来说,select count() 和order by 是最频繁的,大概能占了整个sql总语句的60%以上的操作,而这种操作Innodb其实也是会锁表的,很多人以为Innodb是行级锁,那个只是where对它主键是有效,非主键的都会锁全表的。   5、还有就是经常有很多应用部门需要我给他们定期某些表的数据,MyISAM的话很方便,只要发给他们对应那表的frm.MYD,MYI的文件,让他们自己在对应版本的数据库启动就行,而Innodb就需要导出xxx.sql了,因为光给别人文件,受字典数据文件的影响,对方是无法使用的。   6、如果和MyISAM比insert写操作的话,Innodb还达不到MyISAM的写性能,如果是针对基于索引的update操作,虽然MyISAM可能会逊色Innodb,但是那么高并发的写,从库能否追的上也是一个问题,还不如通过多实例分库分表架构来解决。   7、如果是用MyISAM的话,merge引擎可以大大加快应用部门的开发速度,他们只要对这个merge表做一些select count()操作,非常适合大项目总量约几亿的rows某一类型(如日志,调查统计)的业务表。   当然Innodb也不是绝对不用,用事务的项目如模拟炒股项目,我就是用Innodb的,活跃用户20多万时候,也是很轻松应付了,因此我个人也是很喜欢Innodb的,只是如果从数据库平台应用出发,我还是会首选MyISAM。   另外,可能有人会说你MyISAM无法抗太多写操作,但是我可以通过架构来弥补,说个我现有用的数据库平台容量:主从数据总量在几百T以上,每天十多亿 pv的动态页面,还有几个大项目是通过数据接口方式调用未算进pv总数,(其中包括一个大项目因为初期memcached没部署,导致单台数据库每天处理 9千万的查询)。而我的整体数据库服务器平均负载都在0.5-1左右。

MyISAM

  InnoDB

构成上的区别:

  每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。

.frm文件存储表定义。

数据文件的扩展名为.MYD (MYData)。

索引文件的扩展名是.MYI (MYIndex)。

  基于磁盘的资源是InnoDB表空间数据文件和它的日志文件,InnoDB 表的大小只受限于操作系统文件的大小,一般为 2GB

事务处理上方面:

  MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持

  InnoDB提供事务支持事务,外部键等高级数据库功能

SELECT UPDATE,INSERT,Delete操作 如果执行大量的SELECT,MyISAM是更好的选择

  1.如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表

2.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。

3.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用

对AUTO_INCREMENT的操作

  每表一个AUTO_INCREMEN列的内部处理。

MyISAM为INSERT和UPDATE操作自动更新这一列。这使得AUTO_INCREMENT列更快(至少10%)。在序列顶的值被删除之后就不能再利用。(当AUTO_INCREMENT列被定义为多列索引的最后一列,可以出现重使用从序列顶部删除的值的情况)。

AUTO_INCREMENT值可用ALTER TABLE或myisamch来重置

对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引

更好和更快的auto_increment处理

  如果你为一个表指定AUTO_INCREMENT列,在数据词典里的InnoDB表句柄包含一个名为自动增长计数器的计数器,它被用在为该列赋新值。

自动增长计数器仅被存储在主内存中,而不是存在磁盘上

关于该计算器的算法实现,请参考

AUTO_INCREMENT列在InnoDB里如何工作

表的具体行数 select count() from table,MyISAM只要简单的读出保存好的行数,注意的是,当count()语句包含 where条件时,两种表的操作是一样的

  InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行

锁 表锁

  提供行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in

SELECTs),另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

InnoDB 和 MyISAM,是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。下面是已知的两者之间的差别,仅供参考。

InnoDB InnoDB 给 MySQL 提供了具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。 InnoDB 提供了行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in SELECTs)。这些特性均提高了多用户并发操作的性能表现。在InnoDB表中不需要扩大锁定(lock escalation),因为InnoDB 的列锁定(row level locks)适宜非常小的空间。InnoDB 是 MySQL 上第一个提供外键约束(FOREIGNKEY constraints)的表引擎。 InnoDB 的设计目标是处理大容量数据库系统,它的 CPU 利用率是其它基于磁盘的关系数据库引擎所不能比的。在技术上,InnoDB 是一套放在 MySQL 后台的完整数据库系统,InnoDB 在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。 InnoDB 把数据和索引存放在表空间里,可能包含多个文件,这与其它的不一样,举例来说,在 MyISAM 中,表被存放在单独的文件中。InnoDB 表的大小只受限于操作系统的文件大小,一般为 2 GB。 在 http://www.innodb.com/ 上可以找到 InnoDB 最新的信息。InnoDB 手册的最新版本总是被放置在那里,并且在那里可以得到 InnoDB 的商业许可(order commercial licenses)以及支持。 InnoDB 现在(2001年十月)在一些大的需高性能的数据库站点上被使用。著名的 Internet 新闻站点 Slashdot.org就是使用的 InnoDB。 Mytrix, Inc. 在 InnoDB 表上存储了超过 1 TB 的数据,而且另外的一个站点在 InnoDB 表上处理着平均每秒 800 次的插入/更新的负载。

MyISAM MyISAM 是MySQL缺省存贮引擎 . 每张MyISAM 表被存放在盘在三个文件 frm 文件存放表格定义 数据文件是MYD (MYData) 索引文件是MYI (MYIndex) 引伸 以下是一些细节和具体实现的差别: 1.InnoDB不支持FULLTEXT类型的索引。 2.InnoDB 中不保存表的具体行数,也就是说,执行select count() from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含where条件时,两种表的操作是一样的。 3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。 4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。 5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。 另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%” 任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。 MyIASM是IASM表的新版本,有如下扩展: 1. NULL列索引。
2. 对变长行比ISAM表有更少的碎片。
3. 支持大文件。
4. 更好的索引压缩。
5. 更好的键码统计分布。
6. 更好和更快的auto_increment处理。 7. 二进制层次的可移植性。 转载声明:本文转自(http://www.diybl.com/course/7_databases/mysql/Mysqljs/2008924/145307.html)

MySQL InnoDB 的性能问题讨论

MySQL最为人垢病的缺点就是缺乏事务的支持,MyISAM 性能虽然出众,不是没有代价的,InnoDB 又如何呢?InnoDB 的磁盘性能很令人担心,MySQL 缺乏良好的 tablespace 真是天大的缺陷!

InnoDB的表空间分成三种,一种是裸设备,一种是若干个 ibdata 文件(缺省方式),再一种是 Per-Table 文件,第一种用得少,第二种显然比第三种效率更差,本文的讨论基于 Per-Table,也即 innodb_file_per_table 配置参数。

现象重现:导出一个几百万行数据、带若干索引、有过频繁更新的表出来再导入,如果能以真实环境下的表来做测试就更理想,到 data 目录下观察对应的数据文件的 size 增长情况,会发现前 1G 速度相当令人满意,可是越往后效率越低,到后面基本就是蜗牛般的速度了。

不是只有导入才会让你慢得受不了,alter column/index 都会这样。。。

InnoDB 跟磁盘相关的文件存储,可以分成两个部分,一个是日志文件,另一个是数据文件。当有频繁的 INSERT/UPDATE 操作的时候,InnoDB 需要分别写入这两个文件,日志文件是顺序操作,数据文件包括了表数据和索引数据两个部分(和 MyISAM 直接拆开成表文件和索引文件不同,InnoDB 的表和索引是在同一个文件当中的)。

InnoDB 的索引用的是 BTREE 格式,如果当前更新的记录影响到索引的变化,逻辑上就存在三个操作,从原来的 BTREE 找到并摘除原来这行的记录并做调整、插入行数据、根据新数据查找 BTREE 相应的位置并重新插入新索引信息,假设索引数为 N,相应的逻辑操作数就为 1 + 2N,显然这些信息不能保证在同一个磁盘连续空间上,因此需要 1 + 2N 次的磁头移动,行数越大、文件尺寸越大,磁头的移动幅度也就可能越大,带来的后果显然是极差的磁盘 IO 效率。

MySQL 对于 MyISAM 的的磁盘 IO 优化是如何建议的呢?使用符号链接将表文件和索引文件分别指向不同的不同的目录,分散到不同的磁盘上以增加系统的访问速度。这种优化方式,在 InnoDB 上完全没有可能性!

如果有 tablespace 支持,磁盘效率问题就好解决了,一如商业数据库的做法,将日志、表文件、索引文件分别分布到不同的表空间也就是物理磁盘上,可是 MySQL 一直到 5.1 都没有提供 tablespace 功能,仅在 NDB/NDBCLUSTER 中才提供,但是 -- "CREATE TABLESPACE was added in MySQL 5.1.6. In MySQL 5.1, it is useful only with Disk Data storage for MySQL Cluster."。

不知道 Yahoo 等大网站是怎么解决这个难题的。。。头痛。。。考虑切换到 PostgreSQL 中。。。

转载声明:本文转自http://www.javaeye.com/topic/34676

MySQL提供了数据库的同步功能,这对我们实现数据库的冗灾、备份、恢复、负载均衡等都是有极大帮助的。本文描述了常见的同步设置方法。 一、准备服务器 由于MySQL不同版本之间的(二进制日志)binlog格式可能会不一样,因此最好的搭配组合是Master的MySQL版本和Slave的版本相同或者更低,Master的版本肯定不能高于Slave版本。 本文中,我们假设主服务器(以下简称Master)和从服务器(以下简称Slave)的版本都是5.0.15,操作系统是Linux Ubuntu 5.0.x。 假设同步Master的主机名为:rep1,Slave主机名为:rep2,2个MySQL的basedir目录都是/usr/local/mysql,datadir都是:/usr/local/MySQL/data。 二、设置同步服务器 1、设置同步Master 每个同步服务器都必须设定一个唯一的编号,否则同步就不能正常运行了。接下来开始修改 my.cnf,增加以下几行: server-id = 1 log-bin set-variable=binlog-ignore-db=MySQL 然后在Master上增加一个账号专门用于同步,如下: MySQL>GRANT REPLICATION SLAVE ON . TO rep@rep2 IDENTIFIED BY 'rep'; 如果想要在Slave上有权限执行 "LOAD TABLE FROM MASTER" 或 "LOAD DATA FROM MASTER" 语句的话,必须授予全局的 FILE 和 SELECT 权限: MySQL>GRANT FILE,SELECT,REPLICATION SLAVE ON . TO rep@rep2 IDENTIFIED BY 'rep'; 第三行表示不记录数据库MySQL的更新日志,这就避免了Master上的权限设置等被同步到Slave上,如果对这方面没有限制,就可以不设置这个参数。 接下来备份Master上的数据,首先执行如下SQL语句: MySQL>FLUSH TABLES WITH READ LOCK; 不要退出这个终端,否则这个锁就不生效了;接着导出数据,可以直接打包压缩数据文件,也可以使用MySQLdump工具来做,推荐前者的方法,这样更为快捷简便。 root$cd /usr/local/MySQL root$tar zcf data.tar.gz ./data (在这里也可能是 "var" 等其它实际存放数据文件的目录,根据实情而定) 然后将这些数据拷贝到Slave服务器上,解开,设置好正确的权限及属主等;之后,执行 "UNLOCK TABLES" 语句来释放锁。 2、设置Slave 修改my.cnf,增加如下几行: server-id = 2 master-host = rep1 #主服务器名 master-user = rep #同步账户名,默认是test master-password = rep #同步帐户密码,默认是空 master-port = 3306 #主服务器的 TCP/IP 端口号,默认是3306 set-variable=replicate-ignore-db=MySQL #略过同步的数据库名,如果有多个,请设置多次 set-variable=replicate-do-db=yejr #想要同步的数据库名,如果有多个,请设置多次 接下来在Slave上检验一下是否能正确连接到Master上,并且具备相应的权限。 root$MySQL -hrep1 -urep -prep MySQL>SHOW GRANTS; +---------------------------------------------------------------------------------------------------------------------+ | Grants for rep@rep2 | +---------------------------------------------------------------------------------------------------------------------+ | GRANT SELECT, FILE, REPLICATION SLAVE ON . TO 'rep'@'rep2' IDENTIFIED BY PASSWORD '*9FF2C222F44C7BBA5CC7E3BE8573AA4E1776278C' | +---------------------------------------------------------------------------------------------------------------------+ 现在,可以启动Slave了。启动成功后,登录Slave,查看一下同步状态: MySQL -hlocalhost -uroot MySQL>SHOW SLAVE STATUS/G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: rep1 Master_User: rep Master_Port: 3306 Connect_Retry: 60 Master_Log_File: binlog.000001 Read_Master_Log_Pos: 98 Relay_Log_File: relay.000003 Relay_Log_Pos: 232 Relay_Master_Log_File: binlog.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 98 Relay_Log_Space: 232 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 1 row in set (0.00 sec) 可以看到,Slave_IO_Running 和 Slave_SQL_Running 两列的值都为 "Yes",这表明 Slave 的 I/O 和 SQL 线程都在正常运行。

MySQL服务器安装完之后如何调节性能 key_buffer_size - 这对MyISAM表来说非常重要。如果只是使用MyISAM表,可以把它设置为可用内存的 30-40%。合理的值取决于索引大小、数据量以及负载 -- 记住,MyISAM表会使用操作系统的缓存来缓存数据,因此需要留出部分内存给它们,很多情况下数据比索引大多了。尽管如此,需要总是检查是否所有的 key_buffer 都被利用了 -- .MYI 文件只有 1GB,而 key_buffer 却设置为 4GB 的情况是非常少的。这么做太浪费了。如果你很少使用MyISAM表,那么也保留低于 16-32MB 的 key_buffer_size 以适应给予磁盘的临时表索引所需。

innodb_buffer_pool_size - 这对Innodb表来说非常重要。Innodb相比MyISAM表对缓冲更为敏感。MyISAM可以在默认的 key_buffer_size 设置下运行的可以,然而Innodb在默认的 innodb_buffer_pool_size 设置下却跟蜗牛似的。由于Innodb把数据和索引都缓存起来,无需留给操作系统太多的内存,因此如果只需要用Innodb的话则可以设置它高达 70-80% 的可用内存。一些应用于 key_buffer 的规则有 -- 如果你的数据量不大,并且不会暴增,那么无需把 innodb_buffer_pool_size 设置的太大了。

innodb_additional_pool_size - 这个选项对性能影响并不太多,至少在有差不多足够内存可分配的操作系统上是这样。不过如果你仍然想设置为 20MB(或者更大),因此就需要看一下Innodb其他需要分配的内存有多少。

innodb_log_file_size 在高写入负载尤其是大数据集的情况下很重要。这个值越大则性能相对越高,但是要注意到可能会增加恢复时间。我经常设置为 64-512MB,跟据服务器大小而异。

innodb_log_buffer_size 默认的设置在中等强度写入负载以及较短事务的情况下,服务器性能还可以。如果存在更新操作峰值或者负载较大,就应该考虑加大它的值了。如果它的值设置太高了,可能会浪费内存 -- 它每秒都会刷新一次,因此无需设置超过1秒所需的内存空间。通常 8-16MB 就足够了。越小的系统它的值越小。

innodb_flush_logs_at_trx_commit 是否为Innodb比MyISAM慢1000倍而头大?看来也许你忘了修改这个参数了。默认值是 1,这意味着每次提交的更新事务(或者每个事务之外的语句)都会刷新到磁盘中,而这相当耗费资源,尤其是没有电池备用缓存时。很多应用程序,尤其是从 MyISAM转变过来的那些,把它的值设置为 2 就可以了,也就是不把日志刷新到磁盘上,而只刷新到操作系统的缓存上。日志仍然会每秒刷新到磁盘中去,因此通常不会丢失每秒1-2次更新的消耗。如果设置为 0 就快很多了,不过也相对不安全了 -- MySQL服务器崩溃时就会丢失一些事务。设置为 2 指挥丢失刷新到操作系统缓存的那部分事务。

table_cache -- 打开一个表的开销可能很大。例如MyISAM把MYI文件头标志该表正在使用中。你肯定不希望这种操作太频繁,所以通常要加大缓存数量,使得足以最大限度地缓存打开的表。它需要用到操作系统的资源以及内存,对当前的硬件配置来说当然不是什么问题了。如果你有200多个表的话,那么设置为 1024 也许比较合适(每个线程都需要打开表),如果连接数比较大那么就加大它的值。我曾经见过设置为 100,000 的情况。

thread_cache -- 线程的创建和销毁的开销可能很大,因为每个线程的连接/断开都需要。我通常至少设置为 16。如果应用程序中有大量的跳跃并发连接并且 Threads_Created 的值也比较大,那么我就会加大它的值。它的目的是在通常的操作中无需创建新线程。

query_cache -- 如果你的应用程序有大量读,而且没有应用程序级别的缓存,那么这很有用。不要把它设置太大了,因为想要维护它也需要不少开销,这会导致MySQL变慢。通常设置为 32-512Mb。设置完之后最好是跟踪一段时间,查看是否运行良好。在一定的负载压力下,如果缓存命中率太低了,就启用它。

注意:就像你看到的上面这些全局表量,它们都是依据硬件配置以及不同的存储引擎而不同,但是会话变量通常是根据不同的负载来设定的。如果你只有一些简单的查询,那么就无需增加 sort_buffer_size 的值了,尽管你有 64GB 的内存。搞不好也许会降低性能。 我通常在分析系统负载后才来设置会话变量。

查看引擎类型 一般情况下,MySQL会默认提供多种存储引擎,可以通过下面的查看:

(1)看你的MySQL现在已提供什么存储引擎: mysql> show engines;

(2)看你的MySQL当前默认的存储引擎: mysql> show variables like '%storage_engine%';

(3)你要看某个表用了什么引擎(在显示结果里参数engine后面的就表示该表当前用的存储引擎): mysql> show create table 表名;

(4) 查看引擎状态: show engine innodb status;