您的位置:首页 > 数据库 > MySQL

常用Mysql存储引擎--InnoDB和MyISAM简单总结

2014-07-03 22:14 671 查看

常用Mysql存储引擎--InnoDB和MyISAM简单总结

2013-04-19 10:21:52| 分类: CCST|举报|字号 订阅

MySQL服务器采用了模块化风格,各部分之间保持相对独立,尤其体现在存储架构上。存储引擎负责管理数据存储,以及MySQL的索引管理。通过定义的API,MySQL服务器能够与存储引擎进行通信。目前使用最多的是MyISAM和InnoDB。

MyISAM引擎是一种非事务性的引擎,提供高速存储和检索,以及全文搜索能力,适合数据仓库等查询频繁的应用。MyISAM中,一个table实际保存为三个文件,.frm存储表定义,.MYD存储数据,.MYI存储索引。

InnoDB则是一种支持事务的引擎。所有的数据存储在一个或者多个数据文件中,支持类似于Oracle的锁机制。一般在OLTP应用中使用较广泛。如果没有指定InnoDB配置选项,MySQL将在MySQL数据目录下创建一个名为ibdata1的自动扩展数据文件,以及两个名为ib_logfile0和ib_logfile1的日志文件。

创建table时可以通过engine关键字指定使用的存储引擎:CREATE TABLE t (i INT) ENGINE = MYISAM;。如果省略则使用系统默认的存储引擎。

可用如下语句查看系统中支持的存储引擎类型

mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+

其中DEFAULT表明系统的默认存储引擎,可以通过修改配置参数来变更:

default-storage-engine=MyISAM

下面列出一些常用的关于存储引擎的语句:

1.查看某个存储引擎的具体信息:

mysql> show engine InnoDB status\G;
2.查看表使用的搜索引擎

mysql> show table status from trip_rest_add where name='add_rest_review';
+-----------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length |
+-----------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+
| add_rest_review | MyISAM | 10 | Dynamic | 1598310 | 436 | 697501636 | 281474976710655 | 39308288 |
+-----------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+

3.修改表的存储引擎

ALTER TABLE Table1 ENGINE = INNODB;

下面给个例子:

mysql> show table status from trip_rest_add where name='c_rest';
+--------+--------+---------+------------+--------+----------------+-------------+-
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length |
+--------+--------+---------+------------+--------+----------------+-------------+-
| c_rest | InnoDB | 10 | Compact | 342004 | 149 | 51298304 |
+--------+--------+---------+------------+--------+----------------+-------------+-
1 row in set (0.06 sec)

mysql> alter table c_rest engine=MyISAM;
Query OK, 340098 rows affected (1 min 1.11 sec)
Records: 340098 Duplicates: 0 Warnings: 0

mysql> show table status from trip_rest_add where name='c_rest';
+--------+--------+---------+------------+--------+----------------+-------------+-
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length |
+--------+--------+---------+------------+--------+----------------+-------------+-
| c_rest | MyISAM | 10 | Dynamic | 340098 | 124 | 42200256 |
+--------+--------+---------+------------+--------+----------------+-------------+-
1 row in set (0.00 sec)

这里需要注意,如果表建立的时候是MyISAM,现在要更改整个数据库表的存储引擎为InnoDB的话,一般要一个表一个表的修改,比较繁琐。那么可以采用先把数据库导出,得到SQL,把MyISAM修改成INNODB,再导入的方式。

mysql 的innodb和myisam数据库引擎的认识,自己总结了一下并引用了网络上的相关测试和应用做如下记录:

mysql数据库新加了几个表,结果只有frm文件存在,如果直接复制到别的电脑,这几个表是提示错误的,为什么呢?查了下资料:frm、MYI、MYD分别对应MyISAM表的表结构\索引\数据文件。

我遇到的情况跟表引擎类型有关,我是用innodb,结果出现了这样的情形,MYSQL的默认DB引擎是innodb的时候,innodb表没有没有myd和.myi,其数据文件对应于ibdata1

解决办法,更改表引擎为MYISAM:

最好确认下是否是innodb引擎
进入你的mysql数据库(不管你是用phpmyadmin还是mysql命令行)
mysql> use 数据库名
mysql>show tables;
mysql>show table status like '数据库表名'
如果type=innodb的话
你就可以转换表引擎了

