MySQL 锁机制与死锁分析深度解析

MySQL 锁机制与死锁分析深度解析

锁是数据库并发控制的核心。本文深入剖析 InnoDB 的锁类型体系、Next-Key Lock 的加锁规则、意向锁与表锁的关系、死锁产生的原因与排查方法,以及生产环境的锁优化实战技巧。

一、InnoDB 锁概述

1.1 为什么需要锁

解决方式
并发问题
并发冲突
事务A: 读取 x=100
事务B: 读取 x=100
事务A: x=100+50
事务B: x=100+100
事务A: 提交 x=150
事务B: 提交 x=200
最终 x=200 (错误!)
锁机制
排他锁 (X)
共享锁 (S)
保证数据一致性

1.2 InnoDB 锁分类全景

锁类型
特殊锁
意向锁 (IX/IS)
自增锁 (AUTO-INC)
临键锁
按算法
Record Lock
Gap Lock
Next-Key Lock
Insert Intention Lock
按模式
共享锁 (S)
排他锁 (X)
按作用域
行级锁
表级锁
InnoDB 锁

1.3 锁兼容性矩阵

兼容结论
X 与任何锁都不兼容
S 与 S 兼容
IX 与 IS 兼容
IX 与 S 互斥
锁兼容性
X
S
IX
IS
X
S
IX
IS
X
✅
X
✅
X
S
IX
IS
❌
❌
❌
❌
❌
✅
❌
✅
❌
❌
❌
❌
❌
❌
❌
✅


二、行级锁

2.1 Record Lock(记录锁)

记录锁锁住索引记录本身:
示例
SELECT * FROM t WHERE id = 5 FOR UPDATE
锁住 id=5 这条记录
其他事务无法修改 id=5
但可以修改其他记录
Record Lock
索引记录
Record Lock
锁住 id=5 的记录

2.2 Gap Lock(间隙锁)

间隙锁锁住索引记录之间的间隙:
作用
防止幻读
阻止其他事务插入新记录
锁定间隙内的插入
Gap Lock
索引值: 1, 5, 10, 15
Gap Lock 锁住 (1, 5) 间隙
Gap Lock 锁住 (5, 10) 间隙
Gap Lock 锁住 (10, 15) 间隙
Gap Lock 锁住 (15, +∞) 间隙

2.3 Next-Key Lock

Next-Key Lock = Record Lock + Gap Lock:
示例
索引值: 1, 5, 10
WHERE id >= 5 FOR UPDATE
锁住 [5, 10) 范围
Record Lock on id=5
Gap Lock on (5, 10)
组合结构
Next-Key Lock
Record Lock
Gap Lock
锁住索引记录本身
锁住索引记录之间的间隙

2.4 Next-Key Lock 示例详解

sql 复制代码
-- 假设索引有值: 1, 5, 10, 15
-- 执行语句: SELECT * FROM t WHERE id >= 5 FOR UPDATE;

-- Next-Key Lock 锁住的区间:
-- 1. 对于 id=5: Next-Key Lock on [5, 10)
--    - Record Lock on id=5
--    - Gap Lock on (5, 10)
-- 2. 对于 id=10: Next-Key Lock on [10, 15)
--    - Record Lock on id=10
--    - Gap Lock on (10, 15)
-- 3. 对于 id=15: Next-Key Lock on [15, +∞)
--    - Record Lock on id=15
--    - Gap Lock on (15, +∞)

2.5 唯一索引的特殊性

对比
普通索引: 锁 (pre, 5] + (5, next)
范围更大
更容易死锁
示例
SELECT * FROM t WHERE id = 5 FOR UPDATE
假设 id 是唯一索引
只锁 id=5 这条记录
不锁 (5, next) 间隙
等值查询优化
唯一索引 + 等值查询
只锁住记录本身
不锁间隙


三、表级锁

3.1 表锁

不推荐原因
粒度太粗
并发能力差
容易造成锁等待
InnoDB 推荐使用行级锁
表锁类型
LOCK TABLES ... READ
表级共享锁
其他事务可读不可写
LOCK TABLES ... WRITE
表级排他锁
阻塞其他事务读写

3.2 意向锁

