索引实验

实验目的:了解索引对于全列匹配,最左前缀匹配、范围查询的影响。实验所用数据库见文章最底部连接。

实验软件版本:5.7.19-0ubuntu0.16.04.1-log (Ubuntu)
实验存储引擎:InnoDB

show index from `employees`.`titles`

实验一、全列匹配

explain select * from `employees`.`titles` where `emp_no`='10001' and title='Senior Engineer' and `from_date`='1986-06-26';

很明显,当按照索引中所有列进行精确匹配(这里精确匹配指“=”或“IN”匹配)时,索引可以被用到。这里有一点需要注意,理论上索引对顺序是敏感的,但是由于MySQL的查询优化器会自动调整where子句的条件顺序以使用适合的索引。

explain select * from `employees`.`titles` where `from_date`='1986-06-26' and `emp_no`='10001' and title='Senior Engineer';

实验二、最左前缀匹配

explain select * from `employees`.`titles` where `emp_no`='10001';

当查询条件精确匹配索引的左边连续一个或几个列时,如<emp_no>或<emp_no, title>,所以可以被用到,但是只能用到一部分,即条件所组成的最左前缀。上面的查询从分析结果看用到了PRIMARY索引,但是key_len为4,说明只用到了索引的第一列前缀。

实验三、查询条件用到了索引中列的精确匹配,但是中间某个条件未提供

explain select * from `employees`.`titles` where `emp_no`='10001' and `from_date` = '1986-06-26' ;

此时索引使用情况和实验二相同,因为title未提供,所以查询只用到了索引的第一列,而后面的from_date虽然也在索引中,但是由于title不存在而无法和左前缀连接,因此需要对结果进行扫描过滤from_date(这里由于emp_no唯一,所以不存在扫描)。

如果想让from_date也使用索引而不是where过滤,可以增加一个辅助索引<emp_no, from_date>,此时上面的查询会使用这个索引。除此之外,还可以使用一种称之为“隔离列”的优化方法,将emp_no与from_date之间的“坑”填上。

看下title一共有几种不同的值。

select distinct(title) from `employees`.`titles`;

只有7种。在这种成为“坑”的列值比较少的情况下,可以考虑用“IN”来填补这个“坑”从而形成最左前缀:

explain select * from `employees`.`titles`
where `emp_no` = '10001'
and `title` IN ('Senior Engineer', 'Staff', 'Engineer', 'Senior Staff', 'Assistant Engineer', 'Technique Leader', 'Manager')
and `from_date` = '1986-06-26';

这次key_len为59,说明索引被用全了,但是从type和rows看出IN实际上执行了一个range查询,这里检查了7个key。看下两种查询的性能比较:

“填坑”后性能提升了一点。如果经过emp_no筛选后余下很多数据,则后者性能优势会更加明显。当然,如果title的值很多,用填坑就不合适了,必须建立辅助索引。

实验四:查询条件没有指定索引第一列

explain select * from `employees`.`titles` where `from_date` = '1986-06-26';

由于不是最左前缀,索引这样的查询显然用不到索引。

实验五:匹配某列的前缀字符串

explain select * from `employees`.`titles`where `emp_no` = '10001' and `title` like 'Senior%';

此时可以用到索引。如果配符%不出现在开头,则可以用到索引,但根据具体情况不同可能只会用其中一个前缀。

实验六:范围查询

explain select * from `employees`.`titles` where `emp_no` < '10010' and `title` = 'Senior Engineer';

范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引。同时,索引最多用于一个范围列,因此如果查询条件中有两个范围列则无法全用到索引。

explain select * from `employees`.`titles`
where `emp_no` < '10010'
and `title` = 'Senior Engineer'
and `from_date` between '1986-01-01' and '1986-12-11';

可以看到索引对第二个范围索引无能为力。这里特别要说明MySQL一个有意思的地方,那就是仅用explain可能无法区分范围索引和多值匹配,因为在type中这两者都显示为range。同时,用了“between”并不意味着就是范围查询,例如下面的查询:

explain select * from `employees`.`titles`
where `emp_no` between '10001' and '10010'
and `title` = 'Senior Enginee'
and `from_date` between '1986-01-01' and '1986-12-31';

看起来是用了两个范围查询,但作用于emp_no上的“BETWEEN”实际上相当于“IN”,也就是说emp_no实际是多值精确匹配。可以看到这个查询用到了索引全部三个列。因此在MySQL中要谨慎地区分多值匹配和范围匹配,否则会对MySQL的行为产生困惑。

实验七:查询条件中含有函数或表达式

如果查询条件中含有函数或表达式,则MySQL不会为这列使用索引(虽然某些在数学意义上可以使用)。例如:

explain select * from `employees`.`titles` where `emp_no` = '10001' and left(`title`, 6) = 'Senior';

虽然这个查询和实验五中功能相同,但是由于使用了函数left,则无法为title列应用索引,而实验五中用LIKE则可以。再如:

explain select * from `employees`.`titles` where `emp_no` - 1 = '10000';

显然这个查询等价于查询emp_no为10001的函数,但是由于查询条件是一个表达式,MySQL无法为其使用索引。因此在写查询语句时尽量避免表达式出现在查询中,而是先手工私下代数运算,转换为无表达式的查询语句。

索引选择性与前缀索引

索引选择性

