对于mysql的优化的各种方式中,索引方式占很大比例。当数据很大时,才会感受到索引的魅力。为了方便学习和工作中使用,收集实验整理下的。
索引目的:
我们知道,查询数据时,先把数据从磁盘块中读取到内存中,再做处理的。如果没有索引时,就会容易全表查找。而IO操作又是最耗时的。
比如:1~100放入磁盘快1中,101~200放入磁盘块2中,201~300放在磁盘块3中。如果在没有索引的状态下找299时,就会从头开始,把块1放入内存中;查找没有,再把块2的放入内存;查找没有,放入块3的,这时候才找到数据。其中的IO开销,会随着数据的越大而越大。
如果有索引时,就直接从块3中读取放入内存。节省了很多耗时。
索引的查看:
查看命令为:
SHOW KEYS FROM table_name;
或 SHOW INDEX FROM table_name;
比如表结构如下:
CREATE TABLE `jz_remodeling_product_config` (
`id` int(11) NOT NULL,
`product_id` int(11) DEFAULT '0' COMMENT '商品ID',
`type_id` int(11) DEFAULT '0' COMMENT '类型ID(配置表类型,如改造空间等)',
`obj_id` int(11) DEFAULT '0' COMMENT '数据字典表ID',
`create_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`is_del` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_product_type_obj` (`product_id`,`type_id`,`obj_id`),
KEY `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品配置表';
运行结果:
- Table: 表名
- Non_unique:索引可以包含重复词。0:唯一性。1:可以重复。
- Key_name:索引名称。
- Seq_in_index:索引中的列序列号,从1开始。
- Column_name:列名称。
- Collation:列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。
- Cardinality:索引中唯一值的数目的估计值。该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机 会就越大。
- Sub_part:如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。
- Packed:指示关键字如何被压缩。如果没有被压缩,则为NULL。
- Null:如果列含有NULL,则含有YES。如果没有,则该列含有NO。
- Index_type:所用的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
索引的创建与删除:
创建索引 :
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list]): 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。
主键只能作用于一个列上,添加主键索引时,你需要确保该主键默认不为空(NOT NULL)。
FULLTEXT全文索引,InnoDB不支持,Myisam支持性能比较好,一般在 CHAR、VARCHAR 或 TEXT 列上创建。
删除索引:
1、 DROP INDEX 索引名 ON 表名;
2、 ALTER TABLE 表名 DROP INDEX 索引名;
3、 ALTER TABLE 表名 DROP PRIMARY KEY;
索引添加约定原则:
命名方式(建议方式):
- 键约束pk_字段名
- 唯一约束uniq_字段名
- 普通索引idx_字段名
- 索引名称的最大长度为30个字符
- 索引中的字段数建议不超过5个,唯一键由3个以下字段组成,唯一键不和主键重复
- 单表索引数量最多不能超过5个
约定注意点:
以下表为参考:
CREATE TABLE `jz_remodeling_product_config` (
`id` int(11) NOT NULL,
`product_id` int(11) DEFAULT '0' COMMENT '商品ID',
`type_id` int(11) DEFAULT '0' COMMENT '类型ID(配置表类型,如改造空间等)',
`obj_id` varchar(11) DEFAULT '0' COMMENT '数据字典表ID',
`create_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`is_del` int(11) DEFAULT '0',
`user_name` varchar(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_product_type_obj` (`product_id`,`type_id`,`obj_id`),
KEY `idx_user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品配置表';
- 索引应加到经常使用到的属性。
- 表表之间关联的属性可以加索引。
- 对重复数较少的属性加索引,即区分度高的列作为索引。区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1。因此主键索引和unique索引的效率最高。一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。
- MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。 LIKE为 ‘XX%’方式时才能使用索引。
- 索引不会包含NULL列,如果列中包含NULL值都将不会被包含在索引中,复合索引中如果有一列含有NULL值那么这个组合索引都将失效,一般需要给默认值0或者 ’ ‘字符串。更准确的说,单列索引不存储null值,复合索引不存储全为null的值。索引不能存储Null,所以对这列采用is null条件时,因为索引上根本没Null值,不能利用到索引,只能全表扫描。
- 最左前缀匹配原则,非常重要的原则。最左前缀匹配原则指的是 where 字句中一些条件或表达式中出现的列的顺序要保持和多索引的一致或以多列索引顺序出现,只要出现非顺序出现无法利用到多列索引。如下:
(1)组合索引为(product_id
,type_id
,obj_id
)。根据原则,where中要有product_id的条件才会用到索引。
(注意:=和in可以乱序。mysql的查询优化器会帮你优化成索引可以识别的形式)
以下可以用到索引:
WHERE product_id = 3 AND type_id = 110 AND obj_id = 1;
WHERE product_id = 3 AND type_id = 110;
WHERE product_id = 3 AND obj_id = 1;
WHERE product_id = 3;
WHERE type_id = 110 AND product_id = 3;
WHERE product_id IN (3, 4);
WHERE type_id IN (110, 120) AND product_id IN (3, 4);
而这些就用不到索引:
WHERE type_id IN (110, 120);
WHERE type_id = 110 -- AND obj_id = 1;
如果where中有个全局的or,哪怕or后的有索引。既然还是使用不到索引。
WHERE product_id = 3 AND obj_id = 1 OR user_name = 10;
如果是局部的or。还是有可能的:
WHERE product_id = 3 AND (obj_id = 1 OR user_name = 10)
*还有个特别的现象,跟数据量有关系。
当第一个索引属性使用了>,>= 或 <,<=时。如果右边的值接近最小值,则 <,<= 能用上索引,>,>= 就不能;如果接近最大值时,则>,>= 能用上索引,而<,<= 就不能用上索引。至于原因,还待找寻的,有清楚的可以留言交流下。*
(2)对于单列索引,依然有这个原则。对于(user_name
):
WHERE user_name LIKE '2%'; 就可以使用到索引;
WHERE user_name LIKE '%2%'; 这种就不行了。
- 索引属性不要参与计算和函数。原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。
WHERE product_id + 1 = 3; 就使用不上索引。不过我们可以转换方式为:
WHERE product_id = 3 - 1; 这种方式就可以用上。
- 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。
- MySQL主要提供2种方式的索引:B-Tree索引,Hash索引。
B树索引具有范围查找和前缀查找的能力,对于有N节点的B树,检索一条记录的复杂度为O(LogN)。相当于二分查找。
哈希索引只能做等于查找,但是无论多大的Hash表,查找复杂度都是O(1)。
显然,如果值的差异性大,并且以等值查找(=、 <、>、in)为主,Hash索引是更高效的选择,它有O(1)的查找复杂度。
如果值的差异性相对较差,并且以范围查找为主,B树是更好的选择,它支持范围查找。 - 单独order by 用不了索引,索引考虑加where 或加limit。order by中索引列可以任意组合。
- group by和的distinct 列上加索引 可以加快排序查询的时间。
- 选择越小的数据类型越好,通常越小的数据类型通常在磁盘,内存,cpu,缓存中 占用的空间很少,处理起来更快。
索引的问题:
索引并不是越多越好,也是有维护成本的。
- 索引文件也需要占用一定的空间,会增加额外空间消耗。
- 索引的创建和维护,数据越多,耗时越多。
- 对表数据进行 更新、插入、删除操作时,对应的索引也需要进行变动更新。因此需要额外的时间消耗。
索引主要用于查找操作时,更快的找到数据。所以索引的是否建立以及索引的类型,需要平衡考虑表的查询与更新的比例,以及查询的方式。