上一篇的配置说明主要是说明solrconfig.xml配置中的查询部分配置,在solr的功能中另一个重要的功能是建索引,这是提供快速查询的核心。java
按照Solr学习之一所述关于搜索引擎的原理中说明了创建索引,其实就须要通过分词组件处理,语言组件处理最后创建成一个倒排索引表,linux
经过这个索引表,来进行查询,本篇就是说明solr如何创建索引的也便是solrconfig.xml中关于更新索引的部分,另外因为创建索引须要涉及到web
schemal.xml相关内容定义,这里面也一块儿说明。数据库
在solr的基本概念中,有个文档的概念。solr在创建索引的时候,是按照文档的方式添加到索引中的。那么咱们如何设计一个文档那。apache
举个简单的例子,假设你对图书馆的图书创建索引,你是按照每一个图书的信息来创建索引,仍是按照全部图书的全部章节来创建索引那,json
这其实要看你的应用程序要求。若是你的客户要求查询的时候,出来是一条条书目的信息,那么就比较适合按照一本本图书这个级别来创建windows
文档,那可能涉及到的文档字段(field) 有书名、做者、出版社、价格等基本信息。数组
那在搜索的时候,若是用户想查询的是哪些书包含某些内容的信息是查不到的。 若是 想查到,可能要对书的每篇文章都做为一个索引信息,服务器
固然你要承受大索引的数据了。并发
因此简单的总结来讲,文档定义为一个个客户想查询到的信息粒度比较合适。
文档是由字段组成的,相似于数据库的一条记录,在数据库中记录通常都有一个主键,文档也相似,文档经过惟一键id来在分布式部署的时候路由到
相应的shard上去,也便于索引确认惟一的文档,若是你两次发的id是同样的,solr将会覆盖上一个文档。
solr的字段定义通常形如:
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
若是是惟一键,则配置为: <uniqueKey>id</uniqueKey>
经过上面字段的定义咱们看到字段有几个重要属性:
name: 标示这个字段的名称;
type:标示字段的类型,注意这里面的类型不一样于java的类型,是solr本身定义的,经过这个类型solr知道这个字段
该是否分词等,注意定义为string类型通常是不作分词的,作分词能够定义为text*类型。
indexed:定义文档是否被索引,这个能够理解为咱们是否在此字段上进行搜索,若是在,这个定义为true,不然为false;另外若是你须要在这个字段上进行排序、分组、切片、 提供建议、或者执行一个查询函数,你也须要定义这个索引为true。
举个例子,你假设定义一个test1字段,这个值为true,你就能够能够经过q=test1:foo这种方式来进行查询;若是定义为false,
即便你的文档中有这个字段并且值就是foo,那么也查不到数据。
stored :定义这个字段内容是否能够做为返回结果的一部分,若是是,则定义为true,不然为false。
举个例子,你若是想提取test1字段能够经过制定fl=test1来提取,若是配置为true则能够提取到这个值,若是是false则提取不到。
这个indexed和stored能够都配置为false,通常在你动态字段中定义,通常用来忽略某个值。
docValues:表示此域是否须要添加一个 docValues 域,这对 facet 查询, group 分组,排序, function 查询有好处,尽管这个属性不是必须的,
但他能加快索引数据加载,对 NRT 近实时搜索比较友好,且更节省内存,但它也有一些限制,好比当前docValues 域只支持 strField,UUIDField,
Trie*Field 等类型,且要求域的域值是单值并且是必须有值的或者有默认值的,不能是多值域
solrMissingFirst/solrMissingLast:在查询结果排序的时候,若是这个字段没有值的话,这个文档是放在查询结果有值字段的前面/后面。
multValued: 这个字段是否存在多个值,若是存在多个值设置为true,sorlj用add而不是set来设置这个字段。
omitNorms: 此属性若设置为 true ,即表示将忽略域值的长度标准化,忽略在索引过程当中对当前域的权重设置,且会节省内存。
只有全文本域或者你须要在索引建立过程当中设置域的权重时才须要把这个值设为 false, 对于基本数据类型且不分词的域(field)
如intFeild,longField,StrField 等默认此属性值就是 true, 不然默认就是 false.
这个若是设置为false,能够起到增强短文章的boost做用,好比你有一个100个单词的文档含有这个词,还有一个1000个单词的文档
一样只含有一次这个值,那么在比较相关性的时候,100个单词的文章相关性更强。若是你的文章长度大小差很少能够不设置这个值。
required: 添加文档时,该字段必须存在,相似于数据库的非空。
termVectors: 设置为 true 即表示须要为该 field 存储项向量信息,当你须要MoreLikeThis 功能时,则须要将此属性值设为 true ,这样会带来一些性能提高。
说明下,在文档和查询语句进行匹配的时候,须要用到相关度判断,就是经过单词向量来处理的。
这个设置为true会记录词出现的频次和位置,能够提取出来。
termPositions: 是否termVectors中在存储 Term 的起始位置信息,这会增大索引的体积,但高亮功能须要依赖此项设置,不然没法高亮,
能够经过positions提取出来,目前没测试过。
termOffsets: 设置为true则记录词的偏移量。
default: 若是这个字段没有指定值的时候,使用的默认值。
所谓的动态字段,是为了简化字段的定义,设想下一个文档的字段有几十个,定义起来很是麻烦,有不少属性是相同的,solr支持经过字段名匹配的字段能够通用一个配置,
这就是动态字段.
举个例子:
<dynamicField name="*_txt" type="text_general" indexed="true" stored="true" multiValued="true"/>
标示,以_txt结尾的字段都匹配这个配置,其余属性同通常字段。这样能够不用常常修改这个字段。
复制字段的做用就是能够将多个字段复制到一个字段中,这样便于搜索,固然你要保证两个字段间类型的兼容性。
注意
1)只能从定义其余其余字段复制到复制字段,不能从复制 字段到复制字段,能够限制复制多少个字段长度。
2) 通常复制字段须要定义为多值存储。
3)复制字段通常不用再存储了,存储无心义。
定义以下:
<copyField source="head" dest="teaser" maxChars="300"/>
一个总体定义:
<dynamicField name="*_company" type="string" indexed="true" stored="true"/>
<dynamicField name="*_entity" type="string" indexed="true" stored="true"/>
<field name="database_ids" type="string" indexed="true" stored="false"/>
复制字段定义以下:
<copyField source="*_company" dest="database_ids"/>
<copyField source="*_entity" dest="database_ids"/>
有6种基本类型都是不分词的:
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />
说明: 不被分词,支持docvalue,不能是多值,不能为空或有默认值。
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
说明: 只能取true或false
<fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>
说明:一、支持docvalue字段,一样不能为空或者有默认值不能为多值。
二、若是支持快速范围搜索的话,建议用tint等。
三、precisionStep 这个是字段划分几个范围,这个值设置越小,划分的范围就多,范围查询速度更快,更占索引空间。
positionIncrementGap: 这个是多值使用,多个值之间间隔,经常使用语复制字段防止误匹配。
<fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>
说明: 日期类型,支持格式:1995-12-31T23:59:59Z,默认存储的是UTC-0区的时间。
北京时间存储的时候通常须要+8个小时存储。
<field name=”timestamp” type=”date” indexed=”true” stored=”true” default=”NOW+8HOUR” multiValued=”false”/>
<fieldtype name="binary" class="solr.BinaryField"/>
说明:通过base64编码的二进制数据,查询的时候,查询的内容也要通过base64编码来查询。
<fieldType name="random" class="solr.RandomSortField" indexed="true" />
说明:随机数字段,须要伪随机数排序的时候使用。
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
xxx
</fieldType>
说明: text字段,须要分词的定义为这种类型,xxx标示中间还有不少内容,好比定义分词器等。
按照前面文章所述,咱们知道solr是能够经过HTTP的接口发送JSON或者XML或者Javabin(一种二进制+文本混合模式)的http报文来进行solr的索引的更新的。
经过xml的格式以下,在json格式中,多个文档用数组形式每一个文档必须是json报文格式,多值也采用文档形式。另外solr还能够经过DIH(Data Import Handler)的
方式来从数据库、json格式的报文或者xml报文导入数据。还能够从二进制文件,好比PDF、好比MS office提取数据。
<add> <doc> <field name="id">1</field> <field name="test_s">hello</field> <field name="type_s">post</field> <field name="mutilvalue_s">value1</field> <field name="mutilvalue_s">value2</field> </doc> <doc> <field name="id">2</field> <field name="test_s">hello2</field> <field name="type_s">get</field> <field name="mutilvalue_s">value3</field> <field name="mutilvalue_s">value4</field> </doc> </add>
更新索引在solrconfig.xml中的配置以下:
<updateHandler class="solr.DirectUpdateHandler2"> <updateLog> <str name="dir">${solr.ulog.dir:}</str> </updateLog> <autoCommit> <maxTime>60000</maxTime> <maxDocs>30000</maxDocs> <openSearcher>false</openSearcher> </autoCommit> <autoSoftCommit> <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime> </autoSoftCommit> <!-- <listener event="postCommit" class="solr.RunExecutableListener"> <str name="exe">solr/bin/snapshooter</str> <str name="dir">.</str> <bool name="wait">true</bool> <arr name="args"> <str>arg1</str> <str>arg2</str> </arr> <arr name="env"> <str>MYVAR=val1</str> </arr> </listener> --> </updateHandler>
在配置中有autoCommit 相关配置,在说明以前先说下commit,通常说的commit是指硬提交,目的是使全部在这个collection上的未提交的内容刷新到可持久化设备上去,
而且打开新的搜索器,预热搜索器,使搜索可见。
简单来讲软提交softCommit的目的是为了使搜索可见,并不保证数据被持久化,在服务器崩溃的时候可能数据会丢失。
这里面的自动提交,默认是硬提交,新的搜索器是否打开是经过:openSearcher 控制的,若是为true就打开,那么这时候提交的数据可见,这里面能够配置固定的诗句进行
自动提交(<maxTime>60000</maxTime> 单位是毫秒)或者未提交的文档数量( <maxDocs>30000</maxDocs>)达到多少进行提交,两个都配置的时候,第一个达到的起效。
自动软提交的配置相似。
整个更新的流程:
说明:
一、客户端经过http发送xml、json或javabin格式文档给jetty服务器。
二、jetty服务器根据地址路由到solr这个web程序。
三、solr根据/update 经过solrconfig.xml的配置:
<requestHandler name="/update" class="solr.UpdateRequestHandler">
</requestHandler>
来选定处理的handler。
四、更新的handler根据schemal.xml配置的文档字段类型进行具体的分析处理。
五、将添加文档操做记录到事务日志中。
六、将数据记录到事务日志后,只要成功了,不管是否作了commit均可以返回了,即便后面没有作commit,服务器有问题,在启动的时候仍然能够经过事务日志进行恢复。
事务日志是保障索引数据不丢失的策略之一,只要你更新索引,好比在solrJ中使用了solrJ服务的相关add方法,虽然没提交数据,可是这些数据仍然被记录在了solr的事务日志中,前文配置为:
<updateLog>
<str name="dir">${solr.ulog.dir:}</str>
</updateLog>
事务日志的做用:
一、事务日志在客户端未发送commit并且没有进行自动提交的时候,若是客户端崩溃了,能够经过事务日志进行恢复。
二、事务日志在实时搜索中被使用,经过文档id查询,自动更新时候也是用这个文档。
三、事务日志在solrColud部署中,由副本更新时候使用。
事务日志内容:
我去看过,基本上是记录文档的信息,操做的基本信息。事务日志在提交前都保留,一旦进行了硬提交,老的事务日志会删除,新的事务日志会创建起来。
事务日志的大小:
事务日志的大小,要看你的提交的频率,若是你设置了自动提交,那么自动提交的最大文档数量,也就是一个事务日志的最大大小,更大的事务日志可能
会形成在服务器恢复的时候耗费更长的时间来恢复。
solr中不存在update操做,是经过先delete后add操做完成的,solr能够经过本身的内部机制,设置了update的具体一个文档字段的操做,其实也是经过先提取出来这个文档的信息,再更新的。
<add>
<doc>
<field name ="id">1</field>
<field update="set" name ="count">10</field>
</doc></add>
并发更新:
在solr运行的时候,若是存在多个客户端来更新同一个文档的时候,可能会存在着并发问题,这个问题,solr是经过_version_来控制的,并发更新的时候,为了防止重复更新或者更新错误的状况,在更新的时候带上这个字段(<field name="_version_">1234567890</field>),则solr在作具体的操做的时候,会从索引或事务日志中提取版本信息,若是是这个版本则更新,不是则抛出异常。
版本信息能够经过id和版本号来获得。
这个版本信息字段_version_还能够作其余做用。
1: 仅仅标示这个文档必须存在。
>1:则标示为正式的版本号,必须存在并且是这个版本,不然失败;
0:无并发控制,若是存在则重写;
<0:文档必须不存在。
段合并:
由前面文章咱们知道,solr的索引是由段组成,更新索引的时候是写入一个段的信息,几个段共同组成一个索引,在solr优化索引的时候或其余的时候,solr的段是会合并的。
因此有很多相关段控制的信息。
<mergeFactor>10</mergeFactor>
说明:合并因子,有两层含义:
一、内存中有10个文档的时候,会建立一个段;
二、当一个索引中段的个数达到10的时候,这10个段会合并成一个段,简单的说,一个索引中最多只能有9个段。
注意,若是一味的这样合并可能会致使一个段的内容过多,索引变慢,因此solr经过maxMergeDocs 这个参数来指定一个段的最大文档数量,即便之后solr的段的个数超过了
10的,若是合并后不知足一个段的最大文档个数为:maxMergeDocs的条件,则不进行合并。
minMergeSize: 指定最小的合并段大小,若是段的大小小于这个值,则能够参加合并。
maxMergeSize:当一个段的大小大于这个值的时候就不参与合并了。
maxMergeDocs:当文档数据量大于这个的时候,这个段就不参与合并了。
合并段默认经过: <mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler"/>配置类来处理。
底层更新:
<updateHandler class="solr.DirectUpdateHandler2">是配置底层更新的相关数据,不能同 <requestHandler name="/update" class="solr.UpdateRequestHandler">相互混淆了。
这里面涉及到:
一、首先这些存储存在什么位置:
<dataDir>${solr.data.dir:}</dataDir> 这个更改默认的data位置,通常data是在solr的根目录下面的core的子目录的data里面。
建议若是存在多个磁盘能够不一样的core存放在不一样的磁盘上。
二、具体的写操做的实现:
<directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"> <!-- These will be used if you are using the solr.HdfsDirectoryFactory, otherwise they will be ignored. If you don't plan on using hdfs, you can safely remove this section. --> <!-- The root directory that collection data should be written to. --> <str name="solr.hdfs.home">${solr.hdfs.home:}</str> <!-- The hadoop configuration files to use for the hdfs client. --> <str name="solr.hdfs.confdir">${solr.hdfs.confdir:}</str> <!-- Enable/Disable the hdfs cache. --> <str name="solr.hdfs.blockcache.enabled">${solr.hdfs.blockcache.enabled:true}</str> <!-- Enable/Disable using one global cache for all SolrCores. The settings used will be from the first HdfsDirectoryFactory created. --> <str name="solr.hdfs.blockcache.global">${solr.hdfs.blockcache.global:true}</str> </directoryFactory>
默认的NRTCachingDirectoryFactory 其实是standardDirectoryFactory的封装。
在实际运行过程当中,solr会根据系统和JVM的版本进行。
一、在linux、windows、solaris的64位操做系统用的是MMapDirectory中。
二、在32windows下用的是SimpleFSDirectory。
三、NIOFSDirectory :利用NIO优化,避免从相同的文件读数据须要同步。不能在windows上利用这个,是由于JVM的bug。
在solr的管理界面,能够看到Directory的具体实现。
在solrconfig.xml中,你能够重写默认的Directory的实现,经过以下的更改:
<directoryFactory name="DirectoryFactory"
class="${solr.directoryFactory:solr.MMapDirectoryFactory}"/>
在64位windows上、solaris、和linux上,它经过虚拟内存管理功能能够得到读的更好性能。