基于传统数据查询慢的优化方案


1、背景
由于业务系统使用的是传统关系型数据库sqlserver、mysql,数据随着时间的日积月累,导致数据量不断增大,最后导致系统的查询性能比较低下,用户体检非常不友好。

在传统关系型数据库上,适合使用solr作为优化方式的几种情况:

      1)、基于字段的模糊查询

      2)、单表数据量过大,创建索引之后查询依旧比较慢     

下面简单介绍一下基于solr来优化查询效率的一种实现方式。

2、solr安装:以windows为例
Solr是一个高性能,采用Java开发。Solr基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的搜索引擎。

      Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。

      Solr 的特征:
             高级全文搜索能力:由Lucene ™提供支持,Solr可实现强大的匹配功能,包括短语,通配符,联接,分组以及任何数据类型
             针对大容量流量进行了优化:
             拥有基于标准的开放式接口- XML、JSON 和 HTTP
             综合的管理界面
             实现轻松监控
             高度的可扩展性和容错性
             灵活的适应性,易于配置
             能够实时索引
             可扩展的插件架构

solr链接
Solr 各版本下载地址:Index of /dist/lucene/solr

                本手册翻译自 Solr 官方网站:Resources - Apache Solr

                详细的solr中文手册链接:

Solr官方文档_w3cschool
Solr采用Java5开发,是建立在Apache Lucene上的流行的、快速的开源企业搜索平台。Solr具有高度可靠、可伸缩和容错能力,提供分布式索引、复制和负载平衡查询、自动故障转移和恢复、集中配置等功能。_来自Solr官方文档,w3cschool编程狮。
https://www.w3cschool.cn/solr_doc/

solr安装
在安装solr之前,首先保证已经正确安装了Java。

               solr从6.0之后需要Java1.8,所以如果使用Solr6.0及其以上版本,请确保Java版本在1.8之上

               将Solr下载之后解压在电脑的某个目录,我解压到了C盘目录下.

1)、下载解压之后,solr的结构说明:如下图

在这里插入图片描述
2)、安装
将solr-7.7.0.zip文件拷贝到C盘;(或者其他盘都可以,只要目录中不要出现中文就行。)
解压solr-7.7.0.zip文件,得到solr-4.10.2目录。
进入solr的bin目录中,运行cmd 执行命 solr.cmd start 如下图所示:

在这里插入图片描述

3)、启动:浏览器中输入:http://localhost:8983/solr/

在这里插入图片描述
到此,solr就安装成功了

solr创建Core
创建core有两种方式:通过界面操作创建;通过命令创建

通过界面操作创建:

1)、在C:\home\solr-7.7.0\server\solr内创建一个core_01文件夹:

在这里插入图片描述

2)、将C:\home\solr-7.7.0\server\solr\configsets\sample_techproducts_configs\conf文件夹复制到core_01内
3)、修改core_demo/conf/solrconfig.xml文件,一般不需要修改
4)、在http://localhost:8983/solr内添加core_01实例,实例名称一定要与您第一步创建的文件夹名称相同

在这里插入图片描述
通过命令创建:

进入solr安装bin,执行一下命令:

solr.cmd create -c core_01

设置中文分词器
将中文分词器的jar放到C:\home\solr-7.7.0\server\solr-webapp\webapp\WEB-INF\lib
在这里插入图片描述
找到要配置分词器的core下面的managed-schema文件,添加一下内容:
在这里插入图片描述

3、solr和spring boot项目整合
整合依赖

org.springframework.boot spring-boot-starter-data-solr 配置application.properties

spring.data.solr.host=http://localhost:8983/solr
简单写一下模糊查询接口api