所谓索引的选择性(Selectivity),是指不重复的索引值(也叫基数,Cardinality)与表记录数(#T)的比值:

Index Selectivity = Cardinality / #T

显然选择性的取值范围为(0, 1],选择性越高的索引价值越大,这是由B+Tree的性质决定的。例如,上文用到的employees.titles表,如果title字段经常被单独查询,是否需要建索引,我们看一下它的选择性:

select count(distinct(title))/count(*) as selectivity from `employees`.`titles`;

title的选择性不足0.0001(精确值为0.00001579),所以实在没有什么必要为其单独建索引。

前缀索引

有一种与索引选择性有关的索引优化策略叫做前缀索引,就是用列的前缀代替整个列作为索引key,当前缀长度合适时,可以做到既使得前缀索引的选择性接近全列索引,同时因为索引key变短而减少了索引文件的大小和维护开销。

explain select * from `employees`.`employees` where `first_name` = 'Eric' and `last_name` = 'Anido';

因为employees表只有一个索引<emp_no>,那么如果我们想按名字搜索一个人,就只能全表扫描了:

如果频繁按名字搜索员工,这样显然效率很低,因此我们可以考虑建索引。有两种选择,建<first_name>或<first_name, last_name>,看下两个索引的选择性:

select count(distinct(first_name))/count(*) as selectivity from `employees`.`employees`;

select count(distinct(concat(first_name, last_name)))/count(*) as selectivity from `employees`.`employees`;

<first_name>显然选择性太低,<first_name, last_name>选择性很好,但是first_name和last_name加起来长度为30,有没有兼顾长度和选择性的办法?可以考虑用first_name和last_name的前几个字符建立索引,例如<first_name, left(last_name, 4)>,看看其选择性:

select count(distinct(concat(first_name, left(last_name, 4))))/count(*) as selectivity from `employees`.`employees`;

加索引

ALTER TABLE employees.employees
ADD INDEX `first_name_last_name4` (first_name, last_name(4));

前缀索引兼顾索引大小和查询速度,但是其缺点是不能用于ORDER BY和GROUP BY操作,也不能用于Covering index(即当索引本身包含查询所需全部数据时,不再访问数据文件本身)。

MySQL事务隔离层级实验

实验目的:了解MySQL中事务隔离级别以及什么是脏读,幻读,不可重复读。

实验一:脏读

定义:在两个事务中,一个事务读到了另一个事务未提交的数据。因为数据可能被回滚,不符合隔离性的定义。

1.新建数据库连接执行一下操作

set global transaction isolation level read uncommitted;
set autocommit = 0;
begin;
update `employees`.`titles` set `title` = 'Senior Engineer 1' where `emp_no` = 100001;

注意还没有执行 commit

2.然后新建一个连接 可以看到读到了另一个事物还未被commit的数据,这就是所谓的脏读。

实验二:幻读

定义:一个事务批量读取了一批数据时,另一个事务提交了新的数据,当之前的事务再次读取时,会产生幻影行。

如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。

1.设置事物隔离级别。

set global transaction isolation level read committed;
begin;
select * from `employees`.`titles` where `titles`.`from_date` = '1994-12-15';

2.新开一个连接

begin;
insert into `titles` values (499999, 'Engineer', '1994-12-15', '1994-12-15');
commit;

3.回到第一步的窗口,查询数据。

select * from `employees`.`titles` where `titles`.`from_date` = '1994-12-15';
commit;

实验三:不可重复读

定义:不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。

例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户内存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。
  不可重复读和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
  很多人认为这种情况就对了,无须困惑,当然是后面的为准。我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。

  1. 开启连接查询值。
begin;
select * from `employees`.`titles` where `emp_no` = 100001;
select * from `employees`.`titles` where `emp_no` = 100001;

2.新开一个连接修改emp_no为100001的title的值。

begin;
update `employees`.`titles` set `title` = 'Senior Engineer 1' where `emp_no` = 100001;
commit;

3.回到第一步的连接再次查询

select * from `employees`.`titles` where `emp_no` = 100001;

MySQL事务隔离级别

  • 未提交读:第一个事务还未提交,另一个事务就可以读取,导致脏读。
  • 提交读(不可重复读):一个事务未提交对其他事务不可见,但是会产生幻读和不可重复读。
  • 可重复读(mysql默认隔离级别):保证同一个事务下多次读取的结果一致,但是会产生幻读。
  • 可串行化:严格的串行阻塞,并发能力不好。
隔离级别 脏读 不可重复读 幻读
Read Uncommitted
Read Committed
Repeatable Read (默认)
Serializable

参考资料

1.走进mysql基础
2.MySQL索引背后的数据结构及算法原理
3.datacharmer/test_db

MySQL实验: 实践索引对全列匹配、最左前缀匹配、范围查询等条件的影响以及了解脏读、幻读等...相关推荐

  1. 索引的使用—— 验证索引提升查询效率 || 避免索引失效 —— 全值匹配 /最左前缀法则/范围查询右边的列,不能使用索引/不要在索引列上进行运算操作/字符串不加单引号,造成索引失效

    索引的使用 索引是数据库优化最常用也是最重要的手段之一, 通过索引通常可以帮助用户解决大多数的MySQL的性能优化问题 验证索引提升查询效率 查询速度很快,接近0s ,主要的原因是因为id为主键,有索 ...

  2. 尽量使用覆盖索引,避免select * || 用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到|| 如果MySQL评估使用索引比全表更慢,则不使用索引

    尽量使用覆盖索引,避免select * 尽量使用覆盖索引(只访问索引的查询(索引列完全包含查询列)),减少select * 7). 用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索 ...

  3. MySQL 索引 :哈希索引、B+树索引、最左前缀匹配规则、全文索引

    文章目录 索引 什么是索引 索引优缺点与适用场景 常见的索引 哈希索引 自适应哈希索引 B+树索引 聚集索引 非聚集索引 使用方法 联合索引 最左前缀匹配规则 覆盖索引 全文索引 使用方法 索引 什么 ...

  4. mysql中组合索引创建的原则是什么意思_面试前必须要掌握的MySQL索引最左前缀匹配原则...

    在面试中,经常会遇到这种问题,如果我们设置联合索引的顺序是(a, b, c), 那么如果我们在查询时的顺序却是(a, c, b) 会走索引吗?这个问题被问到的频率之高,令人乍舌,在这篇文章中,我们就深 ...

  5. 找到符合条件的索引_高频面试题:MySQL联合索引的最左前缀匹配原则

    前言 之前在网上看到过很多关于mysql联合索引最左前缀匹配的文章,自以为就了解了其原理,最近面试时和面试官交流,发现遗漏了些东西,这里自己整理一下这方面的内容. 最左前缀匹配原则 在mysql建立联 ...

  6. 原创:史上最全最通俗易懂的,索引最左前缀匹配原则(认真脸)

    索引最左前缀匹配原则 对于最左前缀匹配原则居然没有百度百科,实在是让我感觉不可思议. 最左前缀匹配原则,用几句话来概述就是: 顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中 ...

  7. 联合索引的最左前缀匹配原则

    目录 联合索引 最左前缀匹配原则 最左匹配原则的成因 联合索引 所谓的联合索引就是指,由两个或以上的字段共同构成一个索引. 本文测试用例的数据表结构如下,一张简简单单的学生信息表 tb_student ...

  8. mysql最左前缀概念_Mysql的最左前缀匹配原则(上)

    最左前缀在mysql的官方文档中称之为leftmost prefix,该原则适用于多列索引,想仅仅用三言两语来说清楚什么是最左前缀匹配原则不太现实,但是如果使用官方文档的一个例子来说明该原则,或许会好 ...

  9. oracle组合索引最左原则,索引的最左前缀匹配原则的误区与索引下推技术

    最左前缀匹配原则是指where条件中在使用到 > < in between like等范围搜索的这个即以前的字段,如果可以与联合索引的前几个一一匹配,就可以使用这个索引. 但是实际操作中我 ...

