最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python中MySQL監(jiān)控與日志配置實(shí)戰(zhàn)指南

 更新時(shí)間:2025年12月19日 09:08:20   作者:邏極  
這篇文章主要為大家詳細(xì)介紹了Python中MySQL監(jiān)控與日志配置的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

剛開始用Python操作MySQL時(shí),你是不是也這樣:程序突然變慢,數(shù)據(jù)庫(kù)連接莫名斷開,線上出了Bug卻找不到原因,只能對(duì)著日志文件“盲人摸象”?我當(dāng)年接手第一個(gè)Python Web項(xiàng)目時(shí),就因?yàn)闆]配監(jiān)控,半夜被報(bào)警電話叫醒,花了3小時(shí)才定位到一個(gè)簡(jiǎn)單的慢查詢。今天,我就帶你用30分鐘,從零搭建一套生產(chǎn)級(jí)的MySQL監(jiān)控與日志體系,讓你對(duì)數(shù)據(jù)庫(kù)狀態(tài)“明察秋毫”。

一、 為什么我們需要監(jiān)控與日志

去年我負(fù)責(zé)一個(gè)用戶中心的Python項(xiàng)目,用的是Flask + MySQL架構(gòu)。上線初期一切正常,直到某個(gè)周末,用戶反饋頁面加載要十幾秒。我們查了應(yīng)用日志、服務(wù)器負(fù)載,都沒問題。最后,還是一個(gè)有經(jīng)驗(yàn)的DBA提醒:“看看MySQL的慢查詢?nèi)罩景伞?rdquo;

結(jié)果一查,發(fā)現(xiàn)一條原本0.1秒的查詢,因?yàn)槿鄙偎饕?,在?shù)據(jù)量增長(zhǎng)后變成了15秒的“巨獸”。沒有監(jiān)控,我們就像在黑暗中開車,直到撞墻才知道路有問題。

對(duì)于Python開發(fā)者來說,MySQL監(jiān)控與日志配置能幫你解決三大痛點(diǎn):

  • 性能瓶頸定位:快速找到拖慢系統(tǒng)的SQL語句
  • 故障預(yù)警與排查:連接異常、死鎖發(fā)生時(shí)能及時(shí)知道原因
  • 容量規(guī)劃與優(yōu)化:了解數(shù)據(jù)庫(kù)負(fù)載趨勢(shì),為擴(kuò)容提供數(shù)據(jù)支撐

接下來,我會(huì)手把手帶你配置三個(gè)核心部分:慢查詢?nèi)罩?/strong>、性能監(jiān)控(Performance Schema)Python端的連接健康檢查。

二、 環(huán)境準(zhǔn)備:搭建你的實(shí)驗(yàn)戰(zhàn)場(chǎng)

在開始實(shí)戰(zhàn)前,我們需要準(zhǔn)備好“戰(zhàn)場(chǎng)”。這里我假設(shè)你已經(jīng)有了Python和MySQL的基礎(chǔ)環(huán)境。

2.1 檢查與安裝MySQL(以Ubuntu為例)

# 檢查MySQL是否安裝
mysql --version

# 如果未安裝,使用apt安裝(其他系統(tǒng)請(qǐng)參考官方文檔)
sudo apt update
sudo apt install mysql-server mysql-client

# 啟動(dòng)MySQL服務(wù)
sudo systemctl start mysql
sudo systemctl enable mysql

# 安全初始化(設(shè)置root密碼等)
sudo mysql_secure_installation

2.2 安裝Python MySQL驅(qū)動(dòng)

我們將使用最流行的pymysql驅(qū)動(dòng),它純Python實(shí)現(xiàn),兼容性好。

# 創(chuàng)建虛擬環(huán)境(推薦)
python -m venv mysql-monitor-env
source mysql-monitor-env/bin/activate  # Linux/Mac
# 或 mysql-monitor-env\Scripts\activate  # Windows

# 安裝pymysql
pip install pymysql

# 安裝額外的工具庫(kù),用于后續(xù)的監(jiān)控?cái)?shù)據(jù)可視化
pip install matplotlib pandas

2.3 創(chuàng)建測(cè)試數(shù)據(jù)庫(kù)和數(shù)據(jù)

讓我們創(chuàng)建一個(gè)真實(shí)的業(yè)務(wù)場(chǎng)景——電商用戶訂單系統(tǒng)。

# create_test_data.py
import pymysql
import random
from datetime import datetime, timedelta

