Python 助力 DBA:高效批量管理数据库服务器的多线程解决方案-多库查询汇总工具实现
批量数据库服务器连接测试与数据汇总:Python实现方案
作为数据库服务器运维人员,我们经常需要面对大量服务器的连接测试和数据汇总工作。本文将介绍一个使用Python实现的高效解决方案,可以帮助我们快速完成这些任务。
需求概述
- 从配置文件中读取要测试的数据库服务器IP地址列表。
- 批量测试数据库服务器的连接情况。
- 在所有可连接的服务器上执行相同的SQL查询。
- 将查询结果汇总到一个单独的数据库中,并包含对应服务器的IP地址。
- 自动创建结果表,表名按日期随机生成。
- 提供详细的日志输出,包括实时的处理进度。
实现方案
我们使用Python来实现这个方案,主要利用了以下库和技术:
pyodbc
: 用于数据库连接和操作configparser
: 读取配置文件concurrent.futures
: 实现并发处理logging
: 日志记录- 多线程技术:提高处理效率
代码实现
以下是完整的Python代码实现:
import pyodbc
import logging
import configparser
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
from datetime import datetime
import random
import string# 配置日志
def setup_logger():"""设置日志记录器,同时输出到文件和控制台"""logger = logging.getLogger()logger.setLevel(logging.INFO)# 文件处理器file_handler = logging.FileHandler('db_query_aggregation.log')file_handler.setLevel(logging.INFO)file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')file_handler.setFormatter(file_formatter)# 控制台处理器console_handler = logging.StreamHandler()console_handler.setLevel(logging.INFO)console_formatter = logging.Formatter('%(message)s')console_handler.setFormatter(console_formatter)logger.addHandler(file_handler)logger.addHandler(console_handler)return loggerlogger = setup_logger()def read_config(config_file):"""读取配置文件:param config_file: 配置文件路径:return: 包含配置信息的字典"""try:config = configparser.ConfigParser()config.read(config_file)return {'ip_list_file': config['Files']['ip_list_file'],'source_db_username': config['SourceDB']['username'],'source_db_password': config['SourceDB']['password'],'target_db_info': dict(config['TargetDB']),'max_workers': int(config['Settings']['max_workers']),'query': config['Query']['sql']}except Exception as e:logger.error(f"读取配置文件时出错: {e}")raisedef read_ip_list(file_path):"""从文件中读取IP地址列表:param file_path: IP地址文件路径:return: IP地址列表"""try:with open(file_path, 'r') as file:return [line.strip() for line in file if line.strip()]except IOError as e:logger.error(f"无法读取IP地址文件: {e}")return []def create_connection(server, database, username, password):"""创建数据库连接:param server: 服务器地址:param database: 数据库名称:param username: 用户名:param password: 密码:return: 数据库连接对象,如果连接失败则返回None"""try:conn_str = f'DRIVER={{ODBC Driver 17 for SQL Server}};SERVER={server};DATABASE={database};UID={username};PWD={password}'return pyodbc.connect(conn_str, timeout=5)except pyodbc.Error as e:logger.error(f"连接到服务器 {server} 失败: {e}")return Nonedef execute_query(connection, query):"""执行SQL查询:param connection: 数据库连接对象:param query: SQL查询语句:return: 查询结果列表"""try:cursor = connection.cursor()cursor.execute(query)return cursor.fetchall()except pyodbc.Error as e:logger.error(f"执行查询时出错: {e}")return []def process_server(ip, username, password, query):"""处理单个服务器的查询:param ip: 服务器IP地址:param username: 数据库用户名:param password: 数据库密码:param query: SQL查询语句:return: 元组 (IP地址, 查询结果)"""start_time = time.time()logger.info(f"开始处理服务器 {ip}")try:conn = create_connection(ip, 'master', username, password)if conn:results = execute_query(conn, query)conn.close()end_time = time.time()processing_time = end_time - start_timelogger.info(f"服务器 {ip} 处理完成. 获取 {len(results)} 行数据. 耗时 {processing_time:.2f} 秒")return ip, resultsexcept Exception as e:logger.error(f"处理服务器 {ip} 时发生错误: {e}")logger.info(f"服务器 {ip} 处理失败")return ip, []def create_target_table(connection, table_name, columns):"""在目标数据库中创建表:param connection: 目标数据库连接对象:param table_name: 要创建的表名:param columns: 列定义列表"""try:cursor = connection.cursor()create_table_query = f"CREATE TABLE {table_name} (ServerIP VARCHAR(15), {', '.join(columns)})"cursor.execute(create_table_query)connection.commit()logger.info(f"成功创建表 {table_name}")except pyodbc.Error as e:logger.error(f"创建目标表时出错: {e}")raisedef insert_data(connection, table_name, data):"""将数据插入目标数据库:param connection: 目标数据库连接对象:param table_name: 目标表名:param data: 要插入的数据列表:return: 插入的行数"""try:cursor = connection.cursor()placeholders = ', '.join(['?' for _ in range(len(data[0]))])insert_query = f"INSERT INTO {table_name} VALUES ({placeholders})"cursor.fast_executemany = Truecursor.executemany(insert_query, data)connection.commit()return cursor.rowcountexcept pyodbc.Error as e:logger.error(f"插入数据时出错: {e}")connection.rollback()return 0def generate_table_name():"""生成随机表名:return: 生成的表名"""date_str = datetime.now().strftime("%Y%m%d")random_str = ''.join(random.choices(string.ascii_lowercase, k=5))return f"QueryResults_{date_str}_{random_str}"def main():"""主函数,协调整个数据查询和汇总过程"""try:# 读取配置config = read_config('config.ini')ip_list = read_ip_list(config['ip_list_file'])if not ip_list:logger.error("IP地址列表为空,程序终止")returnlogger.info(f"开始处理 {len(ip_list)} 个服务器")# 并发查询所有服务器results = []with ThreadPoolExecutor(max_workers=config['max_workers']) as executor:future_to_ip = {executor.submit(process_server, ip, config['source_db_username'], config['source_db_password'], config['query']): ip for ip in ip_list}for future in as_completed(future_to_ip):ip, result = future.result()if result:results.extend([(ip,) + tuple(row) for row in result])if not results:logger.info("没有查询到数据,程序终止")return# 连接目标数据库target_conn = create_connection(**config['target_db_info'])if not target_conn:logger.error("无法连接到目标数据库,程序终止")return# 创建目标表并插入数据table_name = generate_table_name()columns = [f"Column{i} VARCHAR(100)" for i in range(len(results[0]) - 1)]create_target_table(target_conn, table_name, columns)rows_inserted = insert_data(target_conn, table_name, results)target_conn.close()logger.info(f"数据汇总完成。插入 {rows_inserted} 行到表 {table_name}")print(f"查询结果已插入表: {table_name}")except Exception as e:logger.critical(f"程序执行过程中发生严重错误: {e}")print(f"程序执行过程中发生错误,请查看日志文件获取详细信息。")if __name__ == "__main__":main()
代码说明
-
配置文件读取:使用
configparser
模块读取配置文件,包括数据库连接信息、查询语句等。 -
多线程处理:使用
ThreadPoolExecutor
并发执行查询,提高效率。 -
异常处理:每个关键操作都包含了异常处理,确保程序的稳定性。
-
模块化设计:将不同功能分解为独立的函数,提高代码的可读性和可维护性。
-
日志记录:使用
logging
模块记录详细的操作日志,同时输出到文件和控制台。 -
动态表创建:在目标数据库中动态创建表,表名包含日期和随机字符串。
-
数据汇总:将所有服务器的查询结果汇总到一个列表中,包括服务器IP地址。
-
批量数据插入:使用
executemany
批量插入数据到目标表。
使用说明
- 创建
config.ini
配置文件,包含以下内容:
[Files]
ip_list_file = server_ip_list.txt[SourceDB]
username = your_source_username
password = your_source_password[TargetDB]
server = your_target_server
database = your_target_database
username = your_target_username
password = your_target_password[Settings]
max_workers = 50[Query]
sql = SELECT column1, column2 FROM your_table
-
准备一个包含要查询的服务器IP地址的文本文件(如
server_ip_list.txt
)。 -
运行脚本,它将并发查询所有服务器,汇总结果(包括服务器IP),并插入到目标数据库的新表中。
-
脚本执行完成后,会输出生成的表名。
结论
这个Python脚本提供了一个高效、灵活的解决方案,可以批量测试数据库服务器连接、执行查询并汇总结果。它具有以下优点:
- 并发处理,大幅提高效率
- 详细的日志记录,便于监控和调试
- 灵活的配置,易于适应不同环境
- 异常处理完善,提高程序稳定性
- 结果包含服务器IP,便于追踪数据来源
对于需要管理大量数据库服务器的运维人员来说,这个脚本可以显著提高工作效率。您可以根据实际需求进一步调整和优化这个脚本,例如添加更多的错误处理、优化查询性能,或者扩展功能以支持更复杂的操作。
通过使用这个脚本,您可以轻松地对多个数据库服务器进行批量操作,并将结果汇总到一个中心位置,大大简化了数据库管理和监控的工作流程。
相关文章:

