二、金融业企业服务总线链路追踪监控分析平台的建设实践--ORACLE存储方案

背景

16年的时候,公司还没有海量数据的存储使用经验,但是公司作为一个金融公司,ORACLE的运维经验非常丰富,当时就想着先解决能使用的问题,因此尝试使用ORACLE来存储。

监控系统主要存储两种数据:

1、统计数据:
a.分钟表、小时表:来自实时计算引擎计算的结果,数据实时更新,每天几百万条,但每天更新次数7-8千万次,保存7天数据,表总量4000万+,每天定时清理7天前的数据。
b.天表、月表:每天晚上定时任务执行存储过程合并小时表数据得到,量少,只有几万数据,永久保存。
2、明细数据:7亿条/日的trace数据,保存7天数据,表总量在50亿+,每天定时清理7天前数据。

从以上数据的特点分析可知:
天表、月表:数量少,ORACLE完全可以支持;
分钟表、小时表:只要正确建好索引,查询性能也完全没有压力,但是因为更新频繁,尽量不要建不必要的索引,更新时使用批量提交的方式,基本上也没多大问题;
明细表:需要考虑读写两方面的性能
查询:50亿条数据中筛选数据;
写:7亿/日的数据写入;
删除:清理7亿条7天前数据。

因此,我们主要针对明细表进行优化设计。

BTree存储引擎原理

B+tree
上图是一个典型的B+TREE索引:
1、 叶子节点(Leaf node):数据行的键值(key value)、键值对应数据行的 ROWID;
2、分支节点(Branch node):最小的键值前缀(minimum key prefix),用于在(本块的)两个键值之间做出分支选择,指向包含所查找键值的子块(child block)的指针;
3、 根节点(Root node):一个B树索引只有一个根节点,它实际就是位于树的最顶端的分支节点;
4、分支节点块(包括根节点块):每条记录都具有两个字段,第一个字段表示当前该分支节点块下面所链接的索引块中所包含的最小键值;第二个字段为四个字节,表示所链接的索引块的地址,该地址指向下面一个索引块。在一个分支节点块中所能容纳的记录行数由数据块大小以及索引键值的长度决定。从上图可以看到,对于根节点块来说,包含三条记录,分别为(0 B1)、(500 B2)、(1000 B3),它们指向三个分支节点块。其中的0、500和1000分别表示这三个分支节点块所链接的键值的最小值。而B1、B2和B3则表示所指向的三个分支节点块的地址。
5、叶子节点块:其所包含的条目与分支节点一样,每条记录也具有两个字段。第一个字段表示索引的键值,对于单列索引来说是一个值;而对于多列索引来说则是多个值组合在一起的。第二个字段表示键值所对应的记录行的ROWID,该ROWID是记录行在表里的物理地址。如果索引是创建在非分区表上或者索引是分区表上的本地索引的话,则该ROWID占用6个字节;如果索引是创建在分区表上的全局索引的话,则该ROWID占用10个字节
6、假设存储引擎中块的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗3)。也就是说一个深度为3的B+Tree索引可以维护103 * 10^3 * 10^3 = 10亿 条记录。
7、B树的写入过程:是一次原位写入的过程,主要分为两个部分,首先是查找到对应的块的位置,然后将新数据写入到刚才查找到的数据块中,然后再查找到块所对应的磁盘物理位置,将数据写入去。当然,在内存比较充足的时候,因为B树的一部分可以被缓存在内存中,所以查找块的过程有一定概率可以在内存内完成,不过为了表述清晰,我们就假定内存很小,只够存一个B树块大小的数据吧。可以看到,在上面的模式中,需要两次随机寻道(一次查找,一次原位写),才能够完成一次数据的写入,代价还是很高的。
8、B树的查询过程:假设缓存不命中的情况下,深度为3的BTREE,需要4次IO就能查询出数据。

方案

我们已经了解了Btree的原理,通过原理可知,即使50亿条数据,只有通过正确的索引,只需要几个IO就能查询出数据;但是对写入数据,每条数据都至少需要一次IO,7亿/日的数据,平均IOPS达8100,考虑到早晚高峰,高峰IOPS至少30000,这么高的IOPS,DB绝对撑不住的。
我们都知道分库分表可以降低IO压力(不同表对应不同的数据文件,不同的数据文件对应不同的物理磁盘),但是分库分表会增加应用的维护难度(维护多个DB或多个表,同时应用层要实现数据流分发不同表或DB的逻辑),因此,我们选择使用ORACLE复合分区技术达到同样的效果。

