Skip to content

库/实例级锁 (Database/Instance-Level Lock) ​

📋 概述 ​

库/实例级锁是数据库锁机制中粒度最大的一种锁类型,它锁定的是整个数据库实例或数据库。当一个事务获得库级锁后,其他事务对该数据库的某些操作会被阻塞,直到锁被释放。

核心特点 ​

✅ 优点:

  • 全局一致性: 确保整个数据库的一致性状态
  • 实现简单: 只需维护一个锁对象
  • 无死锁: 单一锁对象,不存在循环等待
  • 适合维护: 完美支持备份、恢复等操作

❌ 缺点:

  • 并发度极低: 锁定期间几乎无法使用数据库
  • 影响范围大: 所有用户和会话都受影响
  • 可用性差: 不适合在线系统
  • 风险高: 误用会导致服务中断

适用场景 ​

  • 物理备份: 一致性快照、文件系统级别备份
  • 实例维护: 版本升级、配置修改
  • 灾难恢复: 数据恢复、故障切换
  • 迁移操作: 数据迁移、实例迁移
  • 全局统计: 需要全库一致性的分析操作

🔧 工作原理 ​

锁的类型 ​

1. 全局读锁 (Global Read Lock) ​

锁定整个实例,允许读取但阻止写入:

sql
-- MySQL
FLUSH TABLES WITH READ LOCK;
-- 所有表变为只读状态

-- 执行备份操作
mysqldump --all-databases > backup.sql

-- 解锁
UNLOCK TABLES;

特点:

  • 所有会话可以读取数据
  • 所有会话不能修改数据
  • DDL 操作被阻塞
  • 适用于一致性备份

2. 实例独占锁 (Exclusive Instance Lock) ​

完全独占访问,阻止所有其他操作:

sql
-- Oracle 启动到 RESTRICT 模式
STARTUP RESTRICT;
-- 只有 DBA 可以连接

-- 执行维护操作
ALTER DATABASE ...;

-- 恢复正常
ALTER SYSTEM DISABLE RESTRICTED SESSION;

特点:

  • 只有特权用户可以连接
  • 完全控制数据库
  • 适用于重大维护操作

💻 各数据库实现 ​

MySQL / MariaDB ​

FLUSH TABLES WITH READ LOCK (FTWRL) ​

这是 MySQL 中最常用的全局锁命令:

sql
-- 获取全局读锁
FLUSH TABLES WITH READ LOCK;

-- 检查锁状态
SHOW PROCESSLIST;
-- 可以看到持有锁的连接

-- 查看全局锁状态
SHOW STATUS LIKE 'Table_locks%';

-- 释放锁(会话结束也会自动释放)
UNLOCK TABLES;

工作原理:

  1. 关闭所有打开的表
  2. 对所有表加全局读锁
  3. 阻止所有写操作
  4. 阻止 DDL 操作

使用场景:

bash
#!/bin/bash
# 物理备份脚本

# 1. 获取全局锁
mysql -e "FLUSH TABLES WITH READ LOCK;"

# 2. 记录 binlog 位置
mysql -e "SHOW MASTER STATUS;" > master_status.txt

# 3. 执行文件系统备份
tar czf /backup/mysql_data.tar.gz /var/lib/mysql/

# 4. 释放锁
mysql -e "UNLOCK TABLES;"

注意事项 ​

⚠️ 重要警告:

  1. 锁不会自动释放: 如果会话异常退出,锁可能仍然持有
  2. 阻塞所有写: 包括 INSERT、UPDATE、DELETE、DDL
  3. 从库也会锁定: 在主从复制中,从库应用 binlog 也会被阻塞
  4. 超时设置无效: innodb_lock_wait_timeout 对 FTWRL 无效

排查锁问题:

sql
-- 查看谁持有全局锁
SELECT 
    id,
    user,
    host,
    db,
    command,
    time,
    state,
    info
FROM information_schema.processlist
WHERE command = 'Sleep' 
  AND time > 10;

-- 查看是否有长时间运行的查询
SHOW FULL PROCESSLIST;

-- 强制终止会话
KILL <process_id>;

PostgreSQL ​

PostgreSQL 没有真正的全局锁命令,但可以通过其他方式实现类似效果。

方案 1: pg_terminate_backend ​

sql
-- 终止所有普通用户连接
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE usename != 'postgres'
  AND pid != pg_backend_pid();

-- 执行维护操作
VACUUM FULL;

-- 之后允许重新连接

方案 2: 超级用户独占模式 ​

bash
# 修改 postgresql.conf
superuser_reserved_connections = 3

# 重启 PostgreSQL
pg_ctl restart

方案 3: 表空间级别锁定 ​

sql
-- 锁定所有关键表
BEGIN;
LOCK TABLE table1, table2, table3 IN ACCESS EXCLUSIVE MODE;

-- 执行操作
CREATE INDEX ...;

COMMIT;

查看全局活动 ​

