【Oracle】锁

news2025/7/28 0:53:09

在这里插入图片描述

个人主页:Guiat
归属专栏:Oracle

在这里插入图片描述

文章目录

  • 1. 锁基础概述
    • 1.1 锁的概念与作用
    • 1.2 锁的工作原理
    • 1.3 Oracle锁的分类
  • 2. 行级锁 (Row-Level Locks)
    • 2.1 行级锁的基本概念
      • 2.1.1 TX锁(事务锁)
      • 2.1.2 行级锁的工作机制
    • 2.2 行级锁的类型
      • 2.2.1 共享行级锁 (SELECT FOR UPDATE)
      • 2.2.2 排他行级锁 (UPDATE/DELETE)
    • 2.3 行级锁的高级特性
      • 2.3.1 SELECT FOR UPDATE的选项
      • 2.3.2 选择性列锁定
  • 3. 表级锁 (Table-Level Locks)
    • 3.1 表级锁的类型
      • 3.1.1 表锁模式
      • 3.1.2 锁兼容性矩阵
    • 3.2 DDL锁
      • 3.2.1 DDL锁的影响
      • 3.2.2 在线DDL操作
  • 4. 死锁检测与处理
    • 4.1 死锁的产生
      • 4.1.1 典型死锁场景
      • 4.1.2 死锁检测机制
    • 4.2 死锁的解决
      • 4.2.1 Oracle自动死锁检测
      • 4.2.2 预防死锁的策略
  • 5. 锁监控与管理
    • 5.1 锁信息查询
      • 5.1.1 当前锁状态查询
      • 5.1.2 锁等待情况分析
    • 5.2 锁等待诊断
      • 5.2.1 创建锁监控工具
      • 5.2.2 自动化锁监控
  • 6. 锁优化策略
    • 6.1 减少锁争用
      • 6.1.1 优化事务设计
      • 6.1.2 使用适当的隔离级别
    • 6.2 锁性能调优
      • 6.2.1 索引优化减少锁争用
      • 6.2.2 分区表减少锁争用
  • 7. 特殊锁类型
    • 7.1 用户定义锁
      • 7.1.1 DBMS_LOCK包的使用
      • 7.1.2 应用级锁控制
  • 8. 锁的最佳实践
    • 8.1 设计原则
      • 8.1.1 最小化锁持有时间
      • 8.1.2 合理的锁粒度选择
    • 8.2 性能优化技巧
      • 8.2.1 减少锁升级
      • 8.2.2 并发控制优化
    • 8.3 监控和维护
      • 8.3.1 定期锁健康检查
      • 8.3.2 清理和维护脚本

正文

1. 锁基础概述

锁是Oracle数据库中用于控制并发访问数据的重要机制,确保数据的一致性和完整性。Oracle使用多种类型的锁来管理不同级别的资源访问。

1.1 锁的概念与作用

Oracle锁机制
并发控制
数据一致性
事务隔离
资源保护
多用户同时访问
防止数据冲突
ACID特性保证
读写一致性
事务隔离级别
幻读防护
表级保护
行级保护
系统资源保护

1.2 锁的工作原理

无锁
有兼容锁
有冲突锁
事务请求资源
检查锁状态
获得锁
等待队列
执行操作
锁释放?
继续等待
事务结束
释放锁
通知等待事务

1.3 Oracle锁的分类

Oracle锁类型
按锁定对象分类
按锁定模式分类
按锁定范围分类
按锁定时间分类
行级锁 Row Lock
表级锁 Table Lock
页级锁 Page Lock
系统锁 System Lock
共享锁 Shared
排他锁 Exclusive
意图锁 Intent
更新锁 Update
数据库级
表空间级
表级
行级
事务锁
语句锁
临时锁

2. 行级锁 (Row-Level Locks)

行级锁是Oracle中最常用的锁类型,提供最高的并发性。

2.1 行级锁的基本概念

2.1.1 TX锁(事务锁)

-- 演示行级锁的基本操作
-- Session 1
BEGIN
  UPDATE employees 
  SET salary = salary + 1000 
  WHERE employee_id = 100;
  -- 此时employee_id=100的行被锁定
  -- 不要提交,保持锁定状态
END;
/

-- 查看当前锁定信息
SELECT 
    s.sid,
    s.serial#,
    s.username,
    s.program,
    l.type,
    l.mode_held,
    l.mode_requested,
    o.object_name,
    l.block
FROM v$lock l
JOIN v$session s ON l.sid = s.sid
JOIN dba_objects o ON l.id1 = o.object_id
WHERE s.username IS NOT NULL
ORDER BY s.sid;

2.1.2 行级锁的工作机制

-- 创建测试表来演示锁机制
CREATE TABLE lock_demo (
    id NUMBER PRIMARY KEY,
    name VARCHAR2(50),
    amount NUMBER,
    last_updated DATE DEFAULT SYSDATE
);

-- 插入测试数据
INSERT INTO lock_demo VALUES (1, 'Record 1', 1000, SYSDATE);
INSERT INTO lock_demo VALUES (2, 'Record 2', 2000, SYSDATE);
INSERT INTO lock_demo VALUES (3, 'Record 3', 3000, SYSDATE);
COMMIT;

-- Session 1: 锁定第一行
UPDATE lock_demo SET amount = 1500 WHERE id = 1;
-- 不提交,保持锁定

-- Session 2: 可以访问其他行
SELECT * FROM lock_demo WHERE id = 2; -- 成功
UPDATE lock_demo SET amount = 2500 WHERE id = 2; -- 成功

-- Session 2: 尝试访问被锁定的行
UPDATE lock_demo SET amount = 1200 WHERE id = 1; -- 等待

2.2 行级锁的类型

2.2.1 共享行级锁 (SELECT FOR UPDATE)

-- 使用SELECT FOR UPDATE获取共享行级锁
DECLARE
  CURSOR emp_cursor IS
    SELECT employee_id, first_name, last_name, salary
    FROM employees
    WHERE department_id = 20
    FOR UPDATE;
    
  emp_rec emp_cursor%ROWTYPE;
  
BEGIN
  DBMS_OUTPUT.PUT_LINE('=== 锁定部门20的所有员工 ===');
  
  OPEN emp_cursor;
  
  LOOP
    FETCH emp_cursor INTO emp_rec;
    EXIT WHEN emp_cursor%NOTFOUND;
    
    DBMS_OUTPUT.PUT_LINE('锁定员工: ' || emp_rec.employee_id || 
                        ' - ' || emp_rec.first_name || ' ' || emp_rec.last_name);
    
    -- 在这里可以进行其他业务逻辑
    -- 所有相关行都被锁定,防止其他会话修改
    
  END LOOP;
  
  CLOSE emp_cursor;
  
  -- 可以选择提交或回滚
  COMMIT; -- 释放所有锁
  
