全文搜索之MySQL与ElasticSearch搜索引擎

全文搜索之MySQL与ElasticSearch搜索引擎,第1张

MySQL支持全文索引和搜索功能。在MySQL中可以在CHAR、VARCHAR或TEXT列使用FULLTETXT来创建全文索引。

FULLTEXT索引主要用MATCH()AGAINST语法来实现搜索:

MySQL的全文搜索存在以下局限:

通常来说MySQL自带的全文搜索使用起来局限性比较大,性能和功能都不太成熟,主要适用于小项目,大项目还是建议使用elasticsearch来做全文搜索。

ElasticSearch是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据,以下简称ES。

Elasticsearch 在 Apache Lucene 的基础上开发而成,Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件。Elastic Stack 是适用于数据采集、充实、存储、分析和可视化的一组开源工具。

Elasticsearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elasticsearch 数据中心,再通过分词控制器去将对应的数据分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户。

由于ES是基于RESTfull Web接口的,因此我们直接按照惯例传递JSON参数调用接口即可实现增删改查,并且不需要我们做额外的管理 *** 作就可以直接索引文档,ES已经内置了所有的缺省 *** 作,可以自动帮我们定义类型。

再次执行PUT,会对库中已有的id为1的数据进行覆盖,每修改一次_version字段的版本号就会加1。

默认搜索会返回前10个结果:

返回的几个关键词:

查询字符串搜索,可以像传递URL参数一样传递查询语句。

精确查询:

全文搜索:

以上两种方法都需要考虑数据更改后如何与ES进行同步。

数据库吧,你用什么数据库

mysql可以配合lucene做搜索引擎,还不够大可以用cluster一般你用like没问题,索引要做得好。

如果大家有异议,可以在后面补充。我会随时更新的。

现在大概列出如下:(望各位补充)

1数据库的设计

尽量把数据库设计的更小的占磁盘空间

1)尽可能使用更小的整数类型(mediumint就比int更合适)

2)尽可能的定义字段为notnull,除非这个字段需要null(这个规则只适合字段为KEY的情形)

3)如果没有用到变长字段的话比如varchar,那就采用固定大小的纪录格式比如char(CHAR总是比VARCHR快)

4)表的主索引应该尽可能的短这样的话每条纪录都有名字标志且更高效

5)只创建确实需要的索引。索引有利于检索记录,但是不利于快速保存记录。如果总是要在表的组合字段上做搜索,那么就在这些字段上创建索引。索引的第一部分必须是最常使用的字段如果总是需要用到很多字段,首先就应该多复制这些字段,使索引更好的压缩。

(这条只适合MYISAM引擎的表,对于INNODB则在保存记录的时候关系不大,因为INNODB是以事务为基础的,如果想快速保存记录的话,特别是大批量的导入记录的时候)

6)所有数据都得在保存到数据库前进行处理。

7)所有字段都得有默认值。

8)在某些情况下,把一个频繁扫描的表分成两个速度会快好多。在对动态格式表扫描以取得相关记录时,它可能使用更小的静态格式表的情况下更是如此。

(具体的表现为:MYISAM表的MERGE类型,以及MYISAM和INNODB通用的分区,详情见手册)

9)不会用到外键约束的地方尽量不要使用外键。

2系统的用途

1)及时的关闭对MYSQL的连接。

2)explain复杂的SQL语句。(这样能确定你的SELECT语句怎么优化最佳)

3)如果两个关联表要做比较话,做比较的字段必须类型和长度都一致(在数据庞大的时候建立INDEX)

4)LIMIT语句尽量要跟orderby或者distinct这样可以避免做一次fulltablescan

5)如果想要清空表的所有纪录,建议用truncatetabletablename而不是deletefromtablename

不过有一个问题,truncate不会在事务处理中回滚。因为她要调用createtable语句。

(TruncateTable语句先删除表然后再重建,这个是属于文件界别的,所以自然快N多)

这一点手册上有详细解释:

1329TRUNCATE语法

TRUNCATE[TABLE]tbl_name

TRUNCATETABLE用于完全清空一个表。从逻辑上说,该语句与用于删除所有行的DELETE语句等同,但是在有些情况下,两者在使用上有所不同。

对于InnoDB表,如果有需要引用表的外键限制,则TRUNCATETABLE被映射到DELETE上;否则使用快速删减(取消和重新创建表)。使用TRUNCATETABLE重新设置AUTO_INCREMENT计数器,设置时不考虑是否有外键限制。

对于其它存储引擎,在MySQL51中,TRUNCATETABLE与DELETEFROM有以下几处不同:

删减 *** 作会取消并重新创建表,这比一行一行的删除行要快很多。

删减 *** 作不能保证对事务是安全的;在进行事务处理和表锁定的过程中尝试进行删减,会发生错误。

