前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >小商店从0到1的系统能力构建之路

小商店从0到1的系统能力构建之路

作者头像
腾讯大讲堂
发布于 2021-02-26 10:10:11
发布于 2021-02-26 10:10:11
1.4K0
举报

作者:endyxu  腾讯WXG后台开发工程师

|导语 小商店系统设计深度解析,本文将围绕快速上线、系统优化、商家入驻、商家成交四个维度的技术思考进行讲解

小程序交易开放方案包含标准低门槛版方案(小商店)以及自定义开发版方案(交易组件)。小商店是用于帮助商家免开发、0成本快速生成卖货小程序的一款低门槛的开店工具。小商店现提供商品信息发布、商品交易、直播等功能。有兴趣的朋友可以通过小程序搜索“小商店助手” 或者 https://shop.weixin.qq.com/了解更多。

小商店自立项以来,除了产品形态上在不断迭代,开发上的核心目标也在跟随着产品战略而有不同的侧重,本文主要围绕以下几个产品战略阶段展开讨论。

01

快速上线

这个时间点的开发上的核心挑战是2个月时间搭建一套电商系统雏形。

这是一个比较考验开发个人积累的阶段,因为除了“怎么快怎么来”以外,选型的时候更需要考虑以下两点:“业界怎么做”,“后面怎么升级”。

这里我们以商品及订单系统为例:

商品体系

商品体系核心主要是解决各类关系型数据,这里数据结构设计各友商们也基本大同小异。1:N的关系型,基本就是SQL表每一行存储关系树上的一条边(id, father_id),有兴趣的同学可以翻看下面文章。

参考:电商项目数据库设计 | 第二篇:商品相关表结构

订单体系

订单的设计,我们可以围绕以下的几个点来展开讨论:

1. 多维度查询问题

(1)用户维度查询

这是我们设计上的第一考虑要点,C端用户体验,这里TDSQL自带了水平分片的能力,分片路由上我们选用了useruin,一是保证C端用户查询快,二是确保数据存储均衡。

(2)订单号维度查询

由于我们选择了useruin作为shardkey,在以订单号查询时,有两个方法来避免扫所有分片,一个是在与前端的协议上,要求所有请求都带上useruin(因为一个订单号只对应一个订单),另一个方法,也是业界常用的“基因法”,既是将useruin的部分路由信息,融合到订单号的几个固定位置上。

(3)商户维度查询

在订单量过亿(目前在百万级)之前,扫描所有分片所用耗时均在50ms以内,属于可接受级别,在当前阶段没必要过度设计。后续数据量暴涨时,我们可以使用双集群相互复制模式,既多一套以商户维度分片的存储集群,将所有商户维度的请求都走该集群,不过该方案会带来两个衍生问题:1,单表数据过大问题,需要做水平切分,后面会讲;2,集群同步延迟带来的不一致问题,我们从业务角度上考虑,是属于可接受级别的,比如商家端发货,商家端可以马上看到订单状态更新,而用户端看到需要少许延时。

(4)搜索、筛选、排序

这类请求的特点是,性能消耗大,因此,业界通用做法是使用ES作为附加索引在解决。

2. 数据切分问题

(1)垂直切分

当存储空间不足时,就需要对存储做垂直切分了,这里可以订单表与ES只保留最核心的索引数据,冗余部分最常用的数据(金额)。

(2)水平切分

目前的设计主要是使用TDSQL自带的水平分片能力,以useruin作shardkey避免单表过大问题。假如后面需要优化商户维度的查询性能,则需对商户维度的存储做定制水平切分处理。

(3)数据冷热

订单的特点是时间越久被访问的频次越低,可以按照时间维度划分冷人数据,冷数据归档到便宜的存储上,既可以让数据库瘦身保持性能,同时也能降低存储成本。

参考:

大规模订单系统解读-架构篇

微信支付核心订单系统的架构是怎样实现的?

02

系统优化

由于接入场景变多,部分场景存在抢购行为(如视频号直播)或者海量导入行为(交易组件),这些场景会给系统带来一定的冲击,因此这个阶段我们核心目标就是系统稳定性建设。

系统解耦

这是常用的降低故障影响面的套路,主要分为以下几点:

(1)主次分离:将核心业务重点隔离出来,将影响到交易,且不可降级的业务为核心业务,DB层/逻辑层均独立部署

(2)读写分离:C端读 B端写的数据可做读写分离(如商品,优惠券),降低写操作对高频读的影响,并可讲读数据简化提升读性能