END;
/

2.2.2 排他行级锁 (UPDATE/DELETE)

-- 演示排他行级锁
BEGIN
  -- 更新操作自动获取排他锁
  UPDATE employees 
  SET salary = salary * 1.1 
  WHERE department_id = 10;
  
  DBMS_OUTPUT.PUT_LINE('更新了 ' || SQL%ROWCOUNT || ' 行');
  
  -- 删除操作也获取排他锁
  DELETE FROM lock_demo WHERE amount < 1000;
  
  DBMS_OUTPUT.PUT_LINE('删除了 ' || SQL%ROWCOUNT || ' 行');
  
  -- 提交释放锁
  COMMIT;
  
END;
/

2.3 行级锁的高级特性

2.3.1 SELECT FOR UPDATE的选项

-- NOWAIT选项:立即返回而不等待
BEGIN
  -- 尝试锁定,如果不能立即获得锁就返回错误
  SELECT employee_id, salary
  INTO :emp_id, :emp_salary
  FROM employees
  WHERE employee_id = 100
  FOR UPDATE NOWAIT;
  
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -54 THEN -- ORA-00054: resource busy
      DBMS_OUTPUT.PUT_LINE('资源正忙,无法获得锁');
    ELSE
      DBMS_OUTPUT.PUT_LINE('其他错误: ' || SQLERRM);
    END IF;
END;
/

-- WAIT选项:指定等待时间
DECLARE
  v_emp_id NUMBER;
  v_salary NUMBER;
BEGIN
  SELECT employee_id, salary
  INTO v_emp_id, v_salary
  FROM employees
  WHERE employee_id = 100
  FOR UPDATE WAIT 5; -- 等待5秒
  
  DBMS_OUTPUT.PUT_LINE('成功获得锁,员工ID: ' || v_emp_id);
  
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -30006 THEN -- ORA-30006: resource busy; acquire with WAIT timeout expired
      DBMS_OUTPUT.PUT_LINE('等待超时,无法获得锁');
    ELSE
      DBMS_OUTPUT.PUT_LINE('错误: ' || SQLERRM);
    END IF;
END;
/

2.3.2 选择性列锁定

-- 只锁定特定列
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE department_id = 30
FOR UPDATE OF salary;

-- 跳过已锁定的行
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE department_id = 40
FOR UPDATE SKIP LOCKED;

3. 表级锁 (Table-Level Locks)

表级锁控制对整个表的访问,通常在DDL操作或需要表级控制时使用。

3.1 表级锁的类型

3.1.1 表锁模式

-- 手动获取表级锁
-- 共享锁模式 (Share Mode)
LOCK TABLE employees IN SHARE MODE;

-- 排他锁模式 (Exclusive Mode)
LOCK TABLE lock_demo IN EXCLUSIVE MODE;

-- 行共享锁模式 (Row Share Mode)
LOCK TABLE employees IN ROW SHARE MODE;

-- 行排他锁模式 (Row Exclusive Mode)
LOCK TABLE employees IN ROW EXCLUSIVE MODE;

-- 共享行排他锁模式 (Share Row Exclusive Mode)
LOCK TABLE employees IN SHARE ROW EXCLUSIVE MODE;

3.1.2 锁兼容性矩阵

表锁兼容性
RS - Row Share
RX - Row Exclusive
S - Share
SRX - Share Row Exclusive
X - Exclusive
与RS/RX/S兼容
仅与RS/RX兼容
与RS/S兼容
仅与RS兼容
与任何模式不兼容

3.2 DDL锁

DDL操作会自动获取表级排他锁。

3.2.1 DDL锁的影响

-- DDL操作示例
-- 创建表时的锁定
CREATE TABLE ddl_lock_demo (
    id NUMBER,
    description VARCHAR2(100),
    created_date DATE DEFAULT SYSDATE
);

-- 修改表结构时的锁定
ALTER TABLE ddl_lock_demo ADD (
    status VARCHAR2(20) DEFAULT 'ACTIVE'
);

-- 创建索引时的锁定(在线DDL)
CREATE INDEX CONCURRENTLY idx_ddl_demo_status 
ON ddl_lock_demo(status) ONLINE;

-- 删除表时的锁定
DROP TABLE ddl_lock_demo;

3.2.2 在线DDL操作

-- 在线添加列(不锁定表)
ALTER TABLE employees ADD (
    bonus_pct NUMBER(3,2) DEFAULT 0.00
) ONLINE;

-- 在线创建索引
CREATE INDEX idx_emp_bonus 
ON employees(bonus_pct) 
ONLINE;

-- 在线重建索引
ALTER INDEX idx_emp_bonus REBUILD ONLINE;

4. 死锁检测与处理

死锁是两个或多个事务相互等待对方释放锁的情况。

4.1 死锁的产生

4.1.1 典型死锁场景

-- 创建死锁演示表
CREATE TABLE deadlock_demo1 (id NUMBER PRIMARY KEY, value VARCHAR2(50));
CREATE TABLE deadlock_demo2 (id NUMBER PRIMARY KEY, value VARCHAR2(50));

INSERT INTO deadlock_demo1 VALUES (1, 'Value A');
INSERT INTO deadlock_demo2 VALUES (1, 'Value B');
COMMIT;

-- Session 1: 
BEGIN
  UPDATE deadlock_demo1 SET value = 'Updated A' WHERE id = 1;
  -- 等待几秒,然后执行下面的语句
  DBMS_LOCK.SLEEP(5);
  UPDATE deadlock_demo2 SET value = 'Updated B from Session 1' WHERE id = 1;
  COMMIT;
END;
/

-- Session 2: (同时在另一个会话中执行)
BEGIN
  UPDATE deadlock_demo2 SET value = 'Updated B' WHERE id = 1;
  -- 等待几秒,然后执行下面的语句
  DBMS_LOCK.SLEEP(5);
  UPDATE deadlock_demo1 SET value = 'Updated A from Session 2' WHERE id = 1;
  COMMIT;
END;
/

4.1.2 死锁检测机制

