月度归档:2012年01月

胖子的乱看乱想笔记二

1、最困难的时候,你需要做的就是坚持突围,不要想别的,因为没有其他的路可以走。-- 乐淘网CEO毕胜

2、一个好的Leader做好两个字就够了:“勇”和“容”。前者是勇于承担责任;后者是从内心包容下属。 -- 网络:双鱼座

3 、过程的形参名必须局部于有关的过程体–SICP

4、你得让计算机告诉你它在干什么,它干了什么!

5、项目越大,所需要的人越多,从而沟通的成本也就会越高,如果解决了沟通问题呢?

6、并发,时间是一个本质问题 ﹣SCIP

7、将语法分析和执行分离,分离后,可以专注优化各部分,各自成长。

8、第一,要相信没有什么真正难的程序;第二,要意识到程序是写给人看的。–Bernie Cosell

9、不要只阅读那些编程习惯跟自己一样的家伙的代码–Dinald Knuth

10、都是解渴,有的白开水,有的要泡杯茶 - Damon Peng

11、遇到问题,一定要到现场亲自观察确认问题的根本原因,不要用经验轻易下结论。现场有神灵。

12、有其他涉众的问题,先沟通,再做事。

13、做技术也需要了解你的产品所处的环境,可能出问题的不是你的代码,而是你的代码所在服务器国家的某些政策和法规。

14、预警机制的方式不能仅仅是邮件,在这里也应该有高可用性的考量,确保一种方式失效后有其它的方式可以马上替换或自动切换。

15、1.写文章,多发表个人见解,增加个人思考机会;2.大量看书,自学,但一定要选好书;3.多和圈里高手交流,听君一席话,胜读十年书;4.建立个人文件管理系统,不断整理自己的原创;5.参加系统学习,找到短板,快速学习;6.实践,大量实践! — 李开复: 如何提升个人专业能力

16、经验这东西,往往并不能告诉我们什么一定对,但是可以告诉我们什么一定不对。﹣霍炬

17、书是一个很好弥补没有高手在场的方法,因为书是最好的老师。﹣徐宥

18、分析每天时间怎么花的

19、系统发布时要考虑预热时间。

19、 自古不谋万世者,不足谋一时; 不谋全局者,不足谋一域。 —[清] 陈澹然《寤言二迁都建藩议》

20、不登上山顶,你就无法体会登顶的感觉,也无法看到山顶的风景。

21、成功的几率很小,因为必须同时应对的因素太多 - 《形式综合论》

22、敬人者人恒敬之 爱人者人恒爱之 - 《孟子》

23、 我的天性是挺胸直立,骄傲而无所畏惧,勇敢地面对这个世界。-《管理精英宣言》

24、制定计划从大到小,实施计划从小到大,先有想法,然后再想办法让想法落地。

以上的内容纯粹属于胖子各种时间记录在evernote的汇集,此版权不属于胖子

一次查询优化过程

问题描述

在正在维护的系统中有一个数据分析的模块,用于来分析一些用户访问的数据。其中有一个操作是查找某URL地址对应的ID。在这里ID与URL是存储MySQL数据的一个表中,此表就两个字段,id,url.表存储引擎类型MyISAM,当然URL字段是加了索引的。现在每天按小时定时分析数据,开始程序没有问题,当几年的数据积累下来,特别是最近业务量增加时,明显感觉到程序的执行过程变得很漫长,通过xdebug分析一次执行过程的所有执行时间,发现瓶颈在于前面所提到的通过URL地址对应的ID。下面就开始了漫长的优化之路。

第一次优化:优化细节,针对特殊地址的优化

由于应急,先考虑一些细节的调整:
首先,我们针对一些特殊的地址进行了处理,直接返回ID,比如没有URL的情况,比如Google的首页地址;
然后,我们针对一些常见的地址作为Hash存储,以及一些热数据进行内存缓存。

结果,提高一些性能,能满足当时的业务需求,特别是当特殊的地址较多的时候,其情况还是非常乐观的。

