浅谈MySQL和Lucene索引的对比分析

MySQL和Lucene都可以对数据构建索引并通过索引查询数据,一个是关系型数据库,一个是构建搜索引擎(Solr、ElasticSearch)的核心类库。两者的索引(index)有什么区别呢?以前写过一篇《Solr与MySQL查询性能对比》,只是简单的对比了下查询性能,对于内部原理却没有解释,本文简单分析下两者的索引区别。

MySQL索引实现

在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。

MyISAM索引实现

MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:

图1是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。B+Tree的所有叶子节点包含所有关键字且是按照升序排列的。

MyISAM表的索引和数据是分离的,索引保存在”表名.MYI”文件内,而数据保存在“表名.MYD”文件内。

MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。

InnoDB索引实现

虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。

第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

图2是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,图3为定义在Col3上的一个辅助索引:

这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

讲MySQL索引的实现的文章很多,以上也是参考了《MySQL索引背后的数据结构及算法原理》,现在来看看Lucene的索引原理。

Lucene索引实现

Lucene的索引不是B+Tree组织的,而是倒排索引,Lucene的倒排索引由Term index,Team Dictionary和Posting List组成。

有倒排索引(invertedindex)就有正排索引(forwardindex),正排索引就是文档(Document)和它的字段Fields正向对应的关系:

DocID

name

sex

age

1

jack

18

2

lucy

17

3

peter

17

倒排索引是字段Field和拥有这个Field的文档对应的关系:

Sex字段:

[1,3]

[2]

Age字段:

18

[1]

17

[2,3]

Jack,lucy或者17,18这些叫做term,而[1,3]就是posting list。Posting list就是一个int型的数组,存储了所有符合某个term的文档id。那么什么是Term index和Term dictionary?

如上,假设name字段有很多个term,比如:Carla,Sara,Elin,Ada,Patty,Kate,Selena

如果按照这样的顺序排列,找出某个特定的term一定很慢,因为term没有排序,需要全部过滤一遍才能找出特定的term。排序之后就变成了:Ada,Carla,Elin,Kate,Patty,Sara,Selena

这样就可以用二分查找的方式,比全遍历更快地找出目标的term。如何组织这些term的方式就是 Term dictionary,意思就是term的字典。有了Term dictionary之后,就可以用比较少的比较次数和磁盘读次数查找目标。但是磁盘的随机读操作仍然是非常昂贵的,所以尽量少的读磁盘,有必要把一些数据缓存到内存里。但是整个Term dictionary本身又太大了,无法完整地放到内存里。于是就有了Term index。Term index有点像一本字典的大的章节表。比如:

A开头的term ……………. Xxx页

C开头的term ……………. Xxx页

E开头的term ……………. Xxx页

如果所有的term都是英文字符的话,可能这个term index就真的是26个英文字符表构成的了。但是实际的情况是,term未必都是英文字符,term可以是任意的byte数组。而且26个英文字符也未必是每一个字符都有均等的term,比如x字符开头的term可能一个都没有,而s开头的term又特别多。实际的term index是一棵trie 树:

上图例子是一个包含 "A", "to", "tea", "ted", "ten", "i", "in", 和 "inn" 的trie树。这棵树不会包含所有的term,它包含的是term的一些前缀。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。再加上一些压缩技术(想了解更多,搜索 Lucene Finite State Transducers),Term index的尺寸可以只有所有term的尺寸的几十分之一,使得用内存缓存整个term index变成可能。

整体上来说就是这样的效果:

由Term index到Term Dictionary,再到Posting List,通过某个字段的关键字去查询结果的过程就比较清楚了,通过多个关键字的Posting List进行AND或者OR进行交集或者并集的查询也简单了。

对比MySQL的B+Tree索引原理,可以发现:

1)Lucene的Term index和Term Dictionary其实对应的就是MySQL的B+Tree的功能,为关键字key提供索引。Lucene的inverted index可以比MySQL的b-tree检索更快。

2)Term index在内存中是以FST(finite state transducers)的形式保存的,其特点是非常节省内存。所以Lucene搜索一个关键字key的速度是非常快的,而MySQL的B+Tree需要读磁盘比较。

3)Term dictionary在磁盘上是以分block的方式保存的,一个block内部利用公共前缀压缩,比如都是Ab开头的单词就可以把Ab省去。这样Term dictionary可以比B-tree更节约磁盘空间。

4)Lucene对不同的数据类型采用了不同的索引方式,上面分析是针对field为字符串的,比如针对int,有TrieIntField类型,针对经纬度,就可以用GeoHash编码。

5)在 Mysql中给两个字段独立建立的索引无法联合起来使用,必须对联合查询的场景建立复合索引,而Lucene可以任何AND或者OR组合使用索引进行检索。