被删除的行的数目没有被返回。

只要表定义文件tbl_namefrm是合法的,则可以使用TRUNCATETABLE把表重新创建为一个空表,即使数据或索引文件已经被破坏。

表管理程序不记得最后被使用的AUTO_INCREMENT值,但是会从头开始计数。即使对于MyISAM和InnoDB也是如此。MyISAM和InnoDB通常不再次使用序列值。

当被用于带分区的表时,TRUNCATETABLE会保留分区;即,数据和索引文件被取消并重新创建,同时分区定义(par)文件不受影响。

TRUNCATETABLE是在MySQL中采用的一个OracleSQL扩展。

}

6)能使用STOREPROCEDURE或者USERFUNCTION的时候(ROUTINE总是减少了服务器端的开销)

7)在一条insert语句中采用多重纪录插入格式而且使用loaddatainfile来导入大量数据,这比单纯的indert快好多(在MYSQL中具体表现为:INSERTINTOTABLEQVALUES(),(),();)

(还有就是在MYISAM表中插入大量记录的时候先禁用到KEYS后面再建立KEYS,具体表现语句:

ALTERTABLETABLE1DISABLEKEYS;ALTERTABLETABLE1ENABLEKEYS;

而对于INNNODB表在插入前先setautocommit=0;完了后:setautocommit=1;这样效率比较高。)

8)经常OPTIMIZETABLE来整理碎片

9)还有就是date类型的数据如果频繁要做比较的话尽量保存在unsignedint类型比较快。

3系统的瓶颈

1)磁盘搜索

并行搜索,把数据分开存放到多个磁盘中,这样能加快搜索时间

2)磁盘读写(IO)

可以从多个媒介中并行的读取数据。

3)CPU周期

数据存放在主内存中这样就得增加CPU的个数来处理这些数据。

4)内存带宽

当CPU要将更多的数据存放到CPU的缓存中来的话,内存的带宽就成了瓶颈

影响数据检索效率的几个因素

数据检索有两种主要形态。第一种是纯数据库型的。典型的结构是一个关系型数据,比如 mysql。用户通过 SQL 表达出所需要的数据,mysql 把 SQL 翻译成物理的数据检索动作返回结果。第二种形态是现在越来越流行的大数据玩家的玩法。典型的结构是有一个分区的数据存储,最初这种存储就是原始的 HDFS,后来开逐步有人在 HDFS 上加上索引的支持,或者干脆用 Elasticsearc 这样的数据存储。然后在存储之上有一个分布式的实时计算层,比如 Hive 或者 Spark SQL。用户用 Hive SQL 提交给计算层,计算层从存储里拉取出数据,进行计算之后返回给用户。这种大数据的玩法起初是因为 SQL 有很多 ad-hoc 查询是满足不了的,干脆让用户自己写 map/reduce 想怎么算都可以了。但是后来玩大了之后,越来越多的人觉得这些 Hive 之类的方案查询效率怎么那么低下啊。于是一个又一个项目开始去优化这些大数据计算框架的查询性能。这些优化手段和经典的数据库优化到今天的手段是没有什么两样的,很多公司打着搞计算引擎的旗号干着重新发明数据库的活。所以,回归本质,影响数据检索效率的就那么几个因素。我们不妨来看一看。

数据检索干的是什么事情

定位 => 加载 => 变换

找到所需要的数据,把数据从远程或者磁盘加载到内存中。按照规则进行变换,比如按某个字段group by,取另外一个字段的sum之类的计算。

影响效率的四个因素

读取更少的数据

数据本地化,充分遵循底层硬件的限制设计架构

更多的机器

更高效率的计算和计算的物理实现

原则上的四点描述是非常抽象的。我们具体来看这些点映射到实际的数据库中都是一些什么样的优化措施。

读取更少的数据

数据越少,检索需要的时间当然越少了。在考虑所有技术手段之前,最有效果的恐怕是从业务的角度审视一下我们是否需要从那么多的数据中检索出结果来。有没有可能用更少的数据达到同样的效果。减少的数据量的两个手段,聚合和抽样。如果在入库之前把数据就做了聚合或者抽样,是不是可以极大地减少查询所需要的时间,同时效果上并无多少差异呢?极端情况下,如果需要的是一天的总访问量,比如有1个亿。查询的时候去数1亿行肯定快不了。但是如果统计好了一天的总访问量,查询的时候只需要取得一条记录就可以知道今天有1个亿的人访问了。