意向锁 用于协调行级锁 和表级锁:
作用
遍历检查每行
检查 IX/IS
检查表锁时
是否有行级锁?
效率低
O(1) 判断
快速判断是否可以加表锁
意向锁类型
IX: 意图排他锁
表示事务将在某行加 X 锁
IS: 意图共享锁
表示事务将在某行加 S 锁

3.3 意向锁获取顺序

示例
事务A: SELECT * FROM t WHERE id=5 FOR UPDATE
获取行级 X 锁
获取表级 IX
其他事务无法获取表级 X
其他事务可以获取表级 S
加锁流程
有
无
有
无
请求表级 S 锁
检查 IX?
❌ 阻塞
检查 X?
❌ 阻塞
✅ 获取 IS

3.4 意向锁兼容性

理解
IX 与 S 互斥
因为 IX 意味着要加 X 锁
X 与 S 互斥
兼容性矩阵
| IS | IX | S | X
IS | ✅ | ✅ | ✅ | ❌
IX | ✅ | ✅ | ❌ | ❌
S | ✅ | ❌ | ✅ | ❌
X | ❌ | ❌ | ❌ | ❌


四、自增锁

4.1 AUTO-INC 锁

配置参数
innodb_autoinc_lock_mode
0: 传统 (表级锁)
1: 连续 (默认)
2: 交错 (性能最好)
AUTO-INC 锁特性
表级自增锁
插入时自动获取
插入完成后释放
确保自增 ID 连续

4.2 三种自增锁模式

innodb_autoinc_lock_mode = 2
交错锁模式
所有 INSERT 使用互斥量
性能最好
不保证自增ID连续
主从复制可能有问题
innodb_autoinc_lock_mode = 1
连续锁模式
简单 INSERT 使用互斥量
批量 INSERT 使用 AUTO-INC 锁
保证自增ID连续
innodb_autoinc_lock_mode = 0
传统锁模式
使用 AUTO-INC 锁
表级锁,效率低
语句执行完后释放


四·续、锁的内存结构与 MDL 锁

4.3 锁的内存结构

Record Lock 内存结构
lock_rec_t
rec_id: 页号 + 记录号
n_bits: 位图大小
bits\[\]: 记录锁状态

每个 bit 对应一条记录
InnoDB 锁对象结构
lock_t 结构
lock_type_t type

锁类型 (RECORD/TABLE)
ibool impl_trx

持有锁的事务
dict_table_t *tab_def

锁关联的表
lock_rec_t rec_lock

行锁信息
ulint space, page_no, n_bits

表空间、页号、位图

4.4 锁信息查询详解

sql 复制代码
-- 查看当前所有锁
SELECT 
    ENGINE_LOCK_ID,
    ENGINE_TRANSACTION_ID,
    THREAD_ID,
    EVENT_ID,
    OBJECT_SCHEMA,
    OBJECT_NAME,
    LOCK_TYPE,
    LOCK_MODE,
    LOCK_STATUS,
    LOCK_DATA
FROM performance_schema.data_locks
ORDER BY ENGINE_TRANSACTION_ID;

-- 查看锁等待关系
SELECT 
    REQUESTING_THAPHREAD_ID,
    REQUESTING_ENGINE_LOCK_ID,
    REQUESTING_ENGINE_TRANSACTION_ID,
    BLOCKING_ENGINE_LOCK_ID,
    BLOCKING_ENGINE_TRANSACTION_ID
FROM performance_schema.data_lock_waits;

-- 查看具体锁模式
SELECT 
    OBJECT_NAME,
    INDEX_NAME,
    LOCK_MODE,
    LOCK_STATUS,
    LOCK_DATA
FROM performance_schema.data_locks
WHERE OBJECT_NAME = 'orders';

4.5 MDL 锁(元数据锁)

MDL 锁获取场景
MDL_EXCLUSIVE 获取场景
ALTER TABLE
DROP TABLE
RENAME TABLE
ANALYZE TABLE
MDL 锁类型
MDL 锁
MDL_SHARED: 只读访问
MDL_EXCLUSIVE: 修改表结构
MDL_INTENTION_EXCLUSIVE
MDL_SHARED_READ_ONLY
MDL_SHARED_WRITE_LOW_PRIO

4.6 MDL 锁问题排查

