为了账号安全,请及时绑定邮箱和手机立即绑定

Mysql 索引的简单实践

标签:
MySQL
最近在工作中遇见一些问题,在抽取Mysql数据到另一个库的时候会遇到类似这种错误:
 Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
 可能会误认为是数据库连接池没有配置检活,但是这里是因为数据量太大,而且没有使用到索引,走的全表扫描长时间返回不了查询结果而引起的。
 sql类似:select * from xxx where a is null or b = '' and c ='2018-12-18'
 下面简单学习一下mysql的索引。

新建测试表

随便拿了自己平时开发小demo的一个表
CREATE TABLE `faya_job_log` (
  `id` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '日志id',
  `job_id` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务id',
  `job_desc` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务描述',
  `remote_ip` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务执行的机器地址',
  `load_balance` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '负载策略',
  `ha` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '失败策略',
  `status` int(11) DEFAULT NULL COMMENT '任务执行状态 成功 失败',
  `retry` int(11) DEFAULT NULL COMMENT '重试次数',
  `message` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '任务执行信息',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务日志';

导入几条数据
INSERT INTO `faya_job_manager`.`faya_job_log` (`id`, `job_id`, `job_desc`, `remote_ip`, `load_balance`, `ha`, `status`, `retry`, `message`, `create_time`, `update_time`) VALUES ('1', '1', '哈哈哈哈', '10.10.10.1', '1', '1', '0', '1', '哈小米你好', '2018-12-19 10:47:38', '2018-12-19 12:39:04');
INSERT INTO `faya_job_manager`.`faya_job_log` (`id`, `job_id`, `job_desc`, `remote_ip`, `load_balance`, `ha`, `status`, `retry`, `message`, `create_time`, `update_time`) VALUES ('2', '2', '哈哈哈哈', '10.10.10.1', '1', '1', '1', '1', '哈哈哈哈你好', '2018-12-19 10:48:03', '2018-12-19 10:48:03');
INSERT INTO `faya_job_manager`.`faya_job_log` (`id`, `job_id`, `job_desc`, `remote_ip`, `load_balance`, `ha`, `status`, `retry`, `message`, `create_time`, `update_time`) VALUES ('3', '3', '哈哈哈', '10.10.10.1', '1', '1', '1', '1', '哈哈哈哈你好', '2018-12-19 10:48:24', '2018-12-19 10:48:24');
INSERT INTO `faya_job_manager`.`faya_job_log` (`id`, `job_id`, `job_desc`, `remote_ip`, `load_balance`, `ha`, `status`, `retry`, `message`, `create_time`, `update_time`) VALUES ('4', '4', '哈哈哈哈你好', '10.10.10.1', '1', '1', '1', '11', '哈哈哈哈你好', '2018-12-19 10:49:07', '2018-12-19 10:49:21');
INSERT INTO `faya_job_manager`.`faya_job_log` (`id`, `job_id`, `job_desc`, `remote_ip`, `load_balance`, `ha`, `status`, `retry`, `message`, `create_time`, `update_time`) VALUES ('5', '5', '哈哈哈哈你好', '10.10.10.1', '1', '1', NULL, '1', '哈哈哈哈你好', '2018-12-19 10:49:20', '2018-12-19 12:32:28');

索引常用的操作

- 索引常用的操作
新增一个普通索引
alter table faya_job_log add index index_job_id (job_id) ;

删除索引
drop index index_job_id on faya_job_log ;

查询表存在的索引
SHOW INDEX FROM faya_job_log;

EXPLAIN命令

先了解 这个命令具体的返回
EXPLAIN select * from faya_job_log;
id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra 
......

一般我会关注几个字段
(1)rows:扫描行的数量,就是这个sql执行会扫描的数据行数,行数越大意味着查询会越慢。

(2)type:代表MySQL在表中查找数据的方式 ,常见的如下,性能由差到最好:

type=ALL:     全表扫描,MySQL遍历全表来找到匹配行
type=index:   索引全扫描,MySQL遍历整个索引来查询匹配行,并不会扫描表
type=range:   索引范围扫描
type=ref:	  非唯一索引扫描
type=eq_ref:  唯一索引扫描
type=const,system:	单表最多有一个匹配行,出现在根据主键primary key或者 唯一索引 unique index 进行的查询

(3)possible_keys: 表示查询可能使用的索引

(4)key: 实际使用的索引

SQL DEMO

explain select * from faya_job_log where id="1"
;
+----+-------------+--------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table        | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | faya_job_log | NULL       | const | PRIMARY       | PRIMARY | 130     | const |    1 |      100 | NULL  |
+----+-------------+--------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

因为id是表的主键,所以可以看到rows是1,type是const。

接下来我们根据job_id查询
explain select * from faya_job_log where job_id="1";
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | faya_job_log | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |      100 | Using where |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+

可以看到为没有使用索引,所以此时rows是5,type是ALL

新增一个普通索引
alter table faya_job_log add index index_job_id (job_id) ;

 explain select * from faya_job_log where job_id="1";
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table        | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | faya_job_log | NULL       | ref  | index_job_id  | index_job_id | 130     | const |    1 |      100 | NULL  |
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
可以看到当我们新增索引后type变为了ref,rows变为了1。

接下来我们根据job_id和status查询
explain select * from faya_job_log where job_id="1" or status ="0";
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | faya_job_log | NULL       | ALL  | index_job_id  | NULL | NULL    | NULL |    5 |      100 | Using where |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+

发现type=type,rows=5.原因是在 where 子句中使用 or 如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,
这里我们的status没有索引.
可以修改sql语句:
explain select * from faya_job_log where job_id="1" union all  select * from faya_job_log where status ="0";
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | faya_job_log | NULL       | ref  | index_job_id  | index_job_id | 130     | const |    1 |      100 | NULL        |
|  2 | UNION       | faya_job_log | NULL       | ALL  | NULL          | NULL         | NULL    | NULL  |    5 |      100 | Using where |
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+

可以看到id还是会使用主键索引

我们给status加上索引
alter table faya_job_log add index index_status (status) ;

explain select * from faya_job_log where job_id="1" or status ="0";
+----+-------------+--------------+------------+------+---------------------------+------+---------+------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys             | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | faya_job_log | NULL       | ALL  | index_job_id,index_status | NULL | NULL    | NULL |    5 |      100 | Using where |
+----+-------------+--------------+------------+------+---------------------------+------+---------+------+------+----------+-------------+
会发现同样使用不了索引

修改sql执行
explain select * from faya_job_log where job_id="1" union all  select * from faya_job_log where status ="0";
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table        | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
|  1 | PRIMARY     | faya_job_log | NULL       | ref  | index_job_id  | index_job_id | 130     | const |    1 |      100 | NULL  |
|  2 | UNION       | faya_job_log | NULL       | ref  | index_status  | index_status | 5       | const |    1 |      100 | NULL  |
+----+-------------+--------------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
此时会发现使用了我们的索引


我们把之前的两个索引删除
drop index index_job_id on faya_job_log ;
drop index index_status on faya_job_log ;
新建复合索引
ALTER TABLE faya_job_log ADD INDEX index_job_id_status (job_id,status);

执行下面的查询
explain select job_id from faya_job_log where job_id="1" and status =0;
+----+-------------+--------------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys       | key                 | key_len | ref         | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | faya_job_log | NULL       | ref  | index_job_id_status | index_job_id_status | 135     | const,const |    1 |      100 | Using index |
+----+-------------+--------------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-------------+

会发现使用了我们建立的索引,rows=1

但是当我们执行
explain select * from faya_job_log where status =0;
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | faya_job_log | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |      100 | Using where |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
发现没有使用索引
此时注意是因为最左原则:复合索引的情况下,查询条件不包含索引列最左边部分,不会命中复合索引

比如
explain select job_id,status from faya_job_log where job_id="1"
;
+----+-------------+--------------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | faya_job_log | NULL       | ref  | index_job_id_status | index_job_id_status | 130     | const |    1 |      100 | Using index |
+----+-------------+--------------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+

可以看到此时就使用了索引。

总结

关于索引的使用太多了,这里只是简单举例子,学习怎么使用explain命令。
大家可以多使用explain去查看自己的sql执行是否使用了索引。而且select 最好不要和我一样使用*.
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
6395
获赞与收藏
157

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消