MySQL锁等待与死锁问题分析

本文介绍了MySQL中锁等待和死锁的概念、原因,以及如何通过系统表进行现象复现和处理。锁等待是由于事务等待锁超过设定时间导致,可通过`innodb_trx`和`innodb_lock_waits`等表查询事务状态。死锁则是两个事务互相等待资源造成,MySQL会自动选择一个事务回滚。避免死锁的方法包括减小事务大小、按顺序访问记录、及时提交或回滚事务等。

前言:

在 MySQL 运维过程中,锁等待和死锁问题是令各位 DBA 及开发同学非常头痛的事。出现此类问题会造成业务回滚、卡顿等故障,特别是业务繁忙的系统,出现死锁问题后影响会更严重。本篇文章我们一起来学习下什么是锁等待及死锁,出现此类问题又应该如何分析处理呢?

1.了解锁等待与死锁

出现锁等待或死锁的原因是访问数据库需要加锁,那你可能要问了,为啥要加锁呢?原因是为了确保并发更新场景下的数据正确性,保证数据库事务的隔离性。

试想一个场景,如果你要去图书馆借一本《高性能MySQL》,为了防止有人提前把这本书借走,你可以提前进行预约(加锁),这把锁可以怎么加?

  • 封锁图书馆(数据库级别的锁)
  • 把数据库相关的书都锁住(表级别的锁)
  • 只锁 MySQL 相关的书(页级别的锁)
  • 只锁《高性能MySQL》这本书(行级别的锁)

锁的粒度越细,并发级别越高,实现也更复杂。

锁等待也可称为事务等待,后执行的事务等待前面处理的事务释放锁,但是等待时间超过了 MySQL 的锁等待时间,就会引发这个异常。等待超时后的报错为“Lock wait timeout exceeded…”。

死锁发生的原因是两个事务互相等待对方释放相同资源的锁,从而造成的死循环。产生死锁后会立即报错“Deadlock found when trying to get lock…”。

2.现象复现及处理

下面我们以 MySQL 5.7.23 版本为例(隔离级别是 RR ),来复现下上述两种异常现象。

mysql> show create table test_tb\G
*************************** 1. row ***************************
       Table: test_tb
Create Table: CREATE TABLE `test_tb` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `col1` varchar(50) NOT NULL DEFAULT '',
  `col2` int(11) NOT NULL DEFAULT '1',
  `col3` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `idx_col1` (`col1`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from test_tb;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
|  1 | fdg  |    1 | abc  |
|  2 | a    |    2 | fg   |
|  3 | ghrv |    2 | rhdv |
+----+------+------+------+
3 rows in set (0.00 sec)

# 事务一首先执行
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test_tb where col1 = 'a' for update;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
|  2 | a    |    2 | fg   |
+----+------+------+------+
1 row in set (0.00 sec)

# 事务二然后执行
mysql> begin;
Query OK, 0 rows affected (0.01 sec)

mysql> update test_tb set col2 = 1 where col1 = 'a';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

出现上种异常的原因是事务二在等待事务一的行锁,但事务一一直没提交,等待超时而报错。InnoDB 行锁等待超时时间由 innodb_lock_wait_timeout 参数控制,此参数默认值为 50 ,单位为秒,即默认情况下,事务二会等待 50s ,若仍拿不到行锁则会报等待超时异常并回滚此条语句。

对于 5.7 版本,出现锁等待时,我们可以查看 information_schema 中的几张系统表来查询事务状态。

  • innodb_trx 当前运行的所有事务。
  • innodb_locks 当前出现的锁。
  • innodb_lock_waits 锁等待的对应关系
# 锁等待发生时 查看innodb_trx表可以看到所有事务 
# trx_state值为LOCK WAIT 则代表该事务处于等待状态

mysql> select * from information_schema.innodb_trx\G
*************************** 1. row ***************************
                    trx_id: 38511
                 trx_state: LOCK WAIT
               trx_started: 2021-03-24 17:20:43
     trx_requested_lock_id: 38511:156:4:2
          trx_wait_started: 2021-03-24 17:20:43
                trx_weight: 2
       trx_mysql_thread_id: 1668447
                 trx_query: update test_tb set col2 = 1 where col1 = 'a'
       trx_operation_state: starting index read
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
                    trx_id: 38510
                 trx_state: RUNNING
               trx_started: 2021-03-24 17:18:54
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 4
       trx_mysql_thread_id: 1667530
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 4
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 3
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.00 sec)

