【问题标题】:The total number of locks exceeds the lock table size总锁数超过锁表大小
【发布时间】:2011-10-17 14:00:59
【问题描述】:

我正在 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;

我读过更改配置文件以增加缓冲池大小会有所帮助,但这无济于事。有什么方法可以解决这个问题,作为临时解决方法还是永久修复?

编辑:更改了部分查询。不应该影响它,但我做了一个 find-replace all 并没有意识到它搞砸了。不影响提问。

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

【问题讨论】:

  • 你为什么要在子选择中分组?
  • 我觉得这很难理解。如果 t2.customernum = t1.customer 从 topThreetransit 中仅选择 customernum 是没有意义的。那么,SkusBought.typedesc 肯定是与第一列相同的客户代码吗?
  • t2 是 t1 中客户的子集。加入是为了摆脱 t1 中不在 t2 中的客户。 typedesc 的代码实际上是不正确的。再次,在实际的 sql 脚本中更改它,但不是在这里。 Typedesc 是另一列 transactiondata(全部三个)。我会修改它,使它正确且更有意义。
  • 我为一个常见问题写了一个古怪的插图here

标签: mysql sql


【解决方案1】:

来自MySQL documentation(我看到你已经读过):

1206 (ER_LOCK_TABLE_FULL)

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

如果增加 innodb_buffer_pool_size 没有帮助,那么只需按照粗体部分的指示,将您的 INSERT 分成 3 个。跳过 UNION 并进行 3 个 INSERT,每个都有一个 JOIN 到 topThreetransit 表。

【讨论】:

    【解决方案2】:

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

    见: 尝试使用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 Inc. 北美社区关系经理。

    【讨论】:

    • 我按照此答案中的说明进行操作,但没有任何改善。我正在运行 5.6 版。
    • 这个解决方案对我也不起作用,使用 Mysql 5.1。也许这不再适用于更新的版本?
    • 在我的情况下,隔离级别并不重要,我将其更改为 READ UNCOMMITED。这解决了锁的问题。但这取决于您的用例。将事务隔离级别设置为未提交;
    • 锁定该表似乎并不能阻止行被单独锁定。所以还是超出了锁表大小。
    【解决方案3】:

    如果您的表结构正确,每个表都包含相对唯一的值,那么执行此操作的强度较低的方法是执行 3 个单独的插入到语句,每个表 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 语句可能会很昂贵,因为它会首先创建自己的临时表来填充来自三个独立源的数据,然后再插入您希望 ALONG 运行的表中另一个用于过滤结果的子查询。

    【讨论】:

      【解决方案4】:

      可以通过为 MySQL 变量 innodb_buffer_pool_size 设置更高的值来解决此问题。 innodb_buffer_pool_size 的默认值为 8,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重启

      参考The total number of locks exceeds the lock table size

      【讨论】:

      • 诀窍是找到并修改正确的my.cnf文件。在我的 Mac 上,我至少有 20 个具有此名称的文件,这些文件与以前的版本、操作系统等相比已过时。按照建议修改后对我有用的 my.cnf 文件是:/usr/local/mysql-5.6.23-osx10。 8-x86_64/my.cnf
      • 在 Wamp 中是 my.ini
      • 请注意,这些天的最小值是 128MB,所以 64MB 行不通,您需要提供大于 128MB 的空间
      • 从 MySQL 5.7.5 开始,您可以简单地运行 SET GLOBAL innodb_buffer_pool_size=268435456; 而无需搜索 my.cnf 文件并重新启动 mysql。 stackoverflow.com/a/38333056/3553564
      【解决方案5】:

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

      C:\ProgramData\MySQL\MySQL Server 5.7
      

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

      【讨论】:

        【解决方案6】:

        值得一提的是,用于此设置的数字以字节为单位 - 很难找到!

        【讨论】:

        • 这似乎是评论而不是答案。你能把它改成评论吗?
        【解决方案7】:

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

        然后在文件“my.ini”中按control+F,找到buffer_pool_size。 将值设置得更高,我会推荐 64 MB(默认为 8 MB)。

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

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

        【讨论】:

          【解决方案8】:

          首先,可以使用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/ 下。

          【讨论】:

            【解决方案9】:

            下面的这个答案并没有直接回答 OP 的问题。然而, 我在这里添加这个答案是因为这个页面是第一个结果 你谷歌“锁的总数超过了锁表的大小”。


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

            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”列。

            希望这对某人有所帮助!

            【讨论】:

              【解决方案10】:

              修复错误码1206:锁数超过锁表大小。

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

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

              要找到此文件,请转到菜单 Server/Server 状态(在 MySQL Workbench 中)并查看 Server Directories/ Base Directory

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

              [mysqld]
              innodb_buffer_pool_size=4G

              buffer_pool 文件的大小取决于你的具体机器,在大多数情况下,2G 或 4G 可以解决问题。

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

              希望对你有帮助!

              【讨论】:

                【解决方案11】:

                在运行 sql 脚本时,我的 MYSQL 遇到了同样的问题请查看下图。 Error code 1206: The number of locks exceeds the lock table size Picture

                这是 Mysql 配置问题,所以我在my.ini 中进行了一些更改 它正在我的系统上运行,问题已解决。

                我们需要在my.ini 中进行一些更改,这可以通过以下Path:- 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 Service Picture

                【讨论】:

                  【解决方案12】:

                  美好的一天,

                  我在尝试从 MySQL 表中删除数百万行时遇到了同样的错误。

                  我的解决方案与更改 MySQL 的配置文件无关,而只是通过指定每个事务的最大 id 来减少我的目标行数。我建议不要针对每笔交易的所有行,而是针对每笔交易的部分。完成这项工作可能需要更多的事务,但至少你会得到其他地方,而不是试图摆弄 MySQL 配置。

                  例子:

                  delete from myTable where groupKey in ('any1', 'any2') and id < 400000;
                  

                  而不是

                  delete from myTable where groupKey in ('any1', 'any2');
                  

                  查询可能仍会通过使用 groupBy 和 orderBy 子句进行优化。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-05-12
                    相关资源
                    最近更新 更多