一、主从相同表空间相差巨大
1.1 问题描述
我们知道MySQL主从基本上是逻辑的复制,那么有少量的空间差异没有问题,但是本案例主库表只有10G,但是从库表有100G,这么大的差距比较少见,需要分析原因。
1.2 问题分析
实际上这个问题还是要从read view和purge 线程2个方面进行分析,不得不在老生常谈一下(复制一下以前的文章)。
1.2.1 read view和可见性简述
一致性读取(consistent read),根据隔离级别的不同,会在不同的时机建立read view,如下:
- RR 事务的第一个select命令发起的时候建立read view,直到事务提交释放
- RC 事务的每一个select都会单独建立read view
有了read view 就能够对每行数据的可见性进行判断了,下面是read view中的关键属性
- m_up_limit_id:如果行的trx id 小于了m_up_limit_id则可见。
- m_low_limit_id:如果行的trx id 大于了m_low_limit_id则不可见。
- m_ids:是用于记录建立read view时刻的读写事务的vector数组,用于对于m_up_limit_id和m_low_limit_id之间的trx需要根据它来进行判定,是否处于活跃状态。
- m_low_limit_no则用于记录建立read view时刻的最小trx no,主要用于purge线程判断清理undo使用。
而对于可见性的判断我们可以参考如下函数:
/** Check whether the changes by id are visible.
@param[in] id transaction id to check against the view
@param[in] name table name
@return whether the view sees the modifications of id. */
bool changes_visible(
trx_id_t id,
const table_name_t& name) const
MY_ATTRIBUTE((warn_unused_result))
{
ut_ad(id > 0);
if (id < m_up_limit_id || id == m_creator_trx_id) { //小于 可见
return(true);
}
check_trx_id_sanity(id, name);
if (id >= m_low_limit_id) { //大于不可见
return(false);
} else if (m_ids.empty()) { //如果之间的 active 为空 则可见
return(true);
}
const ids_t::value_type* p = m_ids.data();
return(!std::binary_search(p, p + m_ids.size(), id)); //否则比较本trx id 是否在这之中,如果在不可以见,反之可见
}
1.2.2 purge线程主要的工作
总的说来purge线程主要做的工作如下:
- 清理del flag标签的记录
- 清理undo的历史版本
- 如果需要进行undo tablespace截断。
purge线程总会积压一段时间才会进行History list length的清理,如果是小事务(每次修改的page小于innodb_purge_batch_size的设置),那么需要128个这种小事务才清理一次,如果是大事务那么修改两超过了(innodb_purge_batch_size*innodb_purge_rseg_truncate_frequency)的设置则进行一次清理,但是不管如何History list length这个指标持续不为0是正常。
需要注意的是如果长查询结束的时候可能会集中进行purge,purge各个线程可能满负荷工作,这个带来的压力也是不小的,因为purge需要离散的读取各个block做清理工作。如下,9281为一个purge的工作线程:
并且purge线程状态处于running状态,如下:
1.2.3 它们和长查询的关系
假设我们开启了一个select语句,这个语句在不断的查询期间,需要根据行头的trx_id(只考虑主键)和read view去判定扫描到数据的可见性,如果判定为不可见则需要通过行头另外一个信息undo_ptr去构建这行数据的前版本,如果还不可见则继续构建,知道可见为止,然后输出给MySQL上层。
打个比方在select期间执行了大量的数据更改删除(注意可以和select表不是一个表),select 语句扫描出一行带有del flag标签的数据就可以根据上面说的方法构建它的前版本,如果这个时候这些del flag的数据被pruge线程清理了,并且清理了undo信息,那么则没有构建前版本的依据了。
因此我们在做长select的时候,del flag的记录需要保留在segment内部,不能清理,不能重用。当然undo也不能清理。
1.2.4 综合分析
我们知道我们的从库一般来讲都用作读任务,如果有长查询正在进行,那么由于del flag记录不能被清理,所以导致了从库表不断膨胀,同时undo也不能清理掉,也会膨胀。这也就照成了主从库表大小相差巨大的显现。
对于这种现象我们可以在从库的show engine的History list length中确认,实际上History list length 就是当一个update undo log (非insert)的计数器,一个事务只有一个undo log 。来源为trx_sys->rseg_history_len,这个值会在事务提交的时候更新,无论事务大小。但是由于很多内部事务的存在,这个值会远大于可观测的事务个数。栈如下:
#0 trx_purge_add_update_undo_to_history (trx=0x7fffeac7df50, undo_ptr=0x7fffeac7e370, undo_page=0x7fff2837c000 "\373\252\223T", update_rseg_history_len=true, n_added_logs=1,
mtr=0x7fffe8399830) at /home/mysql/soft/percona-server-5.7.29-32/storage/innobase/trx/trx0purge.cc:354
#1 0x0000000001b9c064 in trx_undo_update_cleanup (trx=0x7fffeac7df50, undo_ptr=0x7fffeac7e370, undo_page=0x7fff2837c000 "\373\252\223T", update_rseg_history_len=true, n_added_logs=1,
mtr=0x7fffe8399830) at /home/mysql/soft/percona-server-5.7.29-32/storage/innobase/trx/trx0undo.cc:1970
#2 0x0000000001b8b639 in trx_write_serialisation_history (trx=0x7fffeac7df50, mtr=0x7fffe8399830) at /home/mysql/soft/percona-server-5.7.29-32/storage/innobase/trx/trx0trx.cc:1684
#3 0x0000000001b8c9b0 in trx_commit_low (trx=0x7fffeac7df50, mtr=0x7fffe8399830) at /home/mysql/soft/percona-server-5.7.29-32/storage/innobase/trx/trx0trx.cc:2184
如果查询期间History list length很大则可能遇到了这种情况。
二、checksum table BUG
最近遇到这个bug 同样的数据同样的row format,进行checksum table得到的结果不一致,当然这是非常特殊的情况,一般不会遇到的,测试如下:
8.0.28
mysql> create table myt2(id int );
Query OK, 0 rows affected (0.12 sec)
mysql> insert into myt2 values(10);
Query OK, 1 row affected (0.01 sec)
mysql> insert into myt2 values(20);
Query OK, 1 row affected (0.00 sec)
mysql> insert into myt2 values(30);
Query OK, 1 row affected (0.00 sec)
mysql> checksum table myt2;
+--------------+------------+
| Table | Checksum |
+--------------+------------+
| test517.myt2 | 2306435627 |
+--------------+------------+
1 row in set (0.00 sec)
mysql> alter table myt2 row_format=dynamic;
Query OK, 0 rows affected (0.10 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> checksum table myt2;
+--------------+------------+
| Table | Checksum |
+--------------+------------+
| test517.myt2 | 3470396603 |
+--------------+------------+
1 row in set (0.00 sec)
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.28 |
+-----------+
1 row in set (0.00 sec)
水平有限,如有误请谅解~