一个典型的后台软件系统的设计复盘——(一)如何id一个事物

  这个话题,可以从类与对象说起。

Dog dog1 = new Dog();

哪个是类,哪个是对象?这个问题搞不清楚,后面就无从说起了。然后两个程序员之间沟通说,那个狗有问题。除非两人很默契,不然另一人肯定要懵圈,是狗这个类有问题,还是狗的实例对象的属性有问题。由此引出了今天的话题:如何id一个事物。首先,这个事物是一个类型,还是这个类型中具体的一个对象。

再者,同一个东西,有不同的阶段不同的形态。

  比如,扎啤。从仓库里搬出来的时候是整桶的,销售的时候却是一扎一扎的。计算一扎的利润时,就要进行转换换算了。所以通常设计的时候会有:采购环节的形态,称之为产品;销售环节的形态,称之为商品;两者之间有一定的换算方式,用于转换数量,也用于计算价格(成本)。
  既然举例是食品类的,那么必定有另一个问题。某天发现卖出的一件商品过期了,如何追踪到这件商品是什么时候谁采购的、同批的还有哪些?这便是另一个维度的信息,批次与库存:店内这款商品共N+M个,其中过期这批的有N个,过期这批来自什么时候谁的采购,总共数量有N+M+L个,另外有L个散落在哪些店。当然了,这是个强调保质期强调批次的例子,有些品类的库存只需要记个数量,比如服装类,这一层也可以做薄,要看具体场景。
  有那么一个极端场景,下架商品(改状态)失败,事务等待超时。原因竟然是,订单请求太多太久,修改库存时锁定了商品记录,所以状态也改不了。那么问题来了,库存信息和状态信息是同个数据库表里同个记录?偷懒这么做,请求量一上来就扛不住了。为什么?因为动静要分离啊,高并发的信息和长事务的信息要尽量拆分开。商品名、商品分类这些描述信息,偶尔才改动一次,可以认为是静态的;状态(是否可见、是否可售)、价格这些信息,改动也不频繁,也可以认为是静态的;而库存信息是变动非常频繁的,是动态的。需要动静分离的场景,更典型的例子就是,库存和基础信息放同个表,批量导入改商品标语的一个长事务锁住了表,结果订单扣库存锁等待,严重影响了成交。所以,动静要分离。
  在库存和基础信息这个场景里,指的基本上是同个东西,就是这种商品(所以才可能放在同个数据库表)。另一种场景的动静分离,则并不是同个东西。比如顾客3天前买了A商品2件,当时单价1元,今天顾客要退货退款,但A商品单价涨到2元了,是否去取A商品当前价格去计算退款多少钱?显然不是。所以在这种场景下,商品的当前价格是动态的,时不时会浮动的;而某一刻交易的价格是静态的,是既成事实的。所以订单信息中还应记录着每个商品成交那一刻的价格,我们经常称之为商品快照。当然,这只是设计的基本思想,具体实现可以冗余在订单里,也可以是专门的商品快照表记录而订单直接引用商品快照。
  所以,即便是同一个东西,比如商品,也会有采购、销售等不同阶段,也会有基础信息、批次库存、历史快照等不同形态。它们已经是不同的实体,需要用不同的记录去id去唯一标识。

再者,同一个过程,有不同的颗粒度。

  完成一个订单,可能需要捡货-打包-送出等,且称之为step1-step2-step3,那么也需要一个颗粒度把“执行这次订单”这个事件给组织起来,且称之为message,异步消息投递嘛。单次投送的时刻和状态当然要有。问题是倘若一个订单拆分成若干次执行,如何表达这次订单执行完成了没?可以考虑多一层job的概念,即job与订单对应,job里的多个message表示多个拆分,message里的step表示具体执行的步骤。所以,如果要追踪订单现在执行到哪一步,可以通过job-message-step找到;反过来要追踪某一步失败影响到哪张单也不难。
  正常情况下,job这一层也不是必须的,状态和时间直接挂在订单上也不是不可以。但如果特殊场景呢?比如不只是订单,促销也会发起若干个消息投递。再比如,订单被取消了,要插队追加个撤销,有个job层级做标记,就好操作很多了。
回过头来看颗粒度。如何唯一识别一个订单任务?整个单的是job,这个单的拆分则是message,具体每个步骤则是step。那么,订单中某个项呢?必定是包含在某次拆分中。所以,message应该引用着一个订单拆分项,而step则应引用着若干个订单拆分项明细。如此,便可以追踪到订单中某个商品的进度了。(当然,还是要看具体场景,是不是需要拆分订单,是不是需要追踪某个商品的进度。)

最后,即便同一个对象,也不一定是同个事物

  比如,java JPA中的entity bean。一个新建的实体对象,save前后是完全不一样的。保存前只是个纯粹的java对象,而保存后则是跟数据库记录有绑定的实体对象。所以,把未保存的对象存到引用的字段里是会运行时报错的,比如product实体引用了品牌brand,品牌对象未保存(未绑定到数据库记录)时就存入product.setBrand(brand),运行时就会报错。
  而实体类和承载web接口返回的类,其实也不该是同一个。新手经常会遇到,在controller层报错找不到数据库连接,一轮排查后发现,实体之间的join使用了懒加载,而在controller层是没事务没数据库连接的。其实问题的症结在于,实体类实体对象不应该返回给controller层,应该返回的是定制好了的POJO、符合接口返回的普通的java对象。我们经常偷懒把实体对象返回给controller层,也不是不可以,但是用的时候心里要明白,这里拿到的对象已经不是同个东西了,这里已经没事务没数据库连接了。

  如果哪天又有同事找你看看那个是什么问题,不要犹豫,掀桌而起!那个是哪个啊?什么环境(生产还是测试)什么场景(用户行为)什么环节有什么现象啊?如何id一个事物都讲不清楚,前方绝壁有大坑。。。那就慷慨就义跳进去填吧~