索引是一种非常常见的减少数据读取量的策略了。一般的按行存储的关系型数据库都会有一个主键。用这个主键可以非常快速的查找到对应的行。KV存储也是这样,按照Key可以快速地找到对应的Value。可以理解为一个Hashmap。但是一旦查询的时候不是用主键,而是另外一个字段。那么最糟糕的情况就是进行一次全表的扫描了,也就是把所有的数据都读取出来,然后看要的数据到底在哪里,这就不可能快了。减少数据读取量的最佳方案就是,建立一个类似字典一样的查找表,当我们找 username=wentao 的时候,可以列举出所有有 wentao 作为用户名的行的主键。然后拿这些主键去行存储(就是那个hashmap)里捞数据,就一捞一个准了。

谈到索引就不得不谈一下一个查询使用了两个字段,如何使用两个索引的问题。mysql的行为可以代表大部分主流数据库的处理方式:

基本上来说,经验表明有多个单字段的索引,最后数据库会选一最优的来使用。其余字段的过滤仍然是通过数据读取到内存之后,用predicate去判断的。也就是无法减少数据的读取量。

在这个方面基于inverted index的数据就非常有特点。一个是Elasticsearch为代表的lucene系的数据库。另外一个是新锐的druid数据库。

效果就是,这些数据库可以把单字段的filter结果缓存起来。多个字段的查询可以把之前缓存的结果直接拿过来做 AND 或者 OR *** 作。

索引存在的必要是因为主存储没有提供直接的快速定位的能力。如果访问的就是数据库的主键,那么需要读取的数据也就非常少了。另外一个变种就是支持遍历的主键,比如hbase的rowkey。如果查询的是一个基于rowkey的范围,那么像hbase这样的数据库就可以支持只读取到这个范围内的数据,而不用读取不再这个范围内的额外数据,从而提高速度。这种加速的方式就是利用了主存储自身的物理分布的特性。另外一个更常见的场景就是 partition。比如 mysql 或者 postgresql 都支持分区表的概念。当我们建立了分区表之后,查找的条件如果可以过滤出分区,那么可以大幅减少需要读取的数据量。比 partition 更细粒度一些的是 clustered index。它其实不是一个索引(二级索引),它是改变了数据在主存储内的排列方式,让相同clustered key的数据彼此紧挨着放在一起,从而在查询的时候避免扫描到无关的数据。比 partition 更粗一些的是分库分表分文件。比如我们可以一天建立一张表,查询的时候先定位到表,再执行 SQL。比如 graphite 给每个 metric 创建一个文件存放采集来的 data point,查询的时候给定metric 就可以定位到一个文件,然后只读取这个文件的数据。

另外还有一点就是按行存储和按列存储的区别。按列存储的时候,每个列是一个独立的文件。查询用到了哪几个列就打开哪几个列的文件,没有用到的列的数据碰都不会碰到。反观按行存储,一张中的所有字段是彼此紧挨在磁盘上的。一个表如果有100个字段,哪怕只选取其中的一个字段,在扫描磁盘的时候其余99个字段的数据仍然会被扫描到的。

考虑一个具体的案例,时间序列数据。如何使用读取更少的数据的策略来提高检索的效率呢?首先,我们可以保证入库的时间粒度,维度粒度是正好是查询所需要的。如果查询需要的是5分钟数据,但是入库的是1分钟的,那么就可以先聚合成5分钟的再存入数据库。对于主存储的物理布局选择,如果查询总是针对一个时间范围的。那么把 timestamp 做为 hbase 的 rowkey,或者 mysql 的 clustered index 是合适。这样我们按时间过滤的时候,选择到的是一堆连续的数据,不用读取之后再过滤掉不符合条件的数据。但是如果在一个时间范围内有很多中数据,比如1万个IP,那么即便是查1个IP的数据也需要把1万个IP的数据都读取出来。所以可以把 IP 维度也编码到 rowkey 或者 clustered index 中。但是假如另外还有一个维度是 OS,那么查询的时候 IP 维度的 rowkey 是没有帮助的,仍然是要把所有的数据都查出来。这就是仅依靠主存储是无法满足各种查询条件下都能够读取更少的数据的原因。所以,二级索引是必要的。我们可以把时间序列中的所有维度都拿出来建立索引,然后查询的时候如果指定了维度,就可以用二级索引把真正需要读取的数据过滤出来。但是实践中,很多数据库并不因为使用了索引使得查询变快了,有的时候反而变得更慢了。对于 mysql 来说,存储时间序列的最佳方式是按时间做 partition,不对维度建立任何索引。查询的时候只过滤出对应的 partition,然后进行全 partition 扫描,这样会快过于使用二级索引定位到行之后再去读取主存储的查询方式。究其原因,就是数据本地化的问题了。

[page]

数据本地化

数据本地化的实质是软件工程师们要充分尊重和理解底层硬件的限制,并且用各种手段规避问题最大化利用手里的硬件资源。本地化有很多种形态

