Mysql 索引设计与使用
索引的设计与使用
索引概述
在MYSQL中所有的列类型都可以被索引, 对相关列使用索引是提高SELECT操作性能的最佳途径
每种存储引擎对每个表至少支持16个索引. 总索引长度至少为256个字节
MYSQL还支持全文本(FULLTEXT)索引, 此索引可以用于全文搜索
在MYSQL5.6之后, InnoDB和MyISAM存储引擎都可以支持FULLTEXT索引. 但是只限于CHAR VARCHAR和TEXT列
索引总是对整个列进行, 不支持局部索引
MYSQL还支持空间列索引. MYSQL5.7之前只有MyISAM存储引擎支持空间类型索引, 而且索引的字段必须是非空的. MYSQL5.7中, InnoDB存储引擎也支持空间类型索引, 索引以R-Trees的数据结构保存
默认情况下, MEMORY存储引擎使用HASH索引, 但也支持BTREE索引
索引在创建表的饿时候可以同时创建,也可以随时增加新的索引
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
[index_type]
ON tbl_name (index_col_name...)
[index_option]
[algorithm_option | lock_option]...
index_col_name:
col_name[(length)] [ASC | DESC]
create index cityname on city (city(10));
如果以city为条件进行查询, 可以发现索引cityname被使用
explain select * from city where city = 'Fuzhou' \G
索引的删除
语法如下
DROP INDEX index_name ON tbl_name
例如, 想要删除city表上的索引cityname, 可以操作如下
drop index cityname on city;
设计索引的原则
- 要在条件列上创建索引, 而不是查询列.
- 尽量使用高选择度索引. 考虑列中值得分部, 索引的列的基数越大, 索引的效果越好
- 使用短索引. 如果对字符串列进行索引, 应该制定一个前缀长度, 只要有可能就要这样做
- 利用左索引, 在创建一个n列的索引时, 实际相当于创建了MYSQL可利用的n个索引. 多列索引可起到几个索引的作用, 因为可利用索引中最左边的列集来匹配行. 这样的列集被称为最左前缀.比如在a b c 中创建了一个组合索引, 使用a = ? a = ? and b = ? a = ? and b = ? and c = ? 都可以使用这个索引. 通过这种方式可以大量减少索引的数量, 提高索引的使用效率
- 对于InnoDB存储引擎的表,
尽量
手工指定主键. 记录会按照一定顺序保存, 如果拥有主键, 则会按照主键顺序保存. 如果没有主键但是有唯一索引, 那么按照唯一索引进行保存. 如果没有主键和唯一索引, 则会生成一个内部列. 根据显示的列来进行是查询最快的. 注意 InnoDB表的普通索引都会保存主键的建值. 所以主键要尽可能选择较短的数据类型, 有效减少索引的磁盘占用, 提高索引的缓存效果.
索引设计的误区
设计索引的时候往往有如下误区
- 不是所有的表都需要使用索引. 对于小表没什么必要只需要主键即可. 而对于大表来说最好时刻走索引, 因此要经常分析
- 不要过度使用索引, 不要以为索引是"越多越好". 每一个额外的索引都要占用额外的磁盘空间, 并且会
降低写效率
. 此外再MYSQL生成一个执行计划的时候, 要考虑索引, 这也要花费时间. 创建多余的索引给查询优化带来了更多的工作. 索引太多, 也可能会令mysql选择不到所要使用的最好索引!! - 谨慎创建低选择度索引. 再MYSQL8.0之后也可以使用直方图取得类似的效果
- 谨慎使用
唯一索引
,使用唯一索引在特定查询上速度会提升, 同时也能满足业务上对于唯一索引的需求. 但是在MYSQL5.6之后, InnoDB引擎新增了Change Buffer特效来大幅提升写入性能. 而除主键之外的唯一索引会导致Change Buffer无法被使用
索引设计的一般步骤
- 整理表上所有的SQL , 重点再select update delete操作的where条件使用到的列的组合, 关联查询的关联条件
- 整理所有查询SQL的预期执行频率
- 整理所有涉及的列的选择度, 列不同值相比总非空行数的比例越大, 选择度越好,
- 遵循之前提到的设计原则, 给表选择合适的主键. 没有特别合适的列时, 建议使用自增列作为主键
- 优先给那些执行频率最高的SQL创建索引, 执行频率很高的SQL, 使用到的索引效率对整体性能的影响也会比较大. 选择其中选择度最高的列来创建索引, 如果选择度都不够好, 那么应该考虑是否可以使用其他选择度更好的条件, 或者创建
联合索引
- 按执行顺序排序, 依次检查是否需要为每个SQL创建索引.
- 索引合并, 利用复合索引来降低索引的总数, 充分利用最左前缀的原则, 让索引可以被尽可能多地复用, 同时保证复用率的情况下, 把选择度更高的列放到索引的更左侧
- 上线之后, 通过慢查询分析 . 执行计划分析 . 索引使用统计, 来确定索引的实际使用情况, 并根据情况作出调整
BTREE索引与HASH索引
HASH索引有一些特别重要
的特征再使用时需注意
- 只用于使用=或<=>操作符的等式比较
- 优化器不能使用HASH索引来
加速
ORDER BY操作 - MYSQL不能确定再两个值之间大约有多少行, 如果将一个MyISAM表改为HASH索引的MEMORY表, 会影响一些查询执行效率
- 只能使用整个关键字来搜索一行
而对于BTREE索引, 当使用> < >= <= BETWEEN != 或者<> 或者 LIKE 'pattern'(pattern不以通配符开始)操作符时, 都可以使用相关列上的索引. 下列范围查询适用于BTREE索引和HASH索引
select * from t1 where key_col = 1 or key_col in (15,18,20)
下列查询只适用于BTREE索引
select * from t1 where key_col > 1 and key_col < 10
select * from t1 where key_col LIKE 'ab%' OR key_col between 'lisa' AND 'simon'
例如创建一个和city表完全相同的MEMORY存储引擎的表city_memory
create table city_memory(
city_id smallint unsigned not null AUTO_INCREMENT,
city varchar(50) not null,
country_id smallint unsigned not null,
last_update timestamp not null default current_timestamp on update current_timestamp,
primary key (city_id),
key idx_fk_country_id (country_id)
)ENGINE=Memory DEFAULT CHARSET=UTF8;
当对索引字段进行范围查询时候, 只有BTREE索引可以通过索引访问(B+树最后顺序存储)
而HASH是全表扫描
explain select * from city_memory where country_id > 1 and country_id < 10;
索引在MYSQL8.0中改进
不可见索引
再MYSQL8.0中, 增加了对于不可见索引(invisible index)的支持
这是一个从Oracle中借鉴过来的新特效
所谓不可见指的是查看表结构的时候可以看到, 但是在查询的时候不会走这个
可以在创建表的时候指定invisible关键字来创建不可见索引
create table t1(
i int,
j int,
k int,
index i_idx(i) INVISIBLE
)ENGINE=InnoDB;
可以通过alter命令来修改索引是否可见
alter table t1 alter index i_idx invisible;
alter table t1 alter index i_idx visible;
为什么要使用不可见索引
当表数据量睁大的时候, 达到了几百GB, 几TB甚至更大时候. 如果这个时候对表上的索引进行调整, 往往会有很大风险.
比如删除一个认为不重要的索引, 当有的执行计划还在使用这个索引的话, 就会对这个大表进行全盘扫描.
不可见索引可以很好地确定影响的范围.
同时当增加一个索引之后, 如果对系统带来了负面的影响, 可以首先将索引设置为不可见, 待系统负载恢复之后, 再做索引的删除, 避免压力大的时候雪上加霜
倒序索引
MYSQL8.0中增加了对倒序索引(descending index)的支持
在之前的版本中, 创建表的索引的时候可以指定desc关键字, 但是实际上还是正序索引
由于倒序索引引入, MYSQL8.0取消
了对group by操作的隐式排序
, 如果业务中有依赖于此特性的, 再升级数据库版本的时候需要谨慎.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!