ElasticSearch第3篇 大数据处理3大问题(“10000条”问题解决方案、hits to ...

打印 上一主题 下一主题

主题 1930|帖子 1930|积分 5790

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
“10000条”问题(个人称谓)


  • 症状: 在数据量不大的环境下,可能还会使用from + size的传统分页方式,但是数量受限,只能取前10000条的数据。
  • 缘由:ES限值10000条,是ES团队挑选一个不大不小的数作为阈值,为了避免深度分页的策略。
  • 调整:max_result_window 用于控制在搜索查询中可以检索到的最大文档数,是有符号int范例,最大可设置231 - 1,调大可以,但随着数据量(不是max_result_window 值)增长会有性能问题。
  1. 返回bool
  2. $params = [
  3.     'index' => 'performance_test',
  4.     'body'  => [
  5.         'index' => [
  6.             'max_result_window' => 2147483647 //用于控制在搜索查询中可以检索到的最大文档数,有符号int类型,最大可设置2^31 - 1,但随着数据量增加会有性能问题。
  7.         ]
  8.     ]
  9. ];
  10. $response = $client->indices()->putSettings($params);
  11. dd($response->asBool());
复制代码
hits total值统计总数不精确解决方案(另一种“10000条”问题)


  • 症状:调用search()方法,可能查询到的结果有20000条(count()方法统计得出),ES也返回有10000条数据匹配。
  1. {
  2.     "took": 101,
  3.     "timed_out": false,
  4.     "_shards": {
  5.         "total": 1,
  6.         "successful": 1,
  7.         "skipped": 0,
  8.         "failed": 0
  9.     },
  10.     "hits": {
  11.         "total": {
  12.             "value": 10000, //这里的统计结果不准,解决的就是这里的问题
  13.             "relation": "gte"
  14.         },
  15.         "max_score": 1.0342529,
  16.         "hits": [
  17.             ......
  18.     }
  19. }
复制代码

  • 缘由:ES团队挑选一个不大不小的数作为阈值,处于性能和业务思量,毕竟用户基本没耐心看数万条之后的数据。
  • 解决:添加track_total_hits为true即可。
  1. $params = [
  2.     'index' => 'performance_test',
  3.     'body'  => [
  4.         'query' => [
  5.             'match' => [
  6.                 'content' => '的'
  7.             ]
  8.         ],
  9.         'track_total_hits' => true
  10.     ]
  11. ];
  12. $response = $client->search($params);
  13. dd($response->asArray());
复制代码
大数据深度分页性能问题


  • 极简概括:在大数据环境下,查询很多页后的数据,ES响应速度会变慢甚至崩溃。
  • 问题由来:假设亿级数据,要查询某页(页数很大)之后的数据,ES底层就必要把前面很多页的数据都要过一遍才气定位到指定页,这是一个巨大的开销。以是大数据深度分页一般不推荐使用from + size的传统分页方式。也算是一类业界难题。
  • 测试:花了3个小时向ES插入了1.09亿条中文数据,实测每页显示2条数据,翻页到2000万页,效果如下:
  1. 深度分页会把内存干爆,导致java程序OutOfMemoryError,ES进程被终止。
  2. [2024-07-27T09:01:39,206][INFO ][o.e.c.r.a.AllocationService] [localhost.localdomain] current.health="YELLOW" message="Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[performance_test][0]]])." previous.health="RED" reason="shards started [[performance_test][0]]"
  3. [2024-07-27T09:01:58,487][INFO ][o.e.m.j.JvmGcMonitorService] [localhost.localdomain] [gc][21] overhead, spent [331ms] collecting in the last [1.1s]
  4. [2024-07-27T09:02:00,495][INFO ][o.e.m.j.JvmGcMonitorService] [localhost.localdomain] [gc][23] overhead, spent [334ms] collecting in the last [1s]
  5. [2024-07-27T09:02:02,674][WARN ][o.e.m.j.JvmGcMonitorService] [localhost.localdomain] [gc][24] overhead, spent [2s] collecting in the last [2.1s]
  6. [2024-07-27T09:02:04,181][WARN ][o.e.m.j.JvmGcMonitorService] [localhost.localdomain] [gc][25] overhead, spent [1.2s] collecting in the last [1.5s]
  7. [2024-07-27T09:02:07,837][WARN ][o.e.m.j.JvmGcMonitorService] [localhost.localdomain] [gc][26] overhead, spent [3.5s] collecting in the last [1.8s]
  8. java.lang.OutOfMemoryError: Java heap space
  9. Dumping heap to data/java_pid20168.hprof ...
复制代码
索引与映射结构(下文要用)
  1. $params = [
  2.     'index' => 'performance_test',
  3.     'body'  => [
  4.         'settings' => [
  5.             'analysis' => [
  6.                 'analyzer' => [
  7.                     'ik_analyzer' => [
  8.                         'type'      => 'ik_max_word',
  9.                     ],
  10.                 ],
  11.             ],
  12.         ],
  13.         'mappings' => [
  14.             'properties' => [
  15.                 'id' => [
  16.                     'type'     => 'integer',
  17.                 ],
  18.                 'content' => [
  19.                     'type'     => 'text',
  20.                     'analyzer' => 'ik_analyzer',
  21.                 ],
  22.             ],
  23.         ],
  24.     ],
  25. ];
复制代码
方案1(业务需求优化)

从业务需求源头限定深度分页。
例如某度搜索,深度分页也就50~76页是末了一页(甚至不让用户从小页码直接跳转大页码),某宝搜索最大100页
这个信息自然无法全部汇集。它们的定位不是要汇聚更全的信息,而是找出最精准的(或者金主爸爸打钱)数据。
人为的也几乎没有耐心看完那么多页的词条。
可以说深度分页并非适合所有业务场景,而是要做权衡(说人话就是要和产品经理沟通以上文案)。
方案2 (Scroll API)


  • 辅助理解:类比愚公移山。操尴尬刁难象太大,那就每次少量的处理,然后不停的分批迭代。这和分页的分批处理头脑相似,不过不用手动处理分页问题。
  • 优点:答应处理超大规模的数据集。
  • 缺点:不支持页跳转(第M页跳转N页),只支持递增页和递减页。scroll查询的相应数据是非实时的,如果遍历过程是,其它请求要返回的数据进行了写操纵,是查询不到最新的结果的。
  1. $params = [
  2.     'index' => 'performance_test',
  3.     'scroll' => '1m', //用于设置滚动上下文的存活时间,1m是一分钟,过了这个时间,scroll_id参数就失效了,有这个id,好比创建了一个专门的任务,让ES去处理这件事
  4.     'size' => 2,
  5.     'body'  => [
  6.         'query' => [
  7.             'match' => [
  8.                 'content' => '袁隆平' //要搜索整个文档中,包含袁隆平的
  9.             ]
  10.         ]
  11.     ]
  12. ];
  13. $response = $client->search($params);
  14. dump($response->asArray()); //常规的处理
  15. $scroll_id = $response['_scroll_id'];
  16. while (true) {
  17.     $response = $client->scroll([
  18.         'scroll_id' => $scroll_id,
  19.         'scroll'    => '1m'
  20.     ]);
  21.     // 检查是否有数据返回
  22.     if (empty($response['hits']['hits'])) {
  23.         break;
  24.     }
  25.     // 处理数据,这里只做打印
  26.     foreach ($response['hits']['hits'] as $hit) {
  27.         dump($hit['_source']);
  28.     }
  29.     // 更新 scroll ID
  30.     $scroll_id = $response['_scroll_id'];
  31. }
  32. // 清理Scroll请求,释放资源
  33. $client->clearScroll([
  34.     'scroll_id' => $scroll_id
  35. ]);
复制代码
方案3 (Search After 推荐方案)


  • 辅助理解:上一页的末了一条数据,作为当前页的起始偏移量。操尴尬刁难象太大,那就每次少量的处理,然后不停的迭代,这和分页的分批处理头脑相似。
  • 优点:ES针对深度分页的新解决方案,不会有太多的性能问题。
  • 缺点:必须指定排序字段,否则起始偏移量参考系参数无法使用。
  1. $params = [
  2.     'index' => 'performance_test',
  3.     'body'  => [
  4.         'query' => [
  5.             'match_all' => new stdClass()
  6.         ],
  7.         'sort'  => [
  8.             ['id' => 'asc']
  9.         ],
  10.         'size'  => 2 //这个排序是必须的,不一定是id,但通常是id,排序是为了去上一条数据的时候定位不会错乱
  11.     ]
  12. ];
  13. //发起第一次查询,获取第一页结果
  14. $response = $client->search($params);
  15. //模拟处理第一页的结果
  16. foreach ($response['hits']['hits'] as $hit) {
  17.     //模拟处理
  18.     dump($hit['_source']['content']);
  19. }
  20. /**
  21. * @function SearchAfter方法封装
  22. * @param   $client   object      ElasticSearch对象
  23. * @param   $params   array       search()所需要的参数
  24. * @param   $response object|null search()方法查询出来的结果
  25. * @return  object
  26. */
  27. function searchAfter($client, $params, $response) {
  28.     if(is_object($response)) {
  29.         $response = $response->asArray();
  30.     }
  31.     if(empty($response['hits']['hits'])) {
  32.         return null;
  33.     }
  34.     $last_data = end($response['hits']['hits']);
  35.     //在其余查询条件不变的情况下,只修改search_after值。
  36.     $params['body']['search_after'] = $last_data['sort'];
  37.     //然后再次查询
  38.     $response = $client->search($params);
  39.     return $response;
  40. }
  41. //模拟处理第二页的数据
  42. $response = searchAfter($client, $params, $response);
  43. dd($response->asArray());
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表