暂无图片
暂无图片
3
暂无图片
暂无图片
暂无图片

Mysql数据库批量远程巡检并生成html文件报告

原创 zhoushao12 2023-07-07
681

检查代码

#!/usr/bin/env python # -*- encoding: utf-8 -*- """ @文件 :ora-db_check.py @说明 : @时间 :2023/05/25 14:26:10 @作者 :Jaja @版本 :1.0 """ import pymysql from jinja2 import Environment, FileSystemLoader import yaml import os import time # 获取当前文件的路径 current_path = os.path.abspath(__file__) # 获取当前文件的父路径 father_path = os.path.abspath(os.path.dirname(current_path) + os.path.sep + ".") checkmate = time.strftime("%Y%m%d", time.localtime()) # 创建文件夹 if not os.path.exists(father_path + '/' + checkmate): os.makedirs(father_path + '/' + checkmate) CheckTitle = "¥¥¥¥¥¥¥Mysql数据库巡检报告" CheckUserName = "¥¥¥¥" CheckCompany = "¥¥¥¥¥¥" CheckScriptVersion = "V1.0" CheckExplain = "这是SQL查询结果的巡检说明。" current_datetime = time.strftime("%Y-%m-%d", time.localtime()) def run_query(): # 执行查询并获取结果集的列信息和数据行 queries = [ { 'query': "SELECT now_date,user,CURRENT_USER1,CONNECTION_ID,ifnull(db_name,'') dbname,Server_version,ifnull(all_db_size_MB,'') all_db_size_MB,ifnull(all_datafile_size_MB,'') all_datafile_size_MB,datadir,SOCKET,log_error,autocommit,log_bin,server_id from (SELECT now() now_date,USER() user,CURRENT_USER() CURRENT_USER1,CONNECTION_ID() CONNECTION_ID,DATABASE() db_name,version() Server_version,( SELECT sum( TRUNCATE ( ( data_length + index_length ) / 1024 / 1024, 2 ) ) AS 'all_db_size(MB)' FROM information_schema.TABLES b ) all_db_size_MB,(select truncate(sum(total_extents*extent_size)/1024/1024,2) from information_schema.FILES b) all_datafile_size_MB,( SELECT @@datadir ) datadir,( SELECT @@SOCKET ) SOCKET,( SELECT @@log_error ) log_error,( SELECT @@autocommit ) autocommit,( SELECT @@log_bin ) log_bin,( SELECT @@server_id ) server_id ) V", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '数据库基本信息', 'description': '数据库慢查询信息', 'flag': '0' }, { "query": "SELECT TABLE_SCHEMA,ENGINE,COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA','PERFORMANCE_SCHEMA','SYS','MYSQL') AND TABLE_TYPE='BASE TABLE' GROUP BY TABLE_SCHEMA,ENGINE", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '存储引擎分布', 'description': '存储引擎分布', 'flag': '0' }, { "query": "SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME,ifnull(table_rows,'') table_rows,ifnull(data_size_mb,'') data_size_mb,ifnull(index_size_mb,'') index_size_mb,ifnull(all_size_mb,'') all_size_mb,ifnull(max_size_mb,'') max_size_mb,ifnull(free_size_mb,'') free_size_mb,ifnull(disk_size_mb,'') disk_size_mb from (select a.SCHEMA_NAME, a.DEFAULT_CHARACTER_SET_NAME,a.DEFAULT_COLLATION_NAME,sum(table_rows) as table_rows,truncate(sum(data_length)/1024/1024, 2) as data_size_mb,truncate(sum(index_length)/1024/1024, 2) as index_size_mb,truncate(sum(data_length+index_length)/1024/1024, 2) as all_size_mb,truncate(sum(max_data_length)/1024/1024, 2) as max_size_mb,truncate(sum(data_free)/1024/1024, 2) as free_size_mb,max(f.filesize_M) as disk_size_mb from INFORMATION_SCHEMA.SCHEMATA a left outer join information_schema.tables b on a.SCHEMA_NAME=b.TABLE_SCHEMA left outer join (select substring(b.file_name,3,locate('/',b.file_name,3)-3) as db_name,truncate(sum(total_extents*extent_size)/1024/1024,2) filesize_M from information_schema.FILES b group by substring(b.file_name,3,locate('/',b.file_name,3)-3)) f on ( a.SCHEMA_NAME= f.db_name) group by a.SCHEMA_NAME, a.DEFAULT_CHARACTER_SET_NAME,a.DEFAULT_COLLATION_NAME order by sum(data_length) desc, sum(index_length) desc) V", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '数据库实例的所有数据库及其容量大小', 'description': '数据库实例的所有数据库及其容量大小', 'flag': '0' }, { "query": "select iis.database_name,iis.table_name,iis.index_name,round((iis.stat_value*@@innodb_page_size)/1024/1024, 2) SizeMB,s.NON_UNIQUE,s.INDEX_TYPE,GROUP_CONCAT(s.COLUMN_NAME order by SEQ_IN_INDEX) COLUMN_NAME from (select * from mysql.innodb_index_stats WHERE index_name not in ('PRIMARY','GEN_CLUST_INDEX') and stat_name='size' order by (stat_value*@@innodb_page_size) desc limit 10) iis left join INFORMATION_SCHEMA.STATISTICS s on (iis.database_name=s.TABLE_SCHEMA and iis.table_name=s.TABLE_NAME and iis.index_name=s.INDEX_NAME) GROUP BY iis.database_name,iis.TABLE_NAME,iis.INDEX_NAME,(iis.stat_value*@@innodb_page_size),s.NON_UNIQUE,s.INDEX_TYPE order by SizeMB", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '占用空间最大的前10个索引', 'description': '占用空间最大的前10个索引', 'flag': '0' }, { "query": "SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_COLLATION,ENGINE,TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA' ,'SYS','MYSQL','PERFORMANCE_SCHEMA') AND TABLE_TYPE='BASE TABLE' AND ENGINE NOT IN ('INNODB') ORDER BY TABLE_ROWS DESC", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '非 INNODB 存储引擎表', 'description': '非 INNODB 存储引擎表', 'flag': '0' }, { "query": "select * from performance_schema.global_variables where VARIABLE_NAME in ( 'datadir','SQL_MODE','socket','TIME_ZONE','tx_isolation','transaction_isolation','autocommit','innodb_lock_wait_timeout','max_connections','max_user_connections','slow_query_log','log_output','slow_query_log_file','long_query_time','log_queries_not_using_indexes','log_throttle_queries_not_using_indexes','log_throttle_queries_not_using_indexes','pid_file','log_error','lower_case_table_names','innodb_buffer_pool_size','innodb_flush_log_at_trx_commit','read_only', 'log_slave_updates','innodb_io_capacity','query_cache_type','query_cache_size','max_connect_errors','server_id','innodb_file_per_table')", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': 'Mysql数据库重要的参数', 'description': 'Mysql数据库重要的参数', 'flag': '0' }, { "query": "select * from performance_schema.global_variables where VARIABLE_NAME in ( 'server_id','server_uuid','log_bin','log_bin_basename','sql_log_bin','log_bin_index','log_slave_updates','read_only','slave_skip_errors','max_allowed_packet','slave_max_allowed_packet','auto_increment_increment','auto_increment_offset','sync_binlog','binlog_format','expire_logs_days','max_binlog_size','slave_skip_errors','sql_slave_skip_counter','slave_exec_mode','rpl_semi_sync_master_enabled','rpl_semi_sync_master_timeout','rpl_semi_sync_master_trace_level','rpl_semi_sync_master_wait_for_slave_count','rpl_semi_sync_master_wait_no_slave','rpl_semi_sync_master_wait_point','rpl_semi_sync_slave_enabled','rpl_semi_sync_slave_trace_level')", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': 'Mysql数据库重要的参数(主从复制参数)', 'description': 'Mysql数据库重要的参数(主从复制参数)', 'flag': '0' }, { "query": "select * from performance_schema.global_status where VARIABLE_NAME in ( 'connections','uptime','com_select','com_insert','com_delete','slow_queries','Created_tmp_tables','Created_tmp_files','Created_tmp_disk_tables','table_cache','Handler_read_rnd_next','Table_locks_immediate','Table_locks_waited','Open_files','Opened_tables','Sort_merge_passes','Sort_range','Sort_rows','Sort_scan')", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': 'Mysql数据库性能参数统计', 'description': 'Mysql数据库性能参数统计', 'flag': '0' }, { "query": "select table_schema, table_name from information_schema.tables where table_type='BASE TABLE' and (table_schema, table_name) not in ( select /*+ subquery(materialization) */ a.TABLE_SCHEMA,a.TABLE_NAME from information_schema.TABLE_CONSTRAINTS a where a.CONSTRAINT_TYPE in ('PRIMARY KEY','UNIQUE') and table_schema not in ('mysql', 'information_schema', 'sys', 'performance_schema') ) AND table_schema not in ('mysql', 'information_schema', 'sys', 'performance_schema') limit 100", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '无主键或唯一键的表', 'description': '无主键或唯一键的表', 'flag': '0' }, { 'query': "select * from mysql.slow_log", # 替换为第一个查询语句 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '数据库慢查询信息', 'description': '数据库慢查询信息', 'flag': '0' }, { 'query': "show slave status", # 替换为第一个查询语句 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '数据库主从同步信息', 'description': '数据库主从同步信息', 'flag': '0' }, { 'query': "SELECT query,ifnull(db,''),full_scan,exec_count,err_count,warn_count,total_latency,max_latency,avg_latency,rows_sent,rows_sent_avg,rows_examined,rows_examined_avg,first_seen,last_seen,digest from (SELECT sys.format_statement(DIGEST_TEXT) AS query, SCHEMA_NAME as db, IF(SUM_NO_GOOD_INDEX_USED > 0 OR SUM_NO_INDEX_USED > 0, '*', '') AS full_scan, COUNT_STAR AS exec_count, SUM_ERRORS AS err_count, SUM_WARNINGS AS warn_count, sys.format_time(SUM_TIMER_WAIT) AS total_latency, sys.format_time(MAX_TIMER_WAIT) AS max_latency, sys.format_time(AVG_TIMER_WAIT) AS avg_latency, SUM_ROWS_SENT AS rows_sent, ROUND(IFNULL(SUM_ROWS_SENT / NULLIF(COUNT_STAR, 0), 0)) AS rows_sent_avg, SUM_ROWS_EXAMINED AS rows_examined, ROUND(IFNULL(SUM_ROWS_EXAMINED / NULLIF(COUNT_STAR, 0), 0)) AS rows_examined_avg, FIRST_SEEN AS first_seen, LAST_SEEN AS last_seen, DIGEST AS digest FROM performance_schema.events_statements_summary_by_digest stmts JOIN sys.x$ps_digest_95th_percentile_by_avg_us AS top_percentile ON ROUND(stmts.avg_timer_wait/1000000) >= top_percentile.avg_us ORDER BY AVG_TIMER_WAIT DESC limit 10) V", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '查看平均执行时间值大于95%的平均执行时间的语句', 'description': '查看平均执行时间值大于95%的平均执行时间的语句(可近似地认为是平均执行时间超长的语句),默认情况下按照语句平均延迟(执行时间)降序排序', 'flag': '0' }, { 'query': "SELECT thd_id,conn_id,user,db,command,state,time,current_statement,statement_latency,progress,lock_latency,rows_examined,rows_sent,rows_affected,tmp_tables,tmp_disk_tables,full_scan,last_statement,last_statement_latency,current_memory,last_wait,last_wait_latency,source,trx_latency,trx_state,trx_autocommit,pid,program_name from (select * from sys.session where conn_id!=connection_id() and trx_state='ACTIVE') V", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '查看当前正在执行的语句进度信息', 'description': '查看当前正在执行的语句进度信息', 'flag': '0' }, { 'query': "SELECT thd_id,conn_id,user,ifnull(db,''),command,ifnull(state,''),time,ifnull(current_statement,''),ifnull(statement_latency,''),ifnull(progress,''),lock_latency,rows_examined,rows_sent,rows_affected,tmp_tables,tmp_disk_tables,full_scan,last_statement,last_statement_latency,current_memory,ifnull(last_wait,''),ifnull(last_wait_latency,''),ifnull(source,''),trx_latency,trx_state,trx_autocommit,pid,ifnull(program_name,'') from (select * from sys.session where conn_id!=connection_id() and trx_state='COMMITTED') V", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '查看已经执行完的语句相关统计信息', 'description': '查看已经执行完的语句相关统计信息', 'flag': '0' }, { 'query': "SELECT query,ifnull(db,''),full_scan,exec_count,err_count,warn_count,total_latency,max_latency,avg_latency,lock_latency,rows_sent,rows_sent_avg,rows_examined,rows_examined_avg,rows_affected,rows_affected_avg,tmp_tables,tmp_disk_tables,rows_sorted,sort_merge_passes,digest,first_seen,last_seen from (SELECT * FROM sys.statement_analysis WHERE full_scan = '*' order by exec_count desc limit 10) V", 'columns_var': 'columns1', 'rows_var': 'rows1', 'name': '执行次数Top10', 'description': '执行次数Top10', 'flag': '0' } # 添加更多查询语句和结果集变量 ] results = [] errors = [] for query in queries: try: conn = pymysql.connect(host=hosts['ip'], port=hosts['port'], user=hosts['username'],password=hosts['password'], database=hosts['sid']) cursor = conn.cursor() if query['flag'] == '0': cursor.execute(query['query']) columns = [col[0] for col in cursor.description] rows = cursor.fetchall() results.append({ 'columns_var': query['columns_var'], 'columns': columns, 'rows_var': query['rows_var'], 'rows': rows, 'name': query['name'], 'description': query['description'] }) cursor.close() conn.close() elif query['flag'] == '1': pass except pymysql.MySQLError as e: print('IP:%s 用途为: %s 的数据库无法正常连接,请检查!报错代码: %s' % (hosts['ip'], hosts['use'], e)) break except ConnectionRefusedError: print("连接被拒绝,请检查目标计算机或端口是否可用。") break except UnboundLocalError: break # 关闭数据库连接 # 加载 Jinja2 模板 env = Environment(loader=FileSystemLoader('.')) template = env.get_template('testjinjia2_v1.0.html') # 替换为实际的模板文件路径 # 渲染模板并生成 HTML 文件 output = template.render(results=results,current_datetime=current_datetime,CheckTitle=CheckTitle,CheckUserName=CheckUserName,CheckCompany=CheckCompany,CheckScriptVersion=CheckScriptVersion,CheckExplain=CheckExplain,errors=errors,CheckTitle_info=CheckTitle_info) with open(dbcheck_file, 'w') as file: file.write(output) print("数据库: %s 实例: %s 巡检报告HTML 文件已生成。" % (hosts['ip'],hosts['sid'])) # 读取YAML配置文件 with open('mysql_config.yaml', 'r') as f: try: config = yaml.safe_load(f) except yaml.scanner.ScannerError as e1: print("Yaml文件错误 %s" % e1) for db in config['databases']: for hosts in db['hosts']: dbcheck_file = Check_Oracle_file = father_path + '/' + checkmate + '/' + current_datetime + '_' + hosts['use'] + '_' + hosts['sid'] + '_' + hosts['ip'] + '.html' CheckTitle_info = '<br>' + '巡检数据库' + ':' + hosts['use'] + '<br>' + '巡检主机' + ':' + hosts['ip'] run_query() f.close()