-- 查看死锁信息的存储过程
CREATE OR REPLACE PROCEDURE check_deadlocks
AS
BEGIN
  -- 检查当前的锁等待情况
  FOR rec IN (
    SELECT 
      waiting.sid AS waiting_session,
      waiting.username AS waiting_user,
      waiting.machine AS waiting_machine,
      holding.sid AS holding_session,
      holding.username AS holding_user,
      holding.machine AS holding_machine,
      l.type AS lock_type,
      o.object_name
    FROM v$lock l1
    JOIN v$lock l2 ON l1.id1 = l2.id1 AND l1.id2 = l2.id2
    JOIN v$session waiting ON l1.sid = waiting.sid
    JOIN v$session holding ON l2.sid = holding.sid
    JOIN dba_objects o ON l1.id1 = o.object_id
    WHERE l1.block = 0 AND l2.block = 1
      AND l1.type = l2.type
  ) LOOP
    DBMS_OUTPUT.PUT_LINE('等待会话: ' || rec.waiting_session || 
                        ' 用户: ' || rec.waiting_user ||
                        ' 被会话: ' || rec.holding_session ||
                        ' 用户: ' || rec.holding_user || ' 阻塞');
    DBMS_OUTPUT.PUT_LINE('锁类型: ' || rec.lock_type || 
                        ' 对象: ' || rec.object_name);
    DBMS_OUTPUT.PUT_LINE('---');
  END LOOP;
END;
/

-- 执行死锁检查
EXEC check_deadlocks;

4.2 死锁的解决

4.2.1 Oracle自动死锁检测

Oracle会自动检测死锁并回滚其中一个事务。

-- 监控死锁事件
SELECT 
    name,
    value
FROM v$sysstat
WHERE name LIKE '%deadlock%';

-- 查看死锁的详细信息
SELECT 
    to_char(timestamp, 'YYYY-MM-DD HH24:MI:SS') AS deadlock_time,
    substr(message_text, 1, 100) AS message
FROM dba_hist_active_sess_history
WHERE event = 'enq: TX - row lock contention'
ORDER BY timestamp DESC;

4.2.2 预防死锁的策略

-- 策略1: 按固定顺序访问资源
CREATE OR REPLACE PROCEDURE safe_update_procedure(
  p_id1 NUMBER,
  p_id2 NUMBER,
  p_value1 VARCHAR2,
  p_value2 VARCHAR2
) AS
  v_first_id NUMBER;
  v_second_id NUMBER;
  v_first_value VARCHAR2(50);
  v_second_value VARCHAR2(50);
BEGIN
  -- 确定访问顺序(按ID升序)
  IF p_id1 < p_id2 THEN
    v_first_id := p_id1;
    v_second_id := p_id2;
    v_first_value := p_value1;
    v_second_value := p_value2;
  ELSE
    v_first_id := p_id2;
    v_second_id := p_id1;
    v_first_value := p_value2;
    v_second_value := p_value1;
  END IF;
  
  -- 按固定顺序更新
  UPDATE deadlock_demo1 SET value = v_first_value WHERE id = v_first_id;
  UPDATE deadlock_demo2 SET value = v_second_value WHERE id = v_second_id;
  
  COMMIT;
END;
/

-- 策略2: 使用超时机制
CREATE OR REPLACE PROCEDURE timeout_update_procedure AS
BEGIN
  -- 设置语句超时
  EXECUTE IMMEDIATE 'ALTER SESSION SET ddl_lock_timeout = 10';
  
  UPDATE deadlock_demo1 SET value = 'Safe Update' WHERE id = 1;
  UPDATE deadlock_demo2 SET value = 'Safe Update' WHERE id = 1;
  
  COMMIT;
  
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    IF SQLCODE = -30006 THEN
      DBMS_OUTPUT.PUT_LINE('操作超时,避免了潜在的死锁');
    ELSE
      RAISE;
    END IF;
END;
/

5. 锁监控与管理

5.1 锁信息查询

5.1.1 当前锁状态查询

-- 查看当前所有锁信息
SELECT 
    s.sid,
    s.serial#,
    s.username,
    s.status,
    s.machine,
    s.program,
    l.type AS lock_type,
    DECODE(l.type,
           'TM', 'Table Lock',
           'TX', 'Transaction Lock',
           'UL', 'User Lock',
           l.type) AS lock_description,
    DECODE(l.lmode,
           0, 'None',
           1, 'Null',
           2, 'Row Share',
           3, 'Row Exclusive',
           4, 'Share',
           5, 'Share Row Exclusive',
           6, 'Exclusive',
           l.lmode) AS lock_mode,
    o.owner,
    o.object_name,
    o.object_type
FROM v$lock l
JOIN v$session s ON l.sid = s.sid
LEFT JOIN dba_objects o ON l.id1 = o.object_id
WHERE s.username IS NOT NULL
ORDER BY s.sid, l.type;

5.1.2 锁等待情况分析

-- 创建锁等待分析视图
CREATE OR REPLACE VIEW lock_wait_analysis AS
SELECT 
    waiter.sid AS waiting_session,
    waiter.username AS waiting_user,
    waiter.status AS waiting_status,
    waiter.machine AS waiting_machine,
    waiter.program AS waiting_program,
    holder.sid AS holding_session,
    holder.username AS holding_user,
    holder.status AS holding_status,
    holder.machine AS holding_machine,
    holder.program AS holding_program,
    l1.type AS lock_type,
    DECODE(l1.request,
           0, 'None',
           1, 'Null',
           2, 'Row Share',
           3, 'Row Exclusive', 
           4, 'Share',
           5, 'Share Row Exclusive',
           6, 'Exclusive',
           l1.request) AS requested_mode,
    DECODE(l2.lmode,
           0, 'None',
           1, 'Null',
           2, 'Row Share',
           3, 'Row Exclusive',
           4, 'Share', 
           5, 'Share Row Exclusive',
           6, 'Exclusive',
           l2.lmode) AS held_mode,
    o.owner,
    o.object_name,
    o.object_type
FROM v$lock l1
JOIN v$lock l2 ON l1.id1 = l2.id1 AND l1.id2 = l2.id2
JOIN v$session waiter ON l1.sid = waiter.sid
JOIN v$session holder ON l2.sid = holder.sid
LEFT JOIN dba_objects o ON l1.id1 = o.object_id
WHERE l1.block = 0 
  AND l2.block = 1
  AND l1.type = l2.type;

-- 查询当前锁等待情况
SELECT * FROM lock_wait_analysis;

5.2 锁等待诊断

5.2.1 创建锁监控工具

