mysql myisam vs innodb

问题: 有一张表A记录用户的一些操作信息,每天晚上12点执行批量对这张表中当天的用户操作进行统计,方便后期应用决策。

思路: 为了统计比较快,对表A进行处理,每天统计完成以后,记录当天的统计信息到表B,对外界统一提供B表信息,同时把A表的信息备份到A_HISTORY表中,这样A表中只存储一天的数据,即使数据量比较大,统计也比较快。B表中只存储统计过的信息,A_HISTORY存储除当天的信息。所以后期对A_HISTORY建立分区,每一年一个分区,方便后期按年统计,出年度报表。 这是从设计上进行优化。当然从表上也可以进行优化:
1、是选择myisam表存储引擎还是innodb存储引擎
2、由于查询都是count类型加where条件,如何建立索引,才能让速度更快

1、对于以上需求,准备采用myisam存储引擎原因如下;

  1. 应用程序对查询性能要求较高,就要使用MYISAM了。MYISAM索引和数据是分开的,而且其索引是压缩的,可以更好地利用内存。所以它的查询性能明显优于INNODB。压缩后的索引也能节约一些磁盘空间。MYISAM拥有全文索引的功能,这可以极大地优化LIKE查询的效率。
  2. MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
  3. InnoDB中不保存表的行数,如select count() from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含where条件时MyISAM也需要扫描整个表
  4. MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而Innodb是索引和数据是紧密捆绑的,没有使用压缩从而会造成Innodb比MyISAM体积庞大不小。

基于以上三点原因,选择myisam作为存储引擎。

1、创建一个新库导入数据库创建脚本执行如下

脚本内容在文末

MariaDB [(none)]> create database  perf;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> use perf;
Database changed
MariaDB [perf]> delimiter //
MariaDB [perf]> source /root/tt/test.sql//
Query OK, 0 rows affected (0.04 sec)

Query OK, 0 rows affected (0.06 sec)

Query OK, 0 rows affected (0.10 sec)

Query OK, 0 rows affected (0.10 sec)

调用存储过程执行对myisam插入100万1条数据:
耗时:53秒


MariaDB [perf]> delimiter ;
MariaDB [perf]> call add_data_myisam;
Query OK, 1 row affected, 1 warning (53.75 sec)

MariaDB [perf]> select count(1) from test_myisam;
+----------+
| count(1) |
+----------+
|  1000001 |
+----------+
1 row in set (0.00 sec)

调用存储过程执行对innodb引擎插入100万
直接处于这种状态30分钟了,也没有执行完。为什么innodb这么慢???


MariaDB [perf]> call add_data_innodb();

Innodb需要扫描数据才能确定总行数,而myisam直接存储在数据库管理表中,所以更快:

MariaDB [perf]> select count(*) from test_myisam;
+----------+
| count(*) |
+----------+
|  1000001 |
+----------+
1 row in set (0.00 sec)

MariaDB [perf]> select count(*) from test_innodb;
+----------+
| count(*) |
+----------+
|  1000001 |
+----------+
1 row in set (0.36 sec)

带有where条件的查询myisam和innodb还是有区别的

MariaDB [perf]> SELECT COUNT(1)  FROM test_myisam WHERE account = 'cloudp' and '2016-07-29'=date(create_time);
+----------+
| COUNT(1) |
+----------+
|  1000001 |
+----------+
1 row in set, 1 warning (1.33 sec)

MariaDB [perf]> SELECT COUNT(1)  FROM test_innodb WHERE account = 'cloudp' and '2016-07-29'=date(create_time);
+----------+
| COUNT(1) |
+----------+
|  1000001 |
+----------+
1 row in set, 1 warning (1.76 sec)

MariaDB [perf]> SELECT COUNT(1)   FROM test_myisam WHERE account = 'cloudp' and  status = 3 and '2016-07-29'=date(create_time);
+----------+
| COUNT(1) |
+----------+
|   167198 |
+----------+
1 row in set, 1 warning (0.48 sec)

MariaDB [perf]> SELECT COUNT(1)   FROM test_innodb WHERE account = 'cloudp' and  status = 3 and '2016-07-29'=date(create_time);
+----------+
| COUNT(1) |
+----------+
|   166586 |
+----------+
1 row in set, 1 warning (0.80 sec)

未建立索引之前的操作时间

MariaDB [perf]> explain SELECT COUNT(1)  FROM test_myisam WHERE account = 'cloudp' and '2016-07-29'=date(create_time);
+------+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
| id   | select_type | table       | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+------+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
|    1 | SIMPLE      | test_myisam | ALL  | NULL          | NULL | NULL    | NULL | 1000001 | Using where |
+------+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

MariaDB [perf]> explain SELECT COUNT(1)  FROM test_innodb WHERE account = 'cloudp' and '2016-07-29'=date(create_time);
+------+-------------+-------------+------+---------------+------+---------+------+--------+-------------+
| id   | select_type | table       | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+------+-------------+-------------+------+---------------+------+---------+------+--------+-------------+
|    1 | SIMPLE      | test_innodb | ALL  | NULL          | NULL | NULL    | NULL | 997001 | Using where |
+------+-------------+-------------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

MariaDB [perf]> explain SELECT COUNT(1)   FROM test_myisam WHERE account = 'cloudp' and  status = 3 and '2016-07-29'=date(create_time);
+------+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
| id   | select_type | table       | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+------+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
|    1 | SIMPLE      | test_myisam | ALL  | NULL          | NULL | NULL    | NULL | 1000001 | Using where |
+------+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