来看看怎么实现的
1、DB脚本:
create table mydata.monitor_trace
(
traceId varchar2(100),
departAndDay varchar2(50);
sTime timestamp;

) partition by list(departAndDay) subpartition by HASH(traceId)
subpartition template(
subpartition trace_1,
subpartition trace_2,

subpartition trace_31,
subpartition trace_32
)(
partition departmentA_1 values (‘departmentA_1’),
partition departmentA_2 values (‘departmentA_2’),
partition departmentA_3 values (‘departmentA_3’),
partition departmentA_4 values (‘departmentA_4’),
partition departmentA_5 values (‘departmentA_5’),
partition departmentA_6 values (‘departmentA_6’),
partition departmentA_7 values (‘departmentA_7’),
partition departmentB_1 values (‘departmentB_1’),
partition departmentB_2 values (‘departmentB_2’),
partition departmentB_3 values (‘departmentB_3’),
partition departmentB_4 values (‘departmentB_4’),
partition departmentB_5 values (‘departmentB_5’),
partition departmentB_6 values (‘departmentB_6’),
partition departmentB_7 values (‘departmentB_7’),
partition other_1 values (‘other_1’),
partition other_2 values (‘other_2’),
partition other_3 values (‘other_3’),
partition other_4 values (‘other_4’),
partition other_5 values (‘other_5’),
partition other_6 values (‘other_6’),
partition other_7 values (‘other_7’)
);
create index mydata.local_idx_monitor_trace_id on mydata.monitor_trace(traceId) local;

2、应用层代码很简单,主要讲思路
根据trace数据构造分区建值:departAndDay,目的是让不同部门不同日期的数据流入不同的分区表。我们找了两个流量最大的部门划分出单独的分区表:departmentA,departmentB,其他流量小的划分到other分区表保存
举例说明,假如今天是周二,来至departmentA的数据会生成一个值为departmentA_2键值,保存数据的时候,ORACLE会根据departmentA_2将数据保存到对应的分区表中。

复合分区视图
上图是复合分区表视图,从图可知:
能同时提供读的分区表数量:3 * 7 * 32 = 672个

能同时提供写的分区表数量:3 * 32 = 96个(当前日期的分区表提供写)

写性能:每个分区表的IOPS从30000降为 30000/96 = 312,oracle完全没有压力;

读性能:应用层定位到一层列表分区具体分区号,然后在二层hash分区的32个分区表中依次通过local索引检索,直到查到数据为止。平均每个hash表数据量在:50亿 / 672 = 740万,最多查32个hash表,根据我们的使用经验,大部分1s以内就可以检索出来。

3、数据清理
上面讲了,一天要产生7亿条数据,这些数据越新价值越大,七天前的数据基本没有什么价值了(从监控来说),必须清理出去,否则,等数据积累到一定程度之后就会严重影响DB性能。
a、明细数据表:因为数据按部门及时间做了分区,7天前的数据只需删除对应的分区即可。
假如今天是周一,那么就要删除上周二的数据,使用如下命令即可,效率非常高。
alter table monitor_trace truncate partition departmentA_2;
b、分钟统计数据:所有数据都在一个大表中,需要从表中筛选出7天前的数据进行删除,如下写法效率较高:
procedure deleteData() is
TYPE t_cur IS REF CURSOR;
monc t_cur;
type tmonc is table of varchar2(32) index by binary_integer;
insmon tmonc;
v_cur_sql VARCHAR2(2000);
v_cur_sql := 'select rowid rwid from monitor_statis_min where time_min like ‘xxxx%’ ';
v_delete_sql := ‘delete from monitor_statis_min where rowid = :1’;
OPEN mon for v_cur_sql;
loop
fetch monc bulk collect into insmonc limit 5000;
forall i in insmonc.first…insmonc.last
execute immediate v_delete_sql using insmonc(i);
commit;
exit when (monc%notfound);
end loop;
close monc;
end deleteData;

总结

使用oracle来存储虽然暂时解决了用户使用的问题,但是此方案存在明显的缺陷:
1、性能问题:范围查询的时候非常慢,必须严格控制用户输入的查询条件范围,比如控制在5分钟内的数据检索。
2、费用昂贵:18年的时候数据是2T+,费用都要200万,19年4月的时候存储量达到5T,而目前我们的量已经达到了20T,费用更不敢想象。
3、扩展性:oracle部署在一台48核的物理机上,一旦CPU核数用完,将无法扩容,无法支撑业务快速增长的需求。
4、数据倾斜:一层列表分区是按历史数据提取出来的,随着业务的发展,一些之前调用量少的部门数据量会成倍增长,造成数据倾斜,影响性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值