基于TableStore的海量气象格点数据解决方案实战

前言

气象数据是一类典型的大数据,具有数据量大、时效性高、数据种类丰富等特点。气象数据中大量的数据是时空数据,记录了时间和空间范围内各个点的各个物理量的观测量或者模拟量,每天产生的数据量常在几十TB到上百TB的规模,且在爆发性增长。如何存储和高效的查询这些气象数据越来越成为一个难题。

传统的方案常常采用关系型数据库加文件系统的方式实现这类气象数据的存储和实时查询,这种方案在可扩展性、可维护性和性能上都有一些缺陷,随着数据规模的增大,缺点越来越明显。最近几年,业界开始越来越多的基于分布式NoSQL来解决这一问题,比如基于TableStore来实现气象格点数据的存储和查询。TableStore是一款阿里自研的分布式NoSQL服务,可以提供超大规模的存储容量,支撑超大规模的并发访问和低延迟的性能,可以很好的解决气象数据的规模和查询性能问题。

我们之前也写过相关的解决方案文章《基于云上分布式NoSQL的海量气象数据存储和查询方案》,也有一些客户基于这个方案进行了开发。出于减少客户开发难度,提供通用的实现的想法,我们最近开发了一个TableStore-Grid的Library,基于这个Library用户可以非常方便的实现气象格点数据的存储、查询和管理。本文作为一个实战文章,主要讲解这一解决方案的设计以及使用方式。

背景

格点数据的特点

格点数据具有明显的多维特点,以模式系统每次产生的数据为例,一般包含以下五个维度:

  1. 物理量,或者称为要素:温度、湿度、风向、风速等等。
  2. 预报时效:未来3小时、6小时、9小时、72小时等等。
  3. 高度。
  4. 经度。
  5. 纬度。

当我们固定某一要素某一预报时效,那么高度、经度、纬度就构成一个三维网格数据,如下图所示(图片来自互联网)。每个格点代表了一个三维空间上的点,上面的数值为该点在某一预报时效(比如未来三小时)下,某一物理量(比如温度)的预报值。

假设一个三维格点空间包含10个不同高度的平面,每个平面为一个2880 x 570的格点,每个格点保存一个4字节数据,那么这三维的数据量为2880 x 570 x 4 x 10, 大约64MB。这仅仅是某个模式系统对某个物理量某一时效下的一次预报,可见模式数据的总量是非常大的。

格点数据的查询方式

预报员会通过页面的形式浏览各种模式数据(格点数据),并进行数值模式预报。这个页面需要提供多种模式数据的查询方式,比如:

  1. 查询一个经纬度平面的格点数据:比如未来三小时全球地面温度的格点数据,或者未来三小时浙江省地面温度数据。
  2. 查询某个格点的时间序列数据:比如阿里云公司所在地未来3小时、未来6小时、一直到未来72小时的温度。
  3. 查询不同物理量的数据:比如查询某一预报时效、某一高度、某一点的全部物理量的预报数据。
  4. 查询不同模式系统产生的数据:比如同时查询欧洲中心的某一模式数据和中国气象机构产生的对应数据等。

上面提到,一个格点数据集一般是一个五维结构,各种查询方式实际上就是对这个五维数据进行切分,比如查询某个平面,每个剖面,某个点序列,某个三维、四维子空间等等。而我们的方案设计要保证在各种查询条件的查询性能,这是数据查询方面的主要技术难点。

基于TableStore的方案设计

标准化格点数据模型

首先,我们定义一个规整的五维网格数据为一个GridDataSet,表示一个格点数据集,按照维度顺序,其五维分别为:

  1. variable:变量,比如各种物理量。
  2. time:时间维度。
  3. z: z轴,一般表示空间高度
  4. x: x轴,一般表示经度或纬度。
  5. y:y轴,一般表示经度或纬度。

GridDataSet = F(variable, time, z, x, y)。

一个GridDataSet除了包含五维数据,以及各个维度的长度等外,还包含一些其他信息:

GridDataSetId:唯一标记这个GridDataSet的Id。
Attributes:自定义属性信息,比如该数据的产生时间、数据来源、预报类型等等。用户可以自由定义自定义属性,也可以给某些属性建立索引,建立索引后就可以通过各种组合条件来查询符合条件的数据集。