最常见的最好理解的本地化问题是网络问题。我们都知道网络带宽不是无限的,比本地磁盘慢多了。如果可能尽量不要通过网络去访问数据。即便要访问,也应该一次抓取多一些数据,而不是一次搞一点,然后搞很多次。因为网络连接和来回的开销是非常高的。这就是 data locality 的问题。我们要把计算尽可能的靠近数据,减少网络上传输的数据量。

这种带宽引起的本地化问题,还有很多。网络比硬盘慢,硬盘比内存慢,内存比L2缓存慢。做到极致的数据库可以让计算完全发生在 L2 缓存内,尽可能地避免频繁地在内存和L2之间倒腾数据。

另外一种形态的问题化问题是磁盘的顺序读和随机读的问题。当数据彼此靠近地物理存放在磁盘上的时候,顺序读取一批是非常快的。如果需要随机读取多个不连续的硬盘位置,磁头就要来回移动从而使得读取速度快速下降。即便是 SSD 硬盘,顺序读也是要比随机读快的。

基于尽可能让数据读取本地化的原则,检索应该尽可能地使用顺序读而不是随机读。如果可以的话,把主存储的row key或者clustered index设计为和查询提交一样的。时间序列如果都是按时间查,那么按时间做的row key可以非常高效地以顺序读的方式把数据拉取出来。类似地,按列存储的数据如果要把一个列的数据都取出来加和的话,可以非常快地用顺序读的方式加载出来。

二级索引的访问方式典型的随机读。当查询条件经过了二级索引查找之后得到一堆的主存储的 key,那么就需要对每个 key 进行一次随机读。即便彼此仅靠的key可以用顺序读做一些优化,总体上来说仍然是随机读的模式。这也就是为什么时间序列数据在 mysql 里建立了索引反而比没有建索引还要慢的原因。

为了尽可能的利用顺序读,人们就开始想各种办法了。前面提到了 mysql 里的一行数据的多个列是彼此紧靠地物理存放的。那么如果我们把所需要的数据建成多个列,那么一次查询就可以批量获得更多的数据,减少随机读取的次数。也就是把之前的一些行变为列的方式来存放,减少行的数量。这种做法的经典案例就是时间序列数据,比如可以一分钟存一行数据,每一秒的值变成一个列。那么行的数量可以变成之前的1/60。

但是这种行变列的做法在按列存储的数据库里就不能直接照搬了,有些列式数据库有column family的概念,不同的设置在物理上存放可能是在一起的也可能是分开的。对于 Elasticsearch 来说,要想减少行的数量,让一行多pack一些数据进去,一种做法就是利用 nested document。内部 Elasticsearch 可以保证一个 document 下的所有的 nested document是物理上靠在一起放在同一个 lucene 的 segment 内。

网络的data locality就比较为人熟知了。map reduce的大数据计算模式就是利用map在数据节点的本地把数据先做一次计算,往往计算的结果可以比原数据小很多。然后再通过网络传输汇总后做 reduce 计算。这样就节省了大量网络传输数据的时间浪费和资源消耗。现在 Elasticsearch 就支持在每个 data node 上部署 spark。由 spark 在每个 data node 上做计算。而不用把数据都查询出来,用网络传输到 spark 集群里再去计算。这种数据库和计算集群的混合部署是高性能的关键。类似的还有 storm 和 kafka 之间的关系。

网络的data locality还有一个老大难问题就是分布式大数据下的多表join问题。如果只是查询一个分布式表,那么把计算用 map reduce 表达就没有多大问题了。但是如果需要同时查询两个表,就意味着两个表可能不是在物理上同样均匀分布的。一种最简单的策略就是找出两张表中最小的那张,然后把表的内容广播到每个节点上,再做join。复杂一些的是对两个单表做 map reduce,然后按照相同的 key 把部分计算的结果汇集在一起。第三种策略是保证数据分布的方式,让两张表查询的时候需要用到的数据总在一起。没有完美的方案,也不大可能有完美的方案。除非有一天网络带宽可以大到忽略不计的地步。

更多的机器

这个就没有什么好说的了。多一倍的机器就多一倍的 CPU,可以同时计算更多的数据。多一倍的机器就多一倍的磁头,可以同时扫描更多的字节数。很多大数据框架的故事就是讲如何如何通过 scale out解决无限大的问题。但是值得注意的是,集群可以无限大,数据可以无限多,但是口袋里的银子不会无限多的。堆机器解决问题比升级大型机是要便宜,但是机器堆多了也是非常昂贵的。特别是 Hive 这些从一开始就是分布式多机的检索方案,刚开始的时候效率并不高。堆机器是一个乘数,当数据库本来单机性能不高的时候,乘数大并不能起到决定性的作用。

更高效的计算和计算实现

