Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

基于Lucene的站内搜索 Beta

2,842 views

Published on

Published in: Technology

基于Lucene的站内搜索 Beta

  1. 1. 基于 lucene 的站内搜索实战 tangfulin <tangfulin@gmail.com> www.imobile.com.cn 2009.07.26 @beta 技术沙龙
  2. 2. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  3. 3. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  4. 4. 关于手机之家 手机之家是一个旨在提供全方位的手机相关服务的资讯类网站。在 7 年的 时间里,手机之家从无到有,已经发展成为极具人气、最受关注的手机产 品资讯网站。 目前已有的一些统计数据: ●a. 1100w+ 用户 ●b. 3000w+ 帖子 ●c. 1.1TB+ 附件 ●d. 780w+ Page View/ 每天 ●e. 5~10w 在线用户 /15 分钟
  5. 5. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  6. 6. 手机之家的搜索 V1.0(1/3)  产品大全搜索  新闻 CMS  搜索  论坛搜索(默认只搜索标题)  论坛帖子及回复搜索  手机铃声,主题,电子书,软件搜索  二手交易搜索  其它版块的搜索
  7. 7. 手机之家的搜索 V1.0(2/3)  基本实现了多个业务模块需要的搜索功能  不同的搜索字段  不同的排序方式  不同的更新频率  Java 多进程,多线程实现
  8. 8. 手机之家的搜索 V1.0(3/3)  运行概况  Dev by chaoqian (http://www.longker.org/)  最初基于 Lucene 2.2 ,当前 2.4.1  开发时间: 07 年 12 月- 08 年 4 月  08 年 12 月随新版系统上线  部署在 Imobile-SV39-A49 上: • X86_64 , 8 cpu @ 2.50GHz , 32G mem • CentOS release 5.2, JDK64 1.6,  搜索: 35+ 万查询 / 天,高峰期 > 20 次 /s  更新: 平均 15 条 / 分钟
  9. 9. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  10. 10. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  11. 11. 需求背景 (1/3)  重建索引  索引字段变更  分词算法变更  搜索结果异常(记录重复,记录丢失)  索引文件意外损坏  V1.0 :半手工重建,重建过程需人工参与,重建过程中 不能正常更新  V1.5 :自动重建,重建过程中正常更新原来的索引
  12. 12. 需求背景 (2/3)  缩短更新周期(及时更新)  Google 更新手机之家内容的周期为 30 分钟左右  V1.0 索引更新周期为 10 - 15 分钟左右  V1.5 预期更新周期为 3 分钟,实际为 1-5 分钟
  13. 13. 需求背景 (3/3)  搜索大索引( V1.5 )  3300+ 万条记录的一个库, xml 原始文件 14G  V1.0 索引文件为 13 G ,无法快速更新  V1.5 索引文件为 3.9G (还是无法快速更新?)  V1.5 完整重建一次: 140 分钟
  14. 14. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  15. 15. 目标  及时更新( 3 分钟)  快速重建( < 2 小时)  可配置(拥抱需求变化)  可监控(运维友好)  SLA :永远可写,永远可读,异常的时候唯一的表现是 更新延迟  高性能,能承受较大的流量,并发压力
  16. 16. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  17. 17. 进度  2009 年 4 月 1 号 search 2.0 init  4 月 12 号,修改版本号为 1.5  6 月 1 号, search 1.5 在线上试运行  6 月 22 号,正式替换原来的搜索进程
  18. 18. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  19. 19. 设计 (1/8)  分离索引与存储,二次读取  分离读与写  分离 update 和 rebuild  拆分大库和小库  new open 小库(小库滚动), reopen 大库  新索引预热  更多: http://blog.fulin.org/category/tech/lucene
  20. 20. 设计 (2/8) 分离索引与存储,二次读取  索引里只存储 id ,其他的字段只索引不存储。  优点:  保持索引的大小为一个可接受的范围  提高索引读取速度  提高索引 cache 效率  缺点:搜索时需要额外的请求来获取其它必须的字段 ( lucene + db 方案)  衍生项目: blackdb , memcache 协议 + bdbJE 存储
  21. 21. 设计 (3/8) 分离读与写  优点:  降低编程复杂度  保证搜索服务的可用性,和可扩展性(可以将索引文件 分发到多台机器上,同时对外提供服务)  提升索引更新速度  缺点: • 需要移动索引文件 • 需要额外的索引更新逻辑( reopen ) • 无法使用当前设计中的 lucene 的 real-time search
  22. 22. 设计 (4/8) 分离 update 和 rebuild  Rebuild 的同时, update 正常更新  Rebuild 需要将重建这段时间的更新计入新的索引中  Rebuild 完成后,通知 update 切换到新索引上来,并 继续更新  进程间通信,当前使用最原始的基于文件的方式
  23. 23. 设计 (5/8) 拆分大库和小库  保证及时更新的同时,减少索引频繁同步(由写索引同 步到读索引)带来的 io 压力  更新频率 • 小库(最近更新库) 1 分钟 • 大库(历史库) 1 天到 1 周,可配置  并行搜索,加速搜索速度  问题: group by
  24. 24. 设计 (6/8) 拆分大库和小库(续)  增加新记录:  增加到小库  更新记录:  从大库中删除(标记删除)  从小库中删除(物理删除)  增加到小库  删除记录  从大库中删除(标记删除)  从小库中删除(物理删除)  定期合并小库到大库,并清空小库
  25. 25. 设计 (7/8) 搜索端索引更新  小库每次同步到一个新的文件夹中  保留最近打开的 n (2) 份小库索引目录  检测到新的索引到达,关闭一个最旧的,打开新的,预 热后标识为可用  检测到新的小库到达, reopen 大库(为了逻辑上的简 单起见,大小库同步更新)
  26. 26. 设计 (8/8) 新索引预热  目的:消除新打开的索引上前几次搜索慢的问题  实现: • 过去:遍历一遍新打开的索引,将数据都读入内存 • 引起 gc ,导致搜索暂停 • 对 lucene 内置的 cache 无贡献 • 现在:搜几个热门的词,并遍历结果集 • 将来:使用原来的 cache 填充新的 cache  预热完成后,再投入使用
  27. 27. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  28. 28. 实现 (1/8) IndexRebuilder 搜索管理后台 发出开始重建索引命令 IndexReceiver cron 发送重建数据 Rebuild xml data 端口 1986 Update xml data IndexUpdater DAL 数据更新 配置文件 同步通知搜索 Searcher 搜索客户端调用 Searcher 的 API 端口 1985
  29. 29. 实现 (2/8)IndexReceiver IndexRebuilder 搜索管理后台 IndexReceiver 发出开始重建索引命令 cron 发送重建数据 Rebuild xml data 端口 1986 Update xml data IndexUpdater DAL 数据更新 配置文件 同步通知搜索 Searcher 搜索客户端调用 Searcher 的 API 端口 1985
  30. 30. 实现 (2/8)IndexReceiver  By Java , Daemon 程序  监听端口,使用 SCGI 通讯协议  使用 Monkey 为底层 NIO 处理框架  接收客户端 post 过来的数据,并写入对应的目录  Update 和 rebuild 共用  为了保证原子性,先写入 .0.***.xml ,写入完成后,再 rename  Receiver 只负责写入, updater 和 rebuilder 稍后负责删除(备份)  对外提供服务,所以尽可能简单,单独进程
  31. 31. 实现 (3/8)IndexUpdater IndexRebuilder 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 Rebuild xml data 端口 1986 Update IndexUpdater xml data DAL 数据更新 配置文件 同步通知搜索 Searcher 搜索客户端调用 Searcher 的 API 端口 1985
  32. 32. 实现 (3/8)IndexUpdater  By Java , Daemon 程序  多个索引共用,每个索引起一个线程  可根据需要随时停止或启动单个索引更新的线程  AddShutdownHook 退出前 close 所有打开的 IndexWriter  功能: • 更新新数据到当前索引 • 合并大小库 • 拷贝当前索引的快照供搜索使用 • 切换 rebuild 出来的新索引
  33. 33. 实现 (4/8)IndexRebuilder IndexRebuilder 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 Rebuild xml data 端口 1986 Update xml data DAL 数据更新 配置文件 同步通知搜索 Searcher 搜索客户端调用 Searcher 的 API 端口 1985
  34. 34. 实现 (4/8)IndexRebuilder  By Java , Daemon 程序  多个索引共用,每个索引一个线程  重建索引时序图: Receiver 接到通 知,停止抄送 updater 数据。 客户端发送开 客户端发送重 客户端发送结 重建过程结束 始重建标识 建索引的数据 束重建标识 Receiver 开始抄送 update 数据到 receiver rebuild-update Updater 接到 Updater 正常 通知,切换索 T0 T1 T2 T3 T4 T5 更新 引。完成后通 updater 知 receiver Rebuilder Rebuilder Rebuilder 处理完 t0 开始工作 处理完重 到 当前的更新数据, rebuilder 建数据 通知 updater ,然后 自己退出 开始重建 重建数据 重建完成 时间线 接收完毕
  35. 35. 实现 (5/8)IndexRebuilder  切换索引过程 • Updater 删除自己当前的索引和未更新完的 xml 文件 • Updater 将 rebuilder 的索引和 rebuild-update 的 xml 文件 “据为己有” • 通知 receiver 停止抄送 • 继续正常的数据更新过程  可以证明这个过程中,不会有数据丢失,也不会有数据重复 • 前提: updater 的 xml 文件和 rebuild-update 的 xml 文件是完全相 同的(包括文件名和数据) • t3-t4 : 数据来自 rebuild-update • t4-t5 : rebuild-update 中的数据可以被直接忽略
  36. 36. 实现 (6/8)Transfer 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 Rebuild xml data 端口 1986 Update xml data DAL 数据更新 配置文件 同步通知搜索 Searcher Transfer 搜索客户端调用 Searcher 的 API 端口 1985
  37. 37. 实现 (6/8)Transfer  Bash 脚本,每个 indexId 一个进程,由 ControlCenter 或 monitor 启 动和停止  监控索引快照目录  快照目录下存在子目录,并且子目录中存在 copy.done.sign ( IndexUpdater 拷贝快照完成后 touch 的标识),则 rsync 子目录到 search  Rsync 的时候:先 rsync 大库快照,再 rsync 小库快照,都成功后再 rsync 一个 trans.done.sign  以 0 字节的 sign 文件作为标识,模拟两阶段提交,保证文件拷贝, 传输的原子性
  38. 38. 实现 (7/8)Searcher 搜索管理后台 发出开始重建索引命令 cron 发送重建数据 Rebuild xml data 端口 1986 Update xml data DAL 数据更新 配置文件 同步通知搜索 Searcher 搜索客户端调用 Searcher 的 API 端口 1985
  39. 39. 实现 (7/8)Searcher  By Java , Daemon 程序  使用 Monkey 为底层 NIO 处理框架  使用 SCGI 通讯协议  启动后第一件事: warmUpAllIndex  当有新索引到达的时候,在后台打开,预热后,再投入使用  关闭旧的 IndexSearcher 实例的前提:当前没有线程还在使用它 • 方法:计数 ( get +1 , return -1 ,为 0 表示没有被使用)  Stat 统计
  40. 40. 实现 (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}
  41. 41. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  42. 42. 性能简单测试结果  Apache bench  ­n 1000 ­c 10   ( 99%    545ms     100%  32838ms )  ­n 2000 ­c 20   ( 90%   1171ms    99%   4106ms )  库大小: 4.1G 条目数: 3300+w 更新周期: 1 分钟  总请求 3000 ,慢查询 ( 耗时大于 1 秒 ) 比例 4%  搜索词:手机之家论坛首页的板块名,随机  最长: 37s  > 10s : 22  (索引切换, gc )  2s­10s : 38  1s­2s : 62
  43. 43. 性能对比测试结果 (1/6)  Apache bench  库大小: 4.1G ,条目数: 3300+w ,更新周期: 1 分钟  搜索词:手机之家论坛板块名随机  对比条件  停止更新与正常更新  分配索引 1 倍大小的内存与 2 倍大小的内存  并发 10 个与 20 个  Document Length:     1529 bytes  Total transferred:       3447737 bytes (1722053 bytes)  HTML transferred:    3085737 bytes (1541053 bytes)
  44. 44. 性能对比测试结果 (2/6) Time taken for tests 停止更新 正常更新 分配索引大小1倍内存 ab -n 2000 -c 20 21.85 64.17 分配索引大小1倍内存 ab -n 1000 -c 10 13.1 11.93 分配索引大小2倍内存 ab -n 2000 -c 20 22.8 22.89 分配索引大小2倍内存 ab -n 1000 -c 10 10.37 10.74 备注:单位为秒
  45. 45. 性能对比测试结果 (3/6) Requests per second 停止更新 正常更新 分配索引大小1倍内存 ab -n 2000 -c 20 91.52 31.17 分配索引大小1倍内存 ab -n 1000 -c 10 76.33 83.83 分配索引大小2倍内存 ab -n 2000 -c 20 87.71 87.39 分配索引大小2倍内存 ab -n 1000 -c 10 96.45 93.13
  46. 46. 性能对比测试结果 (4/6) Time per request 停止更新 正常更新 218.531 641.674 分配索引大小1倍内存 ab -n 2000 -c 20 10.927 32.084 131.015 119.288 分配索引大小1倍内存 ab -n 1000 -c 10 13.102 11.929 228.012 228.870 分配索引大小2倍内存 ab -n 2000 -c 20 11.401 11.444 103.676 107.375 分配索引大小2倍内存 ab -n 1000 -c 10 10.368 10.738 备注: [ms] (mean) (mean, across all concurrent requests)
  47. 47. 性能对比测试结果 (5/6) Transfer rate 停止更新 正常更新 分配索引大小1倍内存 ab -n 2000 -c 20 154.06 52.47 分配索引大小1倍内存 ab -n 1000 -c 10 128.46 140.98 分配索引大小2倍内存 ab -n 2000 -c 20 147.63 146.82 分配索引大小2倍内存 ab -n 1000 -c 10 162.07 156.39 备注: (Kbytes/sec)
  48. 48. 性能对比测试结果 (6/6) 耗时比例 停止更新 正常更新 50% 59 50% 162 95% 905 90% 1403 分配索引大小1倍内存 ab -n 2000 -c 20 100% 1525 98% 5738 50% 34 50% 24 99% 978 98% 750 分配索引大小1倍内存 ab -n 1000 -c 10 100% 1420 100% 3600 50% 61 50% 41 95% 906 95% 988 分配索引大小2倍内存 ab -n 2000 -c 20 99% 1519 99% 1678 50% 28 50% 27 99% 744 99% 698 分配索引大小2倍内存 ab -n 1000 -c 10 100% 905 100% 764 备注:百分比 毫秒
  49. 49. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  50. 50. 部署上线  运行概况  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 条 / 分钟
  51. 51. 生产环境运行观察  Slow search  比例: >1s 1.x%; >2s 0.2%  机器负载 : 49(search): 1~3; 50(update): <1  内存消耗: search: ­Xms4096M ­Xmx4096M  Cpu 消耗 :多核之间平均比较分配  索引平均更新速度: ~1 分钟  索引延迟率(因各种原因导致延迟更新的比例)
  52. 52. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  53. 53. 开发中的一些收获 (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 链接替代真实的 大文件拷贝动作
  54. 54. 开发中的一些收获 (2/5)  Kill  的问题  Never kill smart frog, don't kill ­9  Java use Runtime.getRuntime().addShutdownHook  to do the cleaning  things  Lucene IndexWriter need close!  server  程序,都应该考虑信号捕捉和处理的问题( java  程序容易忽 略这个问题)
  55. 55. 开发中的一些收获 (3/5)  更新太频繁导致的磁盘 IO 问题 – 同一台机器目录之间同步:没有问题 – 一拖二,一拖三  暂时解决 – 建索引的机器不提供搜索服务 – rsync 限速
  56. 56. 开发中的一些收获 (4/5)  GC  引起的服务暂停  多个索引共用 Search 进程  每个索引维持了多个 searcher  索引更新太过频繁: 30 秒  暂时解决  纵向拆分:将两个最大的库拆分到单独的进程  减少 searcher 个数到 2  个  谨慎的选择 gc 的类型,并调整 gc 的参数  G1 改进不明显,不稳定
  57. 57. 开发中的一些收获 (5/5)  Java server  程序的 trouble shooting  性能问题: jprofile  内存问题: jmx  + jconsole  线程问题: Thread.currentThread().setName("diffDetect­" +  indexId);   详细的 log : log4j  常用的脚本: logslowsearch.sh  开发调试阶段:方便的重新编译并重启脚本  线上服务阶段:完善的监控及报警机制
  58. 58. 目录  关于手机之家  过去:手机之家搜索 V1.0  现在:手机之家搜索 V1.5  需求背景  目标  进度  设计  实现  测试  上线,运维  经验分享  将来:手机之家搜索 V2.0
  59. 59. 持续改进  配置文件改动检测,自动重新加载  更智能的处理索引字段变更  分词算法改进  搜索关键字数据挖掘(搜索新词自动发现)  搜索建议,搜索联想功能  排序算法改进  全站整合搜索  准实时搜索
  60. 60. Lucene 3  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: 等到花儿也谢了
  61. 61. 定制功能  在某些情况下作为数据库的替代数据源  类似淘宝搜索,按多个字段筛选,过滤,排序  当前解决方案:使用 sql 从数据库中选取数据  问题:数据可能因为业务逻辑的设计而分散在多个不同的 库,表中,联表查询问题,并发压力问题  难点:非典型的复杂的查询条件,如 group by
  62. 62. 参考资料  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
  63. 63. 关于我们  关于 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
  64. 64. 更多讨论  Java GC  的问题  巨大的索引库与频繁更新如何共存  全站整合搜索,后台技术实现与前台表现设计  更多

×