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 Innodb关键特性之插入缓冲(insert buffer)
Apr 08 MySQL
MySQL的join buffer原理
Apr 29 MySQL
MySQL中VARCHAR与CHAR格式数据的区别
May 26 MySQL
MySQL中InnoDB存储引擎的锁的基本使用教程
May 26 MySQL
MySQL 十大常用字符串函数详解
Jun 30 MySQL
MySQL索引是啥?不懂就问
Jul 21 MySQL
MySQL非空约束(not null)案例讲解
Aug 23 MySQL
Mysql如何实现不存在则插入,存在则更新
Mar 25 MySQL
解决Mysql报错 Table 'mysql.user' doesn't exist
May 06 MySQL
MySQL脏读,幻读和不可重复读
May 11 MySQL
Mysql将字符串按照指定字符分割的正确方法
May 30 MySQL
mysql sock文件存储了什么信息
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
Zend的Registry机制的使用说明
2013/05/02 PHP
解析PHP中数组元素升序、降序以及重新排序的函数
2013/06/20 PHP
Codeigniter检测表单post数据的方法
2015/03/21 PHP
Yii2框架可逆加密简单实现方法
2017/08/25 PHP
javascript 拖放效果实现代码
2010/01/22 Javascript
JavaScript移除数组元素减少长度的方法
2013/09/05 Javascript
ANGULARJS中用NG-BIND指令实现单向绑定的例子
2014/12/08 Javascript
浅析Node.js中使用依赖注入的相关问题及解决方法
2015/06/24 Javascript
AngularJS教程之简单应用程序示例
2016/08/16 Javascript
JS实现的图片预览插件与用法示例【不上传图片】
2016/11/25 Javascript
AngularJS中的JSONP实例解析
2016/12/01 Javascript
基于NodeJS+MongoDB+AngularJS+Bootstrap开发书店案例分析
2017/01/12 NodeJs
Underscore之Array_动力节点Java学院整理
2017/07/10 Javascript
详解jquery选择器的原理
2017/08/01 jQuery
Vue触发隐藏input file的方法实例详解
2019/08/14 Javascript
nodejs简单抓包工具使用详解
2019/08/23 NodeJs
JavaScript RegExp 对象用法详解
2019/09/24 Javascript
解决vue项目router切换太慢问题
2020/07/19 Javascript
[02:54]DOTA2英雄基础教程 撼地者
2014/01/14 DOTA
Python简单计算文件夹大小的方法
2015/07/14 Python
python利用urllib实现爬取京东网站商品图片的爬虫实例
2017/08/24 Python
详谈Python中列表list,元祖tuple和numpy中的array区别
2018/04/18 Python
Python绘图Matplotlib之坐标轴及刻度总结
2019/06/28 Python
django数据库自动重连的方法实例
2019/07/21 Python
Djang的model创建的字段和参数详解
2019/07/27 Python
HTML5 form标签之解放表单验证、增加文件上传、集成拖放的使用方法
2013/04/24 HTML / CSS
美国瑜伽服装和装备购物网站:Mukha Yoga
2019/02/22 全球购物
什么是lambda函数
2013/09/17 面试题
平民服装店创业计划书
2014/01/17 职场文书
七一表彰活动方案
2014/01/18 职场文书
财务总监岗位职责
2014/03/07 职场文书
医药营销个人求职信
2014/04/12 职场文书
2015年清明节扫墓演讲稿
2015/03/18 职场文书
稽核岗位职责范本
2015/04/13 职场文书
Python matplotlib多个子图绘制整合
2022/04/13 Python
VMware虚拟机安装 Windows Server 2022的详细图文教程
2022/09/23 Servers