mysql中IN,OR,BETWEEN性能比较

微博上看到@金山 提到了一个MySQL中的一个性能问题,

 

 代码如下 复制代码
select id from table where id > 100 and id < 200 和 select id from table where id = 101 or id = 103 or id = 104 or id = 105 or id = ...

 
哪个更快?

 
这里的查询条件有三种:between,or 和 in。这里id列是索引列,如果不是的话,三个查询都是全表扫描,性能差距应该不大。

 
1  准备环境

 代码如下 复制代码

mysql> show create table tinG
*************************** 1. row ***************************
       Table: tin
Create Table: CREATE TABLE `tin` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=5002 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> show create procedure init_tinG
*************************** 1. row ***************************
           Procedure: init_tin
            sql_mode: NO_ENGINE_SUBSTITUTION
    Create Procedure: CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `init_tin`(cnt int)
begin
declare i int default 0;
repeat
insert into tin(c2) values(repeat('a', 100));
set i:= i+1;
until i > cnt
end repeat;
end
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: latin1_swedish_ci
1 row in set (0.00 sec)

mysql> call init_tin(5000)G

 
2  查看执行计划
为了简单起见,这里并没有选择[100,200]这个区间进行查询,而是只选择了[100,104]这个区间。 查询语句为:

 代码如下 复制代码

 
SELECT * FROM tin where c1 >= 100 and c1 <= 104;
SELECT * FROM tin where c1 in (100, 101, 102, 103, 104);
SELECT * FROM tin where c1 = 100 or c1 = 101 or c1 = 102 or c1 = 103 or c1 = 104;

 
首先查看explain输出,会发现三个语句的explain输出是一样的:

 代码如下 复制代码

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tin
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 5
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

 
MySQL5.6在information_schema中增加了optimizer_trace表,用于跟踪语句生成的执行计划的具体步骤,包含各种关键的优化步骤。 分别看下三种不同语句的执行代价:

1.

 代码如下 复制代码
SELECT * FROM tin where c1 >=100 and c1 <=104;
"chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "PRIMARY",
                      "rows": 5,
                      "ranges": [
                        "100 <= c1 <= 104"
                      ]
                    },
                    "rows_for_plan": 5,
                    "cost_for_plan": 2.0188,
                    "chosen": true
                    }

2.

 代码如下 复制代码
SELECT * FROM tin where c1 in (100, 101, 102, 103, 104);
"chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "PRIMARY",
                      "rows": 5,
                      "ranges": [
                        "100 <= c1 <= 100",
                        "101 <= c1 <= 101",
                        "102 <= c1 <= 102",
                        "103 <= c1 <= 103",
                        "104 <= c1 <= 104"
                      ]
                    },
                    "rows_for_plan": 5,
                    "cost_for_plan": 6.0188,
                    "chosen": true
                  }

3.

 代码如下 复制代码
SELECT * FROM tin where c1 = 100 or c1 = 101 or c1 = 102 or c1 = 103 or c1 =104;
"chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "PRIMARY",
                      "rows": 5,
                      "ranges": [
                        "100 <= c1 <= 100",
                        "101 <= c1 <= 101",
                        "102 <= c1 <= 102",
                        "103 <= c1 <= 103",
                        "104 <= c1 <= 104"
                      ]
                    },
                    "rows_for_plan": 5,
                    "cost_for_plan": 6.0188,
                    "chosen": true
                  }

 
从上面可以看出执行代价最小的语句为SELECT * FROM tin WHERE c1 >= 100 and c1 <=104,代价为2.0118,其他两个计划的代价 为6.0118。

 
3  计划分析
看了上面的代价结果,是不是就理所当然的任务第一个语句的代价真的是最小呢?这就需要知道MySQL代价计算的方法, 一个计划的代价体现在硬件上就是I/O+CPU,I/O就是将所需的物理页载入内存的时间,CPU则是数据计算所消耗的时间, 有些语句是I/O密集的,有些语句是CPU运算密集的。

 
为什么MySQL计算出来的代价会差别这么大呢? MySQL在计算上面三个语句的代价时,I/O代价的计算是由range的个数n_ranges和最终的结果集的行数total_rows得出来的, 语句1的n_ranges=1,语句2和语句3的n_ranges=5,totol_rows都为5,故语句1的在I/O上的代价明显小于语句2和语句3(具体的函数 参见ha_innobase::read_time)。至于CPU的代价,由于返回的行数一致,故CPU的代价一致,CPU的代价主要体现在获取数据后,进行WHERE 条件的匹配操作。

 
这只是MySQL的对于上面三个语句的代价模型,而实际上,上面三个语句所进行的I/O操作其实是一致的,因为数据范围是一样的。所以,仅凭 MySQL给出的代价结果还是不能立刻判断出语句1就肯定好。

 
既然I/O操作的代价可以考虑是一致的,那么只能来看三条语句执行时的区别了。语句2和语句3的range个数都为5个,而且range的范围都是一致的, 这其实是MySQL的优化结果,IN和OR都被优化成了相同的结果。只有语句1只有1个range。MySQL执行时是遍历每个range,而每个range遍历时其实 是两种操作,read_first和read_next,read_first是根据每个range的start key定位到相应的位置,read_next则是根据上次BTREE读到的位置, 继续往后读,read_next是以end key为结束。

 
对于语句1,只有一个range,故需要1次read_first和5次read_next(最后一次read_next不符合end_key,返回结束),对于语句2和语句3, 有5个range,每个range需要1此read_first和一次read_next,总共需要5此read_first和5次read_next。从数据获取的次数来看,语句2和语句3基本是语句1的调用次数的两倍。

 
除了获取数据调用次数的区别外,在获取数据之后,还需要进行数据合法性的验证,即匹配WHERE条件,对于语句1的WHERE条件十分简单,匹配 上下界限即可,即对于每返回的一行数据需要两次验证,时间复杂度为常量O(2)。 而对于语句2和语句3,则需要对IN或OR中的每个条件进行验证,知道找到某一匹配项为止,时间复杂度为O(n)。 但是MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的,故匹配的时候是二分查找, 时间复杂度为O(lgn)。

 
在忽略I/O的情况下,仅仅从CPU的耗时来看,语句1应该是最少的,其次是IN,最差的就是OR了。

 
先就分析到这吧,具体的执行时间的数据我就不测试了,主要是想通过测试了解MySQL内部的优化流程。可能单独测试的时候语句执行效率 差别不是很大。好了,收拾行李,明天回家,年前最后一篇。

 