mysql>ALTER TABLE 数据库表名 ENGINE=MYISAM 或 alter table 数据库表名 type='MYISAM'

这样就可以修改一个表的引擎
如果你的数据库中的表引擎本来就是myisam了,那你需要确认下你的数据目录到底在哪里
mysql>show variables like 'datadir%';

也可以使用phpmyadmin,选中表,操作,表选项,Storage Engine改成MYISAM 执行 就ok了

MySQL导入innodb引擎的 frm文件 直接复制对应的数据库文件夹是不行的,在新的数据库里会提示没有相关表数据

因为innodb的存储格式是这样存储的: 数据库文件夹里只有.frm格式的文件,这样的文件是存储的数据库表的结构,

没有数据,数据统一存在ibdata1文件里,所以用复制的方法备份或是移动数据库时要注意也要将data下的ibdata1文件一起复制过去,

如果用 mysqldump -h mysql服务名 -u 用户名 -p > sql.sql 来备份的话就没这个问题

innodb 类型的数据 是不管你有多少个子数据库, 数据全部写入 ibdata1这一个文件,造成数据库大了以后 如果你的硬件配置不是很好的话执行速度极慢,

myisam 是分开存储,比如你论坛数据库是 bbs 那么论坛数据表就全部储存在mysql/data/bbs 下,其他数据库就保存在其他数据文件夹下。

如果是小表,用myisam比innodbk快,但快的也不明显,毫秒级的。如果是大表,上千万条记录了,那你赶快放弃myisam吧,innodb比它快的太明显了。

如果画条性能曲线,在数据量增长的过程中,myisam的曲线下降的非常快,而innodb就比较平稳.

再者就是myisam不支持事务处理等高级处理! 你用phpmyadmin优化下数据库数据库也会减少

/*
MYSQL的事务处理主要有两种方法。
1、用begin,rollback,commit来实现
begin 开始一个事务
rollback 事务回滚
commit 事务确认
2、直接用set来改变mysql的自动提交模式
MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!我们可以通过
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
来实现事务的处理。
当你用 set autocommit=0 的时候,你以后所有的SQL都将做为事务处理,直到你用commit确认或rollback结束。
注意当你结束这个事务的同时也开启了个新的事务!按第一种方法只将当前的作为一个事务!
个人推荐使用第一种方法!

MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理!其他的类型是不支持的!

***:一般MYSQL数据库默认的引擎是MyISAM,这种引擎不支持事务!如果要让MYSQL支持事务,可以自己手动修改:

方法如下:1.修改c:\appserv\mysql\my.ini文件,找到skip-InnoDB,在前面加上#,后保存文件。
2.重启mysql服务。
3.mysql->show engines;(或执行mysql->show variables like 'have_%'; ),查看have_InnoDB为YES,即表示数据库支持InnoDB了。
也就说明支持事务transaction了。

4.在创建表时,就可以为Storage Engine选择InnoDB引擎了。如果是以前创建的表,
可以使用mysql->alter table table_name type=InnoDB;或 mysql->alter table table_name engine=InnoDB;
来改变数据表的引擎以支持事务。
*/

Mysql 事务简介

什么是事务?
通俗点来说, 事务就是对一组由N条SQL语句(N>=1)组成的逻辑处理单元进行并发控制.


当这组SQL语句都执行成功时才会对数据库构成实际影响. 否则只要有一条SQL语句执行失败,

那么整组SQL都不会执行成功, 都不会对数据库有实际影响. 这样能确保这个事务中的这组SQL操作的统一性, 保证数据一致性.

我们来结合实际的例子来理解事务的概念:

执行下面的操作

1, 建立不支持事务的数据表 tbl_a

CREATE TABLE IF NOT EXISTS `tbl_a` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`val` varchar(100) NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=MyISAM AUTO_INCREMENT=1 ;

2, 为 tbl_a 插入数据, 将下面 4 条语句全部复制, 用 phpMyAdmin 一并执行

INSERT INTO tbl_a (val) VALUES ('a');

INSERT INTO tbl_a (val) VALUES ('b');

INSERT INTO tbl_a (vals) VALUES ('c'); -- 因为表中没有 vals 这个字段, 执行到这句时会报错.

INSERT INTO tbl_a (val) VALUES ('d'); -- 因为程序已经中断, 这句不执行

3, 我们来看看 tbl_a 中的数据, 会发现有两条数据.

