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创建索引需要了解的
Apr 08 MySQL
MySQL分库分表与分区的入门指南
Apr 22 MySQL
MySQL中你可能忽略的COLLATION实例详解
May 12 MySQL
MySQL 外键约束和表关系相关总结
Jun 20 MySQL
MySQL中IF()、IFNULL()、NULLIF()、ISNULL()函数的使用详解
Jun 26 MySQL
MySQL中varchar和char类型的区别
Nov 17 MySQL
MySQL多表查询机制
Mar 17 MySQL
MySQL中一条SQL查询语句是如何执行的
Apr 08 MySQL
MySql分区类型及创建分区的方法
Apr 13 MySQL
Mysql 8.x 创建用户以及授予权限的操作记录
Apr 18 MySQL
MySQL 数据库范式化设计理论
Apr 22 MySQL
浅谈MySql update会锁定哪些范围的数据
Jun 25 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实现Ftp用户的在线管理的代码
2007/03/06 PHP
Zend Framework教程之Zend_Config_Xml用法分析
2016/03/23 PHP
php字符串操作针对负值的判断分析
2016/07/28 PHP
Laravel 在views中加载公共页面的实现代码
2019/10/22 PHP
JS window.opener返回父页面的应用
2009/10/24 Javascript
利用毫秒减值计算时长的js代码
2013/09/22 Javascript
JS测试显示屏分辨率以及屏幕尺寸的方法
2013/11/22 Javascript
深入理解JavaScript是如何实现继承的
2013/12/12 Javascript
用js控制组织结构图可以任意拖拽到指定位置
2014/01/17 Javascript
BOOTSTRAP时间控件显示在模态框下面的bug修复
2015/02/05 Javascript
javascript跨域原因以及解决方案分享
2015/04/08 Javascript
基于jquery实现一个滚动的分步注册向导-附源码
2015/08/26 Javascript
node.js+express制作网页计算器
2016/01/17 Javascript
jquery if条件语句的写法
2016/05/19 Javascript
JS设置CSS样式的方式汇总
2017/01/21 Javascript
jquery实现静态搜索功能(可输入搜索文字)
2017/03/28 jQuery
Vue.js实现模拟微信朋友圈开发demo
2017/04/20 Javascript
vue判断input输入内容全是空格的方法
2018/03/02 Javascript
js实现左右两侧浮动广告
2018/07/09 Javascript
小程序实现多选框功能
2018/10/30 Javascript
判断iOS、Android以及PC端的示例代码
2018/11/15 Javascript
JavaScript判断对象和数组的两种方法
2019/05/31 Javascript
基于iview-admin实现动态路由的示例代码
2019/10/02 Javascript
利用python如何处理nc数据详解
2018/05/23 Python
基于Python实现定时自动给微信好友发送天气预报
2018/10/25 Python
python调用自定义函数的实例操作
2019/06/26 Python
python 利用pyttsx3文字转语音过程详解
2019/09/25 Python
Python爬虫获取页面所有URL链接过程详解
2020/06/04 Python
详解Python高阶函数
2020/08/15 Python
pytorch加载语音类自定义数据集的方法教程
2020/11/10 Python
Bobbi Brown芭比波朗美国官网:化妆师专业彩妆保养品品牌
2016/08/18 全球购物
摩顿布朗英国官方网上商店:奢华沐浴、身体和头发护理
2016/10/29 全球购物
爱他美官方海外旗舰店:Aptamil奶粉
2017/12/22 全球购物
有趣、实用和鼓舞人心的产品:Inspire Uplift
2019/11/05 全球购物
建筑工程专业毕业生自荐信
2013/10/19 职场文书
捐款倡议书怎么写
2014/05/13 职场文书