前言
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 建议比 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 堆积, 从而造成更多的存储成本.
用户可以通过主实例 (考虑到 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 创建后多久系统自动将它删除, 单位是小时. 另外一种方法是通过执行 purge binlog 的命令, 类似 PURGE MASTER LOGS BEFORE/TO XXX 来手动删除 BINLOG. 注意, 删除前请务必确保 BINLOG 已经无效, 误删除后将无法恢复.
目前, 开启 BINLOG 有一个限制: 当底层存储系统升级的时, 开启 BINLOG 的实例不可服务时间目前是分钟级别的, 不开启 BINLOG 的实例是秒级别的. 所以, 如果用户对实例可用性要求比较高, 可以等我们优化后再开启 BINLOG.
限制问题
POLARDB 由于使用了自研的文件系统和自研的块设备存储系统, 因此在一些限制上与 RDS MySQL 有所不同.
由于文件系统是集成在数据库里面的, 即数据库与文件系统共用一个进程, 所以文件系统会占用一部分的规格内存. 另外不同规格的文件个数也有上限. 目前存储最大支持到 10000GB.
此外, 文件名, 即数据库中的表名和库名都不能超过 63 个字符, 实际使用的时候最好控制在 55 个字符以下, 因为还有. frm,.ibd 后缀, 中间过程临时表等. 详细的说明见这里
并发连接问题
数据库最佳性能的线程数一般是 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 的支持.
来源: https://www.cnblogs.com/coderyuhui/p/9797288.html