留心那些潜在的系统设计问题

标签: 系统 设计 问题 | 发表时间:2013-09-19 15:37 | 作者:
出处:http://www.iteye.com

在系统设计阶段考虑全面很难,有许多人倾向于把整个设计分成若干阶段,在迭代中完成整个设计,这本身是非常好的,但是,就如同“先做出来,以后再优化”这样的经典谎言一样,本身并无错,只是许多程序员都不习惯于真正的迭代设计和迭代优化。举例来说,有一个日益复杂的类,每个人都修改一点点,一直到最后都没有人愿意去做重构,大家的心态都是一样的:“我只修改了一点点,为什么要我去动那么大的刀,于我没有任何好处”。我不在这里谈论这一问题的解决办法,我倒是想说,在开始阶段考虑清楚问题在多数情况下还是很有好处的,设计考虑得越是清楚,在后续阶段代码可以承受越多的变更而不腐朽。

再做系统设计的时候,我们常常会这样说:“一般情况下”、“99%”和“基本上”等等。如果你发现这是在悄悄地,或者潜意识地避谈问题,可就要小心了。有时候你可以找到根据,“事情不会那么坏吧”,“不会那么不凑巧吧”,在系统设计阶段尽把事情往好的方向想可未必是件好事;也许更多时候会觉得这是直觉,总觉得某一处设计别扭,不合理却有说不出强硬的理由来,最多只能抱怨一句“通常它不应该是这样设计的”。这种情况发生的时候,请千万不要放过它,很多次,在系统上线以后,最初的问题或者潜在的问题最终暴露出来,而这样的问题很多在系统设计阶段都是有端倪的。

例子1:用户行为记录的持久化

以前我参与做过这样一个系统,用户的行为需要被记录到数据库里去,但是每条记录发生的时候都写一次数据库觉得开销太大,于是设计了一个链表:

  • 用户的行为会首先被即时记录到链表里面去;
  • 每十分钟往数据库里面集中写一次数据,然后清空链表内的数据。

看起来确实可以实现需求,可是,这样的设计有什么问题?

这样的设计当时居然没有受到系统设计评审的人的质疑,我实在觉得奇怪。我想很多人都可以看得出潜在的问题:

  • 清空链表数据是使用时间条件触发的任务来完成,换言之,无论这十分钟内如果事件暴增,也无法触发链表清空的行为,链表很容易变得非常大;
  • 清空链表的任务如果执行过程中出了异常,甚至仅仅是处理速度受到阻塞,将直接导致链表数据无法得到清空;
  • 如果往数据库里写数据和清空链表的行为需要锁定链表,倘若链表很大,或者写数据库过慢,都会导致链表写行为被阻塞。

这些问题当然在明确的情况下可以得到规避,但是毫无疑问,这样的设计充满了潜在的危险。事实上,最终这样的问题也确实发生了,导致的结果是链表巨大,撑死了整个系统,OOM,系统失去响应。

例子2:HashMap并发访问导致死循环

非常常见的并发访问HashMap的问题,我也遇到过。有潜在的危险导致HashMap死循环,表现就是CPU占用100%,而且这样的问题是不可逆的,问题的原因分析我相信大家可以在网上搜得到很多文章,我就不啰嗦了。我印象深刻的是当时定位完问题,向犯下错误的程序员解释原因的时候,他居然还说:“这个HashMap的读写很不频繁,哪有那么巧的事?”,这就是侥幸心理,即便知道了问题依然不愿意做出修正。

例子3:摘要算法的冲突问题

类似的问题还有,使用摘要算法的时候,比如MD5,我在做一个系统,使用一个中心集群缓存,使用一个巨长的字符串的MD5摘要来做key,好处在于key的长度可以大大缩短,但我们都知道,任何摘要算法都会使得结果字符串存在冲突(重复)的可能,即源字符串不同,但是摘要字符串相同,虽说用统计的话来说,单纯两个字符串发生这种情况的概率低到几乎不可能发生。但是我们依然需要谨慎,尤其是在数据量巨大的情况下,一旦发生冲突,要有解决办法(比如把源字符串放在缓存条目的结果对象中,在缓存条目命中,正式取出返回前,再进一步比较源字符串以确定100%的准确性),或者至少必须要能够承担风险。

例子4:文件处理后续流程的两个问题

最近有一位同事向我们介绍了他最近处理的一个问题,这个问题是,用户会上传一个多行的文件,比如文件有一万行,每一行都代表一条待处理的数据,在数据正确的时候,一切都正常;倘若有一行数据处理发生错误,会自动发送一封邮件通知,看起来似乎很不错的系统。但是这个时候问题来了,有一次文件的处理错误过多,导致一口气发送了几千封邮件,变成了邮件洪水。而在他介绍这个系统设计的时候,我们留意到了其中存在一个时间条件触发的任务,任务基于两个数据库的数据执行,这两个数据库的数据同步是单独完成的,因此可能存在数据不一致的情况,并且在这里假定在数据更新的一小时以后,两个库的数据就会一致了。这其实就涉及到了两个问题或者隐患,一个是邮件处理和发送的数量缺乏控制,另一个是用假定的时间来保证数据的一致性。

例子5:单点故障问题

单点故障问题也是很常见的会导致服务失去的问题,出了问题所有人都知道原因,但是有时候就是很难在系统设计阶段识别出来。以前我们给电信运营商提供服务,很多电信运营商通常有钱(比如国内的三家垄断巨头),不太在乎成本。服务器用的单板几万块钱一块,备了几十块,文件存储是一个大型的磁盘阵列,数据库是IBM小型机双机备份(PS:IBM的设备其实挺不可靠的,听维优的同学说,保修期内屁事儿没有,保修期一到一台台IBM的机器开始坏,搞得像定时炸x弹似的),当时唯独忽略了单点的负载分担硬件——F5,F5挂掉的时候,工程师都傻了眼。

例子6:文件不断写入导致磁盘满的问题

文件写满磁盘导致空间不够的例子也非常常见,绝大多数写文件的场景大家都会留意到,并且在系统设计评审的时候都会有人站出来问,“xxx的文件写入是否是可控的?”。但是,由于文件写入的场景非常多,还是有很多情况被忽略。比如JVM的GC日志的打印,这样的文件可以协助定位问题,但是如果不设置文件上限大小参数,就有导致磁盘空间不足的风险;还有日志文件,绝大多数系统都有日志文件压缩或者日志文件转移的脚本,但是和前面提到的例子1一样,一方是生产者,一方是消费者,消费者出了问题,就会导致数据堆积。如果这样的文件处理脚本执行出现问题,或者在系统压力大以及系统异常情况频繁的时候,日志疯涨,来不及及时把日志文件转移出去,导致日志文件把磁盘撑满。通常对于要求比较高的服务,磁盘空间监控是必要的。

例子7:服务器掉电以后的快恢复

再说一个问题,这个问题是从一个技术分享中流传开来的。亚马逊网站的数据都是页面服务器先从缓存服务中获取数据,通常这个命中率很高,如果获取不到数据或者数据过期以后再到数据库里查询。这样的模式非常常见,我们也总能看到很多技术报告里面写平均的缓存命中率能够达到百分之九十多,可以飙到多少多少的TPS,为此可以节约多少多少硬件成本。初看这样的设计真不错,但是很容易忽视的一点是,这样的数据是建立在足够长时间,以及足够多统计数据的基础之上的,但是在单个时间段内,缓存命中率可以低到难以承受的地步,导致底层的数据服务直接被冲垮。有一次亚马逊机房突然掉电,在恢复的时候把网页服务器都通上电,这时候缓存服务还几乎没有缓存数据,缓存命中率几乎为零,于是大量的请求冲向数据库,直接把数据库冲垮。外在的表现就是,掉电导致网站无法提供服务,短期内访问恢复,随后又丧失服务能力。

软件当中有些东西和经验有密切关系,不像很相对容易提高的语言技能和算法,系统设计经验,尤其是对问题的预估很需要时间和项目的磨炼。我不知道这样的系统设计经验怎样才能快速积累,但是我想还是有一些常规模式可循,我不知道是否有比较经典的资料可以学习。另一方面,系统设计真是一个细致和谨慎的活儿,不要随意放过那些潜在的问题,有时候甚至就是一点奇怪的感觉,或者是设计图看起来不那么协调和稳当,细究下去,还真能发现陷阱。如果你也有类似的经历,不妨谈一谈。

文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [系统 设计 问题] 推荐:

思考系统API设计的问题

- edware_love - C++博客-首页原创精华区
最近正好在思考系统API设计中考量的一些问题,. 我现在的理解是这样的,假设有巨大的真实内存. windows首先将高2G的内存自己占了,用作各种内核对象. 这2G内存共享给每个进程,但进程不能直接访问,只能通过windows给定的函数访问. : 然后每个进程都给他2G内存,进程如果创建自己的对象就放到自己那2G内存里面,如果要建立内核对象就放到共享的那高2G里面去.

留心那些潜在的系统设计问题

- - ITeye博客
在系统设计阶段考虑全面很难,有许多人倾向于把整个设计分成若干阶段,在迭代中完成整个设计,这本身是非常好的,但是,就如同“先做出来,以后再优化”这样的经典谎言一样,本身并无错,只是许多程序员都不习惯于真正的迭代设计和迭代优化. 举例来说,有一个日益复杂的类,每个人都修改一点点,一直到最后都没有人愿意去做重构,大家的心态都是一样的:“我只修改了一点点,为什么要我去动那么大的刀,于我没有任何好处”.

