Select 语句可以在 mysql 中触发 table 上的死锁?

Select statement can trigger dead lock on table in mysql?

我的问题是

  1. 为什么 SELECT 语句会导致 table1 死锁?
  2. 遇到这种情况如何避免死锁?
DROP report;

CREATE TABLE IF NOT EXISTS report AS (
    
    SELECT
        DISTINCT
        
        companies.id company_id,

        (
            SELECT 
                SUM(`message_count`) single_phone
            FROM
                `table1`
            WHERE
                `table1`.`company_id` = companies.id
                AND
                `status` != 'error'
        ) AS single_phone,

        (
            SELECT 
                SUM(`message_count`)
            FROM
                `table1`
            WHERE
                `table1`.`company_id` = companies.id
                AND
                `status` != 'not error'
        ) AS log,

        (
            SELECT 
                SUM(`message_count`)
            FROM
                `table1`
            WHERE
                `table1`.`company_id` = companies.id
                AND
                `status` != 'error'
        ) AS log_monthly,

        (
            SELECT 
                SUM(`number_of_sms`) AS aggregate
            FROM
                `messages`
            WHERE
                `messages`.`company_id` = companies.id
        ) AS p_monthly
    FROM
        companies
        INNER JOIN company_users ON companies.id = company_users.company_id
    WHERE
        company_users.confirmed = 1
        AND
        company_users.deleted_at IS NULL
);

您的字段级查询应该在 from 子句中完成一次,以便每个公司 ID 完成一次预聚合,并左连接,以防给定公司在给定类别中可能没有合格记录。此外,您获取 Single_Phone 的查询与您的 'log_monthly' 相同,但没有条件显示 打破或过滤 activity 的日期以过滤掉单个月与所有事物的总计。因此,我添加了一个用于过滤的 where 子句,但仅在存在此类日期时才猜测。

此查询可能会显着提高您的性能。通过左连接将每个公司 ID 的基于 COLUMN 的查询移动到它自己的子查询中,这些将被求和()并按公司 ONCE 分组,然后 JOIN 以获得最终结果。使用 COALESCE() 因此如果不存在此类计数,则返回的值将为 0 而不是 null

DROP report;

CREATE TABLE IF NOT EXISTS report AS (
      SELECT
            c.id company_id,
            coalesce( PhoneSum.Msgs, 0 ) as Single_Phone,
            coalesce( PhoneLog.Msgs, 0 ) as Log,
            coalesce( MonthLog.Msgs, 0 ) as Log_Monthly,
            coalesce( SMSSummary.Aggregate, 0 ) as p_monthly
        from
            -- this will declare an in-line variable if you do need to filter by a month as a couple of your
            -- column result names infer, but have no other indicator of filtering by a given month.
            ( select @yesterday := date_sub( date(curdate()), interval -1 day ),
                    @beginOfThatMonth := date_sub( @yesterday, interval dayOfMonth( @yesterday ) -1 day ) sqlvars,
            companies c
                INNER JOIN company_users cu
                    ON m.company.id = cu.company_id
                    AND cu.confirmed = 1
                    AND cu.deleted_at IS NULL
    
                LEFT JOIN
                ( SELECT 
                        t.company_id,
                        SUM( t.message_count ) Msgs
                    FROM
                        table1 t
                            INNER JOIN company_users cu
                                ON t.company.id = cu.company_id
                                AND cu.confirmed = 1
                                AND cu.deleted_at IS NULL
                    where
                        t.status != 'error'
                    GROUP BY
                        t.company_id ) AS PhoneSum,
                    on c.id = PhoneSum.company_id
    
                LEFT JOIN
                ( SELECT 
                        t.company_id,
                        SUM( t.message_count ) Msgs
                    FROM
                        table1 t
                            INNER JOIN company_users cu
                                ON t.company.id = cu.company_id
                                AND cu.confirmed = 1
                                AND cu.deleted_at IS NULL
                    where
                        t.status != 'not error'
                    GROUP BY
                        t.company_id ) AS PhoneLog,
                    on c.id = PhoneLog.company_id
    
                LEFT JOIN
                ( SELECT 
                        t.company_id,
                        SUM( t.message_count ) Msgs
                    FROM
                        table1 t
                            INNER JOIN company_users cu
                                ON t.company.id = cu.company_id
                                AND cu.confirmed = 1
                                AND cu.deleted_at IS NULL
                    where
                            t.status != 'error'
                        -- this would only get counts of activity for current month currently active
                        -- but since you are running at night, you need the day before current
                        AND t.SomeDateFieldOnTable1 >= @beginOfThatMonth
                    GROUP BY
                        t.company_id ) AS MonthLogMsgs,
                    on c.id = MonthLogMsgs.company_id
    
                LEFT JOIN
                ( SELECT 
                        m.company_id,
                        SUM( m.number_of_sms ) aggregate
                    FROM
                        messages m
                            INNER JOIN company_users cu
                                ON m.company.id = cu.company_id
                                AND cu.confirmed = 1
                                AND cu.deleted_at IS NULL
                    where
                        m.SomeDateFieldOnMessagesTable >= @beginOfThatMonth
                    GROUP BY
                        company_id ) AS SMSSummary,
                    on c.id = SMSSummary.company_id

非常感谢您的帮助,但我已经找到了问题所在。是的,此过程导致 table 上的死锁,但问题的实际原因是我已将 ->everyMinute() 放入我的 laravel 内核中用于时间表 运行。并且还有另一个开发人员配置的 cron 作业,每分钟 运行 相同。这些将 运行 每分钟安排一次,这是死锁问题的真正原因。我已将内核计划更改为 ->dailyAt('02:00');现在问题解决了。