python 统计MySQL大于100万的表

本文介绍了一种统计MySQL中行数超过100万的大表的方法,通过查询information_schema数据库和使用count(1)函数,结合Python脚本实现数据的收集与分析,并最终将结果输出到Excel中。

python 统计MySQL大于100万的表

一、需求分析

线上的MySQL服务器,最近有很多慢查询。需要统计出行数大于100万的表,进行统一优化。

需要筛选出符合条件的表,统计到excel中,格式如下:

库名表名行数
db1users1234567

 

 

 

二、统计表的行数

统计表的行数,有2中方法:

1. 通过查询mysql的information_schema数据库中INFODB_SYS_TABLESTATS表,它记录了innodb类型每个表大致的数据行数

2. select count(1) from 库名.表名 

 

下面来分析一下这2种方案。

第一种方案,不是精确记录的。虽然效率快,但是表会有遗漏!

第二钟方案,才是准确的。虽然慢,但是表不会遗漏。

 

备注:

count(1)其实这个1,并不是表示第一个字段,而是表示一个固定值。

count(1),其实就是计算一共有多少符合条件的行。
1并不是表示第一个字段,而是表示一个固定值。
其实就可以想成表中有这么一个字段,这个字段就是固定值1,count(1),就是计算一共有多少个1.

 

写入json文件

下面这段代码,是参考我之前写的一篇文章:

https://www.cnblogs.com/xiao987334176/p/9901692.html

 

在此基础上,做了部分修改,完整代码如下:

#!/usr/bin/env python3
# coding: utf-8

import pymysql
import json

conn = pymysql.connect(
    host="192.168.91.128",  # mysql ip地址
    user="root",
    passwd="root",
    port=3306,  # mysql 端口号,注意:必须是int类型
    connect_timeout = 3  # 超时时间
)

cur = conn.cursor()  # 创建游标

# 获取mysql中所有数据库
cur.execute('SHOW DATABASES')

data_all = cur.fetchall()  # 获取执行的返回结果
# print(data_all)

dic = {}  # 大字典,第一层
for i in data_all:
    if i[0] not in dic:  # 判断库名不在dic中时
        # 排序列表,排除mysql自带的数据库
        exclude_list = ["sys", "information_schema", "mysql", "performance_schema"]
        if i[0] not in exclude_list:  # 判断不在列表中时
            # 写入第二层数据
            dic[i[0]] = {'name': i[0], 'table_list': []}
            conn.select_db(i[0])  # 切换到指定的库中
            cur.execute('SHOW TABLES')  # 查看库中所有的表
            ret = cur.fetchall()  # 获取执行结果

            for j in ret:
                # 查询表的行数
                cur.execute('select count(1) from `%s`;'% j[0])
                ret = cur.fetchall()
                # print(ret)
                for k in ret:
                    print({'tname': j[0], 'rows': k[0]})
                    dic[i[0]]['table_list'].append({'tname': j[0], 'rows': k[0]})


with open('tj.json','w',encoding='utf-8') as f:
    f.write(json.dumps(dic))
View Code

 

三、写入excel中

直接读取tj.json文件,进行写入,完整代码如下:

#!/usr/bin/env python3
# coding: utf-8

import xlwt
import json
from collections import OrderedDict

f = xlwt.Workbook()
sheet1 = f.add_sheet('统计', cell_overwrite_ok=True)
row0 = ["库名", "表名", "行数"]

# 写第一行
for i in range(0, len(row0)):
    sheet1.write(0, i, row0[i])

# 加载json文件
with open("tj.json", 'r') as load_f:
    load_dict = json.load(load_f)  # 反序列化文件
    order_dic = OrderedDict()  # 有序字典
    for key in sorted(load_dict):  # 先对普通字典key做排序
        order_dic[key] = load_dict[key]  # 再写入key

    num = 0  # 计数器
    for i in order_dic:

        # 遍历所有表
        for j in order_dic[i]["table_list"]:
            # 判断行数大于100万时
            if j['rows'] > 1000000:
                # 写入库名
                sheet1.write(num + 1, 0, i)
                # 写入表名
                sheet1.write(num + 1, 1, j['tname'])
                # 写入行数
                sheet1.write(num + 1, 2, j['rows'])
                num += 1  # 自增1

    f.save('test1.xls')