sql 复制代码
-- 查看 MDL 锁等待
SELECT 
    THREAD_ID,
    PROCESSLIST_ID,
    PROCESSLIST_USER,
    PROCESSLIST_HOST,
    PROCESSLIST_DB,
    PROCESSLIST_COMMAND,
    PROCESSLIST_STATE,
    PROCESSLIST_INFO
FROM performance_schema.THREADS
WHERE PROCESSLIST_COMMAND != 'Sleep';

-- 查看 MDL 锁详情
SELECT 
    OBJECT_SCHEMA,
    OBJECT_NAME,
    COLUMN_NAME,
    COLUMN_COUNT,
    STORAGE,
    ENGINE,
    TABLE_ID,
    PARTITION_NAME,
    SUBPARTITION_NAME,
    INDEX_NAME,
    SCOPE
FROM performance_schema.METADATA_LOCKS;

-- 查看 MDL 锁等待
SELECT 
    REQUESTING_THREAD_ID,
    REQUESTING_EVENT_ID,
    REQUESTING_OBJECT_SCHEMA,
    REQUESTING_OBJECT_NAME,
    BLOCKING_THREAD_ID,
    BLOCKING_EVENT_ID,
    BLOCKING_OBJECT_SCHEMA,
    BLOCKING_OBJECT_NAME,
    BLOCKING_METADATA_LOCK_STATUS
FROM performance_schema.METADATA_LOCK_WAITS;

4.7 常见 MDL 锁问题

sql 复制代码
-- 问题1: 大查询阻塞 DDL
-- 会话1: 执行大查询
SELECT * FROM orders INTO OUTFILE '/tmp/orders.csv';

-- 会话2: 尝试修改表结构 (阻塞)
ALTER TABLE orders ADD COLUMN remark VARCHAR(500);

-- 会话3: 尝试插入数据 (阻塞)
INSERT INTO orders VALUES (...);

-- 解决方案: 使用 pt-online-schema-change

-- 问题2: 长事务阻塞 DDL
-- 会话1: 开启事务
BEGIN;
SELECT * FROM orders LIMIT 1;

-- 会话2: 尝试 DDL (获取 MDL 锁失败)
ALTER TABLE orders ADD COLUMN remark VARCHAR(500);

-- 解决方案: 设置 lock_wait_timeout
SET SESSION lock_wait_timeout = 10;

四·续续、锁升级与锁优化

4.8 锁升级机制

锁升级影响
锁升级的影响
并发能力下降
死锁可能性降低
锁等待时间可能增加
锁升级过程
行锁 -> 表锁
统计 UPDATE/DELETE 影响的记录数
如果 > 阈值,升级为表锁
避免锁结构过多
锁升级条件
锁升级触发
innodb_table_locks = 1 (默认)
UPDATE/DELETE 影响多行
超过阈值 (默认 2000 行)

4.9 锁优化最佳实践

监控指标
关键监控指标
lock_wait_timeout
innodb_row_lock_time_avg
lock_row_lock_current_count
InnoDB row locks
具体措施
优化措施

  1. 使用主键或唯一索引查询
  2. 避免范围查询加锁
  3. 业务层控制并发
  4. 合理设计索引
  5. 拆分大事务
    优化原则
    锁优化核心
    减少锁持有时间
    降低锁粒度
    避免锁冲突

4.10 乐观锁实现

java 复制代码
// 基于版本号的乐观锁
@Entity
@Table(name = "accounts")
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private BigDecimal balance;
    
    @Version
    private Long version;  // 版本字段
}

// Service 实现
@Service
public class AccountService {
    
    @Transactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 重试机制
        int retry = 3;
        while (retry > 0) {
            try {
                Account from = accountRepository.findById(fromId)
                    .orElseThrow(() -> new RuntimeException("账户不存在"));
                Account to = accountRepository.findById(toId)
                    .orElseThrow(() -> new RuntimeException("账户不存在"));
                
                from.setBalance(from.getBalance().subtract(amount));
                to.setBalance(to.getBalance().add(amount));
                
                accountRepository.save(from);
                accountRepository.save(to);
                break;  // 成功则退出
            } catch (OptimisticLockException e) {
                retry--;
                if (retry == 0) throw e;
                // 等待后重试
                Thread.sleep(100);
            }
        }
    }
}

五、死锁原理

