MySQL索引概览

MySql默认索引(InnoDB,B+Tree)

mysql> show variables like '%storage_engine%';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    112
Current database: test

+----------------------------+--------+
| Variable_name              | Value  |
+----------------------------+--------+
| default_storage_engine     | InnoDB |
| default_tmp_storage_engine | InnoDB |
| storage_engine             | InnoDB |
+----------------------------+--------+
3 rows in set (0.06 sec)

mysql>

为什么使用B+树:https://doctording.blog.csdn.net/article/details/145951503

MySql中B+树索引可以分为聚簇索引和非聚簇索引

聚簇索引(clustered index)

聚簇索引中键值的逻辑顺序决定了表中相应行的物理顺序(即索引中的数据物理存放地址和索引的顺序是一致的),可以这么理解:只要是索引是连续的,那么数据在存储介质上的存储位置也是连续的。

聚集索引就像我们根据拼音的顺序查字典一样,可以大大的提高效率。在经常搜索一定范围的值时,通过索引找到第一条数据,根据物理地址连续存储的特点,然后检索相邻的数据,直到到达条件截止项即可。

聚集索引:叶子节点包含了完整的数据记录

Cluster index is a type of index which sorts the data rows in the table on their key values. In the Database, there is only one clustered index per table.

A clustered index defines the order in which data is stored in the table which can be sorted in only one way. So, there can be an only a single clustered index for every table. In an RDBMS, usually, the primary key allows you to create a clustered index based on that specific column.

非聚簇索引

非聚簇索引的逻辑顺序与磁盘上的物理存储顺序不同。非聚集索引的键值在逻辑上也是连续的,但是表中的数据在存储介质上的物理顺序是不一致的,即记录的逻辑顺序和实际存储的物理顺序没有任何联系;索引的记录节点有一个数据指针指向真正的数据存储位置(在 InnoDB 中,这些指针通常是行的主键值)。

A non-clustered index stores the data at one location and indices at another location. The index contains pointers to the location of that data. A single table can have many non-clustered indexes as an index in the non-clustered index is stored in different places.

For example, a book can have more than one index, one at the beginning which displays the contents of a book unit wise while the second index shows the index of terms in alphabetical order.

A non-clustering index is defined in the non-ordering field of the table. This type of indexing method helps you to improve the performance of queries that use keys which are not assigned as a primary key. A non-clustered index allows you to add a unique key for a table.

聚簇/非聚簇对比和注意点

在这里插入图片描述

innodb-index-types

Mysql中关于聚集索引的说明

  • 如果一个主键被定义了,那么这个主键就是作为聚集索引
  • 如果没有主键被定义,那么该表的第一个唯一非空索引会被作为聚集索引
  • 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,该列的值会随着数据的插入进行自增

聚集索引的特点

  • 聚集索引表记录的排列顺序和索引的排列顺序保持一致,所以查询效率相当快。只要找到第一个索引记录的值,其余的连续性的记录也一定是连续存放的
  • 聚集索引的缺点就是修改,删除等操作会比较慢,因为它需要保持表中记录和索引的顺序一致,在插入新记录的时候就会对数据也重新做一次排序,可能产生页分裂/创建操作
  • InnoDB表数据本身就是一个按B+Tree组织的一个索引结构文件,叶节点包含了完整的数据记录(.ibd文件);MyIsam数据和索引文件是分开的(.MYD文件,.MYI文件)
为什么InnoDB要有主键?并且推荐使用整型自增主键?

为什么InnoDB要有主键?(InnoDB设计如此)

  • 如果一个主键被定义了,那么这个主键就是作为聚集索引
  • 如果没有主键被定义,那么该表的第一个唯一非空索引会被作为聚集索引
  • 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,该列的值会随着数据的插入进行自增

推荐使用整型?

  • B+Tree搜索进行比较操作时,显然整型比字符串比较快(原因1);整型占用空间小(原因2)