def create_test_database():
    """創(chuàng)建測(cè)試數(shù)據(jù)庫(kù)和表,并插入模擬數(shù)據(jù)"""
    
    # 連接MySQL(請(qǐng)?zhí)鎿Q為你的實(shí)際密碼)
    connection = pymysql.connect(
        host='localhost',
        user='root',
        password='your_password',  # 改成你的MySQL root密碼
        charset='utf8mb4',
        cursorclass=pymysql.cursors.DictCursor
    )
    
    try:
        with connection.cursor() as cursor:
            # 創(chuàng)建數(shù)據(jù)庫(kù)
            cursor.execute("CREATE DATABASE IF NOT EXISTS ecommerce_monitor")
            cursor.execute("USE ecommerce_monitor")
            
            # 創(chuàng)建用戶表
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS users (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    username VARCHAR(50) NOT NULL UNIQUE,
                    email VARCHAR(100) NOT NULL,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    INDEX idx_username (username),
                    INDEX idx_created_at (created_at)
                )
            """)
            
            # 創(chuàng)建訂單表(故意不加索引,用于演示慢查詢)
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS orders (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    user_id INT NOT NULL,
                    amount DECIMAL(10, 2) NOT NULL,
                    status ENUM('pending', 'paid', 'shipped', 'delivered') DEFAULT 'pending',
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    FOREIGN KEY (user_id) REFERENCES users(id)
                    # 注意:這里故意不在user_id和created_at上加索引!
                )
            """)
            
            # 插入測(cè)試用戶數(shù)據(jù)
            print("插入用戶數(shù)據(jù)...")
            users = []
            for i in range(1, 1001):  # 1000個(gè)用戶
                users.append((f'user{i}', f'user{i}@example.com'))
            
            cursor.executemany(
                "INSERT INTO users (username, email) VALUES (%s, %s)",
                users
            )
            
            # 插入測(cè)試訂單數(shù)據(jù)(更多數(shù)據(jù),用于模擬真實(shí)場(chǎng)景)
            print("插入訂單數(shù)據(jù)...")
            orders = []
            start_date = datetime.now() - timedelta(days=365)
            
            for i in range(1, 50001):  # 5萬條訂單
                user_id = random.randint(1, 1000)
                amount = round(random.uniform(10.0, 1000.0), 2)
                days_ago = random.randint(0, 365)
                order_date = start_date + timedelta(days=days_ago)
                
                # 隨機(jī)狀態(tài)
                status = random.choice(['pending', 'paid', 'shipped', 'delivered'])
                
                orders.append((
                    user_id,
                    amount,
                    status,
                    order_date.strftime('%Y-%m-%d %H:%M:%S')
                ))
            
            # 分批插入,避免單次SQL太大
            batch_size = 1000
            for i in range(0, len(orders), batch_size):
                batch = orders[i:i+batch_size]
                cursor.executemany(
                    """INSERT INTO orders (user_id, amount, status, created_at) 
                       VALUES (%s, %s, %s, %s)""",
                    batch
                )
                connection.commit()
                print(f"已插入 {min(i+batch_size, len(orders))}/{len(orders)} 條訂單")
            
            print("測(cè)試數(shù)據(jù)創(chuàng)建完成!")
            
    finally:
        connection.close()

if __name__ == "__main__":
    create_test_database()

運(yùn)行這個(gè)腳本前,記得把password='your_password'改成你的MySQL root密碼。這個(gè)腳本會(huì)創(chuàng)建5萬條訂單數(shù)據(jù),足夠我們演示監(jiān)控效果了。

三、 核心概念:MySQL監(jiān)控的“三駕馬車”

在深入配置前,我們先理解三個(gè)核心概念,這就像醫(yī)生看病需要了解體溫、血壓、心率一樣。

1. 慢查詢?nèi)罩荆⊿low Query Log)

  • 是什么:記錄執(zhí)行時(shí)間超過指定閾值的SQL語句
  • 為什么重要:80%的性能問題由20%的慢查詢引起。找到它們,就找到了優(yōu)化關(guān)鍵點(diǎn)
  • 怎么用:通過MySQL配置開啟,設(shè)置時(shí)間閾值(如2秒)

2. Performance Schema

  • 是什么:MySQL 5.5+引入的性能監(jiān)控框架,像數(shù)據(jù)庫(kù)的"儀表盤"
  • 為什么重要:提供實(shí)時(shí)、低開銷的性能數(shù)據(jù),包括連接數(shù)、鎖等待、SQL執(zhí)行統(tǒng)計(jì)等
  • 怎么用:默認(rèn)啟用,通過SQL查詢各種性能表

3. 錯(cuò)誤日志(Error Log)

  • 是什么:記錄MySQL啟動(dòng)、運(yùn)行、停止過程中的錯(cuò)誤和警告信息
  • 為什么重要:故障排查的第一現(xiàn)場(chǎng),連接失敗、崩潰原因都在這里
  • 怎么用:MySQL自動(dòng)記錄,只需知道查看位置

四、 實(shí)戰(zhàn)演練:配置你的第一套監(jiān)控系統(tǒng)

4.1 配置慢查詢?nèi)罩?mdash;—找到拖慢系統(tǒng)的"罪魁禍?zhǔn)?quot;

慢查詢?nèi)罩臼莾?yōu)化數(shù)據(jù)庫(kù)的第一步。讓我們配置并分析它。

步驟1:修改MySQL配置

# 編輯MySQL配置文件
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf  # Ubuntu路徑,其他系統(tǒng)可能不同

# 在[mysqld]部分添加或修改以下配置:
"""
[mysqld]
# 開啟慢查詢?nèi)罩?
slow_query_log = 1

# 指定慢查詢?nèi)罩疚募窂?
slow_query_log_file = /var/log/mysql/mysql-slow.log

# 設(shè)置慢查詢閾值(單位:秒),這里設(shè)為1秒,生產(chǎn)環(huán)境通常設(shè)2-3秒
long_query_time = 1

# 記錄未使用索引的查詢(即使執(zhí)行時(shí)間沒超過閾值)
log_queries_not_using_indexes = 1

# 每分鐘最多記錄多少條慢查詢,避免日志爆炸
log_throttle_queries_not_using_indexes = 10
"""

# 保存后重啟MySQL
sudo systemctl restart mysql

步驟2:驗(yàn)證配置并生成慢查詢

# generate_slow_queries.py
import pymysql
import time

def generate_slow_queries():
    """執(zhí)行一些會(huì)觸發(fā)慢查詢的SQL"""
    
    connection = pymysql.connect(
        host='localhost',
        user='root',
        password='your_password',
        database='ecommerce_monitor',
        charset='utf8mb4',
        cursorclass=pymysql.cursors.DictCursor
    )
    
    try:
        with connection.cursor() as cursor:
            print("執(zhí)行可能較慢的查詢...")
            
            # 查詢1:全表掃描(orders表沒有user_id索引)
            start = time.time()
            cursor.execute("""
                SELECT * FROM orders 
                WHERE user_id = 500 
                AND created_at > '2023-01-01'
                ORDER BY created_at DESC
            """)
            result1 = cursor.fetchall()
            elapsed1 = time.time() - start
            print(f"查詢1(無索引條件查詢)耗時(shí): {elapsed1:.3f}秒,返回 {len(result1)} 條記錄")
            
            # 查詢2:復(fù)雜聯(lián)表查詢
            start = time.time()
            cursor.execute("""
                SELECT u.username, COUNT(o.id) as order_count, SUM(o.amount) as total_amount
                FROM users u
                LEFT JOIN orders o ON u.id = o.user_id
                WHERE o.created_at > '2023-06-01'
                GROUP BY u.id
                HAVING order_count > 5
                ORDER BY total_amount DESC
                LIMIT 20
            """)
            result2 = cursor.fetchall()
            elapsed2 = time.time() - start
            print(f"查詢2(復(fù)雜聯(lián)表分組)耗時(shí): {elapsed2:.3f}秒")
            
            # 查詢3:使用索引的快速查詢(作為對(duì)比)
            start = time.time()
            cursor.execute("SELECT * FROM users WHERE username = 'user500'")
            result3 = cursor.fetchall()
            elapsed3 = time.time() - start
            print(f"查詢3(使用索引查詢)耗時(shí): {elapsed3:.3f}秒")
            
    finally:
        connection.close()

if __name__ == "__main__":
    generate_slow_queries()

運(yùn)行這個(gè)腳本,你會(huì)看到前兩個(gè)查詢明顯較慢(特別是第一個(gè),因?yàn)?code>orders表的user_idcreated_at字段沒有索引)。

步驟3:分析慢查詢?nèi)罩?/p>

# 查看慢查詢?nèi)罩荆ㄐ枰猻udo權(quán)限)
sudo tail -100 /var/log/mysql/mysql-slow.log

# 使用mysqldumpslow工具分析(MySQL自帶)
sudo mysqldumpslow /var/log/mysql/mysql-slow.log -t 10 -s at

# 輸出示例:
"""
Count: 3  Time=1.23s (3s)  Lock=0.00s (0s)  Rows=152.7 (458), root[root]@localhost
  SELECT * FROM orders WHERE user_id = N AND created_at > 'S' ORDER BY created_at DESC
"""

這個(gè)輸出告訴我們:同一種模式的查詢執(zhí)行了3次,平均耗時(shí)1.23秒,每次返回約153行數(shù)據(jù)。問題很明顯:需要在orders.user_idorders.created_at上建立索引。

步驟4:根據(jù)分析結(jié)果優(yōu)化

# add_indexes.py
import pymysql

def add_necessary_indexes():
    """根據(jù)慢查詢分析添加缺失的索引"""
    
    connection = pymysql.connect(
        host='localhost',
        user='root',
        password='your_password',
        database='ecommerce_monitor',
        charset='utf8mb4'
    )
    
    try:
        with connection.cursor() as cursor:
            print("添加缺失的索引...")
            
            # 為orders表的user_id添加索引
            cursor.execute("""
                ALTER TABLE orders 
                ADD INDEX idx_user_id (user_id),
                ADD INDEX idx_created_at (created_at),
                ADD INDEX idx_user_created (user_id, created_at)
            """)
            
            print("索引添加完成!")
            
            # 驗(yàn)證優(yōu)化效果
            print("\n驗(yàn)證優(yōu)化效果:")
            cursor.execute("EXPLAIN SELECT * FROM orders WHERE user_id = 500 AND created_at > '2023-01-01'")
            explain_result = cursor.fetchone()
            
            print(f"查詢執(zhí)行計(jì)劃:")
            print(f"- 使用的索引: {explain_result.get('key', '無')}")
            print(f"- 掃描行數(shù): {explain_result.get('rows', '未知')}")
            print(f"- 查詢類型: {explain_result.get('type', '未知')}")
            
    finally:
        connection.close()

if __name__ == "__main__":
    add_necessary_indexes()

運(yùn)行后再執(zhí)行之前的慢查詢腳本,你會(huì)發(fā)現(xiàn)第一個(gè)查詢從秒級(jí)變成了毫秒級(jí)!這就是監(jiān)控的價(jià)值:數(shù)據(jù)驅(qū)動(dòng)優(yōu)化。

4.2 使用Performance Schema——數(shù)據(jù)庫(kù)的實(shí)時(shí)"儀表盤"

Performance Schema(性能模式)是MySQL內(nèi)置的性能監(jiān)控工具,開銷極小,適合生產(chǎn)環(huán)境。

實(shí)戰(zhàn):用Python監(jiān)控實(shí)時(shí)性能

# performance_monitor.py
import pymysql
import time
import pandas as pd
from datetime import datetime

class MySQLPerformanceMonitor:
    """MySQL性能監(jiān)控器"""
    
    def __init__(self, host='localhost', user='root', password='', database=''):
        self.connection = pymysql.connect(
            host=host,
            user=user,
            password=password,
            database=database,
            charset='utf8mb4',
            cursorclass=pymysql.cursors.DictCursor
        )
    
    def get_slow_queries_summary(self):
        """獲取慢查詢摘要統(tǒng)計(jì)"""
        with self.connection.cursor() as cursor:
            cursor.execute("""
                SELECT 
                    DIGEST_TEXT as query_pattern,
                    COUNT_STAR as exec_count,
                    AVG_TIMER_WAIT/1000000000000 as avg_time_sec,
                    MAX_TIMER_WAIT/1000000000000 as max_time_sec,
                    SUM_ROWS_EXAMINED as rows_examined_total,
                    SUM_ROWS_SENT as rows_sent_total
                FROM performance_schema.events_statements_summary_by_digest
                WHERE DIGEST_TEXT IS NOT NULL
                AND AVG_TIMER_WAIT > 1000000000  # 大于1毫秒
                ORDER BY avg_time_sec DESC
                LIMIT 10
            """)
            return cursor.fetchall()
    
    def get_connection_stats(self):
        """獲取連接統(tǒng)計(jì)"""
        with self.connection.cursor() as cursor:
            cursor.execute("""
                SELECT 
                    USER as user,
                    HOST as host,
                    COUNT(*) as connection_count,
                    GROUP_CONCAT(COMMAND) as commands
                FROM information_schema.PROCESSLIST
                WHERE COMMAND != 'Sleep'
                GROUP BY USER, HOST
            """)
            return cursor.fetchall()
    
    def get_table_access_stats(self, hours=24):
        """獲取表訪問統(tǒng)計(jì)"""
        with self.connection.cursor() as cursor:
            # 注意:這個(gè)查詢需要開啟某些consumer,默認(rèn)可能沒有數(shù)據(jù)
            cursor.execute("""
                SELECT 
                    OBJECT_SCHEMA as db_name,
                    OBJECT_NAME as table_name,
                    COUNT_READ as read_count,
                    COUNT_WRITE as write_count,
                    COUNT_FETCH as fetch_count
                FROM performance_schema.table_io_waits_summary_by_table
                WHERE COUNT_STAR > 0
                ORDER BY COUNT_STAR DESC
                LIMIT 10
            """)
            return cursor.fetchall()
    
    def monitor_loop(self, interval=60, duration=300):
        """監(jiān)控循環(huán),定期收集性能數(shù)據(jù)"""
        print(f"開始性能監(jiān)控,每{interval}秒采樣一次,持續(xù){duration}秒...")
        
        data_points = []
        start_time = time.time()
        
        while time.time() - start_time < duration:
            timestamp = datetime.now()
            
            # 收集各種性能指標(biāo)
            slow_queries = self.get_slow_queries_summary()
            connections = self.get_connection_stats()
            
            # 記錄數(shù)據(jù)點(diǎn)
            data_point = {
                'timestamp': timestamp,
                'slow_query_count': len(slow_queries),
                'active_connections': sum(c['connection_count'] for c in connections),
                'top_slow_query_time': slow_queries[0]['avg_time_sec'] if slow_queries else 0
            }
            
            data_points.append(data_point)
            print(f"[{timestamp}] 慢查詢數(shù): {data_point['slow_query_count']}, "
                  f"活躍連接: {data_point['active_connections']}")
            
            time.sleep(interval)
        
        # 轉(zhuǎn)換為DataFrame便于分析
        df = pd.DataFrame(data_points)
        return df
    
    def close(self):
        self.connection.close()

# 使用示例
if __name__ == "__main__":
    monitor = MySQLPerformanceMonitor(
        host='localhost',
        user='root',
        password='your_password',
        database='ecommerce_monitor'
    )
    
    try:
        # 獲取一次性的性能快照
        print("=== 當(dāng)前慢查詢TOP 10 ===")
        slow_queries = monitor.get_slow_queries_summary()
        for i, query in enumerate(slow_queries[:5], 1):
            print(f"{i}. {query['query_pattern'][:80]}...")
            print(f"   平均耗時(shí): {query['avg_time_sec']:.3f}s, 執(zhí)行次數(shù): {query['exec_count']}")
        
        print("\n=== 當(dāng)前連接統(tǒng)計(jì) ===")
        connections = monitor.get_connection_stats()
        for conn in connections:
            print(f"用戶: {conn['user']}, 連接數(shù): {conn['connection_count']}")
        
        # 運(yùn)行監(jiān)控循環(huán)(生產(chǎn)環(huán)境可以改為后臺(tái)任務(wù))
        # df = monitor.monitor_loop(interval=10, duration=60)
        # print(f"\n監(jiān)控?cái)?shù)據(jù)摘要:\n{df.describe()}")
        
    finally:
        monitor.close()

這個(gè)監(jiān)控器展示了如何從Performance Schema獲取關(guān)鍵指標(biāo)。在生產(chǎn)環(huán)境中,你可以將這些數(shù)據(jù)發(fā)送到Prometheus、Grafana等監(jiān)控系統(tǒng)。

4.3 Python端的連接健康檢查與日志集成

除了監(jiān)控MySQL服務(wù)器,我們還需要在Python應(yīng)用層做好健康檢查和日志記錄。

實(shí)戰(zhàn):帶健康檢查和日志的連接池

# db_connection_pool.py
import pymysql
import logging
import time
from threading import Lock
from contextlib import contextmanager
from datetime import datetime, timedelta

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('mysql_operations.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger('MySQLMonitor')

class HealthyConnectionPool:
    """帶健康檢查的MySQL連接池"""
    
    def __init__(self, max_connections=10, **kwargs):
        self.max_connections = max_connections
        self.connection_args = kwargs
        self.pool = []
        self.in_use = set()
        self.lock = Lock()
        self.last_health_check = datetime.min
        
        # 初始化連接池
        self._initialize_pool()
        
        logger.info(f"連接池初始化完成,最大連接數(shù): {max_connections}")
    
    def _initialize_pool(self):
        """初始化連接池"""
        for _ in range(min(3, self.max_connections)):
            conn = self._create_connection()
            if conn:
                self.pool.append(conn)
    
    def _create_connection(self):
        """創(chuàng)建新連接"""
        try:
            conn = pymysql.connect(**self.connection_args)
            
            # 設(shè)置連接屬性,便于追蹤
            with conn.cursor() as cursor:
                cursor.execute("SET @python_client_id = %s", 
                             (f"pool_conn_{len(self.pool)}",))
            
            logger.debug(f"創(chuàng)建新數(shù)據(jù)庫(kù)連接: {conn.server_version}")
            return conn
        except Exception as e:
            logger.error(f"創(chuàng)建數(shù)據(jù)庫(kù)連接失敗: {e}")
            return None
    
    def _health_check(self):
        """定期健康檢查"""
        now = datetime.now()
        if now - self.last_health_check < timedelta(minutes=5):
            return
        
        with self.lock:
            healthy_connections = []
            for conn in self.pool:
                try:
                    with conn.cursor() as cursor:
                        cursor.execute("SELECT 1")
                        cursor.fetchone()
                    healthy_connections.append(conn)
                except Exception as e:
                    logger.warning(f"連接健康檢查失敗,關(guān)閉異常連接: {e}")
                    try:
                        conn.close()
                    except:
                        pass
            
            # 補(bǔ)充連接
            while len(healthy_connections) < self.max_connections:
                new_conn = self._create_connection()
                if new_conn:
                    healthy_connections.append(new_conn)
                else:
                    break
            
            self.pool = healthy_connections
            self.last_health_check = now
            
            logger.info(f"健康檢查完成,活躍連接數(shù): {len(self.pool)}")
    
    @contextmanager
    def get_connection(self):
        """獲取連接(上下文管理器方式)"""
        self._health_check()
        
        conn = None
        start_time = time.time()
        
        with self.lock:
            if self.pool:
                conn = self.pool.pop()
            elif len(self.in_use) < self.max_connections:
                conn = self._create_connection()
            
            if conn:
                self.in_use.add(id(conn))
        
        if not conn:
            wait_time = time.time() - start_time
            logger.error(f"獲取數(shù)據(jù)庫(kù)連接超時(shí),等待{wait_time:.2f}秒后仍無可用連接")
            raise Exception("數(shù)據(jù)庫(kù)連接池耗盡")
        
        try:
            # 記錄連接獲取
            logger.debug(f"獲取數(shù)據(jù)庫(kù)連接,當(dāng)前使用中: {len(self.in_use)}")
            
            # 執(zhí)行查詢前的準(zhǔn)備
            with conn.cursor() as cursor:
                cursor.execute("SET @query_start_time = NOW(6)")
            
            yield conn
            
        except pymysql.Error as e:
            # 記錄數(shù)據(jù)庫(kù)錯(cuò)誤
            error_code, error_msg = e.args
            logger.error(f"數(shù)據(jù)庫(kù)操作錯(cuò)誤 [{error_code}]: {error_msg}")
            
            # 根據(jù)錯(cuò)誤類型決定是否關(guān)閉連接
            if error_code in (2006, 2013):  # 連接相關(guān)錯(cuò)誤
                logger.warning("連接異常,將關(guān)閉并創(chuàng)建新連接")
                try:
                    conn.close()
                except:
                    pass
                conn = self._create_connection()
            raise
            
        finally:
            # 記錄查詢執(zhí)行時(shí)間
            try:
                with conn.cursor() as cursor:
                    cursor.execute("SELECT TIMESTAMPDIFF(MICROSECOND, @query_start_time, NOW(6)) / 1000000 as exec_time")
                    result = cursor.fetchone()
                    exec_time = result['exec_time'] if result else 0
                    
                    if exec_time > 1.0:  # 超過1秒的記錄為慢查詢
                        logger.warning(f"慢查詢檢測(cè): 執(zhí)行時(shí)間 {exec_time:.3f}秒")
            except:
                exec_time = 0
            
            # 歸還連接
            with self.lock:
                if conn and conn.open:
                    self.pool.append(conn)
                if id(conn) in self.in_use:
                    self.in_use.remove(id(conn))
            
            logger.debug(f"歸還數(shù)據(jù)庫(kù)連接,執(zhí)行時(shí)間: {exec_time:.3f}秒")
    
    def close_all(self):
        """關(guān)閉所有連接"""
        with self.lock:
            for conn in self.pool:
                try:
                    conn.close()
                except:
                    pass
            self.pool.clear()
            self.in_use.clear()
        
        logger.info("連接池已關(guān)閉所有連接")

# 使用示例
def example_usage():
    """使用帶監(jiān)控的連接池示例"""
    
    # 創(chuàng)建連接池
    pool = HealthyConnectionPool(
        max_connections=5,
        host='localhost',
        user='root',
        password='your_password',
        database='ecommerce_monitor',
        charset='utf8mb4',
        cursorclass=pymysql.cursors.DictCursor
    )
    
    try:
        # 示例1:正常查詢
        with pool.get_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute("SELECT COUNT(*) as count FROM orders")
                result = cursor.fetchone()
                print(f"訂單總數(shù): {result['count']}")
        
        # 示例2:事務(wù)操作
        with pool.get_connection() as conn:
            try:
                with conn.cursor() as cursor:
                    # 開始事務(wù)
                    cursor.execute("START TRANSACTION")
                    
                    # 插入新訂單
                    cursor.execute(
                        "INSERT INTO orders (user_id, amount, status) VALUES (%s, %s, %s)",
                        (1, 99.99, 'pending')
                    )
                    
                    # 更新用戶統(tǒng)計(jì)(模擬業(yè)務(wù)邏輯)
                    cursor.execute(
                        "UPDATE users SET email = %s WHERE id = %s",
                        ('updated@example.com', 1)
                    )
                    
                    # 提交事務(wù)
                    conn.commit()
                    logger.info("事務(wù)提交成功")
                    
            except Exception as e:
                conn.rollback()
                logger.error(f"事務(wù)回滾: {e}")
                raise
        
        # 示例3:批量查詢(模擬業(yè)務(wù)高峰)
        import concurrent.futures
        
        def query_user_orders(user_id):
            """查詢用戶訂單"""
            with pool.get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(
                        "SELECT * FROM orders WHERE user_id = %s LIMIT 10",
                        (user_id,)
                    )
                    return cursor.fetchall()
        
        # 模擬并發(fā)查詢
        with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
            user_ids = list(range(1, 10))
            futures = [executor.submit(query_user_orders, uid) for uid in user_ids]
            
            for future in concurrent.futures.as_completed(futures):
                try:
                    orders = future.result()
                    print(f"查詢到 {len(orders)} 條訂單")
                except Exception as e:
                    logger.error(f"并發(fā)查詢失敗: {e}")
    
    finally:
        pool.close_all()

if __name__ == "__main__":
    example_usage()

這個(gè)連接池實(shí)現(xiàn)包含了幾個(gè)關(guān)鍵特性:

  • 連接健康檢查:定期驗(yàn)證連接是否可用
  • 慢查詢?nèi)罩?/strong>:自動(dòng)記錄執(zhí)行時(shí)間超過1秒的查詢
  • 錯(cuò)誤處理:根據(jù)錯(cuò)誤類型智能處理連接
  • 連接追蹤:記錄連接使用情況,便于排查問題