sql
-- 查看所有连接
SELECT 
    pid,
    usename,
    application_name,
    client_addr,
    backend_start,
    query_start,
    state,
    query
FROM pg_stat_activity
ORDER BY query_start;

-- 查看长时间运行的事务
SELECT 
    pid,
    now() - xact_start AS duration,
    query,
    state
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;

SQL Server ​

SQL Server 提供多种实例级别的锁定机制。

单用户模式 ​

sql
-- 切换到单用户模式
USE master;
GO
ALTER DATABASE YourDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO

-- 执行维护操作
DBCC CHECKDB('YourDB');
GO

-- 恢复多用户模式
ALTER DATABASE YourDB SET MULTI_USER;
GO

限制访问模式 ​

sql
-- 只允许 db_owner 角色成员访问
ALTER DATABASE YourDB SET RESTRICTED_USER;

-- 执行操作
-- ...

-- 恢复正常
ALTER DATABASE YourDB SET MULTI_USER;

脱机/联机 ​

sql
-- 数据库脱机
ALTER DATABASE YourDB SET OFFLINE WITH ROLLBACK IMMEDIATE;

-- 执行文件系统操作
-- ...

-- 数据库联机
ALTER DATABASE YourDB SET ONLINE;

查看实例级别锁 ​

sql
-- 查看数据库状态
SELECT 
    name,
    state_desc,
    user_access_desc,
    is_read_only
FROM sys.databases;

-- 查看当前连接
EXEC sp_who2;

-- 终止连接
KILL <spid>;

Oracle ​

Oracle 提供强大的实例级别控制。

RESTRICTED SESSION ​

sql
-- 启用受限会话模式
ALTER SYSTEM ENABLE RESTRICTED SESSION;

-- 只有具有 RESTRICTED SESSION 权限的用户可以连接
-- 通常只有 DBA

-- 执行维护操作
-- ...

-- 禁用受限会话
ALTER SYSTEM DISABLE RESTRICTED SESSION;

QUIESCE RESTRICTED ​

sql
-- 静默数据库(非 DBA 会话被挂起)
ALTER SYSTEM QUIESCE RESTRICTED;

-- 执行操作
-- ...

-- 解除静默
ALTER SYSTEM UNQUIESCE;

启动阶段锁定 ​

sql
-- 启动到 NOMOUNT 阶段
STARTUP NOMOUNT;

-- 启动到 MOUNT 阶段
STARTUP MOUNT;

-- 正常打开
ALTER DATABASE OPEN;

-- 只读模式打开
ALTER DATABASE OPEN READ ONLY;

查看实例状态 ​

sql
-- 查看实例状态
SELECT 
    instance_name,
    status,
    database_status,
    active_state,
    restricted_session
FROM v$instance;

-- 查看会话
SELECT 
    sid,
    serial#,
    username,
    status,
    program
FROM v$session
WHERE type = 'USER';

📊 性能影响分析 ​

影响范围 ​

锁类型影响范围持续时间风险等级
全局读锁所有写操作秒-分钟⚠️ 中
单用户模式所有非管理员分钟-小时⚠️⚠️ 高
RESTRICTED所有普通用户分钟-小时⚠️⚠️ 高
实例停机所有用户分钟-小时⚠️⚠️⚠️ 极高

业务影响评估 ​

场景影响程度建议时间窗口
全局读锁写操作完全阻塞业务低峰期
单用户模式应用完全不可用维护窗口
实例重启服务完全中断计划停机
静默模式新请求挂起业务低峰期

🎯 最佳实践 ​

✅ 推荐做法 ​

1. 选择合适的时间窗口 ​

bash
# 在业务低峰期执行
# 例如:凌晨 2:00-4:00

# 提前通知相关人员
# 准备回滚方案

2. 缩短锁持有时间 ​

sql
-- ❌ 不推荐:长时间持有全局锁
FLUSH TABLES WITH READ LOCK;
-- 执行大量无关操作
SLEEP(300);
UNLOCK TABLES;

-- ✅ 推荐:快速完成必要操作
FLUSH TABLES WITH READ LOCK;
-- 立即执行备份
SYSTEM mysqldump --all-databases > backup.sql;
UNLOCK TABLES;

3. 监控和告警 ​

sql
-- 设置监控
-- 当全局锁持有超过 10 秒时告警
SELECT 
    id,
    user,
    time,
    state
FROM information_schema.processlist
WHERE command = 'Sleep' 
  AND time > 10
  AND info LIKE '%FLUSH TABLES%';

4. 自动化脚本 ​

python
#!/usr/bin/env python3
import subprocess
import time
import logging