检索的过程不仅仅是磁盘扫描,它还包括一个可简单可复杂的变换过程。使用 hyperloglog,count min-sketch等有损算法可以极大地提高统计计算的性能。数据库的join也是一个经常有算法创新的地方。

计算实现就是算法是用C++实现的还是用java,还是python实现的。用java是用大Integer实现的,还是小int实现的。不同的语言的实现方式会有一些固定的开销。不是说快就一定要C++,但是 python 写 for 循环是显然没有指望的。任何数据检索的环节只要包含 python/ruby 这些语言的逐条 for 循环就一定快不起来了。

结论

希望这四点可以被记住,成为一种指导性的优化数据检索效率的思维框架。无论你是设计一个mysql表结构,还是优化一个spark sql的应用。从这四个角度想想,都有哪些环节是在拖后腿的,手上的工具有什么样的参数可以调整,让随机读变成顺序读,表结构怎么样设计可以最小化数据读取的量。要做到这一点,你必须非常非常了解工具的底层实现。而不是盲目的相信,xx数据库是最好的数据库,所以它一定很快之类的。如果你不了解你手上的数据库或者计算引擎,当它快的时候你不知道为何快,当它慢的时候你就更加无从优化了。

Java中的搜索插件称为搜索引擎(Search Engine),它是一种可以在网络上搜索内容的软件,可以帮助用户快速找到所需要的信息。搜索引擎使用爬虫来抓取网页中的内容,然后将网页内容组织成一个数据库,最后用户可以根据自己的需要使用搜索引擎检索相关信息。

Java中的搜索引擎有很多,其中最流行的搜索引擎包括Google、Bing、Yahoo、Baidu等,它们都是大型的搜索引擎,可以搜索到全球各地的信息。此外,还有一些小型的搜索引擎,如DuckDuckGo、Yandex等,它们可以搜索到更加准确的信息,但是覆盖的范围较小。

Java中的搜索引擎可以帮助用户更加快捷的搜索到所需要的信息,而且它们还可以根据用户的搜索关键词自动推荐更加准确的信息。另外,它们还可以搜索到网络上的视频、音乐等多媒体内容,以及新闻、等网络资源。

由于需要提升项目的搜索质量,最近研究了一下Elasticsearch,一款非常优秀的分布式搜索程序。最开始的一些笔记放到github,这里只是归纳总结一下。

首先,为什么要使用Elasticsearch?最开始的时候,我们的项目仅仅使用MySQL进行简单的搜索,然后一个不能索引的like语句,直接拉低MySQL的性能。后来,我们曾考虑过sphinx,并且sphinx也在之前的项目中成功实施过,但想想现在的数据量级,多台MySQL,以及搜索服务本身HA,还有后续扩容的问题,我们觉得sphinx并不是一个最优的选择。于是自然将目光放到了Elasticsearch上面。

根据官网自己的介绍,Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard的方式保证数据安全,并且提供自动resharding的功能,加之github等大型的站点也采用 Elasticsearch作为其搜索服务,我们决定在项目中使用Elasticsearch。

对于Elasticsearch,如果要在项目中使用,需要解决如下问题:

索引,对于需要搜索的数据,如何建立合适的索引,还需要根据特定的语言使用不同的analyzer等。

搜索,Elasticsearch提供了非常强大的搜索功能,如何写出高效的搜索语句?

数据源,我们所有的数据是存放到MySQL的,MySQL是唯一数据源,如何将MySQL的数据导入到Elasticsearch?

对于1和2,因为我们的数据都是从MySQL生成,index的field是固定的,主要做的工作就是根据业务场景设计好对应的mapping以及search语句就可以了,当然实际不可能这么简单,需要我们不断的调优。

而对于3,则是需要一个工具将MySQL的数据导入Elasticsearch,因为我们对搜索实时性要求很高,所以需要将MySQL的增量数据实时导入,笔者唯一能想到的就是通过row based binlog来完成。而近段时间的工作,也就是实现一个MySQL增量同步到Elasticsearch的服务。

Lucene

Elasticsearch底层是基于Lucene的,Lucene是一款优秀的搜索lib,当然,笔者以前仍然没有接触使用过。:-)

Lucene关键概念:

Document:用来索引和搜索的主要数据源,包含一个或者多个Field,而这些Field则包含我们跟Lucene交互的数据。

Field:Document的一个组成部分,有两个部分组成,name和value。

Term:不可分割的单词,搜索最小单元。

Token:一个Term呈现方式,包含这个Term的内容,在文档中的起始位置,以及类型。

Lucene使用Inverted index来存储term在document中位置的映射关系。

譬如如下文档:

Elasticsearch Server 10 (document 1)

Mastring Elasticsearch (document 2)

Apache Solr 4 Cookbook (document 3)