(3)前后端分离:前端(展示+效果)与后端(数据+业务逻辑)解耦,方便紧急情况下后端做一些降级策略

读优化

之前由于需要快速上线,整个系统基本处于裸奔无任何cache状态,这一阶段我们对整个系统的所有读调用做了梳理,说到加缓存,其实就是以下这几个套路:

电商场景下,我们可以总结出以下几类数据:

(1)商家/商品相关:核心考虑点是保护商家上架时,用户突然间一波峰的抢购冲击,这里需要在上货时主动set cache。

(2)类目/品牌相关:属于少量数据,且变动不频繁,可做全量cache

(3)用户相关:这类数据,需要考虑点是如何做到访问DB的时机与交易时机错开,这个需要因地制宜,例如直播场景下,我们需要在用户进入直播间时预热这些数据

(4)库存相关:在售时属于动态数据,无需cache,但售罄时,需要第一时间cache住售罄状态,并将多余的流量做剪枝。

(5)商品降级数据:触发降级条件时生效,数据是正常请求时随机1%的结果去set cache,后面会详细讲。

关于如何防止缓存同时大量穿透的问题,其实有很多,如做本地+分布式的两级cache,或者stable cache等。

这一块重点想讨论的是,“如何排查出系统里面有哪些地方没加缓存”,发现未知的隐患,不然某一天一波突发流量进来了,他就是一颗定时炸弹。有人说压测/测试可以吗?可以,但前提是测试case要覆盖全逻辑链路,在一套庞大的系统面前几乎不可能。

这里我们提出了一个的监控方法:

怎么理解呢?我们假设理想情况下,DB里面所有数据有加了完美缓存,那么理论上DB的GET次数为GETuv(多少个key就访问多少次,同一个key第二次访问会被cache挡住)+SETpv(数据更新时,我们会删除缓存,所以需要额外访问一次DB),而实际的DB GET次数为GETpv次。因此我们可以根据该公式,对每一个DB的表做监控,如:

当曲线值远大于1(1为理想情况),且抖动明显时,可通过告警发现系统处理无缓存无优化状态。

写优化

说到写,在整个电商场景里面,最难的应该就是秒杀了,这也是经典的面试题。

秒杀场景的瓶颈在于热点库存的吞吐量,这里由于我们之前使用的是TDSQL的事务来做,基本上限是在100QPS左右。这道面试题标准的解法就是使用Redis集群代理模式来解决,这里我们主要围绕CAP理论展开讨论下实际使用中的一些注意点(面试干货)。

(1)租借式:同一条库存在某个时间节点只有一个DB可用(避免信息同步不及时带来的一致性问题),以DB为准,缓存只做代理租借,借出与归还都要写凭证,成交流水凭证也要同步给DB。

(2)平滑租借:从tdsql借出,到入账redis的过程,如何规避这个短暂不可交易状态?可以从业务上来考虑,在上架前完成租借,或者使用类似两阶段提交的模式,一次只租借部分的库存,保证任何时刻至少有一方有库存。

(3)redis可靠性:lua脚本保证操作的原子性;部署方案使用集群,双机热备,保证5个9的可用性;未知极端选择少卖,这里有一个技巧,所有扣库存的操作,都是先扣库存再写凭证,所有还库存的操作,都是先写凭证再还库存,确保库存当前值<=库存实际值。

(4)最终一致性:以写凭证成功为准,并在系统低峰基于凭证修复库存数。

(5)扩展性:动态扩容分桶模式,一个桶代理不够了,可以使用两个桶代理,这里我们可以对每个库存桶做一个令牌桶告警,当令牌桶水位超过50%时,自动将该库存桶的库存一分为二。由于库存分散在多个桶,可能会有碎片问题,但结合业务实际思考,能触发到扩容(至少2w/s以上的单key并发请求),其实货物马上就售罄了,所以碎片问题不会暴露。

柔性可用

如何做用户无感知的降级?在电商场景下,有一个核心思想很重要:调用峰值=min(库存数,同时购买人数),因为在商品售罄后,我们可以直接从一个复杂case变成简单case。

在突发大流量进来时,我们没必要去硬抗,确保有少部分用户(超过库存数)能正常交易就行,其他用户看到的其实都是静态页面。

这里我们在商详页的读请求做了简单的频率限制,超过3w/s(普遍抢购场景库存数都不超过1k)的请求自动加载降级页面。由于降级页面属于静态页面,因此我们可以直接做在CGI层的local cache,通过正常请求随机1%的流量去更新cache,这样即使有百万级以上的抢购流量进来时,我们只需简单扩容CGI层的机器即可,轻量的水平拓展。

