likes
comments
collection
share

基于中台思想的物流系统设计(三):构建物流地址能力

作者站长头像
站长
· 阅读数 8

一、引言

在电商物流领域我们会涉及到地址,其中包括了基础的四级地址和用户填写的地址。四级地址在整个从下单到收货的业务流程中都会用到,因此设计的时候要考虑如何最大限度地提高QPS。用户地址在下单的时候让用户填写或者选择,然后存在交易订单和物流订单上,后续的流程一般不会变,如果用户需要修改地址,直接变更交易订单和物流订单的地址信息即可,因此设计的时候主要考虑满足各种用户地址场景。

二、物流地址数据模型设计

基于中台思想的物流系统设计(三):构建物流地址能力

1、dvc_division表

描述:四级地址表
表结构:
字段名称
字段类型
是否可以为空
描述
id
bigint
主键
name
varchar(128)
名称
code
varchar(32)
编码
level
int
层级:1、2、3、4
parent_code
varchar(32)
上级地址code
country
varchar(16)
默认CN
language
varchar(16)
默认ZH_CN
status
int
1正常,2废弃
post_code
varchar(16)
邮编
longititude
varchar(32)
经度
latitude
varchar(32)
维度
version
int
版本号
feature
varchar(1024)
扩展字段
gmt_created
datetime
创建时间
gmt_modified
datetime
修改时间
上面的表结构以一种一维的角度描述了四级地址,可能不太直观,下面用一个例子来说明四级地址是怎么组织的:

基于中台思想的物流系统设计(三):构建物流地址能力

四级地址第一级:
省份编码两位,从1开始到34代表34个省级行政区(23省+4直辖市+5自治区+2特别行政区),以浙江为例为8
四级地址第二级:
城市编码两位,从01开始递增,省份+城市构成二级地址,以杭州为例就是801
四级地址第三级:
区域编码两位,从01开始递增,省份+城市+区域构成三级地址,以杭州西湖区为例就是80103
四级地址第四级:
街道编码两位,从01开始递增,省份+城市+区域+街道构成四级地址,以浙江省杭州市西湖区古荡街道为例就是8010301
上面的每一级地址都通过parent_code关联上一级地址,因此只要知道任意一级地址,都可以把整个四级地址都查出来。

2、user_address表

描述:用户地址表
表结构
字段名称
字段类型
是否可以为空
描述
id
bigint
主键
user_id
bigint
用户ID
user_name
varcahr(64)
用户名称
shop_id
bigint
店铺ID
user_type
int
用户类型:1买家,2卖家
telephone
varchar(16)
手机号
phone
varchar(16)
座机号
country
varchar(16)
国家,默认CN
province
varchar(16)
省份名称
province_code
varchar(32)
省份code
city
varchar(32)
城市名称
city_code
varchar(32)
城市code
area
varchar(32)
地区名称
area_code
varchar(32)
地区code
street
varchar(32)
街道名称
street_code
varchar(32)
街道code
detail_address
varchar(1024)
详细地址
address_code
varchar(32)
最小code
is_default
int
是否默认,1默认,2非默认
language
varchar(16)
语言,默认ZH_CN
post_code
varchar(16)
邮编
version
int
版本号
feature
varchar(1024)
扩展字段
gmt_created
datetime
创建时间
gmt_modified
datetime
修改时间
索引:
user_id普通索引

三、四级地址库高并发设计

四级地址库的使用场景是查询非常多,修改和创建几乎没有,因此我们首先想到的是使用缓存。对于缓存,有基于redis的集中式缓存,也有基于JVM的本地缓存,考虑到四级地址库的使用场景,基于redis的集中式缓存在后期不一定能支撑巨大的查询量,因此我们从一开始就选择基于JVM的本地缓存,下面是对本地缓存的技术选型:

1、基于Guava Cache+定时任务

基于中台思想的物流系统设计(三):构建物流地址能力

该策略使用guava cache做本地缓存,由于guava cache本质上是KV数据,因此针对不同的查询场景,需要构建不同的缓存,然后通过elastic-job定时(比如每天凌晨)将数据库数据刷新到缓存。这种策略编码实现比较简单,但是无法适应复杂场景的查询,而且随着查询场景的增多,内存数据会越来越大。

2、基于H2内存数据库

基于中台思想的物流系统设计(三):构建物流地址能力

该策略基于H2内存数据库+Mybatis实现了复杂查询场景,同时又保证了性能。但是,由于H2数据库的数据是在启动的时候从文件加载的,运行期无法变更,因此,每次地址库更新,都需要更新启动时的数据文件。

3、最终决策

考虑到四级地址数据一年也更新不了几次,我们最终选择了方案二:基于H2内存数据库的缓存。我们把H2数据文件、H2数据库SQL、Mybatis接口都封装在了一个client中打包出去,外部系统直接依赖这个client包就可以获得四级地址库的能力。当地址库数据更新,我们会重新打包client,其他应用只要升级这个client包就可以获得最新的四级地址数据。

四、基于经纬度的用户地址查询

用户在下单的时候,需要填写收货地址,普通的填写方式通过四级地址一级一级往下填,用户体验比较繁琐。为了提升用户体验,我们可以根据用户的经纬度直接匹配出四级地址。
我们使用Redis GEO API进行地址与经纬度的映射,整体架构如下:

基于中台思想的物流系统设计(三):构建物流地址能力

我们首先将用户地址经纬度映射到四级地址code中,存入redis,前端应用根经纬度从redis获取四级地址code,然后根据code查询四级地址详细信息。由于数据存储在redis,为了防止缓存被逐出或者redis被重启,每天凌晨使用定时任务刷新redis的数据。
我们使用redis geo API主要使用两个命令:
1.GEOADD
命令:GEOADD key longitude latitude member [longitude latitude member ...]
命令描述:将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。
返回值:添加到sorted set元素的数目,但不包括已更新score的元素。
2.GEORADIUS
命令:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
命令描述:
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
范围可以使用以下其中一个单位:
  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

五、总结

通过上面的介绍,我们基本介绍完了一个物流地址系统的关键技术要点,接下来的一篇文章会把物流详情和物流服务的能力一并介绍,然后进入我们的重头戏:如何构建一个具有良好扩展性的物流产品服务层。

更多文章欢迎访问 http://www.apexyun.com/

联系邮箱:public@space-explore.com

(未经同意,请勿转载)