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 相关文章推荐
MySQL update set 和 and的区别
May 08 MySQL
MySQL时间盲注的五种延时方法实现
May 18 MySQL
SQL之各种join小结详细讲解
Aug 04 MySQL
MySQL窗口函数的具体使用
Nov 17 MySQL
weblogic服务建立数据源连接测试更新mysql驱动包的问题及解决方法
Jan 22 MySQL
一条慢SQL语句引发的改造之路
Mar 16 MySQL
详细聊一聊mysql的树形结构存储以及查询
Apr 05 MySQL
Mysql调整优化之四种分区方式以及组合分区
Apr 13 MySQL
详解Mysql事务并发(脏读、不可重复读、幻读)
Apr 29 MySQL
MySQL池化框架学习接池自定义
Jul 23 MySQL
jdbc中自带MySQL 连接池实践示例
Jul 23 MySQL
MySQL索引失效十种场景与优化方案
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 adodb分页实现代码
2009/03/19 PHP
smarty基础之拼接字符串的详解
2013/06/18 PHP
php实现通用的从数据库表读取数据到数组的函数实例
2015/03/21 PHP
用JavaScript仿PS里的羽化效果代码
2011/12/20 Javascript
javascript作用域容易记错的两个地方分析
2012/06/22 Javascript
html向js方法传递参数具体实现
2013/08/08 Javascript
现如今最流行的JavaScript代码规范
2014/03/08 Javascript
js 模式窗口(模式对话框和非模式对话框)的使用介绍
2014/07/17 Javascript
js插件YprogressBar实现漂亮的进度条效果
2015/04/20 Javascript
Jquery实现纵向横向菜单
2016/01/24 Javascript
读Javascript高性能编程重点笔记
2016/12/21 Javascript
vue v-on监听事件详解
2017/05/17 Javascript
微信小程序 功能函数小结(手机号验证*、密码验证*、获取验证码*)
2017/12/08 Javascript
解决vue动态为数据添加新属性遇到的问题
2018/09/18 Javascript
mpvue将vue项目转换为小程序
2018/09/30 Javascript
小程序登录态管理的方法示例
2018/11/13 Javascript
微信小程序实现打卡日历功能
2020/09/21 Javascript
nvm、nrm、npm 安装和使用详解(小结)
2019/01/17 Javascript
详解JQuery基础动画操作
2019/04/12 jQuery
2020淘宝618理想生活列车自动领喵币js脚本的代码
2020/06/02 Javascript
[06:42]DOTA2每周TOP10 精彩击杀集锦vol.1
2014/06/25 DOTA
JSON Web Tokens的实现原理
2017/04/02 Python
python简单实例训练(21~30)
2017/11/15 Python
python内置函数:lambda、map、filter简单介绍
2017/11/16 Python
python批量导入数据进Elasticsearch的实例
2018/05/30 Python
pandas数据集的端到端处理
2019/02/18 Python
python with语句的原理与用法详解
2020/03/30 Python
python入门:argparse浅析 nargs='+'作用
2020/07/12 Python
医学生就业推荐表自我鉴定
2014/03/26 职场文书
入党函调证明材料
2015/06/19 职场文书
童年读书笔记
2015/06/26 职场文书
2016年小学生新年寄语
2015/08/18 职场文书
劳务派遣管理制度(样本)
2019/08/23 职场文书
Go语言实现一个简单的并发聊天室的项目实战
2022/03/18 Golang
vue报错function () { [native code] },无法出现我们想要的内容 Unknown custom element
2022/04/11 Vue.js
JS高级程序设计之class继承重点详解
2022/07/07 Javascript