作者:xcbeyond
博客:https://xcbeyond.cn/ 公众号:程序猿技术大咖
索引,对于良好的数据库性能非常关键。只要提及到数据库性能优化,都会首先想到“索引”,看看表中是否添加索引。尤其是当表中的数据量越来越大时,索引对性能的影响尤为突出。在数据量较小且负载较低时,没有索引或者不恰当索引对性能的影响可能还不明显,但当数据量逐渐增大时,性能则会急剧下降。
不过,索引却经常被忽略,有时候甚至被误解、误用,在实际使用中经常会遇到糟糕索引而导致的性能问题。本文就索引的概念、类型、优点等方面聊聊,一起深入理解索引的这点事,更有助于你清楚的理解索引,能够正确的使用它,便于利用它来进行数据库的优化。
Index),是帮助MySQL高效获取数据的数据结构,是存储引擎用于快速找到记录的一种数据结构。
要理解MySQL中索引是如何工作的,最简单的例子就是去看看一本书的目录“索引”部分。如果想在一本书中找到某个章节,一般我们会先看书的目录“索引”,就会立即找到对应的页码。
在MySQL中,存储引擎也是用类似的方法使用索引,首先在索引中找到对应的值,然后根据匹配的索引记录找到对应的数据行。
查询是数据库中最常用的操作,我们都希望查询的速度尽可能快,因此数据库系统的设计者会从查询算法角度去进行优化。最基本的查询算法当然就是顺序查找,但是这种算法的复杂度为O(n),在数据量很大时就会显得非常糟糕。例如,在一张用户表t_user中有如下数据,想要查询到年龄为89岁的人,如果按照顺序查找,则得逐行扫描,可见查询效率有多低下(数量量越大,数据分布越不均匀,则花费的时间就更长)。
mysql> select * from t_user;
+----+----------+-----+
| id | name | age |
+----+----------+-----+
| 1 | xcbeyond | 22 |
| 2 | jack | 34 |
| 3 | tom | 77 |
| 4 | kitty | 5 |
| 5 | make | 91 |
| 6 | Mickey | 23 |
| 7 | Andy | 89 |
+----+----------+-----+
7 rows in set
好在,数据库系统的设计者早都意识到了这一点,参考了更优秀的查找算法,如二分查找、二叉树查找等等,但是分析之后发现,每种查找算法都只能应用于特定数据结构之上,如二分查找要求被查询的数据有序,而二叉树查找只能应用于二叉查找树。鉴于此,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,即:这就是数据库中的索引。
为了更好的理解索引,下图就以表t_user中的数据,展示了<u>一种可能的索引方式</u>。
左边是表中的数据,一共7条记录,为加快age列的查找,维护了一个右边所示的二叉查找树,每个节点包含索引键值及一个指向对应数据记录的指针,这样运用二叉查找就能很快的查找对应的数据了,时间复杂度为O(log2 N)。
然而,在实际数据库中,几乎没有使用这样的二叉查找树来实现(因为二叉查找树对数据是有要求的),但其原理和这类似。
CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引。
语法:
CREATE [UNIQUE/FULLTEXT] INDEX <索引名> ON <表名>(<列名>)
ALTER TABLE <表名> ADD INDEX|UNIQUE|PRIMARY KEY|FULLTEXT <索引名>(<列名>)
其中,创建索引时,可以指定索引类型:主键索引(PRIMARY KEY)、 唯一索引(UNIQUE)、 全文索引(FULLTEXT)、 普通索引(INDEX)。
例如:
1)以表index_test为例说明,先创建一个普通的表index_test:
(创建表时,也可以直接创建索引,此处为了说明索引的创建,则单独创建索引)
mysql> create table index_test(id int,ch varchar(32));
Query OK, 0 rows affected
2)为表index_test单独创建索引:
mysql> create index idx on index_test(id);
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
或者
mysql> alter table index_test add index idx(id);
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
mysql> repair table index_test quick;
+-----------------+--------+----------+---------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+-----------------+--------+----------+---------------------------------------------------------+
| test.index_test | repair | note | The storage engine for the table doesn't support repair |
+-----------------+--------+----------+---------------------------------------------------------+
1 row in set
show index from|in table_name来查看索引。
语法:
SHOW INDEX FROM|IN <表名>
例如:
mysql> show index from index_test;
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| index_test | 1 | idx | 1 | id | A | 0 | NULL | NULL | YES | BTREE | | |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set
细心的你,或许看到查询结果中的字段index_type的值BTREE,这也就是接下来会讲到的B Tree索引,从另一方面也可以知道InnoDB默认的索引类型为B Tree。
DROP INDEX 或 ALTER TABLE语句来实现。
语法:
DROP INDEX <索引名> ON <表名>
ALTER TABLE <表名> DROP INDEX <索引名>
例如:
mysql> drop index idx on index_test;
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
或者
mysql> alter table index_test drop index idx;
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
MySQL默认存储引擎是Innodb,只显式支持B-Tree索引,对于频繁访问的表,Innodb会透明建立自适应哈希索引,即在B树索引基础上建立哈希索引,可以显著提高查找效率,对于客户端是透明的,不可控制的,隐式的。
B代表平衡(B-Tree是为磁盘等外存储设备设计的一种平衡查找树。因此在讲B-Tree之前先了解下磁盘的相关知识。 系统从磁盘读取数据到内存时是以磁盘块(balance),而不是二叉(binary),因为B Tree是从最早的平衡二叉树演化而来的。
block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。
InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB存储引擎中默认每个页的大小为16KB,可通过参数innodb_page_size将页的大小设置为4K、8K、16K,在MySQL中可通过如下命令查看页的大小:
mysql> show variables like 'innodb_page_size';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
1 row in set
而系统一个磁盘块的存储空间往往没有这么大,因此InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小16KB。InnoDB在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。
B-Tree定义数据记录为一个二元组[key、data]:
key为记录的主键,即表中的主键值,用于记录唯一的数据行,key值是唯一且互不相同的。
data为一行记录中除主键外的数据。
m阶的B-Tree有如下特性:
ceil(m/2)个孩子。p0,p1,...pn,k1,...kn)ceil(m/2)-1 <= n <= m-1ki(i=1,…n)为关键字,且关键字升序排序。pi(i=1,…n)为指向子节点的指针。p(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)。ceil()为取整函数。
B-Tree中的每个节点根据实际情况,可以包含大量的键值key、数据data、和指针p。如下图所示为一个3阶的B-Tree索引结构:
每个节点占用一个磁盘块空间,一个节点上有两个升序排序的关键字key和三个指向子节点的指针p,指针存储的是子节点所在磁盘块的地址。两个关键词key划分成为三个范围域对应的三个指针p,并指向的子节点的数据的范围域。以根节点为例,关键字为17和35,p1指针指向的子节点的数据范围为小于17,p2指针指向的子节点的数据范围为17~35,p3指针指向的子节点的数据范围为大于35。
模拟查找关键字为29数据行的过程:
29在区间(17,35),找到磁盘块1的指针p2。
p2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
29在区间(26,30),找到磁盘块3的指针p2。
29。
3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字key是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree(高度平衡的二叉树)缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。
B+Tree是在B-Tree基础上的一种优化,使其更适合实现存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。
从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
B+Tree相对于B-Tree有几点不同:
B-Tree进行优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。
可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为10^3)。也就是说一个深度为3的B+Tree索引可以维护10^3 * 10^3 * 10^3 = 10亿条记录。
实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2~4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。
hash index),是基于哈希表实现的。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希值(hash value),不同键值的行计算出来的哈希值也不一样。<u>哈希索引将所有的哈希值存储在索引中,同时在哈希表中保存指向每个数据行的指针。</u>
在MySQL中,只有Memory引擎显示支持哈希索引,同时哈希索引也是Memory存储引擎的默认索引类型,并且Memory存储引擎也是支持B-Tree索引。
如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希值。
继续以表t_user中的数据举例说明,并对字段name设置哈希索引。假设索引使用的哈希函数是f(),则计算出来的哈希值(都是举例数据,并非真实数据)为:
f('xcbeyond')=2390
f('jack')=4010
f('tom')=5178
f('kitty')=1067
f('make')=7901
f('Mickey')=3079
f('Andy')=8301
计算出来的哈希值,会指向对应数据行的数据,指向关系如下图:
执行如下查询,并能够查询到对应的数据。
mysql> select * from t_user where name = 'xcbeyond';
+----+----------+-----+
| id | name | age |
+----+----------+-----+
| 1 | xcbeyond | 22 |
+----+----------+-----+
1 row in set
先计算出xcbeyond的哈希值,根据该哈希值寻找到对应指向的数据行。f('xcbeyond')=2390,所以MySQL在索引中查找2390,并找到指向第1行的数据行,然后比较第1行的值是否等于xcbeyond,以确保查找到数据的准确性。
<u>因为索引自身只需存储对应的哈希值,所有索引的结构十分紧凑,这也让哈希索引查找的速度非常快。</u>然而,哈希索引也有它的限制,即:索引失效。
=、in(),不支持任何范围、模糊查找,例如,where age > 20、where name like '%xc%'。where条件匹配。
在相同的列上同时创建全文索引和基于值的B-Tree索引是不会有冲突的,全文索引适用于全文模糊搜索(MATCH AGAINST)操作,而不是普通的where条件操作。
文章目录一、概述简介原理模块二、配置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
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭3年前。Improvethisquestion我正处于学习Ruby的阶段,我想查看一些小型库的源代码以了解它们是如何构建的。我不知道什么是小型图书馆,但希望SO能推荐一些易于理解的图书馆来学习。因此,如果有人知道一两个非常小的库,这是新手Rubyists学习的好例子,请推荐!我想使用Manveru'sInnatelib,因为它试图保持在2000LOC以下,但我还不熟悉其中经常使用的Ruby速记。也许大约100-5
我发现自己需要这个。假设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
由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A
我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。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
我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0
我正在寻找一个用ruby演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent