索引的作用:快速找出特定的行。
索引一般存储在磁盘的文件中,它是占用物理空间的。
如果不使用索引,为了从数据库中查找特定的数据,那么就必须从第一条记录开始,遍历整张表,直到找出相关的行。数据量越大,查询数据所花费的时间就越多。
如果表中查询的列有一个索引,MySQL 能通过索引,够快速到达一个位置去搜索数据文件,而不必查看所有数据,那么将会节省很大一部分时间。就行翻字典一样,我们需要查找一条数据,你是愿意从第一页开始找,还是愿意通过目录 (索引) 找呢?
总之,可以简单的理解索引就是数据表的目录,索引 ≈ 目录。
说 MySQL 索引,为啥要说二叉树呢?因为 MySQL 的索引就是树形结构,只不过是一颗特殊的树,所以先从二叉树入手,这样能更好的理解。
首先应考虑的是二叉查找树,这是一种节点值之间具有一定数量级次序的二叉树,对于树中每个节点:
由于二叉查找树有上述特性,所以每次查找的时候,只需要去左边 或者 右边查找就可以了,而不需要全部遍历,类似于二分查找,大大缩短了查询所需时间。所以二叉搜索树也能用来做索引,但是:
在一般情况下,二叉查找树的平均时间复杂度是O(logN),但如下图所示,最差情况下的时间复杂度为O(N)。这样跟全表扫描没有区别了,所以二叉查找树可以用来做索引,但不是一个好的选择。

这时,你可能会想到红黑树,红黑树就能避免上述情况。但是,红黑树也是二叉树,当数据量大的时候,树的高度会非常高,这时候,二叉树就不够用了,需要多叉树才行。
上面说到二叉树不适合做索引,因为它太“高”了,其查询次数与树的高度成正比,为了减小高度,那么就很容易想到 B-Tree,因为 B-Tree 是多叉树。
B-Tree 是一种多路平衡查找树,它的每个节点最多包含 k 个子节点,k 被称为 B-Tree 的阶,k 的大小取决于磁盘页的大小。通常来说,k 的值都比较大,所以 B-Tree 是一颗“矮胖”树。
“页”是存储器的逻辑块,操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页大小通常为4k),主存和磁盘以页为单位交换数据。
下面来具体介绍一下 B-Tree,一个 k 阶的 B-Tree 具有如下几个特征:

从 B-Tree 的结构来看,它确实可以做索引,但是其一个变种 B+Tree 有更大的优势。
终于说到重点了,MySQL 的默认索引就是使用 B+Tree 来实现的。
一个 k 阶的 B+Tree 具有如下几个特征:

为什么 MySQL 的默认索引是 B+Tree 而不是 B-Tree 呢?
因为,B-Tree 的数据可以保存在非叶子节点中,搜索时不一定需要找到叶子节点,可能找到某个非叶子节点时就返回了,而 B+Tree 的数据均保存在叶子节点中,搜索时一定要找到叶子节点。
为什么数据要保存在叶子节点呢?
这样做的好处是:一次可以读取更多的索引。
磁盘一次 IO 操作读取的是一个磁盘页大小的数据。如果数据存放在非叶子节点中,那么一次 IO 操作能读取的索引量肯定就少,就可能产生更多的 IO 操作。如果非叶子节点不存储数据,那么一次 IO 操作就能读取到更多的索引,这样检索到数据就能产生更少的 IO 操作,花费的时间也就更少。
此外,B+Tree 所有叶子节点均有一个链指针,指向下一个节点,这样十分适合做范围查询。
综合起来,B+Tree 比 B-Tree 的优势有3点:
所以 B+Tree 更适合成为索引。
上面说了索引能加快数据查询的速度,但是索引是建立的越多越好吗?
答:不是。
尽管索引可以提高数据库的查询效率,但是索引有利也有弊,它也会让写入数据的效率下降。这是为什么呢?
数据的写入过程,会涉及索引的更新,这是索引导致数据写入变慢的主要原因。
建立索引后,索引就必须维持它的结构,即索引需要满足 B+Tree 的结构,而在 B+Tree 中,k 值是根据页的大小事先计算好的(一个磁盘页所能容纳索引的大小),也就是说,每个节点最多只能有 k 个子节点。
在往数据库中写入数据的过程中,这样就有可能使索引中某些节点的子节点个数超过 k,这个节点的大小超过 1 个磁盘页的大小,读取这样 1 个节点, 就会导致多次磁盘 IO 操作。我们该如何解决这个问题呢?
实际上,我们只需要将这个节点分裂成两个节点。但是,节点分裂之后,其上层父节点的子节点个数就有可能超过 k 个。不过这也没关系,我们可以用同样的方法,将父节点也分裂成两个节点。这种级联反应会从下往上,一直影响到根节点。这个分裂过程,你可以结合着下面这个图一块看,会更容易理解。
图中的 B+Tree 是一个三叉树。我们限定叶子节点中,数据的个数超过 2 个就分裂节点;非叶子节点中,子节点的个数超过3个就分裂节点。

