• Solr优化案例分析


     随着umc接入主机的数量越来越多,每天产生的syslog日志数量也在剧增, 之前一天产生的syslog数量才不

    到1W,随着整个集团的网络设备不端接入,导致现在每天产生的syslog数量大概在180w左右,而这些syslog对

    网络和PE同学排查线上网络设备问题又是十分重要的,他们的要求是可以提供查询最近3个月的syslog, 保存一

    年的syslog,在7月份的时候,针对量不多的情况,针对mysql单表做了索引,后来又做了单表备份,但是查询的

    速度还是无法让人接受,后面又结合页面针对mysqlcount(*)语句做了优化: 使用count(*)语句只是在第一次查询

    的时候查询一次,后面保存下来,在后面分页查询的时候,不再需要再查询总数量,即不调用count(*)之类的sql

    语句,结果在翻页时,速度还是比较快,但首次查询的时候,还是非常非常慢,但这毕竟只是一个临时的解决方

    案;后面结合syslog搜索的业务情况,结合各种技术参考,最终选择了使用Solr搜索引擎来解决syslog查找慢的

    问题;

            在做压力测试的时候,发现性能问题:在数据量不大(<1000W)的时候,用Solr搜索是比较快,但随着着

    syslog数据量的不断增多,写索引和搜索的速度越来越不能让人接受,因此不得不考虑对Solr进行优化;

           网上了搜了一下Solr优化的大体方案,主要从如下两个步骤来进行优化:

           1.  Solr系统层面;

           2.  索引字段优化;

           针对Solr系统层的优化,主要有如下的方法:

              1. 适当调大Solr查询缓存;

      2.适当增加Solr集群的切片;

              3根据查询的业务场景,适当调

           整索引合并的时间,等等一系列通用的做法,针对这些修改以后,发现修改前后,对查询的性能没有本质

        的提高;

           针对索引字段的优化,就是针对添加到Lucence中文档各索引字段的优化, syslog索引字段主要的字段有:

           id(Integer),

           ip(String),

           log_level(String), 

          log_value(String)

          syslog_time(Date), 

    要针对这些字段优索引优化,就要首先分析索引查找的过程,要做到字段优化索引,可以从两方面考虑,

    1. 减少字段索引存储量, 

    2. 提高查询索引比较的时间, 针对这两点,

    可以毫不犹豫的考虑把String类型的字段向Integer或Long类型之类的整型字段映射转换(整型的索引存储空间

    一般比字段串所占的存储索引空间要小;整型的查找比较速度比字段串类型要快,其实这也是数据库查找优化

    的一个方面);基于这样的考虑,ip、log_level和syslog_time可以向相应的整型映射,各个字段具体的映射方

    法如下:

           1. ip 地址向整形映射,这个问题比较好解决,在计算机网络协议中,在底层是会把ip地址转换成对应的无

    符号长整型,但由于java没有无符号这个概念,因此可以考虑把ip地址向整型或长整型做相互映射转换,但因为

    整型比长整型占用的字节更少,因此采用整型,具体转换代码如下:

    // 把ip地址转换成整形

    1.  
      public static Long ipToInteger(String ip) {
    2.  
       
    3.  
      String[] ips = StringUtils.split(ip,'.');
    4.  
      if (ips == null || ips.length < 4){
    5.  
      throw newRuntimeException("ip地址非法!");
    6.  
      }
    7.  
      Integer integerIP = 0;
    8.  
      Integer ip0= Integer.parseInteger(ips[0]);
    9.  
      Integer ip1= Integer.parseInteger(ips[1]);
    10.  
      Integer ip2= Integer.parseInteger(ips[2]);
    11.  
      Integer ip3= Integer.parseInteger(ips[3]);
    12.  
      if (ip0 > 255) {
    13.  
      throw newRuntimeException("ip0地址非法:" + ip0);
    14.  
      }
    15.  
      if (ip1 > 255) {
    16.  
      throw newRuntimeException("ip1地址非法:" + ip1);
    17.  
      }
    18.  
      if (ip2 > 255) {
    19.  
      throw newRuntimeException("ip2地址非法:" + ip2);
    20.  
      }
    21.  
      if (ip3 > 255) {
    22.  
      throw newRuntimeException("ip3地址非法:" + ip3);
    23.  
      }
    24.  
      integerIP |=( ip0 << 24) & (0xff000000);
    25.  
      integerIP |=( ip1 << 16) & (0x00ff0000);
    26.  
      integerIP |=( ip2 << 8) & (0x0000ff00);
    27.  
      integerIP |=( ip3 << 0) & (0x000000ff);
    28.  
      return integerIP ;
    29.  
      }
    1.  
      /**
    2.  
      * int类型到ip转换
    3.  
      * @param integerIP
    4.  
      * @return
    5.  
      */
    6.  
      public static String integerToIP(IntegerintegerIP) {
    7.  
       
    8.  
      String ip = "";
    9.  
      Integer ip0= (integerIP & 0xff000000) >>> 24;
    10.  
      Integer ip1= (integerIP & 0x00ff0000) >> 16 ;
    11.  
      Integer ip2= (integerIP & 0x0000ff00) >> 8;
    12.  
      Integer ip3= (integerIP & 0x000000ff) >> 0;
    13.  
      ip = ip0 + "." + ip1 +"." + ip2 + "." + ip3;
    14.  
      return ip;
    15.  
      }
     

    2. log_level 只有8种值,分别是:Emergency, Alert, Critical, Error, Warning,Notice,Informational,

    Debug; 这个好转换,直接按下表的方式做相互映射即可

    Emergency

    0

    Alert

    1

    Critical

    2

    Error

    3

    Warning

    4

    Notice

    5

    Informational

    6

    Debug

    7

    3. 把syslog_time转换成Long类型,这个更容易,java中Date类型有一个getTime()方法

            经过把这三个字段都转换成整型以后,索引插入和搜索速度都提高不少,特别是搜索速度有非常非常明显

    的提升,特别是在数据量超过2亿的时间,效果更明显到此,字段优化完毕;

            但此时还发现一个问题:一次查找返回的数据量太多,导致存在翻页慢的问题,例如:一次返回10W页数

    据,如果一下翻到第10W页,查询的速度会非常非常慢甚至可能出现Solr集群全部宕机的情况,这是不能接受的,

    因为我们页面上提供了翻到最后一面的功能, 虽然用户一般情况下不会这么做,但万一不小点错了,整个线上

    Sorl集群就全部挂了,怎么办?这个问题,让我想起了mysql里的翻页的问题,下面两条SQL语句:

    • select * from umc_syslog where syslog_time between date1 and date2  order by id limit 0, 10;
    • select * from umc_syslog where syslog_time between date1 and date2  order by id limit 10000, 10;

           我们知道,第一条SQL语句查询的速度非常快,第二SQL语句查询的速度非常慢,在系统压力较大的情况下,

    可能会把mysql服务弄挂,根本原因在于limitstart, rows语句,mysql会在扫描时,会扫过满足结果的前start行的

    记录,然后才读取len行的数据,如果start特别大,会非常慢,在高性能mysql这本书中介绍了各种解决分页问

    题的优化,如延迟关联、阶递分页等,其中阶递分页可以在Solr中实现,但我们页面不允许这么做,不然早就

    用Hbase来解决了,怎么办,在无解决的情况下,突然发现,Solr查询中有order by排序的问题,适当用order by

    可以很好来解决limit 分页慢的问题:

           假设 一个Solr查询按id 降序排序返回,假设返回10W行记录,前5W条记录可以随机分页查询到,但后面

    5W条记录很难随机分页查询到,造成这个问题的原因是按id降序返回,如果适当修改一下查询语句,速度可

    能会有所提高,因为Solr查询每次都会返回总的记录条数,这个总的记录条数是已知的, 记为total, 作如下处

    理:如果start 小于等于 (total >> 1),则从前往后读;否则就从后往前读,然后,再把返回的结果逆序,显示

    即可,伪代码如下;

    1.  
      if start <= (total >> 1)
    2.  
      query order by id desc limit start, rows;
    3.  
      else
    4.  
      calculate new start as start';
    5.  
      calculate new rows as rows';
    6.  
      query order by asc limit start', rows';
    7.  
      reverse the return data;
    8.  
      end

    程序中相关代码:

    1.  
      // 是否是按id升序查询, 默认是false
    2.  
      boolean isAscend = false;
    3.  
       
    4.  
      // 升序或降序查询判断
    5.  
      if (totalCount == 0) {
    6.  
      isAscend = false;
    7.  
      } else {
    8.  
      if (start >= (totalCount >> 1) ) {
    9.  
      isAscend = true;
    10.  
      }
    11.  
      }
    12.  
       
    13.  
      if (!isAscend) {
    14.  
      realStart = start;
    15.  
      realRows = rows;
    16.  
      } else {
    17.  
      Integer count = totalCount.intValue();
    18.  
      realStart = (start + rows)>= count? 0 : (count - (start +rows));
    19.  
      realRows = (start + rows)>= count? (count - start + 1) : rows;
    20.  
      }
    21.  
      solrQuery.setStart(realStart);
    22.  
      solrQuery.setRows(realRows);
    23.  
       
    24.  
      if (!isAscend) {// id降序
    25.  
      solrQuery.setSort("id", ORDER.desc);
    26.  
      } else { // id 升序
    27.  
      solrQuery.setSort("id", ORDER.asc);
    28.  
      }
    29.  
       
    30.  
      // 逆序处理
    31.  
      if (isAscend) {
    32.  
      int size = syslogVOList.size();
    33.  
      int left = 0;
    34.  
      int right = size - 1;
    35.  
      while (left < right) {
    36.  
      SyslogVO leftSyslogVO =syslogVOList.get(left);
    37.  
      SyslogVO rightSyslogVO =syslogVOList.get(right);
    38.  
      syslogVOList.set(left, rightSyslogVO);
    39.  
      syslogVOList.set(right, leftSyslogVO);
    40.  
      left ++;
    41.  
      right --;
    42.  
      }
    43.  
      }

    经过这个过程的处理之后,向后翻页查询的速度快了很多,至少不会出现宕机的现象;

    查询页面如下:

      在相同的条件下:

      优化后Solr查询的时间:

      

      

      优化前mysql的查询时间:

     

    其实这个技巧在mysql分页查询,数据量非常大的时候也适用;

  • 相关阅读:
    C++:怎样把一个int转成4个字节?
    安装虚拟机
    [Flux] 1. Development Environment Setup
    [CSS] Animating SVG
    [Node.js] Scraping Dynamic JavaScript Websites with Nightmare
    [React] React Fundamentals: Integrating Components with D3 and AngularJS
    [React] React Fundamentals: with-addons
    [JavaScript] Array.prototype.reduce in JavaScript by example
    [CSS] @keyframes
    [CSS] Transforms
  • 原文地址:https://www.cnblogs.com/cuihongyu3503319/p/9473471.html
Copyright © 2020-2023  润新知