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之浅入深出页原理
Jun 23 MySQL
MySQL里面的子查询的基本使用
Aug 02 MySQL
MySQL8.0的WITH查询详情
Aug 30 MySQL
MySQL对数据表已有表进行分区表的实现
Nov 01 MySQL
基于MySql验证的vsftpd虚拟用户
Nov 07 MySQL
MySQL中int (10) 和 int (11) 的区别
Jan 22 MySQL
一次SQL如何查重及去重的实战记录
Mar 13 MySQL
Mysql分析设计表主键为何不用uuid
Mar 31 MySQL
优化Mysql查询的示例
Apr 26 MySQL
MySQL主从切换的超详细步骤
Jun 28 MySQL
MySQL数据库查询之多表查询总结
Aug 05 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排序算法的复习和总结
2012/02/15 PHP
php后门URL的防范
2013/11/12 PHP
PHP程序员必须清楚的问题汇总
2014/12/18 PHP
php die()与exit()的区别实例详解
2016/12/03 PHP
搜索附近的人PHP实现代码
2018/02/11 PHP
php命令行模式代码实例详解
2021/02/26 PHP
JavaScript的面向对象(二)
2006/11/09 Javascript
jQuery 常见学习网站与参考书
2009/11/09 Javascript
js通过元素class名字获取元素集合的具体实现
2014/01/06 Javascript
jquery xMarquee实现文字水平无缝滚动效果
2014/04/29 Javascript
Javascript非构造函数的继承
2015/04/27 Javascript
js实现年月日表单三级联动
2020/04/17 Javascript
jQuery模拟爆炸倒计时功能实例代码
2017/08/21 jQuery
详解js正则表达式验证时间格式xxxx-xx-xx形式
2018/02/09 Javascript
详解nodejs通过响应回写的方式渲染页面资源
2018/04/07 NodeJs
Vue 子组件与数据传递问题及注意事项
2019/07/11 Javascript
使用PreloadJS加载图片资源的基础方法详解
2020/02/03 Javascript
ES6 Generator基本使用方法示例
2020/06/06 Javascript
node.js文件的复制、创建文件夹等相关操作
2021/02/05 Javascript
[06:49]2018DOTA2国际邀请赛寻真——VirtusPro傲视群雄
2018/08/12 DOTA
[33:33]完美世界DOTA2联赛PWL S2 FTD.C vs SZ 第二场 11.27
2020/11/30 DOTA
python实现zencart产品数据导入到magento(python导入数据)
2014/04/03 Python
几个提升Python运行效率的方法之间的对比
2015/04/03 Python
python删除某个字符
2018/03/19 Python
Python面向对象程序设计示例小结
2019/01/30 Python
Python命令行参数解析工具 docopt 安装和应用过程详解
2019/09/26 Python
详解android与HTML混合开发总结
2018/06/06 HTML / CSS
连卡佛中国官网:Lane Crawford中文站
2018/01/27 全球购物
加大码胸罩、内裤和服装:Just My Size
2019/03/21 全球购物
人力资源管理专业应届生求职信
2013/09/28 职场文书
人事专员工作职责
2014/02/22 职场文书
化学专业大学生职业生涯规划范文
2014/09/13 职场文书
个人作风建设总结
2014/10/23 职场文书
2015年全国科普日活动总结
2015/03/23 职场文书
村官2015年度工作总结
2015/10/14 职场文书
python中super()函数的理解与基本使用
2021/08/30 Python