如何快速构建开发原型

Submitted by Lizhe on Sat, 07/08/2017 - 14:27

Brian (还是稍不留神就能打成brain =。=) 的offer差不多到手了, 为了感谢我的技术支持拉着Roc和Maven 晚上小聚了一下

总的来说我还是非常喜欢他抛给我那两套题的 ( 额...如果这东西能算是题的话 ) , 基本就是个大学生的期末大作业

第一个项目我已经提过了 如何创建restful api

个人觉得第一个项目命题的面试官明显是想考察interviewee如何进行技术选型, 如何组织代码结构, 如何处理异常, 如何进行集成和发布

第二个项目是一个竞拍系统, 题目大概是这样的

我需要一个竞拍系统, 参与的角色有一些owner,bidder什么的, 被拍卖物品只有一种是钻石, owner需要知道当前的最高价, bidder需要知道自己的报价在第几名, 然后这些数据要实时刷新

这个命题是中午11点给出的,下午1点就要面试,打电话的时候Brian在北京, 他讲完需求以后就开始问我

你看我这样设计类行不行 ( 然后他开始巴拉巴拉的讲了一堆class Owner, class Bidder 什么的 )

故事就讲到这里, 对这个故事我的感触是, 其实很多人在一个新项目开始的时候, 往往切入点是错误的( 我认为是错误的 ), 例如 一开始就选择什么SSH 反正现成的框架一把一把的随手抓

然后马上就会陷入到无穷无尽的接口和数据定义的泥坑里不能自拔

然后我只能打断了Brian, 提了3个问题

1. 我们手中有多少可以被抽象的实物 , 也就是真实存在这个世界上的东西 ? 

Brian: owner, bidder , 钻石, 拍卖会 ...

2. 那么现在是否能全部先按照范式整理成数据库表

Brian: owner 表的数据有 用户名

              bidder 表有姓名

             钻石 表 有钻石编号

             拍卖会表 有开始时间, 参与的bidder, 发起的owner, 当前最高出价等

             ......

3. 那么现在你能把表之间的关系整理一下么?

Brian:   一个owner 可以发起多个拍卖会 ( 我: 也就是一对多的关系, 拍卖会表需要一个owner的列)

              一个拍卖会有多个bidder , 一个bidder可以参与多个拍卖会 ( 我: 多对多, 需要一个中间表 )

             .......

这3个问题都整理完成后, 我们得到了一个最基础的数据库模型, 剩下的事其实很简单, 只需要根据需求把所有的sql 先写一遍, 剩下的java 类就已经呼之欲出了

 

当拿到一个新需求之后,我的经验是, 既然上学的时候我们都学过  " 程序 = 算法+ 数据结构 ", 其实我特别不喜欢这句话, 可能是这句话对于我们这些做应用层的人不太实用,

我更喜欢 "程序是一切现实事物的抽象表现" , 你要写的代码永远不可能脱离现实世界的实际物体而存在, 所以当新需求发布之后, 整理需求的第一个步骤就是整理 "实体"

例如一个票据管理的工作流系统, 首先应该明确的是

  • 这个系统中一共有多少参与者,角色都是什么( 例如你可能会关心他们的 员工ID, 角色, 权限等)  ==> 对参与的人进行抽象
  • 这个系统中有多少种需要处理的票据, 都有哪些内容 ( 例如发票, 入库单据, 出库单据, 出纳提供的财务票据之类, 当然这些可能会被抽象成一个表 ) ==> 对需要处理的数据抽象
  • 上面提到的角色和票据都有哪些状态, 可能会进行的动作 ==> 对业务抽象

 

有了上面这些基础数据之后应该优先按照范式来设计数据库, 这样你会对整个模块要处理的数据有一个完整认识, 可以避免自己陷入复杂的对象关系中

但仅仅到这里还是远远不够的, 尽管类似上面的 拍卖系统 只是一个小样例, 但是命题人的高明在于仍然能考察出被面试者的基本功底, 因为这是一个对性能有要求的实时系统

 

这里在设计完数据库模型之后需要一个逆范式的动作, 逆范式的基本思路在于, 冗余需要冗余的数据, 也就是空间换性能

所有的性能调优都是一门玄学, 但越是玄学你越要抓住一些 "摸得着看得见" 的东西 ==> 你能预见到的SQL 语句, 尤其是能被预见到要频繁调用的SQL语句(或复杂运算)

例如 bidder需要查询自己的出价当前是第几位

          owner需要知道当前的最高报价

这两个动作在这个系统中可能是最容易被频繁调用的, 需要有针对性的优化

对于第一个, 出价排名问题, 非冗余的常规做法是 select count 然后找出比自己出价高的人, 这样得到一个比自己出价高的人的人数, 然后 +1 就是自己的排名

这样做的问题在于假设系统用户人很多, 每个人都要频繁的实时获取自己的排名,而且又分布在不同的拍卖会中, 好了, 现在你需要不停的进行count运算, 可能还需要一堆where条件语句, 势必大大影响数据库的响应, 尤其是在索引设计有问题的情况下, 没完没了的全表扫描几乎在所难免 ( 索引的问题我们一会再说 )

如果只有数据库的话, 你可能需要一个类似临时表一样的东西当做缓存来保存每个人的排名, 这样你就不用使用count之类的聚合函数, 也可以使用memcached或者redis之类的作为缓存层,直接在缓存层上操作排名, 当然了竞标的原始数据还是要存储到数据库中的, memcached没法进行数据持久化而redis的持久化似乎性能开销更大, 如果这样还觉得不够简单粗暴,还可以直接将值缓存在java虚拟机内存中, 因为报价总是只加不减, 每次有新报价出现后, 把所有之前的报价排名+1就好了, 总之你要好多种选择把这些数据 冗余 到其他地方

上面的说法对于第二个问题也试用

PS:

为什么我不着急设计索引, 索引? 说得极端一点, 系统Go live之后重新设计索引都没有问题, 第一它不影响数据库结构, 第二它的"重要性"简直太低了, 修改成本几乎为零

 

关于拍卖系统的题目最后隐含着一个对 数据如何显示到浏览器 上的问题

构建基于B/S的实时系统以前确实没有什么好办法,除非你使用浏览器插件之类的, 过去只能使用Ajax不停的请求, 因为http协议本身就是基于request和response的

针对这个问题 Jquery甚至还提供了一个像是 "伪链接" 一样的东西, 底层还是不断的通过loop去获取数据

如果你没注意过现在tomcat已经全部都是http1.1标准了, 这道题你可能要悲剧, 至少如果你给出 " 不断的循环去获取数据 " 这样的答案肯定不是面试时的加分项, 正确的答案是

长链接 和 websocket ... 

 

最后我还是要表达一下自己对这两个题目的钦佩之情, 第一个题目考察了技术选型, 代码结构, source管理器和发布流程的设计(甚至还要了dockerfile,提供一键部署功能)

第二个题目考察了完整的项目设计, 而且如果面试官愿意的话, 面试时基于第二个题目还会有一堆例如 如何提供并发性能,如何扩展,如何做分布式之类的问题等待着候选人

非常不错的题目, 不枉费我上周六周日给Brian抱了两天佛脚 :P