结构

参考 MySQL是怎么运行的 chapter6

  1. mysql> CREATE TABLE index_demo(
  2. -> c1 INT,
  3. -> c2 INT,
  4. -> c3 CHAR(1),
  5. -> PRIMARY KEY(c1)
  6. -> ) ROW_FORMAT = Compact;
  7. Query OK, 0 rows affected (0.03 sec)
  8. mysql> INSERT INTO index_demo VALUES(1, 4, 'u'), (3, 9, 'd'), (5, 3, 'y');
  9. Query OK, 3 rows affected (0.01 sec)
  10. Records: 3 Duplicates: 0 Warnings: 0
  11. mysql> INSERT INTO index_demo VALUES(4, 4, 'a');
  12. Query OK, 1 row affected (0.00 sec)

image.png

类型

  • 唯一索引。对于单列索引,不允许有重复值出现;对于多列(复合)索引,不允许出现重复的组合值。
  • 常规(非唯一性)索引。获得索引的好处,但会出现重复值。
  • fulltext索引。用于全文检索,本仅限于MyISAM表,但MySQL5.6.4之后,InnoDB也支持。
  • spatial索引。只适用于包含空间值得MyISAM表
  • hash索引。memory表的默认索引类型 ```sql

    索引的创建

alter table tbl_name add unique index_name (index_columns); alter table tbl_name add index index_name (index_columns); alter table tbl_name add primary key index_name (index_columns); alter table tbl_name add fulltext index_name (index_columns); alter table tbl_name add spatial index_name (index_columns);

除了primary key,大部分索引都可以用create index来创建

create index index_name on tbl_name (index_columns); create unqiue index_name on tbl_name (index_columns); create fulltext index_name on tbl_name (index_columns); create spatial index_name on tbl_name (index_columns);

create table tbl_name{ …列定义… index index_name (index_columns), unqiue index_name (index_columns), primary key index_name (index_columns), fulltext index_name (index_columns), spatial index_name (index_columns),

}

索引的删除

drop index index_name on tal_name alter table tbl_name drop index index_name

<a name="SWIc7"></a>
### 
<a name="zWEMH"></a>
### 无索引查询的情况
```sql
create table det (a int(2));

insert into det values(1);

explain select * from det where a = 1;

#查询类型为type
1,SIMPLE,det,,ALL,,,,,1,100,Using where

构建高性能索引的策略

关于日期类型索引的处理