SELECT * FROM tbl_a;

-- +----+-----+

-- | id | val |

-- +----+-----+

-- | 1 | a |

-- | 2 | b |

-- +----+-----+

-- 很好理解: 因为我们四条语句一并提交, mysql 还是会一条一条的执行:

-- 执行第一句, 没有ERROR, 对数据库产生实际影响, 将 'a' 真真实实的插入到 tbl_a 中

-- 执行第二句, 没有ERROR, 对数据库产生实际影响, 将 'b' 真真实实的插入到 tbl_a 中

-- 执行第三句, 因为没有指定的字段, 抛出ERROR, 程序中断执行.

接下来我们再看看事务是怎么处理的:

执行下面的操作

建立支持事务的数据表 `tbl_b`

CREATE TABLE IF NOT EXISTS `tbl_b` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`val` varchar(100) NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 ;

-- 2, 为 tbl_b 插入数据

begin; -- 开启一个事务

INSERT INTO tbl_b (val) VALUES ('a');

INSERT INTO tbl_b (val) VALUES ('b');

INSERT INTO tbl_b (vals) VALUES ('c'); -- 报错

INSERT INTO tbl_b (val) VALUES ('d');

commit;

3, 我们来看看 tbl_b 中的数据, 会和 tbl_a 中的数据有什么差别呢?

SELECT * FROM tbl_b;

-- Empty set (0.00 sec)

-- 一条数据也没有, 哈. 这就是事务与非事务的区别,

-- 因为在事务中遇到了一个错误, 所整个事务中的 SQL 语句都不会对数据库构成实际影响

OK. 现在对事务有了形像的认识之后我们再来结合其它的例子进一步学习事务:

-- 1, 清空 tbl_b 记录

TRUNCATE TABLE `tbl_b`

-- 2, 为 tbl_b 插入数据, 将下面 4 条语句全部复制, 用 phpMyAdmin 一并执行

-- begin; -- 注意没有执行 begin

INSERT INTO tbl_b (val) VALUES ('a');

INSERT INTO tbl_b (val) VALUES ('b');

INSERT INTO tbl_b (vals) VALUES ('c'); -- 报错

INSERT INTO tbl_b (val) VALUES ('d');

-- commit;

-- 3, 我们来看看 tbl_b 中的数据, 会和有执行 begin 的效果有什么差别?

SELECT * FROM tbl_b;

-- +----+-----+

-- | id | val |

-- +----+-----+

-- | 1 | a |

-- | 2 | b |

-- +----+-----+

-- 有两条数据, 这是为什么?

-- 因为事务数据表中的事务默认是自动提交的(AUTOCOMMIT), 它的执行过程是:

-- 开启一个事务, 执行第一句, 没有ERROR, 提交事务, 对数据库产生实际影响, 将'a'真真实实的插入到 tbl_b 中

-- 开启一个事务, 执行第二句, 没有ERROR, 提交事务, 对数据库产生实际影响, 将'b'真真实实的插入到 tbl_b 中

-- 开启一个事务, 执行第三句, 因为没有指定的字段, 抛出ERROR, 程序中断执行.

-- 这样明白了为什么要使用 begin 了吧, 换句话说,如果事务表中没有 begin,那它和非事务表执行的效果是一样的

下面是一个成功的例子:

-- 1, 清空 tbl_b 记录

TRUNCATE TABLE `tbl_b`;

-- 2, 为 tbl_b 插入数据, 将下面 4 条语句全部复制, 用 phpMyAdmin 一并执行

begin;

INSERT INTO tbl_b (val) VALUES ('a');

INSERT INTO tbl_b (val) VALUES ('b');

INSERT INTO tbl_b (val) VALUES ('c');

INSERT INTO tbl_b (val) VALUES ('d');

-- rollback; -- roolback 可以理解为 un-commit, 不提交

commit; -- 注意最后要有 conmmit; 如果这有这一句会怎样? 动手试试吧.

-- 3, 我们来看看 tbl_b 中的数据, 四条数据成功插入.

SELECT * FROM tbl_b;

总结一下:
1, 在 Mysql 中要让数据表支持事务, 必须使用 InnoDB 或 BDB 数据引擎.
2, 事务数据表默认是 autocommit 的, 要开启一个事务应该使用 begin; 或者使用 SET AUTOCOMMIT={1|0} 来设置是否自动提交
3, 事务一定要 commit , 否则没有任何效果