举个例子来说,假设某种气象预报,每次预报未来72小时的每个整点的各个高度、各个经纬度的各种物理量,则这次预报就是一个标准的五维数据,是一个单独的数据集(GridDataSet),下一次相同的预报则是另一个数据集,这两个数据集需要有不同的GridDataSetId。这两个数据集比较类似,只是起报时间不同,但是因为起报时间不在五维模型中(五维内的时间为一次预报中的未来不同时刻),所以属于不同的数据集,起报时间可以作为数据集的自定义属性。本方案中,也支持对自定义属性设置条件进行检索。

数据存储方案

我们设计了两张表分别存储数据集(GridDataSet)的meta和data,meta表示这个数据集的各种元数据,比如GridDataSetId、各维度长度、自定义属性等等,data表示这个数据集里实际的网格数据。data相比meta在数据大小上要大很多。

为什么要分为meta和data两张表分开存储,主要是出于这样的考虑:

  1. 用户会有根据多种条件查询数据集的要求,比如查询最近有哪些数据集已经完成入库,或者查询表中有哪些某种类型的数据集等。传统方案中主要是通过MySQL等关系型数据库来存储,在本方案中我们通过单独的meta表来存储,并通过TableStore的多元索引功能来实现多条件的组合查询和多种排序方式,相比传统方案更加易用。
  2. 在查询格点数据之前,一般要知道格点数据中各维度的长度等信息,这些信息就是存储在meta表中的,即需要先查询meta表,再查询data表。因为meta数据一般都很小,因此查询效率相比查询data要高,多一次查询并不会明显增加延迟。

meta表设计

meta表的设计比较简单,主键只有一列,记录GridDataSetId,因为GridDataSetId就可以唯一标记一个GridDataSet。各种系统属性和自定义属性保存在meta表的属性列中。

查询meta表有两种方式,一种是通过GridDataSetId直接查询,另外一种是通过多元索引,可以根据多种属性条件组合进行查询,比如筛选某种类型的数据,按照入库时间从新到老返回等。

data表设计

data表的设计要解决五维数据在不同的切分模式下的查询效率问题,不能简单直接的对数据进行存储。

首先,为了查询效率最高,我们要尽量减少一次查询需要扫描的数据量。一个数据集的数据量可能在几GB的级别,但是一次查询往往只需要其中的几MB的数据,如果无法高效的定位要查询的数据,那么就要扫描全部的几GB的数据,从中筛选出符合某个范围的数据,显然效率是很低的。那么怎么才能做到高效的定位到需要的数据之中呢?

我们首先设计一种表结构设计方式,我们使用四列主键列,分别为:

GridDataSetId:数据集Id,唯一标记这个数据集。
Variable:变量名,即五维模型中的第一维。
Time:时间,即五维模型中的第二维。
Z:高度,即五维模型中的第三维。

这四列主键列标记一行TableStore中的数据,这行数据需要保存后两维的数据,即一个格点平面。

这种设计下,对于五维中的前三维,我们都可以通过主键列的值来定位,即对于前三维的每一种情况,都对应TableStore中的一行。因为前三维分别代表变量、时间和高度,一般而言不会特别的多,每个维度在几个到几十个的级别,我们可以通过一些并行查询的方法来加速查询速度。

剩下的问题就在于后两维数据如何存储和查询。首先后两维代表了一个水平的平面,一般是一个经纬度网格,这两维的大小是比前三维要大很多的,每维在几百到几千的级别,随着数值预报越来越精细化,这个网格的大小还会成倍增加。这样的一个稠密的网格数据,我们不能把每个格点都用一列来保存,这样列的数量会非常多,存储效率也会非常的低。另一方面,如果我们把一个平面的格点数据存储到一列中,在整读整取时效率比较高,但是如果只读取某个点,就会读取很多的无效数据,效率又会变得比较低。因此我们采取一种折中的方案,对平面的二维数据再次进行切分,切分成更小的平面数据块,这样就可以做到只读取部分数据块,而不总是读取整个平面,因此极大的提高了查询性能。

方案实现

基于上面的存储方案,我们实现了一个TableStore-Grid的library,提供以下接口:

public interface GridStore {/*** 创建相关的meta、data表,数据录入前调用。* @throws Exception*/void createStore() throws Exception;/*** 写入gridDataSet的meta信息。* @param meta* @throws Exception*/void putDataSetMeta(GridDataSetMeta meta) throws Exception;/*** 更新meta信息。* @param meta* @throws Exception*/void updateDataSetMeta(GridDataSetMeta meta) throws Exception;/*** 通过gridDataSetId获取meta。* @param dataSetId* @return* @throws Exception*/GridDataSetMeta getDataSetMeta(String dataSetId) throws Exception;/*** // 创建meta表的多元索引。* @param indexName* @param indexSchema* @throws Exception*/void createMetaIndex(String indexName, IndexSchema indexSchema) throws Exception;/*** 通过多种查询条件来查询符合条件的数据集。* @param indexName 多元索引名。* @param query 查询条件,可以通过QueryBuilder构建。* @param queryParams 查询相关参数,包括offset、limit、sort等。* @return* @throws Exception*/QueryGridDataSetResult queryDataSets(String indexName, Query query, QueryParams queryParams) throws Exception;/*** 获取GridDataWriter用于写入数据。* @param meta* @return*/GridDataWriter getDataWriter(GridDataSetMeta meta);/*** 获取GridDataFetcher用于读取数据。* @param meta* @return*/GridDataFetcher getDataFetcher(GridDataSetMeta meta);/*** 释放资源。*/void close();
}public interface GridDataWriter {/*** 写入一个二维平面。* @param variable 变量名。* @param t 时间维的值。* @param z 高度维的值。* @param grid2D 平面数据。* @throws Exception*/void writeGrid2D(String variable, int t, int z, Grid2D grid2D) throws Exception;
}public interface GridDataFetcher {/*** 设置要查询的变量。* @param variables* @return*/GridDataFetcher setVariablesToGet(Collection<String> variables);/*** 设置要读取的各维度起始点和大小。* @param origin 各维度起始点。* @param shape 各维度大小。* @return*/GridDataFetcher setOriginShape(int[] origin, int[] shape);/*** 获取数据。* @return* @throws Exception*/GridDataSet fetch() throws Exception;
}

下面我们分别给出数据录入、数据查询、数据集检索方面的示例。

数据录入

数据录入流程可以分为三部分:

  1. 写入putDataSetMeta接口写入数据集的meta信息。
  2. 通过GridDataWriter录入整个数据集的数据。
  3. 通过updateDataSetMeta接口更新数据集的meta信息,标记数据已经录入完成。

下面的例子中,我们读取一个NetCDF(气象格点数据常用的格式)文件,然后将其中的数据通过GridDataWriter录入到TableStore中。通过GridDataWriter每次写入时,只能写入一个二维平面,所以我们需要在外层进行3层循环,分别枚举变量维、时间维、高度维的值,然后读取对应的二维平面的数据进行录入。

public void importFromNcFile(GridDataSetMeta meta, String ncFileName) throws Exception {GridDataWriter writer = tableStoreGrid.getDataWriter(meta);NetcdfFile ncFile = NetcdfFile.open(ncFileName);List<Variable> variables = ncFile.getVariables();for (Variable variable : variables) {if (meta.getVariables().contains(variable.getShortName())) {for (int t = 0; t < meta.gettSize(); t++) {for (int z = 0; z < meta.getzSize(); z++) {Array array = variable.read(new int[]{t, z, 0, 0}, new int[]{1, 1, meta.getxSize(), meta.getySize()});Grid2D grid2D = new Grid2D(array.getDataAsByteBuffer(), variable.getDataType(),new int[] {0, 0}, new int[] {meta.getxSize(), meta.getySize()});writer.writeGrid2D(variable.getShortName(), t, z, grid2D);}}}}
}

数据查询

GridDataFetcher支持对五维数据进行任意维度的查询。第一维是变量维,通过setVariablesToGet接口设置要读取哪些变量,其余四维通过设置起始点(origin)和读取的大小(shape)就可以实现任意维度读取。

public Array queryByTableStore(String dataSetId, String variable, int[] origin, int[] shape) throws Exception {GridDataFetcher fetcher = this.tableStoreGrid.getDataFetcher(this.tableStoreGrid.getDataSetMeta(dataSetId));fetcher.setVariablesToGet(Arrays.asList(variable));fetcher.setOriginShape(origin, shape);Grid4D grid4D = fetcher.fetch().getVariable(variable);return grid4D.toArray();
}

多条件检索数据集

本方案中,对Meta表建立多元索引后,可以支持通过各种组合条件来进行数据集检索,查询出符合条件的数据集,这个功能对于气象管理系统来说非常重要。