5.1 死锁的定义

死锁四要素
互斥: 资源只能被一个事务持有
持有并等待: 持有资源同时等待其他资源
不抢占: 事务不能抢别人的资源
循环等待: 形成资源等待环
死锁形成
事务A 持有锁1,等待锁2
事务B 持有锁2,等待锁1
形成循环等待
双方都无法继续
死锁

5.2 死锁产生场景

sql 复制代码
-- 场景1: 不同顺序更新多行
-- 事务A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;  -- 锁住 id=1
UPDATE accounts SET balance = balance + 100 WHERE id = 2;  -- 等待 id=2

-- 事务B
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;  -- 锁住 id=2
UPDATE accounts SET balance = balance + 100 WHERE id = 1;  -- 等待 id=1
-- 死锁!

-- 场景2: 索引范围锁
-- 事务A
SELECT * FROM orders WHERE amount > 100 FOR UPDATE;
-- 锁住 (100, +∞) 范围

-- 事务B
INSERT INTO orders VALUES (200, ...);
-- 被阻塞

-- 事务A 回滚后,事务B 可能与事务C 死锁

5.3 死锁时序图

事务管理器 事务B 事务A 事务管理器 事务B 事务A 锁住 id=1 锁住 id=2 事务A 成功 BEGIN BEGIN UPDATE WHERE id=1 (获取X锁1) UPDATE WHERE id=2 (获取X锁2) UPDATE WHERE id=2 (等待X锁2) UPDATE WHERE id=1 (等待X锁1) 检测到死锁 发送 ROLLBACK 继续执行 COMMIT

5.4 InnoDB 死锁处理

处理策略
回滚代价最小的事务
基于 undo log 数量判断
undo 少的事务先回滚
检测机制
等待图 (Wait-For Graph)
节点: 事务
边: 等待关系
检测到环 -> 死锁


六、死锁排查与解决

6.1 查看死锁信息

sql 复制代码
-- 查看当前锁等待
SHOW ENGINE INNODB STATUS;

-- 输出示例
---TRANSACTION 12345, ACTIVE 5 sec
-- mysql tables in use 1, locked 1
-- LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
-- MySQL thread id 123, OS thread handle 0x7f8a, query id 456
-- UPDATE accounts SET balance = balance - 100 WHERE id = 2
-- Waiting for lock at gap of index `idx_id` of table `test`.`accounts`
-- LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
-- locks gap after rec in index `idx_id` of table `test`.`accounts`

6.2 死锁日志分析

sql 复制代码
-- 查看锁信息
SELECT 
    trx_id,
    trx_state,
    trx_started,
    trx_rows_locked,
    trx_rows_modified,
    trx_wait_for
FROM information_schema.INNODB_TRX;

-- 查看锁等待关系
SELECT 
    requesting_trx_id,
    blocking_trx_id,
    lock_mode,
    lock_type,
    lock_table,
    lock_index,
    lock_space,
    lock_page,
    lock_rec,
    lock_data
FROM information_schema.INNODB_LOCK_WAITS;

-- 查看锁详情
SELECT 
    lock_id,
    lock_trx_id,
    lock_mode,
    lock_type,
    lock_table,
    lock_index,
    lock_space,
    lock_page,
    lock_rec,
    lock_data
FROM information_schema.INNODB_LOCKS;

6.3 避免死锁的策略

预防策略
固定顺序访问
按 ID 顺序更新
避免循环等待
减少锁范围
使用主键查询
避免范围查询
减小事务
及时提交
避免长事务
合理隔离级别
读已提交可减少锁
但需注意幻读

6.4 代码层面优化

java 复制代码
// 反例: 随机顺序更新
@Transactional
public void transferRandom(int fromId, int toId, BigDecimal amount) {
    // 随机顺序,可能死锁
    if (Math.random() > 0.5) {
        accountMapper.decrease(fromId, amount);
        accountMapper.increase(toId, amount);
    } else {
        accountMapper.decrease(toId, amount);
        accountMapper.increase(fromId, amount);
    }
}

