锁总数超过锁表大小


76

我正在MySQL中运行报告。查询之一涉及将大量行插入到临时表中。当我尝试运行它时,出现以下错误:

错误代码1206:锁的数量超过了锁表的大小。

有问题的查询是:

create temporary table SkusBought(
customerNum int(11),
sku int(11),
typedesc char(25),
key `customerNum` (customerNum)
)ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into skusBought
select t1.* from
    (select customer, sku, typedesc from transactiondatatransit
    where (cat = 150 or cat = 151)
    AND daysfrom07jan1 > 731
group by customer, sku
union
select customer, sku, typedesc from transactiondatadelaware
    where (cat = 150 or cat = 151)
    AND daysfrom07jan1 > 731
group by customer, sku
union
select customer, sku, typedesc from transactiondataprestige
    where (cat = 150 or cat = 151)
    AND daysfrom07jan1 > 731
group by customer, sku) t1
join
(select customernum from topThreetransit group by customernum) t2
on t1.customer = t2.customernum;

我读过更改配置文件以增加缓冲池大小会有所帮助,但这无济于事。作为临时替代方法或永久修复程序,将以何种方式解决此问题?

编辑:更改查询的一部分。不应该影响它,但是我做了一个查找替换所有操作,却没有意识到它把它搞砸了。不会影响这个问题。

编辑2:将typedesc添加到t1。我在查询中更改了它,但在这里没有更改。


为什么要对子选择分组?
蒂姆(Tim)

我觉得这很难理解。如果t2.customernum = t1.customer,则从topThreetransit中仅选择customernum是没有意义的。当然,SkusBought.typedesc是否与第一列的客户代码相同?
RedGrittyBrick 2011年

t2是t1中的客户子集。加入是为了摆脱t1中没有的客户。typedesc的代码实际上是错误的。再次,在实际的sql脚本中更改它,但不在此处更改。Typedesc是transactiondata的另一列(它们全部三个)。我将其更改为正确,这更有意义。
maxman92 2011年


Answers:


66

可以通过为MySQL变量设置更高的值来解决此问题innodb_buffer_pool_size。作为默认值innodb_buffer_pool_size8,388,608

要更改设置值,innodb_buffer_pool_size请参见以下设置。

  1. my.cnf从服务器中找到文件。对于Linux服务器,大多数情况下/etc/my.cnf
  2. 将行添加innodb_buffer_pool_size=64MB到此文件
  3. 重启MySQL服务器

要重新启动MySQL服务器,可以使用以下2个选项之一:

  1. 服务mysqld重启
  2. /etc/init.d/mysqld重新启动

参考锁的总数超过了锁表的大小


1
诀窍是找到并修改正确的my.cnf文件。在我的Mac上,我至少有20个具有此名称的文件,该文件比以前的版本,操作系统等更旧。按建议修改后对我有用的my.cnf文件是:/usr/local/mysql-5.6.23-osx10。 8-x86_64 / my.cnf
Toren

4
在Wamp是my.ini
Enrique

请注意,这些天的最小值为128MB,因此64MB将无法工作,您需要提供大于128MB的内容
Noam Rathaus

3
从MySQL 5.7.5开始,您可以运行SET GLOBAL innodb_buffer_pool_size=268435456;而无需搜索my.cnf文件并重新启动mysql。stackoverflow.com/a/38333056/3553564
Klim

23

我找到了另一种解决方法-使用表锁。当然,它可能不适合您的应用程序-如果您需要同时更新表。

请参阅:尝试LOCK TABLES用于锁定整个表,而不是InnoDB的MVCC行级锁定的默认操作。如果我没记错的话,“锁定表”是指InnoDB内部结构,用于存储MVCC实现的行和版本标识符,其中一点用于标识正在修改的行,而表中包含6000万行,可能超出分配给它的内存。该LOCK TABLES命令应通过设置表级锁而不是行级锁来缓解此问题:

SET @@AUTOCOMMIT=0;
LOCK TABLES avgvol WRITE, volume READ;
INSERT INTO avgvol(date,vol)
SELECT date,avg(vol) FROM volume
GROUP BY date;
UNLOCK TABLES;

MySQL公司北美地区社区关系经理Jay Pipes


我按照此答案中的说明进行操作,一点也没有改善。我正在运行5.6版。
mbmast'2

7
使用Mysql 5.1,该解决方案对我也不起作用。也许这不再适用于最新版本?
法郎

1
就我而言,隔离级别并不重要,我将其更改为“ READ UNCOMMITED”。那解决了锁的问题。但这取决于您的用例。设置事务隔离级别未提交;
itstata '17

14

MySQL文档中(您已经阅读过):

1206(ER_LOCK_TABLE_FULL)

锁的总数超过了锁表的大小。为避免此错误,请增加innodb_buffer_pool_size的值。在单个应用程序中,一种解决方法可能是将大型操作分解为较小的部分。例如,如果大型INSERT发生错误,请执行几个较小的INSERT操作。

如果增加innodb_buffer_pool_size没有帮助,则只需按照粗体部分的指示将INSERT分成3个即可。跳过UNION并制作3个INSERT,每个INSERT都插入topThreetransit表中。


5

首先,您可以使用sql命令show global variables like 'innodb_buffer%';检查缓冲区大小。

解决方法是找到您的my.cnf文件并添加,

[mysqld]
innodb_buffer_pool_size=1G # depends on your data and machine

不要忘记添加[mysqld],否则它将无法正常工作。

在我的情况下,Ubuntu的16.04my.cnf位于文件夹下/etc/mysql/


4