最新文章

  1. 博士学位论文 | 机器阅读理解与文本问答技术研究
  2. 关于远程终端不能登录问题解决!
  3. 利用task和电平敏感的always快设计经比较后重组信号的组合逻辑
  4. 这是我见过最通俗易懂的单例模式讲解了!
  5. django 获取环境变量_django 环境变量配置过程详解
  6. C#中的正则表达式 \(([^)]*)\) 详解
  7. 店铺淘宝双十一活动利润的快速量化。
  8. 网络安全实验室 基础题 解析
  9. 保密管理规定 涉密计算机销毁,涉密文件保密制度
  10. 英文参考文献格式引用方法分析
  11. (快)开学了,各大编程语言在群里吵翻了天!
  12. 什么是数据挖掘?数据挖掘的目标是什么?
  13. 在线查看相机快门次数_我是亲民_新浪博客
  14. java 云笔记_云笔记系统的设计与实现 PDF 下载
  15. java+代码实现+流星雨,js代码实现流星雨 - osc_zls6dx9i的个人空间 - OSCHINA - 中文开源技术交流社区...
  16. 狂神说——大前端进阶NodeJS、Npm、Es6、Babel、Webpack、模块化使用
  17. 【菜鸡读论文】Former-DFER: Dynamic Facial Expression Recognition Transformer
  18. Kindle PaperWhite3 越狱和PDF插件的安装
  19. 如何屏蔽掉电脑上因下载软件捆绑的广告(烦人的广告让人十分尴尬)
  20. [计算机图形学]辐射度量学、渲染方程与全局光照(前瞻预习/复习回顾)

热门文章

  1. python多线程_Python多线程和队列结合demo
  2. 前端每日一算(我掐指一算,這题不难~入门啊入门)
  3. 学习pytorch的一些自己犯过的错误而总结的注意事项,估计其他也会使用
  4. python重写和重载的区别_Java 重写(Override)与重载(Overload)
  5. android sharedpreference 清空,Android 从SharedPreferences中存储,检索,删除和清除数据...
  6. linux远程登录失败锁定,登录失败锁定策略配置登录超时策略禁用root远程登录脚本...
  7. 怎么讲gis里的符号化_地信(GIS)方向考研~?测绘科学与技术
  8. html怎么在服务器环境,如何搭建node服务器环境?
  9. python保存后不运行_Python后台执行不启用缓存
  10. git切换分支出现head is now at_git寻根——^和~的区别