五、 監(jiān)控指標(biāo)匯總:你需要關(guān)注哪些關(guān)鍵數(shù)據(jù)?

在實(shí)際項(xiàng)目中,你需要關(guān)注以下關(guān)鍵指標(biāo)。我整理了一個(gè)表格,方便你快速參考:

監(jiān)控類別具體指標(biāo)正常范圍告警閾值檢查頻率Python獲取方式
連接狀態(tài)當(dāng)前連接數(shù)< 最大連接數(shù)80%> 最大連接數(shù)90%每分鐘SHOW STATUS LIKE 'Threads_connected'
連接錯(cuò)誤數(shù)接近0每小時(shí)>10每小時(shí)SHOW STATUS LIKE 'Connection_errors%'
查詢性能慢查詢數(shù)量接近0每分鐘>5實(shí)時(shí)慢查詢?nèi)罩?/td>
平均查詢時(shí)間< 100ms> 500ms每分鐘Performance Schema
QPS(每秒查詢)根據(jù)業(yè)務(wù)定突增100%每分鐘SHOW STATUS LIKE 'Queries'
資源使用InnoDB緩沖池命中率> 95%< 90%每分鐘SHOW STATUS LIKE 'Innodb_buffer_pool%'
臨時(shí)表磁盤使用接近0> 100MB每小時(shí)SHOW STATUS LIKE 'Created_tmp%'
復(fù)制狀態(tài)主從延遲< 1秒> 5秒每分鐘SHOW SLAVE STATUS

六、 生產(chǎn)環(huán)境部署建議

當(dāng)你掌握了基本監(jiān)控配置后,在生產(chǎn)環(huán)境中我建議:

分層監(jiān)控

  • 基礎(chǔ)設(shè)施層:服務(wù)器CPU、內(nèi)存、磁盤
  • MySQL層:連接數(shù)、慢查詢、鎖等待
  • 應(yīng)用層:Python連接池狀態(tài)、查詢耗時(shí)

告警策略

  • 緊急告警(電話/短信):數(shù)據(jù)庫(kù)宕機(jī)、連接池耗盡
  • 重要告警(郵件/釘釘):慢查詢突增、主從延遲
  • 提醒通知(郵件):磁盤空間不足、備份完成

日志管理

# 生產(chǎn)環(huán)境日志配置示例
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler

# 按大小輪轉(zhuǎn)的日志文件
size_handler = RotatingFileHandler(
    'mysql_operations.log',
    maxBytes=100*1024*1024,  # 100MB
    backupCount=10
)

# 按時(shí)間輪轉(zhuǎn)的日志文件
time_handler = TimedRotatingFileHandler(
    'mysql_slow_queries.log',
    when='midnight',  # 每天輪轉(zhuǎn)
    backupCount=30
)

# 發(fā)送到監(jiān)控系統(tǒng)(如ELK)
# 可以使用logstash handler或直接API發(fā)送

七、 總結(jié)與進(jìn)階方向

恭喜你!現(xiàn)在你已經(jīng)掌握了Python MySQL監(jiān)控與日志配置的核心技能。讓我們回顧一下今天的收獲:

7.1 核心要點(diǎn)總結(jié)

  • 慢查詢?nèi)罩?/strong>是性能優(yōu)化的起點(diǎn),配置簡(jiǎn)單但效果顯著
  • Performance Schema提供實(shí)時(shí)、低開銷的性能數(shù)據(jù)
  • Python端的健康檢查能提前發(fā)現(xiàn)連接問題
  • 分層監(jiān)控合理告警是生產(chǎn)環(huán)境的必備

7.2 我當(dāng)年踩過的坑

坑1:開啟了慢查詢?nèi)罩镜珱]定期清理,磁盤被撐滿

解決方案:配置日志輪轉(zhuǎn),或使用pt-query-digest分析后清理

坑2:監(jiān)控指標(biāo)太多,反而找不到重點(diǎn)

解決方案:先關(guān)注連接數(shù)、慢查詢、緩沖池命中率這三個(gè)核心指標(biāo)