Python 助力 DBA:高效批量管理数据库服务器的多线程解决方案-多库查询汇总工具实现
批量数据库服务器连接测试与数据汇总:Python实现方案 作为数据库服务器运维人员,我们经常需要面对大量服务器的连接测试和数据汇总工作。本文将介绍一个使用Python实现的高效解决方案,可以帮助我们快速完成这些任务。 需求概述 从配置文件…...

vue响应式数据-修改对象的属性值,视图不更新
如图: 一:问题是: 我把数据处理后能console.log()打印出来,但是页面的内容不能同步的更新渲染; 二:要求: 在数组循环列表里面,我点击单个的item按钮时,需要实时加载进度…...

【OpenCV计算机视觉】图像处理——平滑
本篇文章记录我学习【OpenCV】图像处理中关于“平滑”的知识点,希望我的分享对你有所帮助。 目录 一、什么是平滑处理 1、平滑的目的是什么? 2、常见的图像噪声 (1)椒盐噪声 编辑(2) 高斯噪声 &a…...

C#编程报错- “ComboBox”是“...ComboBox”和“...ComboBox”之间的不明确的引用
1、问题描述 在学习使用C#中的Winform平台编写一个串口助手程序时, 在编写一个更新ComboBox列表是遇到了问题,出错的代码是 2、报错信息 CS1503 参数 2: 无法从“System.Windows.Forms.ComboBox”转换为“System.Windows.Forms.ComboBox” CS1503 …...

