MySQL InnoDB表的常量锁等待超时

MySQL InnoDB表的常量锁等待超时,mysql,locking,innodb,wait,Mysql,Locking,Innodb,Wait,我在创建MySQL InnoDB表时遇到了可怕的锁等待超时问题,如下所示: CREATE TABLE `TableX` ( `colID` int(10) unsigned NOT NULL DEFAULT '0', `colFK` int(10) unsigned NOT NULL DEFAULT '0', `colX` smallint(5) unsigned NOT NULL DEFAULT '0', `colX` int(10) unsigned NOT NULL DEF

我在创建MySQL InnoDB表时遇到了可怕的锁等待超时问题,如下所示:

CREATE TABLE `TableX` (
  `colID` int(10) unsigned NOT NULL DEFAULT '0',
  `colFK` int(10) unsigned NOT NULL DEFAULT '0',
  `colX` smallint(5) unsigned NOT NULL DEFAULT '0',
  `colX` int(10) unsigned NOT NULL DEFAULT '0',
  `colX` smallint(5) unsigned NOT NULL DEFAULT '0',
  `colX` int(10) unsigned NOT NULL DEFAULT '0',
  `colX` smallint(5) unsigned NOT NULL DEFAULT '0',
  `colX` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
  `colX` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
  `colX` smallint(5) unsigned NOT NULL DEFAULT '0',
  `colX` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
  `colX` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`colFK`),
  UNIQUE KEY `colID` (`colID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
错误如下:[Err]1205-超过锁定等待超时;尝试重新启动事务

此表中的记录从未超过120条,但它会受到SELECT、UPDATE和DELETE语句的严重影响。非常基本的查询主要是在tableID上进行过滤,但在一些select语句中会连接到其他记录少于2000条的表。我已经测试了所有select查询,它们执行起来不到100-200毫秒

当问题发生时,InnoDB Status返回以下信息:

---TRANSACTION 2605217, ACTIVE 1 sec inserting
 mysql tables in use 1, locked 1
 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
 MySQL thread id 11826, OS thread handle 4104, query id 1940531 xxxx xxxxx xxxx update
 INSERT INTO TableX(cols) VALUES(values)
 ------- TRX HAS BEEN WAITING 1 SEC FOR THIS LOCK TO BE GRANTED:
 RECORD LOCKS space id 227 page no 3 n bits 168 index PRIMARY of table `TableX` trx id 2605217 lock mode S locks rec but not gap waiting
 Record lock, heap no 97 PHYSICAL RECORD: n_fields 14; compact format; info bits 32
常规查询日志显示4次选择和一秒钟内发生的插入。插入是由于锁定等待超时而失败的事务。所以我的问题是,我能做些什么?我尝试过重新配置服务器,重新安装MySQL,更改事务级别

很抱歉,如果格式设置关闭,我无法将创建表放入代码块。请随意编辑我的帖子或要求提供更多需要的信息。谢谢

编辑:添加常规查询日志+-等待超时

2017-05-02T02:06:26.443095Z 12195 Query SELECT SQL_BUFFER_RESULT * FROM TableX LEFT JOIN TableY USING (ColA) LEFT JOIN TableA USING (ColA) LEFT JOIN TableZ USING (ColA) LEFT JOIN TableH USING (ColA) LEFT JOIN TableI USING(ColA) WHERE UnindexedCol IS NOT NULL AND UnindexedColB <= 0  ORDER BY UnindexedCol ASC
2017-05-02T02:06:26.708769Z 11829 Query SELECT * FROM TableX LEFT JOIN TableA ON TableX.ColA = TableA.ColA WHERE UnindexedCol = 'text' LIMIT 1
2017-05-02T02:06:27.021306Z 11826 Query SELECT * FROM TableX WHERE IDColA = 1000
2017-05-02T02:06:27.068185Z 11826 Query INSERT INTO TableX(cols) VALUES(values)
2017-05-02T02:06:27.224393Z 11829 Query SELECT colList, MIN(ColA) FROM TableX JOIN TableY USING (ColA) WHERE IF (IDColE <> 0, IDColE = (SELECT MAX(IDColE) FROM TableY WHERE IDColF = 22073), IDColF = 22073) GROUP BY UnIndexedColS, UnIndexedColT
2017-05-02T02:06:27.224393Z  1697 Query Show engine innodb status
2017-05-02T02:06:27.224393Z  1696 Query SELECT st.* FROM performance_schema.events_statements_current st JOIN performance_schema.threads thr ON thr.thread_id = st.thread_id WHERE thr.processlist_id = 1697
2017-05-02T02:06:27.224393Z  1696 Query SELECT st.* FROM performance_schema.events_stages_history_long st WHERE st.nesting_event_id = 211
2017-05-02T02:06:27.224393Z  1696 Query SELECT st.* FROM performance_schema.events_waits_history_long st WHERE st.nesting_event_id = 211
2017-05-02T02:06:28.224501Z 11829 Query SELECT ColList FROM TableX WHERE UnIndexedCol = 2 OR UnIndexedCol = 2 GROUP BY ColList

你能做的最好的事情就是迅速完成你的交易

锁定持续时间与查询执行速度无关。这是关于锁保持多久的问题。锁定一直保持到事务提交或回滚

例如,如果会话1执行以下操作:

START TRANSACTION;
UPDATE TableX SET colX = 1234 WHERE colID >= 5678;
此事务将对colID>5678的所有行(包括末尾的间隙)持有锁。这通常是块插入的内容

请参阅以了解有关间隙锁的一些信息

您可以通过将事务隔离级别设置为READ COMMITTED来避免大多数间隙锁,但要确保这在逻辑方面符合应用程序的需要

您还可以通过在代码执行任何需要无限时间的操作之前解析事务来解决此问题。我指的是伪代码:

start transaction;
do some sql query that acquires locks;
post data to a web service that takes 500ms to respond;
commit;
上述操作将不必要地保持锁半秒钟。如果有一打同时运行,最后一个将等待>6秒,因为它必须等待所有在它之前排队的人。如果你有更多,他们会等更长时间

最好这样做:

start transaction;
do some sql query that acquires locks;
commit;
post data to a web service that takes 500ms to respond;
这是你的评论

每个语句都使用一个事务。如果不显式控制事务的开始和结束,则可能使用autocommit,其中每个语句隐式启动事务,并在语句执行完毕后立即提交事务。因此,可能您的SQL语句花费的时间太长了

另一个想法是:您的SQL查询使用对未索引列的搜索。我在示例表中看到,colID作为PK,colFK作为外键,它总是被索引。如果您搜索的是任何其他列,它必须进行表扫描才能进行搜索,这意味着它会锁定所检查的每一行。如果您使用索引来帮助您的搜索,它还将最小化需要锁定的行数,这将大大有助于并发更新

<>您更新查询和C++代码。

INSERT导致锁定,但它们的范围应较小且简短。我们在ShowEngine INNODB状态中看到,INSERT正在等待访问表的主键。所以其他线程必须将其锁定

当您看到锁定问题时,可以查询信息\u SCHEMA.INNODB\u LOCK\u WAITS表,查看哪些事务正在等待,哪些事务正在使它们等待,即阻塞。只有在锁定等待仍在等待时进行查询时,此操作才有效。看

大多数查询都是SELECT语句,它们是非锁定SELECT。如果您只是同时执行INSERT/UPDATE/DELETE,那么在使用InnoDB表时,这些类型的查询不会等待锁定。它们也不会阻止其他线程

如果您正在执行ALTERTABLE或使用显式语句,SELECT可以等待或阻止元数据锁。但你没有提到你正在做这两件事

SELECT有选项要执行,但在显示的SELECT语句中不显示这些选项中的任何一个

另外,请仔细检查配置选项的值,然后单击链接阅读更多有关此内容的信息。默认值为50秒,但如果有人将其设置为非常小的值(如0),则可能会导致虚假超时

mysql> SHOW GLOBAL VARIABLE LIKE 'innodb_lock_wait_timeout';

你能做的最好的事情就是迅速完成你的交易

锁定持续时间与查询执行速度无关。这是关于锁保持多久的问题。锁定一直保持到事务提交或回滚

例如,如果会话1执行以下操作:

START TRANSACTION;
UPDATE TableX SET colX = 1234 WHERE colID >= 5678;
此事务将对colID>5678的所有行(包括末尾的间隙)持有锁。这通常是块插入的内容

请参阅以了解有关间隙锁的一些信息

通过将事务隔离级别设置为读取C,可以避免大多数间隙锁 OMMITTED,但请确保这在逻辑上符合应用程序的需要

您还可以通过在代码执行任何需要无限时间的操作之前解析事务来解决此问题。我指的是伪代码:

start transaction;
do some sql query that acquires locks;
post data to a web service that takes 500ms to respond;
commit;
上述操作将不必要地保持锁半秒钟。如果有一打同时运行,最后一个将等待>6秒,因为它必须等待所有在它之前排队的人。如果你有更多,他们会等更长时间

最好这样做:

start transaction;
do some sql query that acquires locks;
commit;
post data to a web service that takes 500ms to respond;
这是你的评论

每个语句都使用一个事务。如果不显式控制事务的开始和结束,则可能使用autocommit,其中每个语句隐式启动事务,并在语句执行完毕后立即提交事务。因此,可能您的SQL语句花费的时间太长了

另一个想法是:您的SQL查询使用对未索引列的搜索。我在示例表中看到,colID作为PK,colFK作为外键,它总是被索引。如果您搜索的是任何其他列,它必须进行表扫描才能进行搜索,这意味着它会锁定所检查的每一行。如果您使用索引来帮助您的搜索,它还将最小化需要锁定的行数,这将大大有助于并发更新

<>您更新查询和C++代码。

INSERT导致锁定,但它们的范围应较小且简短。我们在ShowEngine INNODB状态中看到,INSERT正在等待访问表的主键。所以其他线程必须将其锁定

当您看到锁定问题时,可以查询信息\u SCHEMA.INNODB\u LOCK\u WAITS表,查看哪些事务正在等待,哪些事务正在使它们等待,即阻塞。只有在锁定等待仍在等待时进行查询时,此操作才有效。看

大多数查询都是SELECT语句,它们是非锁定SELECT。如果您只是同时执行INSERT/UPDATE/DELETE,那么在使用InnoDB表时,这些类型的查询不会等待锁定。它们也不会阻止其他线程

如果您正在执行ALTERTABLE或使用显式语句,SELECT可以等待或阻止元数据锁。但你没有提到你正在做这两件事

SELECT有选项要执行,但在显示的SELECT语句中不显示这些选项中的任何一个

另外,请仔细检查配置选项的值,然后单击链接阅读更多有关此内容的信息。默认值为50秒,但如果有人将其设置为非常小的值(如0),则可能会导致虚假超时

mysql> SHOW GLOBAL VARIABLE LIKE 'innodb_lock_wait_timeout';

我去掉了“死锁”标签。锁等待不是死锁。您真的需要两个唯一的密钥吗?这可能会使所涉及的锁的数量增加一倍。让我们看看并发选择。添加了查询日志,所有选择都在+-等待超时内命中表。我注意到我有一个过滤器,我去掉了“死锁”标签。锁等待不是死锁。您真的需要两个唯一的密钥吗?这可能会使所涉及的锁的数量增加一倍。让我们看看并发选择。添加了查询日志,所有选择都在+-等待超时内命中表。我注意到我在Hi Bill上有一个过滤器,谢谢回复并帮助处理标签!我并没有在我的查询中指定开始/提交事务(只是脏的更新/插入),这些来自多个C++应用程序。我查看了我的SQL包装器,也没有看到任何与事务相关的内容。数据库级别的“tx_isolation”变量设置为“REPEATABLE-READ”,因此我将其更改为“READ-COMMITTED”,并将报告。此外,我的UPDATE语句是使用equal语句运行的,而不是针对范围运行的,如果这有什么区别的话?我的意思是equals语句是=符号过滤器。例如:更新TabLeX设置COLX=值,其中COLID = XXXI希望您不使用AutoPixy.0.AutoMebug,我将尝试在已过滤的列上创建索引,如果这不起作用,我将发布我的C++代码。谢谢你的建议!我添加了C++代码,因为我仍然有问题。开始使用innotop进行尝试和诊断,但到目前为止运气不佳。如果还需要什么,请告诉我。另外,如果它不可能解决这个问题,你知道一个好的MSSQL C++库吗?谢谢嗨,比尔,谢谢你回复和帮助标签!我并没有在我的查询中指定开始/提交事务(只是脏的更新/插入),这些来自多个C++应用程序。我查看了我的SQL包装器,也没有看到任何与事务相关的内容。数据库级别的“tx_isolation”变量设置为“REPEATABLE-READ”,因此我将其更改为“READ-COMMITTED”,并将报告。此外,我的UPDATE语句是使用equal语句运行的,而不是针对范围运行的,如果这有什么区别的话?我的意思是equals语句是=符号过滤器。例如:UPDATE TableX SET ColX=value,其中ColID=xxxI希望您没有使用autocommit=0。autocommit处于启用状态,
我将尝试在过滤的列上创建索引,如果没有帮助,我会发布我的C++代码。谢谢你的建议!我添加了C++代码,因为我仍然有问题。开始使用innotop进行尝试和诊断,但到目前为止运气不佳。如果还需要什么,请告诉我。另外,如果它不可能解决这个问题,你知道一个好的MSSQL C++库吗?谢谢