下面举一个例子,假设我们要查询已经完成入库的,创建时间为最近一天的,来源为ECMWF(欧洲中期天气预报中心)或者NMC(全国气象中心),精度为1KM的气象预报,并按照创建时间从新到老排序,可以用以下代码实现:

查询条件: (status == DONE) and (create_time > System.currentTimeMillis - 86400000) and (source == "ECMWF" or source == "NMC") and (accuracy == "1km")

QueryGridDataSetResult result = tableStoreGrid.queryDataSets(ExampleConfig.GRID_META_INDEX_NAME,QueryBuilder.and().equal("status", "DONE").greaterThan("create_time", System.currentTimeMillis() - 86400000).equal("accuracy", "1km").query(QueryBuilder.or().equal("source", "ECMWF").equal("source", "NMC").build()).build(),new QueryParams(0, 10, new Sort(Arrays.<Sort.Sorter>asList(new FieldSort("create_time", SortOrder.DESC)))));

是不是非常简单?这一部分功能利用了TableStore的多元索引,多元索引可以实现多字段组合查询、模糊查询、全文检索、排序、范围查询、嵌套查询、空间查询等功能,给元数据管理场景提供了强大的底层能力。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519226.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯会议扩容背后:100万核计算资源全由自研服务器星星海支撑

疫情期间&#xff0c;远程会议及协同办公需求暴增。从1月29日开始到2月6日&#xff0c;腾讯会议每天都在进行资源扩容&#xff0c;日均扩容云主机接近1.5万台&#xff0c;8天总共扩容超过10万台云主机&#xff0c;共涉及超百万核的计算资源投入。 值得一提的是&#xff0c;腾讯…

实时计算无线数据分析

本文为您介绍实时计算在无线数据分析中的应用。阿里云实时计算可以为无线App的数据分析场景实时化助力&#xff0c;帮助您做到实时化分析手机AP的各项指标&#xff0c;包括App版本分布情况、Crash检测和等。 阿里云移动数据分析 (Mobile Analytics&#xff0c;下面简称MAN) 是…

对话阿里云Alex Chen:下一代存储应如何面对云转型?

数字经济"乘云而上"。 十年前&#xff0c;阿里云开始自主研发云计算操作系统飞天之路&#xff0c;开启了中国云时代&#xff1b; 十年后&#xff0c;阿里云在中国市场份额超过2-8名总和&#xff0c;培育了整个中国云计算市场&#xff0c;数字经济在云上蓬勃发展。 …

写给大数据从业者:数据科学的5个陷阱与缺陷

来源 | AI 前线作者 | 陈炬&#xff0c;责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;导读&#xff1a; 这篇分享主要总结了数据从业人员在实践中可能遇到的陷阱与缺陷。跟其他新起的行业一样&#xff0c;数据科学从业人员需要不停的去考虑现…

阿里云数据库自研产品亮相国际顶级会议ICDE 推动云原生数据库成为行业标准

4月9日&#xff0c;澳门当地时间下午4:00-5:30&#xff0c;阿里云在ICDE 2019举办了主题为“云时代的数据库”的专场分享研讨会。 本次专场研讨会由阿里巴巴集团副总裁、高级研究员&#xff0c;阿里云智能数据库产品事业部负责人李飞飞&#xff08;花名&#xff1a;飞刀&#…

大神如何一招完美解决Hadoop集群无法正常关闭的问题!| 博文精选

来源 | CSDN 博客作者 | Alice菌&#xff0c;责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;相信对于大部分的大数据初学者来说&#xff0c;一定遇见过Hadoop集群无法正常关闭的情况。有时候当我们更改了Hadoop内组件的配置文件后&#xff0c;…

现代IM系统中的消息系统架构 - 架构篇

前言 IM全称是『Instant Messaging』&#xff0c;中文名是即时通讯。在这个高度信息化的移动互联网时代&#xff0c;生活中IM类产品已经成为必备品&#xff0c;比较有名的如钉钉、微信、QQ等以IM为核心功能的产品。当然目前微信已经成长为一个生态型产品&#xff0c;但其核心功…

华南地区最大数据中心上线 阿里云河源数据中心正式开服

2月18日&#xff0c;阿里云在官网宣布&#xff0c;河源数据中心正式对外提供服务。这是华南地区规模最大的绿色数据中心&#xff0c;可容纳超过30万台服务器&#xff0c;作为深圳地域的新可用区为华南地区上百万企业客户提供领先的云计算、人工智能、物联网等服务。 据悉&#…

