当前位置: 首页 > news >正文

【HBZ分享】Mysql索引的失效场景 以及 创建索引失败报错的原因

如何查看mysql某个表具有的索引

show INDEX from [表名]

创建索引失败的场景 及 原因

  1. 假设有一张表如下: 使用utf8mb4的字符编码
CREATE TABLE `api_case` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`name` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT 'API用例名称',`description` varchar(2048) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 'API用例描述',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
  1. 我们设置一个name + description的联合索引
  2. 执行【CREATE INDEX idx_name_description on api_case(name, description)】
  3. 执行上面语句会发生报错,即索引创建失败:Specified key was too long; max key length is 3072 bytes。 这个表示联合索引中,存在某列的长度达到了3072个bytes,当然这里说的就是description字段的长度
  4. 混淆: 这个3072个字节指的是varchar(2048)的这个2048个字符转换成bytes单位的长度后超过了3072个字符,并不是说索引名字长度是3072个字节,这块别整错了。
  5. 2048是如何转换的,为什么转换成bytes单位后超过了767呢?
    • 首先innodb引擎的索引中的字段要求最大长度为【767】个字节, myisam的最大长度【1000】bytes,这是默认,当然可以更改,但不建议,更改后会影响索引查询效率。
    • 从mysql5.x版本之后,varchar(2048)的这个2048长度指的是字符个数,而不是字节个数
    • 我们使用的是utf8mb4字符编码,所以【1个字符 = 4个byte】。 如果使用的是utf8mb3则【1个字符=3个byte】
    • 我们把2048转换成bytes就是2048 * 3 = 6144个byte, 所以6144 > 3072了, 所以报错了。
    • 正确的范围应该是 767 / 3 = 255个字符以内,即varchar(255)已经是极限了,不能再多了

mysql各类型所占的字节数: 注意这些int家族的就是固定长度,哪怕你写int(10)那占的长度也是4字节

类型所占字节数
char(n)n字节
varchar(n)3n +2 , 这个2是保存字符串长度所需要的字节数是2个
tinyint1字节
int4字节
bigInt8字节
date3字节
dateTime8字节
timeStamp4字节

索引长度key_len的计算方式

  1. 表如下:
CREATE TABLE `api_case` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL COMMENT 'API用例名称',`description` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 'API用例描述',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;# 使用的sql-1
select * from api_case where name='hhh' and descrpition='ss';
# 使用的sql-2
select * from api_case where name='hhh'';
  1. 上面sql-1会命中联合索引,则长度是key_len = 20 * 3 +2 + 1 + 100 * 3 + 2 + 1 = 366
  2. 计算方式: 20 * 3表示转换成字节个数,2表示记录字符串长度需要2个字符的位置, 1表示记录是否为NULL的这个标识占1字节(当该字段NOT NULL的时候这1个字节就省了。) 然后把所有联合索引中的字段长度相加,就是总索引所占的长度
  3. 上面sql-1会命中联合索引,则长度是key_len = 20 * 3 +2 + 1 = 63
  4. 计算方式:计算方式都一样,只不过因为只命中了联合索引的name字段,所以只会计算name字段长度,description不会参与计算

如何强制使用指定的索引?通常情况下会让优化器自行选择

# 使用 force index(索引名称)
select * from api_case force index(idx_name) where name='hhh' and descrpition='ss';

误区:int(10) 和 int(4) 和 int(1) 有什么区别

  1. 结论:除非给字段加了zerofill,否则没任何区别,int(1) 同样可以存100
  2. 不要和varchar(10)和varchar(100)搞混了,varchar这个确实是限制字符长度,但int(10), int(1)可不是
  3. 加了zerofile会是啥样? 比如存了10 ,则int(1)就会展示10, 而int(4)展示的是0010, 索引int(4)这个4表示在有zerofill的情况下,要显示出多少位,要添0补到这个位数,int(10)就是0000000010, 并不是说int(1)只能0-9, 而int(4)只能0000-9999
  4. 切记:int家族的int(1), int(10), int(100)并不是说将该字段长度范围限制在1位,10位还是100位数, 而是说使用zerofill后,不足这么多位的会添0补充上,他们能存粗的数字都是 2^32 -1 将近40亿

