对一次生产环境产生OOM的记录,结论:除非在自己完全有把握的情况下,否则不要偷懒查询无用字段

1、背景

今天下班的时候看到公司微信群里技术总监发的消息,内容是由于代码执行导致公司网站宕机,然后他让网站部门老大立即查找后台日志中对应的bug来源,我一看之前参与过这个项目,这是我写的日志信息,然后问题原因是从数据库中查询的对象集合太大,直接把内存给撑爆了,据说达到了20G,然后网站就宕机了

2、伪代码

本次使用下面伪代码说明代码逻辑

public class Test {

    // 模拟信息数据库
    private static List<A> dataList = new ArrayList<>();

    static {
        imitateMysql();
    }

    /**
     * 模拟分页输出数据
     */
    public static void main(String[] args) {
        // 总数据量
        int total = dataList.size();
        System.out.println("》》》》》》待处理数据总量:" + total + ";当前时间:" + DateUtil.now(DateUtil.DEFAULT_DATE_FORMAT));
        // 页面容量
        int pageSize = 100;
        // 总页码
        int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        // 分页处理数据
        for (int currentPage = 1; currentPage <= totalPage; currentPage++) {
            List<A> pageList = page(currentPage, pageSize);
            for (A entity : pageList) {
                indexData(entity.getId());
            }
            // 回收资源
            pageList = null;
        }
        System.out.println("》》》》》》所有数据都已经处理完毕");
    }

    /**
     * 生成模拟数据库数据
     */
    private static void imitateMysql() {
        for (int i = 0; i < 210000; i++) {
            A a = new A();
            a.setId(UUIDUtil.create().toString().replace("-", ""));
            a.setContent("自行想象此处存在大量文本");
            dataList.add(a);
        }
    }

    /**
     * 根据数据id索引数据到Elasticsearch,此处不再模拟
     * @param id 数据id
     */
    private static void indexData(String id) {
        // 根据数据id查询数据

        // 索引数据到Elasticsearch
        System.out.println("索引数据到Elasticsearch完成,id = " + id);
    }

    /**
     * 模拟分页获取数据
     * @param currentPage 当前页码
     * @param pageSize 页面容量
     * @return
     */
    private static List<A> page(int currentPage, int pageSize) {
        List<A> pageList = new ArrayList<>(pageSize);
        int start = (currentPage - 1) * pageSize;
        int end = start + pageSize - 1;
        for (int i = 0; i < dataList.size(); i++) {
            if (i >= start && i <= end) {
                pageList.add(dataList.get(i));
            }
        }
        return pageList;
    }
}

3、分析

我们来分析一下上面的代码,分页获取数据以及回收资源都是为了减少内存空间占用,但是我们忽略了一个事实,也就是真实环境中content内容巨大,因此可能造成pageList很大,严重情况下甚至导致OOM,虽然我们将分页数据集合置空了,但是垃圾回收器没有这么迅速完成垃圾回收

对于从数据库中分页获取数据的操作,完全可以只分页获取数据id集合,而不获取其他字段,这样产生的好处是分页查询速度更快,占用内存空间更小,而我当时没有考虑这么多,然后偷懒使用公司的默认分页方法获取了所有字段数据,导致了OOM

4、结论

在从数据库获取数据的时候,尽可能只获取自己需要的数据,不要偷懒获取无用数据,小则减慢程序运行效率,大则导致程序OOM,影响客户正常使用,造成生产事故

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值