## 前言html
POLARDB做为阿里云下一代关系型云数据库,自去年9月份公测以来,收到了很多客户的重点关注,今年5月份商业化后,许多大客户开始陆续迁移业务到POLARDB上,可是因为POLARDB的不少默认行为与RDS MySQL兼容版不同,致使不少用户有诸多使用上的困惑,原本总结了几点,给你们答疑解惑。另外,本文提到的参数,在新版本上,用户均可以经过控制台修改,若是没有,能够联系售后服务修改。本文适合读者:阿里云售后服务,POLARDB用户,POLARDB内核开发者,须要有基本的数据库知识,最好对MySQL源码有部分了解。mysql
## 磁盘空间问题
RDS MySQL在购买的时候须要指定购买的磁盘大小,最大为3TB。若是空间不够,须要升级磁盘空间。具体来讲,若是实例所在的物理机磁盘空间充足,这个升级磁盘的任务很快就能够完成,可是若是空间不足,就须要在其余物理机上重建实例,大实例须要几天的时间。为了解决这个问题,POLARDB底层使用存储集群的方式,作到磁盘动态扩容,且磁盘扩容过程对用户无感知,具体来讲,默认磁盘空间分配为规格内存的10倍,当使用了70%,系统就会自动扩容一部分空间,并且扩容不须要中止实例。sql
有了这种机制,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。socket
`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`建议比`loose_replica_lock_wait_timeout `大。
举个实际例子方便理解:
用户能够经过命令`show processlist`中的State列观察,若是发现`Wait for syncing with replicas`字样,那么表示这条DDL目前处在等待只读节点同步的阶段。若是超过`loose_innodb_primary_abort_ddl_wait_replica_timeout`设置的时间,那么主节点会返回错误:
```
ERROR HY000: Rollback the statement as connected replica(s) delay too far away. You can kick out the slowest replica or increase variable 'innodb_abort_ddl_wait_replica_timeout'
```
若是没有超时,那么主节点会检查是否全部只读节点都成功获取MDL锁了,若是失败,那么主节点依然会返回错误:
```
ERROR HY000: Fail to get MDL on replica during DDL synchronize
```
若是主实例返回第二个错误,那么建议用户检查一下主实例以及全部只读实例上是否有未结束的大查询或者长时间未提交的事务。
这里顺便介绍一下大事务长事务的防范手段。参数`loose_max_statement_time`能够控制大查询的最大执行时间,超过这个时间后,会把查询kill掉。参数`loose_rds_strict_trx_idle_timeout`能够控制空闲事务的最长存活时间,当一个事务空闲状态超过这个值时候,会主动把这个链接断掉,从而结束事务,注意,这个参数应该比`wait_timeout/interactive_timeout`小,不然无效。
## 查询缓存问题
在MySQL低版本,查询缓存(Query Cache)能提升查询的性能,尤为是更新少的状况下,可是因为其自己也容易成为性能瓶颈,因此在最新的MySQL中此特性已经被移除。POLARDB目前的版本兼容MySQL 5.6,因此用户依然可使用查询缓存,可是咱们仍是建议不使用,由于咱们在引擎存储层作了不少优化,即便不用查询缓存依然有很好的性能。
因为POLARDB使用了物理复制,不一样于binlog的逻辑复制,查询缓存在只读实例上的失效,依然须要经过redolog来保证,即当某条查询缓存失效的时候,须要经过redolog来通知全部只读节点,让他们把对应的查询记录也失效掉,不然经过只读节点会读到历史版本的数据。
当查询缓存失效时,会写redolog通知全部只读节点,这个机制默认是关闭的,经过参数`loose_innodb_primary_qcache_invalid_log`来控制。
综上所示,若是在只读节点上开启了查询缓存(只要有一个开启),那么必须在主节点上开启`loose_innodb_primary_qcache_invalid_log`,不然只读节点会读到历史版本的数据。考虑到HA切换会切换到任意一个只读节点,所以建议若是开启了查询缓存,在全部只读节点上也把`loose_innodb_primary_qcache_invalid_log`开启。
## 读写分离问题
POLARDB自带一个只读实例,增减只读实例很是快速,因此用户很是适合使用读写分离的功能,可是从目前用户的反馈来看,若是在插入数据后马上查询,很容易查询到以前旧版的数据,为了解决这个问题,咱们给出两种解法。一种是经过POLARDB数据库内核的强同步保证主实例和只读节点数据一致,另一种是经过数据库前面的PROXY层来解决。下面简单介绍一下。
POLARDB集群基于物理复制构建,目前复制除了支持常规的异步复制(默认),半同步复制以外,还有强同步复制,即当事务提交时,只有当指定的只读实例应用完redolog日志后,主实例才给用户返回成功。这样即便后续的读请求发送到了只读节点,也能保证读到最新的数据。可是这个配置会致使性能大幅度降低,只有默认异步复制的三分之一左右,在使用以前请作详细的测试。简单说一下配置过程:
首先须要在主实例上设置:设置`loose_innodb_primary_sync_slave`为3,目的是告诉主实例,它链接的只读实例会有强同步的需求。接着在须要强同步的只读实例上把参数`loose_slave_trans_sync_level `设置为2,注意这个参数须要重启实例。另外,先设置主实例,再设置只读实例的顺序不能乱。设置成功后,在主实例上执行`show polar replicas;`(这个命令能够查看全部的只读实例),在`sync_level`这一列,能够发现由默认的0变成了2,这就表示强同步开启成功了。若是须要关闭强同步,在主实例上设置`loose_innodb_primary_sync_slave`为0,只读节点上设置`loose_slave_trans_sync_level `设置为0便可,注意设置的顺序依然不能乱。此外,若是强同步的只读实例在`loose_innodb_primary_sync_slave_timeout`后还没返回,强同步复制退化为异步复制,还能够经过`loose_innodb_primary_sync_slave`参数控制当只读节点掉线时是否马上退化为异步复制。
另一种解决办法是经过PROXY来解决。主实例每次作完更新就会把当前的日志位点发给PROXY,同时PROXY也会按期去轮询最大的日志位点,当PROXY须要把后续的查询发到只读实例上时,首先会判断只读实例是否应用到了最新的位点,若是不是,就把请求转发到主实例。这个策略操做的单位是链接,即经过这种方法能保证同一个链接中读到的必定是最新的数据。这种方法虽然会致使主库的压力变大,可是其对性能影响较小,是一种推荐的方法。若是用户须要使用,联系售后作一次小版本升级,便可开放这个功能。
## BINLOG问题
POLARDB使用基于redolog的物理复制来构建复制关系,不依赖BINLOG,所以BINLOG默认是关闭的,可是许多用户须要使用BINLOG将数据同步到第三方数据仓库以方便作复杂的数据分析,因此有不少开启BINLOG的需求。用户能够开启BINLOG,可是与RDS不一样,后台不但不会有任务定时上传备份BINLOG,并且也不会有按期删除BINGLOG的任务,彻底须要用户本身控制什么时候删除,不然会致使BINLOG堆积,从而形成更多的存储成本。由于POLARDB不依赖BINLOG复制,咱们不清楚用户已经消费了多少日志(有可能用户的SLAVE端使用了相似复制延迟的技术),所以,须要用户本身决定什么时候清除日志。
用户能够经过主实例(考虑到HA切换,最好把全部只读实例也打开)参数`loose_polar_log_bin`打开BINLOG(须要重启),BINLOG就会自动存储在日志目录下,空间统计在日志空间使用量里面。能够经过常规的`show master logs`查看BINLOG。每一个BINLOG的大小,BINLOG cache的大小,BINLOG格式等参数均可以经过控制台调整。这里注意下,因为POLARDB使用的是自研的存储系统,`sync_binlog`参数无效,所以没有开放。
若是用户须要删除无用的BINLOG,目前有一种方法:经过调节参数`loose_expire_logs_hours`来控制BINLOG自动删除的时间,这个参数表示自BINLOG建立后多久系统自动将它删除,单位是小时。注意,删除前请务必确保BINLOG已经无效,误删除后将没法恢复(BINLOG是否须要后台上传OSS来做为备份,这个需求咱们会参考用户的反馈来决定是否支持)。
目前,开启BINLOG有一个限制:当底层存储系统升级的时,开启BINLOG的实例不可服务时间目前是分钟级别的,不开启BINLOG的实例是秒级别的。因此,若是用户对实例可用性要求比较高,能够等咱们优化后再开启BINLOG。
## 限制问题
POLARDB因为使用了自研的文件系统和自研的块设备存储系统,所以在一些限制上与RDS MySQL有所不一样。
因为文件系统是集成在数据库里面的,即数据库与文件系统共用一个进程,因此文件系统会占用一部分的规格内存。另外不一样规格的文件个数也有上限。目前存储最大支持到10000GB。
此外,文件名,即数据库中的表名和库名都不能超过63个字符,实际使用的时候最好控制在55个字符如下,由于还有.frm,.ibd后缀,中间过程临时表等。详细的说明见[这里](https://help.aliyun.com/document_detail/72671.html?spm=5176.10695662.1996646101.searchclickresult.38b34a84FpSWtb)
## 并发链接问题
数据库最佳性能的线程数通常是CPU核数的2-3倍,线程数太少,不容易发挥出多线程的优点,线程数太多,又容易致使上下文切换过多以及锁争抢严重的问题。不幸的是,不少用户每每会建立不少并发链接,致使数据库CPU打满,性能低下。
为了解决频繁建立释放链接的问题,即高频短链接问题,能够调大`thread_cache_size`,从而减小频繁建立链接的开销。另外,也建议用户使用客户端链接池来代替高频短链接的方案。
为了解决高并发链接的问题,可使用Thread Pool功能。在Thread Pool模式下,用户链接和处理线程再也不是一一对应关系。处理线程的数量是一个可控的数量,不会随着用户链接数的增多而大幅增长,这样能够减小高并发场景下线程上下文切换的消耗。
用户能够经过调整参数`loose_thread_handling`为`pool-of-threads`来打开Thread Pool功能。同时,建议调整参数`thread_pool_size`为实例CPU核数,其余参数保持默认便可。
Thread Pool比较适合短小的查询和更新,大事务大查询会下降其效果。用户须要依据业务模型来斟酌。另外须要注意一点,Thread Pool不会提升性能,可是其能稳定高并发场景下的性能。
## 总结本文简单介绍了POLARDB常见的几种问题,大多数来源于用户真实的反馈。咱们也在不断的探索更多的功能以及更好的交互。若是在使用POLARDB中遇到疑惑,不要犹豫请马上联系咱们,咱们会给您最满意的答复,谢谢对POLARDB的支持。