使用inverted index存储,一个简单地映射关系:

Term

Count

Docuemnt

10 1 <1>

4 1 <3>

Apache 1 <3>

Cookbook 1 <3>

Elasticsearch 2 <1><2>

Mastering 1 <2>

Server 1 <1>

Solr 1 <3>

对于上面例子,我们首先通过分词算法将一个文档切分成一个一个的token,再得到该token与document的映射关系,并记录token出现的总次数。这样就得到了一个简单的inverted index。

Elasticsearch关键概念

要使用Elasticsearch,笔者认为,只需要理解几个基本概念就可以了。

在数据层面,主要有:

Index:Elasticsearch用来存储数据的逻辑区域,它类似于关系型数据库中的db概念。一个index可以在一个或者多个shard上面,同时一个shard也可能会有多个replicas。

Document:Elasticsearch里面存储的实体数据,类似于关系数据中一个table里面的一行数据。

document由多个field组成,不同的document里面同名的field一定具有相同的类型。document里面field可以重复出现,也就是一个field会有多个值,即multivalued。

Document type:为了查询需要,一个index可能会有多种document,也就是document type,但需要注意,不同document里面同名的field一定要是相同类型的。

Mapping:存储field的相关映射信息,不同document type会有不同的mapping。

对于熟悉MySQL的童鞋,我们只需要大概认为Index就是一个db,document就是一行数据,field就是table的column,mapping就是table的定义,而document type就是一个table就可以了。

Document type这个概念其实最开始也把笔者给弄糊涂了,其实它就是为了更好的查询,举个简单的例子,一个index,可能一部分数据我们想使用一种查询方式,而另一部分数据我们想使用另一种查询方式,于是就有了两种type了。不过这种情况应该在我们的项目中不会出现,所以通常一个index下面仅会有一个 type。

在服务层面,主要有:

Node: 一个server实例。

Cluster:多个node组成cluster。

Shard:数据分片,一个index可能会存在于多个shards,不同shards可能在不同nodes。

Replica:shard的备份,有一个primary shard,其余的叫做replica shards。

Elasticsearch之所以能动态resharding,主要在于它最开始就预先分配了多个shards(貌似是1024),然后以shard为单位进行数据迁移。这个做法其实在分布式领域非常的普遍,codis就是使用了1024个slot来进行数据迁移。

因为任意一个index都可配置多个replica,通过冗余备份的方式保证了数据的安全性,同时replica也能分担读压力,类似于MySQL中的slave。

Restful API

Elasticsearch提供了Restful API,使用json格式,这使得它非常利于与外部交互,虽然Elasticsearch的客户端很多,但笔者仍然很容易的就写出了一个简易客户端用于项目中,再次印证了Elasticsearch的使用真心很容易。

Restful的接口很简单,一个url表示一个特定的资源,譬如/blog/article/1,就表示一个index为blog,type为aritcle,id为1的document。

而我们使用>

一.           创建索引

1.一般创建索引的核心步骤

(1) 创建索引写入对象IndexWriter:

IndexWriter indexWriter = new IndexWriter(INDEX_STORE_PATH,new StandardAnalyzer(),create);

参数说明:INDEX_STORE_PATH:  索引文件存放路径

new StandardAnalyzer(): 分词工具

create: 此参数为Boolean型,true表示重新创建整个索引, false 表示增量式创建索引。

(2).创建文档模型,并用IndexWriter对象写入

Document doc = new Document();

Field field1 = new Field(fieldName1, fieldValue ,  FieldStoreYES, FieldIndexTOKENIZED);

docadd(field1);

Field field2 = new Field(fieldName2, fieldValue ,  FieldStoreYES, FieldIndexTOKENIZED);

docadd(field2);

……

indexWriteraddDocument(doc);

indexWriterclose();

参数说明:

Document :负责搜集数据源,它可以从不同的物理文件提取数据并放入同一个Document 中或从一个物理文件中提取出不同的数据并放入同一个Document中。

如下图所示

                 

Field :用来表示不同的数据源

fieldName1: 表示field名称

fieldValue:  表示field 的值

FieldStoreYES,:表示是否在索引文件中完整的存储该值。

在创建索引时,有些内容需要以摘要的形式完整地或以片段的方式显示在页面上,来便于用户查找想要的记录,那么就应该选择存储,如果不需要完整或片段的显示就不需要存储。

FieldIndexTOKENIZED :表示是否索引和分词。

只要是需要当作关键字让用户查找的字段就需要建立索引。

在建立索引的过程中,如果像文章标题、文章内容这样的Field, 一般是靠用户输入几个关键字来查询的,就应该选择分词。

如果需要用户输入完整字符也就是精确查找才能查询到的,例如:beanName,就可以不分词。

Document最直观的理解方式:

