将数据从PostgreSQL同步到Elasticsearch的经验总结

jopen 9年前

Elasticsearch是一款基于Apache Lucene构建的开源全文检索引擎,它能够轻松地进行大规模的横向扩展,以支撑PB级的结构化和非结构化海量数据的处理。而关系型数据库比较擅长对数据的管理,但对全文检索功能的支持相对不足,所以有时候一些实际项目需要将关系型数据库中的数据同步到Elasticsearch中,以提供更加强大的全文检索功能。另外,一些基于关系型数据库的历史遗留系统的存在,当遇到全文检索的新需求时,就更加需要将数据同步到Elasticsearch中。近日,在线银行支付平台GoCardless的软件工程师Chris Sinjakli发表了一篇题为《将数据从PostgreSQL同步到Elasticsearch的经验教训》的博文。在文章中,他结合自己的实际经历(GoCardless使用Elasticsearch增强搜索功能)总结了将数据从关系型数据库PostgreSQL同步到Elasticsearch的经验教训。

Chris首先指出当需要把数据同时存储到PostgreSQL和Elasticsearch两个地方时,开发者需要深入考虑的一些问题,如当Elasticsearch处理有很大延迟时将会发生什么未知事情、如果更新时出现异常将会发生什么情况、怎么知道Elasticsearch正确处理了每次更新等。接下来Chris引出要解决以上问题必须做到异步的更新、达到最终一致性、进行索引重建。

关于如何做到异步更新,Chris指出GoCardless开发团队构造了一个队列用于数据的异步同步,且通过线程池来协助处理。这样既可以单独更新,也可以批量更新,并使用基于JSON格式的数据和利用Elasticsearch的API保证了响应时间和可预知性。

关于如何确保一致性,Chris指出Elasticsearch的更新API不具有线程安全性,尤其在高并发更新时。如果只是调用该更新API来索引更新数据的话,就有可能引起并发问题。不过,Elasticsearch提供了一个具有乐观锁的索引版本系统,通过该系统就可以做到安全的更新。但是当在更新索引的同时,用户还是有可能搜索出脏数据。庆幸的是,Elasticsearch还提供了另一种处理索引版本的方案,该方案是由发起请求的外部程序来设置版本类型并提供版本号,这样使得Elasticsearch总是保持同步的文档具有最高版本号。 GoCardless开发团队考虑到PostgreSQL的事务ID(64位整数)在保证事务情况下能够实现自增,所以GoCardless开发团队就使用PostgreSQL的事务ID作为版本号。这样就可以实现每次同步到Elasticsearch的数据都是最合适的(尽管不是最新的),但最后仍会达到数据的一致性。

关于如何重建索引,Chris指出以上的异步方式仍然存在丢失更新的可能,如网络分区下引起的问题。为了处理以上问题,GoCardless开发团队采取周期地将最近写入到PostgreSQL的记录进行一次批量同步并使用Elasticsearch的Bulk API重新批量索引所同步数据的方案。该方案以较小的重复记录为代价彻底解决了更新丢失的问题,并且只需使用与原来同样的代码和在无需停止服务器的情况下即可实现索引重建。Chris还特别指出,如果想在无需停止服务器的情况实现重建索引,这就需要从一开始就正确地使用Elasticsearch的索引别名。

最后,Chris指出如果要构建更加良好的搜索体验,还有很多工作需要做,尤其是不同的应用程序有着不同的约束条件,所以他建议开发者在开始编写产品代码前就要深入思考相关问题及处理方案。

来自:http://www.infoq.com/cn/news/2015/01/postgresql-elasticsearch