基于TableStore的海量电商订单元数据管理

一、背景

订单系统存在于各行各业,如电商订单、银行流水、运营商话费账单等,是一个非常广泛、通用的系统。对于这类系统,在过去十几年发展中已经形成了经典的做法。但是随着互联网的发展,以及各企业对数据的重视,需要存储和持久化的订单量越来越大。数据的重视程度与数据规模的膨胀带来了新的挑战,原有的系统是否还能继续满足需求成了焦点?

需求场景

某电商平台A,需要进行持久化所有平台产生的订单数据。同时,基于所有的订单数据,系统又需要向外提供面向多种角色:消费者、店家、平台三类人群的多元化的查询服务。消费者可以查询自己的历史订单,商家可以统计热销产品,平台也可以分析用户行为、平台交易规模等。主要查询方式涵盖订单的多维度检索,以及订单数据的分析、统计等,例如:
面向消费者:【A消费者】*【近1年】*【产品名含'电脑'字段】订单查询;
面向店家:【B店家】*【近1个月】*【每个产品】销售量排名;
......

技术点

在订单场景中,技术上通常需要考虑的技术点,主要包含如下几个方面:

  • 查询能力:需要具备丰富的查询类型,如多维度、范围、模糊查询等,同时具备排序、统计等功能;
  • 数据量:存储海量数据的同时,满足强一致、高可用、低成本等要求;
  • 服务性能:应对高并发请求高并发的同时,保证低延迟;

 

二、方案演进

应对订单场景,电商通常会采用MySQL传统方案。借助关系型数据库强大的查询能力,用户可直接通过SQL语句实现订单数据的多维度查询、数据统计等。所谓数据膨胀,分为横向、纵向两种,横向即不断迭代引入的新字段维度,纵向即总的存储数据量。在面对这两种订单数据膨胀上,单MySql方案逐渐变得吃力。 SQL + NoSQL的组合方案(以下称:组合方案)便应运而生,借助两个数据库各自的优势分别解决不同场景各自的需求。但组合方案同样也带来了新的问题,组合方案牺牲空间成本,同时也增加了开发工作量与运维复杂度。在保证数据一致性上产生额外开销。

下面让我们看一下如下几个常规方案:

常规方案

1、MySql分库分表方案

MySql自身拥有强大的数据查询、分析功能,基于MyQql创建订单系统,可以应对订单数据多维查询、统计场景。伴随着订单数据量的增加,用户会采取分库、分表方案应对,通过这种伪分布式方案,解决数据膨胀带来的问题。但数据一旦达到瓶颈,便需要重新创建更大规模的分库+数据的全量迁移,麻烦就会不断出现。数据迭代、膨胀带来的困扰,是MySql方案难于逾越的。仅仅依靠MySql的传统订单方案短板凸显。
1、数据纵向(数据规模)膨胀:采用分库分表方案,MySql在部署时需要预估分库规模,数据量一旦达到上限后,重新部署并做数据全量迁移;
2、数据横向(字段维度)膨胀:schema需预定义,迭代新增新字段变更复杂。而维度到达一定量后影响数据库性能;

2、MySql+HBase方案

引入双数据的方案应运而生,通过实时数据、历史数据分存的方案,可以一定程度解决数据量膨胀问题。该方案将数据归类成两部分存储:实时数据、历史数据。同时通过数据同步服务,将过期数据同步至历史数据。
1、实时订单数据(例如:近3个月的订单):将实时订单存入MySql数据库。实时订单的总量膨胀的速度得到了限制,同时保证了实时数据的多维查询、分析能力;
2、历史订单数据(例如:3个月以前的订单):将历史订单数据存入HBase,借助于HBase这一分布式NoSql数据库,有效应对了订单数据膨胀困扰。也保证了历史订单数据的持久化;
但是,该方案牺牲了历史订单数据对用户、商家、平台的使用价值,假设了历史数据的需求频率极低。但是一旦有需求,便需要全表扫描,查询速度慢、IO成本很高。而维护数据同步又带来了数据一致性、同步运维成本飙升等难题;

3、MySql+Elasticsearch方案

组合方案还有MySql+Elasticsearch,该方案同样是将数据分两部分存储,可以一定程度解决订单索引维度增长问题。用户自己维护数据同步服务,保证两部分数据的一致性;
1、全量数据:将全量的订单数据存入MySql数据库,订单ID之外的数据整体存为一个字段。该全量数据作为持久化存储,也用于非索引字段的反查;
2、查询数据:仅将需要检索的字段存入Elasticsearch(基于Lucene分布式索引数据库),借助于Elasticsearch的索引能力,提供可以应付维度膨胀的订单数据,然后必要时反查MySql获取订单完整信息;
该方案应付了数据维度膨胀带来的困扰,但是随着订单量的不断膨胀,MySql扩展性差的问题再次暴露出来。同时数据同步至Elasticsearch的方案,开发、运维成本很高,方案选择也存在弊端。

能力分析MySqlHBaseElasticsearchTableStore
存储方式行存储列存储索引存储列存储+索引存储
扩展性单机、扩展性差水平扩展水平扩展(自动)水平扩展
一致性强一致性强一致性、时序一致性 强一致性、时序一致性
检索较弱的支持不支持支持支持
数据量~ 1T,~亿行~10 PB,~万亿行~1 PB,~千亿行~10 PB,~万亿行

TableStore方案

如果使用表格存储(TableStore)研发的多元索引(SearchIndex)方案,则可以完美地解决以上问题。TableStore具有即开即用,按量收费等特点。多元索引随时创建,是海量电商订单元数据管理的优质方案。
TableStore作为阿里云提供的一款全托管、分布式NoSql型数据存储服务,具有【海量数据存储】、【热点数据自动分片】、【海量数据多维检索】等功能,天然地解决了订单数据大爆炸这一挑战;
同时,SearchIndex功能在保证用户数据高可用的基础上,提供了数据多维度搜索、统计等能力。针对多种场景创建多种索引,实现多种模式的检索。用户可以仅在需要的时候创建、开通索引。由TableStore来保证数据同步的一致性,这极大的降低了用户的方案设计、服务运维、代码开发等工作量。

 

三、基于表格存储实现的订单场景Demo

业务描述:

每成功完成一笔交易,就会生成一笔交易数据。交易数据包含了交易中的必要元素,如:交易时间、交易的双方、交易的产品、数量、价格等,这里选择最基本元素举例,仅将必要字段简历索引,格式如下:

订单持久化数据

表名:"order_table"

列名索引类型类型索引字段
order_id(主键列)KEYWORDString均匀散列的字符串
time_stampLONGlong交易时间戳
consumer_idKEYWORDString消费者
seller_idKEYWORDString商家unique编号
product_idKEYWORDString产品unique编号
product_nameKEYWORDString产品名
product_typeKEYWORDString产品类型
product_priceDOUBLEdouble产品单价
product_count double 
total_pay double 
description String 
........................

创建订单表

用户仅需维护一个数据库,按如下方式创建:用户可以通过控制台创建、管理Table,也可通过SDK

List<PrimaryKeySchema> primaryKey = Arrays.asList(new PrimaryKeySchema("order_id", PrimaryKeyType.STRING)
);TableMeta tableMeta = new TableMeta(tableName);
tableMeta.addPrimaryKeyColumns(primaryKey);
CreateTableRequest request = new CreateTableRequest(tableMeta, new TableOptions(-1, 1));
CreateTableResponse createTableResponse = otsClient.createTable(request);

创建索引

用户根据自身需求,在需要的时候随时创建索引。TableStore自动做全量、增量的索引数据同步:用户可以通过控制台创建、管理SearchIndex,也可通过SDK按如下方式创建(索引暂不支持update)

CreateSearchIndexRequest createSearchIndexRequest = new CreateSearchIndexRequest();
createSearchIndexRequest.setTableName("tableName");
createSearchIndexRequest.setIndexName("indexName");IndexSchema indexSchema = new IndexSchema();
indexSchema.setIndexSetting(new IndexSetting(1));//必写
indexSchema.setFieldSchemas(Arrays.asList(new FieldSchema("product_id", FieldType.KEYWORD).setIndex(true).setEnableSortAndAgg(true).setStore(true),new FieldSchema("product_name", FieldType.TEXT).setIndex(true),//TEXT不能设置docValuesnew FieldSchema("product_type", FieldType.KEYWORD).setIndex(true).setEnableSortAndAgg(true).setStore(true),new FieldSchema("product_count", FieldType.DOUBLE).setIndex(true).setEnableSortAndAgg(true).setStore(true),new FieldSchema("consumer_id", FieldType.KEYWORD).setIndex(true).setEnableSortAndAgg(true).setStore(true),new FieldSchema("seller_id", FieldType.KEYWORD).setIndex(true).setEnableSortAndAgg(true).setStore(true),new FieldSchema("total_pay", FieldType.DOUBLE).setIndex(true).setEnableSortAndAgg(true).setStore(true),new FieldSchema("time_stamp", FieldType.LONG).setIndex(true).setEnableSortAndAgg(true).setStore(true)
));
createSearchIndexRequest.setIndexSchema(indexSchema);CreateSearchIndexResponse createSearchIndexResponse = otsClient.createSearchIndex(createSearchIndexRequest);

数据读取

数据读取分为两类:
1、基于原生表格存储的主键列获取:getRow, getRange, batchGetRow等;
2、基于新SearchIndex功能Query:search;

主键读取

GetRowRequest getRowRequest = new GetRowRequest();PrimaryKey pk = new PrimaryKey(new PrimaryKeyColumn[]{new PrimaryKeyColumn("order_id", PrimaryKeyValue.fromString("fa960b5af"))
});SingleRowQueryCriteria singleRowQueryCriteria = new SingleRowQueryCriteria("order_table", pk);
singleRowQueryCriteria.setMaxVersions(1);
getRowRequest.setRowQueryCriteria(singleRowQueryCriteria);GetRowResponse rowResponse = o​tsClient.getRow(getRowRequest);

Search读取

新增的search接口,通过设置QueryRequest实现不同query,不同aggregation,不同sort的功能

SearchQuery searchQuery = new SearchQuery();//设置查询条件,用户发挥
searchQuery.setQuery(Query anyQuery);//做分页
searchQuery.setLimit(10);
searchQuery.setOffSet(0);SearchRequest searchRequest = new SearchRequest("tableName", "indexName", searchQuery);SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
columnsToGet.setColumns(columnsToShow);//List<String> columnsToShow
searchRequest.setColumnsToGet(columnsToGet);SearchResponse resp = otsClient.search(searchRequest);

返回结构

SearchResponse extends Response {private long totalCount;//query匹配成功数据总数private List<Row> rows;//query匹配数据列表(1)private boolean isAllSuccess;
}

场景Demo

search功能主要分为三种:(多维度)查询,排序,聚合,使用上通过三种功能的组合来实现;

场景1:多维度查询

【"consumer_001"用户】【上个月】购买【产品名含某"牙膏"字段】的订单记录
使用:BoolQuery, TermQuery, RangeQuery, MatchPhraseQuery

BoolQuery boolQuery = new BoolQuery();TermQuery termQuery = new TermQuery();
termQuery.setFieldName("consumer_id");
termQuery.setTerm(ColumnValue.fromString("consumer_001"));RangeQuery rangeQuery = new RangeQuery();
rangeQuery.setFieldName("time_stamp");
rangeQuery.greaterThanOrEqual(ColumnValue.fromLong(fromTime));
rangeQuery.lessThanOrEqual(ColumnValue.fromLong(toTime));MatchPhraseQuery matchPhraseQuery = new MatchPhraseQuery();
matchPhraseQuery.setFieldName("product_name");
matchPhraseQuery.setText("牙膏");boolQuery.setMustQueries(Arrays.asList(termQuery, rangeQuery, matchPhraseQuery
));SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(boolQuery);
searchQuery.setLimit(10);//仅构建Query
SearchRequest searchRequest = new SearchRequest("tableName", "indexName", searchQuery);

场景2:查询,排序

整个平台【上个月】【单订单支付金额】排行榜Top10
使用:RangeQuery, FieldSort

RangeQuery rangeQuery = new RangeQuery();
rangeQuery.setFieldName("time_stamp");
rangeQuery.greaterThanOrEqual(ColumnValue.fromLong(fromTime));
rangeQuery.lessThanOrEqual(ColumnValue.fromLong(toTime));//排序因子
FieldSort fieldSort = new FieldSort("total_pay");
fieldSort.setOrder(SortOrder.DESC);SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(rangeQuery);
searchQuery.setSort(new Sort(Arrays.asList(fieldSort)));
searchQuery.setLimit(10);//构建Query+Sort
SearchRequest searchRequest = new SearchRequest("tableName", "indexName", searchQuery);


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

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

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

