当前位置: 首页 > 科技观察

一旦参透这九个电商系统架构,全能型架构师无疑了

时间:2023-03-13 20:33:56 科技观察

了解了九大电子商务系统架构,你无疑就是一个全能的架构师。.以业务架构为核心关键词,主要针对不同的业务场景和业务规则,完成业务系统的搭建,为用户提供在线信息服务。现在说业务,有很多方向,比如:旅游、外卖、充电宝、O2O、内容、社交、生鲜、电商,不同的业务有不同的特点。面对这么多的业务领域,有没有什么通用的技术经验可以提炼出来,让我们应对成百上千。在这里,电商业务首当其冲。电子商务系统的复杂度非常高,对高并发、高性能、高可用性、高扩展性的要求很高。你在其他业务中可能遇到的问题,在电商系统中基本上都会遇到。作为一名开发人员,我希望成为几个业务领域的技术专家。最好先精通电商领域,有很强的参考价值。对你在其他业务领域拓展和熟悉个性化玩法会有很大帮助。那么,电子商务领域的技术架构普遍存在哪些问题呢?1.避免重复下单。如果用户快速点击两次“提交订单”按钮,浏览器会向后台发送两次创建订单的请求,最终会创建两个相同的订单。1.解决方案解决方案是采用幂等机制,多次请求和一次请求效果相同。1)选项1利用了数据库本身的“唯一主键约束”。插入订单记录时,带上主键值。如果顺序重复,记录插入将失败。操作流程如下:引入服务生成“全球唯一订单号”;进入订单创建页面时,前端请求服务并预生成订单ID;提交订单时,请求参数必须包含业务参数和关于此预先生成的订单ID。2)方案二前端由js脚本控制,无法解决用户请求刷新提交。也没有办法解决恶意提交。不推荐使用该程序,如果要使用,仅作为辅助程序。3)方案三前后约定附加参数校验。当用户点击购买按钮时,会渲染订单页面,显示商品、送货地址、运费、价格等信息,同时页面会埋入Token信息。当用户提交订单时,后台业务逻辑会验证token。被认为是合理的要求。同一个Token只能使用一次,使用后立即失效。{%csrf_token%}2.订单快照,降低存储成本商品信息可修改。用户下单后,为了更好的解决日后可能出现的销售纠纷,在创建订单时会同步保存一份商品详情信息,称为订单快照。许多用户会购买相同的产品。如果产品受欢迎,短时间内会有数万的订单。如果为每个订单创建一个快照,存储成本太高。另外,商品信息虽然支持修改,但毕竟是低频动作。我们可以理解为大部分订单的商品快照信息是相同的,除非用户在下单时修改过。如何实时识别修改动作是解决快照成本的关键。我们采用抽象比较的方法。创建订单时,首先检查产品信息摘要是否已经存在,如果不存在,将创建快照记录。订单详情将与产品的快照主键相关联。公共类DigestTest{publicstaticvoidencodeStr(Stringdata){StringencodeS=DigestUtils.md5Hex(data);System.out.println(encodeS);}publicstaticvoidmain(String[]args){Stringdata="netpin投连险是保险公司的保险产品,在互联网金融中还是很常见的。"+"比如京东天天赢、网易优钱钱++。这些保险弱化了保险的保障功能,降低了成本,从而提高了保险的理财功能,提高了理财收益。"+》投连险与银行结构性理财产品基本相同,信息披露程度不高。想象一下,投资起点低的银行理财产品。网上销售和投连险普遍受益4-6%,保本无保障。”+“经常有报道说保险公司的保障型长期投连险有投资损失,但是网购的短期投连险投资型投连险目前没有损失,而且基本上可以按照预期收入支付。”+“网销投连险的安全性和盈利性都比较中等,短期产品的风险系数不高。但在债券违约的大环境下,长期产品的安全性并没有多少保障。”+“好在保险公司没有跑路的风险,至少不会赔光本金。";encodeStr(data);}}因为订单快照是非核心操作,即使失败也不应该影响用户正常的购买流程,所以通常在异步流程中执行。匹配函数暂存用户想要购买的商品,分为三个动作:添加商品,查看列表,结算下单,技术设计不是特别复杂,存储的信息比较有限(用户id,商品id,sku_id,quantity,Addingtime)。这里主要关注用户体验层面的几个问题:添加购物车时,后台会检查用户是否未登录。大意是引导用户去跳转到登录页面,登录成功后,然后添加购物车,多一步操作会给用户一种强制感,体验会比较差,有没有更好的方法?如果你体验过京东,淘宝等大平台仔细orms,你会发现即使没有登录也可以添加购物。切,这是怎么实现的?仔细想想,原理并不复杂。服务器端在用户登录时进行分支路由,当用户未登录时,会创建一个临时的token作为用户的唯一标识,购物车数据挂载在Token下,避免相互影响考虑到购物车数据和设计的复杂性,会有一个临时的购物车表。当然,临时购物车表的数据量也不会太大,为什么呢?用户不用一直加购物车玩。当用户登录查看自己的购物车时,服务器会从请求的cookie中查找购物车Token标识符,并检查临时购物车表中是否有数据,然后合并。到官方购物车表。临时购物车是否必须存储在服务器上?不必要。一些架构师倾向于将数据存储在浏览器或APPLocalStorage中。毕竟这部分数据是不共享的,但是也不好增加设计的复杂度。客户端需要使用本地数据索引远程请求查看完整信息;如果是登录状态,需要添加数据合并逻辑;考虑到这两部分数据只是用户标识的区别,笔者还是建议以后统一存储在服务器端,即使业务逻辑发生变化,也只需要改动一个地方即可。毕竟自操作系统需要我们非常注重良好的可维护性。4、库存超卖常见的库存扣减方式有:订单减法:即买家下单时,从商品总库存中减去买家的采购数量。下单减库存是最简单的减库存方式,也是最精准的控制方式。下单时,通过数据库的交易机制直接控制商品的库存,不会出现超卖的情况。但是你要知道,有的人可能下单后不付款。付款减少库存:买家下单后,库存不会立即减少,而是在用户付款后实际减少库存,否则库存会一直留给其他买家。但是因为只是在付款的时候减少了库存,如果并发量比较高,可能会出现买家下单后无法付款的情况,因为商品可能已经被别人买了。预扣库存:这种方法比较复杂。买家下单后,库存会保留一定时间(如30分钟)。过了这个时间,库存会自动释放。发布后,其他买家可以继续购买。买家付款前,系统会检查订单库存是否有预留:如果没有预留,则再次尝试预留;存货不足(即扣缴不合格)的,不得补缴;如果预扣成功,则进行付款并实际减去库存。至于采用哪种减库存方式更多是业务层面的考虑,减库存的核心是在大并发请求时保证数据库中inventory字段的值不能为负。方案一通常在扣库存的场景下使用行级锁,通过数据库引擎自身控制记录的加锁,保证数据库更新的安全性,通过where语句的条件,保证库存不会降低到0以下,即超卖场景可以得到有效控制。update...setamount=amount-1whereid=$idandamount-1>=0方案二将数据库的字段数据设置为无符号整数,这样SQL语句就会报错减去后的库存字段小于零。5.商户发货,物流单更新ABA问题举个例子:商户发货,填写运单号,一开始填写123,后来发现不对,后来修改为456。至此,如果你是在为某一个特殊场景做铺垫,我们来看一下细节:流程如下:启动“requestA”进行发货,调整订单服务接口,更新运单号123;但响应有点慢,超时;这时,商家发现运单号填写错误,发起“请求B”,更新运单号为456,订单服务也响应成功;此时“请求A”触发重试,再次调用订单服务,更新运单号123,订单服务也响应成功;订单服务最后保存的运单号是123,我是不是记错了!!!!那么有什么好的解决办法吗?很多人可能会说,不重试也行。要知道重试机制是服务高可用的重要保障。许多重试是由框架自动发起的。理想方案:在数据库表中引入一个额外的字段version,每次更新时判断表中的版本号与请求参数携带的版本号是否一致。updateordersetlogistics_num=#{logistics_num},version=#{version}+1whereorder_id=1111andversion=#{version}一致:触发更新;不一致:表示在此期间已经进行了数据更新,可能会出错,拒绝执行。6.为了更新账户余额,保证交易用户付款,我们需要从买家账户中扣除一定金额,再向卖家账户中添加一定金额。为了保证数据的完整性和可追溯性,在更换天平时,我们通常会插入一条流水记录。账户交易核心字段:交易ID、金额、交易双方账户、交易时间戳、订单号。账户历史只能添加,不能修改或删除。序列号必须自动递增。以后系统对账的时候,我们只需要积累交易流水的明细数据即可。如果与余额不一致,一般会根据交易流程修复余额数据。虽然更新余额和记录流量属于两个操作,但必须保证要么都成功,要么都失败。做生意。数据库的事务隔离级别有:ReadUncommitted(RU)、ReadCommitted(RC)、RepeatableRead(RR)和Serializable。常用的隔离级别是RC和RR,因为这两种隔离级别都可以防止脏读。当然,如果涉及多个微服务调用,就要用到分布式事务。分布式事务想想就很容易理解。就是将一个大的事务拆分成多个本地事务。本地事务还是由数据库自己的事务来解决。难点在于解决这个分布式一致性问题。借助重试机制,保证最终一致性是我们常用的解决方案。7、MySQL读写分离导致的数据不一致。大部分互联网业务都是读多写少。为了提高数据库集群的吞吐性能,我们通常采用主从架构和读写分离。部署一个主库实例,客户端请求所有的写操作都写入到主库中,然后利用MySQL内置的主从同步功能做一些简单的配置,就可以将主库的数据同步到多个从库实例近乎实时,主从延迟很小,一般不超过1毫秒。客户端请求的读操作全部发送到从库,借助多实例集群提高读请求的整体处理能力。这个解决方案看似天衣无缝,实际上却有副作用。虽然主从同步几乎是实时的,但是还是有时间差的。主库中的数据刚刚被更新,但是数据还没有同步到从库中。后续读请求直接访问从库,看到的还是旧数据,影响用户体验。没有什么是完美的,从主同步也是如此。没有完美的解决方案,我们必须找到一个平衡点。下面以电商为例,看看如何从产品层面解决这个问题。为了实验的真实性,Tom哥特地在淘宝下了购物单。在订单确认页面,点击购买按钮,进入支付页面。输入支付宝支付密码进入支付成功页面,这里有一个可以查看订单详情的入口。点击查看交易详情,跳转至真实订单详情页面,可查看订单支付状态(订单数据取自图书馆)。你明白吗?支付成功后,我们并没有立即跳转到订单详情页面,而是添加了一个无关紧要的中间页面(支付成功页面),一个是告诉你支付成功的结果,钱没有丢,别'担心;也可以添加一些推荐商品来吸引流量,提高网站的GMV。最重要的是,增加了一个缓冲期,为订单的主从库数据同步争取更多的时间。可以说是一举多得,其他互联网业务也是类似的。你学会了另一个技巧吗?8、历史订单、归档根据第28条规则,系统的大部分性能开销花在了20%的业务上。数据也不例外。从数据使用频率来看,业务经常访问的数据称为热点数据;否则,它被称为冷数据。了解了数据的冷热特性之后,就可以指导我们做一些有针对性的性能优化。有业务层面的优化,也有技术层面的优化。例如:电商网站一般只能查询3个月以内的订单。如果您想查看3个月前的订单,您需要访问历史订单页面。实现思路如下:1.区分冷热数据的标准是什么?结合商业思维,做决定前可能要和产品同学商量一下,切记不要敲脑袋。以电商订单为例:1)方案一以“下单时间”为标准,将3个月前的订单数据作为冷数据,3个月内的订单作为热数据。2)方案2根据“订单状态”字段区分,已完成的订单为冷数据,未完成的订单为热数据。3)方案三的组合方式,订单时间>3个月且状态为“已完成”标记为冷数据,其他视为热数据。2、如何触发冷热数据分离?1)方案一直接修改业务代码,每次业务请求触发冷热数据判断,根据结果路由到对应的冷数据表或热数据表。缺点:如果判断标准是时间维度,则无法主动感知过期数据。2)方案2如果觉得修改业务代码耦合度高,后期不易维护。可以通过监听数据库变更日志binlog来触发。3)方案3的常用方法是运行定时任务。一般在凌晨系统压力较低时,会通过运行批任务的方式,将满足条件的冷数据迁移到其他存储介质上。在途业务表中只留下少量热点数据。3、冷热数据如何分离?流程大致分为三步:判断数据是冷还是热;将冷数据插入冷数据表;然后将迁移后的数据从原来的热库中删除。4、冷热数据如何使用?1)选项1界面设计会有选项区分,比如上面的电商订单例子。2)方案2在业务代码中直接区分。9、订单分库分表,多维查询如果电商网站订单过多,我们一般会想到分库分表的解决策略。没问题,这个方向是对的。但是查询维度比较多:买家,查询我的订单列表,需要通过buyer_id查询;查看订单详情,需要通过order_id查询;卖家,查询我的销售列表,需要通过seller_id查询。订单分表只有一个分表键,如何满足多维SQL操作?我们一般是根据买家维度来设计的。下图是淘宝的订单列表:一个订单号有19位,我们会发现同一个用户不同订单的后6位是相同的。是的,就是用户id的末尾6位。这样,上面场景一和场景二的查询就可以通过共性抽取,以buyer_id或order_id的后六位为分表key,取模1000000得到订单分表的编号买方维度。对于场景三seller维度的订单查询,我们可以采用数据异构的方式,根据seller_id维度存储另外一条数据,专门供卖家使用。