03

商家入驻

当系统趋于稳定后,我们的部分精力会去思考如何辅助产品做好这个项目。

电商场景下,商家入驻影响因素:拉力与门槛。而我们是否可以从技术的角度去辅助产品降低门槛呢?

(1)OCR自动填表:优化用户多余的无必要操作

(2)自动识物:拍照即可为商家自动填充所需商品信息,“自动识图”的底层是“扫一扫—识物”的基础能力。基于识物的召回结果,首先对多个相关商品标题进行信息整合,通过Pointer-Generator-Network得到包含品牌、商品名和基础属性的精简标题;其次,采用基于AL-BERT的多标题的文本分类,提取精确的商品三级类目;此外,对召回的商品图集,通过DBSCAN的同款聚类、PHash的相似图去重、CNN的图像质量分排序,为商家挑选高质精美的商品主图。

(3)爬虫代替API模式:电商场景中,部分商家是基于API导入商品的,这个过程本质就是字段的copy。而其实该商品有对应的小程序页面,我们正在规划一种接入模式,商家只需告诉我们path,我们可以自己爬取。

04

商家成交

继上面的命题,商家入驻了之后,我们如何用技术的手段辅助商家促成交易呢?

(1)智能海报:转化率最高的传播方式,这里我们正在做一套类似阿里“鹿班”的系统,结合设计的模版与u2net像素级抠图算法智能生成花样百出的海报

(2)智能文案:多用于微商场景群聊传播,转化率高,但创作门槛也高,这里我们通过对抗网络生成离线生成营销文案库,在线场景通过倒排检索相关营销文案,辅助商家0成本创作

(3)智能视频:用户短视频消费时间越来越长,微商已经把广告投放到短视频生态中,这里我们结合设计的模板(PAG)与智能选图,智能魔性配乐,降低商品视频的制作成本,辅助促成交易

05

小结

本文主要围绕工程算法在不同的时期,不同产品方向目标下,所面临的问题,以及如何做好对应的技术决策的思考小结,很多细节本文没有展开。项目能够在短时间能上线,并取得不错的业务结果,背后有很多人的付出,感谢整个项目团队的共同努力。

近期热文

基于云原生基础设施的后台架构设计思考

云时代,我们需要怎样的数据库?

大数据AI时代的产品修炼之路:A/B测试

