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 22 MySQL
Mysql效率优化定位较低sql的两种方式
May 26 MySQL
mysql获取指定时间段中所有日期或月份的语句(不设存储过程,不加表)
Jun 18 MySQL
MySQL的安装与配置详细教程
Jun 26 MySQL
MySQL Shell import_table数据导入的实现
Aug 07 MySQL
mysql事务隔离级别详情
Oct 24 MySQL
MySQL日期时间函数知识汇总
Mar 17 MySQL
MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)
Apr 09 MySQL
Mysql查询时间区间日期列表,不会由于数据表数据影响
Apr 19 MySQL
MySQL中order by的执行过程
Jun 05 MySQL
SQL语句中EXISTS的详细用法大全
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
做一个有下拉功能的留言版
2006/10/09 PHP
ThinkPHP3.2框架使用addAll()批量插入数据的方法
2017/03/16 PHP
PHP INT类型在内存中占字节详解
2019/07/20 PHP
JavaScript Undefined,Null类型和NaN值区别
2008/10/22 Javascript
利用javascript打开模态对话框(示例代码)
2014/01/11 Javascript
JavaScript格式化日期时间的方法和自定义格式化函数示例
2014/04/04 Javascript
调试JavaScript中正则表达式中遇到的问题
2015/01/27 Javascript
jquery显示loading图片直到网页加载完成的方法
2015/06/25 Javascript
jquery实现垂直和水平菜单导航栏
2020/08/27 Javascript
JavaScript中闭包之浅析解读(必看篇)
2016/08/25 Javascript
JavaScript的事件机制详解
2017/01/17 Javascript
Vue 2.X的状态管理vuex记录详解
2017/03/23 Javascript
Webpack常见静态资源处理-模块加载器(Loaders)+ExtractTextPlugin插件
2017/06/29 Javascript
详解jQuery中关于Ajax的几个常用的函数
2017/07/17 jQuery
微信小程序自定义弹窗wcPop插件
2018/11/19 Javascript
vue+eslint+vscode配置教程
2019/08/09 Javascript
js实现特别简单的钟表效果
2020/09/14 Javascript
python简单的函数定义和用法实例
2015/05/07 Python
Python中处理字符串之endswith()方法的使用简介
2015/05/18 Python
Python中join函数简单代码示例
2018/01/09 Python
python实现事件驱动
2018/11/21 Python
谈一谈基于python的面向对象编程基础
2019/05/21 Python
python 判断linux进程,并杀死进程的实现方法
2019/07/01 Python
HTML5拖拽文件上传的示例代码
2021/03/04 HTML / CSS
西铁城美国官方网站:Citizen Watch美国
2019/11/08 全球购物
文件中有一组整数,要求排序后输出到另一个文件中
2012/01/04 面试题
.net笔试题
2014/03/03 面试题
研究生自我鉴定范文
2013/10/30 职场文书
学前教育毕业生自荐信范文
2013/12/24 职场文书
高中军训感言500字
2014/02/24 职场文书
个人函授自我鉴定
2014/03/25 职场文书
升学宴主持词
2014/04/02 职场文书
禁烟标语大全
2014/06/11 职场文书
英语课前三分钟演讲稿
2014/08/19 职场文书
党员自我评价范文2015
2015/03/03 职场文书
导游词之河北野三坡
2019/12/11 职场文书