-- 创建锁监控存储过程
CREATE OR REPLACE PROCEDURE monitor_locks(
  p_show_details BOOLEAN DEFAULT TRUE
) AS
  v_count NUMBER := 0;
BEGIN
  DBMS_OUTPUT.PUT_LINE('=== Oracle锁监控报告 ===');
  DBMS_OUTPUT.PUT_LINE('时间: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
  DBMS_OUTPUT.PUT_LINE('');
  
  -- 统计锁数量
  SELECT COUNT(*) INTO v_count
  FROM v$lock l
  JOIN v$session s ON l.sid = s.sid
  WHERE s.username IS NOT NULL;
  
  DBMS_OUTPUT.PUT_LINE('当前活跃锁数量: ' || v_count);
  
  -- 检查锁等待
  SELECT COUNT(*) INTO v_count FROM lock_wait_analysis;
  
  IF v_count > 0 THEN
    DBMS_OUTPUT.PUT_LINE('*** 警告: 发现 ' || v_count || ' 个锁等待情况 ***');
    
    IF p_show_details THEN
      DBMS_OUTPUT.PUT_LINE('');
      DBMS_OUTPUT.PUT_LINE('锁等待详情:');
      DBMS_OUTPUT.PUT_LINE('----------------------------------------');
      
      FOR rec IN (SELECT * FROM lock_wait_analysis) LOOP
        DBMS_OUTPUT.PUT_LINE('等待者: SID=' || rec.waiting_session || 
                            ', 用户=' || rec.waiting_user ||
                            ', 机器=' || rec.waiting_machine);
        DBMS_OUTPUT.PUT_LINE('持有者: SID=' || rec.holding_session ||
                            ', 用户=' || rec.holding_user ||
                            ', 机器=' || rec.holding_machine);
        DBMS_OUTPUT.PUT_LINE('锁类型: ' || rec.lock_type ||
                            ', 对象: ' || rec.owner || '.' || rec.object_name);
        DBMS_OUTPUT.PUT_LINE('请求模式: ' || rec.requested_mode ||
                            ', 持有模式: ' || rec.held_mode);
        DBMS_OUTPUT.PUT_LINE('----------------------------------------');
      END LOOP;
    END IF;
  ELSE
    DBMS_OUTPUT.PUT_LINE('没有发现锁等待情况');
  END IF;
  
  -- 显示长时间运行的事务
  SELECT COUNT(*) INTO v_count
  FROM v$session s
  JOIN v$transaction t ON s.saddr = t.ses_addr
  WHERE (SYSDATE - t.start_date) * 24 * 60 > 30; -- 超过30分钟
  
  IF v_count > 0 THEN
    DBMS_OUTPUT.PUT_LINE('');
    DBMS_OUTPUT.PUT_LINE('*** 警告: 发现 ' || v_count || ' 个长时间运行的事务 ***');
  END IF;
  
END;
/

-- 执行锁监控
EXEC monitor_locks(TRUE);

5.2.2 自动化锁监控

-- 创建锁监控表
CREATE TABLE lock_monitor_log (
    log_id NUMBER PRIMARY KEY,
    log_time DATE DEFAULT SYSDATE,
    lock_count NUMBER,
    wait_count NUMBER,
    long_tx_count NUMBER,
    details CLOB
);

CREATE SEQUENCE lock_monitor_seq START WITH 1 INCREMENT BY 1;

-- 创建自动监控过程
CREATE OR REPLACE PROCEDURE auto_lock_monitor AS
  v_lock_count NUMBER;
  v_wait_count NUMBER;
  v_long_tx_count NUMBER;
  v_details CLOB;
BEGIN
  -- 统计锁信息
  SELECT COUNT(*) INTO v_lock_count
  FROM v$lock l JOIN v$session s ON l.sid = s.sid
  WHERE s.username IS NOT NULL;
  
  SELECT COUNT(*) INTO v_wait_count FROM lock_wait_analysis;
  
  SELECT COUNT(*) INTO v_long_tx_count
  FROM v$session s JOIN v$transaction t ON s.saddr = t.ses_addr
  WHERE (SYSDATE - t.start_date) * 24 * 60 > 30;
  
  -- 如果有异常情况,记录详细信息
  IF v_wait_count > 0 OR v_long_tx_count > 0 THEN
    v_details := 'Lock waits detected: ' || v_wait_count || CHR(10) ||
                 'Long transactions: ' || v_long_tx_count;
  END IF;
  
  -- 记录监控日志
  INSERT INTO lock_monitor_log (
    log_id, lock_count, wait_count, long_tx_count, details
  ) VALUES (
    lock_monitor_seq.NEXTVAL, v_lock_count, v_wait_count, v_long_tx_count, v_details
  );
  
  COMMIT;
  
  -- 如果有严重问题,发送告警
  IF v_wait_count > 5 THEN
    DBMS_OUTPUT.PUT_LINE('ALERT: High number of lock waits detected!');
  END IF;
  
END;
/

-- 设置定时任务(需要DBMS_SCHEDULER权限)
BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'LOCK_MONITOR_JOB',
    job_type        => 'PLSQL_BLOCK',
    job_action      => 'BEGIN auto_lock_monitor; END;',
    start_date      => SYSDATE,
    repeat_interval => 'FREQ=MINUTELY; INTERVAL=5', -- 每5分钟执行一次
    enabled         => TRUE
  );
END;
/

6. 锁优化策略

6.1 减少锁争用

6.1.1 优化事务设计

-- 不好的做法:长事务
CREATE OR REPLACE PROCEDURE bad_batch_update AS
BEGIN
  -- 开始一个长事务
  FOR rec IN (SELECT * FROM large_table) LOOP
    -- 复杂的业务逻辑
    UPDATE another_table SET status = 'PROCESSED' 
    WHERE id = rec.id;
    
    -- 更多的处理...
    DBMS_LOCK.SLEEP(1); -- 模拟耗时操作
  END LOOP;
  
  COMMIT; -- 长时间持有锁
END;
/

-- 好的做法:短事务批处理
CREATE OR REPLACE PROCEDURE good_batch_update AS
  CURSOR data_cursor IS SELECT * FROM large_table;
  TYPE data_array IS TABLE OF data_cursor%ROWTYPE;
  v_data data_array;
  v_batch_size CONSTANT NUMBER := 1000;
BEGIN
  OPEN data_cursor;
  
  LOOP
    FETCH data_cursor BULK COLLECT INTO v_data LIMIT v_batch_size;
    
    -- 处理一批数据
    FORALL i IN 1..v_data.COUNT
      UPDATE another_table 
      SET status = 'PROCESSED' 
      WHERE id = v_data(i).id;
    
    COMMIT; -- 及时提交,释放锁
    
    EXIT WHEN data_cursor%NOTFOUND;
  END LOOP;
  
  CLOSE data_cursor;
