问题

​ 系统里的操作日志表,主要记录员工发起的各个请求(CRUD),刚建了没几个月,已经上千万条记录了,导致页面查询的时候,大概需要3-5秒的时间,如果加上条件查询,基本5秒多了。这个时间是完全不能接受的,所以打算重新设计一下。

日志表的元数据:(id,name,url,param,add_time…)

分析

  • 现在表每天大概30-50w的数据增长,基本每秒都有十几条的数据插入
  • 写多查少,数据库没有做读写分离
  • 一般只查看最近的数据
  • 基本不会逐个查询,条件查询用的比较多
  • 几个在where中用到的字段都没加索引
  • sql比较简单,基本没有什么可以优化的

方案

  • 给where条件上的字段加索引

    ​ 由于name和url查询的时候需要用到like “%xxx%”,这样就算给name和url加上索引,查询的时候也不会用到。add_time字段加索引可以用到,而且where中用到的比较多。但是给数据量这么大的表加索引,耗时惊人。之后越来越多的数据,索引消耗的空间也很可观。

  • 分表

    • 根据hash(name)取模分表

      ​ 需要预估分多少个表,按当前数据量和数据增长速度来看,几十个表都不够。而且后期增长后添加分表重新分表的工作量太大了。

    • 根据add_time按每几个月增量分表

      ​ 根据数据增长速度来看,基本需要每周都建个新表,未来表的数量多到难以想象。

  • 归档

    ​ 因为一般只查看最近的操作日志,可以限制原表储存数据的时间,先把之前的数据存到一张old表中,再每天转存一个月前一天的数据,保持原表只保存最近一个月的数据。

  • 集群

    • 主从复制

    • 读写分离

      由于暂时不考虑集群,这两个pass了

解决

​ 重新分析了一下原来的需求,发现对于员工查询类型的请求,并不是很重要,可以不保存。而表里保存的记录基本有90%都是查询类型的请求记录。所以只要过滤改类型的请求,只保存增、删、改的请求记录,就可以使增长量下降90%,这样表的数据压力就没有原来这么大了。再每天将一个月前的数据归档,原表的数据量就大大减少,查询速度也就跟着上来了。

过滤请求