以上就是小编为大家带来的浅谈MySQL和Lucene索引的对比分析的全部内容了,希望对大家有所帮助,多多支持我们~

(0)

相关推荐

  • MySQL多线程复制遇到Error_code: 1872的解决方案

    上周在生产环境上遇到一个问题,不敢独享,拿出来给小伙伴们做个简单的分享. 起因 :由于IDC机房断电(估计又是哪里被挖掘机碰了下吧),导致所有服务器重启,影响到了其中的MySQL数据库.来看下这时数据库遇到的问题: 数据库版本 :MySQL 5.7.10 问题表现 :从机复制报如下错误:Slave SQL for channel ": Slave failed to initialize relay log info structure from the repository, Error_co

  • mysql 5.7 zip 文件在 windows下的安装教程详解

    1.下载mysql最新版本. http://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.15-winx64.zip 2.解压到文件夹. D:\software\mysql\mysql5.7a 将my-default.ini 复制为 my.ini 3.编辑my.ini # These are commonly set, remove the # and set as required. basedir ="D:/software/mysql/mysql

  • mysql alter table命令修改表结构实例详解

    mysql alter table语句可以修改表的基本结构,例如添加字段.删除字段.添加主键.添加索引.修改字段数据类型.对表重命名等等操作,本文章通过两个简单的实例向大家介绍mysql alter table的使用方法.  实例一:使用ALTER TABLE命令向表中添加字段.修改字段类型以及设置主键. 首先创建一个表,SQL语句如下: mysql> CREATE TABLE myTable( -> ID SMALLINT -> ); 使用desc命令查看表结构: mysql>

  • mysql use命令选择数据库详解

    连接到MySQL服务器后,则需要选择特定的数据库的来工作.这是因为可能有多个数据库可使用在MySQL服务器上. use命令格式: use <数据库名>; 如果我们想要切换到test数据库,那我们可以使用如下命令: mysql> USE test; Database changed 现在,我们已经选择 test 数据库,后续所有操作将在 test 数据库上执行. 注意: 所有的数据库名,表名,表中的字段名称是区分大小写的.所以,我们必须使用适当的名称,在给定任何SQL命令. 另外,use命

  • Python增量循环删除MySQL表数据的方法

    需求场景: 有一业务数据库,使用MySQL 5.5版本,每天会写入大量数据,需要不定期将多表中"指定时期前"的数据进行删除,在SQL SERVER中很容易实现,写几个WHILE循环就搞定,虽然MySQL中也存在类似功能,怎奈自己不精通,于是采用Python来实现 话不多少,上脚本: # coding: utf-8 import MySQLdb import time # delete config DELETE_DATETIME = '2016-08-31 23:59:59' DELE

  • mysql desc(DESCRIBE)命令实例讲解

    mysql desc命令用于查看表结构,是DESCRIBE命令的简写形式. mysql desc命令语法: desc tablename 该命令会显示表哪些信息呢?具体包括: 字段名称(Field) 字段类型(Type) 字段是否为null 字段是否为主键(key) 字段的默认值(default) Extra 实例: mysql> CREATE TABLE employee ( -> ID INT(2) auto_increment primary key, -> First_name

  • mysql 开发技巧之JOIN 更新和数据查重/去重

    主要涉及:JOIN .JOIN 更新.GROUP BY HAVING 数据查重/去重 1 INNER JOIN.LEFT JOIN.RIGHT JOIN.FULL JOIN(MySQL 不支持).CROSS JOIN 这是在网上找到的非常好的一篇博文,图解 join 语句: CODING HORROR-A Visual Explanation of SQL Joins 下图可以很清楚的明白,join 的数据选取范围 [][1] [1]: http://7xs09x.com1.z0.glb.clo

  • mysql查找删除重复数据并只保留一条实例详解

    有这样一张表,表数据及结果如下: school_id school_name total_student test_takers 1239 Abraham Lincoln High School 55 50 1240 Abraham Lincoln High School 70 35 1241 Acalanes High School 120 89 1242 Academy Of The Canyons 30 30 1243 Agoura High School 89 40 1244 Agour

  • 浅谈MySQL和Lucene索引的对比分析

    MySQL和Lucene都可以对数据构建索引并通过索引查询数据,一个是关系型数据库,一个是构建搜索引擎(Solr.ElasticSearch)的核心类库.两者的索引(index)有什么区别呢?以前写过一篇<Solr与MySQL查询性能对比>,只是简单的对比了下查询性能,对于内部原理却没有解释,本文简单分析下两者的索引区别. MySQL索引实现 在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式. M

  • 浅谈Mysql主键索引与非主键索引区别

    目录 什么是索引 主键索引和普通索引的区别 索引具体采用的哪种数据结构 InnoDB使用的B+ Tree的索引模型,那么为什么采用B+ 树?这和Hash索引比较起来有什么优缺点? B+ Tree的叶子节点都可以存哪些东西? 聚簇索引和非聚簇索引,在查询数据的时候有区别? Index Condition Pushdown(索引下推) 查询优化器 关于索引的题 什么是索引 MySql官方索引的定义:索引(Index)是帮助MySql高效获取数据的数据结构,索引的目的在于提高查询效率,类比字典:实际上

  • 浅谈MySql整型索引和字符串索引失效或隐式转换问题

    目录 问题概述 问题重现 问题引申 结论 问题概述 今天在上班时,DBA突然找出来一段sql,表示该sql存在隐式转换,不走索引.经过我们的查看后,发现是类型varchar的字段, 我们使用条件传入了数值型的值,由于担心违反保密协议,在此就不贴图了,由我重现一下类似情况给大家看一下. 问题重现 首先我们先创建一张用户表test_user,其中USER_ID为了效果我们设置为varchar类型且加上唯一索引. CREATE TABLE test_user ( ID int(11) NOT NULL

  • 浅谈Linux vfork与fork简单对比分析

    本文分享了Linux vfork与fork简单对比分析,分享给大家,具体如下: fork相关问题: 一.fork基础了解 fork作用为创建一个子进程,在使用了fork命令后,内核会分配新的内存块和数据结构给子进程,并且将父进程的部分数据结构内容拷贝到子进程,最后再将子进程添加到系统进程列表中,添加完成后fork返回,开始调度. 头文件:#include < unistd.h > 函数原型:pid_t fork( ) 返回值:返回值大于0则当前进程为父进程,等于0代表为子进程,小于零代表创建子

  • 浅谈mysql哪些情况会导致索引失效

    下面有一些培训教学机构的口诀和我个人的一些总结: 为了讲解以下索引内容,我们先建立一个临时的表 test02 CREATE TABLE `sys_user` ( `id` varchar(64) NOT NULL COMMENT '主键', `name` varchar(64) DEFAULT NULL COMMENT '名字', `age` int(64) DEFAULT NULL COMMENT '年龄', `pos` varchar(64) DEFAULT NULL COMMENT '职位

  • 浅谈MySQL为什么会选错索引

    目录 1.引例 2.优化器的逻辑 3.解决办法 1.引例 首先创建一张表,并对字段a,b分别建立索引: create table t ( id int(11) not null, a int(11) default null, b int(11) default null, primary key (id), key a(a), key b(b) )engine=InnoDB; 然后往表中,插入十万行数据,值按整数递增:(1,1,1).(2,2,2).(3,3,3)… delimiter ;;

  • 浅谈mysql的索引设计原则以及常见索引的区别

    索引定义:是一个单独的,存储在磁盘上的数据库结构,其包含着对数据表里所有记录的引用指针. 数据库索引的设计原则: 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引. 那么索引设计原则又是怎样的? 1.选择唯一性索引 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录. 例如,学生表中学号是具有唯一性的字段.为该字段建立唯一性索引可以很快的确定某个学生的信息. 如果使用姓名的话,可能存在同名现象,从而降低查询速度. 2.为经常需要排序.分组和联合操

  • 浅谈Mysql哪些字段适合建立索引

    1 数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考虑用单字段索引代替: A.正确选择复合索引中的主列字段,一般是选择性较好的字段: B .复合索引的几个字段是否经常同

  • 浅谈mysql增加索引不生效的几种情况

    增加索引可以提高查询效率. 增加索引就是增加一个索引文件,存放的是数据的地址,类似与我们文档的目录,在查找过程中可以不用从书的内容查找,直接根据目录对应的页码查找.索引是根据地址查找. 创建索引,索引使用的数据结构也有很多种.常见的是B-tree,哈希等.mysql默认使用的数据库索引是innerDB,innerDB的索引结构是B-tree. 但是在使用过程中哪些情况增加索引无法达到预期的效果呢?下面列举几种常见情况: 假设name age address 都已经加了索引.索引名字分别为 ind

  • 浅谈MySQL如何优雅的做大表删除

    随着时间的推移或者业务量的增长,数据库空间使用率也不断的呈稳定上升状态,当数据库空间将要达到瓶颈的时候,可能我们才会发现数据库有那么一两张的超级大表!他们堆积了从业务开始到现在的全部数据,但是90%的数据都是没有业务价值的,这时候该如何处理这些大表? 既然是没有价值的数据,我们通常一般会选择直接删除或者归档后删除两种,对于数据删除的操作方式来说又可分为两大类: 通过truncate直接删除表中全部数据 通过delete删除表中满足条件记录 一.Truncate操作 从逻辑意义上来讲,trunca

随机推荐