相关文章

python 文件上传 web_pythonweb自动化三种文件上传方法

文件上传三种方式&#xff1a;(一)查看元素标签&#xff0c;如果是input&#xff0c;则可以参照文本框输入的形式进行文件上传方法&#xff1a;和用户输入是一样的&#xff0c;使用send_keys步骤&#xff1a;1、找到定位元素&#xff0c;2&#xff0c;输入文件路径eledriver.fi…

Mac 神兵利器(三) 使用Intellij IDEA打造全栈IDE

前言 作为一个开发者&#xff0c;包括职业与业余&#xff0c;相信大家都在使用集成开发环境IDE。作为专业的开发者&#xff0c;相信大家都是Full Stack Developer&#xff0c;意味着我们的日常开发通常都会涉及多种编程语言比如Java、Python、Shell、Golang、大前端等&#xf…

阿里云与MongoDB达成战略合作,成“唯一”;苹果将推出三款5G版iPhone;谷歌正式推出 TensorFlow 企业版……...

戳蓝字“CSDN云计算”关注我们哦&#xff01;嗨&#xff0c;大家好&#xff0c;重磅君带来的【云重磅】特别栏目&#xff0c;如期而至&#xff0c;每周五第一时间为大家带来重磅新闻。把握技术风向标&#xff0c;了解行业应用与实践&#xff0c;就交给我重磅君吧&#xff01;重…

RabbitMQ 镜像集群队列_集群高可用篇_03

文章目录一、普通集群搭建1. 停止 全部 MQ服务节点2. 文件(.erlang.cookie)同步3. 组成集群操作3. slave 加入集群操作4. 查看集群状态5. 访问管控台界面二、配置镜像队列2.1. 镜像队列思路2.2. 策略执行2.3. 登录管控台查看配置的策略信息一、普通集群搭建 1. 停止 全部 MQ服…

oracle数据库触发器删除不,Oracle之后删除触发器

本Oracle教程解释了如何在Oracle中创建Before Delete触发器的语法和示例。Before Delete触发器表示Oracle将在执行删除操作之前触发此触发器。语法在Oracle / PLSQL中创建Before Delete触发器的语法是&#xff1a;CREATE [ OR REPLACE ] TRIGGER trigger_nameBEFORE DELETEON t…

MaxCompute Tunnel上传典型问题场景

数据问题 Q&#xff1a;使用Tunnel Java SDK上传数据&#xff0c;上传数据可以自动分配到各个分区吗&#xff1f; A&#xff1a;目前Tunnel是无法自动上传数据并自动分配到各个分区的&#xff1a;每一次上传只支持数据上传到一张表或表的一个分区&#xff0c;有分区的表一定要…

开箱即用的安全方案:MaxCompute数据安全方案介绍

MaxCompute 是一个支持多租户的统一大数据处理平台&#xff0c;不同的用户对数据安全需求不尽相同。为了满足不同租户对数据安全的灵活需求&#xff0c;MaxCompute 支持项目空间级别的安全配置&#xff0c;ProjectOwner 可以定制适合自己的外部账号支持和鉴权模型并且在某种程度…

RabbitMQ+haproxy+keeplived 高可用负载均衡+镜像集群模式_集成负载均衡组件 Ha-Proxy_02

服务器IPhostname节点说明端口管控台地址账号密码192.168.0.115mq-01rabbitmq master5672http://192.168.0.115:15672guestguest192.168.0.117mq-02rabbitmq slave5672http://192.168.0.117:15672guestguest192.168.0.118mq-03rabbitmq slave5672http://192.168.0.118:15672gue…

腾讯云连续三年登上KVM开源贡献榜,引领KVM技术标准!

近日在KVM社区最为重要和权威的大会KVM Forum上&#xff0c;2019年全球企业对KVM的贡献排名正式对外公布&#xff0c;腾讯云凭借本年度40个patch的贡献名列全球第七&#xff0c;连续三年成为国内唯一登榜的云计算服务商。 对此腾讯云虚拟化资深研发专家李万鹏介绍&#xff0c;腾…