系统设计典型问题的思考

- - 四火的唠叨
最近我老婆在找工作,于是我也一起学习了一些系统设计的知识,这里总结典型的思路和题目. 首先,反复沟通和澄清系统需求. 只有把需求澄清清楚了,才可以开始思考并落到纸面上. 但是需求的沟通应该是持续和循序渐进的,问题很难从一开始就思考全面. 其次,尝试抽象一个简单的模型,从简单模型开始,思考不同的场景和约束,逐步完善.

iOS最大问题已非系统设计 功能性成最大软肋

- - cnBeta全文版
美国知名媒体《商业内幕》特约撰稿人杰-耶罗(Jay Yarow)日前撰文表示,苹果iOS操作系统目前存在着更深层次的问题,许多第三方应用软件的设计已经远远超越了苹果内置应用,因此在乔纳森-艾维 (Jony Ive)领导下的iOS 7软件设计团队仅仅对应用外观作出改进恐怕还远远不够. 目前,有不少消息指出苹果正在针对下一代驱动iPhone、iPad和iPod Touch的iOS操作系统进行重新设计.

评价系统设计篇

- - 互联网 - ITeye博客
评论系统大家都见得非常多了,大到京东、淘宝、亚马逊,小到个人网站、博客都有评论系统,小型网站采用传统PHP+Mysql方式就能很快将系统搭建起来,同时采用单库单表方式就能轻松解决数据存储、数据查询等问题,但是对于上述中大型网站而言,已经远远不能支撑系统正常运行了. 接下来将从系统架构、数据存储、高性能服务等方面来揭示京东的评价系统在面对海量数据、海量请求的情况是如何处理的.

正在关注的设计问题

- - 曉生
有些想法不方便写到博客中分享,还有太多的问题需要研究. 以实践为主,以后分享希望都是自己的实际项目经验. 写博客耗费精力,时间不如用来阅读更多的资料. 有工作三五年的设计师朋友感觉遇到瓶颈,所谓瓶颈就是不知道未来的发展方向和应该做什么,分享我正在关注的设计问题,希望能有所启发. 对WP7手机本身没有好感,Metro风格有些超前,自身还存在不少缺陷,但微软将这一风格衍生到其他产品,特别是Win8.

20个设计模式和软件设计面试问题

- - ImportNew
不管是参加Java面试还是C#面试,. 设计模式和软件设计都是任何编程面试中的必问问题. 实际上,编程能力和设计技巧是对彼此很好的补充. 一个好的程序员通常都是一个好的软件设计人员. 他们知道怎么把一个问题分割成一段段代码或者软件设计,但这些能力和技巧并不能凭空而来. 你需要持续做大型、小型系统的设计和编码,并且不断从错误中学习.

系统设计中的简单法则

- - 酷勤网-挖经验 [expanded by feedex.net]
最近,包云岗在自己的 博客中总结了系统设计中的基本法则——简单之美,列举了不少经典观点和案例. 他首先总结了麻省理工方法(MIT Approach)和新泽西方法(New Jersey Approach)的异同:. 简单性:两种方法都强调设计必须简单,这既是对实现的要求,也是对接口的要求. 但是,MIT方法认为接口的简单要比实现的简单更加重要,而NJ方法认为实现的简单要比接口的简单更加重要.

财务系统设计的思考

- - 行业应用 - ITeye博客
说到财务系统的设计,就不由得联想到了目前很流行的一个职业“互联网产品经理”,他们的设计着眼于用户体验,创造出新的功能,改善着上亿网民的生活,比如扫一扫,摇一摇等. 财务系统不同于互联网的产品,它的复杂性对于没有深入了解它的人来说,是不太能想象出来的. 互联网的功能开发,讲究的是时效,从一个点子,到产品发布可能只用一周的时间,然后如果市场冷淡,可能第三周就下线了.

12306订票系统设计关键点

- - 互联网旁观者
12306全国火车票网上售票网站的情况大家都见到了,如果让你来设计该订票网站,你会如何设计才能应对如此大规模以及高并发的情况呢. 以下是百度前技术总监邵辉给出的设计:. 列车在线订票系统的业务逻辑比较简单,不用多说. 可能的瓶颈有两个,一个是车次和剩余票量的查询,一个是下单. 在设计软件架构之前,需要先研究产品需求、软硬件条件、网络环境以及关联系统的接口,但这些资料无从获得,所以只能做几点分析和假设,做为设计的前提条件.