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 27 MySQL
一篇文章弄懂MySQL查询语句的执行过程
May 07 MySQL
解读MySQL的客户端和服务端协议
May 10 MySQL
mysql 如何获取两个集合的交集/差集/并集
Jun 08 MySQL
MySQL GRANT用户授权的实现
Jun 18 MySQL
MySQL Shell import_table数据导入的实现
Aug 07 MySQL
MySQL中的隐藏列的具体查看
Sep 04 MySQL
MYSQL如何查看进程和kill进程
Mar 13 MySQL
MySql中的json_extract函数处理json字段详情
Jun 05 MySQL
MySQL慢查询中的commit慢和binlog中慢事务的区别
Jun 16 MySQL
mysql幻读详解实例以及解决办法
Jun 16 MySQL
SQLServer常见数学函数梳理总结
Aug 05 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生成唯一的订单函数分享
2015/02/02 PHP
Adnroid 微信内置浏览器清除缓存
2016/07/11 PHP
thinkphp3.2.3 分页代码分享
2016/07/28 PHP
javascript indexOf函数使用说明
2008/07/03 Javascript
JavaScript 继承机制的实现(待续)
2010/05/18 Javascript
jQuery插件开发基础简单介绍
2013/01/07 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 语法
2015/01/09 Javascript
JS仿iGoogle自定义首页模块拖拽特效的方法
2015/02/13 Javascript
浅谈js基本数据类型和typeof
2016/08/09 Javascript
js中scrollTop()方法和scroll()方法用法示例
2016/10/03 Javascript
获取jqGrid中选择的行的数据
2016/11/30 Javascript
详解Angular中的自定义服务Service、Provider以及Factory
2017/04/22 Javascript
深入理解Javascript中的作用域链和闭包
2017/04/25 Javascript
React应用中使用Bootstrap的方法
2017/08/15 Javascript
JS与jQuery实现ListBox上移,下移,左移,右移操作功能示例
2018/05/31 jQuery
基于Vue+Webpack拆分路由文件实现管理
2020/11/16 Javascript
获取Django项目的全部url方法详解
2017/10/26 Python
利用pandas进行大文件计数处理的方法
2018/07/25 Python
对Python+opencv将图片生成视频的实例详解
2019/01/08 Python
python 切换root 执行命令的方法
2019/01/19 Python
Python的缺点和劣势分析
2019/11/19 Python
Python zip函数打包元素实例解析
2019/12/11 Python
浅析Python数字类型和字符串类型的内置方法
2019/12/22 Python
python“静态”变量、实例变量与本地变量的声明示例
2020/11/13 Python
python Autopep8实现按PEP8风格自动排版Python代码
2021/03/02 Python
英国旅行箱包和行李箱购物网站:Travel Luggage & Cabin Bags
2019/08/26 全球购物
《蝙蝠和雷达》教学反思
2014/04/23 职场文书
社区活动策划方案
2014/08/21 职场文书
2014年企业党建工作总结
2014/12/18 职场文书
2015个人简历自我评价语
2015/03/11 职场文书
创业计划书之奶茶店开店方案范本!
2019/08/06 职场文书
导游词之新疆-喀纳斯
2019/10/10 职场文书
老生常谈 使用 CSS 实现三角形的技巧(多种方法)
2021/04/13 HTML / CSS
Pytorch中的数据集划分&正则化方法
2021/05/27 Python
go select编译期的优化处理逻辑使用场景分析
2021/06/28 Golang
Tomcat starup.bat 脚本实现开机自启动
2022/04/20 Servers