@Override
public List<result_am_electro_data> searchByName(String keywords) throws Exception {
//创建查询
SolrQuery query = new SolrQuery();
System.out.println(“输出参数:”+keywords);
//精准查询,查询关键词用英文双引号
query.setQuery(“st_name:”“+keywords+”“”);
//模糊查询,关键词不需要双引号
//query.setQuery(“st_name:”+keywords);
QueryResponse res = solrClient.query(“result_am_electro_data”, query);
//获取结果集
SolrDocumentList results = res.getResults();
System.out.println(“输出结果:”+results.getNumFound());
List<result_am_electro_data> list = new ArrayList<>();
if(results.getNumFound() > 0){
for(SolrDocument doc:results){
result_am_electro_data en = new result_am_electro_data();
en.setApp_pc(Double.valueOf(doc.get(“acc_pc”).toString()));
en.setGuid(doc.get(“guid”).toString());
en.setId(doc.get(“id”).toString());
en.setSt_code(doc.get(“st_code”).toString());
en.setSt_name(doc.get(“st_name”).toString());
en.setTm(doc.get(“tm”).toString());
en.setSt_lang(BigDecimal.valueOf(Double.valueOf(doc.get(“st_long”).toString())));
en.setSt_lat(BigDecimal.valueOf(Double.valueOf(doc.get(“st_lat”).toString())));
list.add(en);
}

}
return list;

}
4、solr数据初始化方式
将数据库数据初始化到solr中,比较常用的两种方式有通过配置文件进行初始化,也可以通java API方式同步,下面介绍一下sqlserver数据同步到solr的两种方式。

通过配置文件:
1、先将sqlserver的驱动包以及solr同步的依赖包拷贝到C:\home\solr-7.7.0\server\solr-webapp\webapp\WEB-INF\lib:
在这里插入图片描述
2、创建solr的core,这里使用命令创建:solr.cmd create -c core_02

3、然后修改配置文件

进入core_02的conf目录,修改solrconfig.xml配置文件,修改内容如下:

在标签后填写如下内容

      data-config.xml   

再conf目录下创建data-config.xml文件,内容如下

3.1单表同步至solr的data-config.xml配置:

<?xml version="1.0" encoding="UTF-8" ?>

单表最后再修改conf下的managed-schema,修改内容如下

3.2多表关联查询结果同步至solr的data-config.xml配置:

<?xml version="1.0" encoding="UTF-8" ?>

多表关联最后再修改conf下的managed-schema,修改内容如下

4、重启solr,在页面上进行如下操作,即可将数据从sqlserver同步到solr中

停止命令:solr.cmd stop all

启动命令:solr.cmd start
在这里插入图片描述

或者直接点击下面链接即可实现全量导入:

http://localhost:8983/solr/core_04/dataimport?command=full-import&clean=true&commit=true&wt=json&indent=true&verbose=false&optimize=false&debug=false&id=1

5、简单查询一下同步结果

在这里插入图片描述

通过API方式
通过编写solr API方式将sqlserver数据同步到solr中,此代码参数主要有要同步的solrcore名称,关系型表的主键、数据库用户名、密码、驱动器、数据库连接地址、要操作的数据库名称、solr连接地址以及同步方式(增量同步还是初始化),

因此一个代码可以实现增量和全量的自动切换。

具体代码如下:

