mysql有那些索引?分别在啥场景使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql有那些索引?分别在啥场景使用相关的知识,希望对你有一定的参考价值。

一、普通索引
这是最基本的索引,它没有任何限制。有以下几种创建方式:
1.创建索引
代码如下:
CREATE INDEX indexName ON mytable(username(length));
如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length,下同。
2.修改表结构
代码如下:
ALTER mytable ADD INDEX [indexName] ON (username(length)) -- 创建表的时候直接指定。
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, INDEX [indexName] (username(length)) );
-- 删除索引的语法:
DROP INDEX [indexName] ON mytable;
二、唯一索引
它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
代码如下:
CREATE UNIQUE INDEX indexName ON mytable(username(length))
-- 修改表结构
ALTER mytable ADD UNIQUE [indexName] ON (username(length))
-- 创建表的时候直接指定
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, UNIQUE [indexName] (username(length)) );
三、主键索引
它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引:
代码如下:
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, PRIMARY KEY(ID) );
当然也可以用 ALTER 命令。记住:一个表只能有一个主键。
四、组合索引
为了形象地对比单列索引和组合索引,为表添加多个字段:
代码如下:
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL );
为了进一步榨取mysql的效率,就要考虑建立组合索引。
二:使用索引的注意事项
使用索引时,有以下一些技巧和注意事项:
1.索引不会包含有NULL值的列
只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
2.使用短索引
对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
3.索引列排序
MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
4.like语句操作
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
5.不要在列上进行运算
select * from users where YEAR(adddate)<2007;
将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:
select * from users where adddate<‘2007-01-01';
6.不使用NOT IN和<>操作。
三:sql优化原则
常见的简化规则如下:
1.不要有超过5个以上的表连接(JOIN)
2.考虑使用临时表或表变量存放中间结果。
3.少用子查询
4.视图嵌套不要过深,一般视图嵌套不要超过2个为宜。
5.连接的表越多,其编译的时间和连接的开销也越大,性能越不好控制。
6.最好是把连接拆开成较小的几个部分逐个顺序执行。
7.优先执行那些能够大量减少结果的连接。
8.拆分的好处不仅仅是减少SQL Server优化的时间,更使得SQL语句能够以你可以预测的方式和顺序执行。
如果一定需要连接很多表才能得到数据,那么很可能意味着设计上的缺陷。
参考技术A