View Code

 

执行程序,打开excel文件,效果如下:

 

posted @ 2018-11-26 17:18 肖祥 阅读( ...) 评论( ...) 编辑 收藏
<think> 我们正在讨论的是如何高效地从MySQL中分页拉取30条数据。根据引用内容,我们可以总结出几个关键点: 1. 传统分页(使用LIMIT offset, size)在数据量很时(例如30)效率很低,因为需要先扫描前offset条记录,然后取后面的size条。 2. 引用[1]提到了一种优化方法:利用主键或唯一索引,通过条件(id_pk > 上一页最后一条记录的id)来避免扫描量数据。 3. 引用[2]提到了分库分的思路,但30数据通常不需要分库分(除非单数据量极),但我们可以考虑分区或分。 4. 引用[3]提到了内存溢出问题,因此我们在拉取量数据时要注意分批处理,避免一次性拉取过多导致内存溢出。 因此,针对30数据的分页拉取,最佳实践应该是: 1. 避免使用传统的LIMIT offset, size方式,特别是当offset很时。 2. 采用基于游标的分页(即利用索引列,如自增主键,记录上一页最后一条记录的id,然后查询条件为id>last_id,按每页固定小取数据)。 3. 如果查询条件复杂,可能需要建立合适的索引来支持快速定位。 4. 如果数据量确实很,考虑分批次拉取(例如每次1条,循环30次),并在每次拉取后处理数据并释放资源。 具体步骤: 方法一(推荐):使用索引列(如自增主键id)进行分页 SELECT * FROM table_name WHERE id > last_id ORDER BY id LIMIT page_size; 其中last_id是上一页最后一条记录的id,初始为0(或最小值),然后每次将上一页的最后一条id作为下一页的last_id。 方法二:如果必须使用页码而不是游标,可以尝试使用子查询优化 SELECT * FROM table_name WHERE id >= (SELECT id FROM table_name ORDER BY id LIMIT offset, 1) LIMIT page_size; 但这种方法在offset很时,子查询仍然需要扫描offset条记录,效率不如方法一。 方法三:如果查询条件复杂,无法使用主键索引,则考虑在where条件上建立合适的索引,并尽量使用覆盖索引来减少回。 另外,注意: - 30数据一次性拉取可能导致网络传输和内存压力,建议分页拉取(例如每页1条,分30次),并在应用程序中逐批处理。 - 如果数据需要全部拉取(例如导出),则使用游标方式(方法一)循环获取直到结束。 代码示例(伪代码): last_id = 0 page_size = 10000 # 每页1条 total = 300000 while last_id < total: sql = "SELECT * FROM table_name WHERE id > %s ORDER BY id LIMIT %s" results = execute(sql, (last_id, page_size)) # 处理results if len(results) > 0: last_id = results[-1]['id'] # 更新last_id为当前页最后一条记录的id 注意:这里假设id是连续递增的,如果id不连续(例如有删除),那么这种方法可能会漏掉部分数据(比如删除后的空缺),但通常不会影响,因为我们是按照id顺序取数据,不会重复也不会漏掉(除非在分页过程中有新数据插入,但插入的数据id大于当前last_id,会在后续获取,不影响已有数据)。 如果没有自增主键,那么可以考虑使用其他唯一且递增的列(如时间戳,但要确保唯一性,否则可能有重复或遗漏),或者使用组合索引。 总结:最佳实践是使用基于游标的分页(方法一),避免偏移量带来的性能问题,并分批拉取避免内存溢出。 相关问题: 1. 为什么传统分页(LIMIT offset, size)在数据量时性能差? 2. 基于游标的分页(Cursor-based Pagination)适用于哪些场景? 3. 如何解决在分页过程中新增或删除数据导致的数据重复或遗漏问题? </think> ### MySQL分页拉取30数据的最佳实践 针对数据量分页查询(30级),以下是优化方法和最佳实践,结合引用内容和MySQL特性: #### ⚠️ 核心问题分析 1. **传统分页的性能瓶颈** 使用 `LIMIT offset, size` 时(如 `LIMIT 200000, 1000`),MySQL需扫描前20+1000条数据,效率极低[^1]。 2. **内存与I/O压力** 单次拉取30数据可能导致内存溢出和网络阻塞[^3]。 --- #### ✅ 最佳实践方案 ##### 方法1:基于游标的分页(推荐) **原理**:通过主键或有序索引定位起始点,避免全扫描。 ```sql SELECT * FROM table WHERE id > {上一页最后一条id} -- 通过有序索引定位 ORDER BY id LIMIT {pageSize}; -- 每次拉取固定条数 ``` - **优势** - 复杂度从 $O(n)$ 降至 $O(1)$,无需计算偏移量 - 索引扫描速度快,适合连续分页[^1] - **适用场景** 主键自增、有序数据遍历(如日志导出) ##### 方法2:延迟关联优化 **原理**:先通过索引检索主键,再回查询数据。 ```sql SELECT t.* FROM table t JOIN ( SELECT id FROM table ORDER BY id LIMIT {offset}, {pageSize} -- 仅扫描索引 ) AS tmp ON t.id = tmp.id; ``` - **优势** - 减少回数据量,尤其对含 `TEXT/BLOB` 字段的有效 - 偏移量较时性能提升显著(对比直接 `LIMIT`) ##### 方法3:分区策略 **原理**:将按规则拆分为子(如按ID范围),并行查询后合并结果[^2]。 ```sql -- 示例:按ID范围拆分查询 (SELECT * FROM table_part1 WHERE ... LIMIT 10000) UNION ALL (SELECT * FROM table_part2 WHERE ... LIMIT 10000) ``` - **适用场景** 超数据量(亿级),需结合业务分区规则 - **注意** 需预先设计分区方案,维护成本较高 --- #### 🔧 辅助优化措施 1. **索引优化** - 确保 `WHERE` 和 `ORDER BY` 字段有复合索引 (如 `INDEX(create_time, id)`) 2. **分批处理** ```python # Python伪代码示例 last_id = 0 batch_size = 5000 # 每批5000条 while True: data = query("SELECT * FROM table WHERE id > %s ORDER BY id LIMIT %s", (last_id, batch_size)) if not data: break process(data) # 处理本批数据 last_id = data[-1].id # 更新游标 ``` 3. **避免 `SELECT *`** 只查询必要字段,减少数据传输和内存占用。 --- #### ️ 注意事项 1. **避免在分页过程中增删数据** 可能导致游标分页的重复或遗漏,建议在业务低峰期操作。 2. **超偏移量场景** 若必须用 `LIMIT offset`,可配合条件过滤: ```sql SELECT * FROM table WHERE create_time > '2023-01-01' -- 缩小扫描范围 ORDER BY id LIMIT 100000, 1000; ``` 3. **内存控制** 单次分页小建议 ≤ 1条[JDBC默认Fetch Size为100],通过多次分批完成30拉取[^3]。 > **总结**:优先使用**游标分页(方法1)**,结合**索引优化**和**分批处理**,可高效稳定地完成30数据拉取。30数据在合理优化下通常无需分库分[^2]。 --- ### 📚 相关问题 1. 游标分页在数据频繁增删的场景下如何保证准确性? 2. MySQL中哪些索引类型最适合分页优化? 3. 如何评估分页查询是否需要分库分?(参考指标:数据量、增长速度、QPS) [^1]: MySQL数据量分页查询方法及其优化 [^2]: mysql亿级数据数据库优化方案 [^3]: 数据量分页查询的内存问题解决方案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值