时间: 2024-10-21 08:11:15

mysql中IN,OR,BETWEEN性能比较的相关文章

MySQL中distinct与group by之间的性能进行比较_Mysql

最近在网上看到了一些测试,感觉不是很准确,今天亲自测试了一番.得出了结论,测试过程在个人计算机上,可能不够全面,仅供参考. 测试过程: 准备一张测试表  CREATE TABLE `test_test` ( `id` int(11) NOT NULL auto_increment, `num` int(11) NOT NULL default '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1

MySQL中MyISAM引擎与InnoDB引擎性能简单测试

[硬件配置] CPU : AMD2500+ (1.8G) 内存: 1G/现代 硬盘: 80G/IDE [软件配置] OS : Windows XP SP2 SE : PHP5.2.1 DB : MySQL5.0.37 Web: IIS6 [MySQL表结构] CREATE TABLE `myisam` ( `id` int(11) NOT NULL auto_increment, `name` varchar(100) default NULL, `content` text, PRIMARY

从MySQL得到最大的优化性能

优化是一项复杂的任务,因为它最终需要对整个系统的理解.当用你的系统/应用的小知识做一些局部优化是可能的时候,你越想让你的系统更优化,你必须知道它也越多. 因此,本章将试图解释并给出优化MySQL的不同方法的一些例子.但是记住总是有某些(逐渐变难)是系统更快的方法留着去做. 为了使一个系统更快的最重要部分当然是基本设计.你也需要知道你的系统将做这样的事情,那就是你的瓶颈. 最常见的瓶颈是: 磁盘寻道.磁盘花时间找到一个数据,用在1999年的现代磁盘其平均时间通常小于10ms,因此理论上我们能大约一

MySQL中海量数据统计处理及模拟物化视图

一 物化视图 物化视图,它是用于预先计算并保存表连接或聚集等耗时较多的操作 的结果,这样,在执行查询时,就可以避免进行这些耗时的操作,而从快速的得到结果 . 只有大型数据库oracle10g,db2才支持这个功能,而MySQL5.1暂时还没有这个功能. 本人通过事件调度和存储过程模拟了物化视图.下面大家一起来讨论吧.^-^ 二 准备知识 1)存储过程:玩过数据库的人,都知道他是啥~,~ 2)事件调度:在 MySQL5.1开始才有的新功能.说白了就是个定时器.跟java里的timer差不多. Sq

MySQL中使用DNS的方法

当新的客户连接mysqld时,mysqld创建一个新的线程来处理请求.该线程先检查是否主机名在主机名缓存中.如果不在,线程试图解析主机名: 如果操作系统支持线程安全gethostbyaddr_r  ()和gethostbyname_r()调用,线程使用它们来执行主机名解析. 如果操作系统不支持线程安全调用,线程锁定一个互斥体并调用gethostbyaddr()和gethostbyname().在这种情况下,在第1个线程解锁互斥体前,没有其它线程可以解析不在主机名缓存中的主机名. 你可以用--sk

MySQL Index Condition Pushdown(ICP)性能优化方法实例

  这篇文章主要介绍了MySQL Index Condition Pushdown(ICP)性能优化方法实例,本文讲解了概念介绍.原理.实践案例.案例分析.ICP的使用限制等内容,需要的朋友可以参考下 一 概念介绍 Index Condition Pushdown (ICP)是MySQL 5.6 版本中的新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式. a 当关闭ICP时,index 仅仅是data access 的一种访问方式,存储引擎通过索引回表获取的数据会传递到MySQL Ser

深入研究mysql中的varchar和limit(容易被忽略的知识)

 为什么标题要起这个名字呢?commen sence指的是那些大家都应该知道的事情,但往往大家又会会略这些东西,或者对这些东西一知半解,今天我总结下自己在mysql中遇到的一些commen sense类型的问题. 1.varchar(5)可以存储多少个汉字,多少个字母数字? 相信有好多人应该跟我一样,对这个已经很熟悉了,根据经验我们能很快的做出决定,比如说用varchar(200)去存储url等等,但是,即使你用了很多次也很熟悉了,也有可能对上面的问题做出错误的回答. 这个问题我查了好多资料,有

MySQL中优化sql语句查询常用的30种方法

本篇文章是对MySQL中优化sql语句查询常用的30种方法进行了详细的分析介绍,需要的朋友参考下   1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以

mysql中不同事务隔离级别下数据的显示效果

  事务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都不会执行.也就是说,事务内的语句要么全部执行,要么一句也不执行. 事务的特性:acid,也称为事务的四个测试(原子性,一致性,隔离性,持久性) automicity:原子性,事务所引起的数据库操作,要么都完成,要么都不执行 consisitency:一致性,事务执行前的总和和事务执行后的总和是不