END;
/

6.1.2 使用适当的隔离级别

-- 设置事务隔离级别
-- READ COMMITTED(默认)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- SERIALIZABLE(更高的一致性,但可能增加锁争用)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- READ ONLY(只读事务,不获取写锁)
SET TRANSACTION READ ONLY;

-- 使用只读事务进行报表查询
BEGIN
  SET TRANSACTION READ ONLY;
  
  -- 执行复杂的报表查询
  SELECT d.department_name,
         COUNT(e.employee_id) AS emp_count,
         AVG(e.salary) AS avg_salary
  FROM departments d
  LEFT JOIN employees e ON d.department_id = e.department_id
  GROUP BY d.department_name
  ORDER BY avg_salary DESC;
  
  COMMIT;
END;
/

6.2 锁性能调优

6.2.1 索引优化减少锁争用

-- 创建适当的索引减少锁扫描范围
CREATE INDEX idx_emp_dept_status ON employees(department_id, status);

-- 使用索引友好的查询
UPDATE employees 
SET status = 'INACTIVE' 
WHERE department_id = 10 AND status = 'ACTIVE';

-- 避免全表扫描的锁定
-- 不好的做法
UPDATE employees SET last_login = SYSDATE; -- 锁定所有行

-- 好的做法
UPDATE employees 
SET last_login = SYSDATE 
WHERE employee_id IN (
  SELECT employee_id FROM recent_logins
); -- 只锁定必要的行

6.2.2 分区表减少锁争用

-- 创建分区表减少锁争用
CREATE TABLE partitioned_transactions (
    transaction_id NUMBER,
    transaction_date DATE,
    amount NUMBER,
    status VARCHAR2(20)
)
PARTITION BY RANGE (transaction_date) (
    PARTITION p_2023 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION p_2024 VALUES LESS THAN (DATE '2025-01-01'),
    PARTITION p_2025 VALUES LESS THAN (DATE '2026-01-01')
);

-- 并行处理不同分区,减少锁争用
-- Session 1: 处理2023年数据
UPDATE partitioned_transactions 
SET status = 'PROCESSED' 
WHERE transaction_date >= DATE '2023-01-01' 
  AND transaction_date < DATE '2024-01-01'
  AND status = 'PENDING';

-- Session 2: 同时处理2024年数据(不同分区,无锁争用)
UPDATE partitioned_transactions 
SET status = 'PROCESSED' 
WHERE transaction_date >= DATE '2024-01-01' 
  AND transaction_date < DATE '2025-01-01'
  AND status = 'PENDING';

7. 特殊锁类型

7.1 用户定义锁

7.1.1 DBMS_LOCK包的使用

-- 使用DBMS_LOCK创建用户定义锁
CREATE OR REPLACE PROCEDURE demo_user_lock AS
  v_lockhandle VARCHAR2(128);
  v_lock_result NUMBER;
BEGIN
  -- 分配锁句柄
  DBMS_LOCK.ALLOCATE_UNIQUE('MY_CUSTOM_LOCK', v_lockhandle);
  
  DBMS_OUTPUT.PUT_LINE('尝试获取用户锁...');
  
  -- 请求锁(排他模式,等待10秒)
  v_lock_result := DBMS_LOCK.REQUEST(
    lockhandle => v_lockhandle,
    lockmode   => DBMS_LOCK.X_MODE, -- 排他模式
    timeout    => 10,               -- 10秒超时
    release_on_commit => TRUE       -- 提交时释放
  );
  
  CASE v_lock_result
    WHEN 0 THEN 
      DBMS_OUTPUT.PUT_LINE('成功获得锁');
      
      -- 执行需要同步的操作
      DBMS_OUTPUT.PUT_LINE('执行关键业务逻辑...');
      DBMS_LOCK.SLEEP(5); -- 模拟处理时间
      
      -- 手动释放锁
      v_lock_result := DBMS_LOCK.RELEASE(v_lockhandle);
      DBMS_OUTPUT.PUT_LINE('锁已释放');
      
    WHEN 1 THEN
      DBMS_OUTPUT.PUT_LINE('获取锁超时');
    WHEN 2 THEN
      DBMS_OUTPUT.PUT_LINE('死锁检测到');
    WHEN 3 THEN
      DBMS_OUTPUT.PUT_LINE('参数错误');
    WHEN 4 THEN
      DBMS_OUTPUT.PUT_LINE('锁已被持有');
    ELSE
      DBMS_OUTPUT.PUT_LINE('未知错误: ' || v_lock_result);
  END CASE;
  
END;
/

-- 测试用户锁
EXEC demo_user_lock;

7.1.2 应用级锁控制

-- 创建应用级锁管理表
CREATE TABLE app_locks (
    lock_name VARCHAR2(100) PRIMARY KEY,
    session_id NUMBER,
    username VARCHAR2(30),
    acquired_time DATE,
    purpose VARCHAR2(200)
);

-- 创建应用锁管理包
CREATE OR REPLACE PACKAGE app_lock_mgr AS
  FUNCTION acquire_lock(
    p_lock_name VARCHAR2,
    p_purpose VARCHAR2 DEFAULT NULL,
    p_timeout NUMBER DEFAULT 30
  ) RETURN BOOLEAN;
  
  FUNCTION release_lock(p_lock_name VARCHAR2) RETURN BOOLEAN;
  
  PROCEDURE show_locks;
END app_lock_mgr;
/