public class SyncDateToSolr {
public static void main(String[] args) {
String tableName = “”;
String uniqueKey = “”;
String userName = “”;
String password = “”;
String driver = “”;
String solrurl = “”;
String dburl = “”;
String db = “”;
String mode = “”;

    if(args.length < 9){
        System.out.println("程序没有参数退出");
        System.exit(0);
    }else{
        tableName = args[0];
        uniqueKey = args[1];
        userName = args[2];
        password = args[3];
        driver = args[4];
        solrurl = args[5];
        dburl = args[6];
        db = args[7];
        mode = args[8];

    }
    Properties proper = GetProperties.getProper();
    String sql = proper.getProperty(tableName);

    String sqlUrl = dburl+";DatabaseName="+db;

    try {
        saveSqlserverToSolr(tableName,uniqueKey,sql,userName,password,driver,solrurl,sqlUrl,mode);
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("程序执行结束");

}

public static void saveSqlserverToSolr(String core,String key,String sql,String USERNAME,String PASSWORD,String DRIVERNAME,String solrUrl,String sqlurl,String mode) throws Exception{
    String yesday = DateUtil.getYesterDate();
    String solrurl = "";
    String selectSqlInc = "";
    if(mode.equals("inc")){
        selectSqlInc = sql + yesday+"';";
        solrurl = solrUrl+core;
    }else{
        selectSqlInc = sql;
        core=core.replaceAll("\\d+","");
        solrurl = solrUrl+core;
    }
    Connection connection;
    PreparedStatement preparedStatement;
    HttpSolrClient solrClient = new HttpSolrClient.Builder(solrurl).withConnectionTimeout(10000).withSocketTimeout(60000).build();
    Class.forName(DRIVERNAME);
    connection = DriverManager.getConnection(sqlurl, USERNAME, PASSWORD);
    System.out.println("拼接sql:"+selectSqlInc);
    Statement sm = connection.createStatement();
    ResultSet rs = sm.executeQuery(selectSqlInc);
    ResultSetMetaData metaData = rs.getMetaData();
    int count = metaData.getColumnCount();
    String[] keys = key.split(",");
    StringBuffer sb = new StringBuffer();
   int m=0;
    while (rs.next()) {
        SolrInputDocument doc = new SolrInputDocument();
        for(int i = 1;i <= count;i++){
            String columnName = metaData.getColumnName(i);
            String value = rs.getString(columnName);
            for(int j = 0;j < keys.length;j++){
                if(keys[j].equals(columnName)){
                    sb.append(value).append("_");
                }else{
                    continue;
                }
            }
            doc.setField(columnName,value);
        }
        sb.delete(sb.length()-1,sb.length());
        doc.setField("id", sb.toString());
        sb.delete(0,sb.length());
        solrClient.add(doc);
        m++;
        System.out.println("已经处理:"+m+"条数据了");
    }
    
    solrClient.commit();
    solrClient.close();
    sm.close();
    connection.close();
}

}

代码中读取配置文件的代码如下:

public class GetProperties {

public static Properties getProper() {
    Properties properties = new Properties();
    try {
        //增量,一天

// InputStream is = GetProperties.class.getClassLoader().getResourceAsStream(“inc_sql.properties”);
//全量
InputStream is = GetProperties.class.getClassLoader().getResourceAsStream(“full_sql.properties”);

        properties.load(is);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return properties;
}

}

其中full_sql.properties的文件内容如下:

am_message_info1=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021110’
am_message_info2=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021111’
am_message_info3=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021112’
am_message_info4=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021113’
am_message_info5=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021100’
am_message_info6=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021101’
am_message_info7=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021102’
am_message_info8=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021103’
am_message_info9=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021090’
am_message_info10=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021091’
am_message_info11=select massid,guid,massage,tm from am_message_info where left(CONVERT(varchar(100), tm, 112),7)=‘2021092’
此代码的是打包上传到linux执行,架包的名称为:Solr_Study-1.0-SNAPSHOT.jar,具体执行的脚本如下:

#!/bin/bash
#while [ true ]
#do
#数据库用户名
username=sa
password=atmk!123
driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
solrurl=http://172.18.255.197:8983/solr/
dburl=jdbc:sqlserver://172.18.255.197:1433
db=sy_bd
mode=full
#获取当前小时
h=date "+%H"
#获取当前分钟
m=date "+%M"
#每天凌晨3点10分跑定时任务
if [[ KaTeX parse error: Expected 'EOF', got '&' at position 13: h -eq 15 ]] &̲& [[ 4 -eq 4 ]]…(cd "$(dirname " 0 " ) " ; p w d ) c o n f p a t h = 0")"; pwd) confpath= 0")";pwd)confpath={dir}/conf
for line in cat ${confpath}/inc_save_data.properties
do

