MySQL单表千万级数据处理的思路分享


Posted in MySQL onJune 05, 2021

项目背景

在处理过程中,今天上午需要更新A字段,下午爬虫组完成了规格书或图片的爬取又需要更新图片和规格书字段,由于单表千万级深度翻页会导致处理速度越来越慢。

select a,b,c from db.tb limit 10000 offset 9000000

但是时间是有限的,是否有更好的方法去解决这种问题呢?

改进思路

是否有可以不需要深度翻页也可以进行数据更新的凭据?
是的,利用自增id列

观察数据特征

此单表有自增id列且为主键,根据索引列查询数据和更新数据是最理想的途径。

select a,b, c from db.tb where id=9999999;
update db.tb set a=x where id=9999999;

多进程处理

每个进程处理一定id范围内的数据,这样既避免的深度翻页又可以同时多进程处理数据。
提高数据查询速度的同时也提高了数据处理速度。
下面是我编写的任务分配函数,供参考:

def mission_handler(all_missions, worker_mission_size):
    """
    根据总任务数和每个worker的任务数计算出任务列表, 任务列表元素为(任务开始id, 任务结束id)。
    例: 总任务数100个,每个worker的任务数40, 那么任务列表为:[(1, 40), (41, 80), (81, 100)]
    :param all_missions: 总任务数
    :param worker_mission_size: 每个worker的最大任务数
    :return: [(start_id, end_id), (start_id, end_id), ...]
    """
    worker_mission_ids = []
    current_id = 0
    while current_id <= all_missions:
        start_id = all_missions if current_id + 1 >= all_missions else current_id + 1
        end_id = all_missions if current_id + worker_mission_size >= all_missions else current_id + worker_mission_size
        if start_id == end_id:
            if worker_mission_ids[-1][1] == start_id:
                break
        worker_mission_ids.append((start_id, end_id))
        current_id += worker_mission_size

    return worker_mission_ids

假设单表id最大值为100, 然后我们希望每个进程处理20个id,那么任务列表将为:

>>> mission_handler(100, 40)
[(1, 40), (41, 80), (81, 100)]

那么,
进程1将只需要处理id between 1 to 40的数据;
进程2将只需要处理id between 41 to 80的数据;
进程3将只需要处理id between 81 to 100的数据。

from concurrent.futures import ProcessPoolExecutor


def main():
    # 自增id最大值
    max_id = 30000000
    # 单worker处理数据量
    worker_mission_size = 1000000
    # 使用多进程进行处理
    missions = mission_handler(max_id, worker_mission_size)
    workers = []
    executor = ProcessPoolExecutor()
    for idx, mission in enumerate(missions):
        start_id, end_id = mission
        workers.append(executor.submit(data_handler, start_id, end_id, idx))


def data_handler(start_id, end_id, worker_id):
    pass

思路总结

  1. 避免深度翻页进而使用自增id进行查询数据和数据
  2. 使用多进程处理数据

数据处理技巧

记录处理成功与处理失败的数据id,以便后续跟进处理

# 用另外一张表记录处理状态
insert into db.tb_handle_status(row_id, success) values (999, 0);

循环体内进行异常捕获,避免程序异常退出

def data_handler(start_id, end_id, worker_id):
    # 数据连接
    conn, cursor = mysql()
    current_id = start_id
        try:
            while current_id <= end_id:
                try:
                    # TODO 数据处理代码
                    pass

                except Exception as e:
                    # TODO 记录处理结果
                    # 数据移动到下一条
                    current_id += 1
                    continue
                else:
                    # 无异常,继续处理下一条数据
                    current_id += 1
        except Exception as e:
            return 'worker_id({}): result({})'.format(worker_id, False)
        finally:
            # 数据库资源释放
            cursor.close()
            conn.close()

        return 'worker_id({}): result({})'.format(worker_id, True)

更新数据库数据尽量使用批量提交

sql = """update db.tb set a=%s, b=%s where id=%s"""
values = [
            ('a_value', 'b_value', 9999),
            ('a_value', 'b_value', 9998),
            ...
         ]
# 批量提交,减少网络io以及锁获取频率
cursor.executemany(sql, values)

