解决方案,而不是功能
扪心自问,你真正了解你卖给用户的是什么玩意么?你所认为革命性的,一定会震惊世界的功能、特色,用户真的买单么?
我的意思是,我们总是习惯性的忘记一个事实:我们并不是向用户出售一款产品或者服务;我们是向用户出售一个能够搞掂问题的 解决方案,它能够为用户 创造价值,并让用户为这个问题 少操心一点。
举个简单的例子:当你认为你在卖钻子的时候,其实用户想买的是洞。
It’s about solution, not features
作为产品的设计者,你应该关注在产品所能给用户解决的问题,而不是把焦点集中在具体功能之上。
经常性地,某个功能被你的用户或你的同事提出时,而且这个功能听起来是那么的激动人心,并且你的竞争对手还没做,你得先冷静的思考一下:它们属于哪种问题的解决方案,有没有更好的?
往往你所认为的一个好功能,放在你的产品内,也许是个很糟糕的解决方案。
阅读优秀代码是提高开发人员修为的一种捷径
豆瓣css开发规范
今天无意间看到了豆瓣的一些前端开发规范, 攻城师作战指南
里面的 Javascript代码风格规范 差不多就是基本的javascript规范,主要还是分享一下 css部分的规范
因为规范是在google docs上,需要穿过篱笆,所以我就直接帖过来了,一起学习一下哈
以下就是转的部分
——————————————–华丽的分割线————————————————
Douban CSS Code Guideline
1. CSS浏览器支持标准
| WinXP | Win7 | OS X | |
| IE9 | C | C | |
| IE8 | A | A | |
| IE7 | A | A | |
| IE6 | A | A | |
| Chrome7 | C | C | C |
| Chrome6 | A | A | A |
| Chrome3 | B | B | B |
| Firefox4 | C | C | C |
| Firefox3.6 | A | A | A |
| Firefox3.5 | C | C | |
| Firefox3 | C | C | |
| Safari | B | B | B |
| Opera | C | C | C |
(注:根据2010年11月10日数据整理)
- A级-交互和视觉完全符全设计的要求
- B级-视觉上允许有所差异,不破坏页面整体效果
- C级-可忽视视觉上的问题,但不防碍使用
2. 尽可能的通过继承和层叠重用已有样式
3. 根据新建样式的适用范围分为三级:全站级、产品级、页面级
- 3-1. 目前全站级的CSS文件仅有core.css和douban.css(向全站级CSS文件中添加规则参见4和5)。
- 3-2. 产品级CSS是指作用于某一垂直产品(如音乐、读书、电台等),文件放在css/下相应目录下。
页面级指仅在一个或少量几个页面中用到。如果仅在一个页面中用到的采用内联方式嵌入于页面head里,多于两个页面的放到外联的CSS文件中,该文件再放到相应的产品目录下。
4. core.css是全站基本样式。它需要放在所有CSS引用的最前面。它包括:标签reset、常用规则(链接、字体、隐藏、清浮动等)、布局、各种模块基本样式等
参见: http://img3.douban.com/css/packed_core1.css
5. 不要轻易改动全站级CSS。改动后,要经过全面测试
6. 单条CSS规则的书写格式要求
- 6-1. 属性需要写在一行。需要在“{“和”}”前后加空格。
.selector { property:value;property:value; }
- 6-2. 多个(>2)selector每个占一行:
.selector1,
.selector2,
.selector3 { property:value;property:value; }
- 6-3. 兼容多个浏览器时,将标准属性写在后面,如:
-webkit-border-radius:4px;-moz-border-radius:4px;border-radius: 4px;
7. 将作用于不同模块的CSS规则集中放在一起,同时用注释说明
注释的格式:
/* mod: doulist */
通用规则同样分类放在一起。通用规则在具体模块规则的前面。如:
/* button */
…
/* mod */
…
/* nav */
…
/* mod: events album */
8. ID和Class命名。命名不要用缩写,单词间用”-”做为连接符
- 8-1. ID是用来标识具体模块,命名必须具体且唯一,由前缀和名字组成。不要滥用ID。如: #db-video-list。
- 8-2. Class是用来标识某一类型的元素,命名简洁表意清楚。如:.list。
- 8-3. 命名示例:
坏:
#rec
.gray-link
.broadsmr
.pl
好:
#db-nav-main
.infobox
.item
- 8-4. 推荐使用的class名:
| 表示状态 | .on, .active, .selected, .hili |
| 表示位置 | .first, .last, .main, .side |
| 表示结构 | .hd, .bd, .ft, .col, .section |
| 通用元素 | .tb, .frm, .nav, .list, .item, .tag, .pic, .info |
9. 尽量避免使用CSS Hack
推荐使用下面的:
区别属性:
| IE6 | _property:value |
| IE6/7 | *property:value |
| IE6/7/8/9 | property:value\9 |
| 非IE6 | property//:value |
区别规则:
| IE6 | * html selector { … } |
| IE7 | *:first-child+html selector { … } |
| 非IE6 | html>body selector { … } |
| firefox only | @-moz-document url-prefix() { … } |
| saf3+/chrome1+ | @media all and (-webkit-min-device-pixel-ratio:0) { … } |
| opera only | @media all and (-webkit-min-device-pixel-ratio:10000),not all and (-webkit-min-device-pixel-ratio:0) { … } |
| iPhone/mobile webkit | @media screen and (max-device-width: 480px) { … } |
10. 使用after或overflow的方式清浮动
11. 内联和外联的CSS都必须放在页面的head里。顺序是:全站级CSS,产品级CSS,页面级(外联)CSS,页面级(内联)CSS,内联CSS
12. 避免使用低效的选择器
如:
body > * {…}
ul > li > a {…}
#footer > h3 {…}
ul#top_blue_nav {…}
#searbar span.submit a { … }
13. 尽量避免使用filter
14. 不要直接修改标签的样式
如: div { … }
15. 不要在标签上直接写样式
如:<div style=”margin-bottom:30px;”>
16. 不要在CSS中使用 expression
17. 不要在CSS中使用 @import
18. 不要在CSS中使用 !important
19. 绝对不要在CSS中使用 “*” 选择符
史蒂夫·乔布斯很懂团队建设
我偶然读到了由Rama Dev Jager 和 Rafael Ortiz 在1998年写的《 In the Company of Giants》这本书里的一段节选。他们采访苹果公司CEO 史蒂夫·乔布斯,下面的就是他关于团队建设的一些谈话:
问: 你一直在向苹果公司、NeXT公司和Pixar公司输送人才,你认为他们是什么样的人才?
史蒂夫·乔布斯: 我想我一直在寻找真正的聪明的人,与他们一起共事。我们所从事的这些重要工作中没有一项是可以由一两个人或三四个人完成的 … 为了把这些一两个人不能完成的任务做好,你必须 找到杰出的人。
这关键的总结发现是,对于生活中的大多数事情,一般的和最好的相比,一个最好的能抵两个一般的 …
但是,在我所感兴趣的这个领域 —— 最初是硬件设计 —— 我发现 一个最优秀的人完成工作的能力能抵50到100个一般水平的人。鉴于此,我们一直在追求精华之中的精华。
这就是我们所做的事情。我们建设一个团队,保证里面的成员都是A+水平。 一个都是A+水平的小团队能抵上100个都是B或C水平的巨型团队。

问: 你的所有才能归功于善于发现人才吗?
史蒂夫·乔布斯: 并不只是发现人才。在招到人才后,你要建设一个团队氛围,让 人们都感到他们周围都围绕着跟他一样有才能的人,而且工作是第一的。就要他们知道, 他们的工作成绩代表了一切,这是一个深刻的明白的认识。 —— 这就是全部。
招募人才并不是你一个人能干的了的,需要更多的帮助,所以我发现大家一起推荐、 培养出唯才是举的文化氛围才是最好的方法。
问: 然而,对于一个创业公司,管理者并不会有那么多时间花在招募人才的事情上。
史蒂夫·乔布斯: 我完全的不赞同。 我认为那是最重要的工作 … 在一个创业公司里, 最初的十个人决定了这个公司的成败与否。
史蒂夫是对的。这就是我上周 Tweeted这个的原因:
真正的IT/安全专业天才应该为创造不同而工作,而不是为了降低成本、“调整业务”、或解决其它困境工作。
我强调这一点:有志向的人 希望创造出不同。他们想要 给生活创造更好的东西。(我喜欢这句格言 — time to junk the present one, if you catch my drift, and go back!)
图片来源: 维基百科
来源: http://www.aqee.net/2011/01/18/steve-jobs-understands-team-building/
MySQL向Hive/HBase的迁移工具
Apache Hive是目前大型数据仓库的免费首选产品之一,使用Apache Hive的人是不会期望在小数据量上做什么文章,例如把MySQL中的数据搬到Hive/HBase中去,那样的话原先很快能执行完毕的SQL,估计在Hive上运行跟原来相比时间延长10倍都不止。但如果你有MySQL数据可以把大量的数据向Hive导入,如果上亿条的数据量再加上复杂的SQL查询条件对于MySQL来说是一件比较头疼的事情,此时相比而言对于Hive来说还算比较easy没有那么非常的头痛,但是两者之间缺少一个沟通的桥梁。

