OceanBase 开源生态总监、OceanBase 开源社区负责人封仲淹:文章从一个非常具体的场景出发, 整体上给用户一个新的解决方案zabbix + oceanbase的方案, 给zabbix用户和occeanbase用户一个新的思路, 整个文章中, 细节满满, 非常详实; 在文章中, 详细的描述了之前zabbix + mysql所遇到各种细节问题, 并介绍如何进行选型, 对比候选系统, 最终使用oceanbase获得收益。
———– 以下为投稿正文 ———–
作为产值百亿的企业,监控系统是重要的IT管理工具之一,对保障企业业务连续性、预告风险有重要意义。2022年,公司选用Zabbix为企业监控系统,对公司分布在国内外的服务器、操作系统、中间件、数据库、网络设备等进行指标监控;对集团业务系统设置监控预警,确保集团所有系统异常时准确告警;对IT设施的巡检、事件的回溯提供指标数据支撑,便于IT管理人员可以快速获取系统中各个组件的历史数据。
之所以选择Zabbix是因为其开源,且经过多年发展,不仅架构稳定还具备“监控万物”的能力,适合我们这种以传统架构为主,云原生架构比例很低的企业。公司内部相关IT人员也有Zabbix使用经验,上手门槛较低。
彼时,笔者刚加入公司,对刚上线的Zabbix监控系统做持续且深度的优化和改造,不断提升监控系统的及时性、准确性。由于Zabbix底层数据库使用MySQL 8.0,被MySQL的架构限制,因此,很快就出现了问题。
第一个问题,架构高可用瓶颈,无论选择哪种架构,都不尽人意。
主从架构的问题:
非读写方式,与单点的性能无差异不推荐。主节点故障后,需要停机切换,还需要校验主从节点的数据差异,所以不推荐。
读写分离方式有2种改造方式,一是改造DAL层代码,这种方式影响后续版本功能的迭代,不推荐;二是引入如 ProxySQL的中间件,但增加了一层组件,在性能和可靠性上有所降低。
双主架构的问题:
单写是我们目前采用的方式,节点上套用一层keepalived作为虚拟地址,方便切换。
双写的话需要控制写入行的ID,避免主键冲突和数据冗余。需要改造,不推荐。
MGR架构的问题:
一致性强,但需要配合如ProxySQL之类的中间件实现读写分离。在实际测试中,MGR容易产生雪崩效应,即一个节点掉出后,可能导致整个集群崩溃。
所有采用复制方式的架构最大的问题点在于,Zabbix的写入量很大,binlog不能保留太久(非常占用磁盘空间),如果复制关系断开太久会导致主从节点之间无法找到同步位点,无法重新同步上。
第二个问题,读写冲突。Zabbix的写入量很大,在业务高峰时段,运维人员和业务人员查询监控数据、history数据向trends数据转换、告警比较等,很容易产生读写冲突(乐观锁)。但Zabbix还有一个管家服务,会定期清理过期的监控数据,这容易造成悲观锁,使数据库性能急剧下降。在数据量不断增大的同时,这个冲突会越来越明显。
第三个问题,容量问题。尽管对监控项的数量、保留时间做了大量的改造,但仍有大量的数据需要保存下来。运行1年多,Zabbix的数据库已经超过1TB,最大单表数据量超7亿。数据本身的容量只是问题中的其一,其二是InnoDB的binlog。
在众多优化案例里,笔者挑选了一个典型的数据优化案例来分享。
在制作Zabbix监控模板时,其中Linux操作系统监控模板的监控项就多达100个,面对公司2000+生产级别服务器,监控项规模达到了20w个,假设每隔5分钟对所有的监控项收集一次数据,那么,每小时数据库库中将有20w*(60/5)=240w 笔数据要写入history表。
5分钟采集一次是一种理想情况,因为采集间隔变大后,数据的精度会降低。对于一些流量数据、CPU、内存、I/O等使用率数据,用户往往要求以较高的精度采集,采集间隔可能是在1分钟左右,这样的代价是产生了更多的监控数据。
同时考虑到 history 到 trends 转换,每小时从hisoty 和 history_unit 表中取出完整1小时的监控值进行运算后(min、avg、max),分别 insert 到 trends 和 trends_unit 表中。
这个过程中,查询到的结果集大、运算量大,需要大量的缓存,往往引起MySQL的临时表或临时表文件创建过快、磁盘I/O过大、占用SWAP、引发大事务等问题。
随着数据量的增大,以及上文提到的事务悲观锁冲突,导致在清理历史数据时,往往会清理任务失败,进而历史数据越积越多。同时,锁表的问题导致在环境中无法使用dump方式进行备份,只能使用物理备份。但在使用物理备份时,由于清理历史数据的过程中使用delete方式,造成主要业务表中的空间未得到正常释放,产生碎片,在备份前需要进行碎片整理,使得备份业务推进起来非常艰巨。
大批量的insert、delete操作使服务器的binlog变得庞大,导致存储压力很大。调小后,如果主从一断,主库上的binlog位点很快就会循环覆盖,导致从节点要重新恢复才能加入到集群。
对于上述问题,结合Zabbix数据库中的数据表,我们就需要通过优化以下数据表来解决问题。
表名 | 作用 | 数据类型 |
---|---|---|
history | 存储原始的历史数据 | 数字(浮点数) |
history_uint | 存储原始的历史数据 | 数字(无符号) |
history_str | 存储原始的短字符串数据 | 字符型 |
history_text | 存储原始的长字符串数据 | 文本 |
history_log | 存储原始的日志字符串数据 | 日志 |
trends | 存储每小时统计数据(趋势) | 数字(浮点数) |
trends_uint | 保持每小时统计数据(趋势) | 数字(无符号) |
auditlog | 审计日志表 |
其中,以 history 开头的表为存储历史数据,trends开头的是趋势数据。历史和趋势是在Zabbix中存储数据的两种方法。
History,历史保存着每一个item的数据。即从客户端收集过来的原始数据,这部分表的表结构都差不多,唯一的不同是保存的数据类型。如果一个监控项(item)一分钟采集1次,那每天就有24*60*60=86400笔数据。
如果是数值类型,表中字段如下:itemid bigint(20)、clock int(11)、value bigint(20)、ns int(11),大概一笔数据是8+4+8+4=24 B,一天有24*86400≈2 MB;
如果是history_str 或者 history_text 两个表,其中的数据字段变成了value varchar(255) COLLATE utf8mb4_bin 和 value text COLLATE utf8mb4_bin ,varchar(255) utf8mb4 最长是 255*4=1020 B,而text 字段最大可以到65535 B ,我们考虑极端情况,一个字符型或文本型的 item ,按1分钟采集1次,分别对应产生的数据量为(8+4+1020+4)*86400≈85 MB 或 (8+4+65535+4)*86400≈5 GB ;
历史数据的大小取决于监控项(item)条目数量、保留时长、监控项(item)值类型、监控项(item)的采集间隔等条件。
Trends,是每小时监控的数据聚合后的结果,保存一小时内某个item的平均值、最大值和最小值,可以理解为是history表的压缩数据,因此减少了对资源的需求。
trends 仅针对数值类型的 history 表,而 history_str 、history_log 和 history_text 是没有趋势表的;
趋势表是由历史表通过housekeeper(管家服务,类似与一个定时任务)转换而来,转换过程必须消耗数据库的性能。
04
history表中存在2个时间字段,一个是clock,另外一个是ns,接收item值时的时间值存放在两个字段内,大于1秒的部分存放找clock字段单位是秒(s),小于一秒的部分存放在ns字段单位是纳秒(ns)。
两个字段相加的值才是接收item值时的时间值,一般不用关心小于1秒的部分。但是,在Zabbix自己做统计时很多查询中都用到了这两个字段,而这些表中,并没有在ns上面做索引,导致了全表扫描,所以我们将history表进行改造:
CREATE TABLE `history_old` (
`itemid` bigint(20) unsigned NOT NULL,
`clock` int(11) NOT NULL DEFAULT '0',
`value` double NOT NULL DEFAULT '0',
`ns` int(11) NOT NULL DEFAULT '0',
KEY `history_1` (`itemid`, `clock`) BLOCK_SIZE 16384 LOCAL
) DEFAULT CHARSET = utf8mb4
CREATE TABLE `history` (
`itemid` bigint(20) unsigned NOT NULL,
`clock` int(11) NOT NULL DEFAULT '0',
`value` double NOT NULL DEFAULT '0',
`ns` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`itemid`, `clock`, `ns`)
) DEFAULT CHARSET = utf8mb4
其余history表的改造参照上面的方法
针对history系列表和trends系列表进行分区,定期create 分区和drop 分区。在做分区管理时,遇到三个问题。
第一,因为监控数据表的数据量太大,直接在源表上操作风险很大,执行效率也会很低。所以采用了创建新表,然后将原表中的数据
insert /*+ ENABLE_PARALLEL_DML PARALLEL(2) */ …… select……
的方式处理(这里用到了OB DML的并行,具体可以参考链接🔗:https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001050810),但发现部分数据量少的表还能够导数成功,大表、业务繁忙的表根本无法执行。
第二,如果我们使用dump方式去备份还原表,中间停机时间要很久。
06
在Zabbix中使用频度最高的就是将最新的数据写入数据库,按一定的时间间隔,将历史数据(history)转换趋势数据(trends),删除过期的历史数据,以及IT运维人员查询监控数据、报表(grafana)等操作。查询(排序、汇总、计算)、增删(大事务)会极大地消耗MySQL数据库的性能,使业务的处理时间变长。最简单的优化方案就是优化MySQL的
innodb buffer pool、query_cache_size、tmp_table_size、innodb_log_buffer_size、 sort_buffer_size、read_buffer_size、join_buffer_size、binlog_cache_size
的大小,尽可能将数据留在内存中,加快数据的处理速度。
但是无论如何优化buffer,也不能无限制的增大,当达到物理内存的上限时,就需要去扩容内存。同时,大量的大事务会过度消耗磁盘I/O(当脏页达到一定量的时候就会落盘,落盘就会产生I/O),加剧数据库的压力。底层物理设备的扩容会涉及停机、迁移等中断服务的事件,也是额外增加成本。
07
对于新的数据存储方案,我们希望除了能够解决现有架构瓶颈和业务痛点外,还需要具备三个条件:
1、必须兼容MySQL的语法、函数与表达式、数据类型、表分区、字符集、字符序。
2、具备HTAP能力,能够在基于关系型数据结构在数据引擎层面处理好TP和AP的关系。
3、在不依托于其他技术手段的情况下具备高可用、多活能力。
由于Zabbix server 支持MySQL、PostgreSQL,但团队中没有人熟练驾驭PostgreSQL,同时,在Zabbix的 MySQL 数据库上已经开发了比较多的应用、报表,如果贸然将数据库从MySQL切换成PostgreSQL,需要对之前的应用、报表做再开发,因此,暂时不考虑PostgreSQL,于是将选型的范围放在兼容 MySQL 模式的数据库。
上文提到我们同时在使用InnoDB引擎,MySQL的TokuDB引擎作为InnoDB引擎的优化升级版本,就成为了我们选型数据存储方案中的备选。
首选还是考虑目前热度较高的新兴数据库,比如TiDB、华为GaussDB和openGauss、OceanBase、达梦数据库。
TiDB不支持MySQL的存储过程、外键,首先排除。
openGauss基于PostgreSQL,与PostgreSQL数据库一样,作为第二梯队方案。
华为GaussDB需要付费购买,暂时不考虑。
达梦数据库需要付费购买,暂时不考虑。
OceanBase兼容MySQL,支持分布式,而且开源,可以进行下一步调研。
对比项 | TokuDB | OceanBase |
---|---|---|
部署难易度 | 较容易 | 较容易 |
容灾架构 | 主从 | 分布式 |
数据压缩比 | 中 | 高 |
信创支持 | 无 | 支持 |
性能对比 | 优化了写入,但读性能缺少依据 | 未测试 |
综合考虑下,由于TokuDB读能力不满足预期,OceanBase支持HTAP、多活高可用,以及社区活跃,有非常多的参考资料,容易上手。因此,我们最终决定使用OceanBase。
08
我们按照官方文档执行部署,过程中遇到了一些小问题,通过查询官方文档、咨询社区技术人员等方式逐一解决(后续我会把部署过程分享到博客中,大家可以拍砖、借鉴)。
总的来说,OceanBase的上线流程较为顺利,从 MyySQL 迁移到 OceanBase非常容易。因为我们用的是OceanBase 社区版,所以,在迁移前无法使用迁移评估(OceanBase Migration Assessment,OMA)来对迁移过程进行评估,只能依靠经验来判断,在社区的官方人员和社区用户群朋友们的帮助下,我们在迁移前做了如下检查,供大家参考。
字符集检查。因为OceanBase没有完整支持MySQL的字符集和排序,所以,需要在迁移前做好评估。
OceanBase中没有event,原库中有event时,需要通过别的方法实现,后文列举了案例。
OceanBase中对大小写不敏感,有些MySQL库可能对大小写敏感,所以要注意原库中lower_case_table_names的配置。
如果需要做反向同步,在原库中提前建立好相应的账号、授权,并把omstxndb库建立好。
如果主外键约束的话,需要提前关闭主外键约束检查SET FOREIGN_KEY_CHECKS=0;
09
在写本文时,我们的Zabbix监控系统已经在OceanBase中运行了半年,相比之前使用MySQL时,从资源配置上来讲,在获取相同性能的情况下,可以大幅降低硬件平台的投入。
最初,由于OceanBase兼容MySQL,公司的开发人员和系统管理人员对于数据迁移并没有表示反对,在迁移过程中,OceanBase的高可靠、HTAP等能力使笔者在与业务系统运维部门沟通迁移计划时都比较顺利,迁移后带来的性能提升和80%的空间节约,也让业务部门非常满意。
在性能提升方面,最直观的感受是之前在查询周期较长的历史数据(几周或几个月)时,通常需要好几秒(至少4秒)时间才能将数据渲染出来,现在运行在OB上之后,基本上点击完后就可以立即加载出统计图;另外一个感受就是,以前zabbix有各种各样的性能告警,频度也比较高,自从迁移到OB上之后,此类告警明显减少。
总而言之,OceanBase是一个符合我们预期的数据库平台,目前,我们也在不断探索和实践OceanBase的新功能。以下,我将结合公司业务场景与OceanBase的功能进行简单总结。
支持并行,对于大量数据的操作,打开并行功能,可以大幅提高处理的效率。
对于资源的热更新,依托于OCP平台,可以对于数据库的资源(CPU、内存等)、zone、参数进行热更新,减少停机。
压缩率高,我们在导入Zabbix数据库时,在MySQL上看到的库的大小是1.2TB,导入OceanBase后,数据库的大小只有260GB左右,数据量缩小了80%,,极大地节省了存储空间。
与生俱来的伸缩性和分布式能力,可以支持在线横向(扩展zone)、纵向(扩展分片)扩容,所有的数据同步都交由后台自动完成,基本不需要人工参与。不论是横向还是纵向都提供了数据的冗余能力。原先我们还尝试过使用MySQL的MGR,虽然保障了一致性,但运维过程非常艰辛,一旦有节点挂掉很有可能把整个集群打穿。
支持慢查询单独队列,如果遇到慢查询,OceanBase会将慢查询放在一个单独的队列里面慢慢执行,不影响短平快的查询,不阻塞。
此外,OceanBase丰富的生态工具也为我们带来了更加自动化、更加便捷的运维管理能力。
首先,OCP 为数据库的管理提供了很多实用的功能:
云原生、多租户:可以将数据库在一个平台统一管理,简化数据库的创建、运维浑然一体。使用租户模式,实现租户与租户之间资源隔离。
SQL诊断:集成SQL诊断功能,便捷、快捷的观测TopSQL、SlowSQL、并行SQL。可以通过可视化的手段,观测SQL的执行情况、快速诊断。
备份:原生支持物理备份和日志备份功能。
其次,OMS支持 OceanBase-CE、MySQL、PostgreSQL、TiDB、Kafka 和 RocketMQ 等多种类型的数据源与 OceanBase 社区版进行实时数据传输,以及 OceanBase 社区版 MySQL 租户间的数据迁移。
通过自动化、流程化的方案,轻松对数据进行迁移,简化迁移流程。
在一个流程中支持结构同步、数据同步、数据增量同步、数据反向同步,减少运维人员的运维工作。
支持通过匹配规则对部分表进行同步、对表中的数据进行同步。
最后,ODC作为开发者中心,提供了许多便捷的功能,比如:
集成了SQL开发的IDE功能,同时在开发过程中可以对SQL进行审核,对于高风险的操作进行二次确认。
支持工作流,可以实现数据源访问权限的审批,增加协同性能。
支持与企业AD做集成身份验证,方面管理账户,增强安全性。同时也可以当做数据库访问管理的平台(类似堡垒机),关键是免费!
支持对接OB MYSQL模式,同时还支持了MySQL、Oracle的接入,是企业数据库IDE薅羊毛的好工具。
支持定时任务:
SQL 计划:定期执行一段SQL,类似于event功能。
分区计划:原生支持分区创建、删除的功能,非常好用,可以参照我的博客《zabbix 大表在OB中实现表分区管理》。
数据归档:对一些历史数据通过一定的规则进行归档(从一个表(库)中复制到另外一个表(库))当中。
数据清理:按照规则,定时清理表(库)中的数据。
对与所有的执行作业可以查看执行是否成功,方便运维。
所有功能开箱即用,省时省力。
10
对于OceanBase的这些特性,结合我们实际的业务,未来会将更多的库迁移到OceanBase上面来。目前正在进行的有生产设备数据采集系统(下称:数采系统)、报表系统的迁移。
数采系统的需求也是类似于Zabbix,特点是:
数据量大。部分数据都以秒级从生产设备中采集数据,并发量大,数据量大,导致整个库里的数据会很大。
数据时序性。部分表被设计成了横表,横表中存在时序字段,在查询时需要将横表转换为纵表,查询过程复杂关联多,这里我们使用了窗口函数来解决时序内采集结果的对比分析,后面可以升级到OB新版,采用列存方式实现。
定期归档、清理:这个功能可以结合ODC的数据归档和数据清理功能。在清理过程中结合并行方式,加快处理的能力。
表分区。对大表进行分区管控。
报表系统的特点是:
典型要求HTAP,借助于OB支持慢查询队列,可以有效的保障大事务、大查询最大程度不干涉短平快事务的执行。
OceanBase的功能非常强大是事实,每一个事务都在不断进步。从我们试用到使用OceanBase的过程中,有“坑”也有惊喜,遇到问题,先看手册,自己思考。如果自己想不明白可以再社区的问答板块和用户答疑群寻求帮助,社区的老师会及时答复(相较于其他开源社区,OceanBase的及时性和准确性要好很多)。
OceanBase还在不断发展,对于4.3版本中的列存、物化视图等功能还等着我们探索,道阻且长,行则将至。
- END -
IT168与ITPUB技术社区强强联手,收集数百款主流数据库产品,重磅推出“数据库全景图”,旨在打造一款集知识普及、产品对比、选型参考于一体的综合性资源平台。“数据库全景图(11月版)”可扫描上方左侧二维码回复关键词获取,识别右侧二维码直达“数据库全景图”链接(右上角浏览器打开获取更好体验)。