时间: 2024-10-24 11:06:26

一个典型的后台软件系统的设计复盘——(一)如何id一个事物的相关文章

App 后台架构设计方案 设计思想与最佳实践

转载请注明出处:http://blog.csdn.net/smartbetter/article/details/53933096 做App做的久了,就想研究一下与之相关的App后台,发现也是蛮有趣的.App后台的两个重要作用就是 远程存储数据 和 消息中转.这里面的知识体系也是相当复杂,做好一个App后台也是需要长期锤炼的.本篇文章从 App 后台架构 的角度介绍.好了,下面进入正题: 说起架构,我们先看一下何为架构,百度百科是这样说的:架构,又名软件架构,是有关软件整体结构与组件的抽象描述,

一个星期自动态的网站设计

(现场:http://chentingpc.me) 该网站的设计与框架0.1实现版本号,它比共六天了更,将近10个小时56.的确,在做需求分析.页面和系统设计时间占了近4天,态功能的部分仅仅花了两天时间来完毕. 当然,这当中包含了从零開始对站点的设计与实现的相关语言与工具(HTML.CSS.Javascript.PHP.Mysql)的学习:除了以前用wordpress与discuz之类的框架搭建过站点的经历,我对HTML.CSS.Javascript.PHP.Mysql的编程基本是一无所知.这篇

高性能后台服务器架构设计

ref:https://www.cnblogs.com/lidabo/p/6627642.html 如何设计高性能的大型网站系统?在移动互联网时代,客户端应用开发本身,并不是体验的决胜之处,真正对团队挑战的地方,还在于后端,无论是承压能力,还是安全性等方面,如果这些地方过不了关,整个应用的基础是不扎实的. 提高服务器性能最简单粗暴的方式,就是增加机器和升级硬件配置.虽然现在的硬件越来越便宜,但是一味地通过增加机器来解决并发量的增长,成本是非常高昂的.结合技术优化方案,才是更有效的解决方法. 一.

Linux下一个简单的日志系统的设计及其C代码实现

1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回值及其执行情况.脚本执行及调用情况等.通过阅读日志文件,我们能够较快地跟踪程序流程,并发现程序问题. 因此,熟练掌握日志系统的编写方法并快速地阅读日志文件,是对一个软件开发工程师的基本要求. 本文详细地介绍了Linux下一个简单的日志系统的设计方法,并给出了其C代码实现.本文为相关开发项目Linux

一个推荐系统,实现完整的设计-在百度搜索关键词推荐案例

在之前一篇博文中, 有同学在评论中问了个问题: 怎样解决因式分解带来的推荐冷门.热门关键词的问题. 在回答这个问题的时候, 想到了近几年在做搜索推荐系统的过程中, 学术界和工业界的一些差别. 正好近期正在做技术规划, 于是写偏文章说下工业界完整推荐系统的设计.结论是: 没有某种算法可以全然解决这个问题, 多重算法+交互设计, 才干解决特定场景的需求. 下文也对之前的一些博文进行梳理.构成一个完整工业界推荐系统所具有的方方面面(主要以百度关键词搜索推荐系统为例) 完整的推荐系统肯定不会仅仅用一种推

如何搭建类似湖南卫视芒果TV直播的APP后台软件系统

要说看网络电视,那湖南卫视的直播APP绝对是话题谈论的焦点.据中商情报网数据显示:2017年4月OTT直播端媒体排名:湖南卫视第一. 直播端媒体日均活跃终端数量的差距比较大,第一名与第十名的2倍多.直播频道日均收视时长中,湖南卫视排名第一位,日均单终端收视时长约1个半小时,是第十位的近2倍. 同时我们也可以注意到:通过手机端APP来观看电视频道直播已经成为大的趋势,也可以说移动端的电视观看已然成为主流.网上通过搜索引擎的搜素发现很多的运营者都在找能够支撑大并发的OTT-TV电视直播后台系统解决方

Android中UI线程与后台线程交互设计的5种方法

我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启一个后台线程运行这些任务.然而,往往这些任务最终又会直接或者 间接的需要访问和控制UI控件.例如访问网络获取数据,然后需要将这些数据处理显示出来.就出现了上面所说的情况.原本这是在正常不过的现象了,但是 android规定除了UI线程外,其他线程都不可以对那些UI控件访问和操控.为了解决

Linux内核设计第三周——构造一个简单的Linux系统

Linux内核设计第三周 ——构造一个简单的Linux系统 一.知识点总结 计算机三个法宝: 存储程序计算机 函数调用堆栈 中断 操作系统两把宝剑: 中断上下文的切换 进程上下文的切换 linux内核源代码分析 arch/目录保存支持多种CPU类型的源代码 其中的关键目录包括:Documentation.drivers.firewall.fs(文件系统).include init目录:含有main.c,内核启动相关的代码基本都在init目录下 start_kernal()函数为启动函数,初始化内

C++多态性的一个典型例子(转)

本文摘录自C++多态性的一个典型例子. 例题:先建立一个Point(点)类,包含数据成员x,y(坐标点).以它为基类,派生出一个Circle(圆)类,增加数据成员r(半径),再以Circle类为直接基类,派生出一个Cylinder(圆柱体)类,再增加数据成员h(高).要求编写程序,重载运算符“<<”和“>>”,使之能用于输出以上类对象. 这个例题难度不大,但程序很长.对于一个比较大的程序,应当分成若干步骤进行.先声明基类,再声明派生类,逐级进行,分步调试. 1. 声明基类Point