# innodb_trx 字段值含义
trx_id:事务ID。
trx_state:事务状态,有以下几种状态:RUNNING、LOCK WAIT、ROLLING BACK 和 COMMITTING。
trx_started:事务开始时间。
trx_requested_lock_id:事务当前正在等待锁的标识,可以和 INNODB_LOCKS 表 JOIN 以得到更多详细信息。
trx_wait_started:事务开始等待的时间。
trx_weight:事务的权重。
trx_mysql_thread_id:事务线程 ID,可以和 PROCESSLIST 表 JOIN。
trx_query:事务正在执行的 SQL 语句。
trx_operation_state:事务当前操作状态。
trx_tables_in_use:当前事务执行的 SQL 中使用的表的个数。
trx_tables_locked:当前执行 SQL 的行锁数量。
trx_lock_structs:事务保留的锁数量。
trx_isolation_level:当前事务的隔离级别。

# sys.innodb_lock_waits 视图也可看到事务等待状况,且给出了杀链接的SQL
mysql> select * from sys.innodb_lock_waits\G
*************************** 1. row ***************************
                wait_started: 2021-03-24 17:20:43
                    wait_age: 00:00:22
               wait_age_secs: 22
                locked_table: `testdb`.`test_tb`
                locked_index: idx_col1
                 locked_type: RECORD
              waiting_trx_id: 38511
         waiting_trx_started: 2021-03-24 17:20:43
             waiting_trx_age: 00:00:22
     waiting_trx_rows_locked: 1
   waiting_trx_rows_modified: 0
                 waiting_pid: 1668447
               waiting_query: update test_tb set col2 = 1 where col1 = 'a'
             waiting_lock_id: 38511:156:4:2
           waiting_lock_mode: X
             blocking_trx_id: 38510
                blocking_pid: 1667530
              blocking_query: NULL
            blocking_lock_id: 38510:156:4:2
          blocking_lock_mode: X
        blocking_trx_started: 2021-03-24 17:18:54
            blocking_trx_age: 00:02:11
    blocking_trx_rows_locked: 3
  blocking_trx_rows_modified: 0
     sql_kill_blocking_query: KILL QUERY 1667530
sql_kill_blocking_connection: KILL 1667530

sys.innodb_lock_waits 视图整合了事务等待状况,同时给出杀掉堵塞源端的 kill 语句。不过是否要杀掉链接还是需要综合考虑的。

死锁与锁等待稍有不同,我们同样也来简单复现下死锁现象。

# 开启两个事务
# 事务一执行
mysql> update test_tb set col2 = 1 where col1 = 'a';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

# 事务二执行
mysql> update test_tb set col2 = 1 where id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

# 回到事务一执行 回车后 此条语句处于锁等待状态
mysql> update test_tb set col1 = 'abcd' where id = 3;
Query OK, 1 row affected (5.71 sec)
Rows matched: 1  Changed: 1  Warnings: 0

# 回到事务二再执行 此时二者相互等待发生死锁
mysql> update test_tb set col3 = 'gddx' where col1 = 'a';
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

发生死锁后会选择一个事务进行回滚,想查明死锁原因,可以执行 show engine innodb status 来查看死锁日志,根据死锁日志,结合业务逻辑来进一步定位死锁原因。

在实际应用中,我们要尽量避免死锁现象的发生,可以从以下几个方面入手:

  • 事务尽可能小,不要将复杂逻辑放进一个事务里。
  • 涉及多行记录时,约定不同事务以相同顺序访问。
  • 业务中要及时提交或者回滚事务,可减少死锁产生的概率。
  • 表要有合适的索引。
  • 可尝试将隔离级别改为 RC 。

总结:

本篇文章简单介绍了锁等待及死锁发生的原因,其实真实业务中发生死锁还是很难分析的,需要一定的经验积累。本篇文章只是面向初学者,希望各位对死锁能够有个简单的印象。