Document就相当于我们平台中的一个普通javaBean,,而Field 就是javaBean中的一个属性。lucene搜索的机制就是靠搜索指定的Field的值 ,来得到含有要搜索内容的Document 集合,所以问题的关键在于如何组织Document

2.结合平台创建索引的思路

(1) 经分析搜索元素应该由如下内容组成(Document的属性)

(2) 数据库数据转化为Document 的构造过程:

JavaBean / Attachment     →   (Temp Object) BaseData  →   (Finally Object) Document

分析:

要建立索引的源数据分为两大部分:一个是数据库数据 BeanData ,另一个是附件数据 FileData , 这样可以建立一个抽象类 BaseData , 来存放它们共有的属性。同时为了管理这些相应的数据,在相同的等级结构上,建立了相应的管理类(xxxDataManager) ,对这些数据类的 *** 作(建立或删除索引)进行管理,并用一个工厂类(DataManagerFactory)来创建所需要的管理类,IndexHelper用来充当整个索引模块对外的接口,为了实现一些与平台特定的业务,特用SupportManager来提供一些额外的业务支持,索引模块代码结构如下图所示。

二.搜索索引

1   lucene 搜索的核心步骤:

String[]  fields  =  {“title”, “summary”,……};     //要查找的field范围

BooleanClauseOccur[]   flags  =  {BooleanClauseOccurSHOULD, BooleanClauseOccur MUST ,……};

Query  query = MultiFieldQueryParserparse(queryStr, fields,flags,new StandardAnalyzer());

Hits  hits  =  new  IndexSearcher(INDEX_STORE_PATH)search(query);

for (int i = 0;i < hitsLength ; i++)

{

Document doc = hitsdoc(i);

String title = docget(“title”);

String summary = docget(“summary”);

// 搜索出来的结果高亮显示在页面上

if (title != null) {

TokenStream tokenStream = analyzertokenStream(“title”,new StringReader(title));

String highlighterValue = highlightergetBestFragment(tokenStream, title) ;

if(highlighterValue != null){

title = highlighterValue ;

}

//loginfo("SearchHelpersearchtitle="+title);

}

if(summary!= null){

TokenStream tokenStream = analyzertokenStream(“summary”,new StringReader(summary));

String highlighterValue = highlightergetBestFragment(tokenStream, creator) ;

if(highlighterValue != null){

summary = highlighterValue ;

}

//loginfo("SearchHelpersearch summary ="+ summary);

}

}

2.结合平台构造搜索模块

PageData 类用来存放检索结果集数据。

PageInfo 类用来存放页面相关信息例如,PageData对象集合、总记录个数、每一页的记录数、 总页面数量等等。

SearchHelper用来充当整个搜索模块的对外接口。

三.为平台组件添加索引的步骤(以知识中心为例)

1.在comcscecoasearchengineextendmodule 目录下添加一个新的package

例如:comcscecoasearchengineextendmoduleresourcestore

2.在新的目录下建立data package 并建立相应的数据类,并使这个数据类继承BeanData。

例如:

package comcscecoasearchengineextendmoduleresourcestoredata

public class ResourceStoreBeanData extends BeanData{

}

3 与data package 同一级目录建立manager package 并建立相应管理类,并使这个管理类继承BeanDataManager

例如:

comcscecoasearchengineextendmoduleresourcestoremanager

public class ResourceStoreBeanDataManagerImpl extends BeanDataManager{

}

4.以管理员的身份登陆OA后,在菜单中找到“索引模块管理”链接,将相应信息添加完成后,便可以在List 页面 点击“创建索引”对该模块的数据进行索引的建立,建立完成后便可以进行查询。

discuz缺点:

不能批量编辑用户,如不能批量移动到指定用户组。

帖间随机广告代码不能“每条随机广告一行,用回车分开”。

没有“沉底”功能。

没有道具,用户不能自行改名。

不能批量删除某个用户若干天内的所有回复或主题。

优点:

discuz免费。

使用快速回复框时不会说“您提交的参数错误。请仔细阅读论坛帮助文件,确保您有相应的 *** 作权限。”

不会因为css代码下载不完全而导致版面格式混乱。

积分、用户组、发帖数级别设置更灵活。

比asp+access版论坛更节约空间。转换后数据库大小只有动网Access数据库的三分之一。

速度快。

可以匿名发帖。

可以设置昵称。

可以直接使用纯真IP库。

后台设置简单明了。

可以“用户栏目定制”。

可以“合并用户”。

可以在后台直接更改用户名。

页面上下都有页码,方便翻页。

可以前台批量 *** 作。

不会多重引用。

用户编辑帖子可以自己更改主题分类。

phpwind特点:

独创、成熟