推荐使用自增?

补充:hash索引,直接定位到记录的磁盘地址(等值查找);但是区间查找用hash行不通,所以hash索引在数据库中用的少

自增是页不断的创建新增,尾部数据增加,调整小;如果非自增,涉及到页分裂/创建,B+Tree调整大

B+Tree 可视化操作

聚簇索引等于主键索引吗?

不完全

  1. 当表存在主键时,聚簇索引等于主键索引
  2. 当表中没有主键索引时,聚簇索引等于第一个不为null的唯一索引
  3. 如果mysql没有一个不为null的唯一索引,mysql会生成一个隐式主键列作为聚簇索引,这个隐藏的主键是一个6个字节的列,该列的值会随着数据的插入进行自增。

索引类型

主键索引和非主键索引的区别?
  • 非主键索引的叶子节点存放的是主键的值
  • 主键索引的叶子节点存放的是整行数据
  • 其中非主键索引也被称为二级索引,而主键索引也被称为聚簇索引。

eg:

  1. 如果查询语句是select * from table where ID = 100,即主键查询的方式,则只需要搜索 ID =100 的这棵 B+树。
  2. 如果查询语句是select * from table where k = 1,即非主键的查询方式,则先搜索k索引树,得到ID=100,再到ID索引树搜索一次,这个过程也被称为回表
主键和唯一索引有什么区别?
  • 主键是一种约束,唯一索引是一种索引,两者在本质上是不同的
  • 主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。
  • 唯一性索引列允许空值,而主键列不允许为空值。
  • 主键列在创建时,已经默认为空值 + 唯一索引了。
  • 主键可以被其它表引用为外键,而唯一索引不能。
  • 一个表最多只能创建一个主键,但可以创建多个唯一索引。
  • 主键更适合那些不容易更改的唯一标识,如自增列、身份证号等。