<think>嗯,用户之前让我介绍了关于“512B交织、8 Bank、Stride为512/576/640/768/1024的哈希算法”,现在他又问“具体hash算法”。我需要先弄清楚他到底想知道什么。 首先,之前的回答主要聚焦在内存结构、步长和Bank设计,用于优化哈希冲突和并行访问。现在用户要求“具体hash算法”,可能是指具体的哈希函数实现,或者更详细的算法步骤。可能他觉得之前的解释还不够深入,想要了解具体的计算过程或者不同的哈希算法类型。 我需要考虑用户可能的背景。他可能是在进行高性能计算、GPU编程,或者数据库设计,因为这些场景常用到内存优化和哈希表。他提到的参数如512B交织、8 Bank和不同步长,这些通常用于减少冲突和提高并行性,尤其是在GPU哈希表中常见。 用户现在问具体算法,可能需要知道除了结构设计之外,具体的哈希函数是如何计算的,比如使用哪种哈希算法(如MurmurHash、CRC等),或者如何结合这些结构参数来设计哈希函数。另外,也可能想了解在遇到冲突时,如何处理,比如开放寻址法中的线性探测或双重哈希,但结合特定的步长和Bank分配。 需要注意的是,之前的回答中提到了步长动态调整和Bank交织分配,但用户可能想要更具体的算法步骤,或者具体的哈希函数示例。比如,如何将键映射到不同的Bank,如何结合步长来避免冲突,或者具体的代码逻辑。 另外,用户可能对哈希算法在具体应用中的实现感兴趣,比如在CUDA中如何利用Bank和交织来优化哈希表性能。或者,在分布式系统中如何利用这些参数进行数据分片。 需要确保回答覆盖常见的哈希算法类型,并结合之前的参数,解释它们如何协同工作。同时,可能需要提供具体的例子,说明在不同步长和Bank数量下,哈希值是如何计算的,冲突解决策略如何实施。 还要注意用户可能对性能优化感兴趣,比如如何通过选择不同的步长来最大化并行性,减少Bank冲突。或者,如何根据数据分布调整哈希参数,以达到最佳效果。 总结来说,用户需要的是具体的哈希算法实现细节,结合之前提到的内存结构参数,了解如何设计一个高效、低冲突的哈希系统。需要分步骤解释,可能包括哈希函数的选择、冲突解决策略、Bank结构的结合方式等。</think>### 关于“具体哈希算法”的解析 哈希算法的核心目标是将任意输入(键)快速映射到固定范围的输出(哈希值),同时最小化冲突。以下是常见哈希算法的分类、原理及典型实现: --- #### **1. 哈希算法的核心类型** | 类型 | 特点 | 典型应用场景 | |----------------|----------------------------------------------------------------------|----------------------------| | **通用哈希** | 通过随机化函数减少碰撞,适合未知数据分布 | 密码学、负载均衡 | | **加密哈希** | 抗碰撞、不可逆,输出长度固定(如SHA-256) | 数据完整性校验、区块链 | | **非加密哈希** | 快速计算,牺牲部分安全性换取性能 | 哈希表、缓存、数据分片 | --- #### **2. 常见非加密哈希算法** ##### **(1) 乘法哈希(Multiplicative Hashing)** - **公式**: $$ h(k) = \lfloor M \cdot (k \cdot A \mod 1) \rfloor $$ - $A$为常数(通常取黄金分割比例 $A \approx 0.618$),$M$为哈希表大小。 - **优点**:计算快,适合整数键。 - **缺点**:对键分布敏感,可能产生聚集。 ##### **(2) 多项式滚动哈希(Polynomial Rolling Hash)** - **公式**: $$ h(s) = \sum_{i=0}^{n-1} s[i] \cdot p^{n-1-i} \mod M $$ - $p$为质数(如31或257),$s$为字符串,$M$为哈希表大小。 - **优点**:适合字符串键,减少碰撞。 - **缺点**:长字符串计算较慢。 ##### **(3) MurmurHash** - **原理**:混合位操作(移位、异或、乘法),最终混淆结果。 ```c uint32_t murmur3_32(const uint8_t* key, size_t len, uint32_t seed) { uint32_t h = seed; for (size_t i = 0; i < len; i++) { h ^= key[i]; h *= 0x5bd1e995; h ^= h >> 15; } return h; } ``` - **优点**:速度快,分布均匀,抗碰撞强。 - **应用**:Redis、Cassandra等数据库。 ##### **(4) CRC(循环冗余校验)哈希** - **原理**:通过多项式除法生成校验和,硬件加速支持。 $$ \text{CRC}(k) = k \cdot x^r \mod G(x) $$ - $G(x)$为生成多项式(如CRC32用$0xEDB88320$)。 - **优点**:硬件高效,错误检测能力强。 - **缺点**:非加密安全,可能碰撞。 --- #### **3. 冲突解决策略** | 方法 | 原理 | 适用场景 | |-----------------|----------------------------------------------------------------------|----------------------------| | **链地址法** | 哈希表的每个槽位存储链表或树结构,冲突元素链接到同一槽位 | 通用场景,Java `HashMap` | | **开放寻址法** | 通过探测序列(线性探测、二次探测、双重哈希)寻找空槽位 | 内存紧凑型系统 | | **布谷鸟哈希** | 使用两个哈希函数,冲突时“踢出”旧元素到备用位置 | 高负载因子场景 | | **Robin Hood哈希** | 插入时平衡探测距离,将“富有的”长探测元素替换为“贫穷的”短探测元素 | 减少最坏情况访问时间 | --- #### **4. 针对特定硬件的优化设计(结合用户问题)** 若哈希算法需适配**512B交织、8 Bank、可变步长**的硬件架构,可能采用以下设计: ##### **(1) Bank-Aware哈希函数** - **目标**:将键均匀分散到8个Bank,避免Bank冲突。 - **公式**: $$ \text{Bank\_ID} = (\text{Hash}(k) \div \text{Block\_Size}) \mod \text{Num\_Banks} $$ - 例如:$\text{Block\_Size}=512$字节,$\text{Num\_Banks}=8$。 ##### **(2) 步长动态选择** - **原理**:在开放寻址中,使用不同步长(如512/576/640/768/1024)生成探测序列,确保跨Bank访问。 $$ \text{Next\_Address} = (\text{Current} + \text{Stride}_i) \mod N $$ - 步长需Bank数量和块大小互质,避免周期性冲突。 ##### **(3) 示例:GPU哈希表实现** 1. **哈希函数**:使用MurmurHash或CRC快速生成初始地址。 2. **Bank分配**:根据初始地址的块偏移计算Bank_ID。 3. **冲突处理**: - 若目标Bank已满,按步长列表跳转到其他Bank的块。 - 步长选择Bank数互质(如5128互质),确保遍历所有Bank。 --- #### **5. 性能优化关键点** - **对齐访问**:512B块内存总线宽度对齐,提升吞吐量。 - **局部性优化**:同一Bank内数据连续存储,利用缓存预取。 - **步长调参**:通过实验选择最佳步长列表(如512/576/640/768/1024),适配数据分布。 --- #### **6. 典型代码片段(简化版)** ```c // 假设:8 Banks,512B块,步长列表[512, 576, 640, 768, 1024] uint64_t hash_key(uint64_t key, int attempt) { uint64_t base_hash = murmur3_64(key); // 初始哈希 uint64_t stride = stride_list[attempt % 5]; // 循环选择步长 return (base_hash + attempt * stride) % TABLE_SIZE; } int get_bank_id(uint64_t address) { return (address / 512) % 8; // 512B块,8 Banks } ``` --- #### **7. 应用场景对比** | 场景 | 推荐算法 | 原因 | |-----------------|-------------------------------|----------------------------------------------------------------------| | **GPU哈希表** | Bank-Aware MurmurHash + 步长探测 | 利用Bank并行性,减少内存竞争 | | **分布式缓存** | 一致性哈希(如Ketama) | 数据分片均匀,节点增减影响小 | | **实时流处理** | CRC32 + 链地址法 | 硬件加速CRC计算,低延迟 | --- #### **8. 深入问题** - **如何选择步长?** 步长需Bank数互质(如$\gcd(\text{Stride}, 8 \times 512) = 1$),避免重复访问同一Bank。 示例:5128×512=4096的最大公约数为512,不互质;而5764096的公约数为64,仍不互质。需重新设计步长。 - **如何减少假共享(False Sharing)?** 每个Bank的块内数据按缓存行对齐(如64B),避免不同Bank数据共享同一缓存行。 --- 如需进一步探讨某类哈希算法的实现细节或数学证明,请提供具体方向!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值