而然伟大的云计算公司cloudera.com也是Hadoop强力支持者推出了Sqoop,Sqoop顾名思义SQL-to-Hadoop,在sqoop中通过 ManagerFactory 抽象类对多种数据库类型进行了抽象,可以做到 Hsqldb、MySQL、Oracle、PostgreSQL 这些数据库中的数据可以向Hive中写入。

从导出/导入所有数据一条命令即可,而且可以对表和数据的筛选,开发的效率提升和配置的简洁是这个工具的特色所在,同样的机器配置、机器数量、数据量和数据内容,但是换了不同的环境得到了不同的执行效率,通过对RMDBS到Hadoop的迁移,带来了性能的提升,所以就体现了sqoop的价值。
在一次开发大会上提到的Sqoop主要功能
JDBC-based implementation
▪ Works with many popular database vendors
Auto-generation of tedious user-side code
▪ Write MapReduce applications to work with your data, faster
Integration with Hive
▪ Allows you to stay in a SQL-based environment
Extensible backend
▪ Database-specific code paths for better performance

具体操作手册相见:
http://archive.cloudera.com/cdh/3/sqoop/SqoopUserGuide.html (官方)
程序员的绩效与代码行数
一位读者写道:
我是一个软件工程师。对于任何一个从事于这个领域的人,这有一个众所周知而且毫无疑问的事实:最有效率最专业的程序员的产能会比最差的那个高上 1000倍。如果这个看起来不太可能,请记住,如果一个程序员写出了很多bug而需要其他程序员去修改,那他的生产率是负的。而且除非他造成的破坏性已经 产生后果,否则你很难发现问题所在。我过去曾给专业的程序员上课,即使在我的这些学生中,你也可以很容易的发现这种事实。
我没有发现哪个企业,不管在哪 — 即使在硅谷 –也没有一个企业能把对程序员的工资等级差异化到接近一个数量级的程度,更别说三个了。事实上,我们更倾向的做法是辞退或拒绝考虑任何超过35岁的人。给出的理由就是他们要求更多的钱。
在某种程度上讲这是有些道理的。如果你不能区分哪一个更好,你就该要那个便宜的。你实在是太难去评估一个程序员的效能了。
跟其他的人相比,一个好的程序员能用更精简的代码和更少的时间解决一个问题。所以你不能按代码行数和所花的时间来评估。按Bug数也不行 — 对于其他程序员,当看到有人漂亮的解决了一个问题后,都会确信自己也会这样的解决这类问题。不止一次的,当我按时的不带一点差错的做完一个任务后,我就会 被告知:因为那是个任务太简单。而同时,逾期未完的团队因为一周的通宵加班表现出来的敬业和苦干精神而受到嘉奖。完成了工作的优秀程序员也许并不知道他所 解决的问题对于其他的同事来说有多么的困难。
在一个公司里,你赢得了声誉,大家看到了你的工作。但你写的程序是商业机密,他们不可能轻易的让你把它们带走。不论你是好是差,打算雇你的人都看不到你的更详细的作品。他们可能会通过让你在白板上写几行代码的形式来筛选你,但这种事情就像是让钢琴师为观众用嘴哼出曲子。
我觉得印度外包产业所创造的一个辉煌成就就是放弃追求最好的程序员的思想。(我并没有侮辱印度软件工程师的意思 — 他们有很多人都很优秀,但单从数字上讲,我可以确信的说,如果他们能有像美国人那样多的机会,他们一定会从事其它行业。)人们知道,如果你能在印度雇到 20个普通的程序员,你的报价可以压的很低,即使他们花了20倍的努力完成任务,你的成本是一样的,软件的交付也是可预料的。相对于判断你招的那个程序员 究竟是高手还是低手来说,判断这20个程序员要多少时间完成任务还是更容易些。用20个普通的程序员,也许会用掉你两倍的时间。用一个程序员,也许只需要 一个普通团队花的时间的二十分之一,也许会是100倍。
[英文出处]:Paying The Experienced Hand Less, Ctd
[译文来源]:外刊IT评论
李开复:互联网的九个产品精神
1) 关注用户:要以用户痛处为契机;把用户需求放在第一位。
2) 快速迭代:经过开源、云计算、网络商店降低的开发成本,先推出 Minimally Valuable Product,每周更新,专注很少新功能,小团队,快动作,敏捷地开发验证,直到可解决用户痛处,然后才开始推广。
3) 数据导向:用互联网的特性获取用户使用轨迹,做 A-B Test 理解用户需求和选项,从中判断如何设计产品和排序功能。
4) 清晰定位:知道你的产品的核心需求和功能,两句话说清楚。清晰目标用户是谁,不能太广。用定位来挑选功能,避免功能膨胀。