// 正例: 固定顺序更新
@Transactional
public void transferOrdered(int fromId, int toId, BigDecimal amount) {
    // 按 ID 大小固定顺序
    int firstId = Math.min(fromId, toId);
    int secondId = Math.max(fromId, toId);
    
    if (firstId == fromId) {
        accountMapper.decrease(fromId, amount);
        accountMapper.increase(toId, amount);
    } else {
        accountMapper.decrease(toId, amount);
        accountMapper.increase(fromId, amount);
    }
}

// 更优: 使用乐观锁
@Version
private Long version;

public boolean transferOptimistic(int fromId, int toId, BigDecimal amount) {
    // 先扣减,利用乐观锁冲突检测
    int affected = accountMapper.decreaseWithVersion(
        fromId, amount, version);
    if (affected == 0) {
        throw new OptimisticLockException("余额已变化");
    }
    accountMapper.increase(toId, amount);
    return true;
}

6.5 SQL 层面优化

sql 复制代码
-- 反例: 范围查询(锁住大量记录)
UPDATE orders 
SET status = 'completed' 
WHERE create_time > '2024-01-01' AND create_time < '2024-12-31';
-- 可能锁住数万条记录,死锁风险高

-- 正例1: 主键精准查询
UPDATE orders 
SET status = 'completed' 
WHERE id = 123;
-- 只锁一条记录

-- 正例2: 分批处理
UPDATE orders 
SET status = 'completed' 
WHERE id IN (SELECT id FROM orders WHERE status = 'pending' LIMIT 1000);
-- 分批小事务

-- 正例3: 使用低区分度字段
UPDATE orders 
SET status = 'completed' 
WHERE status = 'pending' AND DATE(create_time) = '2024-06-01';
-- 按小批量日期拆分

七、锁监控与优化

7.1 锁相关参数

sql 复制代码
-- 查看锁相关配置
SHOW VARIABLES LIKE 'innodb_lock%';

-- innodb_lock_wait_timeout: 锁等待超时时间(默认50秒)
-- innodb_lock_strict_mode: 严格模式,忽略唯一索引空值
-- innodb_print_lock_atomic_bugs: 打印锁相关的原子性bug

7.2 监控慢查询

sql 复制代码
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- 超过1秒记录
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

-- 查找长时间锁等待
SELECT 
    trx_mysql_thread_id,
    trx_query,
    trx_state,
    trx_started,
    UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(trx_started) AS duration
FROM information_schema.INNODB_TRX
WHERE trx_started < NOW() - INTERVAL 10 SECOND;

7.3 性能优化建议

隔离级别
选择合适隔离级别
读已提交可减少锁
但需注意幻读处理
事务优化
减小事务范围
避免长事务
及时提交
查询优化
避免全表扫描
使用主键或索引查询
减少锁覆盖的记录数
索引优化
合理创建索引
减少扫描范围
使用覆盖索引


八、面试高频问题

8.1 InnoDB 行级锁是如何实现的?

复制代码
InnoDB 行级锁依赖于索引:

1. 锁住索引而非数据行
   - 如果 UPDATE 使用主键索引
     -> 锁住主键索引
   - 如果 UPDATE 使用普通索引
     -> 锁住普通索引 + 主键索引

2. 加锁过程
   - 定位到第一个满足条件的记录
   - 对记录加 Record Lock
   - 对记录之间的间隙加 Gap Lock
   - 组合为 Next-Key Lock
   - 继续扫描,对每条记录加 Next-Key Lock

3. 释放过程
   - 按加锁顺序反向释放
   - 事务提交时统一释放

8.2 什么是 Next-Key Lock?解决了什么问题?

复制代码
Next-Key Lock = Record Lock + Gap Lock

解决的问题:幻读

示例:
假设表有数据: id=1, 5, 10

事务A:
  SELECT * FROM t WHERE id >= 5;  -- 读到 id=5, 10

事务B:
  INSERT INTO t VALUES (6);  -- 插入 id=6

  -- 如果没有 Gap Lock,事务A 再次查询会看到 id=6(幻读)
  -- 如果有 Gap Lock,INSERT 会被阻塞或回滚

  -- Next-Key Lock 锁住 [5, 10),INSERT INTO (6) 被阻塞

8.3 什么是意向锁?为什么需要意向锁?

复制代码
意向锁是表级锁,表示事务将在某行加锁:

- IS (Intent Share Lock): 意图加共享锁
- IX (Intent Exclusive Lock): 意图加排他锁

