基于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) 是…

php依赖注入解决什么问题,php – 了解依赖注入的问题

我正在建立一个小项目&#xff0c;尽量教自己尽可能多的基础知识&#xff0c;对我而言&#xff0c;意味着不要使用预制框架(As Jeffonce put it&#xff0c;“不要重塑轮子&#xff0c;除非你计划学习更多关于轮子“[重点])&#xff0c;并遵循测试驱动发展的原则。在我的追求中…

图片批量转换pdf文件

文章目录1. 依赖2. 工具类抽取1. 依赖 <!--图片转换pdf Start--><!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf --><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version&g…

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

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

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

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

winform php 交互,WinForm开发,窗体显示和窗体传值

以前对WinForm窗体显示和窗体间传值了解不是很清楚最近做了一些WinForm开发,把用到的相关知识整理如下A.WinForm中窗体显示显示窗体可以有以下2种方法&#xff1a;Form.ShowDialog方法 (窗体显示为模式窗体)Form.Show方法 (窗体显示为无模式窗体)2者具体区别如下&#xff1a;1.…

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

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

常用git

常用git #在此分支基础上创建test分支&#xff0c;并切换到test 注&#xff1a;本分值的以前提交记录会保存 git checkout -b test #在此分支基础上创建uat分支&#xff0c;并切换到uat 注&#xff1a;本分值的以前提交记录会保存 git checkout -b uat #将本地的uat分支推送…

springboot Java实现多文件的zip压缩操作 + 通过浏览器下载文件的两种方式

注只适配utf-8的场景&#xff0c;待完善&#xff01; 压缩为zip文件 通过java程序输出文件 /*** 功能:压缩多个文件成一个zip文件* param srcfile&#xff1a;源文件列表* param zipfile&#xff1a;压缩后的文件*/ public static void zipFiles(File[] srcfile, File zipfil…

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

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

oracle怎么查语句消耗时常,Oracle 查找常见耗性能的语句

-- 最耗缓存SELECT * FROM (SELECT SQL_FULLTEXT sql,buffer_gets, executions, buffer_gets/executions "Gets/Exec",hash_value,address,LAST_ACTIVE_TIMEFROM V$SQLAREAWHERE buffer_gets > 10000ORDER BY buffer_gets DESC)WHERE rownum < 10 ;-- 最多物理…

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

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

ORA-12505, TNS:listener does not currently know of SID given in connect descriptor

ORA-12505: TNS: 监听程序当前无法识别连接描述符中所给出的SID 给程序配置新的数据库的时候&#xff0c;出现了以下错误 ORA-12505, TNS:listener does not currently know of SID given in connect descriptor说明给到的sid有误,很有可能就是服务名和sid混淆使用。 首先了解…

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

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

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

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

oracle删除查询的数据库语句,Oracle简略的语句 查询 删除 修改(1)

Oracle简单的语句 查询 删除 修改(1)Dual系统表介绍数据类型&#xff1a;数据是信息数字表现形式,信息的加工处理是以大量的结构化数据为载体进行的,数据库管理系统的核心是数据库,数据库的主要对象是表,表是结构化数据存储的地方.Oracle系统也提供了大量的数据类型主要包括两大…

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

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

装载服务器安装和配置_一站式解决方案

文章目录一、安装搭建篇1.1. 安装ORACLE客户端1.2. 安装LFTP1.3. 安装DBI和DBD&#xff08;1.4. 将应用包导入数据加载服务器1.5. 数据库账号密码加密策略二、进阶篇2.1. 数据库操作2.2. 加载器操作2.3. 数据库连接2.4. 模型变更2.5. 装载数据到目标表2.6. 装载特殊场景三、特色…