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 05 MySQL
Mysql 性能监控及调优
Apr 06 MySQL
Mysql实现主从配置和多主多从配置
Jun 02 MySQL
Mysql排序的特性详情
Nov 01 MySQL
MySQL中varchar和char类型的区别
Nov 17 MySQL
mysql 联合索引生效的条件及索引失效的条件
Nov 20 MySQL
mysql中整数数据类型tinyint详解
Dec 06 MySQL
JMeter对MySQL数据库进行压力测试的实现步骤
Jan 22 MySQL
Mysql外键约束的创建与删除的使用
Mar 03 MySQL
详解Mysql事务并发(脏读、不可重复读、幻读)
Apr 29 MySQL
MySQL中正则表达式(REGEXP)使用详解
Jul 07 MySQL
MySQL的意向共享锁、意向排它锁和死锁
Jul 15 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在XP下IIS和Apache2服务器上的安装
2006/09/05 PHP
php数组转换js数组操作及json_encode的用法详解
2013/10/26 PHP
PHP程序员不应该忽略的3点
2015/10/09 PHP
实例详解PHP中html word 互转的方法
2016/01/28 PHP
弹出模态框modal的实现方法及实例
2017/09/19 PHP
PHP项目多语言配置平台实现过程解析
2020/05/18 PHP
Javascript Global对象
2009/08/13 Javascript
JQuery Tips(2) 关于$()包装集你不知道的
2009/12/14 Javascript
ext前台接收action传过来的json数据示例
2014/06/17 Javascript
简介JavaScript中的getSeconds()方法的使用
2015/06/10 Javascript
jQuery实现简单下拉导航效果
2015/09/07 Javascript
javascript函数自动执行常用方法汇总
2016/03/28 Javascript
Javascript基础_嵌入图像的简单实现
2016/06/14 Javascript
打造自己的jQuery插件入门教程
2016/09/23 Javascript
JS实现选定指定HTML元素对象中指定文本内容功能示例
2017/02/13 Javascript
jquery.param()实现数组或对象的序列化方法
2018/10/08 jQuery
如何优雅的在一台vps(云主机)上面部署vue+mongodb+express项目
2019/01/20 Javascript
JavaScript数组排序小程序实现解析
2020/01/13 Javascript
mac PyCharm添加Python解释器及添加package路径的方法
2018/10/29 Python
python 用下标截取字符串的实例
2018/12/25 Python
python 杀死自身进程的实现方法
2019/07/01 Python
基于django传递数据到后端的例子
2019/08/16 Python
简单分析python的类变量、实例变量
2019/08/23 Python
利用Tensorflow构建和训练自己的CNN来做简单的验证码识别方式
2020/01/20 Python
Amcal中文官网:澳洲综合性连锁药房
2019/03/28 全球购物
size?丹麦官网:英国伦敦的球鞋精品店
2019/04/15 全球购物
学校安全工作制度
2014/01/19 职场文书
表彰大会策划方案
2014/05/13 职场文书
工作失职造成投诉的检讨书范文
2014/10/05 职场文书
党的群众路线教育实践活动方案
2014/10/31 职场文书
2015年宣传思想工作总结
2015/05/22 职场文书
正规借条模板
2015/05/26 职场文书
学籍证明模板
2015/06/18 职场文书
Pytorch distributed 多卡并行载入模型操作
2021/06/05 Python
JS实现数组去重的11种方法总结
2022/04/04 Javascript
Java由浅入深通关抽象类与接口(上篇)
2022/04/26 Java/Android