生产数据库批量更新数据的一点实践

· 2 min read

最近工作内容有一部分是在与SQL打交道,还是遇到了点坑,这里mark下。

背景

当前系统,面对数十万的数据表进行复杂update操作会出现问题,最终采用一条条的更新方式解决

程序源码

办法见源码

/**
 * @description
 *
 * 1. SQL查出目标数据主键,导出CSV
 * 2. 创建单条update SQL模版
 * 3. 执行该JS文件,会生成目标SQL
 * 4. 目标机器执行SQL
 *
 * 注意:修改CHUNK_SIZE来控制逐条更新的记录量
 */
const csv = require('csv-parser');
const fs = require('fs');
const ids = [];
const CHUNK_SIZE = 10;
// 输入数据文件
const INPUT_FILE = 'quote_ids.csv';
// 目标生成SQL文件
const OUTPUT_FILE = 'repair_data.sql';
const createSQLTemplate = (idArr) => `update \`a-prod\`.quote a left join \`b-prod\`.geographic_unit b
    on a.sales_org = b.sales_org and a.sales_office = b.sales_office and a.business_unit = b.business_unit and
       a.country = b.country_iso_code
set a.geographic_unit_id=b.id
where a.id in ('${idArr.join('\',\'')}');\n\n\n`;
fs.createReadStream(INPUT_FILE)
    .pipe(csv())
    .on('data', (row) => {
        ids.push(row.id);
    })
    .on('end', () => {
        const chunkIds = ids.reduce((res, item, index) => {
            let group = Math.floor(index / CHUNK_SIZE);
            if (res[group] === undefined) {
                res[group] = [];
            }
            res[group].push(item);
            return res;
        }, []);

        fs.writeFile(OUTPUT_FILE, chunkIds.map(item => createSQLTemplate(item)).join(''), () => {
            console.log('write success');
        });
    });

按照如上的方式即可避免一次性更新造成MySQL程序

思考

  • 当前面临的单表数据也无非20万,数据并不多,但是即使查询有时候也很慢,原因是有些字段比较大,比如是text,而查假如询需要展示这个字段就很慢,经验就是按需所取,这也就是为什么不提倡使用通配符*,当然查询如果再join几张表也会降低查询效率
  • 对于update操作,强烈要求增加where条件,否则影响范围太大,除非你很确定必须那么做
  • 生产如果出现大规模要更新数据,一方面其实我认为暴露了程序BUG,毕竟这种需求场景都不该存在,另一方面如果更新很麻烦,也暴露了表是否design的不合理呢