【问题标题】:MySQL - Executing intensive queries on live serverMySQL - 在实时服务器上执行密集查询
【发布时间】:2011-09-01 22:19:05
【问题描述】:

我在处理 MySQL 数据库中更新和插入数百万行时遇到了一些问题。我需要在表 A 中标记 5000 万行,将标记的 5000 万行中的一些数据插入表 B,然后再次更新表 A 中相同的 5000 万行。表 A 大约有 1.3 亿行,表 B 大约有 8000 万行。

这需要在实时服务器上进行,而不会拒绝访问来自网站的其他查询。问题是当这个存储过程运行时,来自网站的其他查询最终被锁定并且 HTTP 请求超时。

以下是 SP 的要点,为了便于说明而稍微简化:

CREATE DEFINER=`user`@`localhost` PROCEDURE `MyProcedure`(  
  totalLimit  int
)
BEGIN
  SET @totalLimit = totalLimit; 
  /* Prepare new rows to be issued */
  PREPARE STMT FROM 'UPDATE tableA SET `status` = "Being-Issued" WHERE `status` = "Available" LIMIT ?';
  EXECUTE STMT USING @totalLimit;
  /* Insert new rows for usage into tableB */
  INSERT INTO tableB (/* my fields */)
    SELECT /* some values from TableA */ 
    FROM tableA
    WHERE `status` = "Being-Issued";
  /* Set rows as being issued */
  UPDATE tableB SET `status` = 'Issued' WHERE `status` = 'Being-Issued';
END$$

DELIMITER ;

【问题讨论】:

  • 这些表使用哪个数据库引擎?
  • status 也可以变成tinyintchar(1)。更新长字符字段比较慢。
  • 这些是 MyISAM 表,status 字段实际上是一个具有 3 个可能值的枚举

标签: mysql database large-data myisam large-data-volumes


【解决方案1】:

不管你在做什么,处理 50M 行 3 次都会很慢。

确保您的更新影响较小的、不相交的集合。并一个一个地执行它们,而不是在同一个事务中执行它们。

如果您已经这样做并且 MySQL 行为不端,请尝试对您的代码进行微调:

create a temporary table

begin

insert into tmp_table
select your stuff
limit ?
for update

do your update on A using tmp_table

commit

begin
do your insert on B using tmp_table
do your update on A using tmp_table
commit

这应该使锁定保持最短时间。

【讨论】:

  • 将其分解为更小的进程是有意义的。我实际上创建了一个存储过程,该存储过程在较小的卡盘中多次运行另一个存储过程,它似乎工作得更好(即:为 10,000 个块运行原始存储过程,直到总共达到 5,000,000 个。)似乎是成功的保持锁的时间更短,并允许处理其他查询。
  • “我实际上创建了一个存储过程,它在较小的卡盘中多次运行另一个”——但这会在一个事务中运行整个事情,不是吗? (这正是我的观点......)
  • 嗯,确实有道理。我主要关心的是锁定所有内容,以使网站无法使用数据库。你能看看我发布的答案,让我知道你的想法吗?它似乎工作得很好,但你是对的......因为它仍然在一个存储过程中,它会在一个事务中,但它似乎并没有保持对表的锁定(这对我有好处) .非常感谢您的帮助!!
  • 坦率地说,我不确定 MySQL 将如何处理它。它可能会起作用。我只知道一个事实,当以这种方式处理时,Postgres 将在一个事务中处理整个事情,并相应地锁定事情。 (出于充分的理由,他们还没有实现存储过程——尤其是异步事务。)
【解决方案2】:

这个呢?它基本上循环调用原始存储过程,直到达到所需的总量,并且在调用之间有一个休眠期(如 2 秒)以允许处理其他查询。

increment 是一次要做的数量(在这种情况下使用 10,000)
totalLimit 是要处理的总量
sleepSec 是调用之间的休息时间
/p>

BEGIN
SET @x = 0;
REPEAT
    SELECT SLEEP(sleepSec);
    SET @x = @x + increment;
    CALL OriginalProcedure( increment );

    UNTIL @x >= totalLimit
END REPEAT;
END$$

显然,它可以使用一些数学方法来确保增量不会超过总限制,如果它不能均匀整除,但它似乎可以工作(我的意思是允许其他查询仍然从网络请求中处理) ,并且总体上似乎也更快。

这里有什么见解吗?这是一个好主意吗?坏主意?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-11
    • 1970-01-01
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    相关资源
    最近更新 更多