异构计算:软硬件结合全栈助力AI大爆发

9月20日上午&#xff0c;杭州云栖小镇E1-2会场&#xff0c;备受业界关注的2018年杭州云栖大会异构计算专场召开。 近年来&#xff0c;人工智能持续爆发&#xff0c;对算力提出了更高的要求。异构计算作为大计算时代的解决方案&#xff0c;意在打破传统通用计算的限制&#xff…

php数组获取差集,php数组求差集问题

最近在写一个项目 项目中 涉及到数组差集问题 代码如下$arr1 [["cardId" > 1010284722,"beforeAmounts" > 100,"afterAmounts" > 20,"diffAmounts" > -80,"diffStatus" > 1],["cardId" > 1010…

弹性计算平台技术:云服务器“安全”“稳定”“弹性”的基石

9月19日上午9点&#xff0c;杭州云栖小镇E1-3会场&#xff0c;2018年杭州云栖大会弹性计算平台技术专场拉开帷幕。 弹性计算系列产品是云时代的基石产品之一&#xff0c;一直备受外界关注。作为弹性计算团队在本届云栖大会的开场大戏&#xff0c;平台技术专场吸引了超过200位与…

RabbitMQ+haproxy+keeplived 高可用负载均衡+镜像集群模式_集成高性能高可用组件 Keepalived_03

服务器IPhostname节点说明端口管控台地址账号密码192.168.0.115mq-01rabbitmq master5672http://192.168.0.115:15672guestguest192.168.0.117mq-02rabbitmq slave5672http://192.168.0.117:15672guestguest192.168.0.118mq-03rabbitmq slave5672http://192.168.0.118:15672gue…

AirPods Pro 到底「Pro」在哪里?

2019 嵌入式智能国际大会即将来袭&#xff01;购票官网&#xff1a;https://dwz.cn/z1jHouwEAirPods Pro一出&#xff0c;又被网友疯狂恶搞。有吐槽AirPods Pro&#xff0c;只是AirPods的复制版本的。有吐槽AirPods Pro&#xff0c;像个吹风机的。有吐槽像植物大战僵尸的。2018…

python pow和**_「Python学习笔记」Python函数高级应用

Python, 函数本身也是一个对象函数既可以赋值&#xff0c;也可以用作其他函数的参数&#xff0c;还可作为其他函数的返回值。使用函数变量Python的函数也是一种值&#xff1a;所有函数都是function对象&#xff0c;这意味着可以把函数本身赋值给变量&#xff0c;就像把整数、浮…

oracle中sga的合理设置,oracle学习:SGA_MAX_SIZE参数设置

oracle学习&#xff1a;SGA_MAX_SIZE参数设置时间&#xff1a;2017-07-11 来源&#xff1a;SGA_MAX_SIZE这个参数顾名思义&#xff0c;它用来控制SGA 使用虚拟内存的最大大小&#xff0c;这里的虚拟内存的含义可能会有所模糊&#xff0c;先可以这样理解&#xff0c;就是Oracle …

黑科技揭秘:如何通过阿里云超算,使得汽车仿真效率提升25%

在汽车行业&#xff0c;过去有一句俗话&#xff0c;一辆车从设计到下线&#xff0c;“至少要11辆真实碰撞试验”&#xff0c;今天&#xff0c;在现代化的汽车制造业&#xff0c;通过长期发展的设计和仿真软件&#xff0c;几乎所有的环节&#xff0c;都可以做到设计与仿真一体化…

更改应用程序图标_苹果更新 TestFlight 应用图标,增加更多拟真细节

TestFlight 是苹果公司针对开发者分发测试软件的应用&#xff0c;开发者可通过 TestFlight 向最多1万名测试者分发待测试应用。测试者在测试使用软件的同时&#xff0c;还可以向开发者提供使用意见或遇到的问题。最近&#xff0c;标志情报局发现&#xff0c;TestFlight 应用程序…

在一夜暴富之前,我先一夜秃了头

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 朱小五and王小九责编 | 阿秃与天斗&#xff0c;其乐无穷&#xff1b;与地斗&#xff0c;其乐无穷&#xff1b;与发际线斗&#xff0c;虽败犹荣……年轻人&#xff0c;今天的你比昨天更秃了吗&#xff1f;口说无凭&#xff…