美文网首页MySQL(总)
10.22锁等待事件复盘(未完结)

10.22锁等待事件复盘(未完结)

作者: 飞翔的Tallgeese | 来源:发表于2018-11-02 18:09 被阅读0次

    事件还原

    10.22业务端反馈部分业务失败,从程序段检测到部分数据未能添加成功;通过RDS的慢查询,发现部分insert语句的lock持续长达800秒之久

    很明显的这部分插入失败了

    10.29当天,由于某测试库的原因,导致mysqldump在早晨9点55左右执行,因此线上库再次出现了update等待问题

    涉及知识面

    表面上看,这2次事件仅仅涉及到lock,然而实际上此次事件还涉及到latch

    分析

    首先是10.29的mysqldump阻塞生产库事件

    该测试库在每晚的凌晨4点会执行对生产库的mysqldump,该库的log冗余天数为10天,由于数据量暴涨,该库的空间并不足以应付10天的log量,在空间爆满后,该库无论是data还是log都无法正常写入,因此mysqldump被挂起,直到第二天早晨的9点55分,研发发现数据库因为空间满了无法登陆。删除binlog之后,mysqldump开始执行,于是悲剧发生了。

    mysqldump为了产生全表一致性快照,因此对RDS对应实例加了一个FTWRL锁,该锁的粒度为表级,因此对应实例下所有表均被锁住,生产库的所有DML操作均要等待这个FTWRL锁释放之后才能获取资源,从而造成锁等待,update全部挂起。

    其次是10.22的insert等待

    由于information_schema(后续简称IS库)内的3张与锁信息有关的表均为临时表,数据不会永久保存,所以到底是什么阻碍了10.22的那堆insert无法确定。从正常的lock层面来看,insert毫无疑问会寻求x锁,而x锁与s\x\is\ix锁均为互斥关系,所以之前无论是什么级别的行级锁都会造成insert的等待,但是...

    从lock的时间来看,这个时间等待从200秒到800秒不等,而lock_wait_timeout参数设定为50,也就是说如果lock超过50秒还不能获取的话,语句就会被判定为失败,所以...

    很明显这里造成800秒等待的并不是单纯的行级锁!

    后续通过设置定时任务将IS库里的3张表保存下来也并没有发现行级锁之间阻塞insert的情况

    通过后台可以发现大量的慢SQL,以及大量的死链。在10.23当天我甚至观测到过1000的死链(但后续查看当天最高400+,难道我眼花了?)

    大量的死链会抬高CPU占用率,自然也会有latch的争用

    在MySQL中lock针对表、页、行;而latch针对线程;lock存在等待、排队,latch不存在,与之相关的是spin,sleep

    10.22的情况很可能就是下面我描述的这样

    假设a线程需要执行一个垃圾SQL,全表扫描的那种;b线程需要执行一个insert或者update

    a在b之前先行到达,于是a获取了这个latch;b随后到达,他觉得a会很快执行完,于是他开始spin(spin:自旋,空占着cpu,执行一段空代码,等待一个随机的时间;如果不spin,会被踢出去),当b执行完spin之后回来找a,发现a还在执行那个垃圾SQL,于是他继续spin,又过了一会他回来了,但是他发现这时候后面的c已经在他spin的时间获得了这个latch,于是他继续spin...在spin了无数次之后,他还是无法获得这个latch,于是他sleep,退出cpu了。

    我们在show processlist中看到的大量sleep的行,就是这么来的,每一个sleep占用着一个死链;那天我看到的1000多个死链,也就是1000多个sleep,在此之前他们都不断的spin过。

    所以:阻碍获得x锁的,未必是lock层面的锁,他完全也可能是被一个单纯的快照读从latch层面上阻碍了!

    解决方案

    对于10.29的事件,只要mysqldump不在忙时产生就没事,也就是确保其在半夜执行完成就OK;

    临时方案,已经将该测试库的log冗余天数缩减到3天

    后续方案,另外找一台服务器作为中间库,只单纯部署数据库,由运维部门监控该中间库

    更优方案,开启金融云RDS的读写分离,xxx部门只能访问只读库

    对于10.22事件

    1.修改wait_timeout和interactive_timeout时间,原wait_timeout时间为86400秒,也就是1天才会清理死链,这个值设置的太过巨大,已经将其修改为300秒,目前来看死链基本控制在130以下

    2.持续的慢SQL优化;最近金融云RDS的慢SQL多半是由一张特定的表查询产生的,需要对这个表进行索引的优化

    -------------------------------------------------------------------------------------

    整合一下IS库那3张临时表写入永久表的建表语句、存储过程以及event

    --2018.10.31

    金融云RDS 2018.10.22出现大面积锁等待事件

    由于IS里面的3张表都是临时表,因此需要将其存到永久表中进行分析

    创建ISPS库用于性能分析

    创建3张表结构(不加主键)

    CREATE TABLE trx (

    `trx_id` varchar(18) NOT NULL DEFAULT '',

    `trx_state` varchar(13) NOT NULL DEFAULT '',

    `trx_started` datetime NOT NULL,

    `trx_requested_lock_id` varchar(81) DEFAULT NULL,

    `trx_wait_started` datetime DEFAULT NULL,

    `trx_weight` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_mysql_thread_id` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_query` varchar(1024) DEFAULT NULL,

    `trx_operation_state` varchar(64) DEFAULT NULL,

    `trx_tables_in_use` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_tables_locked` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_lock_structs` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_lock_memory_bytes` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_rows_locked` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_rows_modified` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_concurrency_tickets` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_isolation_level` varchar(16) NOT NULL DEFAULT '',

    `trx_unique_checks` int(1) NOT NULL DEFAULT '0',

    `trx_foreign_key_checks` int(1) NOT NULL DEFAULT '0',

    `trx_last_foreign_key_error` varchar(256) DEFAULT NULL,

    `trx_adaptive_hash_latched` int(1) NOT NULL DEFAULT '0',

    `trx_adaptive_hash_timeout` bigint(21) unsigned NOT NULL DEFAULT '0',

    `trx_is_read_only` int(1) NOT NULL DEFAULT '0',

    `trx_autocommit_non_locking` int(1) NOT NULL DEFAULT '0'

    ) ENGINE=MEMORY DEFAULT CHARSET=utf8;

    CREATE TABLE `locks` (

    `lock_id` varchar(81) NOT NULL,

    `lock_trx_id` varchar(18) NOT NULL DEFAULT '',

    `lock_mode` varchar(32) NOT NULL DEFAULT '',

    `lock_type` varchar(32) NOT NULL DEFAULT '',

    `lock_table` varchar(1024) NOT NULL DEFAULT '',

    `lock_index` varchar(1024) DEFAULT NULL,

    `lock_space` bigint(21) unsigned DEFAULT NULL,

    `lock_page` bigint(21) unsigned DEFAULT NULL,

    `lock_rec` bigint(21) unsigned DEFAULT NULL,

    `lock_data` varchar(8192) DEFAULT NULL

    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    CREATE TABLE LOCK_WAITS (

    `requesting_trx_id` varchar(18) NOT NULL DEFAULT '',

    `requested_lock_id` varchar(81) NOT NULL DEFAULT '',

    `blocking_trx_id` varchar(18) NOT NULL DEFAULT '',

    `blocking_lock_id` varchar(81) NOT NULL DEFAULT '',

    `requesting_thd_id` varchar(18) NOT NULL DEFAULT '',

    `blocking_thd_id` varchar(18) NOT NULL DEFAULT ''

    ) ENGINE=MEMORY DEFAULT CHARSET=utf8

    创建存储过程

    delimiter //

    create procedure insert_lock_information ()

    begin

    insert into isps.trx select * from information_schema.innodb_trx;

    insert into isps.locks select * from information_schema.innodb_locks;

    insert into isps.lock_waits select * from information_schema.innodb_lock_waits;

    commit;

    end

    //

    创建event

    create event if not exists insert_lock_information

    on schedule every 30 second

    on completion preserve

    do call insert_lock_information ()

    RDS锁等待超时的参数为50秒,超过这个时间,事务会被中断后重新执行

    event本来计划为每秒执行1次,那样会产生相当多的数据,将其修改为30秒以观效果;如果锁问题出现较多,会将event更改为每秒执行。

    该event会增加系统的IO负担,因此在完成观察之后,需要关闭该event

    相关文章

      网友评论

        本文标题:10.22锁等待事件复盘(未完结)

        本文链接:https://www.haomeiwen.com/subject/kqwcxqtx.html