yaml配置文件

databases: - type: mysql hosts: - ip: 192.168.98.139 port: 3306 username: root password: Passw0rd@123 sid: mysql use: 测试mysql数据库主 - ip: 192.168.98.139 port: 3307 username: root password: Passw0rd@123 sid: mysql use: 测试mysql数据库备

jinjia2模板

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> {{ CheckTitle }} </title> <style> .logo { width: 200px; /* 自定义宽度 */ height: auto; /* 自定义高度或使用 auto 保持纵横比 */ margin-right: 10px; } body { font-family: Arial, sans-serif; margin: 20px; } header { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; text-align: center; margin-bottom: 30px; } h1 { text-align: center; font-size: 40px; margin-bottom: 30px; } h0 { text-align: center; font-size: 40px; margin-bottom: 30px; } h2 { margin-top: 40px; margin-bottom: 16px; } table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #4CAF50; color: white; } ul { list-style-type: none; margin-left: 20px; } li { margin-bottom: 10px; } #top-link { position: fixed; bottom: 20px; right: 20px; padding: 10px; background-color: #4CAF50; color: white; text-decoration: none; display: none; } .result-set { margin-bottom: 40px; } .result-set-title { margin-bottom: 20px; } .result-set-title a { font-size: 18px; } .result-set-remark { font-style: italic; margin-top: 10px; } </style> <script> window.addEventListener('scroll', function() { var topLink = document.getElementById('top-link'); if (window.pageYOffset > 200) { topLink.style.display = 'block'; } else { topLink.style.display = 'none'; } }); </script> </head> <body> <header> <!--<img class="logo" src="jxbank.jpeg" alt="江西银行LOGO">--> <h1>{{ CheckTitle }} {{ CheckTitle_info }}</h1> </header> <header1> <p>巡检人员: {{ CheckUserName }}</p> <p>巡检公司: {{ CheckCompany }}</p> <p>巡检版本: {{ CheckScriptVersion }}</p> <p>巡检日期: {{ current_datetime }}</p> <p>巡检说明: {{ CheckExplain }}</p> </header1> <a href="#top" id="top-link">返回顶部</a> <h2>目录</h2> <ul> {% for i in range(results|length) %} <li><a href="#result-set-{{ i+1 }}">{{ results[i]['name'] }}</a></li> {% endfor %} {% if errors %} <li><a href="#sql-errors">SQL 执行错误</a></li> {% endif %} </ul> {% for i in range(results|length) %} <div class="result-set"> <h2 id="result-set-{{ i+1 }}" class="result-set-title">{{ results[i]['name'] }} <a href="#top">返回顶部</a></h2> <table> <tr> {% for column in results[i]['columns'] %} <th>{{ column }}</th> {% endfor %} </tr> {% for row in results[i]['rows'] %} <tr> {% for value in row %} <td>{{ value }}</td> {% endfor %} </tr> {% endfor %} </table> <p class="result-set-remark">{{ results[i]['description'] }}</p> </div> {% endfor %} {% if errors %} <div class="result-set"> <h2 id="sql-errors" class="result-set-title">SQL 执行错误 <a href="#top">返回顶部</a></h2> <ul> {% for error in errors %} <li>{{ error }}</li> {% endfor %} </ul> </div> {% endif %} </body> </html>

截屏20230707 16.19.10.png

最后修改时间:2023-07-10 10:08:07
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论