主键索引和普通索引例子
mysql> show create table test_user;
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table     | Create Table                                                                                                                                                                                                                                                                                                 |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test_user | CREATE TABLE `test_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `email` varchar(30) DEFAULT NULL,
  `password` varchar(32) DEFAULT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10000001 DEFAULT CHARSET=utf8 |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
  • 查看表索引

如下是个主键索引

mysql> SHOW INDEX FROM test_user;
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table     | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test_user |          0 | PRIMARY  |            1 | id          | A         |     9702644 |     NULL | NULL   |      | BTREE      |         |               |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> 
  • 1千万条数据select count(*)需要2 sec多
mysql> select count(*) from test_user;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row in set (2.06 sec)

mysql> select count(*) from test_user;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row in set (2.08 sec)

mysql> 
  • sql 主键 与 非主键 查询
mysql> EXPLAIN SELECT id,username,email,password FROM sample_data.test_user WHERE id=999999;
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table     | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | test_user | const | PRIMARY       | PRIMARY | 8       | const |    1 | NULL  |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)

mysql> EXPLAIN SELECT id,username,email,password FROM test_user WHERE username='username_9000000';
+----+-------------+-----------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | test_user | ALL  | NULL          | NULL | NULL    | NULL | 9702644 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

mysql> 
  • 查询时间
mysql> SELECT id,username,email,password FROM test_user WHERE username='username_9000000';
+---------+------------------+----------------+----------------------------------+
| id      | username         | email          | password                         |
+---------+------------------+----------------+----------------------------------+
| 9000000 | username_9000000 | 9000000@qq.com | 48e4e6ac22db9be84820222d57841428 |
+---------+------------------+----------------+----------------------------------+
1 row in set (3.02 sec)

mysql> SELECT id,username,email,password FROM sample_data.test_user WHERE id=999999;
+--------+-----------------+---------------+----------------------------------+
| id     | username        | email         | password                         |
+--------+-----------------+---------------+----------------------------------+
| 999999 | username_999999 | 999999@qq.com | 52c69e3a57331081823331c4e69d3f2e |
+--------+-----------------+---------------+----------------------------------+
1 row in set (0.00 sec)

mysql> 
  • 给 usename 建立索引
mysql> ALTER TABLE `test_user` ADD INDEX index_name(username) ;
Query OK, 0 rows affected (27.21 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql>  SHOW INDEX FROM test_user;
+-----------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table     | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test_user |          0 | PRIMARY    |            1 | id          | A         |     9702644 |     NULL | NULL   |      | BTREE      |         |               |
| test_user |          1 | index_name |            1 | username    | A         |     9702644 |     NULL | NULL   | YES  | BTREE      |         |               |
+-----------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)

mysql> 
  • 对普通字段建立索引后查询
mysql> EXPLAIN SELECT id,username,email,password FROM test_user WHERE username='username_9000000';
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
| id | select_type | table     | type | possible_keys | key        | key_len | ref   | rows | Extra                 |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | test_user | ref  | index_name    | index_name | 153     | const |    1 | Using index condition |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
1 row in set (0.01 sec)

mysql> SELECT id,username,email,password FROM test_user WHERE username='username_9000000';
+---------+------------------+----------------+----------------------------------+
| id      | username         | email          | password                         |
+---------+------------------+----------------+----------------------------------+
| 9000000 | username_9000000 | 9000000@qq.com | 48e4e6ac22db9be84820222d57841428 |
+---------+------------------+----------------+----------------------------------+
1 row in set (0.00 sec)

mysql>
  • and or 有索引的区别
mysql> SELECT id, username, email, PASSWORD FROM test_user WHERE `password` = '7ece221bf3f5dbddbe3c2770ac19b419' OR username = 'username_900000';
+--------+-----------------+---------------+----------------------------------+
| id     | username        | email         | PASSWORD                         |
+--------+-----------------+---------------+----------------------------------+
|  24601 | username_24601  | 24601@qq.com  | 7ece221bf3f5dbddbe3c2770ac19b419 |
| 900000 | username_900000 | 900000@qq.com | c3c4f8c964d8fbd9ce62ff3921bd5e79 |
+--------+-----------------+---------------+----------------------------------+
2 rows in set (3.42 sec)

mysql> SELECT id, username, email, PASSWORD FROM test_user WHERE `password` = '7ece221bf3f5dbddbe3c2770ac19b419' and username = 'username_900000';
Empty set (0.01 sec)

mysql> 

and语句中,username有索引,优先使用了索引
or语句中,username有索引,但是password是没有索引的,or语句还是扫描全部数据,所以很耗时

mysql> explain SELECT id, username, email, PASSWORD FROM test_user WHERE `password` = '7ece221bf3f5dbddbe3c2770ac19b419' and username = 'username_900000';
+----+-------------+-----------+------+---------------+------------+---------+-------+------+------------------------------------+
| id | select_type | table     | type | possible_keys | key        | key_len | ref   | rows | Extra                              |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+------------------------------------+
|  1 | SIMPLE      | test_user | ref  | index_name    | index_name | 153     | const |    1 | Using index condition; Using where |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+------------------------------------+
1 row in set (0.00 sec)

mysql> explain  SELECT id, username, email, PASSWORD FROM test_user WHERE `password` = '7ece221bf3f5dbddbe3c2770ac19b419' OR username = 'username_900000';
+----+-------------+-----------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | test_user | ALL  | index_name    | NULL | NULL    | NULL | 9702644 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

mysql> 
联合索引

https://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html
在这里插入图片描述

仍然是B+Tree(联合索引也是一个B+树,在索引比较的时候会按照次序的比较,否则无法比较,也就不能走联合索引), 索引中包含多个字段,按照联合索引的先后顺序;如a,b,c联合索引,则a; a,b; a,b,c;可以走索引,其它则不能

eg:对id列.name列.age列建立了一个联合索引 id_name_age_index,实际上相当于建立了三个索引(id),(id_name),(id_name_age)

联合索引为什么是最左前缀匹配

数据结构底层决定(严格的按照第一个,第二个,第三个字段一个一个匹配),不符合最左匹配则需要全局扫描了;且最左匹配原则遇到范围查询就停止匹配

eg:

create table test(
a int,
b int,
c int,
KEY a(a,b,c)
);

mysql> show index from test;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test  |          1 | a        |            1 | a           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| test  |          1 | a        |            2 | b           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| test  |          1 | a        |            3 | c           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.01 sec)

如下各种语句索引的使用情况:

where a = 3 用到索引 a

where a = 3 and b = 3 用到索引 a,b

where a = 3 and b = 4 and c = 5 用到索引 a,b,c

where b = 3 或者 where b = 3 and c = 4 或者 where c = 4 索引失效

where a = 3 and c = 5 用到索引 a

where a = 3 and b > 4 and c = 5 用到索引a和b(b的范围导致未用到c

where a = 3 and b like 'kk%' and c = 4 用到索引a,b,c

where a = 3 and b like '%kk' and c = 4 只用索引a

where a = 3 and b like '%kk%' and c = 4 只用到索引a

where a = 3 and b like 'k%kk%' and c = 4 用到a,b,c

where a > 1 and a < 3 and b > 1 用到索引a,没有用到索引b

联合索引优化例子

select * from table where a = xxx and b = xxx;占60%
select b from table where b = xxx; 占39%;

其它占1%;怎么优化?

解答:将第一条sql语句改为select * from table where b = xxx and a = xxx,然后建立ba的联合索引,因为ba的联合索引可以支持b和ba这两种组合的查找,这样就减少了开销

索引跳跃
特性说明
功能允许跳过联合索引的某些列,扩展索引使用范围。
适用条件跳过列是等值条件,且剩余列构成有效索引前缀。
性能影响减少全表扫描,但可能增加分段扫描开销。
验证方法使用 EXPLAIN 查看 Using index skip scan。

跳跃扫描的限制与注意事项

  1. 适用条件
    等值条件:跳过列必须是 = 条件。
    索引列顺序:跳过列后,剩余列必须构成有效索引前缀。
    数据分布:跳过列的唯一值数量不宜过多(否则性能下降)。
  2. 性能权衡
    优势:避免全表扫描,减少 I/O 开销。
    代价:分段扫描可能导致额外的 CPU 和内存开销。
  3. 存储引擎支持
    InnoDB:支持跳跃扫描。
    MyISAM:不支持(需升级到 InnoDB)。

类似mysql主动拼接了字段条件,让其符合最左扫描,但是对于字段要求基数(count distinct不能太大),否则就近乎全表扫描了

Mysql中的like是否使用索引

通过like '%XX%'查询的时候会造成索引失效,一般采用like 'XX%'右边匹配的方式来索引。但是这样一定会使用索引吗?

一般建议使用全文索引:如果你的查询是基于大量文本的搜索,并且你使用的是 MyISAM 或 InnoDB 存储引擎(后者支持全文索引),可以考虑使用全文搜索(FULLTEXT)。全文搜索是为文本内容设计的,可以更有效地处理大量文本数据的搜索。

MySql索引概述和分类

Most MySQL indexes (PRIMARY KEY, UNIQUE, INDEX, and FULLTEXT) are stored in B-trees. Exceptions: Indexes on spatial data types use R-trees; MEMORY tables also support hash indexes; InnoDB uses inverted lists for FULLTEXT indexes.

  • 主键索引:又叫聚簇索引,是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值
  • 普通索引:也叫非主键索引,回表,借助主键索引
  • 唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一;唯一索引可以保证数据记录的唯一性。事实上,在许多场合,人们创建唯一索引的目的往往不是为了提高访问速度,而只是为了避免数据出现重复。
  • 全文索引:倒排索引

https://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html

Indexes are used to find rows with specific column values quickly. Without an index, MySQL must begin with the first row and then read through the entire table to find the relevant rows. The larger the table, the more this costs. If the table has an index for the columns in question, MySQL can quickly determine the position to seek to in the middle of the data file without having to look at all the data. This is much faster than reading every row sequentially.(索引适用于快速查找某些行,而不需要让引擎去做全表扫描.当然这些做是有条件的,索引的创建和更新是需要资源的(CPU IO 内存 磁盘空间).所以索引通过牺牲插入和更新的效率来大幅度提高读的效率.)

非主键索引(回表:bookmark lookup)

在这里插入图片描述

  1. InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。

  2. 若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树中再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(重点在于通过其它键需要建立辅助索引)

聚集索引的优势:

  1. 由于行数据和叶子节点存储在一起,同一页中会有多条行数据,访问同一数据页不同行记录时,已经把页加载到了Buffer中,再次访问的时候,会在内存中完成访问,不必访问磁盘。这样主键和行数据是一起被载入内存的,找到叶子节点就可以立刻将行数据返回了,如果按照主键Id来组织数据,获得数据更快

  2. 辅助索引使用主键作为"指针"而不是使用地址值作为指针的好处是,减少了当出现行移动或者数据页分裂时辅助索引的维护工作,使用主键值当作指针会让辅助索引占用更多的空间,换来的好处是InnoDB在移动行时无须更新辅助索引中的这个"指针"。也就是说行的位置(实现中通过16K的Page来定位)会随着数据库里数据的修改而发生变化(前面的B+树节点分裂以及Page的分裂),使用聚簇索引就可以保证不管这个主键B+树的节点如何变化,辅助索引树都不受影响

  3. 聚簇索引适合用在排序的场合,非聚簇索引不适合

  4. 取出一定范围数据的时候,使用用聚簇索引

  5. 二级索引需要两次索引查找,而不是一次才能取到数据,因为存储引擎第一次需要通过二级索引找到索引的叶子节点,从而找到数据的主键,然后在聚簇索引中用主键再次查找索引,再找到数据

  6. 可以把相关数据保存在一起。例如实现电子邮箱时,可以根据用户 ID 来聚集数据,这样只需要从磁盘读取少数的数据页就能获取某个用户的全部邮件。如果没有使用聚簇索引,则每封邮件都可能导致一次磁盘 I/O

覆盖索引(Covering Index)

覆盖索引(Covering Index) 是一种优化技术,通过在索引中包含查询所需的所有字段,使得数据库可以直接通过索引获取数据,而无需回表查询数据行(即 避免回表)。这显著减少了 I/O 操作,提升了查询性能。

create table user_test (
id bigint(20) not null auto_increment ,
name varchar(255) not null,
password varchar(255) ,
primary key (id),
index index_name (name) using btree)
engine=innodb default character set=utf8;

如下两个查询

语句A: select id from t_user where name = '张三'; -- 不需要回表
语句B: select password from t_user where name = '张三'; -- 需要回表

当sql语句的所求查询字段(select列)和查询条件字段(where子句)全都包含在一个索引中(联合索引),可以直接使用索引查询而不需要回表。可以建立(name,password)的联合索引,这样查询的时候就不需要再去回表操作了,可以提高查询效率。

那么不用主键索引就一定需要回表吗?不一定:如果查询的列本身就存在于索引中,那么即使使用二级索引,一样也是不需要回表的

覆盖索引的限制与注意事项

  1. 存储开销
    • 索引大小:覆盖索引占用更多存储空间(包含更多字段)。
    • 维护成本:插入/更新/删除操作时需维护索引,增加写操作开销。
  2. 索引设计的权衡
    • 避免过度设计:并非所有查询都需要覆盖索引,需 根据业务优先级选择关键查询优化。
    • 字段顺序:联合索引的字段顺序需满足最左前缀原则(如 (A, B, C) 可支持 A、A+B、A+B+C 查询)。
索引下推(index condition pushdown)

索引下推(index condition pushdown )简称ICP,在Mysql5.6的版本上推出,它能减少回表查询次数,提高查询效率。

索引下推(Index Condition Pushdown, ICP) 是一种查询优化技术,允许将部分查询条件下推到 存储引擎层,在索引扫描过程中提前过滤不满足条件的记录,从而减少回表操作和 I/O 开销。

  • 在不使用ICP的情况下,在使用非主键索引(又叫普通索引或者二级索引)进行查询时,存储引擎通过索引检索到数据,然后返回给MySQL服务器,服务器然后判断数据是否符合条件 。

  • 在使用ICP的情况下,如果存在某些被索引的列的判断条件时,MySQL服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器 。

适用条件

  1. 非索引字段的条件必须是等值或范围条件。
  2. 联合索引的最左前缀必须被使用(如 idx_name_age 需要 name 条件)。

索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储引擎接收数据的次数。

例子:

使用一张用户表tuser,表里创建联合索引(name, age)

select * from tuser where name like '张%' and age=10;按照最左匹配原则,这个语句在搜索索引树的时候,只能用匹配,假设有2条记录符合,但是age=10的只有一条记录;

没有使用ICP,假设主键id(1、4),逐一进行回表扫描,去聚簇索引找到完整的行记录,server层再对数据根据age=10进行筛选。

使用ICP,存储引擎根据(name,age)联合索引,找到由于联合索引中包含列,直接在联合索引里按照age=10过滤;按照过滤后的数据再一一进行回表扫描。这样只回表了一次。

查看和设置索引下推

select @@optimizer_switch
set ="index_condition_pushdown=off";
set ="index_condition_pushdown=on";

其它知识点

排序原理
  • 内存排序
    当数据量较小且字段总长度小于max_length_for_sort_data(默认1024字节)时,MySQL采用单路排序模式。它会一次性提取所有需要的列进行排序,避免二次回表查询

  • 磁盘排序
    当数据量较大或字段总长度超过max_length_for_sort_data时,MySQL会使用双路排序模式。该模式先提取排序字段和行ID,完成内存排序后,再通过行ID回表查询其他字段。 ‌

  • 索引优化
    若排序字段有索引,MySQL可直接利用索引有序性完成排序,无需额外操作。例如ORDER BY id ASC时,若索引覆盖id列,则直接使用索引排序。

参数影响

  • sort_buffer_size‌:配置内存排序缓冲区大小,默认值可能不足导致频繁使用磁盘排序。 ‌
  • max_length_for_sort_data‌:超过该值的字段组合会触发双路排序。 ‌
  • innodb_disable_sort_file_cache‌:禁用InnoDB的临时文件缓存,可能影响排序性能。 ‌

  • 两路外部排序

假设内存只有100M,但是排序的数据有900M,那么对应的外部排序算法如下:

  1. 从要排序的900M数据中读取100MB数据到内存中,并按照传统的内部排序算法(快速排序)进行排序;
  2. 将排序好的数据写入磁盘;
  3. 重复1,2两步,直到每个100MB chunk大小排序好的数据都被写入磁盘;
  4. 每次读取排序好的chunk中前10MB(= 100MB / (9 chunks + 1))数据,一共9个chunk需要90MB,剩下的10MB作为输出缓存;
  5. 对这些数据进行一个“9路归并”,并将结果写入输出缓存。如果输出缓存满了,则直接写入最终排序结果文件并清空输出缓存;如果9个10MB的输入缓存空了,从对应的文件再读10MB的数据,直到读完整个文件。最终输出的排序结果文件就是900MB排好序的数据了。
  • 多路外部排序
    上述排序算法是一个两路排序算法(先排序,后归并)。但是这种算法有一个问题,假设要排序的数据是50GB而内存只有100MB,那么每次从500个排序好的分片中取200KB(100MB / 501 约等于200KB)就是很多个随机IO。效率非常慢,对应可以这样来改进:
  1. 从要排序的50GB数据中读取100MB数据到内存中,并按照传统的内部排序算法(快速排序)进行排序;
  2. 将排序好的数据写入磁盘;
  3. 重复1,2两步,直到每个100MB chunk大小排序好的数据都被写入磁盘;
  4. 每次取25个分片进行归并排序,这样就形成了20个(500/25=20)更大的2.5GB有序的文件;
  5. 对这20个2.5GB的有序文件进行归并排序,形成最终排序结果文件。
CHAR、VARCHAR 和 TEXT
类型存储方式最大长度是否可变是否自动填充空格适用场景
CHAR固定长度255 字符固定长度数据(如身份证号、国家代码)
VARCHAR可变长度65535 字符(受行大小限制)可变长度数据(如用户名、地址)
TEXT行外存储65535 字符(TEXT 类型),最大 4GB(LONGBLOB/LONGTEXT)大文本数据(如文章内容、日志)
  • CHAR:
    • 固定长度:无论实际数据长度如何,存储时都会用空格填充到定义的长度。
    • 示例:CHAR(10) 存储 “abc”,实际占用 10 字节(包括 7 个空格)。
  • VARCHAR:
    • 可变长度:仅存储实际数据,外加 1~2 字节的长度信息(记录数据长度)。
    • 示例:VARCHAR(10) 存储 “abc”,实际占用 3 字节 + 1 字节长度信息。
  • TEXT:
    • 行外存储:数据存储在行外的专用区域,表中仅存储指针(20 字节左右)。
    • 示例:TEXT 存储 1MB 数据,表中仅记录指向该数据的指针。
类型优点缺点适用场景
CHAR查询快,固定长度浪费空间固定长度数据
VARCHAR节省空间,适应变化略慢于 CHAR可变长度数据
TEXT支持超长文本性能低,行外存储大文本数据
什么是视图,它有什么优点?

视图是基于SQL语句的结果集的可视化表现。它像一个虚拟表,包含了从一个或多个表中获取的数据。视图的优点包括简化复杂SQL查询、保护数据(通过限制对特定数据的访问)、更改数据格式和表示等。

MySQL中的存储过程是什么?

存储过程是一组为了完成特定功能的SQL语句,它存储在数据库中,可以通过指定的名称和参数进行调用。存储过程可以提高SQL代码的重用性,减少网络通信量,提高性能。

范式
  1. 第一范式(1NF)
    要求:表中的每一列都是不可分割的原子数据项。
    解决的问题:消除重复组(嵌套结构)。

  2. 第二范式(2NF)
    前提:满足 1NF。
    要求:所有非主属性完全依赖于 整个候选键(消除部分依赖)。
    解决的问题:部分依赖导致的数据冗余。

  3. 第三范式(3NF)
    前提:满足 2NF。
    要求:所有非主属性 不传递依赖 于候选键(消除传递依赖)。
    解决的问题:传递依赖导致的数据冗余。

  4. 巴斯-科德范式(BCNF)
    前提:满足 3NF。
    要求:对于所有函数依赖 X → Y,X 必须是超键(即消除所有非平凡且非函数依赖)。
    解决的问题:多值依赖导致的冗余(比 3NF 更严格)。

  5. 第四范式(4NF)
    前提:满足 BCNF。
    要求:消除 多值依赖(Multi-Valued Dependency)。
    解决的问题:多值字段导致的冗余。

  6. 第五范式(5NF)
    前提:满足 4NF。
    要求:消除 连接依赖(Join Dependency),确保表无法进一步分解而不丢失信息。
    解决的问题:复杂多表连接导致的冗余。
    应用场景:极少使用,通常用于理论研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值