为什么需要:
假设要加表级 X 锁
  -> 需要检查是否有行级 X/S 锁
  -> 逐行检查效率低 O(n)
  
使用意向锁:
  -> 只需检查表级 IX/IS
  -> O(1) 判断

8.4 如何避免死锁?

复制代码
1. 固定顺序访问资源
   - 按主键 ID 排序后操作
   - 避免循环等待

2. 减小锁范围
   - 使用主键精准查询
   - 避免范围查询

3. 使用低隔离级别
   - READ COMMITTED 减少 Gap Lock
   - 但需要注意幻读

4. 使用乐观锁
   - 通过版本号控制并发
   - 减少锁冲突

5. 监控和超时处理
   - 设置合理的锁等待超时
   - 及时发现和处理死锁

8.5 死锁和锁等待的区别?

复制代码
┌─────────────────────────────────────────────────────────────┐
│ 锁等待                                                                │
├─────────────────────────────────────────────────────────────┤
│ 事务A 持有锁,等待事务B 释放锁                                       │
│ 事务B 持有锁,等待事务C 释放锁                                       │
│ 事务C 持有锁,等待事务A 释放锁                                       │
│ -> 形成等待链,但还没有死锁                                          │
│ -> 正常情况,设置超时时间即可                                        │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 死锁                                                                    │
├─────────────────────────────────────────────────────────────┤
│ 事务A 持有锁1,等待锁2                                                │
│ 事务B 持有锁2,等待锁1                                                │
│ -> 形成循环等待                                                       │
│ -> InnoDB 检测到死锁后,选择代价小的事务回滚                          │
└─────────────────────────────────────────────────────────────┘

区别:
- 锁等待: A等B,可能最终A或B完成
- 死锁: A等B,B等A,无法自行解除,需要回滚

九、总结

9.1 锁类型总结

表级锁
LOCK TABLES
意向锁 (IX/IS)
AUTO-INC 锁
行级锁
Record Lock
Gap Lock
Next-Key Lock

9.2 死锁处理流程

复制代码
死锁处理流程:

1. 检测死锁
   - InnoDB 使用 Wait-For Graph
   - 检测到环则死锁

2. 选择回滚事务
   - 基于 undo log 数量
   - 选择代价小的事务回滚

3. 回滚并通知
   - 发送错误给客户端
   - 错误码: 1213 Deadlock found

4. 业务处理
   - 业务层捕获异常
   - 重试或人工处理

9.3 最佳实践

复制代码
锁优化最佳实践:

1. 索引设计
   ✅ 使用主键或索引查询
   ✅ 创建合适的索引减少扫描范围
   
2. SQL 编写
   ✅ 使用精准查询
   ❌ 避免范围查询 FOR UPDATE
   
3. 事务设计
   ✅ 事务尽量短小
   ✅ 避免长事务
   ✅ 固定顺序访问资源
   
4. 隔离级别
   ✅ 根据业务选择合适隔离级别
   ✅ 读多写少可考虑 RC
   
5. 监控
   ✅ 监控锁等待时间
   ✅ 分析慢查询日志
   ✅ 定期检查死锁日志
相关推荐
许彰午13 小时前
01-Oracle数据库简介与版本选择
数据库·oracle
菜鸟~noob23313 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
hasty14 小时前
policy.xml 在,不代表策略生效:ImageMagick 补丁给安全配置验收的提醒
xml·mysql·安全
Seraphina3615 小时前
SQL注入(二)盲注
网络·数据库·经验分享·笔记·sql
盟接之桥16 小时前
AI助手:你的7×24小时智能管家——让异常预警无处不在
大数据·网络·数据库·人工智能·制造·ai编程
waoooqwe16 小时前
问卷样本真实吗
大数据·数据库·算法·数据分析
可乐ea17 小时前
Agent 评测换判据:用终态数据库状态判定任务是否完成
数据库·ui·oracle·mcp工具·agent评测·终态断言·智能体可靠性
l1t17 小时前
DeepSeek总结的一个不含数据的 DuckDB 数据库
服务器·数据库·duckdb
PaperData17 小时前
2007-2020年税收调查面板数据
数据库
程序员清风18 小时前
CSV、Excel 与数据库数据读取实践
数据库·oracle·excel