索引的类型:
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)
- 全值匹配(多列一起使用)
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)
- 匹配做左前缀使用最左列
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)
- 匹配列前缀
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)
- 匹配范围值
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)
- 精确匹配某一列并范围匹配某一行
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)
- 覆盖索引
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.