phpwind独创的技术架构和程序设计,获得国家专利认证,从根基着手塑造快速、稳健、可扩展的论坛程序。7年砺炼,在用户需求基础上进行不断创新与完善,无论企业还是娱乐,全面满足论坛建设。

安全

高效防护算法、程序监控技术、注入式入侵过滤技术、防CC攻击技术、安全验证机制构筑的站点安全体系,将风险防患于未然

快捷、稳定、高效

核心参数的合理配置、负载均衡处理技术、数据库分表技术、文件读写稳定性算法帮助您最大限度地节省硬件资源,保障站点在服务器繁忙时快速稳定运行。

不仅为站长而设计

站长建站,影响的却是网民。PHPWind“不仅为站长而设计”的设计理念,将人性化融入论坛功能的点点滴滴。清晰的用户等级与权限设置,便捷完整的论坛功能与管理 *** 作,详细地日志与统计记录,集成交易与支付,轻松备份/恢复数据给予站长和每一位会员良好的用户体验。

可扩展

开源、规范化的代码编写、标准化的插件接口和清晰简洁的模板体系理念,让站点无限延伸至未来。

完美整合

PHPWind所取得的同业合作,为您提供更多建站软件选择和无间隙的软件服务,帮助站点多面需求。查看详情

丰富的第三方资源

近千款风格,近百款插件,仍在不断丰富。查看详情

*** 作系统要求

PHPWind具备跨平台特性,支持 Linux/Unix及Windows 2000/2003/ XP等 *** 作系统。

针对上述 *** 作系统,我们队软件做了大量的测试和实地检验,保证PHPWind可以安全稳定的运行,但您仍需做好服务器 *** 作系统的安全防备措施,例如Windows用户需更改MYSQL的初始密码,使用较新的稳定的软件版本等。

语言及数据库支撑环境要求可用的 web 服务器(如 Apache、Zeus、IIS 等)

php 52x及以上

MySQL51x及以上

Zend Optimizer 3x及以上(可以不用)

如果您租用虚拟主机,请咨询虚拟主机提供商,您的空间服务器是否已安装了上述软件。

由于

PHPWind的数据表具有前缀设计,因此通常情况下可以将PHPWind与其他软件安装在同一个数据库中,或采用不同的前缀名在同一个数据库中安装多个PHPWind

论坛而不产生冲突。

您的 MySQL 数据库账号应当拥有 CREATE、DROP、ALTER 等执行权限。

安装可能用到的工具软件

PHPWind论坛系统的安装使用非常简单,因此您可能需要用到的工具软件也非常少。

1)您可能需要一个FTP客户端软件来上传PHPWind程序文件;

2)PHPWind是一个开源系统,您可以按自己的想法来修改程序代码,从而得到自己想要的社区呈现效果,或者社区系统某些配置文件的参数需要修改,此时您可能需要一个简单的文本文件编辑软件,一般 *** 作系统自带的如Windows的记事本或第三方软件如EditPlus,都可以。

Spacebuilder - 特色

1产品架构:采用“平台+应用”的设计思想,可以方便及快捷的扩展新的应用;

2 Ajax:表现层采用大量的jQuery技术,使用户获得良好的用户体验;

3 aspnet mvc:采用微软最新的aspnet

mvc(c#)进行表现层开发。彻底消除了ViewState可以万全控制html代码的输出;Controller与View完全分离充分避免了皮肤机制的性能损失;原生态的url

routing更利于urlrewrite。

4 数据存储:目前采用SQL Server2000/SQLServer2005/SQL

Server2008进行数据存储,由于数据访问层采用了Provide设计模式所以可以方便的移植到其他关系型数据库;

5 数据访问:为了提升性能以及数据库安全性,数据访问全部采用存储过程;

6 高效缓存:使用可分区的缓存技术,并且具有优秀的缓存过期策略,使站点获得性能提升的同时,保证缓存与数据库数据同步;

7 全文检索:以Lucene为核心实现全文检索功能,实现索引库自动更新,并提供企业级的全文检索性能;

8 扩展机制:优秀的皮肤实现机制,使客户可以方便的对现有皮肤进行修改或开发新的皮肤;核心模块提供了全局事件,客户可以开发自己的Module;

9 配置文件:配置信息均采用XML格式,易于管理设置;

10 所有页面均采用xhtml+css进行设计,符合web标准,兼容所有主流浏览器(IE、Firefox),降低页面流量,提高加载速度;

以上就是关于全文搜索之MySQL与ElasticSearch搜索引擎全部的内容,包括:全文搜索之MySQL与ElasticSearch搜索引擎、千万量级数据库设计能力是指什么、影响数据检索效率的几个因素等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/9769272.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-01
下一篇 2023-05-01

发表评论

登录后才能评论

评论列表(0条)

保存