CREATE OR REPLACE PACKAGE BODY app_lock_mgr AS
  
  FUNCTION acquire_lock(
    p_lock_name VARCHAR2,
    p_purpose VARCHAR2 DEFAULT NULL,
    p_timeout NUMBER DEFAULT 30
  ) RETURN BOOLEAN IS
    v_count NUMBER;
    v_session_id NUMBER;
    v_start_time DATE := SYSDATE;
  BEGIN
    SELECT SYS_CONTEXT('USERENV', 'SID') INTO v_session_id FROM DUAL;
    
    LOOP
      -- 尝试获取锁
      BEGIN
        INSERT INTO app_locks (
          lock_name, session_id, username, acquired_time, purpose
        ) VALUES (
          p_lock_name, v_session_id, USER, SYSDATE, p_purpose
        );
        
        COMMIT;
        RETURN TRUE; -- 成功获取锁
        
      EXCEPTION
        WHEN DUP_VAL_ON_INDEX THEN
          -- 锁已被其他会话持有
          IF (SYSDATE - v_start_time) * 24 * 60 * 60 > p_timeout THEN
            RETURN FALSE; -- 超时
          END IF;
          
          DBMS_LOCK.SLEEP(1); -- 等待1秒后重试
      END;
    END LOOP;
    
  END acquire_lock;
  
  FUNCTION release_lock(p_lock_name VARCHAR2) RETURN BOOLEAN IS
    v_session_id NUMBER;
    v_count NUMBER;
  BEGIN
    SELECT SYS_CONTEXT('USERENV', 'SID') INTO v_session_id FROM DUAL;
    
    DELETE FROM app_locks 
    WHERE lock_name = p_lock_name 
      AND session_id = v_session_id;
    
    v_count := SQL%ROWCOUNT;
    COMMIT;
    
    RETURN v_count > 0;
  END release_lock;
  
  PROCEDURE show_locks IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE('=== 应用级锁状态 ===');
    FOR rec IN (
      SELECT lock_name, session_id, username, 
             TO_CHAR(acquired_time, 'YYYY-MM-DD HH24:MI:SS') AS acquired,
             purpose
      FROM app_locks
      ORDER BY acquired_time
    ) LOOP
      DBMS_OUTPUT.PUT_LINE('锁名: ' || rec.lock_name ||
                          ', 会话: ' || rec.session_id ||
                          ', 用户: ' || rec.username ||
                          ', 获取时间: ' || rec.acquired);
      IF rec.purpose IS NOT NULL THEN
        DBMS_OUTPUT.PUT_LINE('  目的: ' || rec.purpose);
      END IF;
    END LOOP;
  END show_locks;
  
END app_lock_mgr;
/

-- 使用应用级锁
DECLARE
  v_result BOOLEAN;
BEGIN
  -- 尝试获取锁
  v_result := app_lock_mgr.acquire_lock('BATCH_PROCESS', '每日批处理作业');
  
  IF v_result THEN
    DBMS_OUTPUT.PUT_LINE('成功获取应用锁');
    
    -- 执行批处理逻辑
    DBMS_OUTPUT.PUT_LINE('执行批处理...');
    DBMS_LOCK.SLEEP(3);
    
    -- 释放锁
    v_result := app_lock_mgr.release_lock('BATCH_PROCESS');
    DBMS_OUTPUT.PUT_LINE('锁已释放');
  ELSE
    DBMS_OUTPUT.PUT_LINE('无法获取应用锁');
  END IF;
END;
/

-- 查看当前锁状态
EXEC app_lock_mgr.show_locks;

8. 锁的最佳实践

8.1 设计原则

8.1.1 最小化锁持有时间

-- 原则1: 尽快提交事务
CREATE OR REPLACE PROCEDURE process_orders AS
BEGIN
  FOR order_rec IN (
    SELECT order_id, customer_id, total_amount 
    FROM orders 
    WHERE status = 'PENDING'
  ) LOOP
    -- 处理单个订单
    UPDATE orders 
    SET status = 'PROCESSING', 
        last_updated = SYSDATE 
    WHERE order_id = order_rec.order_id;
    
    -- 立即提交,不要等待整个循环结束
    COMMIT;
    
    -- 非数据库操作(如调用外部服务)放在事务外
    -- call_external_service(order_rec.order_id);
  END LOOP;
END;
/

-- 原则2: 避免用户交互期间持有锁
CREATE OR REPLACE PROCEDURE safe_interactive_update(
  p_emp_id NUMBER,
  p_new_salary NUMBER
) AS
  v_current_salary NUMBER;
  v_emp_name VARCHAR2(100);
BEGIN
  -- 首先获取数据(不锁定)
  SELECT salary, first_name || ' ' || last_name
  INTO v_current_salary, v_emp_name
  FROM employees
  WHERE employee_id = p_emp_id;
  
  -- 显示信息给用户确认(这里不持有锁)
  DBMS_OUTPUT.PUT_LINE('员工: ' || v_emp_name);
  DBMS_OUTPUT.PUT_LINE('当前工资: ' || v_current_salary);
  DBMS_OUTPUT.PUT_LINE('新工资: ' || p_new_salary);
  
  -- 只在实际更新时才获取锁
  UPDATE employees 
  SET salary = p_new_salary,
      last_updated = SYSDATE
  WHERE employee_id = p_emp_id;
  
  COMMIT; -- 立即提交
END;
/

8.1.2 合理的锁粒度选择

-- 选择合适的锁粒度
-- 场景1: 批量更新少量记录 - 使用行级锁
UPDATE employees 
SET salary = salary * 1.1 
WHERE department_id = 10; -- 只影响几行

-- 场景2: 大量更新操作 - 考虑表级锁
LOCK TABLE temp_import_data IN EXCLUSIVE MODE;
-- 执行大量数据导入
INSERT /*+ APPEND */ INTO main_table SELECT * FROM temp_import_data;
COMMIT;

-- 场景3: 复杂报表查询 - 使用读锁或快照
SET TRANSACTION READ ONLY;
-- 执行复杂查询,确保数据一致性
SELECT /* 复杂报表查询 */ * FROM multiple_tables;
COMMIT;

8.2 性能优化技巧

8.2.1 减少锁升级

-- 避免锁升级的技巧
-- 1. 使用绑定变量
CREATE OR REPLACE PROCEDURE update_salaries_efficient(
  p_dept_id NUMBER,
  p_increase_pct NUMBER
) AS
BEGIN
  -- 使用绑定变量,减少SQL解析开销
  UPDATE employees 
  SET salary = salary * (1 + p_increase_pct / 100)
  WHERE department_id = p_dept_id;
  
  COMMIT;
END;
/

-- 2. 批量操作
CREATE OR REPLACE PROCEDURE bulk_salary_update AS
  TYPE emp_id_array IS TABLE OF employees.employee_id%TYPE;
  TYPE salary_array IS TABLE OF employees.salary%TYPE;
  
  v_emp_ids emp_id_array;
  v_new_salaries salary_array;
  
BEGIN
  -- 批量获取数据
  SELECT employee_id, salary * 1.1
  BULK COLLECT INTO v_emp_ids, v_new_salaries
  FROM employees
  WHERE department_id = 20;
  
  -- 批量更新
  FORALL i IN 1..v_emp_ids.COUNT
    UPDATE employees 
    SET salary = v_new_salaries(i)
    WHERE employee_id = v_emp_ids(i);
    
  COMMIT;