JAVA:访问者模式(Visitor Pattern)的技术指南
1、简述 访问者模式(Visitor Pattern)是一种行为型设计模式,允许你将操作分离到不同的对象中,而无需修改对象本身的结构。这种模式特别适合复杂对象结构中对其元素进行操作的场景。 本文将介绍访问者模式的核心概念、优缺点,并通过详细代码示例展示如何在实际应用中实现…...

YashanDB共享集群产品能力观测:细节足见功底
本文基于前泽塔数科研发总监-王若楠2024年11月在“2024年国产数据库创新生态大会”-“根”技术专场的演讲整理形成,主要对崖山共享集群YAC的架构、功能、高可用性、性能四大方面进行全面测试,并分享了测试环境和测试结论。 年初,基于某些商业…...

【Linux】—简单实现一个shell(myshell)
大家好呀,我是残念,希望在你看完之后,能对你有所帮助,有什么不足请指正!共同学习交流哦! 本文由:残念ing原创CSDN首发,如需要转载请通知 个人主页:残念ing-CSDN博客&…...

@FeignClient用于Nacos微服务间的接口调用
依赖:<!-- spring-boot启动依赖 --> <!-- 提供者 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- openFeign --> <…...

认识javascript中的模块化
什么是模块化? 将程序⽂件依据⼀定规则拆分成多个文件,拆分出来每个⽂件就是⼀个模块,模块中的数据都是私有的,模块之间互相隔离。如果不进行隔离,可能会造成模块间的变量定义有冲突,导致程序崩溃 为啥要使…...

容器设计模式:Sidecar
文章目录 容器设计模式:Sidecar 模式1. 什么是 Sidecar 模式?2. Sidecar 模式的原理2.1 工作机制2.2 常见用途 3. Sidecar 模式示例示例:日志收集 4. Sidecar 模式的架构图图例: 5. Sidecar 模式的优点6. Sidecar 模式的局限性7. …...

ensp 静态路由配置
A公司有广州总部、重庆分部和深圳分部3个办公地点,各分部与总部之间使用路由器互联。广州、重庆、深圳的路由器分别为R1、R2、R3,为路由器配置静态路由,使所有计算机能够互相访问,实训拓扑图如图所示 绘制拓扑图 给pc机配置ip地址…...

MATLAB图卷积神经网络GCN处理分子数据集节点分类研究
全文链接:https://tecdat.cn/?p38570 本文主要探讨了如何利用图卷积网络(GCN)对图中的节点进行分类。介绍了相关的数据处理、模型构建、训练及测试等环节,通过对分子数据集的操作实践,展示了完整的节点分类流程&#…...

Android-Glide详解
目录 一,介绍 二,使用 三,源码分析思路 四,with源码分析 五,模拟Glide生命周期管理 一,介绍 Glide目前是安卓最主流的加载图片的框架,也是源码最为复杂的框架之一。 要想完完全全吃透Glide的源…...

2.Nuxt学习 组件使用和路由跳转相关
组件定义和使用 普通组件的使用 在Nuxt的项目中,可以直接在components文件夹下建立组件 在页面中直接使用 无需引入 多层级组件的使用 我们有时候会需要多层级组件来简化代码结构 比如我们需要给Banner组件添加一个子组件 我们直接建立其名称的文件夹 写入子组…...

代码开发相关操作
使用Vue项目管理器创建项目:(vue脚手架安装一次就可以全局使用) windowR打开命令窗口,输入vue ui,进入GUI页面,点击创建-> 设置项目名称,在初始化git下面输入:init project&…...

动态导出word文件支持转pdf
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、功能说明二、使用步骤1.controller2.工具类 DocumentUtil 导出样式 前言 提示:这里可以添加本文要记录的大概内容: 例如ÿ…...