正是因为 B+Tree 需要维护一个这样的结构,所以,索引会导致数据库的写入速度变慢。实际上,不仅是写入速度变慢,删除速度也同样会变慢,当节点个数少于一定数量时,会进行节点的合并,原理与上图类似。
所以,不建议用 UUID 或者随机字符串作为主键值,尽量用连续增长的值对于 InnoDB 而言,因为节点下有数据文件,因此节点的分裂将会比较慢。对于 InnoDB 的主键,尽量用整型而且是递增的整型。如果是无规律的数据,在页的分裂时,影响速度。
如果一个 WHERE 条件无法通过索引快速过滤,存储引擎层面就会将所有记录加锁后返回,再由 MySQL Server 层进行过滤。不过在实际使用过程中,MySQL 做了一些改进,在 MySQL Server 层进行过滤的时候,如果发现不满足,会调用 unlock_row 方法,把不满足条件的记录释放锁(显然这违背了二段锁协议)。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。可见在没有索引时,不仅会消耗大量的锁资源,增加数据库的开销,而且极大的降低了数据库的并发性能.。
但在一定的情况下,可能在有索引的情况下也不会用到索引,而是全表扫描。
1、联合索引的最左匹配原则
在联合索引的情况下,最左索引优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询 (>、<、between、like) 就会停止匹配。
例如:如果建立 (a, b) 顺序的索引,b = 2,或者 a = 1 or b = 2 是匹配不到 (a, b) 索引的;但是如果查询条件是 a = 1 and b = 2,或者 a=1,又或者是 b = 2 and b = 1 就可以,优化器会自动调整 a, b 的顺序。
再比如 a = 1 and b = 2 and c > 3 and d = 4 如果建立 (a, b, c, d) 顺序的索引,d 是用不到索引的,因为 c 字段是一个范围查询,它之后的字段会停止匹配。

上图中的联合索引为 (col3, col2),先通过 col3 找到 col2,但是无法通过 col2 找到 col3,这就是联合索引的最左匹配原则。最左不能丢,中间不能断,其实就是最左索引对应树的最上层索引。
2、如果条件中有 or ,即使其中有条件带索引也不会命中。
3、like 查询是以 % 开头:如果是 int 型索引不会命中,字符型需要 % 只有在右边才可以命中,如 test%;而 %test 和 %test% 不会命中。
4、如果字段型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。
5、没有查询条件,或者查询条件没有建立索引。
6、查询条件中,在索引列上使用函数( + , - , * , / ), 这种情况下需建立函数索引。
7、采用 not in,not exist。
InnoDB 与 MyISAM 都是 MySQL 的数据库引擎之一,现在 MySQL 的默认存储引擎为 InnoDB。
在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的 key 可以重复。