【联合索引】的失效场景(联合索引前提: name, age,sex 按顺序组成联合索引)

  1. 联合索引没遵循【最左匹配原则】,比如name, age, sex3个字段按顺序组成联合索引,但where条件只有age = 18,最左边的name字段没有,那就不会生效,可以是 【where age = 19 and name=‘hhh’】, 这种顺序反了不要紧,优化器会自动排序,但name必须要存在于where中。
  2. 使用【or】连接了,这时不会生效,哪怕第一个条件是name,使用了or就不会不生效。 如果or左右两边的字段都有独立的索引,则可以生效(注意!!!: 前提是数据量一定要大,前提是数据量一定要大,前提是数据量一定要大)
# 下面写法用or连接 并且 顺序还不对的不会生效
select * from api_case where age=19 or name='hhh';
  1. 部分失效: 跳过中间字段age,则右边sex不生效,当where条件有联合索引的第一个字段name和第三个字段sex时,第二个字段age不存在where条件里,则只会生效第一个字段name的索引,而sex不会生效 (联合索引顺序: name, age, sex)
#  索引只会生效name, sex不会生效,因为联合索引第二个字段age不存在
select * from api_case where name='hhh' and sex = '女';
  1. 部分失效:存在范围查询,则本身和左边生效,右边不生效,请看下面示例:
# name, age生效,但sex不生效
# 看似都在联合索引之内,但age是范围,所以根据范围字段本身和左边生效,右边不生效,所以name, age是生效的,但sex不生效
select * from api_case where name = 'hhh' and age > 20 and sex='女';
  1. 当加索引的字段重复率非常高的情况,比如name加了索引,但表中name的值100条数据有99条是hhh,只有1条是aaa,则where name = ‘hhh’ 也不会走索引,因为优化器认为不需要走索引了,因为和全表扫描基本一样了。但where name = 'aaa’那就会走索引

踩坑: 神奇的现象or连接

场景:即使or左右两边条件都设置了各自独立的索引,但是发现依然走了全表扫描,没走索引,这是什么原因呢?
答: mysql优化器会进行判断,是走全表扫描快,还是走索引快,如果走全表扫描更快,则即使有索引那也不会走的。所以我们上面有个特别注意点:就是数据量一定要大!!!, 如果全表就几条数据,那即使建立了索引,也不会走的。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Spring IOC 小演示
  • 【区块链+乡村振兴】阳光农安农产品质量安全监管与服务平台 | FISCO BCOS应用案例
  • Fal.ai Flux 1-Pro/Viva.ai/哩布哩布AI:AI绘图部分免费工具+原图提示词Prompt
  • 慢SQL优化
  • Vue3+Ts封装类似el-drawer的抽屉组件
  • 【C语言篇】编译和链接以及预处理介绍(下篇)
  • springboot controller参数中如何传递字符串数组
  • MySQL 常用 SQL 语句大全
  • LVS的简单配置及对Mysql主从复制的补充
  • 食品零食小吃商城管理系统-计算机毕设Java|springboot实战项目
  • 求职Leetcode算法题(7)
  • c语言基础知识学习
  • 井字棋游戏(HTML+CSS+JavaScript)
  • Java的反射原理
  • 天途推出无人机软硬件定制服务
  • android图片蒙层
  • Android组件 - 收藏集 - 掘金
  • Angular 4.x 动态创建组件
  • Apache Spark Streaming 使用实例
  • docker python 配置
  • echarts的各种常用效果展示
  • ES6 ...操作符
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • ES6系统学习----从Apollo Client看解构赋值
  • JavaScript的使用你知道几种?(上)
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • Linux Process Manage
  • linux学习笔记
  • Mysql5.6主从复制
  • node 版本过低
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • Python学习之路13-记分
  • SpiderData 2019年2月16日 DApp数据排行榜
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • Vue 重置组件到初始状态
  • Windows Containers 大冒险: 容器网络
  • 程序员最讨厌的9句话,你可有补充?
  • 爬虫模拟登陆 SegmentFault
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何设计一个微型分布式架构?
  • 深度学习在携程攻略社区的应用
  • 数据可视化之 Sankey 桑基图的实现
  • 数组的操作
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​水经微图Web1.5.0版即将上线
  • ![CDATA[ ]] 是什么东东
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #window11设置系统变量#
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (LeetCode 49)Anagrams
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (二)fiber的基本认识