我正在使用MySQL工作台运行MySQL Windows。转到“服务器”>“服务器状态”,在顶部显示配置文件:“路径”(C:\ProgramData\MySQL\...\my.ini

然后在文件“ my.ini”中按Ctrl + F并查找buffer_pool_size。将该值设置得更高,我建议使用64 MB(默认值为8 MB)。

通过转到“实例”>“启动/关闭”>“停止服务器”来重新启动服务器(然后再重新启动服务器)

就我而言,我无法从表中删除条目。


2

如果您已经正确地构造了表,以使每个表都包含相对唯一的值,那么执行此操作的强度较小的方法是执行3个单独的insert-into语句,每个表1个,并为每个插入使用适当的连接过滤器-

INSERT INTO SkusBought...

SELECT t1.customer, t1.SKU, t1.TypeDesc
FROM transactiondatatransit AS T1
LEFT OUTER JOIN topThreetransit AS T2
ON t1.customer = t2.customernum
WHERE T2.customernum IS NOT NULL

对其他两个表重复此操作-复制/粘贴是一种很好的方法,只需更改FROM表名即可。**如果您要防止SkusBought表中的条目重复,则可以在WHERE子句之前的每个部分中添加以下联接代码。

LEFT OUTER JOIN SkusBought AS T3
ON  t1.customer = t3.customer
AND t1.sku = t3.sku

-然后是WHERE子句的最后一行-

AND t3.customer IS NULL

您的初始代码使用了许多子查询,并且UNION语句可能会很昂贵,因为它会先创建自己的临时表来填充来自三个单独来源的数据,然后再将您要插入的表插入运行另一个子目录的表中查询以过滤结果。


2

在Windows中:如果您有mysql工作台。转到服务器状态。在我的情况下,找到正在运行的服务器文件的位置是:

C:\ProgramData\MySQL\MySQL Server 5.7

打开my.ini文件,然后找到buffer_pool_size。将该值设置为高。默认值为8M。这就是我解决这个问题的方法


1

修复错误代码1206:锁的数量超过了锁表的大小。

就我而言,我使用在Windows上使用WampServer 2.5运行的MySQL Workbench(5.6.17)。

对于Windows / WampServer,您必须编辑my.ini文件(而不是my.cnf文件)

要找到此文件,请转到菜单服务器/服务器状态(在MySQL Workbench中),然后在服务器目录/基本目录下查找

MySQL服务器-服务器状态

在my.ini文件中,为不同的设置定义了部分,查找[mysqld]部分(如果不存在则创建它)并添加命令: innodb_buffer_pool_size = 4G

[mysqld]
innodb_buffer_pool_size = 4G

buffer_pool文件的大小取决于您的特定计算机,在大多数情况下,2G或4G将解决该问题。

记住要重新启动服务器,以便采用新配置,它可以为我解决问题。

希望能帮助到你!


1

同样的问题,我在运行sql脚本时遇到了我的MYSQL,请查看下图。 错误代码1206:锁的数量超出了锁表的大小图片

这是Mysql配置问题,因此我进行了一些更改,my.ini 并且该问题在我的系统上有效并且已解决。

我们需要对 my.ini其中包括以下 路径:- C:\ProgramData\MySQL\MySQL Server 5.7\my.ini ,请更新my.ini配置文件字段中的以下更改:-

key_buffer_size=64M
read_buffer_size=64M
read_rnd_buffer_size=128M
innodb_log_buffer_size=10M
innodb_buffer_pool_size=256M
query_cache_type=2
max_allowed_packet=16M

完成上述所有更改后,请重新启动MYSQL服务。请参考图片:-Microsoft MYSQL服务图片


0

值得一提的是,用于此设置的数字在BYTES中-很难找到!


2
这似乎是评论而不是答案。您可以将其更改为评论吗?
Ben Ootjers '19

0

下面的答案不能直接回答OP的问题。但是,我在此处添加此答案,因为当您使用Google“锁的总数超过锁表的大小”时,此页面是第一个结果。


如果您正在运行的查询正在分析跨越数百万行的整个表,则可以尝试使用while循环,而不用更改配置中的限制。

一会儿看起来会把它弄成碎片。下面是循环遍历DATETIME的索引列的示例。

# Drop
DROP TABLE IF EXISTS
new_table;

# Create (we will add keys later)
CREATE TABLE
new_table
(
    num INT(11),
    row_id VARCHAR(255),
    row_value VARCHAR(255),
    row_date DATETIME
);

# Change the delimimter
DELIMITER //

# Create procedure
CREATE PROCEDURE do_repeat(IN current_loop_date DATETIME)
BEGIN

    # Loops WEEK by WEEK until NOW(). Change WEEK to something shorter like DAY if you still get the lock errors like.
    WHILE current_loop_date <= NOW() DO

        # Do something
        INSERT INTO
            user_behavior_search_tagged_keyword_statistics_with_type
            (
                num,
                row_id,
                row_value,
                row_date
            )
        SELECT
            # Do something interesting here
            num,
            row_id,
            row_value,
            row_date
        FROM
            old_table
        WHERE
            row_date >= current_loop_date AND
            row_date < current_loop_date + INTERVAL 1 WEEK;

        # Increment
        SET current_loop_date = current_loop_date + INTERVAL 1 WEEK;

    END WHILE;

END//

# Run
CALL do_repeat('2017-01-01');

# Cleanup
DROP PROCEDURE IF EXISTS do_repeat//

# Change the delimimter back
DELIMITER ;

# Add keys
ALTER TABLE
    new_table
MODIFY COLUMN
    num int(11) NOT NULL,
ADD PRIMARY KEY
    (num),
ADD KEY
    row_id (row_id) USING BTREE,
ADD KEY
    row_date (row_date) USING BTREE;

如果表不使用日期,也可以对其进行调整以使其遍历“ num”列。

希望这对某人有帮助!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.