5) 重视细节:产品经理必须是骨灰级玩家,对所有产品了如指掌,关注每个细节,在意每一个缺陷,把产品当作自己的 Baby。
6) 打破陈规:不要被过去的思维束缚,才能开发出有创意的产品,有时候,一个小小的修改就可能是大大有价值的创意。
7) 追求简约:不要太复杂,最简单的解决方案是最好的,要用简约的界面隐藏复杂的内涵。
8) 整合领域:大部分的创新产品既不是山寨也不是突破式,而是结合不同领域的点子,形成新产品。
9) 洞悉未来:看清楚方向可能就是成功的一半,要对产业有清晰的战略判断。
Facebook 对工程师的管理方式
Facebook 的 Yishan Wong 总结了 Facebook 的 Engineering Management 的五条心得:
1. Hiring is number one
2. Let process be implemented by those who practice it
3. Promotion from within
4. Tools are top priority
5. Technical Leaders 。
信息来源请阅读:http://algeri-wong.com/yishan/engineering-management.html
只有Facebook技术团队能做到这样吧?
这五条管理技术团队的经验简单说就是:
1、招聘很紧急,而且要找牛人
2、流程由实践者确立
3、内部晋升,不找空降的管理人员
4、开发工具代替人力低效劳动
5、技术领导,不要外行指挥内行。
国内99%的公司都是反着来的 ![]()
1、国内大一点的技术公司招聘都是抽风式招聘: 要么说没有 HeadCount(人员编制)了,要么以数量为目标,完成招聘人头即选宣告胜利...
2、很多公司的做法是成立一个具备行政性质的流程制定小组... 骆驼是委员会设计的马
3、这一点不用多说了,越忙越不给你晋升的机会,直接找个空降兵(很可能是关系户哦)管理你整个团队,尽管这个人半年也不能真正了解公司的业务
4、更好的工具,可以有效减少人工的投入。Facebok 内部开发的大量的高效的工具。作者列举了针对客服需求开发的工具取得的回报,以及数据库管理上开发工具取得的回报-一个人管理过上千个节点的DB。
5、技术领导要具备足够的技能,如果迫不得已用外来和尚的话,人要足够牛才行,起码代码经验、项目经验要能盖过团队现有的人。而不是一些没有没什么工程师经验,只有管理经验的人。
布隆过滤器(Bloom Filter)之java实例
在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中);在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是费存储空间。当集合比较小时,这个问题不显著,但是当集合巨大时,哈希表存储效率低的问题就显现出来了。比如说,一个象 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。如果用哈希表,每存储一亿个 email 地址, 就需要 1.6GB 的内存(用哈希表实现的具体办法是将每一个 email 地址对应成一个八字节的信息指纹 googlechinablog.com/2006/08/blog-post.html,然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email 地址需要占用十六个字节。一亿个地址大约要 1.6GB, 即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB 的内存。除非是超级计算机,一般服务器是无法存储的。
今天,我们介绍一种称作布隆过滤器的数学工具,它只需要哈希表 1/8 到 1/4 的大小就能解决同样的问题。
布隆过滤器是由巴顿.布隆于一九七零年提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。我们通过上面的例子来说明起工作原理。
假定我们存储一亿个电子邮件地址,我们先建立一个十六亿二进制(比特),即两亿字节的向量,然后将这十六亿个二进制全部设置为零。对于每一个电子邮件地址 X,我们用八个不同的随机数产生器(F1,F2, ...,F8) 产生八个信息指纹(f1, f2, ..., f8)。再用一个随机数产生器 G 把这八个信息指纹映射到 1 到十六亿中的八个自然数 g1, g2, ...,g8。现在我们把这八个位置的二进制全部设置为一。当我们对这一亿个 email 地址都进行这样的处理后。一个针对这些 email 地址的布隆过滤器就建成了。(见下图)

现在,让我们看看如何用布隆过滤器来检测一个可疑的电子邮件地址 Y 是否在黑名单中。我们用相同的八个随机数产生器(F1, F2, ..., F8)对这个地址产生八个信息指纹 s1,s2,...,s8,然后将这八个指纹对应到布隆过滤器的八个二进制位,分别是 t1,t2,...,t8。如果 Y 在黑名单中,显然,t1,t2,..,t8 对应的八个二进制一定是一。这样在遇到任何在黑名单中的电子邮件地址,我们都能准确地发现。
布隆过滤器决不会漏掉任何一个在黑名单中的可疑地址。但是,它有一条不足之处。也就是它有极小的可能将一个不在黑名单中的电子邮件地址判定为在黑名单中,因为有可能某个好的邮件地址正巧对应个八个都被设置成一的二进制位。好在这种可能性很小。我们把它称为误识概率。在上面的例子中,误识概率在万分之一以下。
布隆过滤器的好处在于快速,省空间。但是有一定的误识别率。常见的补救办法是在建立一个小的白名单,存储那些可能别误判的邮件地址。
Java使用BitSet做大数据量查重复
That's what I look for a long time. May used in Bloom Filter.
public static void main(String[] args) throws ParseException {
BitSet bit = new BitSet (100);
bit.set(1);
bit.set(10);
BitSet anBit = new BitSet();
anBit.set(10);
anBit.set(5);
//bit.and(anBit);
bit.or(anBit);
for(int i=0; i<bit.length(); i++)
{
System.out.println(bit.get(i));
}
}results:
false
true
false
false
false
true
false
false
false
false
true
改良程序的11技巧
有很多理由都能说明为什么我们应该写出清晰、可读性好的程序。最重要的一点,程序你只写一次,但以后会无数次的阅读。当你第二天回头来看你的代码时,你就要开始阅读它了。当你把代码拿给其他人看时,他必须阅读你的代码。因此,在编写时多花一点时间,你会在阅读它时节省大量的时间。
让我们看一些基本的编程技巧:
- 尽量保持方法简短
- 永远永远不要把同一个变量用于多个不同的目的
- 使用自描述的变量名和方法名
- 尽可能的把变量定义在靠近使用它的地方
- 拒绝神秘数字
- 友好的对待你的语言
- 不要逆常规而行
- 警惕过早优化
- 积极重构测试过的程序
- 不要过度沉迷于技巧
- 通过习例学习新知
海量数据处理常用思路和方法
大数据量的问题是很多面试笔试中经常出现的问题,比如 google、淘宝、百度、 腾讯 这样的一些涉及到海量数据的公司经常会问到。
下面的方法是我对海量数据的处理方法进行了一个一般性的总结,当然这些方法可能并不能完全覆盖所有的问题,但是这样的一些方法也基本可以处理绝大多数遇到的问题。下面的一些问题基本直接来源于公司的面试笔试题目,方法不一定最优,如果你有更好的处理方法,欢迎与我讨论。
1.Bloom filter
适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集
基本原理及要点:
对于原理来说很简单,位数组+k个独立hash函数。将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。
还有一个比较重要的问题,如何根据输入元素个数n,确定位数组m的大小及hash函数个数。当hash函数个数k=(ln2)*(m/n)时错误率最小。在错误率不大于E的情况下,m至少要等于n*lg(1/E)才能表示任意n个元素的集合。但m还应该更大些,因为还要保证bit数组里至少一半为0,则m应该>=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2为底的对数)。
举个例子我们假设错误率为0.01,则此时m应大概是n的13倍。这样k大概是8个。
注意这里m与n的单位不同,m是bit为单位,而n则是以元素个数为单位(准确的说是不同元素的个数)。通常单个元素的长度都是有很多bit的。所以使用bloom filter内存上通常都是节省的。
扩展:
Bloom filter将集合中的元素映射到位数组中,用k(k为哈希函数个数)个映射位是否全1表示元素在不在这个集合中。Counting bloom filter(CBF)将位数组中的每一位扩展为一个counter,从而支持了元素的删除操作。Spectral Bloom Filter(SBF)将其与集合元素的出现次数关联。SBF采用counter中的最小值来近似表示元素的出现频率。
问题实例:给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?
根据这个问题我们来计算下内存的占用,4G=2^32大概是40亿*8大概是340亿,n=50亿,如果按出错率0.01算需要的大概是650亿个bit。现在可用的是340亿,相差并不多,这样可能会使出错率上升些。另外如果这些urlip是一一对应的,就可以转换成ip,则大大简单了。
2.Hashing
适用范围:快速查找,删除的基本数据结构,通常需要总数据量可以放入内存
基本原理及要点:
hash函数选择,针对字符串,整数,排列,具体相应的hash方法。
碰撞处理,一种是open hashing,也称为拉链法;另一种就是closed hashing,也称开地址法,opened addressing。
扩展:
d-left hashing中的d是多个的意思,我们先简化这个问题,看一看2-left hashing。2-left hashing指的是将一个哈希表分成长度相等的两半,分别叫做T1和T2,给T1和T2分别配备一个哈希函数,h1和h2。在存储一个新的key时,同时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]。这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,哪一个位置已经存储的(有碰撞的)key比较多,然后将新key存储在负载少的位置。如果两边一样多,比如两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1子表中,2-left也由此而来。在查找一个key时,必须进行两次hash,同时查找两个位置。
问题实例:
1).海量日志数据,提取出某日访问百度次数最多的那个IP。
IP的数目还是有限的,最多2^32个,所以可以考虑使用hash将ip直接存入内存,然后进行统计。
3.bit-map
适用范围:可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下
基本原理及要点:使用bit数组来表示某些元素是否存在,比如8位电话号码
扩展:bloom filter可以看做是对bit-map的扩展
问题实例:
1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。
2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上。或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map。
4.堆
适用范围:海量数据前n大,并且n比较小,堆可以放入内存
基本原理及要点:最大堆求前n小,最小堆求前n大。方法,比如求前n小,我们比较当前元素与最大堆里的最大元素,如果它小于最大元素,则应该替换那个最大元素。这样最后得到的n个元素就是最小的n个。适合大数据量,求前n小,n的大小比较小的情况,这样可以扫描一遍即可得到所有的前n元素,效率很高。
扩展:双堆,一个最大堆与一个最小堆结合,可以用来维护中位数。
问题实例:
1)100w个数中找最大的前100个数。
用一个100个元素大小的最小堆即可。
5.双层桶划分 —-其实本质上就是【 分而治之】的思想,重在“分”的技巧上!
适用范围:第k大,中位数,不重复或重复的数字
基本原理及要点:因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行。可以通过多次缩小,双层只是一个例子。
扩展:
问题实例:
1).2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
有点像鸽巢原理,整数个数为2^32,也就是,我们可以将这2^32个数,划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域,然后不同的区域在利用bitmap就可以直接解决了。也就是说只要有足够的磁盘空间,就可以很方便的解决。
2).5亿个int找它们的中位数。
这个例子比上面那个更明显。首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数。然后第二次扫描我们只统计落在这个区域中的那些数就可以了。
实际上,如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度。即可以先将int64分成2^24个区域,然后确定区域的第几大数,在将该区域分成2^20个子区域,然后确定是子区域的第几大数,然后子区域里的数的个数只有2^20,就可以直接利用direct addr table进行统计了。
6.数据库索引
适用范围:大数据量的增删改查
基本原理及要点:利用数据的设计实现方法,对海量数据的增删改查进行处理。
扩展:
问题实例:
7.倒排索引(Inverted index)
适用范围:搜索引擎,关键字查询
基本原理及要点:为何叫倒排索引?一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。
以英文为例,下面是要被索引的文本:
T0 = “it is what it is”
T1 = “what is it”
T2 = “it is a banana”
我们就能得到下面的反向文件索引:
“a”: {2}
“banana”: {2}
“is”: {0, 1, 2}
“it”: {0, 1, 2}
“what”: {0, 1}
检索的条件”what”, “is” 和 “it” 将对应集合的交集。
正向索引开发出来用来存储每个文档的单词的列表。正向索引的查询往往满足每个文档有序频繁的全文查询和每个单词在校验文档中的验证这样的查询。在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。也就是说文档指向了它包含的那些单词,而反向索引则是单词指向了包含它的文档,很容易看到这个反向的关系。
扩展:
问题实例:文档检索系统,查询那些文件包含了某单词,比如常见的学术论文的关键字搜索。
8.外排序
适用范围:大数据的排序,去重
基本原理及要点:外排序的归并方法,置换选择 败者树原理,最优归并树
扩展:
问题实例:
1).有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M。返回频数最高的100个词。
这个数据具有很明显的特点,词的大小为16个字节,但是内存只有1m做hash有些不够,所以可以用来排序。内存可以当输入缓冲区使用。
9.trie树
适用范围:数据量大,重复多,但是数据种类小可以放入内存
基本原理及要点:实现方式,节点孩子的表示方式
扩展:压缩实现。
问题实例:
1).有10个文件,每个文件1G, 每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。要你按照query的频度排序 。
2).1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串。请问怎么设计和实现?
3).寻找热门查询:查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个,每个不超过255字节。
10.分布式处理 mapreduce
适用范围:数据量大,但是数据种类小可以放入内存
基本原理及要点:将数据交给不同的机器去处理,数据划分,结果归约。
扩展:
问题实例:
1).The canonical example application of MapReduce is a process to count the appearances of
each different word in a set of documents:
void map(String name, String document):
// name: document name
// document: document contents
for each word w in document:
EmitIntermediate(w, 1);
void reduce(String word, Iterator partialCounts):
// key: a word
// values: a list of aggregated partial counts
int result = 0;
for each v in partialCounts:
result += ParseInt(v);
Emit(result);
Here, each document is split in words, and each word is counted initially with a “1″ value by
the Map function, using the word as the result key. The framework puts together all the pairs
with the same key and feeds them to the same call to Reduce, thus this function just needs to
sum all of its input values to find the total appearances of that word.
2).海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。
3).一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到N^2个数的中数(median)?
经典问题分析
上千万or亿数据(有重复),统计其中出现次数最多的前N个数据,分两种情况:可一次读入内存,不可一次读入。
可用思路:trie树+堆,数据库索引,划分子集分别统计,hash,分布式计算,近似统计,外排序
所谓的是否能一次读入内存,实际上应该指去除重复后的数据量。如果去重后数据可以放入内存,我们可以为数据建立字典,比如通过 map,hashmap,trie,然后直接进行统计即可。当然在更新每条数据的出现次数的时候,我们可以利用一个堆来维护出现次数最多的前N个数据,当然这样导致维护次数增加,不如完全统计后在求前N大效率高。
如果数据无法放入内存。一方面我们可以考虑上面的字典方法能否被改进以适应这种情形,可以做的改变就是将字典存放到硬盘上,而不是内存,这可以参考数据库的存储方法。
当然还有更好的方法,就是可以采用分布式计算,基本上就是map-reduce过程,首先可以根据数据值或者把数据hash(md5)后的值,将数据按照范围划分到不同的机子,最好可以让数据划分后可以一次读入内存,这样不同的机子负责处理各种的数值范围,实际上就是map。得到结果后,各个机子只需拿出各自的出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是reduce过程。
实际上可能想直接将数据均分到不同的机子上进行处理,这样是无法得到正确的解的。因为一个数据可能被均分到不同的机子上,而另一个则可能完全聚集到一个机子上,同时还可能存在具有相同数目的数据。比如我们要找出现次数最多的前100个,我们将1000万的数据分布到10台机器上,找到每台出现次数最多的前 100个,归并之后这样不能保证找到真正的第100个,因为比如出现次数最多的第100个可能有1万个,但是它被分到了10台机子,这样在每台上只有1千个,假设这些机子排名在1000个之前的那些都是单独分布在一台机子上的,比如有1001个,这样本来具有1万个的这个就会被淘汰,即使我们让每台机子选出出现次数最多的1000个再归并,仍然会出错,因为可能存在大量个数为1001个的发生聚集。因此不能将数据随便均分到不同机子上,而是要根据hash 后的值将它们映射到不同的机子上处理,让不同的机器处理一个数值范围。
而外排序的方法会消耗大量的IO,效率不会很高。而上面的分布式方法,也可以用于单机版本,也就是将总的数据根据值的范围,划分成多个不同的子文件,然后逐个处理。处理完毕之后再对这些单词的及其出现频率进行一个归并。实际上就可以利用一个外排序的归并过程。
另外还可以考虑近似计算,也就是我们可以通过结合自然语言属性,只将那些真正实际中出现最多的那些词作为一个字典,使得这个规模可以放入内存。
转载请注明出处:http://bbs.xjtu.edu.cn
作者phylips@bmy
参考文献:
http://blog.csdn.net/jiaomeng/archive/2007/03/08/1523940.aspx d-Left Hashing
http://blog.csdn.net/jiaomeng/archive/2007/01/27/1495500.aspx
http://en.wikipedia.org/wiki/Bloom_filter
http://hi.baidu.com/xdzhang_china/blog/item/2847777e83fb020229388a15.html 应用Bloom Filter的几个小技巧
http://zh.wikipedia.org/wiki/%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95
通过XMLHTTP加载HTML文件
<html>
<head>
<script type="text/javascript">
var xmlhttp;
function loadXMLDoc(url){
xmlhttp = null;
// code for Firefox, Opera, IE7, etc.
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
else {
// code for IE6, IE5
if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
if (xmlhttp != null) {
xmlhttp.onreadystatechange = state_Change;
xmlhttp.open("GET", url, true);
xmlhttp.send(null);
}
else {
alert("Your browser does not support XMLHTTP.");
}
}
function state_Change(){
// 4 = "loaded"
if (xmlhttp.readyState == 4) {
// 200 = "OK"
if (xmlhttp.status == 200) {
document.getElementById('T1').innerHTML = xmlhttp.responseText;
}
else {
alert("Problem retrieving data:" + xmlhttp.statusText);
}
}
}
</script>
</head>
<body onload="loadXMLDoc('test.html')">
<div id="T1" style="border:1px solid black;height:40;width:300;padding:5">
</div>
<br/>
<button onclick="loadXMLDoc('test.html')">
Click
</button>
</body>
</html>
程序员对索引的误解
1、索引中最常见的就是B树索引,B树索引的实现与二叉查找树相似,但是B的意思不是binary,而是balance(平衡)。
2、B树索引上的每个结点都是一个块,有叶子块和分支块之分。块中的数据包括各个索引以及一个rowid。走索引查询时,会按照树的分支将需要查询数据路径上的相应的分支块和叶子块读到内存。
3、B树索引不存在非唯一性条目,在一个非唯一性索引中,Oracle会把rowid作为一个额外的列追加到键上,使得键唯一。非唯一性索引,会先按索引键值排序,然后按rowid升序排序。
4、B树索引时高度平衡的,大多数情况,B树索引的高度都是2或3,即使索引数百万行记录也是如此。这说明,一般情况,在索引中找到键值只需2到3次IO(2到3个数据块)。
5、通常有两种使用索引的方法:
(1)索引用于访问表中的行,访问表中很少一部分行。
这是DBA给的经验值:
小表(记录数小于10000行的表):筛选比例<10%;
大表:(筛选返回记录数)<(表总记录数*单条记录长度)/10000/16
单条记录长度≈字段平均内容长度之和+字段数*2
(2)索引用于回答一个查询,索引中的信息足够回答整个查询。
6、不能视图加索引,视图只是存储了一种查询,具体的访问还是要访问基表。
7、B树索引不会存储null的条目。
8、外键上要加索引,否则容易造成死锁。
9、组合索引,查询时只使用单一或没有加上全部的索引条件的查询。
10、count查询默认是会走B树索引的,但是如果索引建在可能为null的字段上,则不会走索引查询(因为B树索引不能允许null,使用索引查出的count和使用表查询出来的count可能1出现不一致情况)。
11、索引列上使用函数或使用数据类型转化函数,将会不走索引查询。
12、优化器拒绝使用索引查询,优化器绝大多数时候是英明的,不要贸然强制使用索引。
13、没有将最有差别的列放在索引最前面会使索引更小或更有效率的说法,实际上,如果使用索引键压缩,情况恰恰相反(即把没有差别的列放在索引最前面最优的做法)。
基于MongoDB GridFS的图片存储
商品图片,平均200-500K,说大不大,说小不小,但量大且细碎,最早通过页面上传,全部保存在文件里,且不分目录,管理和索引都很慢,几乎无法备份,读取也很慢。
改进方案由 大鱼设计,图片是保存在MySQL表里,每10万张图就换一张新表,操作语言是PHP,它解决了图片备份和缓存的问题。
经过一段运行时间后,我对效果并不满意,主要是速度还是有些慢,尤其是第一次加载的过程。这期间又负责主体商品数据迁移到MongoDB,大致研究了一下GridFS,并做了些测试,感觉这个比MySQL要靠谱,且 MongoDB还有 Sharding和 Replica Set支持,帮我解决了分布存储的问题,很是诱人,决定一试。
设计思路和需求整理:
- 决不允许重复图片存在
- 文件只有原始的需要保留,其他各尺寸和效果都可以由原图生成
- 图片URL总是固定的,不管它出现在哪里
- 缩略图生成规则也简单的体现在URL里,参考 Abusing Amazon images
- 第一次请求,由图片处理程序生成静态文件,以后请求即直接定位到静态文件
多年前, 郝培强同学送过我一本《Python语言入门》,但这么些年里只写过三两个小工具,按熟悉程度依然算新手。既然这个解决方案撞枪口上了,于是有改用Python实现的念头。我是这么想的,1.要简单;2.要快,不光运行快,还要写得快;3.最好是常驻型程序。貌似只有Python和Ruby符合,但Ruby我不熟悉,那就用Python好了。某个周六,花了一天时间,写了个雏形,支持图片读取并显示。又花了N天,添加管理和上传。
上传到 github,命名为 ImSto,将它作为我的第一个开源项目。
关于Python的Web框架,不得不絮叨几句,那真叫一个多,看了一些评论,也粗略研究了Pylons和CherryPy,很久以前学过一点点Django。但感觉似乎都有些复杂,这个需求也不复杂,决定干脆不用任何框架,自己动手丰衣足食算了。
所用到的组件:
- MongoDB (GridFS): 这个不用说了,核心存储,初期环境用到了三台主机组成的 Replica Sets
- Nginx: 解析URL并定位到静态文件,如果未生成则转向uWSGI程序去生成图片
- Python + pymongo
- ImageMagick: 用来生成图片缩略图,有两种调用方式:Api和命令行shell。 Demo环境由于内存使用限制,用了shell方式。
- uWSGI: 是用纯C写的WSGI服务器,支持多种语言(主要是Python)和Web Server(官方首推Cherokee和Nginx)。有点儿类似PHP-FPM。
项目地址: https://github.com/liut/imsto 只是完成了 TODO里的部分核心功能,代码结构也比较粗糙,待逐步重构。
演示地址: http://demo.imsto.org:81/ 稍后添加权限认证,所以,在这之前请随便添随便删,不要客气。
Update:
1. 上传的页面使用了 html5的一些特性,为了实现Ajax上传和进度显示,以及即时预览,暂时只支持Firefox3.5+。稍后再改进。
2. 请不要向我推荐用 Flash,谢谢。
