zoukankan      html  css  js  c++  java
  • mysql 索引相关知识

    由where 1 =1 引发的思考

    最近工作上被说了

    说代码中不能用 where 1=1,当时觉得是应该可以用的,但是找不到什么理据,
    而且mysql 语句优化这方面确实很薄弱
     
    感觉自己mysql方面是知识还是不够哇
    得好好研究研究
    还有发现 很多知识点 光看的话根本记不住,也不深刻。还是得亲手实践下
     
    so  ~~~ 挫折越多进步越快
     

     1、关于sql语句大小问题

    1)mysql默认情况下没有设置 root用户密码

    给mysql的root用户设置密码先
    [ztao@localhost ~]$ mysqladmin -uroot password "root"

    2)默认情况下sql语句是限制在1M以内

    max_allowed_packet         表示sql最大为1M
    mysql> show VARIABLES like '%max_allowed_packet%';
    +--------------------------+------------+
    | Variable_name            | Value      |
    +--------------------------+------------+
    | max_allowed_packet       | 1048576    |
    | slave_max_allowed_packet | 1073741824 |
    +--------------------------+------------+
    2 rows in set (0.00 sec)

    这里最好改大,如果太小的话sql语句超过1M会导致sql语句执行失败

    3)

    [root@localhost ztao]# vim /etc/my.cnf
    
    添加红色的那一行
    [mysqld]
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    user=mysql
    # Disabling symbolic-links is recommended to prevent assorted security risks
    symbolic-links=0
    max_allowed_packet = 160M
    [mysqld_safe]
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid
    4)
    然后重启mysql 在查看下
    160M = 160*1024*1024=167772160
    mysql> show VARIABLES like '%max_allowed_packet%';
    +--------------------------+------------+
    | Variable_name            | Value      |
    +--------------------------+------------+
    | max_allowed_packet       | 167772160  |
    | slave_max_allowed_packet | 1073741824 |
    +--------------------------+------------+
    2 rows in set (0.00 sec)

    这样能支持你一条sql语句在160M以内

    2、在window下写的脚本文件放在linux上执行失败

     
    在Windows下写了一个shell脚本,
    上传到Linux下执行时经常会报这种错误:
    [root@localhost test]# ./test.sh   
    -bash: ./test.sh: /bin/sh^M: bad interpreter: No such file or directory

    主要原因是test.sh是我在windows下编辑然后上传到linux系统里执行的。
    windows下编辑的.sh文件的格式为dos格式。
    而linux只能执行格式为unix格式的脚本。
    因为在dos/window下按一次回车键实际上输入的是“回车(CR)”和“换行(LF)”,
    而Linux/unix下按一次回车键只输入“换行(LF)”,所以修改的sh文件在每行都会多了一个CR,所以Linux下运行时就会报错找不到命令。

    我们可以通过vi编辑器来查看文件的format格式。步骤如下:
    1.首先用vi命令打开文件
    2.在vi命令模式中使用 :set ff 命令
    可以看到改文件的格式为
    fileformat=dos
    3.修改文件format为unix
    使用vi/vim修改文件format
    命令::set ff=unix
    或者::set fileformat=unix
    然后:wq保存退出就可以了

     

    3、通过脚本创建表并插入数据
    1)通过shell创建表
    #!/bin/sh 
    dbenv=dev
      
    if [ "${dbenv}" = "dev" ]; then
        mysql_user=root
        mysql_pass=root
        mysql_host=
    fi
    if [ "${dbenv}" = "idc" ]; then 
        mysql_user=root
        mysql_pass=root
        mysql_host="-h 10.10.10.10"
    fi
      
    mysql_cmd="mysql -u${mysql_user} -p${mysql_pass} ${mysql_host} --default-character-set=utf8"
      
     
    ${mysql_cmd}<<EOF
    use test;
     
    drop table if exists t_user;
    create table t_user
    (
        f_uin bigint(20) NOT NULL  auto_increment,
        f_name varchar(256) NOT NULL DEFAULT '',
        f_createtime datetime not null default '0000-00-00 00:00:00',
        f_status int(11) NOT NULL DEFAULT '0',
        f_rangenum int(11) NOT NULL DEFAULT '0',
        PRIMARY KEY (f_uin)
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    EOF
    echo "create table t_user ok"

    2)通过python脚本来创建sql脚本

    #!/usr/bin/python
    #-*-coding:UTF-8-*-
     
    import sys
     
     
     
    def Insert(num):
     
     
            if(num<=10000000):
                    str = "insert into t_user (f_name,f_createtime,f_status,f_rangenum) values "
                    for i in range(num):
                            name = "zhantao_%d"%i
                            status = 1
                            rangenum = i
                            if(i==0):
                                    strsql = "('%s',now(),%d,%d)"%(name,status,rangenum)
                            else:
                                    strsql = ",('%s',now(),%d,%d)"%(name,status,rangenum)
                            str += strsql
                    f = open("insert.sql","w")
                    f.write(str)
                    f.close
     
     
     
     
     
     
    if __name__ == '__main__':
            #num = int(sys.argv[1])
            Insert(1000000)
    可以看到创建出来的sql脚本 一个就33兆 如果前面没有修改sql语句最大长度,是肯定执行不了的
    [ztao@localhost 0418]$ ll -alh
    total 33M
    drwxrwxr-x 2 ztao ztao 4.0K Apr 18 20:16 .
    drwxrwxr-x 3 ztao ztao 4.0K Apr 18 20:02 ..
    -rwxrwxr-x 1 ztao ztao  733 Apr 18 20:03 create_table.sh
    -rwxrwxr-x 1 ztao ztao  538 Apr 18 20:10 insert.py
    -rw-rw-r-- 1 ztao ztao  33M Apr 18 20:11 insert.sql

     

    3)把数据导入到sql里面
    这里面导入的五次,一次100w
    因为主键是自增的所以重复插入不会产生冲突
    mysql> use test
    Reading table information for completion of table and column names
    You can turn off this feature to get a quicker startup with -A
     
    Database changed
    mysql> source /home/ztao/Work/Test/0418/insert.sql
    Query OK, 1000000 rows affected (11.77 sec)
    Records: 1000000  Duplicates: 0  Warnings: 0
     
    mysql> source /home/ztao/Work/Test/0418/insert.sql
    Query OK, 1000000 rows affected (8.90 sec)
    Records: 1000000  Duplicates: 0  Warnings: 0
     
    mysql> source /home/ztao/Work/Test/0418/insert.sql
    Query OK, 1000000 rows affected (8.43 sec)
    Records: 1000000  Duplicates: 0  Warnings: 0
     
    mysql> source /home/ztao/Work/Test/0418/insert.sql
    Query OK, 1000000 rows affected (8.28 sec)
    Records: 1000000  Duplicates: 0  Warnings: 0
     
    mysql> source /home/ztao/Work/Test/0418/insert.sql
    Query OK, 1000000 rows affected (7.87 sec)
    Records: 1000000  Duplicates: 0  Warnings: 0
    
    
    mysql> select count(*) from t_user;
    +----------+
    | count(*) |
    +----------+
    |  5000000 |
    +----------+
    1 row in set (3.78 sec)
    以下是实验的重点
    还有以下测试情况不考虑联合索引
    联合索引的情况复杂一点、下次再验证
    1、我们先分析在没有索引的情况下。
    mysql> select * from t_user where f_rangenum = 123568;
    +---------+----------------+---------------------+----------+------------+
    | f_uin   | f_name         | f_createtime        | f_status | f_rangenum |
    +---------+----------------+---------------------+----------+------------+
    |  123569 | zhantao_123568 | 2015-04-18 20:11:36 |        1 |     123568 |
    | 1123569 | zhantao_123568 | 2015-04-18 20:11:54 |        1 |     123568 |
    | 2123569 | zhantao_123568 | 2015-04-18 20:12:05 |        1 |     123568 |
    | 3123569 | zhantao_123568 | 2015-04-18 20:12:15 |        1 |     123568 |
    | 4123569 | zhantao_123568 | 2015-04-18 20:12:25 |        1 |     123568 |
    +---------+----------------+---------------------+----------+------------+
    5 rows in set (2.19 sec)
     
    mysql> explain select * from t_user where f_rangenum = 123568;
    +----+-------------+--------+------+---------------+------+---------+------+---------+-------------+
    | id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
    +----+-------------+--------+------+---------------+------+---------+------+---------+-------------+
    |  1 | SIMPLE      | t_user | ALL  | NULL          | NULL | NULL    | NULL | 4989899 | Using where |
    +----+-------------+--------+------+---------------+------+---------+------+---------+-------------+
    1 row in set (0.00 sec)
    在没有索引的情况下执行了2秒多
    explian sql语句  发现几乎它的type类型是All  即效率最差的
    rows 为mysql认为需要查看的条数。 几乎是全表每条数据的扫了一边
    效率极低
     
    2、在f_rangenum上增加索引,分析sql语句
    mysql> alter table t_user add index f_rangenum_index(f_rangenum);
    Query OK, 5000000 rows affected (1 min 45.16 sec)
    Records: 5000000  Duplicates: 0  Warnings: 0

    数量量大了,添加索引很慢。。。。。。。。

    mysql> select * from t_user where f_rangenum = 123199;
    +---------+----------------+---------------------+----------+------------+
    | f_uin   | f_name         | f_createtime        | f_status | f_rangenum |
    +---------+----------------+---------------------+----------+------------+
    |  123200 | zhantao_123199 | 2015-04-18 20:11:36 |        1 |     123199 |
    | 1123200 | zhantao_123199 | 2015-04-18 20:11:54 |        1 |     123199 |
    | 2123200 | zhantao_123199 | 2015-04-18 20:12:05 |        1 |     123199 |
    | 3123200 | zhantao_123199 | 2015-04-18 20:12:15 |        1 |     123199 |
    | 4123200 | zhantao_123199 | 2015-04-18 20:12:25 |        1 |     123199 |
    +---------+----------------+---------------------+----------+------------+
    5 rows in set (0.02 sec)
    
    mysql> explain select * from t_user where f_rangenum = 123199;     
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    | id | select_type | table  | type | possible_keys    | key              | key_len | ref   | rows | Extra |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    |  1 | SIMPLE      | t_user | ref  | f_rangenum_index | f_rangenum_index | 4       | const |    5 |       |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    1 row in set (0.00 sec)
    在有索引的情况下  执行效率非常高
    看explain   rows为5   只查了五条数据
    它的type为ref 表示 是根据key(f_rangenum_index )常数123199来查询的 
    所以效果很好
     
    3、where后面的  过滤条件顺序是否有影响
    mysql> select * from t_user where f_rangenum = 123199;
    +---------+-------------------------+---------------------+----------+------------+
    | f_uin   | f_name                  | f_createtime        | f_status | f_rangenum |
    +---------+-------------------------+---------------------+----------+------------+
    |  123200 | zhantao_123599001123200 | 2015-04-18 20:11:36 |        1 |     123199 |
    | 1123200 | zhantao_123599002       | 2015-04-18 20:11:54 |        1 |     123199 |
    | 2123200 | zhantao_123199          | 2015-04-18 20:12:05 |        1 |     123199 |
    | 3123200 | zhantao_123199          | 2015-04-18 20:12:15 |        1 |     123199 |
    | 4123200 | zhantao_123199          | 2015-04-18 20:12:25 |        1 |     123199 |
    +---------+-------------------------+---------------------+----------+------------+
    5 rows in set (0.00 sec)
     
    mysql> select * from t_user where f_rangenum = 123199 and f_name = "zhantao_123599001123200";
    +--------+-------------------------+---------------------+----------+------------+
    | f_uin  | f_name                  | f_createtime        | f_status | f_rangenum |
    +--------+-------------------------+---------------------+----------+------------+
    | 123200 | zhantao_123599001123200 | 2015-04-18 20:11:36 |        1 |     123199 |
    +--------+-------------------------+---------------------+----------+------------+
    1 row in set (0.00 sec)
     
    mysql> select * from t_user where  f_name = "zhantao_123599001123200" and  f_rangenum = 123199;
    +--------+-------------------------+---------------------+----------+------------+
    | f_uin  | f_name                  | f_createtime        | f_status | f_rangenum |
    +--------+-------------------------+---------------------+----------+------------+
    | 123200 | zhantao_123599001123200 | 2015-04-18 20:11:36 |        1 |     123199 |
    +--------+-------------------------+---------------------+----------+------------+
    1 row in set (0.00 sec)
     
    mysql> explain select * from t_user where  f_name = "zhantao_123599001123200" and  f_rangenum = 123199;
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------------+
    | id | select_type | table  | type | possible_keys    | key              | key_len | ref   | rows | Extra       |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t_user | ref  | f_rangenum_index | f_rangenum_index | 4       | const |    5 | Using where |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------------+
    1 row in set (0.00 sec)
     
    mysql> explain select * from t_user where f_rangenum = 123199 and f_name = "zhantao_123599001123200";
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------------+
    | id | select_type | table  | type | possible_keys    | key              | key_len | ref   | rows | Extra       |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t_user | ref  | f_rangenum_index | f_rangenum_index | 4       | const |    5 | Using where |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------------+
    1 row in set (0.00 sec)
    select from t_user where  f_name = "zhantao_123599001123200" and  f_rangenum = 123199;
    select from t_user where f_rangenum = 123199 and f_name = "zhantao_123599001123200";
     
    两个语句一样
    只是把where后面的字段顺序挑换了一下
    一个先写设置了索引的过滤条件,另一个先写没有过滤索引的字段
    我们可以发现 它们执行的时间是一样的。
    它们的explain的结果也一样
    说明它们where后面的顺序不影响。
    我想应该是mysql  的sql优化器 会去先执行有索引的字段然后再查找非索引的字段
     
    4、关于where 1=1 是否会导致全变扫描
    废话不多说  情况跟上面的一样
    应该是mysql 的sql优化器会过滤掉1=1  所以根本不会导致全表扫描
    mysql> explain select * from t_user where  1=1 and  f_rangenum = 123199;                                  
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    | id | select_type | table  | type | possible_keys    | key              | key_len | ref   | rows | Extra |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    |  1 | SIMPLE      | t_user | ref  | f_rangenum_index | f_rangenum_index | 4       | const |    5 |       |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    1 row in set (0.00 sec)
     
    mysql> explain select * from t_user where   f_rangenum = 123199;        
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    | id | select_type | table  | type | possible_keys    | key              | key_len | ref   | rows | Extra |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    |  1 | SIMPLE      | t_user | ref  | f_rangenum_index | f_rangenum_index | 4       | const |    5 |       |
    +----+-------------+--------+------+------------------+------------------+---------+-------+------+-------+
    1 row in set (0.00 sec)
     
    mysql> select * from t_user where   f_rangenum = 123199;        
    +---------+-------------------------+---------------------+----------+------------+
    | f_uin   | f_name                  | f_createtime        | f_status | f_rangenum |
    +---------+-------------------------+---------------------+----------+------------+
    |  123200 | zhantao_123599001123200 | 2015-04-18 20:11:36 |        1 |     123199 |
    | 1123200 | zhantao_123599002       | 2015-04-18 20:11:54 |        1 |     123199 |
    | 2123200 | zhantao_123199          | 2015-04-18 20:12:05 |        1 |     123199 |
    | 3123200 | zhantao_123199          | 2015-04-18 20:12:15 |        1 |     123199 |
    | 4123200 | zhantao_123199          | 2015-04-18 20:12:25 |        1 |     123199 |
    +---------+-------------------------+---------------------+----------+------------+
    5 rows in set (0.00 sec)
     
    mysql> select * from t_user where 1=1 and  f_rangenum = 123199;
    +---------+-------------------------+---------------------+----------+------------+
    | f_uin   | f_name                  | f_createtime        | f_status | f_rangenum |
    +---------+-------------------------+---------------------+----------+------------+
    |  123200 | zhantao_123599001123200 | 2015-04-18 20:11:36 |        1 |     123199 |
    | 1123200 | zhantao_123599002       | 2015-04-18 20:11:54 |        1 |     123199 |
    | 2123200 | zhantao_123199          | 2015-04-18 20:12:05 |        1 |     123199 |
    | 3123200 | zhantao_123199          | 2015-04-18 20:12:15 |        1 |     123199 |
    | 4123200 | zhantao_123199          | 2015-04-18 20:12:25 |        1 |     123199 |
    +---------+-------------------------+---------------------+----------+------------+
    5 rows in set (0.00 sec)

    总结是无论where后面的索引在什么位置都会使用索引

    mysql 组合索引

    1、组合索引
    1)添加组合索引
    mysql> alter table t_user_bak add index f_namerangenum_index(f_name,f_rangenum);
    Query OK, 5000000 rows affected, 2 warnings (2 min 54.18 sec)
    Records: 5000000  Duplicates: 0  Warnings: 0
    
    mysql> show create table t_user_bakG;
    *************************** 1. row ***************************
           Table: t_user_bak
    Create Table: CREATE TABLE `t_user_bak` (
      `f_uin` bigint(20) NOT NULL AUTO_INCREMENT,
      `f_name` varchar(256) NOT NULL DEFAULT '',
      `f_createtime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
      `f_status` int(11) NOT NULL DEFAULT '0',
      `f_rangenum` int(11) NOT NULL DEFAULT '0',
      PRIMARY KEY (`f_uin`),
      KEY `f_namerangenum_index` (`f_name`(255),`f_rangenum`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5000001 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
    2)
    对于组合索引
    我们看到 只要是查询条件中有联合索引中最左边的那个索引字段
    那么都会走索引
    (3个或者多个联合索引  道理是一样的 )
    mysql> explain select * from t_user_bak where  f_rangenum= 155465 and f_name="zhantao_155465";       
    +----+-------------+------------+------+----------------------+----------------------+---------+-------------+------+-------------+
    | id | select_type | table      | type | possible_keys        | key                  | key_len | ref         | rows | Extra       |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------------+------+-------------+
    |  1 | SIMPLE      | t_user_bak | ref  | f_namerangenum_index | f_namerangenum_index | 771     | const,const |    5 | Using where |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------------+------+-------------+
    1 row in set (0.00 sec)
     
    mysql> explain select * from t_user_bak where   f_name="zhantao_155465" and  f_rangenum= 155465;
    +----+-------------+------------+------+----------------------+----------------------+---------+-------------+------+-------------+
    | id | select_type | table      | type | possible_keys        | key                  | key_len | ref         | rows | Extra       |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------------+------+-------------+
    |  1 | SIMPLE      | t_user_bak | ref  | f_namerangenum_index | f_namerangenum_index | 771     | const,const |    5 | Using where |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------------+------+-------------+
    1 row in set (0.00 sec)
    
    mysql> explain select * from t_user_bak where   f_name="zhantao_155465" ;                       
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    | id | select_type | table      | type | possible_keys        | key                  | key_len | ref   | rows | Extra       |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t_user_bak | ref  | f_namerangenum_index | f_namerangenum_index | 767     | const |    5 | Using where |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    1 row in set (0.00 sec)
     
    mysql> explain select * from t_user_bak where  1=1 and f_name="zhantao_155465" ;
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    | id | select_type | table      | type | possible_keys        | key                  | key_len | ref   | rows | Extra       |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t_user_bak | ref  | f_namerangenum_index | f_namerangenum_index | 767     | const |    5 | Using where |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    1 row in set (0.00 sec)
     
    mysql> explain select * from t_user_bak where  f_createtime = '2015-04-18 21:19:35' and f_name="zhantao_155465" ;   
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    | id | select_type | table      | type | possible_keys        | key                  | key_len | ref   | rows | Extra       |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t_user_bak | ref  | f_namerangenum_index | f_namerangenum_index | 767     | const |    5 | Using where |
    +----+-------------+------------+------+----------------------+----------------------+---------+-------+------+-------------+
    1 row in set (0.00 sec)

    3) 各个查询条件下的速度

    mysql> show create table t_user_bakG;
    *************************** 1. row ***************************
           Table: t_user_bak
    Create Table: CREATE TABLE `t_user_bak` (
      `f_uin` bigint(20) NOT NULL AUTO_INCREMENT,
      `f_name` varchar(256) NOT NULL DEFAULT '',
      `f_createtime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
      `f_status` int(11) NOT NULL DEFAULT '0',
      `f_rangenum` int(11) NOT NULL DEFAULT '0',
      PRIMARY KEY (`f_uin`),
      KEY `f_namecreatetimerangenum_index` (`f_name`(255),`f_createtime`,`f_rangenum`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5000001 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
     
    ERROR: 
    No query specified
     
    mysql> show create table t_userG;    
    *************************** 1. row ***************************
           Table: t_user
    Create Table: CREATE TABLE `t_user` (
      `f_uin` bigint(20) NOT NULL AUTO_INCREMENT,
      `f_name` varchar(256) NOT NULL DEFAULT '',
      `f_createtime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
      `f_status` int(11) NOT NULL DEFAULT '0',
      `f_rangenum` int(11) NOT NULL DEFAULT '0',
      PRIMARY KEY (`f_uin`),
      KEY `f_name_index` (`f_name`(255)),
      KEY `f_rangenum_index` (`f_rangenum`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6000001 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
    如下图
    可以看出来  如果如果是联合索引  只会在最左边的那个索引字段上显示 MUL
    但是是单列索引  每个索引上都有 MUL
    这也就可以明白为什么在使用联合索引的时候 一定要有最左边的那个索引字段
    mysql> desc t_user_bak;               
    +--------------+--------------+------+-----+---------------------+----------------+
    | Field        | Type         | Null | Key | Default             | Extra          |
    +--------------+--------------+------+-----+---------------------+----------------+
    | f_uin        | bigint(20)   | NO   | PRI | NULL                | auto_increment |
    | f_name       | varchar(256) | NO   | MUL |                     |                |
    | f_createtime | datetime     | NO   |     | 0000-00-00 00:00:00 |                |
    | f_status     | int(11)      | NO   |     | 0                   |                |
    | f_rangenum   | int(11)      | NO   |     | 0                   |                |
    +--------------+--------------+------+-----+---------------------+----------------+
    5 rows in set (0.00 sec)
     
    mysql> desc t_user;               
    +--------------+--------------+------+-----+---------------------+----------------+
    | Field        | Type         | Null | Key | Default             | Extra          |
    +--------------+--------------+------+-----+---------------------+----------------+
    | f_uin        | bigint(20)   | NO   | PRI | NULL                | auto_increment |
    | f_name       | varchar(256) | NO   | MUL |                     |                |
    | f_createtime | datetime     | NO   |     | 0000-00-00 00:00:00 |                |
    | f_status     | int(11)      | NO   |     | 0                   |                |
    | f_rangenum   | int(11)      | NO   | MUL | 0                   |                |
    +--------------+--------------+------+-----+---------------------+----------------+
    5 rows in set (0.00 sec)
    可以看到 单列索引 和 组合索引  查询效果都是差不多的  不知道为什么
    对组合索引  和 在各个单列上建索引的区别
    mysql>  select * from t_user where  1=1 and f_name = "zhantao_155465" and f_status !=2 and f_rangenum= 155465 ;    
    +---------+----------------+---------------------+----------+------------+
    | f_uin   | f_name         | f_createtime        | f_status | f_rangenum |
    +---------+----------------+---------------------+----------+------------+
    |  155466 | zhantao_155465 | 2015-04-18 21:21:11 |        1 |     155465 |
    | 1155466 | zhantao_155465 | 2015-04-18 21:21:20 |        1 |     155465 |
    | 2155466 | zhantao_155465 | 2015-04-18 21:21:29 |        1 |     155465 |
    | 3155466 | zhantao_155465 | 2015-04-18 21:21:38 |        1 |     155465 |
    | 4155466 | zhantao_155465 | 2015-04-18 21:21:48 |        1 |     155465 |
    +---------+----------------+---------------------+----------+------------+
    5 rows in set (0.03 sec)
     
    mysql>  select * from t_user_bak where  1=1 and f_name = "zhantao_155465" and f_status !=2 and f_rangenum= 155465 ;
    +---------+----------------+---------------------+----------+------------+
    | f_uin   | f_name         | f_createtime        | f_status | f_rangenum |
    +---------+----------------+---------------------+----------+------------+
    |  155466 | zhantao_155465 | 2015-04-18 21:19:35 |        1 |     155465 |
    | 1155466 | zhantao_155465 | 2015-04-18 21:19:47 |        1 |     155465 |
    | 2155466 | zhantao_155465 | 2015-04-18 21:20:10 |        1 |     155465 |
    | 3155466 | zhantao_155465 | 2015-04-18 21:20:20 |        1 |     155465 |
    | 4155466 | zhantao_155465 | 2015-04-18 21:20:37 |        1 |     155465 |
    +---------+----------------+---------------------+----------+------------+
    5 rows in set (0.03 sec)
    下面是从网上找的分析
    感觉说的很有道理
    给我的感觉就是联合索引  不用回表
     
     

    有了上面这些数据,我们整理下吧。未存在缓存下的数据。

    看这个表,全表扫描最慢,我们可以理解,同时主键查询比覆盖所有扫描慢也还能接受,但是为什么主键扫描会比非主键扫描慢?

    而且非主键查询需要消耗的1次查询的io+一次回表的查询IO,理论上是要比主键扫描慢,而出来的数据缺不是如此。

    那么就仔细看下是个查询方式在各个主要阶段消耗的时间吧。

    查询是否存在缓存,打开表及锁表这些操作时间是差不多,我们不会计入。具体还是看init,optimizing等环节消耗的时间。

    1.从这个表中,我们看到非主键索引和覆盖索引在准备时间上需要开销很多的时间,预估这两种查询方式都需要进行回表操作,所以花在准备上更多时间。

    2.第二项optimizing上,可以清晰知道,覆盖索引话在优化上大量的时间,这样在二级索引上就无需回表

    3. Sendingdata,全表扫描慢就慢在这一项上,因为是加载所有的数据页,所以花费在这块上时间较大,其他三者都差不多。

    4. 非主键查询话在freeingitems上时间最少,那么可以看出它在读取数据块的时候最少。

    5.相比较主键查询和非主键查询,非主键查询在Init,statistics都远高于主键查询,只是在freeingitems开销时间比主键查询少。因为这里测试数据比较少,但是我们可以预见在大数据量的查询上,不走缓存的话,那么主键查询的速度是要快于非主键查询的,本次数据不过是太小体现不出差距而已。

    6.在大多数情况下,全表扫描还是要慢于索引扫描的。

    tips:

    过程中的辅助命令:
    1.清楚缓存
    reset query cache ;
    flush tables;
    2.查看表的索引:
    show index from tablename;
     
    mysql> set profiling=1; 
    Query OK, 0 rows affected (0.00 sec)
     
    mysql> show profiles;  
    +----------+------------+------------------------------------------------------------+
    | Query_ID | Duration   | Query                                                      |
    +----------+------------+------------------------------------------------------------+
    |        1 | 0.00665000 | select * from t_user where f_rangenum in (1,2,3,4,5,6,7,8) |
    +----------+------------+------------------------------------------------------------+
    1 row in set (0.00 sec)
     
     
    mysql> show profile for query 1; 
    +--------------------+----------+
    | Status             | Duration |
    +--------------------+----------+
    | starting           | 0.000153 |
    | Opening tables     | 0.000023 |
    | System lock        | 0.000006 |
    | Table lock         | 0.000012 |
    | init               | 0.000084 |
    | optimizing         | 0.000014 |
    | statistics         | 0.001265 |
    | preparing          | 0.000018 |
    | executing          | 0.000004 |
    | Sending data       | 0.003164 |
    | end                | 0.000010 |
    | query end          | 0.000004 |
    | freeing items      | 0.001880 |
    | logging slow query | 0.000007 |
    | cleaning up        | 0.000006 |
    +--------------------+----------+
    15 rows in set (0.00 sec)
    最后关于mysql  where in 是否启用索引
    直接上explain结果 一目了然
    mysql> explain select * from t_user where f_rangenum in (select f_id from t_globe_num);                   
    +----+--------------------+-------------+-----------------+---------------+---------+---------+------+---------+--------------------------+
    | id | select_type        | table       | type            | possible_keys | key     | key_len | ref  | rows    | Extra                    |
    +----+--------------------+-------------+-----------------+---------------+---------+---------+------+---------+--------------------------+
    |  1 | PRIMARY            | t_user      | ALL             | NULL          | NULL    | NULL    | NULL | 6001509 | Using where              |
    |  2 | DEPENDENT SUBQUERY | t_globe_num | unique_subquery | PRIMARY       | PRIMARY | 8       | func |       1 | Using index; Using where |
    +----+--------------------+-------------+-----------------+---------------+---------+---------+------+---------+--------------------------+
    2 rows in set (0.00 sec)
     
    mysql> explain select * from t_user where f_rangenum in (1,2,3,4,5,6);                 
    +----+-------------+--------+-------+------------------+------------------+---------+------+------+-------------+
    | id | select_type | table  | type  | possible_keys    | key              | key_len | ref  | rows | Extra       |
    +----+-------------+--------+-------+------------------+------------------+---------+------+------+-------------+
    |  1 | SIMPLE      | t_user | range | f_rangenum_index | f_rangenum_index | 4       | NULL |   36 | Using where |
    +----+-------------+--------+-------+------------------+------------------+---------+------+------+-------------+
    1 row in set (0.04 sec)
    可以看到 如果in里面是值  那么会启用index
    但是如果 是子句则不走index是全表扫描
     
    t_user表600万个数据
    t_globe_num 表1万个数据
    rows 那里显示6001509 ~~~
    mysql要查600多万条数据 卡死你~~~~
     
  • 相关阅读:
    UML建模之时序图(Sequence Diagram)
    UML统一建模语UML2和EnterpriseArchitect
    FTP服务器的搭建
    Ubuntu下Apache重启错误:Could not reliably determine解决
    JSP的优势 和劣势 与php的比较
    [置顶] Ajax 初步学习总结
    pv ticketlock解决虚拟环境下的spinlock问题
    Tomcat从零开始(十)Loader
    HDU 4740 The Donkey of Gui Zhou (模拟)
    关于cvScalar的那些事
  • 原文地址:https://www.cnblogs.com/ztteng/p/4438466.html
Copyright © 2011-2022 走看看