see 《MySQL技术内幕》 chapter5

  • MySQL 5.7.21 有问题
    ``sql CREATE TABLEmember(member_idint(10) unsigned NOT NULL AUTO_INCREMENT,last_namevarchar(20) NOT NULL,first_namevarchar(20) NOT NULL,suffixvarchar(5) DEFAULT NULL,expirationdate DEFAULT NULL,emailvarchar(100) DEFAULT NULL,streetvarchar(50) DEFAULT NULL,cityvarchar(50) DEFAULT NULL,statevarchar(2) DEFAULT NULL,zipvarchar(10) DEFAULT NULL,phonevarchar(20) DEFAULT NULL,interestsvarchar(255) DEFAULT NULL, PRIMARY KEY (member_id`) ) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8

select count(*) from member where TIMESTAMPDIFF(day,curdate(),expiration) between 0 and 30;

select count(*) from member where to_days(expiration) - to_days(curdate()) between 0 and 30;

需求:查询任期距离当前日期不足一月的数据

to_days函数用法示例

select to_days(curdate()) - to_days(‘2021-05-01’)

随机修改日期为今年度

update member set expiration = concat(‘2021’,’-‘,floor(1+rand()12),’-‘,floor(1+rand()30));

step1,添加数据库

alter table member add index (expiration);

以下查询类型全为all

explain select * from sampdb.member where to_days(expiration) -to_days(curdate()) < 30;

explain select * from sampdb.member where to_days(expiration) < 30 + to_days(curdate());

查看该表中有那些索引

show index from member;

explain select * from member where expiration < date_add(curdate(),interval 30 day);

![image.png](https://cdn.nlark.com/yuque/0/2021/png/96947/1623052449695-718f9c59-6fac-4be4-a38c-33167859cb6f.png#clientId=u0d397a24-a674-4&from=paste&height=333&id=u062eec9e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=666&originWidth=1769&originalType=binary&ratio=2&size=106208&status=done&style=none&taskId=udea50092-9c9c-4044-89c4-ec6ee477838&width=884.5)

<a name="NUXuI"></a>
##### 日期范围的查询
```sql
mysql> explain select * from member where expiration  between '2021-06-01' and '2021-06-30';
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | member | NULL       | range | expiration    | expiration | 4       | NULL |   12 |   100.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from member where   expiration >='2021-06-01'  and expiration <= '2021-06-30';
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | member | NULL       | range | expiration    | expiration | 4       | NULL |   12 |   100.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

索引列不能是表达式的一部分或函数的一部分

create table t3(id int primary key auto_increment,name varchar(10)) engine = InnoDb;

insert into t3(name) values ('a'),('b'),('c');

explain select * from t3 where id+1 =2;

1,SIMPLE,t3,,ALL,,,,,3,100,Using where

前缀索引的选择处理

CREATE TABLE sakila.city_demo(city VARCHAR(50) NOT NULL);
INSERT INTO sakila.city_demo(city) SELECT city FROM sakila.city;
-- Repeat the next statement five times:
INSERT INTO sakila.city_demo(city) SELECT city FROM sakila.city demo;
-- Now randomize the distribution (inefficiently but convenientiy):
UPDATE sakila.city_demo
 SET city = (SELECT city FROM sakila.city ORDER BY RAND() LIMIT 1);



#完整性列表
select count(*) as cnt,city from sakila.city_demo group by city order by cnt desc limit 10;

select count(*) as cnt,left(city,3) as pref from sakila.city_demo group by pref order by cnt desc limit 10;
#接近完整列
select count(*) as cnt,left(city,7) as pref from sakila.city_demo group by pref order by cnt desc limit 10;

#计算完整列的选择性
select count(distinct city) /count(*) from sakila.city_demo;

#使前缀的选择性接近于完整列的选择性
select count(*) as cnt,left(city,7) as pref from sakila.city_demo group by pref order by cnt desc limit 10;

select count(distinct city) /count(*) from sakila.city_demo;

select count(distinct LEFT(city,3))/count(*) as sel3,
       count(distinct LEFT(city,4))/count(*) as sel4,
       count(distinct LEFT(city,5))/count(*) as sel5,
       count(distinct LEFT(city,6))/count(*) as sel6,
       count(distinct LEFT(city,7))/count(*) as sel7 from sakila.city_demo;

#增加索引
alter table sakila.city_demo add key (city(7));

联合索引

create table people (
    last_name varchar(50) not null ,
    first_name varchar(50) not null ,
    dob  date not null ,
    gender enum('m','f') not null ,
    key(last_name,first_name,dob)
);

#可以使用索引的情况

#全职匹配
explain select * from people where last_name='li' and first_name = 'si' and dob = '2010-05-20';

#匹配最左前缀
explain select * from people where last_name = 'zhao';

#匹配列前缀
explain select * from people where last_name  like 'l%';

#匹配列后缀 无法使用索引
explain select * from people where last_name  like '%l';


#匹配范围值
explain select * from people where last_name > 'li' and last_name < 'wang';

#精确匹配某一列并范围匹配另外一列
explain select * from people where last_name = 'li' and last_name > 'er';

#只访问索引的查询
explain select last_name, first_name, dob from people;


####无法使用索引的情况

#不是按照索引的最左列开始查找,出生日期同理
explain select * from people where first_name = 'si';

#索引列在函数中 无法使用
explain select * from people where left(last_name,1)  = 'l';