基于  lucene  的站内搜索 tangfulin <tangfulin@gmail.com>  www.imobile.com.cn
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
关于手机之家 (1/2)‏
关于手机之家 (2/2)‏ 手机之家是一个旨在提供全方位的手机相关服务的资讯类网站。在 7 年的 时间里,手机之家从无到有,已经发展成为极具人气、最受关注的手机产 品资讯网站。 目前已有的一些统计数据: a. 1000w+ 用户 b. 3000w+ 帖子 c. 1.1TB+ 附件 d. 780w+ Page View/ 每天 e. 5~10w 在线用户 /15 分钟
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
产品大全搜索 新闻  CMS  搜索 论坛搜索(默认只搜索标题) 论坛帖子及回复搜索 手机铃声,主题,电子书,软件搜索 二手交易搜索 。。。 手机之家的搜索 V1.0(1/3)‏
手机之家的搜索 V1.0(2/3)‏ 实现了多个业务模块需要的搜索功能 不同的搜索字段 不同的排序方式 不同的更新频率 Java  多进程,多线程实现
手机之家的搜索 V1.0(3/3)‏ 运行概况 Dev by chaoqian ( http://www.longker.org/ )‏ 最初基于  Lucene 2.2 ,当前  2.4.1 07 年 12 月开始开发 08 年 12 月随新版系统上线 部署在 Imobile-SV39-A49 上: X86_64 , 8 cpu @ 2.50GHz , 32G mem CentOS release 5.2, JDK64 1.6,  搜索: 35+  万查询 / 天,高峰期  > 20 次 /s 更新: 平均 15  条 /  分钟
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
需求背景 (1/3)‏ 重建索引 索引字段变更 分词算法变更 搜索结果异常(记录重复,记录丢失) 索引文件意外损坏 V1.0 :手工重建,人工参与重建过程,重建过程中不能正常更新 V1.5  :自动重建,重建过程中正常更新原来的索引
需求背景 (2/3)‏ 缩短更新周期(及时更新) Google  索引  imobile  更新周期为  30  分钟 V1.0  索引更新周期为  15  分钟 V1.5  预期更新周期为  3  分钟,实际为  1-5  分钟
需求背景 (3/3)‏ 搜索大索引( V1.5 ) 3000+ 万条记录的一个库, xml  原始文件 14G V1.0  索引文件为  13 G ,无法快速更新 V1.5  索引文件为  3.9G (还是无法快速更新?) V1.5  完整重建一次: 140 分钟
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
目标 及时更新( 3 分钟) 快速重建( < 2 小时) 可配置(拥抱需求变化) 可监控(运维友好) SLA : always  可写, always  可读,异常的时候唯一的表现是更新延迟 高性能,能承受较大的流量,并发压力
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
进度 2009 年 4 月 1 号  search 2.0 init (节日快乐!) 4 月 12 号,修改版本号为  1.5 6 月 1 号, search 1.5  在线上试运行 (节日快乐  again !)
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
设计 (1/8)‏ 分离索引与存储,二次读取 分离读与写 分离  update  和  rebuild 拆分大库和小库 new open  小库(小库滚动), reopen 大库 新索引预热 更多: http://blog.fulin.org/category/tech/lucene
设计 (2/8) 分离索引与存储,二次读取 索引里只存储  id  ,其他的字段只索引不存储。 优点: 保持索引的大小为一个可接受的范围 提高索引读取速度 提高索引  cache  效率 缺点:搜索时需要额外的请求来获取其它必须的字段( lucene  +  db 方案) 衍生项目: blackdb , memcache 协议  + bdbJE 存储
设计 (3/8) 分离读与写 优点: 降低编程复杂度 保证搜索服务的可用性,和可扩展性(可以将索引文件分发到多台机器上,同时对外提供服务) 提升索引更新速度 缺点: 需要移动索引文件 需要额外的索引更新逻辑( reopen ) 无法使用当前设计中的 lucene 的  real-time search
设计 (4/8) 分离  update  和  rebuild Rebuild  的同时, update  正常更新 Rebuild  需要将重建这段时间的更新计入新的索引中 Rebuild  完成后,通知  update  切换到新索引上来,并继续更新 进程间通信,当前使用最原始的基于文件的方式
设计 (5/8) 拆分大库和小库 保证及时更新的同时,减少索引频繁同步(由写索引同步到读索引)带来的  io  压力 更新频率 小库(最近更新库) 1 分钟 大库(历史库) 1 天到 1 周,可配置 并行搜索,加速搜索速度 问题: group by
设计 (6/8) 拆分大库和小库(续) 增加新记录: 增加到小库 更新记录: 从大库中删除(标记删除) 从小库中删除(物理删除) 增加到小库 删除记录 从大库中删除(标记删除) 从小库中删除(物理删除) 定期合并小库到大库,并清空小库
设计 (7/8) 搜索端索引更新 小库每次同步到一个新的文件夹中 保留最近打开的  n (2) 份小库索引目录 检测到新的索引到达,关闭一个最旧的,打开新的,预热后标识为可用 检测到新的小库到达, reopen  大库(为了逻辑上的简单起见,大小库同步更新)
设计 (8/8) 新索引预热 目的:消除新打开的索引上前几次搜索慢的问题 实现: 遍历一遍新打开的索引,将数据都读入内存 引起  gc  ,导致搜索暂停 对  lucene  内置的  cache  无贡献 搜几个热门的词,并遍历结果集 使用原来的 cache 填充新的 cache 预热完成后,再投入使用
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
实现 (1/8) 架构示意图 端口 1985 IndexRebuilder IndexUpdater Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 IndexReceiver Rebuild xml data Update xml data
实现 (1/8) 架构示意图 端口 1985 IndexRebuilder IndexUpdater Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 IndexReceiver Rebuild xml data Update xml data
实现 (2/8) IndexReceiver By Java , Daemon  程序 监听端口,使用  SCGI  通讯协议 使用  Monkey  为底层 NIO  处理框架 接收客户端  post  过来的数据,并写入对应的目录 Update  和  rebuild  共用 为了保证原子性,先写入  .0.***.xml  ,写入完成后,再  rename Receiver  只负责写入, updater  和  rebuilder  稍后负责删除(备份) 对外提供服务,所以尽可能简单,单独进程
实现 (1/8) 架构示意图 端口 1985 IndexRebuilder IndexUpdater Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data
实现 (3/8) IndexUpdater By Java , Daemon  程序 多个索引共用,每个索引起一个线程 可根据需要随时停止或启动单个索引更新的线程 AddShutdownHook  退出前  close  所有打开的  IndexWriter 功能: 更新新数据到当前索引 合并大小库 拷贝当前索引的快照供搜索使用 切换 rebuild 出来的新索引
实现 (1/8) 架构示意图 端口 1985 IndexRebuilder Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data
实现 (4/8) IndexRebuilder By Java , Daemon  程序 多个索引共用,每个索引一个线程 重建索引时序图: 时间线 rebuilder updater receiver 开始重建 客户端发送开始重建标识 客户端发送重建索引的数据 客户端发送结束重建标识 重建数据接收完毕 重建完成 Receiver  开始抄送 update  数据到  rebuild-update Updater  正常更新 Rebuilder  开始工作 Rebuilder  处理完重建数据 Rebuilder  处理完 t0  到 当前的更新数据,通知 updater  ,然后自己退出 Updater  接到通知,切换索引。完成后通知 receiver Receiver  接到通知,停止抄送 updater  数据。 重建过程结束 T0 T1 T2 T3 T4 T5
实现 (5/8) IndexRebuilder 切换索引过程 Updater  删除自己当前的索引和未更新完的 xml 文件 Updater  将 rebuilder 的索引和  rebuild-update  的 xml 文件 “据为己有” 通知  receiver  停止抄送 继续正常的数据更新过程 可以证明这个过程中,不会有数据丢失,也不会有数据重复 前提: updater  的  xml  文件和  rebuild-update  的  xml  文件是完全相同的(包括文件名和数据) t3-t4 :  数据来自 rebuild-update t4-t5 :  rebuild-update  中的数据可以被直接忽略
实现 (1/8) 架构示意图 端口 1985 Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data Transfer
实现 (6/8) Transfer Bash  脚本,每个 indexId  一个进程,由  ControlCenter  或  monitor  启动和停止 监控索引快照目录 快照目录下存在子目录,并且子目录中存在  copy.done.sign  ( IndexUpdater  拷贝快照完成后 touch 的标识),则  rsync  子目录到  search Rsync  的时候:先  rsync  大库快照,再  rsync  小库快照,都成功后再  rsync  一个  trans.done.sign 以  0  字节的  sign  文件作为标识,模拟两阶段提交,保证文件拷贝,传输的原子性
实现 (1/8) 架构示意图 端口 1985 Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data
实现 (7/8) Searcher By Java , Daemon  程序 使用  Monkey  为底层 NIO  处理框架 使用  SCGI  通讯协议 启动后第一件事: warmUpAllIndex 当有新索引到达的时候,在后台打开,预热后,再投入使用 关闭旧的 IndexSearcher 实例的前提:当前没有线程还在使用它 方法:计数 ( get +1 , return -1 ,为 0 表示没有被使用) Stat  统计
实现 (8/8)Utils Cleaner Bash  脚本,每个 indexId  一个进程 用来删除 Searcher  已经关闭或跳过的索引 Monitor 监控系统的各个进程是否存在,如不存在,则启动一个新的进程 ControlCenter Usage: ./controlCenter.sh {start|stop|restart} {all|receiver|updater|rebuilder|searcher|trans|cleaner} OR: ./controlCenter.sh {start|stop|restart} {monitor|logSlowSearch}  OR: ./controlCenter.sh {mkdirs}
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
性能测试结果 ab -n 1000 -c 10  ( 99%  545ms  100%  32838ms ) -n 2000 -c 20  ( 90%  1171ms  99%  4106ms ) 总请求  3000 ,慢查询比例  4% 搜索词:手机之家论坛首页的板块名,随机 最长: 37s > 10s : 22  (索引切换, gc ) 2s-10s : 38 1s-2s : 62
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
部署上线 运行概况 updater , rebuilder  部署在  Imobile-SV25-B50  上 IntelXeon 4 cpu @2.60GHz , 6G mem Slackware 12.1, JDK32 1.6 Search  部署在 Imobile-SV39-A49 上: X86_64 , 8 cpu @ 2.50GHz , 32G mem CentOS release 5.2, JDK64 1.6 搜索: 35+  万查询 / 天,高峰期  > 20 次 /s 更新: 平均 15  条 /  分钟
线上运行观察 Slow search  比例: >1s 1.x%; >2s 0.2% 机器负载 : 49(search): 1~3; 50(update): <1 内存消耗: search: -Xms4096M -Xmx4096M Cpu 消耗 :多核之间平均比较分配 索引平均更新速度: ~1 分钟 索引延迟率(因各种原因导致延迟更新的比例)
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
开发中的一些收获 (1/5)‏ FileChannel.transferTo  拷贝文件失败: Fewer than the requested number of bytes are transferred if the target channel is non-blocking and it has fewer than count  bytes free in its output buffer. 解决: check copied size ,断点续传 根据  lucene  索引文件更新的特性 ( 每次更新一个新版本的时候,都会新建一个全新的文件 ) ,使用  cp -l  链接替代真实的大文件拷贝动作
开发中的一些收获 (2/5)‏ Kill  的问题 Never kill smart frog, don't kill -9 Java use Runtime.getRuntime().addShutdownHook  to do the cleaning things Lucene IndexWrite need close! server  程序,都应该考虑信号捕捉和处理的问题( java  程序容易忽略这个问题)
开发中的一些收获 (3/5)‏ 更新太频繁导致的磁盘  IO  问题 同一台机器目录之间同步:没有问题 一拖二,一拖三 解决 建索引的机器不提供搜索服务 rsync  限速
开发中的一些收获 (4/5)‏ GC  引起的服务暂停 多个索引共用 Search 进程 每个索引维持了多个  searcher 索引更新太过频繁: 30 秒 解决: 纵向拆分:将两个最大的库拆分到单独的进程 减少 searcher 个数到  2  个 谨慎的选择 gc 的类型,并调整 gc 的参数 G1 改进不明显,不稳定 -XX:+UseParNewGC  -XX:ParallelGCThreads=20  -XX:+UseConcMarkSweepGC -XX:SurvivorRatio=8  -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
开发中的一些收获 (5/5)‏ Java server  程序的  trouble shooting 性能问题: jprofile 内存问题: jmx  +  jconsole 线程问题: Thread.currentThread().setName(&quot;diffDetect-&quot; + indexId);  ps. Java  线程对传统的  unix  工具不友好 详细的 log : log4j 常用的脚本: logslowsearch.sh 开发调试阶段:方便的重新编译并重启脚本 线上服务阶段:完善的监控及报警机制
关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
持续改进 配置文件改动检测,自动重新加载 更智能的处理索引字段变更 分词算法改进 搜索关键字数据挖掘(搜索新词自动发现) 搜索建议,搜索联想功能 排序算法改进 整合搜索 准实时搜索
Lucene 3.0 Lucene 2.9 Searchable.search(Weight, Filter, Collector): collector  终于可用了 Added new MultiReaderHitCollector:  解决  group by near real-time search via IndexWriter.getReader():  单机方案 Lucene 3.0 Del  deprecated  apis:  大版本升级要谨慎 Port to Java5:  应该会有性能的提升 Lucene 3.1 Near Realtime Search (using a built in RAMDirectory):  还是单机方案 Complete overhaul of FieldCache API/Implementation:  等到花儿也谢了
定制功能 在某些情况下作为数据库的替代数据源 类似淘宝搜索,按多个字段筛选,过滤,排序 当前解决方案:使用  sql  从数据库中选取数据 问题:数据可能因为业务逻辑的设计而分散在多个不同的库,表中,联表查询问题,并发压力问题 难点:非典型的复杂的查询条件,如  group by
参考资料 Lucene http://lucene.apache.org/ http://lucene.apache.org/java/2_4_1/api/index.html 中文分词 http://code.google.com/p/paoding/ http://code.google.com/p/imdict-chinese-analyzer/ http://code.google.com/p/mmseg4j/ Monkey ( Java 底层异步网络 IO 框架) DAL
更多讨论 关于  imobile 网站首页  http://www.imobile.com.cn/ 关于  http://www.imobile.com.cn/about.html Longker ( V1.0 版本) http://www.longker.org/ 关于我( V1.5 版本) http://www.fulin.org/ http://twitter.com/tangfl

More Related Content

PDF
基于Lucene的站内搜索 Beta
PDF
基于Lucene的站内搜索 Beta
PPT
基于Lucene的站内搜索
ODP
基于Lucene的站内搜索
PDF
Oracle数据库升级前必要的准备工作
PDF
了解Oracle critical patch update
PDF
PostgreSQL 9 1 新特性
DOCX
Oda安装 恢复步骤
基于Lucene的站内搜索 Beta
基于Lucene的站内搜索 Beta
基于Lucene的站内搜索
基于Lucene的站内搜索
Oracle数据库升级前必要的准备工作
了解Oracle critical patch update
PostgreSQL 9 1 新特性
Oda安装 恢复步骤

What's hot (12)

PDF
I pv6 windows 實做課程_0705
PDF
Installation and configuration 11g r2
PDF
Openstack document for apac v3
DOC
Liferay环境搭建
PPTX
Kafka in Depth
PDF
自由軟體鑄造場_20111023_Subversion版本控制系統之操作_曾義峰(ant)
PDF
NF core-workflow
PDF
【诗檀软件 郭兆伟-技术报告】跨国企业级Oracle数据库备份策略
PPTX
Ansible 101
PDF
Bypat博客出品-利用cent os快速构建自己的发行版
PPTX
使用Rpm&yum进行基础软件管理
PDF
dbdao.com 汪伟华 my-sql-replication复制高可用配置方案
I pv6 windows 實做課程_0705
Installation and configuration 11g r2
Openstack document for apac v3
Liferay环境搭建
Kafka in Depth
自由軟體鑄造場_20111023_Subversion版本控制系統之操作_曾義峰(ant)
NF core-workflow
【诗檀软件 郭兆伟-技术报告】跨国企业级Oracle数据库备份策略
Ansible 101
Bypat博客出品-利用cent os快速构建自己的发行版
使用Rpm&yum进行基础软件管理
dbdao.com 汪伟华 my-sql-replication复制高可用配置方案
Ad

Similar to 基于 lucene 的站内搜索 (20)

PDF
基于Lucene的站内搜索 Beta
PPT
Software Engineer Talk
PPT
从林书豪到全明星 - 虎扑网技术架构如何化解流量高峰
PPT
Subversion
PPT
Java@taobao
PPT
網站設計100步
PDF
构建基于Lamp的网站架构
PPT
深入研究 Windows 系統服務 效能調校與故障排除
PPTX
高性能队列Fqueue的设计和使用实践
PPTX
数据库持续集成
PDF
Envrionment as a code with vagrant & chef
PPTX
华强北商城自动化发布系统
PPT
Coreseek/Sphinx 全文检索实践指南
KEY
Beyond rails server
PDF
Delta Lake Architecture: Delta Lake + Apache Spark Structured Streaming
PPT
互联网创业服务器运维工具集
PPTX
Ocean base海量结构化数据存储系统 hadoop in china
PDF
基于Symfony框架下的快速企业级应用开发
PDF
Itpub电子杂志第四期第二稿
PPT
敦群學院-SharePoint精英計畫-系統開發-Day 3
基于Lucene的站内搜索 Beta
Software Engineer Talk
从林书豪到全明星 - 虎扑网技术架构如何化解流量高峰
Subversion
Java@taobao
網站設計100步
构建基于Lamp的网站架构
深入研究 Windows 系統服務 效能調校與故障排除
高性能队列Fqueue的设计和使用实践
数据库持续集成
Envrionment as a code with vagrant & chef
华强北商城自动化发布系统
Coreseek/Sphinx 全文检索实践指南
Beyond rails server
Delta Lake Architecture: Delta Lake + Apache Spark Structured Streaming
互联网创业服务器运维工具集
Ocean base海量结构化数据存储系统 hadoop in china
基于Symfony框架下的快速企业级应用开发
Itpub电子杂志第四期第二稿
敦群學院-SharePoint精英計畫-系統開發-Day 3
Ad

More from fulin tang (12)

PDF
雪球大数据体系实践
PDF
雪球服务化实践历程.Print
PPTX
订阅互联网
PPTX
我的奋斗 @ weibo
PDF
一个Nosql的故事
PDF
Redis大数据之路 dtcc-唐福林
PDF
新浪微博开放平台中的 Redis 实践
PDF
Redis 坑
PDF
Gizzard, DAL and more
PDF
音乐搜索的极致
PPT
Voldemort Intro Tangfl
PDF
毕业设计-Slide
雪球大数据体系实践
雪球服务化实践历程.Print
订阅互联网
我的奋斗 @ weibo
一个Nosql的故事
Redis大数据之路 dtcc-唐福林
新浪微博开放平台中的 Redis 实践
Redis 坑
Gizzard, DAL and more
音乐搜索的极致
Voldemort Intro Tangfl
毕业设计-Slide

基于 lucene 的站内搜索

  • 1. 基于 lucene 的站内搜索 tangfulin <[email protected]> www.imobile.com.cn
  • 2. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 3. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 5. 关于手机之家 (2/2)‏ 手机之家是一个旨在提供全方位的手机相关服务的资讯类网站。在 7 年的 时间里,手机之家从无到有,已经发展成为极具人气、最受关注的手机产 品资讯网站。 目前已有的一些统计数据: a. 1000w+ 用户 b. 3000w+ 帖子 c. 1.1TB+ 附件 d. 780w+ Page View/ 每天 e. 5~10w 在线用户 /15 分钟
  • 6. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 7. 产品大全搜索 新闻 CMS 搜索 论坛搜索(默认只搜索标题) 论坛帖子及回复搜索 手机铃声,主题,电子书,软件搜索 二手交易搜索 。。。 手机之家的搜索 V1.0(1/3)‏
  • 8. 手机之家的搜索 V1.0(2/3)‏ 实现了多个业务模块需要的搜索功能 不同的搜索字段 不同的排序方式 不同的更新频率 Java 多进程,多线程实现
  • 9. 手机之家的搜索 V1.0(3/3)‏ 运行概况 Dev by chaoqian ( http://www.longker.org/ )‏ 最初基于 Lucene 2.2 ,当前 2.4.1 07 年 12 月开始开发 08 年 12 月随新版系统上线 部署在 Imobile-SV39-A49 上: X86_64 , 8 cpu @ 2.50GHz , 32G mem CentOS release 5.2, JDK64 1.6, 搜索: 35+ 万查询 / 天,高峰期 > 20 次 /s 更新: 平均 15 条 / 分钟
  • 10. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 11. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 12. 需求背景 (1/3)‏ 重建索引 索引字段变更 分词算法变更 搜索结果异常(记录重复,记录丢失) 索引文件意外损坏 V1.0 :手工重建,人工参与重建过程,重建过程中不能正常更新 V1.5 :自动重建,重建过程中正常更新原来的索引
  • 13. 需求背景 (2/3)‏ 缩短更新周期(及时更新) Google 索引 imobile 更新周期为 30 分钟 V1.0 索引更新周期为 15 分钟 V1.5 预期更新周期为 3 分钟,实际为 1-5 分钟
  • 14. 需求背景 (3/3)‏ 搜索大索引( V1.5 ) 3000+ 万条记录的一个库, xml 原始文件 14G V1.0 索引文件为 13 G ,无法快速更新 V1.5 索引文件为 3.9G (还是无法快速更新?) V1.5 完整重建一次: 140 分钟
  • 15. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 16. 目标 及时更新( 3 分钟) 快速重建( < 2 小时) 可配置(拥抱需求变化) 可监控(运维友好) SLA : always 可写, always 可读,异常的时候唯一的表现是更新延迟 高性能,能承受较大的流量,并发压力
  • 17. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 18. 进度 2009 年 4 月 1 号 search 2.0 init (节日快乐!) 4 月 12 号,修改版本号为 1.5 6 月 1 号, search 1.5 在线上试运行 (节日快乐 again !)
  • 19. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 20. 设计 (1/8)‏ 分离索引与存储,二次读取 分离读与写 分离 update 和 rebuild 拆分大库和小库 new open 小库(小库滚动), reopen 大库 新索引预热 更多: http://blog.fulin.org/category/tech/lucene
  • 21. 设计 (2/8) 分离索引与存储,二次读取 索引里只存储 id ,其他的字段只索引不存储。 优点: 保持索引的大小为一个可接受的范围 提高索引读取速度 提高索引 cache 效率 缺点:搜索时需要额外的请求来获取其它必须的字段( lucene + db 方案) 衍生项目: blackdb , memcache 协议 + bdbJE 存储
  • 22. 设计 (3/8) 分离读与写 优点: 降低编程复杂度 保证搜索服务的可用性,和可扩展性(可以将索引文件分发到多台机器上,同时对外提供服务) 提升索引更新速度 缺点: 需要移动索引文件 需要额外的索引更新逻辑( reopen ) 无法使用当前设计中的 lucene 的 real-time search
  • 23. 设计 (4/8) 分离 update 和 rebuild Rebuild 的同时, update 正常更新 Rebuild 需要将重建这段时间的更新计入新的索引中 Rebuild 完成后,通知 update 切换到新索引上来,并继续更新 进程间通信,当前使用最原始的基于文件的方式
  • 24. 设计 (5/8) 拆分大库和小库 保证及时更新的同时,减少索引频繁同步(由写索引同步到读索引)带来的 io 压力 更新频率 小库(最近更新库) 1 分钟 大库(历史库) 1 天到 1 周,可配置 并行搜索,加速搜索速度 问题: group by
  • 25. 设计 (6/8) 拆分大库和小库(续) 增加新记录: 增加到小库 更新记录: 从大库中删除(标记删除) 从小库中删除(物理删除) 增加到小库 删除记录 从大库中删除(标记删除) 从小库中删除(物理删除) 定期合并小库到大库,并清空小库
  • 26. 设计 (7/8) 搜索端索引更新 小库每次同步到一个新的文件夹中 保留最近打开的 n (2) 份小库索引目录 检测到新的索引到达,关闭一个最旧的,打开新的,预热后标识为可用 检测到新的小库到达, reopen 大库(为了逻辑上的简单起见,大小库同步更新)
  • 27. 设计 (8/8) 新索引预热 目的:消除新打开的索引上前几次搜索慢的问题 实现: 遍历一遍新打开的索引,将数据都读入内存 引起 gc ,导致搜索暂停 对 lucene 内置的 cache 无贡献 搜几个热门的词,并遍历结果集 使用原来的 cache 填充新的 cache 预热完成后,再投入使用
  • 28. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 29. 实现 (1/8) 架构示意图 端口 1985 IndexRebuilder IndexUpdater Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 IndexReceiver Rebuild xml data Update xml data
  • 30. 实现 (1/8) 架构示意图 端口 1985 IndexRebuilder IndexUpdater Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 IndexReceiver Rebuild xml data Update xml data
  • 31. 实现 (2/8) IndexReceiver By Java , Daemon 程序 监听端口,使用 SCGI 通讯协议 使用 Monkey 为底层 NIO 处理框架 接收客户端 post 过来的数据,并写入对应的目录 Update 和 rebuild 共用 为了保证原子性,先写入 .0.***.xml ,写入完成后,再 rename Receiver 只负责写入, updater 和 rebuilder 稍后负责删除(备份) 对外提供服务,所以尽可能简单,单独进程
  • 32. 实现 (1/8) 架构示意图 端口 1985 IndexRebuilder IndexUpdater Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data
  • 33. 实现 (3/8) IndexUpdater By Java , Daemon 程序 多个索引共用,每个索引起一个线程 可根据需要随时停止或启动单个索引更新的线程 AddShutdownHook 退出前 close 所有打开的 IndexWriter 功能: 更新新数据到当前索引 合并大小库 拷贝当前索引的快照供搜索使用 切换 rebuild 出来的新索引
  • 34. 实现 (1/8) 架构示意图 端口 1985 IndexRebuilder Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data
  • 35. 实现 (4/8) IndexRebuilder By Java , Daemon 程序 多个索引共用,每个索引一个线程 重建索引时序图: 时间线 rebuilder updater receiver 开始重建 客户端发送开始重建标识 客户端发送重建索引的数据 客户端发送结束重建标识 重建数据接收完毕 重建完成 Receiver 开始抄送 update 数据到 rebuild-update Updater 正常更新 Rebuilder 开始工作 Rebuilder 处理完重建数据 Rebuilder 处理完 t0 到 当前的更新数据,通知 updater ,然后自己退出 Updater 接到通知,切换索引。完成后通知 receiver Receiver 接到通知,停止抄送 updater 数据。 重建过程结束 T0 T1 T2 T3 T4 T5
  • 36. 实现 (5/8) IndexRebuilder 切换索引过程 Updater 删除自己当前的索引和未更新完的 xml 文件 Updater 将 rebuilder 的索引和 rebuild-update 的 xml 文件 “据为己有” 通知 receiver 停止抄送 继续正常的数据更新过程 可以证明这个过程中,不会有数据丢失,也不会有数据重复 前提: updater 的 xml 文件和 rebuild-update 的 xml 文件是完全相同的(包括文件名和数据) t3-t4 : 数据来自 rebuild-update t4-t5 : rebuild-update 中的数据可以被直接忽略
  • 37. 实现 (1/8) 架构示意图 端口 1985 Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data Transfer
  • 38. 实现 (6/8) Transfer Bash 脚本,每个 indexId 一个进程,由 ControlCenter 或 monitor 启动和停止 监控索引快照目录 快照目录下存在子目录,并且子目录中存在 copy.done.sign ( IndexUpdater 拷贝快照完成后 touch 的标识),则 rsync 子目录到 search Rsync 的时候:先 rsync 大库快照,再 rsync 小库快照,都成功后再 rsync 一个 trans.done.sign 以 0 字节的 sign 文件作为标识,模拟两阶段提交,保证文件拷贝,传输的原子性
  • 39. 实现 (1/8) 架构示意图 端口 1985 Searcher 配置文件 DAL 数据更新 同步通知搜索 搜索客户端调用 Searcher 的 API 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 端口 1986 Rebuild xml data Update xml data
  • 40. 实现 (7/8) Searcher By Java , Daemon 程序 使用 Monkey 为底层 NIO 处理框架 使用 SCGI 通讯协议 启动后第一件事: warmUpAllIndex 当有新索引到达的时候,在后台打开,预热后,再投入使用 关闭旧的 IndexSearcher 实例的前提:当前没有线程还在使用它 方法:计数 ( get +1 , return -1 ,为 0 表示没有被使用) Stat 统计
  • 41. 实现 (8/8)Utils Cleaner Bash 脚本,每个 indexId 一个进程 用来删除 Searcher 已经关闭或跳过的索引 Monitor 监控系统的各个进程是否存在,如不存在,则启动一个新的进程 ControlCenter Usage: ./controlCenter.sh {start|stop|restart} {all|receiver|updater|rebuilder|searcher|trans|cleaner} OR: ./controlCenter.sh {start|stop|restart} {monitor|logSlowSearch} OR: ./controlCenter.sh {mkdirs}
  • 42. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 43. 性能测试结果 ab -n 1000 -c 10 ( 99% 545ms 100% 32838ms ) -n 2000 -c 20 ( 90% 1171ms 99% 4106ms ) 总请求 3000 ,慢查询比例 4% 搜索词:手机之家论坛首页的板块名,随机 最长: 37s > 10s : 22 (索引切换, gc ) 2s-10s : 38 1s-2s : 62
  • 44. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 45. 部署上线 运行概况 updater , rebuilder 部署在 Imobile-SV25-B50 上 IntelXeon 4 cpu @2.60GHz , 6G mem Slackware 12.1, JDK32 1.6 Search 部署在 Imobile-SV39-A49 上: X86_64 , 8 cpu @ 2.50GHz , 32G mem CentOS release 5.2, JDK64 1.6 搜索: 35+ 万查询 / 天,高峰期 > 20 次 /s 更新: 平均 15 条 / 分钟
  • 46. 线上运行观察 Slow search 比例: >1s 1.x%; >2s 0.2% 机器负载 : 49(search): 1~3; 50(update): <1 内存消耗: search: -Xms4096M -Xmx4096M Cpu 消耗 :多核之间平均比较分配 索引平均更新速度: ~1 分钟 索引延迟率(因各种原因导致延迟更新的比例)
  • 47. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 48. 开发中的一些收获 (1/5)‏ FileChannel.transferTo 拷贝文件失败: Fewer than the requested number of bytes are transferred if the target channel is non-blocking and it has fewer than count bytes free in its output buffer. 解决: check copied size ,断点续传 根据 lucene 索引文件更新的特性 ( 每次更新一个新版本的时候,都会新建一个全新的文件 ) ,使用 cp -l 链接替代真实的大文件拷贝动作
  • 49. 开发中的一些收获 (2/5)‏ Kill 的问题 Never kill smart frog, don't kill -9 Java use Runtime.getRuntime().addShutdownHook to do the cleaning things Lucene IndexWrite need close! server 程序,都应该考虑信号捕捉和处理的问题( java 程序容易忽略这个问题)
  • 50. 开发中的一些收获 (3/5)‏ 更新太频繁导致的磁盘 IO 问题 同一台机器目录之间同步:没有问题 一拖二,一拖三 解决 建索引的机器不提供搜索服务 rsync 限速
  • 51. 开发中的一些收获 (4/5)‏ GC 引起的服务暂停 多个索引共用 Search 进程 每个索引维持了多个 searcher 索引更新太过频繁: 30 秒 解决: 纵向拆分:将两个最大的库拆分到单独的进程 减少 searcher 个数到 2 个 谨慎的选择 gc 的类型,并调整 gc 的参数 G1 改进不明显,不稳定 -XX:+UseParNewGC -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
  • 52. 开发中的一些收获 (5/5)‏ Java server 程序的 trouble shooting 性能问题: jprofile 内存问题: jmx + jconsole 线程问题: Thread.currentThread().setName(&quot;diffDetect-&quot; + indexId); ps. Java 线程对传统的 unix 工具不友好 详细的 log : log4j 常用的脚本: logslowsearch.sh 开发调试阶段:方便的重新编译并重启脚本 线上服务阶段:完善的监控及报警机制
  • 53. 关于手机之家 过去:手机之家搜索 V1.0 现在:手机之家搜索 V1.5 需求背景 目标 进度 设计 实现 测试 上线,运维 经验分享 将来:手机之家搜索 V2.0 目录
  • 54. 持续改进 配置文件改动检测,自动重新加载 更智能的处理索引字段变更 分词算法改进 搜索关键字数据挖掘(搜索新词自动发现) 搜索建议,搜索联想功能 排序算法改进 整合搜索 准实时搜索
  • 55. Lucene 3.0 Lucene 2.9 Searchable.search(Weight, Filter, Collector): collector 终于可用了 Added new MultiReaderHitCollector: 解决 group by near real-time search via IndexWriter.getReader(): 单机方案 Lucene 3.0 Del deprecated apis: 大版本升级要谨慎 Port to Java5: 应该会有性能的提升 Lucene 3.1 Near Realtime Search (using a built in RAMDirectory): 还是单机方案 Complete overhaul of FieldCache API/Implementation: 等到花儿也谢了
  • 56. 定制功能 在某些情况下作为数据库的替代数据源 类似淘宝搜索,按多个字段筛选,过滤,排序 当前解决方案:使用 sql 从数据库中选取数据 问题:数据可能因为业务逻辑的设计而分散在多个不同的库,表中,联表查询问题,并发压力问题 难点:非典型的复杂的查询条件,如 group by
  • 57. 参考资料 Lucene http://lucene.apache.org/ http://lucene.apache.org/java/2_4_1/api/index.html 中文分词 http://code.google.com/p/paoding/ http://code.google.com/p/imdict-chinese-analyzer/ http://code.google.com/p/mmseg4j/ Monkey ( Java 底层异步网络 IO 框架) DAL
  • 58. 更多讨论 关于 imobile 网站首页 http://www.imobile.com.cn/ 关于 http://www.imobile.com.cn/about.html Longker ( V1.0 版本) http://www.longker.org/ 关于我( V1.5 版本) http://www.fulin.org/ http://twitter.com/tangfl

Editor's Notes

  • #5: 手机之家是一个旨在提供全方位的手机相关服务的资讯类网站。在 7 年的 时间里,手机之家从无到有,已经发展成为极具人气、最受关注的手机产 品资讯网站。