def backup_with_global_lock():
    """带全局锁的备份"""
    try:
        # 获取锁
        subprocess.run(["mysql", "-e", "FLUSH TABLES WITH READ LOCK;"], check=True)
        logging.info("全局锁已获取")
        
        # 执行备份(带超时)
        result = subprocess.run([
            "mysqldump", "--all-databases"
        ], capture_output=True, timeout=300)
        
        if result.returncode == 0:
            with open("/backup/backup.sql", "w") as f:
                f.write(result.stdout.decode())
            logging.info("备份成功")
        else:
            raise Exception("备份失败")
            
    except Exception as e:
        logging.error(f"备份失败: {e}")
        raise
    finally:
        # 确保释放锁
        subprocess.run(["mysql", "-e", "UNLOCK TABLES;"], check=True)
        logging.info("全局锁已释放")

if __name__ == "__main__":
    backup_with_global_lock()

❌ 避免陷阱 ​

1. 避免忘记解锁 ​

sql
-- ❌ 危险:异常时未解锁
FLUSH TABLES WITH READ LOCK;
-- 如果这里出错
DUMP DATABASE;  -- 假设命令失败
-- UNLOCK TABLES;  -- 不会执行,锁一直持有

-- ✅ 使用事务或确保清理
FLUSH TABLES WITH READ LOCK;
DO 
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        UNLOCK TABLES;
        RESIGNAL;
    END;
    
    -- 执行操作
    -- ...
    
    UNLOCK TABLES;
END;

2. 避免在生产环境随意使用 ​

sql
-- ❌ 禁止:生产环境测试
FLUSH TABLES WITH READ LOCK;
-- 导致线上服务不可写

-- ✅ 在测试环境验证
-- 在预发布环境演练
-- 制定详细的执行计划

3. 避免忽略从库影响 ​

sql
-- 主从架构中要注意
-- 主库执行 FTWRL 会阻塞从库同步

-- 解决方案:先在从库执行
STOP SLAVE;
-- 然后在主库备份
-- 最后启动从库
START SLAVE;

4. 避免长时间持有锁 ​

sql
-- ❌ 不推荐
FLUSH TABLES WITH READ LOCK;
SELECT SLEEP(600);  -- 锁定10分钟
UNLOCK TABLES;

-- ✅ 推荐:控制在秒级
FLUSH TABLES WITH READ LOCK;
-- 快速备份(< 30秒)
UNLOCK TABLES;

🔍 监控与诊断 ​

MySQL 监控 ​

sql
-- 查看全局锁状态
SHOW STATUS LIKE 'Table_locks_waited';

-- 查看进程列表
SHOW FULL PROCESSLIST;

-- 查看 InnoDB 状态
SHOW ENGINE INNODB STATUS\G

-- 检查是否有锁等待
SELECT * FROM performance_schema.metadata_locks 
WHERE lock_status = 'PENDING';

PostgreSQL 监控 ​

sql
-- 查看所有活动
SELECT 
    pid,
    usename,
    datname,
    state,
    query,
    now() - query_start AS duration
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;

-- 查看连接数
SELECT count(*) FROM pg_stat_activity;

SQL Server 监控 ​

sql
-- 查看数据库用户访问
SELECT 
    DB_NAME(database_id) AS database_name,
    COUNT(*) AS connection_count
FROM sys.dm_exec_sessions
GROUP BY database_id;

-- 查看当前活动
EXEC sp_who2;

Oracle 监控 ​

sql
-- 查看实例状态
SELECT 
    instance_name,
    status,
    logins,
    startup_time
FROM v$instance;

-- 查看会话
SELECT 
    count(*) AS total_sessions,
    sum(CASE WHEN status = 'ACTIVE' THEN 1 ELSE 0 END) AS active_sessions
FROM v$session;

🚨 常见问题 ​

Q1: 如何紧急解除全局锁? ​

答:

sql
-- MySQL: 找到持有锁的会话并终止
SHOW FULL PROCESSLIST;
KILL <process_id>;

-- 如果找不到,重启 MySQL 服务(最后手段)
sudo systemctl restart mysql

Q2: 全局锁会影响从库吗? ​

答:

  • MySQL: 会影响。主库的全局锁会阻塞 binlog 写入,从库同步会延迟
  • PostgreSQL: 取决于复制方式
  • SQL Server: AlwaysOn 可用性组不受影响
  • Oracle: Data Guard 备库不受影响

Q3: 备份时必须使用全局锁吗? ​

答: 不一定。

需要全局锁的场景:

  • 物理备份(文件系统级别)
  • MyISAM 表备份

不需要全局锁的场景:

  • InnoDB 热备份(使用 --single-transaction)
  • 逻辑备份(mysqldump)
  • 使用快照技术
bash
# InnoDB 热备份,无需全局锁
mysqldump --single-transaction --all-databases > backup.sql

Q4: 如何最小化全局锁的影响? ​

答:

  1. 使用热备份工具: Percona XtraBackup, MySQL Enterprise Backup
  2. 读写分离: 在从库执行备份
  3. 快照技术: LVM 快照、云存储快照
  4. 分批处理: 逐库备份而非全实例
  5. 业务低峰期: 选择影响最小的时间窗口

📚 相关资源 ​

内部链接 ​

外部资源 ​


最后更新: 2026-04-12
维护状态: ✅ 完整

Released under MIT License.