MariaDB [perf]> explain  SELECT COUNT(1)   FROM test_innodb WHERE account = 'cloudp' and  status = 3 and '2016-07-29'=date(create_time);
+------+-------------+-------------+------+---------------+------+---------+------+--------+-------------+
| id   | select_type | table       | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+------+-------------+-------------+------+---------------+------+---------+------+--------+-------------+
|    1 | SIMPLE      | test_innodb | ALL  | NULL          | NULL | NULL    | NULL | 997001 | Using where |
+------+-------------+-------------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

根据索引的最左前缀原则:建立索引如下

create index test_index on test_myisam(account,create_time,status); 
--这里的索引建立的也不合理,因为首先我们把日期放在最前面,然后是account,最后status,我们要取当天的数据,所以日期放最前面可以一次性定位扫描更少的数据。

create index test_index on test_innodb(account,create_time,status);

建立完索引以后来看下再次查询:
加完以后更慢了,为什么呢?来看下查询计划 explain一下:
扫描行数依然是200万行,依然是全扫描,并且200万索引扫描后,还要和具体表内容结合,所以更慢了。理论上,我们扫描的行数会减少,如果索引真正生效。

MariaDB [perf]> select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+----------+
| count(*) |
+----------+
|   332220 |
+----------+
1 row in set, 1 warning (1.86 sec)

MariaDB [perf]> explain select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
| id   | select_type | table       | type  | possible_keys | key        | key_len | ref  | rows    | Extra                    |
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
|    1 | SIMPLE      | test_myisam | index | NULL          | test_index | 14      | NULL | 2000002 | Using where; Using index |
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
1 row in set (0.00 sec)

下面需要解决如何让索引真正生效的问题?

下面提供一种思路: 修改索引的顺序,让account,status、前两个生效,create_time上有函数所以create_time没有办法用索引。这是一个次优的方案,最优的方案是三个都走索引!

create index test_f_index on test_myisam(account,status,create_time);

下面是索引使用情况:节省了0.7秒,已经是很大的提升了,下面我们再修改索引顺序。

MariaDB [perf]> select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+----------+
| count(*) |
+----------+
|   332220 |
+----------+
1 row in set, 1 warning (1.86 sec)

MariaDB [perf]> explain select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
| id   | select_type | table       | type  | possible_keys | key        | key_len | ref  | rows    | Extra                    |
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
|    1 | SIMPLE      | test_myisam | index | NULL          | test_index | 14      | NULL | 2000002 | Using where; Using index |
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
1 row in set (0.00 sec)

MariaDB [perf]> explain select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
| id   | select_type | table       | type  | possible_keys | key        | key_len | ref  | rows    | Extra                    |
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
|    1 | SIMPLE      | test_myisam | index | NULL          | test_index | 14      | NULL | 2000002 | Using where; Using index |
+------+-------------+-------------+-------+---------------+------------+---------+------+---------+--------------------------+
1 row in set (0.00 sec)

MariaDB [perf]> create index test_f_index on test_myisam(account,status,create_time);//
Query OK, 2000002 rows affected (14.12 sec)
Records: 2000002  Duplicates: 0  Warnings: 0

MariaDB [perf]> explain select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+------+-------------+-------------+------+---------------+--------------+---------+-------------+--------+--------------------------+
| id   | select_type | table       | type | possible_keys | key          | key_len | ref         | rows   | Extra                    |
+------+-------------+-------------+------+---------------+--------------+---------+-------------+--------+--------------------------+
|    1 | SIMPLE      | test_myisam | ref  | test_f_index  | test_f_index | 10      | const,const | 660048 | Using where; Using index |
+------+-------------+-------------+------+---------------+--------------+---------+-------------+--------+--------------------------+
1 row in set (0.06 sec)

MariaDB [perf]> select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+----------+
| count(*) |
+----------+
|   332220 |
+----------+
1 row in set (1.13 sec)

如何让create_time的索引也用上呢:如下: 对create_time用like 'xxx%'走这个索引:这次优化变为了0.56秒,又加速了一倍。



MariaDB [perf]> select count(*) from test_myisam where account='cloudp' and status=3 and create_time like '2016-07-29%';//
+----------+
| count(*) |
+----------+
|   167198 |
+----------+
1 row in set (0.56 sec)

MariaDB [perf]> select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+----------+
| count(*) |
+----------+
|   332220 |
+----------+
1 row in set (1.11 sec)

MariaDB [perf]> explain select count(*) from test_myisam where account='cloudp' and status=3 and create_time like '2016-07-29%';//
+------+-------------+-------------+------+-------------------------+--------------+---------+-------------+--------+--------------------------+
| id   | select_type | table       | type | possible_keys           | key          | key_len | ref         | rows   | Extra                    |
+------+-------------+-------------+------+-------------------------+--------------+---------+-------------+--------+--------------------------+
|    1 | SIMPLE      | test_myisam | ref  | test_index,test_f_index | test_f_index | 10      | const,const | 347582 | Using where; Using index |
+------+-------------+-------------+------+-------------------------+--------------+---------+-------------+--------+--------------------------+
1 row in set (0.00 sec)

MariaDB [perf]> explain select count(*) from test_myisam where account='cloudp' and status=1 and '2016-07-29'= date(create_time);//
+------+-------------+-------------+------+---------------+--------------+---------+-------------+--------+--------------------------+
| id   | select_type | table       | type | possible_keys | key          | key_len | ref         | rows   | Extra                    |
+------+-------------+-------------+------+---------------+--------------+---------+-------------+--------+--------------------------+
|    1 | SIMPLE      | test_myisam | ref  | test_f_index  | test_f_index | 10      | const,const | 660048 | Using where; Using index |
+------+-------------+-------------+------+---------------+--------------+---------+-------------+--------+--------------------------+
1 row in set (0.00 sec)

reference

你可能感兴趣的:(mysql myisam vs innodb)