table_flag=KaTeX parse error: Expected '}', got 'EOF' at end of input: …ine%%:*} if [ xtable_flag == x"exe_table" ];then
table_nm=${line%%#*}

table_nm=${table_nm:10}

line=KaTeX parse error: Expected '}', got '#' at position 6: {line#̲*\#} key_flag={line%%😗}
key=“”

if [ x k e y f l a g = = x " k e y " ] ; t h e n k e y = {key_flag} == x"key" ];then key= keyflag==x"key"];thenkey={line%%#*}

key=${key:4}
java -jar Solr_Study-1.0-SNAPSHOT.jar $table_nm $key $username $password $driver $solrurl $dburl $db $mode

fi
fi
done

其中配置文件inc_save_data.properties内容如下:

exe_table:am_message_info1#key:massid#
exe_table:am_message_info2#key:massid#
exe_table:am_message_info3#key:massid#
exe_table:am_message_info4#key:massid#
exe_table:am_message_info5#key:massid#
exe_table:am_message_info6#key:massid#
exe_table:am_message_info7#key:massid#
exe_table:am_message_info8#key:massid#
exe_table:am_message_info9#key:massid#
exe_table:am_message_info10#key:massid#
exe_table:am_message_info11#key:massid#
exe_table:am_message_info12#key:massid#
exe_table:am_message_info13#key:massid#
exe_table:am_message_info14#key:massid#
exe_table:am_message_info15#key:massid#

5、solr索引增量同步方式
5.1 通过配置方式实现增量同步:

5.1.1、修改data-config.xml内容:

<?xml version="1.0" encoding="UTF-8" ?>

5.1.2 再修改managed-schema,内容如下:

注意:用于增量的字段类型设置成pdate

或者定时访问下面链接即可实现增量导入:

http://localhost:8983/solr/core_04/dataimport?command=delta-import&clean=false&commit=true&wt=json&indent=true&verbose=false&optimize=false&debug=false

5.2 实时性要求高的话,可以在调用接口时调用api方式同步数据

5.2.1 假如新增或修改一条数据时,也将数据同步到solr的代码实现方式

再执行保存数据到sqlserver时,也调用保存数据到solr的接口,实现数据实时同步

//将数据保存到sqlserver代码:

@Insert(“insert into AM_ELECTRO_DATA(guid,tm,acc_pc) values(#{guid},#{tm},#{acc_pc})”)
public void saveData(AM_ELECTRO_DATA_TWO en);
//将数据保存到solr代码

@Override
public void saveData(AM_ELECTRO_DATA_TWO en) throws Exception {
SolrInputDocument doc = new SolrInputDocument();
doc.addField(“id”,en.getGuid());
doc.addField(“guid”,en.getGuid());
doc.addField(“tm”,en.getTm());
doc.setField(“acc_pc”,en.getAcc_pc());
solrClient.add(doc);
//solrClient.commit(“AM_ELECTRO_DATA”);
solrClient.commit();
}
5.2.2 在进行删除数据时,同时删除sqlserver和solr

//删除sqlserver数据代码

@Delete(“delete from AM_ELECTRO_DATA where guid=#{id}”)
public void deleteByID(String id);
//删除solr数据代码

@Override
public void deleteByID(String id) throws Exception {
//solrClient.deleteById(“AM_ELECTRO_DATA”,id);
solrClient.deleteById(id);
solrClient.commit();
}
5.3 实时性要求不高的话,可以T+1调用API方式同步的方式:

api的方式和初始化的api是一样的,就是将shell脚本中的mode有full改为inc即可

6、sqlserver和solr性能对比
总之来说,solr在数据量比较大和模糊查询性能要比sqlserver高很多

6.1 数据量为8263649性能对比
sqlserver耗时:0.566

在这里插入图片描述
solr耗时:0.960
在这里插入图片描述
6.2 数据量为34898880性能对比
sqlserver耗时:6.527
在这里插入图片描述
solr耗时:0.912
在这里插入图片描述
6.3 数据量为一亿两千万的数据
sqlserver耗时:55.528
在这里插入图片描述
solr耗时:0.354
在这里插入图片描述
6.4 模糊查询性能对比
数据量:8263649

sqlserver查询用时:68.394
在这里插入图片描述
solr查询用时:0.346
在这里插入图片描述

​​​

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【赛迪网-IT技术报道】SQL Server数据查询速度的原因有很多,常见的有以下几种:   1、没有索引或者没有用到索引(这是查询最常见的问题,是程序设计的缺陷)     2、I/O吞吐量小,形成了瓶颈效应。     3、没有创建计算列导致查询优化。     4、内存不足     5、网络速度     6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)     7、锁或者死锁(这也是查询最常见的问题,是程序设计的缺陷)     8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。     9、返回了不必要的行和列     10、查询语句不好,没有优化 ●可以通过以下方法来优化查询 : 1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要。 2、纵向、横向分割表,减少表的尺寸(sp_spaceuse) 3、升级硬件 4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段。 5、提高网速。 6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。 配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的1.5倍。如果另外安装了全文检索功能,并打算运行Microsoft搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的3倍。将SQL Server max server memory服务器配置选项配置为物理内存的1.5倍(虚拟内存大小设置的一半)。 7、增加服务器CPU个数;但是必须 明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询 的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT, DELETE还不能并行处理。 8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。 like ''a%'' 使用索引 like ''%a'' 不使用索引用 like ''%a%'' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。 9、DB Server 和APPLication Server 分离;OLTP和OLAP分离 10、分布式分区视图可用于实现数据库服务器联合体。 联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件''分区视图'') a、在实现分区视图之前,必须先水平分区表 b、 在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上 运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。 11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE. 设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。 在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:   1、 查询语句的词法、语法检查     2、 将语句提交给DBMS的查询优化器     3、 优化器做代数优化和存取路径的优化     4、 由预编译模块生成查询规划     5、 然后在合适的时间提交给系统处理执行     6、 最后将执行结果返回给用户。 其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值