登陆harbor发现证书是错误的, 那么如何更新harbor的证书呢
Error response from daemon: Get "https://172.16.21.35/v2/": tls: failed to verify certificate: x509: certificate is valid for 127.0.0.1, ::1, 172.16.21.30, not 172.16.21.35 版本 v2.10.1-b7b88476 不需要从头看, 直接看最下面的成功的证书创建 这里面首…...

【Leetcode Top 100】199. 二叉树的右视图
问题背景 给定一个二叉树的 根节点 r o o t root root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 数据约束 二叉树的节点个数的范围是 [ 0 , 100 ] [0,100] [0,100] − 100 ≤ N o d e . v a l ≤ 100…...

React自学:如何使用localStorage,以及如何实现删除笔记操作
1. 初始化notes 以下这段代码完成了这些操作: 调用 localStorage.getItem("notes") 从浏览器的本地存储中获取名为 “notes” 的数据。使用 JSON.parse 将获取到的字符串解析成数组。如果本地存储中没有 “notes” 数据(返回值为 null&#…...

go语言使用websocket发送一条消息A,持续接收返回的消息
在Go语言中实现一个WebSocket客户端,可以使用gorilla/websocket这个非常流行的库来处理WebSocket连接。下面是一个简单的示例,展示了如何创建一个WebSocket客户端,向服务器发送消息"A",并持续接收来自服务器的响应。 首…...

如何对小型固定翼无人机进行最优的路径跟随控制?
控制架构 文章继续采用的是 ULTRA-Extra无人机,相关参数如下: 这里用于guidance law的无人机运动学模型为: { x ˙ p V a cos γ cos χ V w cos γ w cos χ w y ˙ p V a cos γ sin χ V w cos γ w sin χ…...

C++常见面试题-初级2
1. C和C有什么区别? C是面向对象的语言,而C是面向过程的语言;C引入new/delete运算符,取代了C中的malloc/free库函数;C引入引用的概念,而C中没有;C引入类的概念,而C中没有࿱…...

Spring Security 6 系列之二 - 基于数据库的用户认证和认证原理
之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风…...

mfc140.dll是什么东西?mfc140.dll缺失的几种具体解决方法
mfc140.dll是Microsoft Foundation Classes(MFC)库中的一个动态链接库(DLL)文件,它是微软基础类库的一部分,为Windows应用程序的开发提供了丰富的类库和接口。MFC库旨在简化Windows应用程序的开发过程&…...

【STM32 Modbus编程】-作为主设备写入多个线圈和寄存器
作为主设备写入多个线圈和寄存器 文章目录 作为主设备写入多个线圈和寄存器1、硬件准备与连接1.1 RS485模块介绍1.2 硬件配置与接线1.3 软件准备2、写入多个线圈2.1 数据格式2.2 发送数据2.3 结果3、写入多个寄存器3.1 数据格式3.2 发送数据3.3 结果本文将实现STM32作为ModBus主…...

Windows安全中心(病毒和威胁防护)的注册
文章目录 Windows安全中心(病毒和威胁防护)的注册1. 简介2. WSC注册初探3. WSC注册原理分析4. 关于AMPPL5. 参考 Windows安全中心(病毒和威胁防护)的注册 本文我们来分析一下Windows安全中心(Windows Security Center…...

微积分复习笔记 Calculus Volume 2 - 4.2 Direction Fields and Numerical Methods
4.2 Direction Fields and Numerical Methods - Calculus Volume 2 | OpenStax...

深入理解旋转位置编码(RoPE)及其在大型语言模型中的应用
文章目录 前言一、 旋转位置编码原理1、RoPE概述2、 复数域内的旋转1、位置编码生成2、 应用位置编码二、RoPE的实现细节1、RotaryEmbedding类设计2、apply_rotary_pos_emb函数3、demo_apply_rotary_pos_emb函数三、完整RoPE代码Demo前言 随着自然语言处理(NLP)领域的快速发…...

内网穿透的应用-在OpenWrt上轻松搭建SFTP服务,安全传输文件不再难!
文章目录 前言1. 安装openssh-sftp-server2. 安装cpolar工具3.配置SFTP远程访问4.固定远程连接地址 前言 本次教程我们将在OpenWRT系统上安装SFTP服务,并结合cpolar内网穿透,创建安全隧道映射22端口,实现在公网环境下远程OpenWRT SFTP&#…...

【图像处理lec3、4】空间域的图像增强
目录 1. 空间域图像增强的背景与目标 2. 空间域处理的数学描述 3. 灰度级变换 4. 幂律变换(Power-Law Transformation) 5、 分段线性变换 Case 1: 对比度拉伸 Case 2: 灰度切片 Case 3: 按位切片 6、对数变换(Logarithmic Transform…...