zoukankan      html  css  js  c++  java
  • mysql索引

    一、索引简介

    数据库中专门用于帮助用户快速查找数据的一种数据结构。类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置吗,然后直接获取。


    ## 二、索引的作用 索引的作用是约束和加速查找
    ## 三、无索引与有索引的区别 无索引: 从前往后一条一条查询

    有索引:创建索引的本质,就是创建额外的文件(以某种格式存储),查询的时候,先去额外的文件找,定好位置,然后再去原始表中直接查询。但是创建索引越多,对硬盘也是有损耗的。

    注意:建立索引后查询加快,但是插入更新删除依然慢;创建索引之后,必须命中索引才会达到加速查询的效果


    ## 四、常见的几种索引

    单列:普通索引,唯一索引,主键索引

    多列:联合索引(组合索引),包括联合主键索引、联合唯一索引、联合普通索引

    唯一索引

    加速查找 + unique(约束) 可以为空

    普通索引

    只能加速查找
    语法为create index ix_name on userinfo(name);

    主键索引

    加速查找 + 约束(不为空)

    注意:主键索引比普通索引快


    ## 五、索引的类型 索引按类型分为hash索引和BTree索引

    hash索引

    hash索引生成的hash值是无序的,所以查询单条快,范围查询(> < like)慢

    BTree索引

    B+树,层数越多,数据量呈指数增长,BTree索引是innodb的默认索引


    ## 六、索引的使用

    普通索引

    普通索引仅有一个作用:加速查找

    创建表的时候添加索引

    create table userinfo(
       nid int not null auto_increment primary key,
       name varchar(32) not null,
       email varchar(64) not null,
       index ix_name(name)
    );
    
    

    给已有表添加索引 ```sql create index 索引的名字 on 表名(列名)
    
    删除索引
    ```sql
    drop index 索引的名字 on 表名
    
    

    查看索引

    show index from 表名
    
    

    ### 唯一索引 唯一索引的两个作用:加速查找和唯一约束(可以为null)

    创建表的时候添加唯一索引

    create table userinfo(
       id int not null auto_increment primary key,
       name varchar(32) not null,
       email varchar(64) not null,
       unique  index  ix_name(name)
    );
    

    给已有的表添加唯一索引

    create unique index 索引名 on 表名(列名)
    

    删除索引

    drop unique index 索引名 on 表名
    

    ### 主键索引

    主键索引的两个作用:加速查找和唯一约束(不能为null)

    创建表的时候添加主键索引

    create table userinfo(
        id int not null auto_increment primary key,  # 主键索引
        name varchar(32) not null,
        email varchar(64) not null,
        unique  index  ix_name(name)
    );
    或者
    create table userinfo(
        id int not null auto_increment,
        name varchar(32) not null,
        email varchar(64) not null,
        primary key(id),
        unique  index  ix_name(name)
    );
    
    

    ### 组合索引 组合索引是将多个列组合成一个索引 ```sql create index 索引名 on 表名(列名1,列名2);
    <br>
    ## 覆盖索引和索引合并
    
    覆盖索引:在索引文件中直接获取数据(不需要回到数据表)
    例如把name设为索引
    ```sql
    select name from userinfo where name='mandy';
    

    索引合并:把多个单列索引合并使用

    select * from userinfo where name='sophia' and id=12;
    
    

    七、利用索引加快查询速度

    数据库中添加索引确实会让查询速度加快,但是前提是要命中索引

    准备数据

    准备300w条数据

    #1. 准备表
    create table userinfo(
    id int,
    name varchar(20),
    gender char(6),
    email varchar(50)
    );
    
    #2. 创建存储过程,实现批量插入300万条记录
    delimiter $$ #声明存储过程的结束符号为$$
    create procedure auto_insert1()
    BEGIN
        #设置变量1,默认值为1
        declare i int default 1;
        while(i<=3000000)do
           #concat,字符串拼接。当i为1时,那么concat('alex',i)表示为alex1
            insert into userinfo values(i,concat('alex',i),'male',concat('egon',i,'@oldboy'));
            set i=i+1;
        end while;
    END$$ #$$结束
    delimiter ; #重新声明分号为结束符号
    
    #3. 查看存储过程
    show create procedure auto_insert1G 
    
    #4. 调用存储过程
    call auto_insert1();
    
    

    上面这种方式太慢了,300万条数据,最快也要几个小时 下面我们使用协程来帮助我们,插入300万条数据,只需要几十秒 ```python import pymysql import gevent import time

    class MyPyMysql:
    def init(self, host, port, username, password, db, charset='utf8'):
    self.host = host # mysql主机地址
    self.port = port # mysql端口
    self.username = username # mysql远程连接用户名
    self.password = password # mysql远程连接密码
    self.db = db # mysql使用的数据库名
    self.charset = charset # mysql使用的字符编码,默认为utf8
    self.pymysql_connect() # __init__初始化之后,执行的函数

    def pymysql_connect(self):
        # 用pymysql连接数据库
        self.conn = pymysql.connect(
            host=self.host,
            port=self.port,
            user=self.username,
            password=self.password,
            db=self.db,
            charset=self.charset,
        )
        # 连接mysql之后执行的函数
        self.asynchronous()
    
    def run(self, nmin, nmax):
        # 创建游标
        self.cur = self.conn.cursor()
    
        # 定义sql语句,插入id,name,gender,email
        sql = 'insert into userinfo(id,name,gender,email) values (%s,%s,%s,%s)'
    
        # 定义总插入行数为一个空列表
        data_list = []
        for i in range(nmin, nmax):
            # 添加所有任务到总的任务列表
            result = (i, 'alice' + str(i), 'female', 'girl' + str(i) + '@qq.com')
            data_list.append(result)
    
        # 执行多行插入,executemany,需要接收的第一个参数为sql语句,第二个参数为序列
        content = self.cur.executemany(sql, data_list)
        if content:
            print('成功插入第{}条数据'.format(nmax-1))
    
        # 提交数据,必须提交,不然数据不会保存
        self.conn.commit()
    
    def asynchronous(self):
        # g_l任务列表
        # 定义了异步的函数:这里用到了gevent.spawn方法
        max_line = 10000  # 定义每次插入行数,即一次插入10000行
        # spawn开启协程,第一个参数为执行的函数,后面为协程函数传入的参数,返回一个greenlet对象
        g_l = [gevent.spawn(self.run, i, i+max_line) for i in range(1, 3000001, max_line)]
    
        # gevent.joinall()  等待所有操作都执行完毕
        gevent.joinall(g_l)
        self.cur.close()    # 关闭游标
        self.conn.close()   # 关闭pymysql连接
    

    if name == 'main':
    start_time = time.time()
    st = MyPyMysql('127.0.0.1', 3306, 'root', '123', 'db2') # 实例化类,传入参数
    end_time = time.time()
    delta_time = end_time-start_time
    print('程序耗时:%s秒' % delta_time)

    
    <br>
    运行结果
    ```python
    成功插入第2970000条数据
    成功插入第2980000条数据
    成功插入第2990000条数据
    成功插入第3000000条数据
    程序耗时:38.6165976524353秒
    
    

    可以看到,之间大大缩短了,只用了38秒!

    这是因为,一般插入表数据是这样的


    insert into userinfo(id,name,gender,email) values ('1','alex1','male','egon1@oldboy')
    
    

    使用协程插入数据是这样的

    insert into userinfo(id,name,gender,email) values ('1','alex1','male','egon1@oldboy'),('2','alex2','male','egon2@oldboy'),('3','alex3','male','egon3@oldboy')...后面有1万个元组
    
    

    一次插入10000条的效率显然比一次1条高,同时,方式二开启了300个协程,遇到I/O切换,综合起来效率就大大高于方式一了


    ### 测试

    注意:现在还没有建立任何索引!

    查询id=2834567的记录

    mysql> select * from userinfo where id=2834567;
    +---------+--------------+--------+--------------------+
    | id      | name         | gender | email              |
    +---------+--------------+--------+--------------------+
    | 2834567 | alice2834567 | female | girl2834567@qq.com |
    +---------+--------------+--------+--------------------+
    1 row in set (1.13 sec)
    

    查询name='alice1929876'的记录 ```sql mysql> select * from userinfo where name='alice1929876'; +---------+--------------+--------+--------------------+ | id | name | gender | email | +---------+--------------+--------+--------------------+ | 1929876 | alice1929876 | female | girl1929876@qq.com | +---------+--------------+--------+--------------------+ 1 row in set (1.38 sec)
    可以看到查询速度是比较慢的
    
    <br>
    现在我们给userinfo表的name字段建立索引
    
    ```sql
    mysql> create index ix_name on userinfo(name);
    Query OK, 0 rows affected (8.10 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    

    可以看到给已经有很多数据的表建立索引是非常耗时的,因为要扫描数据生成索引文件

    使用索引查询name='alice2340268'的记录

    mysql> select * from userinfo where name='alice2340268';
    +---------+--------------+--------+--------------------+
    | id      | name         | gender | email              |
    +---------+--------------+--------+--------------------+
    | 2340268 | alice2340268 | female | girl2340268@qq.com |
    +---------+--------------+--------+--------------------+
    1 row in set (0.01 sec)
    
    

    与没有索引相比,查询速度快了一百多倍!


    前面提到,需要命中索引才能提高查询速度,下面演示一种没有命中索引的情况
    mysql> select * from userinfo where name like '%ice4586';
    +------+-----------+--------+-----------------+
    | id   | name      | gender | email           |
    +------+-----------+--------+-----------------+
    | 4586 | alice4586 | female | girl4586@qq.com |
    +------+-----------+--------+-----------------+
    1 row in set (1.63 sec)
    
    

    当使用 like '%xx' 时,因为%在前面,相当于包括了所有,所以这种情况下会进行全表扫描,而不会使用索引


    ### 小结

    不会使用索引的情况

    • 比较的字段未设置索引时
    • like模糊匹配通配符在前时 like '%xx'
    • 条件是 > < != 时(主键或索引是整数类型时还是会走索引)

    ### 最左前缀 最左前缀是组合索引里的说法,意为组合索引会先匹配前面的字段

    删除ix_name索引,避免干扰联合索引

    drop index ix_name on userinfo;
    
    

    创建联合索引 ```sql mysql> create index ix_name_email on userinfo(name,email); Query OK, 0 rows affected (9.71 sec) Records: 0 Duplicates: 0 Warnings: 0
    <br>
    
    设置name和email组合索引后,查询name,email字段会命中吗?来看看
    
    #### 查询name
    
    ```sql
    mysql> select * from userinfo where name='alice234239';
    +--------+-------------+--------+-------------------+
    | id     | name        | gender | email             |
    +--------+-------------+--------+-------------------+
    | 234239 | alice234239 | female | girl234239@qq.com |
    +--------+-------------+--------+-------------------+
    1 row in set (0.01 sec)
    
    

    显然name字段命中了


    #### 查询email ```sql mysql> select * from userinfo where email='girl1830002@qq.com'; +---------+--------------+--------+--------------------+ | id | name | gender | email | +---------+--------------+--------+--------------------+ | 1830002 | alice1830002 | female | girl1830002@qq.com | +---------+--------------+--------+--------------------+ 1 row in set (1.34 sec)
    email字段没有命中!!!这就是最左前缀,为了验证,我们再来看下name和email一起是否会命中
    
    <br>
    #### 查询name和email
    ```sql
    mysql> select * from userinfo where name='alice989986' and email='girl989986@qq.com';
    +--------+-------------+--------+-------------------+
    | id     | name        | gender | email             |
    +--------+-------------+--------+-------------------+
    | 989986 | alice989986 | female | girl989986@qq.com |
    +--------+-------------+--------+-------------------+
    1 row in set (0.01 sec)
    
    

    name和email命中了


    小结

    当使用name和email作为组合索引时

    字段 是否使用索引
    name
    name和email
    email

    ## 八、索引的注意事项 - 避免使用select * - 用count(1)或count(列)代替count(*) - 创建表时尽量使用char代替varchar - 表的字段顺序固定长度的字段优先 - 组合索引代替多个单列索引(经常使用多个条件查询时) - 尽量使用短索引 - 使用连接(join)来代替子查询 - 连表时注意条件类型一致 - 索引散列(重复少)不适合建索引

    关于第7点,mysql5.7版本没有区别

  • 相关阅读:
    lua 逻辑运算 and, or, not
    redis和memcache列出所有key
    Linux protobuf
    CGI,FastCGI,PHP-CGI与PHP-FPM
    lua使用笔记2:Linux 中安装php的lua扩展
    lua使用笔记1:Linux 中安装lua
    Android APK反编译详解
    git 常用命令
    git push 403错误解决方法
    linux(centos)搭建SVN服务器
  • 原文地址:https://www.cnblogs.com/zzliu/p/10664896.html
Copyright © 2011-2022 走看看