END;
/

8.2.2 并发控制优化

-- 乐观锁实现
ALTER TABLE employees ADD version_number NUMBER DEFAULT 1;

CREATE OR REPLACE PROCEDURE optimistic_update_salary(
  p_emp_id NUMBER,
  p_new_salary NUMBER,
  p_version NUMBER
) AS
  v_rows_updated NUMBER;
BEGIN
  UPDATE employees 
  SET salary = p_new_salary,
      version_number = version_number + 1,
      last_updated = SYSDATE
  WHERE employee_id = p_emp_id 
    AND version_number = p_version;
    
  v_rows_updated := SQL%ROWCOUNT;
  
  IF v_rows_updated = 0 THEN
    RAISE_APPLICATION_ERROR(-20001, 
      '数据已被其他用户修改,请刷新后重试');
  END IF;
  
  COMMIT;
END;
/

-- 使用乐观锁
DECLARE
  v_emp_id NUMBER := 100;
  v_current_salary NUMBER;
  v_current_version NUMBER;
  v_new_salary NUMBER := 8000;
BEGIN
  -- 获取当前数据和版本号
  SELECT salary, version_number
  INTO v_current_salary, v_current_version
  FROM employees
  WHERE employee_id = v_emp_id;
  
  -- 执行业务逻辑...
  
  -- 尝试更新
  optimistic_update_salary(v_emp_id, v_new_salary, v_current_version);
  
  DBMS_OUTPUT.PUT_LINE('工资更新成功');
  
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('更新失败: ' || SQLERRM);
END;
/

8.3 监控和维护

8.3.1 定期锁健康检查

-- 创建锁健康检查报告
CREATE OR REPLACE PROCEDURE lock_health_report AS
  v_total_locks NUMBER;
  v_waiting_locks NUMBER;
  v_long_tx NUMBER;
  v_deadlocks NUMBER;
BEGIN
  DBMS_OUTPUT.PUT_LINE('=== Oracle锁健康报告 ===');
  DBMS_OUTPUT.PUT_LINE('报告时间: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
  DBMS_OUTPUT.PUT_LINE('');
  
  -- 总锁数量
  SELECT COUNT(*) INTO v_total_locks
  FROM v$lock l JOIN v$session s ON l.sid = s.sid
  WHERE s.username IS NOT NULL;
  
  DBMS_OUTPUT.PUT_LINE('1. 当前活跃锁数量: ' || v_total_locks);
  
  -- 等待锁数量
  SELECT COUNT(*) INTO v_waiting_locks FROM lock_wait_analysis;
  DBMS_OUTPUT.PUT_LINE('2. 锁等待情况: ' || v_waiting_locks);
  
  -- 长事务数量
  SELECT COUNT(*) INTO v_long_tx
  FROM v$session s JOIN v$transaction t ON s.saddr = t.ses_addr
  WHERE (SYSDATE - t.start_date) * 24 * 60 > 15; -- 超过15分钟
  
  DBMS_OUTPUT.PUT_LINE('3. 长事务数量: ' || v_long_tx || ' (>15分钟)');
  
  -- 死锁统计
  SELECT value INTO v_deadlocks
  FROM v$sysstat
  WHERE name = 'enqueue deadlocks';
  
  DBMS_OUTPUT.PUT_LINE('4. 总死锁次数: ' || v_deadlocks);
  
  -- 健康评估
  DBMS_OUTPUT.PUT_LINE('');
  DBMS_OUTPUT.PUT_LINE('=== 健康评估 ===');
  
  IF v_waiting_locks = 0 AND v_long_tx = 0 THEN
    DBMS_OUTPUT.PUT_LINE('状态: 良好 ✓');
  ELSIF v_waiting_locks < 5 AND v_long_tx < 3 THEN
    DBMS_OUTPUT.PUT_LINE('状态: 正常 ⚠');
  ELSE
    DBMS_OUTPUT.PUT_LINE('状态: 需要注意 ✗');
    DBMS_OUTPUT.PUT_LINE('建议: 检查长事务和锁等待情况');
  END IF;
  
END;
/

-- 执行健康检查
EXEC lock_health_report;

8.3.2 清理和维护脚本

-- 清理孤立锁的脚本
CREATE OR REPLACE PROCEDURE cleanup_orphaned_locks AS
  v_count NUMBER := 0;
BEGIN
  -- 查找并清理应用级锁表中的孤立锁
  FOR rec IN (
    SELECT al.lock_name, al.session_id
    FROM app_locks al
    LEFT JOIN v$session s ON al.session_id = s.sid
    WHERE s.sid IS NULL  -- 会话已不存在
  ) LOOP
    DELETE FROM app_locks 
    WHERE lock_name = rec.lock_name 
      AND session_id = rec.session_id;
    
    v_count := v_count + 1;
    DBMS_OUTPUT.PUT_LINE('清理孤立锁: ' || rec.lock_name);
  END LOOP;
  
  COMMIT;
  
  DBMS_OUTPUT.PUT_LINE('清理完成,共清理 ' || v_count || ' 个孤立锁');
END;
/

-- 定期执行清理
EXEC cleanup_orphaned_locks;

通过这个全面的Oracle锁教程,我们深入探讨了Oracle数据库中锁的各个方面,从基础概念到高级应用,从监控诊断到性能优化。理解和正确使用锁机制对于构建高性能、高并发的数据库应用至关重要。在实际应用中,应该根据具体的业务需求和并发模式来选择合适的锁策略,并建立完善的监控和维护机制。

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2401816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS逆向爬虫教程与实战技巧

想要一个关于爬虫JS逆向的详细教程。这是一个很专业的技术需求&#xff0c;最近有个可能是正在学习爬虫技术的开发者或者数据分析师&#xff0c;遇到了需要破解JavaScript加密的反爬机制的问题&#xff0c;想让我出一期实战教程&#xff0c;话不多说&#xff0c;开干。 以下是我…

【Redis】Redis 的常见客户端汇总

目录 一、命令行客户端 二、图形界面的客户端 三、Java 客户端 3.1 SpringDataRedis 3.2 Jedis 3.2.1 连接池的配置 3.3 Lettuce 3.3.1 RedisTemplate 工具类实现 3.3.2 自定义序列化器 3.3.3 StringRedisTemplate 3.3.4 集群配置 3.3.4.1 刷新节点集群拓扑动态感应…

关于akka官方quickstart示例程序(scala)的记录

参考资料 https://doc.akka.io/libraries/akka-core/current/typed/actors.html#first-example 关于scala语法的注意事项 extends App是个语法糖&#xff0c;等同于直接在伴生对象中编写main 方法对象是通过apply方法创建的&#xff0c;也可以通过对象的名称单独创建&#x…

2025年渗透测试面试题总结-腾讯[实习]玄武实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]玄武实验室-安全工程师 1. 自我介绍 2. CSRF原理 3. Web安全入门时间 4. 学习Web安全的原因 …