相关网络测试:

环境:win32,mysql5.0.18,php5.2.3,数据库编码:gbk;
测试程序类似如下:
CODE:

<?php
set_time_limit(0);
function getmicrotime(){
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
$link = mysql_connect('localhost','root','');
mysql_select_db('v2l');
mysql_query('set names gbk');

$time_start = getmicrotime();
for ($i=0;$i<100;$i++)
$result = mysql_query("insert into user(groupid,username,pass) values(3,'dafdsfff','dfasfsdfsdf')");
$time_end = getmicrotime();
echo $time_end - $time_start.'<br>';

?>
user表结构:
CODE:

CREATE TABLE `user` (
`id` int(10) NOT NULL auto_increment,
`groupid` int(10) NOT NULL,
`username` varchar(50) NOT NULL default '',
`pass` varchar(50) NOT NULL default '',
`capital` int(10) NOT NULL default '0' COMMENT '资产',
`credits` int(10) NOT NULL default '0',
`address` varchar(200) NOT NULL default '0',
`email` varchar(50) NOT NULL default '0',
`phone` varchar(20) NOT NULL default '0',
`post` int(6) NOT NULL default '0',
`num` int(10) NOT NULL default '0' COMMENT '消费次数',
`time` int(15) NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=gbk ROW_FORMAT=DYNAMIC
有5557行数据;

测试select:
1:循环1000次,username字段有索引:
查询语句:select * from user where username = 'wwd',估计在3000行左右,只有一条记录
innodb:维持在0.255799055099秒左右;
myisam:维持在0.252280950546秒左右;

2:循环100次,username字段没有索引:
查询语句同上;
innodb:维持在0.793882846832秒左右;
myisam:维持在0.417747974396秒左右;

得出的结论是:无索引时innodb表比myisam慢89%,有基于索引的查询速度差不多。

测试insert:
1:循环100次,username字段有索引:
innodb:维持在2.79042601585秒左右;
myisam:维持在0.0191547870636秒左右;

2:循环100次,username字段没有索引:
innodb:维持在1.58328294754秒左右;
myisam:维持在0.0159208774567秒左右;

得出的结论是:
1:innodb有索引时插入比没有索引时慢77%,myisam有索引时插入比没有索引时慢20%;
2:有索引时innodb比myisam慢146倍,无索引时innodb比myisam慢99倍;

测试update:
1:更新1400条记录,username字段有索引:
更新语句类似:update user set username='aaaaaaaaaafdasfs' where username = 'dafdsfff';此条语句更新1400条记录
innodb:维持在0.167110919952秒左右;
myisam:维持在0.0893239974976秒左右;

2:更新1400条记录,username字段没有索引:
更新语句类似上面;
innodb:维持在0.133745908737秒左右;
myisam:维持在0.0571432113647秒左右;

得出的结论是:
1:innodb有索引时更新比没有索引时慢25%,myisam有索引时更新比没有索引时慢56%;
2:有索引时innodb比myisam慢87%,无索引时innodb比myisam慢135%;

测试delete:
删除100条记录,基于主键id的删除:
删除语句:delete from user where id >5588;
innodb:维持在0.0407979488373秒左右;
myisam:维持在0.0035240650177秒左右;

得出的结论是:
基于主键时删除innodb比myisam慢11倍;
fleaphp (2007-8-01 11:47:50)操作 innodb 表前打开事务,操作完成后再提交事务,性能会有很大不同哦,呵呵
adslcat (2007-8-01 12:05:21)又是一个比较马和鱼哪个跑的快的月经帖.
wwd (2007-8-01 12:12:28)QUOTE:

原帖由 fleaphp 于 2007-8-1 11:47 发表
操作 innodb 表前打开事务,操作完成后再提交事务,性能会有很大不同哦,呵呵
这个好像没用。我的代码是这么写的:
[php]for ($i=0;$i<100;$i++)
{
mysql_query("start transaction");
$result = mysql_query("insert into user(groupid,username,pass) values(3,'dafdsfff','dfasfsdfsdf')");
mysql_query("commit");
}[/php]

把innodb_flush_log_at_trx_commit设置成0或者2都会提升性能,但是在崩溃恢复是会损失最后一秒的事务。
神仙 (2007-8-01 13:42:38)mysql_query("start transaction");
for ($i=0;$i<100;$i++)
{
$result = mysql_query("insert into user(groupid,username,pass) values(3,'dafdsfff','dfasfsdfsdf')");
}
mysql_query("commit");
你放循环里面和不加那两行代码没啥区别的

还有,你没有测试在高并发读写时的性能。这才是innodb的长处。
Snake.Zero (2007-8-01 16:53:11)QUOTE:

原帖由 fleaphp 于 2007-8-1 11:47 发表
操作 innodb 表前打开事务,操作完成后再提交事务,性能会有很大不同哦,呵呵
选择innoDb的理由和myISAM是肯定不同的,什么时候用什么类型是根据需求决定的,这种比较是完全没有意义的
怪物史莱克 (2007-8-01 17:14:08)QUOTE:

原帖由 Snake.Zero 于 2007-8-1 16:53 发表

选择innoDb的理由和myISAM是肯定不同的,什么时候用什么类型是根据需求决定的,这种比较是完全没有意义的
yeah
存在就一定有他存在的道理。
wwd (2007-8-01 19:27:06)QUOTE:

原帖由 神仙 于 2007-8-1 13:42 发表
mysql_query("start transaction");
for ($i=0;$i
嗯,我在手册里也看到了,想研究一下高并发下面测试mysql性能。
结果发现用apache的ab测试的话,高于1000个并发就不行了,导致系统死机;
用smacks测试的话,并发高于200的就不行了,会出现插入失败,低于200的话插入正常;
请问有没有好的办法?
nightsailer (2007-8-02 00:04:45)Innodb和MyISAM纯比较速度就是比性能了?太片面了吧。
二者的特点和适用情况都有很多的成熟的方案和讨论,搜一下就知道不用这样去测,
得出的数据也是片面的不能作为决策的依据的。
建议你去 http://www.mysqlperformanceblog.com/ 看看
http://www.mysqlperformanceblog.com/2007/01/08/innodb-vs-myisam-vs-falcon-benchmarks-part-1/
作为参考

[ 本帖最后由 nightsailer 于 2007-8-2 00:15 编辑 ]
Snake.Zero (2007-8-02 07:53:29)QUOTE:

原帖由 wwd 于 2007-8-1 19:27 发表

嗯,我在手册里也看到了,想研究一下高并发下面测试mysql性能。
结果发现用apache的ab测试的话,高于1000个并发就不行了,导致系统死机;
用smacks测试的话,并发高于200的就不行了,会出现插入失败,低于2 ...
MYSQL是有瓶颈的,一般的应用不可能在写入的时候达到200,算算看一瞬间能有200次插入的网站,那已经不是程序所需要考虑的事情了,已经涉及到硬件了
wwd (2007-8-02 10:17:31)QUOTE:

原帖由 Snake.Zero 于 2007-8-1 16:53 发表

选择innoDb的理由和myISAM是肯定不同的,什么时候用什么类型是根据需求决定的,这种比较是完全没有意义的
^_^,我有的时候不知道用什么表,因为同样的需求有不同的解决方法。我举个例子吧。比方说批量删除文章。我有两个思路:
第一个(使用事务):
1:先根据条件查出要删除文章的总数;
2:根据条件删除文章并得到受影响的行数;
3:比较上述两条得到的数目,如果相等则commit,否则rollback;
第二个(不使用事务):
根据条件直接删除文章;


我觉得第一个思路更安全一些,不过它使用事务,而且查询比较多,用innodb表,而第二个思路用myisam就可以,查询也相对少一些。但通常我看到的源代码程序都是使用第二个思路,请问你觉得应该用哪个?

[ 本帖最后由 wwd 于 2007-8-2 10:19 编辑 ]
wwd (2007-8-02 10:22:47)QUOTE:

原帖由 nightsailer 于 2007-8-2 00:04 发表
Innodb和MyISAM纯比较速度就是比性能了?太片面了吧。
二者的特点和适用情况都有很多的成熟的方案和讨论,搜一下就知道不用这样去测,
得出的数据也是片面的不能作为决策的依据的。
建议你去 http://www.my... 完全同意你所说的道理,不过我就是想测试一下他们之间的速度差别。
如果是某一个场合必须用innodb,那不管他速度如何我都会用;
但是如果一个场合可以用innodb,也可以用myisam,那我就想测试下他们之间的速度差别,以此来决定到底用哪个。呵呵
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: