前言
POLARDB作为阿里云下一代关系型云数据库,自去年9月份公测以来,收到了不少客户的重点关注,今年5月份商业化后,许多大客户开始陆续迁移业务到POLARDB上,但是由于POLARDB的很多默认行为与RDS MySQL兼容版不一样,导致很多用户有诸多使用上的困惑,本来总结了几点,给大家答疑解惑。另外,本文提到的参数,在新版本上,用户都可以通过控制台修改,如果没有,可以联系售后服务修改。本文适合读者:阿里云售后服务,POLARDB用户,POLARDB内核开发者,需要有基本的数据库知识,最好对MySQL源码有部分了解。
磁盘空间问题
RDS MySQL在购买的时候需要指定购买的磁盘大小,最大为3TB。如果空间不够,需要升级磁盘空间。具体来说,如果实例所在的物理机磁盘空间充足,这个升级磁盘的任务很快就可以完成,但是如果空间不足,就需要在其他物理机上重建实例,大实例需要几天的时间。为了解决这个问题,POLARDB底层使用存储集群的方式,做到磁盘动态扩容,且磁盘扩容过程对用户无感知,具体来说,默认磁盘空间分配为规格内存的10倍,当使用了70%,系统就会自动扩容一部分空间,而且扩容不需要停止实例。
有了这种机制,POLARDB的存储可以做到按照使用量来收费,真正做到使用多少就收多少钱,计费周期是一小时。同时,由于存储按量收费,导致许多用户对存储的使用量非常敏感,在我们的控制台上,有五种空间统计,分别是磁盘空间使用量,数据空间使用量,日志空间使用量,临时空间使用量和系统文件空间使用量。
磁盘空间使用量是后四者之和,数据空间使用量包括用户创建的所有库,mysql库,test库,performance_schema库,日志空间使用量包括redolog,undolog,ibdata1,ib_checkpoint(存储checkpoint信息),innodb_repl.info(存储切换信息,物理复制信息等),临时空间使用量包括socket文件,pid文件,临时表(大查询排序用),审计日志文件,系统文件空间使用量包括错误日志,慢日志,general日志以及主库信息(用于构建复制关系)。虽然有四部分空间使用量,但大多数主要被数据空间和日志空间占用,数据空间比较好理解,主要就是表空间聚集索引和二级索引的占用量,但是这个日志空间很多用户不是很了解,常常提上来的问题是,为什么我的日志空间占了100多个G,而数据空间就几个G,这里简单解释一下。
日志空间使用量,如上所述,有很多组成部分。redolog,主要用来构建物理复制,同时也可以被当做增量日志来支持还原到时间点/克隆实例的任务,类似原生的binlog,文件名按顺序递增,主节点产生日志,只读节点/灾备节点应用日志,同时后台管控任务会定时上传redolog(只要发现新的就立即上传)并且定时删除(目前一小时触发一次删除任务),具体大小与DML总量有关。undolog,主要用来构建数据历史版本以支持MVCC机制和回滚机制,不同于RDS MySQL的undolog都在ibdata1文件中,POLARDB的undolog大部分是以独立表空间/文件存在,具体大小与用户使用习惯有关。ibdata1,主要存储系统元数据信息等系统信息,具体大小与用户表数量有关,但是一般不会太大。ib_checkpoint,这个是POLARDB特有的,用于存储checkpoint信息,大小固定。innodb_repl.info也是POLARDB独有的,存储物理复制相关的信息以及切换信息,一般不会太大。由此可见,日志空间使用量虽然也有很多组成部分,但主要是被redolog日志和undolog日志占用。
redolog日志占用
redolog日志,由于对数据的修改都会记录redolog,所以对数据修改的越快,redolog产生的速度就会越快,而且上传OSS(保留下来做增量日志)的速度有限,所以在实例导数据阶段,会导致redolog堆积,当导入完成后,redolog会慢慢上传完然后删除,这样空间就会降下来,但是不会完全降为0。具体原因需要介绍一下:目前所有规格,redolog大小都为1G,被删除的redolog不会马上被删除,而是放入一个缓冲池(rename成一个临时文件),当需要新的redolog时候,先看看缓冲池里面还有没有可用的文件,如果有直接rename成目标文件,如果没有再创建,这个优化主要是为了减少创建新文件时的io对系统的抖动,缓冲池的大小由参数loose_innodb_polar_log_file_max_reuse控制,默认是8,如果用户想减少缓存池的文件个数,就可以减少这个参数从而减少日志空间占用量,但是在压力大的情况下,性能可能会出现周期性的小幅波动。所以当写入大量数据后,即使redolog都被上传,默认也有8G的空间用作缓存。注意,调整这个参数后,缓冲池不会立刻被清空,随着dml被执行,才会慢慢减少,如果需要立即清空,建议联系售后服务。
另外,POLARDB会提前创建好下一个需要写的redolog日志(每个日志都是固定的1G,即使没有被写过),主要目的是当当前的redolog被写完后,能快速的切换到下一个,因此,也会占用额外1G空间。此外,后台定时删除任务目前是一个小时清理一次(还有优化的空间),但是不会清理到最后一个日志,会保留一个日志,主要用来做按时间点还原任务。
接下来,举个经典的例子,方便理解上述的策略:
mysql> show polar logs; +-----------------+----------------+-------------+ | Log_name | Start_lsn | Log_version | +-----------------+----------------+-------------+ | ib_logfile41008 | 19089701633024 | 100 | | ib_logfile41009 | 19090775372800 | 100 | +-----------------+----------------+-------------+ 2 rows in set (0.00 sec) mysql> show polar status\G ...... ----------------- Log File Info ----------------- 2 active ib_logfiles The oldest log file number: 41008, start_lsn: 19089701633024 The newest log file number: 41009, start_lsn: 19090775372800 Log purge up to file number: 41008 8 free files for reallocation Lastest(Doing) checkpoint at lsn 19091025469814(ib_logfile41009, offset 250099062) ......show polar logs这条命令可以查看系统中的redolog日志,上个例子中,ib_logfile41008这文件已经被写完,但是这个日志需要被保留用来支持按照时间点还原和克隆实例任务,ib_logfile41009是最后一个redolog,表示目前正在写的redolog。
show polar status\G可以显示POLARDB很多内部信息,这里只截取了redolog相关的一部分,前四行就是字面的意思,不具体解释了。第五行表示缓冲池目前有8个redolog。
另外,上文提到过,POLARDB会提前创建一个redolog用以快速的切换,名字一般是最后一个文件编号加一,所以是ib_logfile41010。
结合这些信息,就可以推断出,目前系统中redolog占用量为11G = 8G(缓冲池中的)+1G(保留的ib_logfile41008)+1G(正在被写的ib_logfile41009)+1G(提前创建的ib_logfile41010)。
另外,透露一个好消息,我们内部正在调研redolog日志不收费的可行性,如果通过验证,这部分占用的空间将不会收取用户费用。
undolog日志占用
讲完了redolog日志,接下里讲讲undolog日志。上文说过在POLARDB中undolog大部分是以独立表空间存在的,也就是说是独立的文件,而不是聚集在ibdata1文件中。目前分了8个文件,文件名为undo001-undo008,每个文件默认初始大小为10M,会随着使用增大,在某些不推荐的用法下,会导致undolog空间增长很快。这里简单举个例子,可以使undolog撑的很大:使用START TRANSACTION WITH consistent snapshot开启一个事务,注意要在RR隔离级别下,然后开启另外一个连接,对库中的表进行高频率的更新,可以使用sysbench辅助,很快,就会发现undolog膨胀。从数据库内核的角度来讲,就是由于一个很老的readview,导致需要把很多的历史版本都保留下来,从而导致undolog膨胀。在线上,往往是一个大查询或者一个长时间不提交的事务导致undolog膨胀。undolog膨胀后,即使所有事务都结束后,也不会自动缩小,需要使用下文的方法进行在线truncate。
目前,用户还不能直接查看undolog的占用量,后续我们会在information_schema加上,方便用户查看,但是可以通过间接的方法:如果控制台上显示日志占用量很大,但是redolog占用量很小,那么一般就是undolog了,因为其他几个都占用很小的空间,几乎可以忽略不计。
如果发现undolog占用量比较大,POLARDB也有办法清理。原理是,等undolog所对应的事务都结束后,把清理开关打开,如果发现大小超过执行大小的undo tablespace,就会在purge线程中进行undo的truncate。尽量在业务低峰期进行,并且保证没有大事务长事务。具体操作方法就两步,首先调整innodb_max_undo_log_size大小,这个参数表示当每个undo tablespace大于这个值时候,后续会把它缩小,重新调整为10M。接着,打开truncate开关innodb_undo_log_truncate,这样,后台线程就会把所有大于innodb_max_undo_log_size设置的undo tablespace调整为10M。注意,这里要保证没有大事务长事务,因为后台线程会等待undo tablespace中所有事务都提交后,才会下发命令,同时也要保证innodb_undo_logs大于等于2。另外,不建议这个功能长期开着,如果在控制台发现日志占用量减少了,建议关闭truncate功能,因为其有可能在您业务高峰期运行,导致数据库延迟。
DDL与大事务问题
如果有一个大事务或者长事务长时间未提交,由于其长期持有MDL读锁,这个会带来很多问题。在RDS MySQL上,如果后续对这张表又有DDL操作,那么这个操作会被这个大事务给堵住。在POLARDB上,这个问题更加严重,简单的说,如果只读实例上有一个大事务或者长期未提交的事务,会影响主实例上的DDL,导致其超时失败。纠其本质的原因,是因为POLARDB基于共享存储的架构,因此在对表结构变更前,必须保证所有的读操作(包括主实例上的读和只读实例上的读)结束。
具体解释一下POLARDB上DDL的过程。在DDL的不同阶段,当需要对表进行结构变更前,主实例自己获取MDL锁后,会写一条redolog日志,只读实例解析到这个日志后,会尝试获取同一个表上的MDL锁,如果失败,会反馈给主实例。主实例会等待所有只读实例同步到最新的复制位点,即所有实例都解析到这条加锁日志,主实例同时判断是否有实例加锁失败,如果没有,DDL就成功,否则失败回滚。
这里涉及到两个时间,一个是主实例等待所有只读实例同步的超时时间,这个由参数loose_innodb_primary_abort_ddl_wait_replica_timeout控制,默认是一个小时。另外一个是只读实例尝试加MDL锁的超时时间,由参数loose_replica_lock_wait_timeout控制,默认是50秒。可以调整这两个参数来提前结束回滚DDL,通过返回的错误信息,来判断是否有事务没结束。loose_innodb_primary_abort_ddl_wait_replica_timeout建议比
