做过搜索的工程师都了解,索引数据是搜索的数据基础。现在的数据是从各个业务方同步过来的,然后搜索构建出一个完整的数据结构,对应这些数据,类似大宽表。而索引数据就相当于大宽表中的数据。
针对这样一种数据结构,数据的稳定性是我们关注的重中之重。搜索的数据结构并没有像mysql一样拥有严格的定义,严格的数据规范。所以它的稳定性不如数据库。搜索的数据有一个特点是不能像mysql一样进行“表的关联”,这就意味着一个索引的数据结构只能无限的扩大。一条索引的数据,类似于数据库的一行数据。搜索的一条数据是一个doc文件,相当于一个文本,所以一旦某一个字段出现了问题,他会影响整条数据。针对以上两个特点,可以说它是比较脆弱的。
数据很重要,但是数据的安全性较低,构成破坏性很大。那么索引数据的备份必然非常重要。试想一下,你把一个字段从string格式改成int格式,相当于脏数据扰乱了正常数据,导致整个collection坏掉。我们无法找到全部脏数据,那么就需要重新删除数据重建才可以,消耗的时间可能是巨大的。所以备份是必须的。
既然是备份,那么必然会有数据的备份。Solr支持直接镜像的备份,镜像的备份优势是数据量小,solr自身支持好,恢复快。但是有2个问题,一个是备份的数据不见得是最新的,可能只能恢复到其中的一个时间点。另外一个问题,再快速的恢复也是有时间的代价。
基于上述两个问题,提出一种备份方案。对一个collection,提供一个结构一样的collection。他们可以共享一份schemal(solr的配置文件,相当于数据库的ddl)。这意味着他们有相同的“表结构”。在数据层面,每次新的数据更新都需要向collection提供一次更新或者插入的要求,把我们要更新的索引文件通过solr的api发送给solr进行更新。我们可以在发送索引数据的阶段,把数据分别发给两个core更新,这样就做到了数据的同步更新。
一般数据出问题都出在更新schemal的时候,也就是更新“表结构”的时候,容易出现数据问题。这个时候,在solr中是reload的操作,那么可以先reload核心的collection,建一次索引,检查索引的正确性,再去reload另外一个备份的collection。一旦collection出现了问题,可以立刻把流量切换到bak上去,这样就做到了collection的备份。
如果备份的collection仅仅用来做备份,那么这个代价也是比较大的。核心的collection占用多少容量和cpu,那么备份的collection同样需要那么多的容量和cpu。因此,在把collection作为备份的基础上,我们可以再做一个操作。把collection当作提供服务的接口。我们知道既然collection能够提供一样的数据结构,也可以迅速的进行流量切换,那么它一定可以提原有接口的所有功能。
在接口对外提供的过程中,大部分的接口都是正常的接口。但是仍然有一些特殊的接口调用。比如搜索的大批量数据导出。对于特别大的数据量,在sql中,我们是通过分批进行数据导出。但是分批导出,意味着短时间内接口需要接受大量的请求,对于接口和solr的压力都是很大的。如果这时候有正常的请求进来,那么这个正常的请求就会被影响。因此,我们可以把collection的备份作为针对这种特殊请求的数据源。在接口端,正常的请求和特殊的请求进行业务分流,正常的请求可以走核心的collection,特殊的请求(影响正常访问的请求,如短时间批量插叙,含有聚合参数facet的请求)走备份的collection,从这点来说,同样可以提高接口的稳定性。
综上所述,我们提出了一种collection备份的方法,不仅能够做到数据的实时备份,瞬时恢复,同样提高了用户的搜索体验。
关于索引备份的想法
内容纲要
抽出几点标题分开讲,会清晰些