坑3:Python連接泄露,導(dǎo)致連接數(shù)緩慢增長(zhǎng)

解決方案:使用上下文管理器(with語句),確保連接總是被正確歸還

7.3 進(jìn)階學(xué)習(xí)方向

如果你想深入MySQL監(jiān)控,我建議按這個(gè)路徑學(xué)習(xí):

  • 監(jiān)控工具:學(xué)習(xí)使用Percona Monitoring and Management (PMM) 或 VividCortex
  • SQL優(yōu)化:深入理解EXPLAIN執(zhí)行計(jì)劃,學(xué)習(xí)索引優(yōu)化技巧
  • 架構(gòu)設(shè)計(jì):了解讀寫分離、分庫(kù)分表下的監(jiān)控策略
  • 自動(dòng)化:使用Ansible/Terraform自動(dòng)化監(jiān)控部署

到此這篇關(guān)于Python中MySQL監(jiān)控與日志配置實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Python MySQL監(jiān)控內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • django如何自定義manage.py管理命令

    django如何自定義manage.py管理命令

    這篇文章主要介紹了django如何自定義manage.py管理命令,幫助大家更好的理解和學(xué)習(xí)使用django框架,感興趣的朋友可以了解下
    2021-04-04
  • python3.6+django2.0+mysql搭建網(wǎng)站過程詳解

    python3.6+django2.0+mysql搭建網(wǎng)站過程詳解

    這篇文章主要介紹了python3.6+django2.0+mysql搭建網(wǎng)站過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 詳解Python3中字符串中的數(shù)字提取方法

    詳解Python3中字符串中的數(shù)字提取方法

    這篇文章主要介紹了詳解Python3中字符串中的數(shù)字提取方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-01-01
  • python創(chuàng)建關(guān)聯(lián)數(shù)組(字典)的方法

    python創(chuàng)建關(guān)聯(lián)數(shù)組(字典)的方法

    這篇文章主要介紹了python創(chuàng)建關(guān)聯(lián)數(shù)組(字典)的方法,涉及Python操作字典的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-05-05
  • Python3.5 創(chuàng)建文件的簡(jiǎn)單實(shí)例

    Python3.5 創(chuàng)建文件的簡(jiǎn)單實(shí)例

    下面小編就為大家分享一篇Python3.5 創(chuàng)建文件的簡(jiǎn)單實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-04-04
  • Python版本信息獲取方法詳解與實(shí)戰(zhàn)

    Python版本信息獲取方法詳解與實(shí)戰(zhàn)

    在Python開發(fā)中,獲取Python版本號(hào)是調(diào)試、兼容性檢查和版本控制的重要基礎(chǔ)操作,本文詳細(xì)介紹了如何使用 sys 和 platform 模塊獲取Python的主版本號(hào)、次版本號(hào)、微版本號(hào)以及完整的版本信息,需要的朋友可以參考下
    2025-09-09
  • 解決Python?出現(xiàn)File?“<stdin>“,?line?1非語法錯(cuò)誤的問題

    解決Python?出現(xiàn)File?“<stdin>“,?line?1非語法錯(cuò)誤的問題

    這篇文章主要介紹了Python?出現(xiàn)File?“<stdin>“,?line?1非語法錯(cuò)誤的解決辦法,本文給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • 詳解Python裝飾器 給你的咖啡加點(diǎn)料

    詳解Python裝飾器 給你的咖啡加點(diǎn)料

    今天你的咖啡加糖了嗎? 讓我們通過一個(gè)簡(jiǎn)單的例子來引出裝飾器的概念及用法。在引出裝飾器之前,我們先來了解一下函數(shù)的概念,感興趣的朋友跟隨小編一起看看吧
    2021-07-07
  • numpy中np.nanmax和np.max的區(qū)別及坑

    numpy中np.nanmax和np.max的區(qū)別及坑

    這篇文章主要介紹了numpy中np.nanmax和np.max的區(qū)別及坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • python創(chuàng)建列表并給列表賦初始值的方法

    python創(chuàng)建列表并給列表賦初始值的方法

    這篇文章主要介紹了python創(chuàng)建列表并給列表賦初始值的方法,涉及Python列表的定義與賦值技巧,需要的朋友可以參考下
    2015-07-07

最新評(píng)論

论坛| 德阳市| 镶黄旗| 樟树市| 鹤山市| 静乐县| 鄱阳县| 济源市| 蕲春县| 郧西县| 清苑县| 舒兰市| 武功县| 信宜市| 塘沽区| 英吉沙县| 徐汇区| 固阳县| 怀来县| 循化| 金山区| 东方市| 清苑县| 民县| 阿拉善左旗| 双城市| 和平区| 寿光市| 根河市| 广州市| 宜兰市| 虎林市| 枣阳市| 都兰县| 承德县| 囊谦县| 筠连县| 绥芬河市| 穆棱市| 英吉沙县| 陕西省|