网站首页菜单两种布局vue+elementui顶部和左侧栏导航

顶部菜单实现 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Vue.js Element UI 路由导航</…

@Builder的用法

Builder 是 Lombok 提供的一个注解&#xff0c;用于简化 Java 中构建对象的方式&#xff08;Builder 模式&#xff09;。它可以让你以更加简洁、链式的方式来创建对象&#xff0c;尤其适用于构造参数较多或部分可选的类。

vue实现点击按钮input保持聚焦状态

主要功能&#xff1a; 点击"停顿"按钮切换对话框显示状态输入框聚焦时保持状态点击对话框外的区域自动关闭 以下是代码版本&#xff1a; <template><div class"input-container"><el-inputv-model"input"style"width: 2…

[蓝桥杯]取球博弈

取球博弈 题目描述 两个人玩取球的游戏。 一共有 NN 个球&#xff0c;每人轮流取球&#xff0c;每次可取集合 n1,n2,n3n1​,n2​,n3​中的任何一个数目。 如果无法继续取球&#xff0c;则游戏结束。 此时&#xff0c;持有奇数个球的一方获胜。 如果两人都是奇数&#xff…

[Java 基础]数组

什么是数组&#xff1f;想象一下&#xff0c;你需要存储 5 个学生的考试成绩。你可以声明 5 个不同的 int 变量&#xff0c;但这会显得很笨拙。数组提供了一种更简洁、更有组织的方式来存储和管理这些数据。 数组可以看作是相同类型元素的集合&#xff0c;这些元素在内存中是连…

‘pnpm‘ 不是内部或外部命令,也不是可运行的程序

npm install -g pnpm changed 1 package in 4s 1 package is looking for funding run npm fund for details C:\Users\gang>pnpm pnpm 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 原来是安装的全局路径被我改了 npm list -g --depth 0 把上述…

Android Test2 获取系统android id

Android Test2 获取系统 android id 这篇文章针对一个常用的功能做一个测试。 在项目中&#xff0c;时常会遇到的一个需求就是&#xff1a;一台设备的唯一标识值。然后&#xff0c;在网络请求中将这个识别值传送到后端服务器&#xff0c;用作后端数据查询的条件。Android 设备…

webpack打包学习

vue开发 现在项目里安装vue&#xff1a; npm install vue vue的文件后缀是.vue webpack不认识vue的话就接着安插件 npm install vue-loader -D 这是.vue文件&#xff1a; <template> <div><h2 class"title">{{title}}</h2><p cla…

基于Java(Jsp+servelet+Javabean)+MySQL实现图书管理系统

图书管理系统 一、需求分析 1.1 功能描述 1.1.1“读者”功能 1&#xff09;图书的查询&#xff1a;图书的查询可以通过搜索图书 id、书名、作者名、出版社来实现,显示结果中需要包括书籍信息以及是否被借阅的情况&#xff1b; 2&#xff09;图书的借阅&#xff1a;借阅图书…

服务器CPU被WMI Provider Host系统进程占用过高,导致系统偶尔卡顿的排查处理方案

问题现状 最近一个项目遇到一个非常奇葩的问题&#xff1a;正式服务器被一个WMI Provider Host的系统进程占用大量的CPU资源&#xff0c;导致我们的系统偶尔卡顿 任务管理器-详细信息中CPU时间&#xff0c;这个进程也是占用最多的 接口时不时慢很多 但单独访问我们的接口又正…

JavaSwing之--JMenuBar

Java Swing之–JMenuBar(菜单栏) JMenuBar是 Java Swing 库中的一个组件&#xff0c;用于创建菜单栏&#xff0c;通常位于窗口的顶部。它是菜单系统的容器&#xff0c;用于组织和显示应用程序的菜单结构 菜单栏由菜单构成&#xff0c;菜单由菜单项或子菜单构成&#xff0c;也…

【物联网-S7Comm协议】

物联网-S7Comm协议 ■ 调试工具■ S7协议-简介■ S7协议和modbusTCP协议区别■ OSI 层 S7 协议■ S7协议数据结构 &#xff08;TPKTCOTPS7Comm&#xff09;■ TPKT&#xff08;第五层&#xff1a;会话层&#xff09; 总共占4个字节■ COTP&#xff08;第六层&#xff1a;表示层…

数据分析后台设计指南:实战案例解析与5大设计要点总结

引言 数据于企业而言异常重要&#xff0c;企业通过数据可以优化战略决策&#xff0c;因此企业对数据的采集正趋向智能化、数字化&#xff0c;数据分析后台就是企业智能化、数字化记录、分析数据的渠道。本文分享一个数据分析后台原型实战案例&#xff0c;通过页面拆解总结原型…

网络测试实战:金融数据传输的生死时速

阅读原文 7.4 网络测试实战--数据传输&#xff1a;当毫秒决定百万盈亏 你的交易指令为何总是慢人一步&#xff1f; 在2020年"原油宝"事件中&#xff0c;中行原油宝产品因为数据传输延迟导致客户未能及时平仓&#xff0c;最终亏损超过90亿元。这个血淋淋的案例揭示了…

数据库系统概论(十四)详细讲解SQL中空值的处理

数据库系统概论&#xff08;十四&#xff09;详细讲解SQL中空值的处理 前言一、什么是空值&#xff1f;二、空值是怎么产生的&#xff1f;1. 插入数据时主动留空2. 更新数据时设置为空3. 外连接查询时自然出现 三、如何判断空值&#xff1f;例子&#xff1a;查“漏填数据的学生…

【信创-k8s】海光/兆芯+银河麒麟V10离线部署k8s1.31.8+kubesphere4.1.3

❝ KubeSphere V4已经开源半年多&#xff0c;而且v4.1.3也已经出来了&#xff0c;修复了众多bug。介于V4优秀的LuBan架构&#xff0c;核心组件非常少&#xff0c;资源占用也显著降低&#xff0c;同时带来众多功能和便利性。我们决定与时俱进&#xff0c;使用1.30版本的Kubernet…