【问题标题】:Select Query Blocks read/write/update operations on transactional table在事务表上选择查询块读/写/更新操作
【发布时间】:2023-03-19 14:43:01
【问题描述】:

我有一个 Select 查询,它在具有超过 400 万条记录的事务表上执行。每当我执行此查询时,我观察到该特定事务表上的所有写入和更新操作都已挂起,并且我们开始从 Java 端获取超过锁定等待超时的异常,请尝试重新启动事务。当前锁定等待超时设置为 200 秒。我无法理解为什么 select 语句可以在表上创建这样的锁并阻止所有插入/更新语句。 表存储引擎是 InnoDb,主键是自增键。 MySQL 版本是 5.1.40。执行此查询时我没有开始任何事务

有什么想法吗?

这里是查询

选择 cd.acc_id accId, cast(cd.ci_time 作为日期) trdate, 合并(cd.cnumber,替换(cd.executer_id,'+',''))作为 cno, 案子 WHEN coalesce(cd.language, 'English') = 'English' 那么 1 其他 2 END 作为语言, 案子 当 cd.cnumber 为空时 然后'N' 否则'Y' 结尾 iscno, 替换(cd.execute_id,'+','')执行者_id, 计数(*) trcount, 总和(合并 ( ( 选择 计数(DISTINCT distribution_log.dist_id) 从 分布日志, distribution_log_detail 在哪里 distribution_log.distribution_log_id = distribution_log_detail.distribution_log_id AND distribution_log_detail.service_id 不在('P1','P3') 和 distribution_log.state_id = '注册' AND distribution_log.dist_id = cd.dist_id ) , 0 ) ) accAbandonduring, 和( 案子 何时合并 ( ( 选择 计数(DISTINCT distribution_log.dist_id) 从 分布日志 在哪里 分布日志.acc_id = 58 AND distribution_log.dist_id = cd.dist_id 和 distribution_log.state_id = '注册' ) ,0 )

0 那么 0 其他 1 结尾 ) accAbandonedbe, 总和(合并 ( ( 选择 数数(*) 从 cq_detail cqd 在哪里 cqd.dist_id = cd.dist_id ) ,0 ) ) 作为 opted_for_csr, 总和(合并 ( ( 选择 计数(DISTINCT cqd.dist_id) 从 cq_detail cqd, ca_detail cad 在哪里 cqd.dist_id = cd.dist_id AND cad.dist_id = cd.dist_id 通过...分组 cqd.dist_id 拥有 总和(cad.agent_answered_flag)> 0 ) ,0 ) ) 作为 csr_trs_ans, 总和(合并 ( ( 选择 计数(DISTINCT cqd.dist_id) 从 cq_detail cqd 在哪里 cqd.dist_id = cd.dist_id AND FAnswer(cqd.dist_id) = 0 AND time_to_sec(timediff(cqd.cq_end_time, cqd.cq_init_time)) = 60 ) ,0 ) ) 作为 abon_at_csr_abv, 总和(合并 ( 案子 什么时候 ( 选择 计数(DISTINCT distribution_log.dist_id) 从 分布日志, distribution_log_detail 在哪里 distribution_log.distribution_log_id = distribution_log_detail.distribution_log_id AND distribution_log_detail.service_id = 'P1' 和 distribution_log_detail.resp_code = '00' AND distribution_log.dist_id = cd.dist_id ) 0 那么 1 结尾 , 0 ) ) 作为 acc_successful, 总和(合并 ( 案子 什么时候 ( 选择 计数(DISTINCT distribution_log.dist_id) 从 分布日志, distribution_log_detail 在哪里 distribution_log.distribution_log_id = distribution_log_detail.distribution_log_id AND distribution_log_detail.service_id = 'P1' 和 distribution_log_detail.resp_code '00' AND distribution_log.dist_id = cd.dist_id ) 0 那么 1 结尾 , 0 ) ) 作为 acc_unsuccessful 从 tr_detail 光盘 在哪里 cd.acc_id = 58 AND cd.ci_time >= '2009/11/05' AND cd.ci_time

【问题讨论】:

    标签: mysql


    【解决方案1】:

    如果您的事务隔离级别为 REPEATABLE READ 或更低,则 SELECT 语句不会创建锁,除非您使用“FOR UPDATE”。

    检查您的 txn 隔离级别是什么。

    没有必要使用 READ UNCOMMITTED,甚至 READ COMMITTED。只需避免 SERIALIZABLE。

    另一方面,select 语句仍然会占用资源,这可能会影响机器的行为。

    你是在对 4M 行的表做全表扫描吗?

    【讨论】:

    • Mark,事务隔离级别是 REPEATABLE READ,我没有进行任何全表扫描。此查询最多运行 100,000 行。
    • 我在我的问题中添加了查询。请看一下
    • 我问它是否在进行全表扫描——你可以通过 EXPLAIN 找到它。另一方面,如果它使用 100k 行,它可能几乎与全表扫描(4M 行)一样糟糕。对二级索引的 10 万个条目进行范围扫描并不便宜。
    • EXPLAIN 表明 where 子句中的所有字段都基于索引并获得适当的命中。我是否应该假设 MySQL InnoDB Engine 将锁定表并阻止所有插入/更新操作,如果存在像我这样的繁重的选择查询。
    【解决方案2】:

    (根据MarkR的回答重读后编辑)

    InnoDb 事务的online help text 非常好。默认隔离级别是可重复读取。根据 MarkR 的回答,默认隔离级别的 SELECT 不会发出行锁,也不会在其事务打开时阻止更新或插入。

    如果您在可序列化(最高隔离级别)下运行,您可以通过以下方式回退到默认设置:

    SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
    

    这只会更改当前会话的隔离级别。

    【讨论】:

    • 非常感谢您的回答。我还想让您知道,我不会在事务中执行此查询。我在没有开始任何事务的情况下执行此查询,即使这样,选择查询是否有可能在表上获得锁??
    • 每个查询都在事务中运行。如果没有找到事务,则开始新的事务。默认情况下,MySQL 在启用自动提交模式的情况下运行,这意味着它将在运行语句后提交(关闭)事务。见dev.mysql.com/doc/refman/5.0/en/commit.html
    • 我已经尝试过你给出的解决方案。但不幸的是,现在所有更新语句都进入了停止语句,我们的应用程序无法更新表中的任何行。
    • @Anees:也许将 SELECT 查询添加到您的帖子中。另请阅读 MarkR 的答案,他听起来像是一个更老练的 MySQL 管理员(我主要从开发人员方面了解 MySQL)
    猜你喜欢
    • 1970-01-01
    • 2022-01-08
    • 2019-10-07
    • 1970-01-01
    • 1970-01-01
    • 2016-01-16
    • 2015-02-02
    • 2021-07-13
    • 2014-12-23
    相关资源
    最近更新 更多