从上图中可以看出,MyISAM 叶子节点仅保存了键位信息以及该行数据的地址,并不保存真正的数据,索引和实际数据是分开的,只不过是用索引指向了实际的数据,MyISAM 索引仅仅保存数据记录的地址,这种索引就是所谓的非聚集索引。
相比 MyISAM 索引文件和数据文件是分离的,InnoDB 的叶节点 data 域保存了完整的数据记录。这个索引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。这被称为聚集索引。
InnoDB 其余的索引都作为辅助索引,辅助索引域存储相应记录主键的值。也就是说,InnoDB 在根据主索引搜索时,直接找到 key 所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。
因此,在设计表的时候,不建议使用过长的字段作为主键,过长的主索引会令辅助索引变得过大,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
因为 InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有主键(MyISAM 可以没有),如果没有显式指定,则自动选择一个唯一标识数据记录的列作为主键,如果不存在这种列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主键。
而 MyISAM 无论主索引还是辅助索引,都可以直接查找到对应的数据。
总结 InnoDB 与 MyISAM 的区别:
MyISAM 类型的表强调的是性能,其执行数度比 InnoDB 类型更快,但是不支持事务,不支持外键;而 InnoDB 提供事务支持外部键等高级数据库功能。
MyISAM 只支持表锁,不支持行锁;InnoDB 既支持表锁,也支持行锁,但只有在用到索引的情况下才会使用行锁。采用MVCC来支持高并发,有可能死锁。
MyISAM 适合:1、做 count 的计算;2、插入不频繁,查询非常频繁;3、没有事务。
InnoDB 适合:1、可靠性要求比较高,或者要求事务;2、表更新和查询都相当的频繁,并且行锁定的机会比较大的情况
1、根据慢日志定位慢 SQL
MySQL 的慢查询日志是一种日志记录,它用来记录在 MySQL 中响应时间超过阀值的语句,具体指运行时间超过 long_query_time 值的SQL,则会被记录到慢查询日志中。long_query_time 的默认值为 10s。
2、使用 explain 分析 SQL
explain 用来分析 SELECT 查询语句,通过分析结果来优化查询语句。在分析的结果中,比较重要的字段有:
select_type : 查询类型,有简单查询、联合查询、子查询等
key : 使用的索引
rows : 扫描的行数
3、修改 SQL 或让 SQL 走索引
只返回必要的列:最好不要使用 SELECT * 语句。
只返回必要的行:使用 LIMIT 语句来限制返回的数据。
缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
我发现自己需要这个。假设cart是一个包含用户列表的模型。defindex_of_itemcart.users.each_with_indexdo|u,i|ifu==current_userreturniendend获取此类关联索引的更简单方法是什么? 最佳答案 indexArray上的方法与您的index_of_item方法相同,例如cart.users.index(current_user)返回数组中第一个对象的索引==给obj。如果未找到匹配项,则返回nil。 关于ruby-on-
因此,当我遵循MichaelHartl的RubyonRails教程时,我注意到在用户表中,我们为:email属性添加了一个唯一索引,以提高find的效率方法,因此它不会逐行搜索。到目前为止,我们一直在根据情况使用find_by_email和find_by_id进行搜索。然而,我们从未为:id属性设置索引。:id是否自动索引,因为它在默认情况下是唯一的并且本质上是顺序的?或者情况并非如此,我应该为:id搜索添加索引吗? 最佳答案 大多数数据库(包括sqlite,这是RoR中的默认数据库)会自动索引主键,对于RailsMigration
我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi
假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum
在我的场景中,Logstash收到的系统日志行的“时间戳”是UTC,我们在Elasticsearch输出中使用事件“时间戳”:output{elasticsearch{embedded=>falsehost=>localhostport=>9200protocol=>httpcluster=>'elasticsearch'index=>"syslog-%{+YYYY.MM.dd}"}}我的问题是,在UTC午夜,Logstash在外时区(GMT-4=>America/Montreal)结束前将日志发送到不同的索引,并且索引在20小时(晚上8点)之后没有日志,因为“时间戳”是UTC。我们已
我想从特定索引开始遍历数组。我该怎么做?myj.eachdo|temp|...end 最佳答案 执行以下操作:your_array[your_index..-1].eachdo|temp|###end 关于ruby-从特定索引开始迭代数组,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/44151758/
我一直在努力学习如何处理由数组组成的数组。假设我有这个数组:my_array=[['ORANGE',1],['APPLE',2],['PEACH',3]我将如何找到包含'apple'的my_array索引并删除该索引(删除子数组['APPLE',2]因为'apple'包含在该索引的数组中)?谢谢-我非常感谢这里的帮助。 最佳答案 您可以使用Array.select过滤掉项目:>>a=[['ORANGE',1],['APPLE',2],['PEACH',3]]=>[["ORANGE",1],["APPLE",2],["PEACH",3
我想使用部分字符串搜索数组,然后获取找到该字符串的索引。例如:a=["Thisisline1","Wehaveline2here","andfinallyline3","potato"]a.index("potato")#thisreturns3a.index("Wehave")#thisreturnsnil使用a.grep将返回完整的字符串,使用a.any?将返回正确的true/false语句,但都不会返回匹配的索引找到了,或者至少我不知道该怎么做。我正在编写一段代码,该代码读取文件、查找特定header,然后返回该header的索引,以便它可以将其用作future搜索的偏移量。如果