​ 在原来的拦截器中,对request.getRequestURI()进行过滤,原先请求url地址中包含”/get”、”/fetch”、”/list”、”/query”等请求相关的关键字(需要当时设计接口地址的时候有一定的规范),全部直接return。部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class LogInterceptor extends HandlerInterceptorAdapter {    //不需要记录日志的url关键字
    private static final String[] NO_LOG_URL_KEYWORD = {"/get", "/fetch", "/list", "/query"};

    /**
     * 记录操作日志
     *
     * @param request
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        this.insertLogOperate(request);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }

    private void insertLogOperate(HttpServletRequest request) {        //过滤uri
        String uri = request.getRequestURI();
        for (String key : NO_LOG_URL_KEYWORD) {            if (uri.contains(key)) {                return;
            }
        }
        // 记录操作日志入库
        LogOperate log = new LogOperate();
        logOperate.setName(authUtil.getCurrentUserName());
        logOperate.setParam(JsonUtil.toJsonString(request.getParameterMap()));
        logOperate.setIp(RequestUtils.getClientIP(request));
        logOperate.setUrl(uri);
        logOperate.setAddTime(new Timestamp(System.currentTimeMillis()));
        logOperateService.save(logOperate);
    }
}

​ 修改过程中发现一个问题:如果是POST类型的请求,request.getParameterMap()不能获取到请求的参数,需要用流读取request,而如果在拦截器中读取了,Controller层就读取不到参数了。网上查了一下,需要重写一个方法来解决,这个就不再这里说明了。

数据表处理

新建一张log_old表,和原来的log表结构一样:

create table log_old like log;

写一个存储过程,把一个月前的记录存到log_old中:

delimiter $
CREATE PROCEDURE proc_logBackUp()
begin
set @time = NOW() - INTERVAL 1 MONTH;
insert into log_old select * from log where add_time < @time;
delete from log where add_time < @time;
end
$
delimiter ;

创建事件,每天执行上面这个过程:

create event if not exists event_logBackUp
on schedule every 1 day
on completion preserve
do call proc_logBackUp();

备注:如果数据量太大,第一次执行proc_logBackUp()可能会执行失败,可以根据数据量,把@time改成手动设置某个时间点,分多次执行过程。之后的每天执行时间就不会出什么问题了。

日志表数据量大查询慢问题相关推荐

  1. 历史数据如何处理_数据库表数据量大读写缓慢如何优化(1)【冷热分离】

    今天讨论的内容是冷热分离,也许概念并不陌生,对其使用场景也比较熟悉,但涉及锁的内容时仍然需要认真思考,这部分内容在我们实际开发中的"坑"还是不少的. 业务场景一 曾经经历过供应链相 ...

  2. 面试官问单表数据量大一定要分库分表吗?我们用六个字和十张图回答

    1 文章概述 在业务发展初期单表完全可以满足业务需求,在阿里巴巴开发手册也建议:单表行数超过500万行或者单表容量超过2GB才推荐进行分库分表,如果预计三年后数据量根本达不到这个级别,请不要在创建表时 ...

  3. 软件架构场景之—— 分表分库:单表数据量大读写缓慢如何解决?

    业务背景 一个电商系统的架构优化,该系统中包含用户和订单 2 个主要实体,每个实体涵盖数据量如下表所示 实体 数据量 增长趋势 用户 上千万 每日十万 订单 上亿 每日百万级速度增长,之后可能是千万级 ...

  4. MySQL将表中的yes改成no_mysql在不需要改程序的情况下通过操作数据库对单表数据量大的表进行分表...

    1.为什么要分表? 数据库数据越来越大,随之而来的是单个表中数据太多.以至于查询速度变慢,而且由于表的锁机制导致应用操作也搜到严重影响,出现了数据库性能瓶颈. mysql中有一种机制是表锁定和行锁定, ...

  5. MySQL 和 Oracle 大数据量分页查询方法及其优化

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

  6. oracle单表数据量上亿_MySQL数据库中,数据量越来越大,有什么具体的优化方案么?...

    个人的观点,这种大表的优化,不一定上来就要分库分表,因为表一旦被拆分,开发.运维的复杂度会直线上升,而大多数公司和开发人员是欠缺这种能力的. 所以MySQL中几百万甚至小几千万的表,先考虑做单表的优化 ...

  7. 大数据量分页查询方法(转)

    本文旨在介绍一种对数据库中的大数据量表格进行分页查询的实现方法,该方法对应用服务器.数据库服务器.查询客户端的cpu和内存占用都较低,查询速度较快,是一个较为理想的分页查询实现方案. 1.问题的提出  ...

  8. mysql查看比较大的数据表_mysql 如何查看哪些表数据量比较大

    数据库中有几十上百张表,那么哪些表的数据量比较大呢,总不能一个表一个表的去查询吧,在mysql中也有类似于oracle的数据字典表,只不过mysql没有oracle记录的那么多和详细,但也足够我们查询 ...

  9. 单表数据量过大处理策略

    今天和一个朋友在讨论怎么样应对单表数据量过大,比如一些交易数据,每天都有10W的交易量.没有多久该表的查询,插入速度将变慢,最终将不可用. 对于关系数据库来说,应对单表数据量过大的策略大体上有两种. ...

  10. 千万级别数据查询优化_MySQL大数据量分页查询方法及其优化

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

最新文章

  1. SLAM | 使用三维位姿图优化减少单目视觉里程计(3D Visual Odometry)定位轨迹的漂移(附源代码)
  2. mysql 5.7 udf http_mysql下mysql-udf-http效率测试小记
  3. 历时 4 年,阿里云推出金融核心系统转型实践书
  4. Android源码分析(十一)-----Android源码中如何引用aar文件
  5. Linux7使用光盘作为本地repo源
  6. Fast RCNN总结
  7. 文字 竖排居中_微信朋友圈文字如何设置居中居右?
  8. Flutter基础—常用控件之图片
  9. 代码快速“检”“修”不是梦,阿里云MaxCompute Studio 2.9.0 新版本发布
  10. Last-Modify、ETag、Expires和Cache-Control(转载)
  11. mysql的本地id可以随便设置马_Mysql主从复制原理及搭建
  12. Express框架学习笔记-post请求参数的获取
  13. java图片旋转90度_Java实现图片内容无损任意角度旋转
  14. js根据经纬度计算距离
  15. Linux傲腾内存,傲腾内存价格流出:每GB最低35元,最高容量512GB
  16. latex插入参考文献技巧
  17. js中函数的传递参数
  18. BIM技术之Dynamo图元编程:柱、球面坐标系节点绘制螺旋线
  19. plc中int数据类型范围_PLC数据类型INT 和WORD的区别点-工业支持中心-西门子中国...
  20. 手写数字识别问题实战

热门文章

  1. linux mysql 备份 压缩_Linux下mysql定时备份压缩
  2. LeetCode题目分类整理
  3. tp5 MySQL发红包功能_ThinkPHP5微信现金红包的开发
  4. 微信小程序弹框种类汇总
  5. 二路归并排序和多路归并排序
  6. c语言 格式字符,c语言的格式符
  7. mysql卸载不干净无法重新安装mysql
  8. visio用例图箭头怎么画_visio2010绘制用例图-带图例
  9. Activiti7的用法和简述
  10. C语言找出1000之内的完数