过了一段时间,到了另一个高峰点,发现此分析模块仍然会时不时的出些问题,导致数据不准确,各种情况,各种应付…不得已,开始思考另一个方案。

第一个方案: key-value数据库

依据前面的对于热点数据,将其存储到内存缓存的思路,我们考虑是否将所有数据都作为热点数据,于是先做前期预研,将部分数据导到redis中,发现速度有了极大的提升,大概在500倍的一个量级,那叫一个激动,于是决定将所有数据都迁移到redis中,想法是美好的,结果是不言而喻的。失败了。为什么呢?很简单,没资源,我们没有那么大的内存存储N个上千万的表。

第二个方案:基于单词查找树的文件结构

从前面的key-value数据库方案看,存储url到id的映射,在实现思路上还是可行的,于是开动了小心思,既然内存不够用,那么用硬盘呢?

这个既便宜又实惠,是居家旅行,杀人越货的必备良品。于是计划以URL的主域名为目录,以每个地址的md5值为文件名,每个文件存储这个地址对应的ID。考虑到域名可能很多,于是计划使用变形的单词查找树来设置多级目录,但是当域名地址太长时,文件系统在生成目录的时候会出错,只得去掉单词查找树,使用某种简单的按主域名转化后的字符串转建立目录。在预研时,先生成了5万数据的文件结构,发现生成的速率很慢,并且由于大量小文件的生成,对于整个目录的查询会很慢,但对于单个文件的读取还是很快的,然而将随着数据生成越来越多,当达到40万时,整个目录占用了大概4G+的硬盘空间。

假想一下,当1千万的数据以这种方式存储到硬盘上,会是多少?如果数据持续增加呢?到达一个亿呢?感觉到不靠谱了。于是继续想下一种方案,这个方案继续执行。

第三个方案:信赖于数据库,增加md5值存储的字段

在第二种方案中,我们是使用md5值作为文件名,在想在数据库对于md5后的文件名的查找会更快一些呢?在ID和URL映射表中,增加一个字段url_md5,存储url使用md5后的值,在这里我们直接使用MySQL的md5函数更新表数据,测试后,发现其速度有了显著提升,单个查找操作大概能提升50倍+。整体性能提升5倍的样子。于是,果断采用此方案,结果是该分析模块一直没啥事发生。(^_^)

总结

  1. 不要过早优化
  2. 优化要先找瓶颈
  3. 数据结构和对工具的使用很重要
  4. 优化时需要理清问题的根本原因是什么,最好能到理论层面或实现原理层面。

无分支开发

在一个代码库上创建分支的能力是版本控制系统最重要的特性。它的作用是创建一个副本,并将在这个副本上进行操作,从此它就有了它自己的发展方向,也许有一天会回来,回来时就会合并。分支的作用就是为了更好的帮助并行开发,即在同一时刻能够在两个或更多的工作流上面开发,而不会相互影响。分支开会可以用于哪些应用场景呢?

假设我们现在使用的是SVN,并且都是在主干上开发,此时A项目组下在开发,而B项目组准备发布,并且两个项目组中间有一些公共的代码,这个时候就产生了B项目组发布对于A项目组的时间依赖。此时分支开发是一种解决方案。

常见的分支方法有两种:

  1. 在开发时分支。针对不同的项目或开发团队做分支操作,每个项目或开发团队有一个副本,其所有的操作都在副本上进行,在发布时将分支合并到主干,做整体的合并集成,并做整体发布。这是先分后合的招式。
  2. 在发布时分支。主干上开发完成发布后,创建分支,在分支上修改BUG,当修改的BUG发布时将修改的内容合并到主干。这是先合后分再合的招式。

分支是基于并行开发的一种理想状态,即各个分支是完全独立存在的,并且互不干扰,各成系统。但是现实是各个分支基本上都是有一些关联或依赖的。在开发时可能没有感觉,但是当需要将分支的内容合并时,就会产生较多的冲突和问题,虽然现在的版本控制系统已经在这个方面做得相当好了,但是实际操作中还是会存在一些问题,还是会花费较多的时间。因此在做分支开发时,通常会建议以尽可能高的频率将分支的代码合并到主干净,并且确保主干的正常运行,这样会在一定程序上减缓最终实施合并时的冲突。

