【发布时间】:2017-10-29 05:13:28
【问题描述】:
我在使用这样创建的 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 - Lock wait timeout exceeded; try restarting transaction”
该表中的记录永远不会超过 120 条,但它会受到 SELECT、UPDATE 和 DELETE 语句的严重影响。非常基本的查询主要在 tableID 上进行过滤,但在一些选择语句中连接到少于 2,000 条记录的其他表。我已经测试了所有的选择查询,它们的执行时间不到 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 次选择和一秒钟内发生的插入。 INSERT 是由于锁定等待超时而失败的事务。所以我的问题是,我能做些什么呢?我试过重新配置服务器,重新安装 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
这是用于调用查询的 C++ 代码:
* Executes a query. *
int32 Sql_Query(Sql_t* self, const char* query, ...)
{
int32 res;
va_list args;
va_start(args, query);
res = Sql_QueryV(self, query, args);
va_end(args);
return res;
}
* Executes a query. *
int32 Sql_QueryV(Sql_t* self, const char* query, va_list args)
{
if( self == NULL )
return SQL_ERROR;
Sql_FreeResult(self);
StringBuf_Clear(&self->buf);
StringBuf_Vprintf(&self->buf, query, args);
if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (uint32)StringBuf_Length(&self->buf)) )
{
ShowSQL("DB error - %s\n", mysql_error(&self->handle));
ShowSQL("Query: %s\n", StringBuf_Value(&self->buf));
return SQL_ERROR;
}
self->result = mysql_store_result(&self->handle);
if( mysql_errno(&self->handle) != 0 )
{
ShowSQL("DB error - %s\n", mysql_error(&self->handle));
ShowSQL("Query: %s\n", StringBuf_Value(&self->buf));
return SQL_ERROR;
}
return SQL_SUCCESS;
}
int STDCALL mysql_real_query(MYSQL *mysql, const char *q,
unsigned int length);
MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql);
【问题讨论】:
-
我删除了“死锁”标签。锁等待不是死锁。
-
您真的需要 2 个唯一键吗?这可能会使所涉及的锁数量增加一倍。
-
看看并发
SELECTs. -
添加了查询日志,所有选择在 +- 等待超时内命中表。我注意到我在