让我知道你在看

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯大讲堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
React项目实战(React后台管理系统、TypeScript+React18)
官网:https://ant.design/docs/react/introduce-cn
王小婷
2023/11/21
1.2K0
React项目实战(React后台管理系统、TypeScript+React18)
初探 MicroApp,一个极致简洁的微前端框架
在微前端的领域里,相信大家都听说过阿里的 qiankun。我自己在上几个月也一直用它来做一些实践:
写代码的海怪
2022/03/30
1.7K0
初探 MicroApp,一个极致简洁的微前端框架
大爱并发模式!React Router 路由跳转最佳实践的秘密
在 Next.js 大热之前,React Router 是 React 生态中,最流行的路由库。也是我最喜爱的路由库。不过随着版本的迭代,React Router 变得越来越庞大了。他的复杂度已经快要比得上一个框架了。
用户6901603
2024/06/07
5870
大爱并发模式!React Router 路由跳转最佳实践的秘密
react-RouterV6
替代routes组件,以 JavaScript 对象的结构生成routes路由模版,省去了嵌套循环。
程序员王天
2023/10/18
3080
基于webpack4+react 的js懒加载
此处主要介绍使用动态导入(通过模块中的内联函数调用来分离代码)的懒加载。这种动态代码拆分的方式是webpack提供并推荐选择的方式。其原理是使用符合 ECMAScript 提案 的 import() 语法 来实现动态导入。
shirley
2019/03/24
4.4K0
React 路由守卫 Guarded Routes
在现代 Web 应用中,路由守卫(Guarded Routes)是一种常见的模式,用于在用户访问特定路由之前进行权限检查或其他逻辑验证。React 生态系统中,最常用的路由库是 react-router-dom,它提供了丰富的 API 来实现路由守卫。本文将从浅到深地介绍 React 路由守卫的基本概念、常见问题、易错点及如何避免这些问题,并通过具体的代码案例进行解释。
Jimaks
2024/11/10
4340
React 路由守卫 Guarded Routes
React技巧之鼠标悬浮添加行内样式
原文链接:https://bobbyhadz.com/blog/react-inline-style-hover[1]
chuckQu
2022/08/19
2.1K0
React技巧之鼠标悬浮添加行内样式
React Router V6详解
SAP全称是【single-page application】,中文译为单页面应用。它是网站应用的一种模型,可以动态重写当前的页面来与用户交互,而不需要重新加载整个页面。相对于传统的 Web 应用程序,单页应用做到了前后端分离,即后端只负责处理数据提供接口,而页面逻辑和页面渲染都交由前端处理。前端发展到现在,单页应用的使用已经很广泛,目前时兴的 React、Vue、Angular 等前端框架均采用了 SPA 原则。
xiangzhihong
2023/01/06
8.2K0
细说React组件性能优化
React 组件性能优化的核心是减少渲染真实 DOM 节点的频率,减少 Virtual DOM 比对的频率。如果子组件未发生数据改变不渲染子组件。
xiaofeng123aa
2022/10/18
1.5K0
angular2路由预加载
1. 实现PreloadingStrategy 类 import { PreloadingStrategy, Route } from "@angular/router"; import { Observable } from "rxjs"; /** * 预加载策略 */ export class SelectivePreloadingStrategy implements PreloadingStrategy { preload(route: Route, load: Function): O
用户1437675
2018/08/20
1.1K0
react-router 的使用与优化
react-router 可以创建单页应用。可以将组件映射到路由上,将对应的组件渲染到想要渲染的位置(根据路径的变化渲染出组件)。
多云转晴
2020/03/11
3.5K0
react 同构初步(3)
后端ssr只是渲染了网页模板(ul),列表(li)的html都是异步请求加载出来的。再回看首页列表的代码:
一粒小麦
2019/12/19
1.6K0
react-react-dom v6 知识整合
1. BrowserRouter / HashRouter 相当于容器(类似router-view),用于指定路由的模式
用户9914333
2022/12/14
6.6K0
react-react-dom v6 知识整合
【路由】:路由那些事——上
前端三杰 Angular、React、Vue 都推荐单页面应用 SPA 开发模式,它们都有自己的前端路由解决方案:
WEBJ2EE
2021/04/07
1.9K0
React Router 6 (React路由) 最详细教程
React Router 经历多个版本的发展,现在已经到了 React Router 6。虽然网络上写 React-Router 路由本身的教程很多,但真正讲到 React-Router 6 的并不多。同时因为第 6 版引入了很多新的概念,以及大量使用 Hook,因此网上的很多旧教程已经不实用了。这篇文章里我们总结 React Router 6 路由器的用法,用例子说明如何实现各种场景和需求,比如程序化跳转等等。
蒋川
2022/03/29
24.9K3
React Router 6 (React路由) 最详细教程
解读 React Router v7:新功能与性能优化详解
今日推荐 《掌控软件管理:详解 APT、YUM 和 DNF 的使用方法》这篇文章介绍了软件包管理器帮助我们轻松地安装、更新、卸载和管理系统中的软件包。APT、YUM 和 DNF 是当前最流行的包管理器,分别用于不同的 Linux 发行版。本文将深入解析它们的使用方法,并通过代码示例展示如何高效地管理软件包。
Front_Yue
2024/11/24
4.3K0
解读 React Router v7:新功能与性能优化详解
React进阶篇(九)React Router
单页面应用(SPA)可以让Web应用看起来像多页面应用,URL变化时,不会向服务端发起请求,而是利用自身监听路由变化而更新UI。 通过使用React Router可以让Web应用根据不同URL渲染不同组件。
娜姐
2020/12/11
3.1K0
滴滴前端二面常考react面试题(持续更新中)_2023-03-01
refs允许你直接访问DOM元素或组件实例。为了使用它们,可以向组件添加个ref属性。
用户10376779
2023/03/01
4.7K0
React-router配置路由模块化及嵌套路由
在src目录下新建router文件夹创建index.js文件 注意导入路径,@为我项目配置路径,默认指向src目录 import { lazy } from 'react'; // 基于路由进行代码分割 // 经测试可进行路由懒加载 // https://zh-hans.reactjs.org/docs/code-splitting.html#route-based-code-splitting const router = [ { path: "/", c
明知山
2021/06/21
2.6K0
React 实战
只需要保证,在同一个数组中的兄弟元素之间的 key 是唯一的。而不需要在整个应用程序甚至单个组件中保持唯一。
王秀龙
2021/08/26
1.3K0
React 实战
相关推荐
React项目实战(React后台管理系统、TypeScript+React18)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档