什么是无分支开发呢?所谓无分支开发就是指所有的开发工作都在主干上进行,当然这是一种理想情况,但是我们可以尽可能向它靠拢。如果实施了无分支开发,此时就不会存在分支、合并、解决合并冲突等问题了,解决合并冲突的问题最好的方法就是永远不要合并,这和赌博不输的最好的办法就是永远不赌一样。为什么说这是理想情况呢?因为在我们的项目中存在大量的依赖,一个文件或一个类,甚至一个函数可能会被多个人或多个团队修改,如果是多个团队的多个项目并行的话,这咱依赖关系将会影响项目的发布。那么怎么减少这些依赖,嗯,是减少,因为我们基本不可能完全去掉这种依赖,这和我们写代码一样,一些有必要的耦合是完全存在的,并且有其存在的必要性。

  1. 迪米特法则。迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),”不要和陌生人说话“,也就是说一个对象应当对其他对象有尽可能少的了解。这是到类级,对象级别的讲究。
  2. 减小需求的粒度。以一种更加敏捷的方式实现需求,将大的需求分割成小的需求,以增量的方式实现并发布。
  3. 使用通过抽象来模拟分支的方式实现代码库中大范围的变更。
  4. 对于公共部分做到文件级的版本发布。即一次发布多个文件的多个版本。
  5. 使用模块和组件,从功能、团队或变更频率等角度对代码进行切割,以解耦代码间的依赖.
  6. 将新功能隐藏起来,直到它完成。

以上的几点更适合于小规模团队。

关于第一点,这是面向对象设计的重要原则之一,除此之外,我们经常还会念叨开闭原则,KISS等等。这些都是实现无分支开发时代码级的一些优化措施。

关于第二点,将大的需求变为小的需求本身就是一个比较困难的工作,拆分的原则是什么?粒度以多大 为优,这些都需要依据实现的情况进行判断,以周为单位?以页面为单位?这种方式会比较轻,可以随时停下,即使需求方向有问题,我们也是可以在较少损失的情况下调头。如果我们坚持这样做,那么就意味着我们在解决一个问题:保持应用的持续可工作。

关于第三点,通过抽象来模拟分支的实现步骤如下:

  1. 当需求分解后还是无法增量开发或增量开发无法解决问题时,考虑引入中间层或抽象层。使要修改的部分与调用者分离,使其直接低耦合。
  2. 在需要修改的那部分系统代码上创建一个抽象层。
  3. 重构系统的其他部分,让他使用这个抽象层。
  4. 创建一种新的实现代码,在它完成之前不要将它作为产品代码的一部分。
  5. 更新抽象层,让他使用这个新的实现代码。(如果没有完善的回滚机制,建议在此处做切换开关,即以一个变量控制抽象层的实现,当然,这并不是一个优雅的解决方案)。
  6. 移除原来的实现代码
  7. 如果不再需要抽象层,就移除它。

关于第四点,对于公共部分的代码,其修改频率较之其它功能模块更频繁,并且可能存在需要发布旧版本的需求。

关于第五点,模块化,组件化在架构设计中现在已经是非常普遍的行为,但是要想完全模块化或组件化,其难度较大,尽可能的向这方面靠拢,以一定的规则划分模块,针对模块进行设计实现,并在最后将所有模块的结果有机的结合起来。这是在设计层面的分支和合并操作的替代品。

关于第六点,将新功能隐藏起来,直到它完成。这是在没有增量式发布,小步快跑的前提下,或需求确实无法拆分,一组特性一定要一起实现,此时通常会开一个分支做这些新功能的发布,而无分支开发中,可以将这部分代码放在主干,但这些功能对用户不可见,以某种配置项(文件或数据库都行)的方式来操作其对用户的可见性。

《持续交付》总结一