在满足语句需求的情况下,尽量少的访问资源是数据库设计的重要原则,这和执行的 SQL 有直接的关系,索引问题又是 SQL 问题中出现频率最高的,常见的索引问题包括:无索引(失效)、隐式转换。
1. SQL 执行流程看一个问题,在下面这个表 T 中,如果我要执行 select * from T where k between 3 and 5; 需要执行几次树的搜索操作,会扫描多少行?mysql> create table T (    -> ID int primary key,    -> k int NOT NULL DEFAULT 0,    -> s varchar(16) NOT NULL DEFAULT '',    -> index k(k))    -> engine=InnoDB;mysql> insert into T values(100,1, 'aa'),(200,2,'bb'),\\      (300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');
这分别是 ID 字段索引树、k 字段索引树。 

这条 SQL 语句的执行流程:

1. 在 k 索引树上找到 k=3,获得 ID=3002. 回表到 ID 索引树查找 ID=300 的记录,对应 R33. 在 k 索引树找到下一个值 k=5,ID=5004. 再回到 ID 索引树找到对应 ID=500 的 R4

5. 在 k 索引树去下一个值 k=6,不符合条件,循环结束

这个过程读取了 k 索引树的三条记录,回表了两次。因为查询结果所需要的数据只在主键索引上有,所以必须得回表。所以,我们该如何通过优化索引,来避免回表呢?
2. 常见索引优化2.1 覆盖索引覆盖索引,换言之就是索引要覆盖我们的查询请求,无需回表。

如果执行的语句是 select ID from T wherek between 3 and 5;,这样的话因为 ID 的值在 k 索引树上,就不需要回表了。

覆盖索引可以减少树的搜索次数,显著提升查询性能,是常用的性能优化手段。

但是,维护索引是有代价的,所以在建立冗余索引来支持覆盖索引时要权衡利弊。

2.2 最左前缀原则

B+ 树的数据项是复合的数据结构,比如 (name,sex,age) 的时候,B+ 树是按照从左到右的顺序来建立搜索树的,当 (张三,F,26) 这样的数据来检索的时候,B+ 树会优先比较 name 来确定下一步的检索方向,如果 name 相同再依次比较 sex 和 age,最后得到检索的数据。

    # 有这样一个表 P

    mysql> create table P (id int primary key, name varchar(10) not null, sex varchar(1), age int, index tl(name,sex,age)) engine=IInnoDB;

    mysql> insert into P values(1,'张三','F',26),(2,'张三','M',27),(3,'李四','F',28),(4,'乌兹','F',22),(5,'张三','M',21),(6,'王五','M',28);

    # 下面的语句结果相同

    mysql> select * from P where name='张三' and sex='F';     ## A1

    mysql> select * from P where sex='F' and age=26;         ## A2

    # explain 看一下

    mysql> explain select * from P where name='张三' and sex='F';

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

    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref         | rows | filtered | Extra       |

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

    |  1 | SIMPLE      | P     | NULL       | ref  | tl            | tl   | 38      | const,const |    1 |   100.00 | Using index |

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

    mysql> explain select * from P where sex='F' and age=26;

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

    | id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |

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

    |  1 | SIMPLE      | P     | NULL       | index | NULL          | tl   | 43      | NULL |    6 |    16.67 | Using where; Using index |

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

    可以清楚的看到,A1 使用 tl 索引,A2 进行了全表扫描,虽然 A2 的两个条件都在 tl 索引中出现,但是没有使用到 name 列,不符合最左前缀原则,无法使用索引。所以在建立联合索引的时候,如何安排索引内的字段排序是关键。评估标准是索引的复用能力,因为支持最左前缀,所以当建立(a,b)这个联合索引之后,就不需要给 a 单独建立索引。原则上,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。上面这个例子中,如果查询条件里只有 b,就是没法利用(a,b)这个联合索引的,这时候就不得不维护另一个索引,也就是说要同时维护(a,b)、(b)两个索引。这样的话,就需要考虑空间占用了,比如,name 和 age 的联合索引,name 字段比 age 字段占用空间大,所以创建(name,age)联合索引和(age)索引占用空间是要小于(age,name)、(name)索引的。

    2.3 索引下推

    以人员表的联合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是26岁的所有男性”。那么,SQL 语句是这么写的mysql> select * from tuser where name like '张%' and age=26 and sex=M;
    通过最左前缀索引规则,会找到 ID1,然后需要判断其他条件是否满足在 MySQL 5.6 之前,只能从 ID1 开始一个个回表。到主键索引上找出数据行,再对比字段值。而 MySQL 5.6 引入的索引下推优化(index condition pushdown),可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。这样,减少了回表次数和之后再次过滤的工作量,明显提高检索速度。

    2.4 隐式类型转化

    隐式类型转化主要原因是,表结构中指定的数据类型与传入的数据类型不同,导致索引无法使用。所以有两种方案:修改表结构,修改字段数据类型。

    修改应用,将应用中传入的字符类型改为与表结构相同类型。

    3. 为什么会选错索引3.1 优化器选择索引是优化器的工作,其目的是找到一个最优的执行方案,用最小的代价去执行语句。在数据库中,扫描行数是影响执行代价的因素之一。扫描的行数越少,意味着访问磁盘数据的次数越少,消耗的 CPU 资源越少。当然,扫描行数并不是唯一的判断标准,优化器还会结合是否使用临时表、是否排序等因素进行综合判断。

    3.2 扫描行数

    MySQL 在真正开始执行语句之前,并不能精确的知道满足这个条件的记录有多少条,只能通过索引的区分度来判断。显然,一个索引上不同的值越多,索引的区分度就越好,而一个索引上不同值的个数我们称为“基数”,也就是说,这个基数越大,索引的区分度越好。# 通过 show index 方法,查看索引的基数mysql> show index from t;+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+| t     |          0 | PRIMARY  |            1 | id          | A         |       95636 |     NULL | NULL   |      | BTREE      |         |               || t     |          1 | a        |            1 | a           | A         |       96436 |     NULL | NULL   | YES  | BTREE      |         |               || t     |          1 | b        |            1 | b           | A         |       96436 |     NULL | NULL   | YES  | BTREE      |         |               |+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    MySQL 使用采样统计方法来估算基数:采样统计的时候,InnoDB 默认会选择 N 个数据页,统计这些页面上的不同值,得到一个平均值,然后乘以这个索引的页面数,就得到了这个索引的基数。而数据表是会持续更新的,索引统计信息也不会固定不变。所以,当变更的数据行数超过 1/M 的时候,会自动触发重新做一次索引统计。

    在 MySQL 中,有两种存储索引统计的方式,可以通过设置参数 innodb_stats_persistent 的值来选择:

    on 表示统计信息会持久化存储。默认 N = 20,M = 10。

    off 表示统计信息只存储在内存中。默认 N = 8,M = 16。

    由于是采样统计,所以不管 N 是 20 还是 8,这个基数都很容易不准确。所以,冤有头债有主,MySQL 选错索引,还得归咎到没能准确地判断出扫描行数。

    可以用 analyze table 来重新统计索引信息,进行修正。

    ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
    3.3 索引选择异常和处理1. 采用 force index 强行选择一个索引。2. 可以考虑修改语句,引导 MySQL 使用我们期望的索引。3. 有些场景下,可以新建一个更合适的索引,来提供给优化器做选择,或删掉误用的索引。

mysql 知识2

mysql 知识2

  • 存储引擎
  1. InnoDB 和MYISAM 存储引擎的区别?
  • 索引
  1. 什么是索引?你知道Mysql 有哪些索引?分别介绍一下
  2. 索引的优缺点?那些情况适合建索引那些情况不适合建索引?
  • 事务
  1. 什么是事务,它有哪些特性?说一说事务的隔离级别,分别解决了什么问题?
  2. 事务的实现原理?事务的分类?使用事务应该注意的问题?
  • SQL 优化
  1. SQL 优化的一般步骤
  2. 常用的SQL 的优化
  • 其他
  1. 数据库的三大范式
  2. 什么是存储过程?有哪些优缺点?
  3. drop、truncate、与delete的区别?
  4. 什么是视图?以及视图的使用场景有哪些?
  5. 超键、候选键、主键、外键分别是什么?
  6. Mysql 的几种连接方式?
  7. 什么是Mysql 的完整性约束?

 

  • InnoDB 和MYISAM 存储引擎的区别

InnoDB:

InnoDB 存储引擎支持事务、支持外键、支持非锁定读、行锁设计其设计主要面向OLTP 应用。
InnoDB 存储引擎表采用聚集的方式存储,因此每张表的存储顺序都按主键的顺序存放,如果没有指定主键,InnoDB 存储引擎会为每一行生成一个6字节的ROWID并以此作为主键。
InnoDB 存储引擎通过MVCC 获的高并发性,并提供了插入缓冲、二次写、自适应哈希索引和预读等高性能高可用功能
InnoDB 存储引擎默认隔离级别为REPEATABLE_READ(重复读)并采用next-key locking(间隙锁)来避免幻读

MySIAM:

MYISAM 存储引擎不支持事务、表锁设计、支持全文索引其设计主要面向OLAP 应用
MYISAM 存储引擎表由frm、MYD 和MYI 组成,frm 文件存放表格定义,MYD 用来存放数据文件,MYI 存放索引文件。MYISAM 存储引擎与众不同的地方在于它的缓冲池只缓存索引文件而不缓存数据文件,数据文件的缓存依赖于操作系统。

操作区别:

MYISAM 保存表的具体行数,不带where 是可直接返回。InnoDB 要扫描全表。
DELETE 表时,InnoDB 是一行一行的删除,MYISAM 是先drop表,然后重建表
InnoDB 跨平台可直接拷贝使用,MYISAM 不行
InnoDB 表格很难被压缩,MYISAM 可以

选择:

MyISAM相对简单所以在效率上要优于InnoDB。如果系统读多,写少。对原子性要求低。那么MyISAM最好的选择。且MyISAM恢复速度快。可直接用备份覆盖恢复。
InnoDB 更适合系统读少,写多的时候,尤其是高并发场景。

以下是一个具体的选择场景来自:https://www.cnblogs.com/kuangdaoyizhimei/p/3933546.html

  • 索引
  1. 什么是索引?你知道Mysql 有哪些索引?分别介绍一下

在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。——百度百科

索引是对数据库表中一或多个列的值进行排序的结构,是帮助MySQL高效获取数据的数据结构

(数据库是磁盘文件,磁盘IO 的代价较高,所以采用索引减少IO 次数)

Mysql 中常用的索引有B+ 树索引(包括普通索引、唯一索引、主键索引),哈希索引,全文索引,R-TREE 索引(空间索引,主要用于地理空间数据类型,很少使用)。

Mysql 传统意义上的索引为B+ 树索引,B+ 树索引的本质就是B+ 树在数据库中的实现,由于B+ 树的高扇出性,数据库中的B+ 树的高一般为2-4层,因此查找某一键值的行记录只需2-4次IO,大概0.02~0.04秒。

(扇出性:是指该模块直接调用的下级模块的个数。扇出大表示模块的复杂度高,需要控制和协调过多的下级模块)

B+ 树索引主要分为聚集索引和辅助索引。

聚集索引是根据每张表的主键建造的一棵B+ 树,叶子节点中存放的是整张表的行记录。一张表只能有一个聚集索引。因为聚集索引在逻辑上是连续的,所以它对于主键的排序查找和范围查找速度非常快。

辅助索引与聚集索引不同的地方在于,辅助索引不是唯一的,它的叶子节点只包含行记录的部分数据以及对应聚集索引的节点位置。通过辅助索引来查找数据时,先遍历辅助索引找到对应主键索引,再通过主键索引查找对应记录。

在MYISAM 中主键索引和辅助索引都相当上述辅助索引,索引页中存放的是主键和指向数据页的偏移量,数据页中存放的是主键和该主键所属行记录的地址空间。唯一的区别是MYISAM 中主键索引不能重复,辅助索引可以。

B+ 树索引从使用上来说还有联合索引和覆盖索引。

联合索引是指对表上的多个列进行索引。它对对应多个列的指定获取比较快。另外一个好处是联合索引对第二个键已经排好序了,所以对两个列的排序获取可以避免多做一次排序操作。

覆盖索引其实更算一种思想,能够从辅助索引中获取信息,就不需要查询聚集索引中的数据。使用辅助索引的好处在于辅助索引包含的信息少,所以大小远小于聚集索引,因此可以大大减少IO 操作。

哈希索引是一种自适应的索引,数据库会根据表的使用情况自动生成哈希索引,我们人为是没办法干预的。

InnoDB 储存引擎采用的哈希函数为除法散列方式,采用的冲突处理方法为链地址法。它指定查询的速度很快,但是范围查询就无能为力了。

全文索引用于实现关键词搜索。但它只能根据空格分词,因此不支持中文。

  1. 索引的优缺点?那些情况适合建索引那些情况不适合建索引?

以下内容摘自博客:https://blog.csdn.net/u013310119/article/details/52527632

索引的优点:

通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

索引的缺点:

创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

哪些情况需要加索引?

在经常需要搜索的列上,可以加快搜索的速度;
在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

哪些情况不需要加索引?

对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

  • 事务

什么是事务,它有哪些特性?说一说事务的隔离级别,分别解决了什么问题?

事务就是一组原子性的操作,这些操作要么全部发生,要么全部不发生。事务把数据库从一种一致性状态转换成另一种一致性状态。

事务具有ACID 四种特性,即原子性(atomicity),一致性(consistency),隔离性(isolation),持久性(durability):

原子性:指的是事务是一个不可分割的操作,要么全都正确执行,要么全都不执行。
一致性:指的是事务把数据库从一种一致性状态转换成另一种一致性状态,事务开始前和事务结束后,数据库的完整性约束没有被破坏。
隔离性:要求每个读写事务相互之间是分开的,在事务提交前对其他事务是不可见的
持久性:指的是事务一旦提交,其结果就是永久性的,即使宕机也能恢复。
说一说事务的隔离级别,分别解决了什么问题?

事务有4 个隔离级别,分别是:

读未提交(read uncommit)
读已提交(read commit)
可重复读(repeatable read)
和序列化(serializable)。
隔离级别依次提高,分别解决了脏读、不可重读和幻读。

【拓展】

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

InnoDB 默认隔离级别为repeatable read,但是通过next-key lock 解决了幻读,保证了ACID。

  • 事务的实现原理?事务的分类?使用事务应该注意的问题?

事务是基于重做日志文件(redo log)和回滚日志(undo log)实现的。

每提交一个事务必须先将该事务的所有日志写入到重做日志文件进行持久化,数据库就可以通过重做日志来保证事务的原子性和持久性。

每当有修改事务时,还会产生undo log,如果需要回滚,则根据undo log 的反向语句进行逻辑操作,比如insert 一条记录就delete 一条记录。undo log 主要实现数据库的一致性,还可以用来实现MVCC。

事务主要分为:

扁平事务
带有保存点的扁平事务
链事务
嵌套事务
分布式事务

使用事务应该注意的问题?

不要在循环中使用事务(循环提交会导致大量的redo log)
不要使用自动提交
不要使用自动回滚
长事务切分处理

  • SQL 优化

SQL 优化的一般步骤

1. 通过show status 命令了解各SQL 的执行频率

show [session|global] status like “Com_%”;

session:当前连接执行的统计结果
global:上一次数据库启动至今的统计结果
常见的执行参数:

Com_select:执行查询的次数
Com_insert:执行插入的次数
Com_update:执行更新的次数
Com_delete:执行删除的次数
Com_rows_read:执行查询的返回行数(举一反三:inserted、updated、deleted)
Connection:试图连接Mysql 服务器的次数
Uptime:服务器工作时间
Slow_queries:慢查询次数
mysql> show global status like " Slow_queries";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries | 0 |
+---------------+-------+

2. 定位执行效率最低的SQL 语句

可以通过两个办法定位效率较低的SQL 语句:

通过慢查询日志定位执行效率低的SQL,但是只能在查询完过后
使用show processlist 命令查看当前Mysql 正在进行的线程,包括线程状态、是否锁表


3. 通过EXPLAIN 分析低效SQL 的执行计划

查询到效率低的sql 语句后,可以通过EXPLAIN 分析低效SQL 的执行计划。

mysql> explain select * from comment
;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | comment | NULL | ALL | NULL | NULL | NULL | NULL | 92 | 100 | NULL |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set


select_type: 表示select 的类型,常见取值有SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY、UNION、SUBQUERY等。
table: 输出结果集的表
type: 表示Mysql 的访问方式(从上到下依次变快)
type=all,全表扫描,Mysql 遍历全表来找到匹配的行
type=index,Mysql 遍历整个索引来找到匹配的行
type=range,索引范围扫面
type=ref,使用非唯一索引扫描或唯一索引的前缀扫描
type=eq_ref,使用唯一索引
type-const/system,单表中最多只有一个匹配行
type=NUll,MySQL 不用访问表或者索引就能直接得到结果
possible_key: 表示查询时可能用到的索引
key: 表示实际用到的索引
key_len: 使用到索引字段的长度
rows: 扫描行的数量
Extra: 执行情况的说明和描述。
4. 通过show profile 分析SQL

//默认不开启profile,使用时先开启profile
mysql> set profiling=1;
Query OK, 0 rows affected

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
| 1 |
+-------------+
1 row in set

//在InnoDB 下获取表行数
mysql> select count(*) from comment;
+----------+
| count(*) |
+----------+
| 92 |
+----------+
1 row in set

// 查开执行时间
mysql> show profiles;
+----------+------------+------------------------------+
| Query_ID | Duration | Query |
+----------+------------+------------------------------+
| 1 | 0.0109785 | select @@profiling |
| 2 | 0.05502275 | select count(*) from comment |
+----------+------------+------------------------------+
2 rows in set

//具体查开每一步执行时间
mysql> show profile for query 2
;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 9.2E-5 |
| checking permissions | 9E-6 |
| Opening tables | 2E-5 |
| init | 1.8E-5 |
| System lock | 9E-6 |
| optimizing | 0.054635 | //在优化处理耗时最多
| statistics | 5.6E-5 |
| preparing | 1.7E-5 |
| executing | 5E-6 |
| Sending data | 8E-5 |
| end | 5E-6 |
| query end | 1.5E-5 |
| closing tables | 9E-6 |
| freeing items | 3.6E-5 |
| cleaning up | 1.9E-5 |
+----------------------+----------+
15 rows in set


5. 还可以通过trace 分析优化器如何选择执行计划的(有兴趣可以看一下)


常用的SQL 的优化

1. 对大批量插入数据

MYISAM 表
可以通过DISABLE KEY 和 ENABLE KEY 关闭和打开MYISAM 表索引的更新来提高效率。

ALTER TABLE table_name DISABLE KEY //在导入数据前关闭索引更新
loading the data
ALTER TABLE table_name ENABLE KEY //导入完成后开启

一个书上的例子:

直接导入数据(115.12 s) VS 优化导入(18.59 s)

InnoDB 表
按主键顺序导入
关闭唯一性检验,导入后再开启
关闭自动提交,导入后再开启
2. 优化INSERT 语句

同一个客户端,应使用多个值表的insert 语句,这种方式可以大大缩减客户端与数据库之间的连接、关闭等消耗。
insert into table_name values(1,2)(1,3)(1,4)...
1
从不同的客户端插入的话,可以采用INSERT DELAYED 语句获得更高的速度。它的意思是让INSERT 语句马上执行,因为数据都被放在内存的队列中,并没有真正写入磁盘。
从文本文件装载一个表时,使用LOAD DATA INFILE 通常比INSERT 快20 倍。
将索引文件和数据文件分放在不同的磁盘上(利用建表中的选项)。
如果是批量插入,MYISAM 可以通过改变bulk_insert_buffer_size (插入缓存容量大小)变量值提高速度。
3. 优化order by 语句

在MySQL 数据库中有两种排序方式:

通过有序索引扫描直接返回有序数据
通过对返回数据进行排序(Filesort 排序)
其中Filesort 会多排一次序,所以在使用order by 语句的时候尽量用到索引。下面的规则是可以用到索引的情况:

where 和 order by 使用相同的索引
order by 的顺序和索引的顺序相同
order by 的字段都是升序或都是降序
同理用不到索引的情况:

order by 的字段混合ASC 和DESC
用于查询行的关键字与ORDER BY 中所使用的不同
对不同的关键字使用order by
4. 优化group by 语句

默认情况下group by 语句对分组的数据进行排序操作,如果不需要排序操作可以通过order by null 禁止排序。

5. 优化嵌套语句

如果可以的话,特别是where 中包含索引的情况,用join 语法来代替嵌套语法(in)因为join 不需要在MySQL 的内存中创建临时表。

6. 优化or语句

对于含有or 的查询语句,如果要利用索引,则or 之间的每一个条件都必须用到索引,如果没用索引,可以考虑增加索引。否则会全表扫面。

7. 优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好的提高性能。一个常见又头痛的场景是"limit 1000,20" 此时MySQL 排序出前1020 条数据后,只需要返回20条数据,查询和排序的代价都很高。有两种优化方案

在索引上完成排序分页的操作,最后根据主键关联查询需要的内容,让MySQL 扫描尽可能少的页面来提高分页操作。
mysql> explain select comment_id,comment_content from comment order by comment_create_time limit 50,5;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | comment | NULL | ALL | NULL | NULL | NULL | NULL | 92 | 100 | Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set

mysql> explain select a.comment_id,a.comment_content from comment a inner join(select comment_id from comment order by comment_create_time limit 50,5)b on a.comment_id=b.comment_id ;
+----+-------------+------------+------------+--------+---------------+---------+---------+--------------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+---------+---------+--------------+------+----------+----------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 55 | 100 | NULL |
| 1 | PRIMARY | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | b.comment_id | 1 | 100 | NULL |
| 2 | DERIVED | comment | NULL | ALL | NULL | NULL | NULL | NULL | 92 | 100 | Using filesort |
+----+-------------+------------+------------+--------+---------------+---------+---------+--------------+------+----------+----------------+
3 rows in set

把limit 查询转换成某个位置的查询,limit m,n -> limit n
8. 使用SQL 提示

SQL 提示是优化数据库的一个重要手段,常用的SQL 提示:

USE INDEX
推荐数据库使用某个索引,可以让Mysql 不再考虑其他可用索引

sql语句 use index(index_name);
1
IGNORE INDEX
忽视数据库某个索引,可以让Mysql 不再考虑这个索引

sql语句 ignore index(index_name);
1
FORCE INDEX
强迫数据库使用某个索引,使用use index 数据库还是可能不用这个索引,但是force index 数据库必须使用这个索引

sql语句 force index(index_name);
1
其他

  • 数据库的三大范式

第一范式:

确保每列的原子性(强调的是列的原子性,即列不能够再分成其他几列)。如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式.

例如:顾客表(姓名、编号、地址、……)其中"地址"列还可以细分为国家、省、市、区等。

第二范式:

在第一范式的基础上更进一层,目标是确保表中的每列都和主键相关(一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的部分)如果一个关系满足第一范式,并且除了主键以外的其它列,都依赖于该主键,则满足第二范式。

例如:订单表(订单编号、产品编号、定购日期、价格、……),"订单编号"为主键,"产品编号"和主键列没有直接的关系,即"产品编号"列不依赖于主键列,应删除该列。

第三范式:

在第二范式的基础上更进一层,目标是确保每列都和主键列直接相关,而不是间接相关(另外非主键列必须直接依赖于主键,不能存在传递依赖)。如果一个关系满足第二范式,并且不依赖于除了主键以外的其它列,则满足第三范式.

为了理解第三范式,需要根据Armstrong公里之一定义传递依赖。假设A、B和C是关系R的三个属性,如果A-〉B且B-〉C,则从这些函数依赖中,可以得出A-〉C,如上所述,
依赖A-〉C是传递依赖。

例如:订单表(订单编号,定购日期,顾客编号,顾客姓名,……),初看该表没有问题,满足第二范式,每列都和主键列"订单编号"相关,再细看你会发现"顾客姓名"和"顾客
编号"相关,"顾客编号"和"订单编号"又相关,最后经过传递依赖,"顾客姓名"也和"订单编号"相关。为了满足第三范式,应去掉"顾客姓名"列,放入客户表中。

  • 什么是存储过程?有哪些优缺点?

存储过程是一些预编译的SQL语句。

优点:

存储过程是一个预编译的代码块,执行效率比较高
存储过程在服务器端运行,减少客户端的压力
允许模块化程序设计,只需要创建一次过程,以后在程序中就可以调用该过程任意次,类似方法的复用
一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率
可以一定程度上确保数据安全,对于没有权限执行存储过程的用户,也可授权他们执行存储过程。
缺点:

可移植性不灵活(因为存储过程依赖于具体的数据库)
不便于调试。
没办法应用缓存。虽然有全局临时表之类的方法可以做缓存,但同样加重了数据库的负担。如果缓存并发严重,经常要加锁,那效率实在堪忧。
无法适应数据库的切割(水平或垂直切割)。数据库切割之后,存储过程并不清楚数据存储在哪个数据库中。

  • 说一说drop、delete与truncate的区别?

drop直接删掉表有关的一切(数据/结构/约束…),不会记录日志,为DDL(Data Definition Language,数据库定义语言)操作。
truncate 删除表中所有数据(再插入时自增长id又从1开始),该操作也不会记录日志所以比较快,为DDL操作。只能删table。
DELETE语句执行删除的过程是每次从表中删除一行,需要记录日志,比较慢,可以加where 语句,为DML(Data Manipulation Language, 数据操纵语言)。可以删table 和view 。
速度上drop > truncate > delete

  • 什么是视图?以及视图的使用场景有哪些?

视图是一种虚拟的表,具有和物理表相同的功能,没有物理存储。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。

使用场景:

只希望用户查看特定信息的列

来源于多个表,可以创建视图提取我们需要的信息,简化操作。

  • 超键、候选键、主键、外键分别是什么?

超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。

候选键:是最小超键,即没有冗余元素的超键。

主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。

外键:在一个表中存在的另一个表的主键称此表的外键。

  • Mysql 的几种连接方式?

内连接:inner join on
组合两个表中的记录,返回关联字段相符的记录,也就是返回两个表的交集(阴影)部分。

左连接:left join on / left outer join on
left join 是left outer join的简写,它的全称是左外连接,是外连接中的一种。
左(外)连接,左表(a_table)的记录将会全部表示出来,而右表(b_table)只会显示符合搜索条件的记录。右表记录不足的地方均为NULL。

3. 右连接:right join on / right outer join on

right join是right outer join的简写,它的全称是右外连接,是外连接中的一种。
与左(外)连接相反,右(外)连接,左表(a_table)只会显示符合搜索条件的记录,而右表(b_table)的记录将会全部表示出来。左表记录不足的地方均为NULL。

全连接
MySQL目前不支持此种方式,可以用其他方式替代解决。

 

  • 什么是Mysql 的完整性约束?

完整性约束是对字段进行限制,从而符合该字段达到我们期望的效果。比如字段含有默认值,不能是NULL等,主要有唯一、自增、主键、外键约束。

PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录
FOREIGN KEY (FK) 标识该字段为该表的外键
NOT NULL 标识该字段不能为空
UNIQUE KEY (UK) 标识该字段的值是唯一的
AUTO_INCREMENT 标识该字段的值自动增长(整数类型,而且为主键)
DEFAULT 为该字段设置默认值

UNSIGNED 无符号
ZEROFILL 使用0填充

以上是关于mysql有那些索引?分别在啥场景使用的主要内容,如果未能解决你的问题,请参考以下文章

数据库MySQL中关于“多表关联更新”的那些事

BlockingQueue和List 有啥区别,BlockingQueue用在啥场景下

MYSQL无法使用索引的场景

MySQL like 在啥情况下会走索引

mysql 主键索引,联合索引,单列索引使用场景

MySQL 常见索引的使用场景与区别(SQL小技巧)