以上就是MySQL单表千万级数据处理的思路分享的详细内容,更多关于MySQL单表千万级数据处理的资料请关注三水点靠木其它相关文章!

MySQL 相关文章推荐
多表查询、事务、DCL
Apr 05 MySQL
仅用一句SQL更新整张表的涨跌幅、涨跌率的解决方案
May 06 MySQL
MySQL EXPLAIN输出列的详细解释
May 12 MySQL
mysql 8.0.24 安装配置方法图文教程
May 12 MySQL
MySQL优化之如何写出高质量sql语句
May 17 MySQL
MySQL为id选择合适的数据类型
Jun 07 MySQL
MySQL系列之开篇 MySQL关系型数据库基础概念
Jul 02 MySQL
mysql事务对效率的影响分析总结
Oct 24 MySQL
MySQL学习之基础命令实操总结
Mar 19 MySQL
Windows 64位 安装 mysql 8.0.28 图文教程
Apr 19 MySQL
mysql查找连续出现n次以上的数字
May 11 MySQL
postgresql如何找到表中重复数据的行并删除
May 08 MySQL
MySQL 时间类型的选择
Jun 05 #MySQL
MySQL索引失效的典型案例
Jun 05 #MySQL
MySQL库表名大小写的选择
Jun 05 #MySQL
mysql 带多个条件的查询方式
Mysql 如何实现多张无关联表查询数据并分页
Jun 05 #MySQL
Mysql中存储引擎的区别及比较
浅谈mysql返回Boolean类型的几种情况
Jun 04 #MySQL
You might like
php读取本地文件常用函数(fopen与file_get_contents)
2013/09/09 PHP
php二维数组排序方法(array_multisort usort)
2013/12/25 PHP
thinkPHP js文件中U方法不被解析问题的解决方法
2016/12/05 PHP
PHP实现百度人脸识别
2019/05/06 PHP
PHP 计算两个时间段之间交集的天数示例
2019/10/24 PHP
[对联广告] JS脚本类
2006/08/27 Javascript
use jscript with List Proxy Server Information
2007/06/11 Javascript
javascript cookies操作集合
2010/04/12 Javascript
基于jQuery的简单的列表导航菜单
2011/03/02 Javascript
利用javascript打开模态对话框(示例代码)
2014/01/11 Javascript
node.js解决获取图片真实文件类型的问题
2014/12/20 Javascript
javascript中动态函数用法实例分析
2015/05/14 Javascript
jquery移动端TAB触屏切换实现效果
2020/12/22 Javascript
javascript设置文本框光标的方法实例小结
2016/11/04 Javascript
JS正则表达式修饰符global(/g)用法分析
2016/12/27 Javascript
ES6数组的扩展详解
2017/04/25 Javascript
详解小程序设置缓存并且不覆盖原有数据
2019/04/15 Javascript
解决vue-pdf查看pdf文件及打印乱码的问题
2020/11/04 Javascript
vue3.0实现插件封装
2020/12/14 Vue.js
[48:56]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 VG vs KG
2018/03/31 DOTA
python中range()与xrange()用法分析
2016/09/21 Python
Python实现的栈(Stack)
2018/01/26 Python
Python模拟登录的多种方法(四种)
2018/06/01 Python
Python中作用域的深入讲解
2018/12/10 Python
python GUI库图形界面开发之PyQt5不规则窗口实现与显示GIF动画的详细方法与实例
2020/03/09 Python
在html页面中取得session中的值的方法
2020/08/11 HTML / CSS
即时搜索数百万张门票:SeatsForEveryone.com
2018/08/26 全球购物
数控专业个人求职信范文
2014/02/05 职场文书
《只有一个地球》教学反思
2014/02/14 职场文书
毕业自我鉴定总结
2014/03/24 职场文书
仓库文员岗位职责
2014/04/06 职场文书
孝老爱亲模范事迹材料
2014/05/25 职场文书
承诺保证书格式
2015/02/28 职场文书
Python网络编程之ZeroMQ知识总结
2021/04/25 Python
css3中2D转换之有趣的transform形变效果
2022/02/24 HTML / CSS
vue组件vue-esign实现电子签名
2022/04/21 Vue.js