索引的类型:

B-Tree索引:

使用B-Tree这一数据结构做为支撑的索引。

B树,多路平衡查找树,B+树,叶子节点之间有指针的多路平衡查找树。
每个叶子节点存储数据,非叶子节点存储下一层节点的指针,实际上在经过非叶子节点的时候,就发现非叶子节点里面存储的其实是下级节点的范围,所以是很方便做范围查询的。

能加快查询的速度,能做范围查询。

  • 全值匹配
  • 匹配最左前缀
  • 匹配列前缀
  • 匹配范围值
  • 精确匹配某一列并范围匹配另外一列

实战:

mysql> show create table person;
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                                                                                |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| person | CREATE TABLE `person` (
  `A` int(50) NOT NULL,
  `B` int(50) NOT NULL,
  `C` int(50) NOT NULL,
  `version` varchar(20) NOT NULL,
  PRIMARY KEY (`version`),
  KEY `A` (`A`,`B`,`C`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
  1. 全值匹配(多列一起使用)
mysql> explain select  * from  person where a = 12 and b = 129 and c= 12995;
+----+-------------+--------+------------+------+---------------+------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | person | NULL       | ref  | A             | A    | 12      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+---------------+------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
  1. 匹配做左前缀使用最左列
mysql> explain select  * from  person where a = 12;
+----+-------------+--------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | person | NULL       | ref  | A             | A    | 4       | const | 1100 |   100.00 | Using index |
+----+-------------+--------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
  1. 匹配列前缀
mysql> explain select  * from  person where a like  '12%';
+----+-------------+--------+------------+-------+---------------+------+---------+------+-------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows  | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------+---------+------+-------+----------+--------------------------+
|  1 | SIMPLE      | person | NULL       | index | A             | A    | 12      | NULL | 98829 |    11.11 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+-------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
  1. 匹配范围值
mysql> explain select  * from  person where a > 12 and a < 14;
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | person | NULL       | range | A             | A    | 4       | NULL | 1100 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
  1. 精确匹配某一列并范围匹配某一行
mysql> explain select  * from  person where a =12 and b >14;
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | person | NULL       | range | A             | A    | 8       | NULL | 1100 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
  1. 覆盖索引
mysql> explain select  a,b,c from  person where a =12 and b >14;
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | person | NULL       | range | A             | A    | 8       | NULL | 1100 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

多列索引请看多列索引相关的实战

hash索引

特点:只能精确匹配值,不能做排序
hash索引工作的原理,将表里面做hash索引的列,每个数据都计算hash存储到一个map中,在使用这个列做等值查询的时候,先计算等式右边的hash值,然后通过hash值去map里面找到对应的数据,如果出现hash冲突,那么再去对比真正的数据。

mysql> show create table testhash;
+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                        |
+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| testhash | CREATE TABLE `testhash` (
  `fname` varchar(50) NOT NULL,
  `lname` varchar(50) NOT NULL,
  KEY `fname` (`fname`) USING HASH
) ENGINE=MEMORY DEFAULT CHARSET=latin1 |
+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into testhash values ('Arjen', 'lentz');
Query OK, 1 row affected (0.00 sec)

mysql> insert into testhash values ('Baron', 'schwartz') ,('Peter','zaitsv'),('Vadim', 'Tkachenko');

hash索引实战

mysql> explain select * from testhash where fname = 'Peter';
+----+-------------+----------+------------+------+---------------+-------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key   | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+-------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | testhash | NULL       | ref  | fname         | fname | 52      | const |    2 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+-------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from testhash order by 'Peter';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | testhash | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

发现hash索引做等值查询的时候可以使用索引,但是使用他作为排序字段的时候就不能使用

mysql> explain select * from testhash where fname like  'Peter%';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | testhash | NULL       | ALL  | fname         | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select fname from testhash where fname like  'Peter%';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | testhash | NULL       | ALL  | fname         | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

发现hash索引无法做范围查询,因为计算hash的时候都是全字计算的

hash冲突

hash冲突越厉害,删除效率就越低。

自适应hash

在B-Tree索引上面如果一个数据被频繁查询,那么这个数据将生成一个类似hash索引的结构,这个是一个内部实现逻辑。

mysql> show variables like '%hash%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| innodb_adaptive_hash_index       | ON    |
| innodb_adaptive_hash_index_parts | 8     |
| metadata_locks_hash_instances    | 8     |
+----------------------------------+-------+
3 rows in set (0.01 sec)

手动的hash索引

对于需要频繁对比的字符串,并且区分度比较大的情况下可以手动维护一个hash列来做hash索引。

建表和触发器

mysql> show create pseudohash;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'pseudohash' at line 1
mysql> show create table pseudohash;
+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table      | Create Table                                                                                                                                                                                                               |
+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| pseudohash | CREATE TABLE `pseudohash` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `url` varchar(255) NOT NULL,
  `url_src` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show triggers;
+--------------------+--------+------------+---------------------------------------------+--------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+---------+----------------------+----------------------+--------------------+
| Trigger            | Event  | Table      | Statement                                   | Timing | Created                | sql_mode                                                                                                                                  | Definer | character_set_client | collation_connection | Database Collation |
+--------------------+--------+------------+---------------------------------------------+--------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+---------+----------------------+----------------------+--------------------+
| dohash_ins         | INSERT | dohash     | begin set new.url_crc=crc32(new.url);
end   | BEFORE | 2021-05-06 13:26:07.53 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | dev@%   | utf8mb4              | utf8mb4_general_ci   | latin1_swedish_ci  |
| dohash_upd         | UPDATE | dohash     | begin set new.url_crc=crc32(new.url);
end   | BEFORE | 2021-05-06 13:26:07.55 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | dev@%   | utf8mb4              | utf8mb4_general_ci   | latin1_swedish_ci  |
| pseudohash_crc_ins | INSERT | pseudohash | begin set new.url_src = crc32(new.url); end | BEFORE | 2021-05-09 00:16:24.57 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | root@%  | utf8                 | utf8_general_ci      | latin1_swedish_ci  |
| pseudohash_crc_upd | UPDATE | pseudohash | begin set new.url_src = crc32(new.url);
end | BEFORE | 2021-05-09 00:17:21.71 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | root@%  | utf8                 | utf8_general_ci      | latin1_swedish_ci  |
+--------------------+--------+------------+---------------------------------------------+--------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+---------+----------------------+----------------------+--------------------+
4 rows in set (0.00 sec)

插入,修改

mysql> insert into pseudohash(url) values ('http://www.baidu.com')
    -> ;
Query OK, 1 row affected (0.00 sec)

mysql> select * from pseudohash;
+----+----------------------+------------+
| id | url                  | url_src    |
+----+----------------------+------------+
|  1 | http://www.baidu.com | 3500265894 |
+----+----------------------+------------+
1 row in set (0.01 sec)

mysql> update pseudohash set url = 'http://localhost:8080' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from pseudohash;
+----+-----------------------+------------+
| id | url                   | url_src    |
+----+-----------------------+------------+
|  1 | http://localhost:8080 | 1929295430 |
+----+-----------------------+------------+
1 row in set (0.00 sec)
注意
  • hash索引应该是短的字符串,如果说太长了耗费磁盘空间存储数据
  • 查询的时候应该带上原来的值,而不是直接使用hash之后的值去查询,如果只仅仅使用hash之后的值去做查询,那么冲突之后,将会出现多个值。
mysql> insert into pseudohash(url) values ('codding'), ('gnu');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from pseudohash;
+----+-----------------------+------------+
| id | url                   | url_src    |
+----+-----------------------+------------+
|  1 | http://localhost:8080 | 1929295430 |
|  2 | codding               | 1774765869 |
|  3 | gnu                   | 1774765869 |
+----+-----------------------+------------+
3 rows in set (0.00 sec)

mysql> select * from pseudohash where url_src = crc32('gnu')
    -> ;
+----+---------+------------+
| id | url     | url_src    |
+----+---------+------------+
|  2 | codding | 1774765869 |
|  3 | gnu     | 1774765869 |
+----+---------+------------+
2 rows in set (0.00 sec)

mysql> select * from pseudohash where url_src = crc32('gnu') and url = 'gnu';
+----+-----+------------+
| id | url | url_src    |
+----+-----+------------+
|  3 | gnu | 1774765869 |
+----+-----+------------+
1 row in set (0.00 sec)

以上 codding 和 gnu 的crc32计算出来的值是一样的,如果只使用url_src去过滤,就会出现两个值,这个就是hash冲突,解决方法就是在对比的时候加上url。

空间索引

MyISAM支持空间索引,用来存储地理数据,从所有的维度存储索引,可以使用各种维度去查询地理数据。

全文索引

特殊类型的索引,查找文本的关键字,适用于match against.