在阿里做了五年技术主管,我有话想说

阿里妹导读&#xff1a;在历史文章《如何成为优秀的技术主管&#xff1f;》中&#xff0c;阿里巴巴高级技术专家云狄从开发规范、开发流程、技术规划与管理三个角度&#xff0c;分享对技术 TL 的理解与思考。 今天的文章&#xff0c;他将继续深入探讨这一话题&#xff0c;从管理…

Fish Redux中的Dispatch是怎么实现的?

零.前言 我们在使用fish-redux构建应用的时候&#xff0c;界面代码&#xff08;view&#xff09;和事件的处理逻辑&#xff08;reducer&#xff0c;effect&#xff09;是完全解耦的&#xff0c;界面需要处理事件的时候将action分发给对应的事件处理逻辑去进行处理&#xff0c;…

码上用它开始Flutter混合开发——FlutterBoost

开源地址: https://github.com/alibaba/flutter_boost为什么需要混合方案 具有一定规模的App通常有一套成熟通用的基础库&#xff0c;尤其是阿里系App&#xff0c;一般需要依赖很多体系内的基础库。那么使用Flutter重新从头开发App的成本和风险都较高。所以在Native App进行渐…

Linux系统常用指令总结

来源 | CSDN 博客作者 | 不撸代码闲得慌&#xff0c;责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;系统的运行级别0&#xff1a;关机1&#xff1a;单用户模式&#xff08;可以找回丢失的密码&#xff09;2&#xff1a;多用户状态没有网络服务…

像数据科学家一样思考:12步指南(下)

第三阶段-完成 一旦产品构建完成&#xff0c;你仍然需要做一些事情来使项目更加成功并使你的未来生活更轻松。那么我们如何完成数据科学项目呢&#xff1f; 10-交付产品 完成阶段的第一步是产品交付。为了创建可以交付给客户的有效产品&#xff0c;首先必须了解客户的观点。其…

基于Tablestore管理海量快递轨迹数据架构实现

快递轨迹管理 对于一个快递公司&#xff0c;在全国范围内有着大量的快递点、快递员、运输车辆以及仓储中心。而快递自产生后&#xff0c;就会在这些地点、人物之间流转。因而&#xff0c;一套完善的快递管理追踪系统是快递公司的重要管理工具&#xff1b; 用户通过平台客户端…

完了!Python黄了! 80%的程序员:痛快!你怎么看?

Python真的万能语言&#xff1f;在我的一个朋友看来&#xff0c;他坚信 Python 可以做任何事情。其实我是不服的&#xff0c;因为我在某网站看到有条评论&#xff1a;Python将要黄了&#xff01;事实究竟如何&#xff1f;这篇文章会揭开这个黑幕&#xff0c;让程序员看清现实&a…

趣谈预留实例券,一文搞懂云上省钱最新玩法

ECS近期推出了预留实例券&#xff08;Reserved Instances&#xff09;&#xff0c;简称RI&#xff0c;这东西很cool&#xff0c;今天我们聊聊这个。 首先这篇Blog不是文档&#xff0c;读完后想详细了解文档的朋友请点击 一个小故事 我来给大家讲一个故事理解云上的几种付费方…

到底什么是空指针?如何避免空指针_01

文章目录1. 场景案例2. 针对于空指针的场景&#xff0c;有哪些方式可以避免问题1. 场景案例 package com.gblfy;import org.springframework.beans.factory.annotation.Autowired;import javax.servlet.http.HttpServletRequest;/*** 理解什么是空指针*/ public class WhatIsn…

在 IntelliJ IDEA 中部署应用到服务器(Eclipse)

在之前的文章《在 Intellij IDEA 中部署 Java 应用到 阿里云 ECS》中讲解了如何将一个本地应用部署到阿里云 ECS 上去&#xff0c;有些读者反馈目前还有一些测试机器是在经典网络&#xff0c;甚至是在本地机房中&#xff0c;咨询是否可以通过 Cloud Toolkit 插件将应用部署到这…

linux所有文件打包压缩,Linux基础教程:对文件打包压缩

一、须知文件数量太多&#xff0c; 如果需要拷来拷去是不是很麻烦&#xff1f; 怎么办&#xff1f; 打包&#xff01;文件太大&#xff0c;通过网络下载、传输会不会很费时间&#xff1f; 怎么办&#xff1f; 压缩&#xff01;在Linux环境中&#xff0c;打包压缩文件的…