博客许久没更新了,上一篇还是在4年多前,之后没有更新,但网站空间、域名还在续费,为了还是之前写的几篇文章还是有一定阅读量的,关掉可惜了,能帮助一人是一人。
这几年来对我来说变化挺大的,先起个开头,后面陆续在此篇更新。。。
博客许久没更新了,上一篇还是在4年多前,之后没有更新,但网站空间、域名还在续费,为了还是之前写的几篇文章还是有一定阅读量的,关掉可惜了,能帮助一人是一人。
这几年来对我来说变化挺大的,先起个开头,后面陆续在此篇更新。。。
今年比以往不同的是,看了比较多的书,当然都是浅看,不是工具书我一般都不来回看,看完一遍就在脑子里过一下,也不强求能学到什么知识,只要能从在学到一些新的体会就达到看书的目的了。没错,看书还是要带着目的看的。
大多数书都是用微信读书看的,纸质的也有,但比较少,因为手机在任何地方任何地点都能看,压榨空隙时间第一选择。微信读书的体验还是很不错的,功能简单,而且可以通过读书时长获取读书币,然后读书币可以买更多的书,良性循环。
接下来介绍下每本我看过的书和一些心得体会吧。
NO1. 《刻意练习》 ⭐️⭐️⭐️⭐️⭐️
这本书是讲一些高效的学习方法的,就是想要高效的学习并深入,除了努力之外,我们还需要带着明确的目标,配套合适的方法,才能做到短时间内领悟更多。 这本书还有一个观点是,天才并不存在的,很多我们看到的天才,只不过是他们在很小的时候,通过《刻意练习》的方法,让自己在某一领域深入掌握各种技巧而已。当然,刻意练习并不简单,需要考验你的耐心,方法以及能指导你的老师(高人)。3F法则(Focus、Feedback、Fix)已成为我的座右铭:)。总之,这本书比《一万小时理论》更值得一看
NO2. 《腾讯传》 ⭐️⭐️⭐️⭐️
这本书就是讲腾讯的发家史,当然写的看似波澜不惊,但是每一段波澜不惊的创业背后都是创业者苦逼的持久战。创始人对公司文化的影响极其深远,创业除了碰运气,还要在浪潮来之前准备好姿势(这需要比常人多十倍、百倍的努力),踏浪而上,成为弄潮儿。另外一点是创业者需要不断的居安思危,不能看到眼前有收入可以养活团队就能『躺着赚钱』了,灾难来临的时候从来不会告诉你,你跟其他公司的区别就是能不能顺利度过去。
NO3. 《资本的逻辑》 ⭐️⭐️⭐️
这本书不建议看电子版,因为电子版制作很粗糙,很多数据图表都没有了,而且小数点也没有,所以会经常看到一些奇怪的巨额数字,需要自己去脑补实际的金额。最重要的一句话:企业的估值是向前看,而不是向后看。 估值就是对未来的期望值。
NO4. 《未来简史》 ⭐️⭐️⭐️⭐️⭐️
这本书可以和《奇点临近》一起看,预测在不远的未来,随着超级人工智能降世,人来的价值观会发生什么样的变化。从科学的角度讲,人类本身没什么意义,都是一堆算法和随机过程的组合而已,人类的这几百万年的进化,极有可能是为了唤醒地球上的超级人工智能生命体而已(远古的天外文明埋下的种子,哈哈)。未来,如果人类和超级人工智能结合之后,不再死亡,也不再有肉体,那么我们现在的宗教里面天堂、地狱、神都不再有意义,那么,这些有神论者,该如何自处?
NO5. 《即将到来的场景时代》 ⭐️⭐️⭐️⭐️
传感器 + 物联网 组成的雾计算,以及以云计算为基础的ai技术,通过整合人类的各种数据,做到书中描述的场景时代并不难,难的是道德上的变革:隐私数据该不该共享?数据的所有权、隐私问题、网络安全问题,这几大问题没有好好的解决,还是很难铺开。当然,可以接受的是,新技术的变革和流行,反过来推动这些问题的解决(或者更大的利益来抵消损失的权益)
NO6. 《人民的名义》 ⭐️⭐️⭐️⭐️
这个不多说,一切为了剧透。 这本书(或者这部剧)名义上是以『人民的名义』写反腐,实际上是以反腐的名义写当代中国官场现形记。 主角和他的盆友们都是官二代、红二代,很容易形成抱团取暖、勾心斗角。
NO7. 《奇点临近》 ⭐️⭐️⭐️⭐️⭐️
技术正在加速进化,我们处在变化中,如果看的时间不够长,会以为是比较常规的线性变化,而把时间轴拉长,会发现我们正处于技术变革的指数曲线上。我们正在走向变革的奇点。如果不抓住机会乘势而上,那我们之于即将到来的超级人工智能就像尼安特人至于现代智人一样,将会被灭族!不过作者对强人工智能总体上是比较乐观的,认为生物智能(比如人类)和非生物智能可以完美结合,和谐相处,合体之后变成全新的超人类(感觉在看龙珠Z)。技术的进步总会带来一些毁灭,但阻止技术进步并不能阻止毁灭的降临,所以我们应该更加加大力度发展技术,让毁灭来临之前,我们人类的技术可以与之抗衡。
计算机终将拥有意识,到时候人类不小心把一个计算机关机了并摧毁了,会不会犯了杀计算机(or机器人)罪?
作者的一些预测观点已经发生或者正在发生,再过20年,人类之上,会不会有新的物种出现?拭目以待吧。
NO8. 《增长黑客》 ⭐️⭐️⭐️⭐️
这本书内容还可以,就是文法比较散漫,语法比较随意,对于刚接触互联网的新人(尤其是上了年纪的),不会容易理解。增长黑客的主要工作就是用各种『黑客手段』(超出常人一般的做法,不是指真的黑客)驱动公司的业务增长。主要从运营的角度,从获客、到转化、活跃、留存、老带新等阶段,做与之相匹配的操作,驱动各个阶段的增长。
NO9. 《格鲁夫给经理人的第一课》 ⭐️⭐️⭐️⭐️⭐️
这本书字字珠玑,对刚接触管理工作的人,会很有帮助。不多说了,自己看吧。
NO10. 《你一定爱读的极简欧洲史》 ⭐️⭐️⭐️⭐️
今年流行各种《极简xx》,这本书对于历史经常考试不及格的我还是很有启发,简洁明了。总结起来,整个欧洲的历史和罗马文明分不开,虽然罗马早就亡国了,但他的影响,一直留存到现在,这就是文明的力量。
NO11. 《周鸿祎自述:我的互联网方法论》⭐️⭐️⭐️⭐️
这本书主要讲解周鸿祎的互联网思维,每个互联网大佬都有自己互联网思维。雷军的互联网思维就是用效率升级改造产业链,产出物美价廉的高性价比产品打击存量市场,把用户留住,然后在用后续的其他手段获利。周鸿祎也差不多,只不过在软件层,而不是硬件层这么做。互联网思维的核心点除了免费(或高性价比)之外,其实是要做到以用户为中心,以产品价值为核心,才能最终留住客户,你才能做后面的事情。
NO12. 《自私的基因》 ⭐️⭐️⭐️⭐️⭐️
这本书看书名容易产生误解,以为基因本身有思考能力,否则他怎么做到『自私』?其实不然,基因就是一种复制子,他的使命是更长久的(复制自己并)存活下去。基因的自私不代表人性的自私。基因创造我们的身体和大脑,但他并不能直接控制我们,而是通过代码片段,对我们造成影响。人类的文明史就是基因和幂母(MEME)的斗争史。幂母也是一种基因,他也是一种复制子,也有『自私』的特性。以后产生的超级人工智能,可能就是幂母的一个后代而已。 机器人要变成『人』,一定要突破如何繁衍下一代的关卡,比如纳米机器人很可能就是下一代地球的霸主。
NO13. 《极简宇宙史》 ⭐️⭐️⭐️⭐️
非常Exciting的一本书,一场非常有趣的思想实验。从万有引力到相对论到量子力学再到弦理论,每种很牛逼的理论都有他们的适用范围,接下来科学家都在搞的就是一个大而统一的理论,可以把这些所有的理论都兼并起来,解决他们之间的冲突。这一切只是时间的问题,而这个时间,对浩瀚的宇宙来说,不过是一瞬间的事。
NO14. 《大国大城:当代中国的统一、发展与平衡》 ⭐️⭐️⭐️⭐️
看完这部书,我为中国感到前途光明。因为我们的城市化是这么的畸形,而这些畸形都是内部的问题造成的,不是别人束缚我们,所以我们只要愿意改革,再上进几个层次都不是问题。从大局的眼光看,当前的既得利益,都一文不值。其实我们生活在城市里,有城市户口,对大多数农民工工来说,我们都是既得利益者。最近北京搞的一些脑残的清理xxx的运动,简直就是逆城市化行为,历史的车轮的滚滚向前的,这些拍脑袋的人阻止不了我们伟大的时代到来。
看的书比较杂,这里技术类的不列出来,技术类的书我建议还是看纸质的,图文并茂更容易理解。关键要带着目的去看,然后看的同时一定要多动手,举一反三,才能更快掌握技术类的新知识。 当然不是所有书都要带着功利的目的去看的,比如《人民的名义》这种就是为了娱乐放松下。
读书容易给人一个感觉,就是读的书越多,会越觉得自己无知。但没必要觉得气馁,你get到了,你就比别人进步了。
《长尾理论》
这本书刚开始看,没看完,2018年看完。
《小米生态链战地笔记》
这本书看了几章,都比较接地气,同时我目前也正在做物联网相关的工作,然而小米在4年前就开始布局了,然后现在去他们的米家商城看,哇塞,除了手机,其他都做的很不错,特别他们的只能家居这块,米家app里面居然可以编程,不得不佩服雷军的眼光。
《创新者的xx三步曲》
周鸿祎教主推荐的,在他的方法论那本书里,今年买的还没看多少,2018看完。
《TensorFlow 系列》
了解下当前ai都在干啥,希望在超级人工智能这个新物种降临之前,能知道他们是怎么生存的。
好久没动笔写博客了,为了不给2017留空白,特写此篇。 这篇文章提到的案例、方法、方案有些是在之前的文章中会零散提到过,都是在公司内部分享多次以及迭代多次的结果,当然,敏感数据的地方我会换成一些fake的数据,注重方法论,数据上有问题的地方请包容。
这个话题其实可以涉及到各种各样的场景,我这里仅仅是把我们公司内部经常遇到的一些问题拉出来分享。本文的案例基本上都基于linux环境的java程序进行分析,如果你是windows或者其他的操作系统,可能有些工具需要自己寻找、下载。
一般两种问题:
– a. 业务代码中有大(或死)循环,消耗大量CPU计算资源
– b. 垃圾回收(GC)频繁,导致gc线程消耗大量CPU (FullGC、YoungGC均会消耗CPU资源)
至此,定位到的代码段一般就是耗CPU的代码段了。
很简单,通过jstack工具,他会直接告诉你死锁在哪。
1. 找出进程ID
2. jstack -l dump出线程栈
3. 通过 deadlock 关键字找到相关线程信息即可
首先,要先知道有哪些OOM,每种OOM都是有什么问题引起的。
本文来自 https://github.com/FallibleInc/security-guide-for-developers ,这是一篇web开发人员的安全须知(指南),是由 FallibleInc 公司提炼出来的,发布在github上面,我自己把的相关文档翻译成简体中文,他们也对我比较信任,给我开了这个repo的写权限,如果你发现有翻译得不正确的地方,欢迎提交PR, 我这边有权限可以合并。
安全问题主要由以下两类原因导致:
我们的详细说明应该可以帮到第1类开发者,而我们希望的我们的checklist可以帮到第2类的开发者构建更多安全的系统。这绝不是一个综合性的指南,仅仅是覆盖了大多数我们过去发现的比较常见的问题。
我们是全栈开发工程师,讨厌看到那些所谓为了做某件事情而hack,但写了一堆不安全的代码的开发者。在过去六个月,我们保护了超过1500w信用卡信息以及超过4500w用户的个人信息以及被盗,以及防止了大量的公司倒闭。最近,我们发现一个安全问题就能使一家比特币交易公司数据泄露从而倒闭。我们以及帮助了若干创业公司让他们的系统更安全,大多数是免费的,有时候甚至连『谢谢』都没收到:)
如果你不同意我们的观点或者找到bug,请开启一个issue或者提交一个PR给我们。另外,你也可以通过 hello@fallible.co 与我们交流。
Bcrypt
存储密码哈希 (没有使用盐的必要 – Bcrypt
干的就是加密这个事情).登出
之后销毁会话ID .state
参数.OTP
验证时,当调用generate OTP
或者 Resend OTP
API时不能吧OTP(One Time Password)直接返回。(一般是通过发送手机验证短信,邮箱随机code等方式,而不是直接response)Login
、Verify OTP
、 Resend OTP
、generate OTP
等API的调用次数,使用Captcha等手段防止暴力破解.我的购物车
、我的浏览历史
之类的资源访问,必须检查当前登录的用户是否有这些资源的访问权限。/me/orders
代替 /user/37153/orders
以防你忘了检查权限,导致数据泄露。修改邮箱/手机号码
功能必须首先确认用户已经验证过邮箱/手机是他自己的。个人头像上传
功能应该过滤所有的 EXIF
标签,即便没有这个需求.UUID
而不是整数. 你可以从github找到你所用的语言的实现.盐(salt)
不应该硬编码secret
和 auth token
不应该硬编码添加
CSP 头信息,减缓 XSS 和数据注入攻击. 这很重要.添加
CSRF 头信息防止跨站请求调用(CSRF)攻击.同时添加
SameSite 属性到cookie里面.添加
HSTS 头信息防止SSL stripping攻击.添加
你的域名到 HSTS 预加载列表添加
X-Frame-Options 防止点击劫持.添加
X-XSS-Protection 缓解XSS攻击.更新
DNS记录,增加 SPF 记录防止垃圾邮件和钓鱼攻击.添加
内部资源集成检查 。为了更加安全,添加require-sri-forCSP-directive 就不会加载到没有SRI的资源。过滤
防止 XSS 攻击.CSV导入
过滤
一些特殊的用户输入,例如将robots.txt作为用户名,而你刚好提供了 coolcorp.io/username 之类的url来提供用户信息访问页面。(此时变成 coolcorp.io/robots.txt,可能无法正常工作)过滤
那些有点像URL的输入,防止 SSRF 攻击过滤
,防止XSS攻击.端口
目前为止,Hackerone平台已经发现1731个公开的漏洞,主要来自 Twitter、Uber、Dropbox、Github 等公司。其中8个已经删除,9个来自互联网或者特定的语言,剩下的1714个中,有1359个我们可以通过代码或者人工的方式进行分类。
类型 | 数量 | 占比 |
---|---|---|
用户输入过滤 | 481 | 27.8 |
其他代码问题 | 549 | 31.7 |
配置问题 | 325 | 18.8 |
无法归类+信息+垃圾 | 376 | 21.7 |
其中1/3的问题与XSS、不安全的数据引用 (数据泄露) 或者忘记设置 CSRF token有关,这个 页面 列举的这些问题非常有趣,值得一读.
类型 | 数量 | 占比 |
---|---|---|
XSS | 375 | 21.87 |
非安全引用 + 数据泄露 | 104 | 6.06 |
CSRF Token | 99 | 5.77 |
开放重定向 | 59 | 3.44 |
信息/源代码泄露 | 57 | 3.32 |
DNS 配置错误 + Apache/Nginx + 子域名接管 + Open AWS_S3 | 44 | 2.56 |
不正确的session管理/固定 | 39 | 2.27 |
TLS/SSL/POODLE/Heartbleed | 39 | 2.27 |
HTML/JS/XXE/内容注入 | 37 | 2.15 |
HTTP 头信息问题 | 34 | 1.98 |
空指针 + 段错误 + 在free()之后使用内存 | 33 | 1.92 |
DMARC/DKIM/邮件SPF设置 | 31 | 1.8 |
SQL 注入 | 28 | 1.63 |
点击劫持 | 27 | 1.57 |
不正确的cookie使用 (secure/httpOnly/暴露) | 25 | 1.45 |
路径暴露 | 25 | 1.45 |
开放权限 | 24 | 1.4 |
暴力破解 | 24 | 1.4 |
内容欺诈 | 20 | 1.16 |
缓冲区溢出 | 20 | 1.16 |
拒绝服务 | 19 | 1.1 |
服务端请求伪造 | 18 | 1.05 |
Adobe Flash 漏洞 | 18 | 1.05 |
用户/信息 枚举 | 17 | 0.99 |
远程代码执行 | 15 | 0.87 |
密码重置 token 过期/尝试/其他 | 13 | 0.75 |
整型溢出 | 11 | 0.64 |
版本泄露 | 11 | 0.64 |
CSV 注入 | 10 | 0.58 |
权限放大 | 9 | 0.52 |
OAuth 状态/泄露和其他问题 | 9 | 0.52 |
密码策略 | 7 | 0.4 |
CRLF | 7 | 0.4 |
python语言 | 6 | 0.35 |
单向攻击 | 6 | 0.35 |
文件上传类型/大小/存储位置 过滤 | 6 | 0.35 |
Captcha | 5 | 0.29 |
远程/本地 文件包含 | 4 | 0.23 |
目录列表 | 4 | 0.23 |
路径遍历 | 4 | 0.23 |
远程文件上传 | 4 | 0.23 |
(WEB表单)开启自动填充 | 4 | 0.23 |
通过引用泄露 | 3 | 0.17 |
Pixel Flood Attack | 3 | 0.17 |
输入控制字符 | 2 | 0.11 |
在java内存分析软件(mat,jhat等)中,有两个概念是 shallow heap
和 retained heap
(有时候叫shallow size 和 retained size)。
比较好理解(好理解不代表好计算),直译就是浅层堆,其实就是这个对象实际占用的堆大小。
比较难理解,直译过来是保留堆,一般会大于或者等于shallow heap,那么retained heap如何理解呢?
首先,不能按照 shallow(浅) 和 deep(深)的层次来理解这个retained heap,其实最简单的理解就是,如果这个对象被删除了(GC回收掉),能节省出多少内存,这个值就是所谓的retained heap。而GC算法中,是否回收一个对象,主要是判断一个对象是否存在引用(还有一些系统级别或特定对象不在此列),至于标记还是引用计数算法,最终都是为了判断是否被引用。简单理解,如果一个对象没有被引用了,就可以回收了。
这里我们先定义一下引用(这里不包含所有“引用”的定义,比如数组会引用他的所有元素,所有对象都会引用他的Class对象等等,这里只是为了简单举例):如果一个类的对象出现在另一个类的成员里,那我们就认为后者引用了前者。比如 类 A
和 B
, 其中 B
中有一个成员变量是 A
的对象,那么就说B引用了A。如下代码:
这本书值得读是因为他不是一本讲成功学的书,虽然大部分数据都是来自那些世界顶级的卓越
的公司。成功学的书讲的都是你跟我这样做,做完你就成功了。这本书是讲,你要是不这样做,你就难以达到卓越,也就是你这样做仅仅是一个最基础的东西,不能导致你一定卓越,但却是必不可少的。作者也很会写书,第一章就把一个富有噱头作用的标题甩出来,成功是卓越的杀手(or绊脚石),你一定会想跟他争辩,然后就会看下去了。优秀确实是卓越的杀手,但其实优秀也是卓越的基础。你要想卓越,首先得优秀,然后不甘于优秀(现状),最后才能卓越。书后面的飞轮理论也说了,卓越不是突然之间导致的,而是之前所有努力的总和导致的。
这个词出现了好几次,最主要就是自我管理,而且是非常严格的要求。书中提到的那些第5级经理人都是非常牛逼的人,自我驱动,目标明确,不计较个人利益,把公司利益放在第一位,知道如何培养下一个第5级经理人。看看国内这些顶级的互联网公司,马化腾,李彦宏,雷军等等都基本上算的上是第五级经理人。大家经常说他们处在风口上,我想说,他们即使没在风口上,也是会飞的鸟,而不是。。猪。雷军在小米创业的前几年,主要精力都是投入在招人上面,寻找合适的的人很重要。如果创始人很牛逼,后来者都很平庸,那么公司也就是初期因为idea不错能拿到一点风投,想做大做强,靠1个天才是不够的,而是应该大家都是天才(或者说精英)。雷军刚创立小米那年也是中国经济的低迷时期,08年次贷危机之后,人才多,价格便宜,捞到就是赚到,在经济低迷的时候招牛逼的人,也很重要。当然我不知道小米一开始创立的时候是不是已经想到了现在这个局面,我倾向于相信,他们是先上车,再定目的地。
找出自己最擅长的点,在一个点上发力,遇到困难还可以及时收缩战线,等困难过去又能继续前行,这是何等的信念才能做到?Keep it simple and simpler.
飞轮理论其实不是为了讲1.01的365次方是37.78,而是一个企业的卓越不是偶然导致的,而是很早以前就开始推动轮子了。就像历史的轮子滚滚向前,一个朝代的灭亡和另一个朝代的兴起不是突然之前的,而是靠日积月累,积少成多而成的。不积跬步,无以至千里,说的就是这个道理。
1.自我管理人人都会,但对自己严格要求的人很少;
2.招最好的人,宁缺毋滥;
3.分享你的思想,不能让自己一个人牛逼,而应该带领其他人通通牛逼。
4.努力努力再努力,对卓越者来说,除了努力其他就是运气(旁观者则不是这么觉得,于是才有了本书);
5.成功(卓越)绝非偶然;
6.卓越并不比优秀难以达到
这本书去年(农历年)年底的时候读完了,翻译的还行,关键译者还有自己的观点。之前(2011年)知道译者周涛老师是在杭州一个大数据的分享会议上,冒出来一个新公司叫百分点的公司做推荐算法的,当时不是觉得这个公司牛,而是这个人牛。因为但是他年龄也就二十五六岁吧,已经是电子科技大学的教授了(主任级),这点是很难的。买这本书其实也是关联推荐买的,本来我是买一本大数据决策,然后推荐的列表里面看到了这本,也就顺手买了。
1T数据够大吗?1P呢?1Z呢?No No No! 大数据其实跟数据量的大小没有确定性的关系,大数据
其实是相对小数据
而言的。什么是小数据?其实小数据跟数据量也无关,根计算方式有关。小数据是基于总体数据
下面的抽样分析
,得到精确的结果。抽样分析的准确性是跟数据的随机性
相关的,随机性越高,抽样的结果越能准确的反应总体的数据。小数据时代下的抽样统计方式之所以流行是因为那个时代的计算能力
不够强大。与之相对的,大数据时代下,数据是更全面
的,计算资源也丰富,足以让我们对数据做全盘的分析
,从而直接得到总体的结果。这里的样本就是总体
。当然大数据也有不足的地方,就是他的精确度
不够(但是往往是可以容忍
这种不精确的)
首先人们的思维方式
会受到挑战。考虑需要更全面,不再由抽样来反应总体。需要各种各样不同的数据源,但同时也会引入一些噪音
,要容忍这些噪音。要明白数据反馈的是事件之间的相关性
,而不是因果性
。从数据推倒出来某个因果关系基本上都是加上了主观
意识的干涉的,不够理性。不要刻意通过数据去追求因果关系,因为最终的结论其实都是主观的。
其实就是概率论中的条件概率
(贝叶斯定理)。 比如一个山脚的别墅,里面养了一条狗,根据主人统计,狗叫的时候,刚好遇到有盗贼的情况的概率为 80%,也就是平日里,10天狗叫有8天是因为有盗贼来了。然后突然有一天,又听到了狗叫,这时你会认为有没有盗贼呢?你不知道,你知道说很可能是来盗贼的,概率大约80%。因为狗叫和盗贼之间不存在因果关系,而只是存在相关性。哪一天来了个主人的远房亲戚,狗也是会叫的,但亲戚并不是贼。贝叶斯公式展开讲就比较复杂了,这里不深入。
相关性很重要,忘掉因果性,这点很重要。
比如有提供基础数据
的大数据公司,像twitter,facebook,微信,微博这种大规模用户下信息流为主的公司。像twitter,他自己虽然也分析数据,但是他还会把数据授权给第三方公司去做分析,然后得到商业价值。还有一种是数据技术公司
,比如google,通过大数据不断地训练自己的神经网络,提供各种牛逼的功能。比如google翻译,google翻译的开发者其实没有一个人懂全世界那么多种语言,但是他们可以互转就是用了大数据,把大量的数据信息录入到数据库,然后以英语为中间语言,互相转换。还有一种公司是大数据思维
公司,也就是第一个例子中twitter授权给分析的公司,这种公司主要是做数据分析
,自己不产生源数据。
在大数据下面,每个人几乎都是“裸体”的,只要有耐性,你在网络上的信息都可以分析到,甚至可以精确到你的住址,家人,朋友,以及朋友的朋友,家人的朋友。
比如错把相关性当初因果性,狗叫时你就拿了个武器去把对方放到了,结果一看是亲戚艾玛。另外是大数据往往是反映了群体性的结果,而不是单个人的结果。通过主观意识,把总体的行为反馈到个人行为身上,产生数据保证。比如种族歧视就是最好的例子。
没错,就是通过数据分析,得出一些结果,就依次推导未来的发展。再次声明,数据只能提供相关性,不提供因果性,数据只能说明有点关系,不能推导出什么玩意。最好的预测未来就是去创造未来。 如果一切都按数据说了算,福特就不会发明汽车了。苹果手机,You think too beatiful !
本书最重要的一个理念就是:在大数据时代,相关性比因果性更重要,同时不要把相关性错当为因果性。
mysql的文本(varchar, text),对emoji表情符号不是很好的支持,在5.5之前的版本,varchar和text都是不支持存储emoji表情符号的(即使是utf8)的编码模式。原因在于mysql的utf8是规定了每一个utf8字符按照3个字节来存储,而一个emoji(最初来自苹果系统,现在流行于各种移动操作系统)却需要4个字节来存储。这就导致了如果强制将emoji存储到varchar,text等字段上的时候,mysql会抛出异常,认为emoji是个“不正确”的文本。
- ERROR 1366 (HY000): Incorrect string value: ‘\xF0\x9F\x91\xBD\xF0\x9F…’ for column ‘name’ at row 31
所幸,mysql在5.5之后的版本,针对四个字节的utf8字符推出了一种新的兼容的编码,叫 utf8mb4。utf8mb4比utf8支持的字符集更广,可以支持utf8以及四个字节的字符集,关于utf8mb4和utf8的区别可以这篇官方文档1。
简而言之就是:“utf8mb4 is a superset of utf8”
,utf8mb4是utf8的超集,utf8是utf8mb4的子集。utf8mb4理论上是兼容utf8. 所以如果你的项目需要支持存储emoji表情,同时mysql的版本是5.5以上的版本,那么就可以把字段的charset改为 utf8mb4就可以完美支持emoji了。
alter table category modify name text charset utf8mb4;
那如果当前mysql版本不支持utf8mb4编码怎么办?
blob类型一般是用来存储二进制文件的,当时用来存储文本其实也是可以的,只不过存进去之前,把文本变成byte数据就可以了。已java为例,使用String.getBytes(charset)方法,可以把字符串转化成二进制,然后存储到数据库中。如果你有很多字段都要这么搞的话,估计都得疯了。怎么办?用orm框架~
已ibatis为例,如果你的对象字段是String文本,存储的字段确实blob,其实是没有关系的,不需要写特殊的代码,直接支持写入。但是读出来的时候就需要做转换,否则出来的是乱码。所以这里需要借助ibatis的typehandler和resultMap来解决这个问题。ORM框架的好处就是你不用一直重复劳动,可以在各种地方留着钩子(hook),随时让你在需要的时候可以插点东西到关键的地方上去。好了,废话不多说,看看这个typeHandler怎么实现:(这里是ibatis2.3.*的版本,如果是myBatis,可能报名和接口参数不太一样,但实现方式是一样的)
public class BlobStringTypeHandler extends BaseTypeHandler { //charset private static final String DEFAULT_CHARSET = "utf-8"; @Override public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType) throws SQLException { ByteArrayInputStream bis; String param = (String) parameter; try { //###把String转化成byte流 bis = new ByteArrayInputStream(param.getBytes(DEFAULT_CHARSET)); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Blob Encoding Error!"); } ps.setBinaryStream(i, bis, param.length()); } @Override public Object getResult(ResultSet rs, String columnName) throws SQLException { Blob blob = rs.getBlob(columnName); byte[] returnValue = null; if (null != blob) { returnValue = blob.getBytes(1, (int) blob.length()); } try { //###把byte转化成string return new String(returnValue, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Blob Encoding Error!"); } } @Override public Object getResult(ResultSet rs, int columnIndex) throws SQLException { Blob blob = rs.getBlob(columnIndex); byte[] returnValue = null; if (null != blob) { returnValue = blob.getBytes(1, (int) blob.length()); } try { //###把byte转化成string return new String(returnValue, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Blob Encoding Error!"); } } @Override public Object getResult(CallableStatement cs, int columnIndex) throws SQLException { Blob blob = cs.getBlob(columnIndex); byte[] returnValue = null; if (null != blob) { returnValue = blob.getBytes(1, (int) blob.length()); } try { return new String(returnValue, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Blob Encoding Error!"); } } @Override public Object valueOf(String s) { try{ return s.getBytes(DEFAULT_CHARSET); }catch (Exception e){ return null; } } }
重点看几个getResult方法,就是从ResultSet中拿到blob字段数据(byte[]),然后把byte数组转化成string就OK了。
怎么使用?
在sqlMap文件定义resultmap, 对需要转换的字段指定这个typeHandler就可以了:
<resultMap id=“EntityMap" class=“your.pack.Entity"> <result property="name" column="name" jdbcType="BLOB" javaType="java.lang.String" typeHandler=“your.BlobStringTypeHandler"></result> </resultMap>
注意select的statement语句返回使用resultMap指定这个 resultMap
<select id=“EntityDAO.getByXXX" parameterClass="java.util.Map" resultMap="EntityMap"> select name from your_table where ... limit 1 </select>
OK, mysql支持emoji了:)
首先怀疑是不是环境变量没设置好:于是增加了JAVA_HOME环境变量之后,问题依旧:
jdk明明是向前兼容的,1.7下跑1.6能支持的软件一般(1)情况下是没问题的,为什么会出现这样的提示呢?原因肯定是在于idea哪里配置指定了只能使用1.6版本的jre才能运行。所以解决方法应该是从idea的配置文件入手。
进入idea的安装目录,有个Contents目录,里面有个Info.plist配置文件。
打开Info.plist,检索“1.6”字符串,果不其然,还真找到了:
配置项是JVMVersion,配置的值是1.6, 把值改成1.7之后启动idea:
perfect,完美运行。看下这个Info.plist,其实还有其他的配置项在这里可以修改,在JVMOptions下面,除了有JVMVersion之外,还有VMOptions, ClassPath等配置项,可以修改。当然一般情况下,这些选项是够用的,不需要手工去修改他。
扩展阅读:
1: http://stackoverflow.com/questions/13575224/comparison-method-violates-its-general-contract-timsort-and-gridlayout
2: http://stackoverflow.com/questions/6626437/why-does-my-compare-method-throw-exception-comparison-method-violates-its-gen
几年前介绍过一种基于zk的分布式锁1的实现,那种是没有经过实践证明的,听过一场分享,然后觉得原来分布式锁可以这么搞,然后在实验环境写了一些代码,简单验证一下,就认为成了。其实那里面有几个比较严重的问题,第一个是锁操作如果在并发的情况下不是block的,而是通过循环+sleep的方式来反复判断,性能上是比较差的,不够实时。第二个是设计太复杂,其实可以不需要ip地址的介入的。
基于zookeeper,其实可以设计出优雅的分布式锁。这个锁可以有这几个API:lock()
, unlock()
, isLock()
,lock用于加锁,unlock用于解锁,isLock用于判断是否已经锁住了。zookeeper
提供了这么一套机制,你可以监控watch
节点的变化(内容更新,子节点添加,删除),然后节点变化的时候通过回调我们的监控器(watcher
)来通知我们节点的实时变化。在这种机制下,我们可以很简单的做一个锁。
在单机模式,没有引入zookeeper
时,我们可以通过创建一个临时文件来加锁,然后在事务处理完毕的时候,把这个临时文件删除就能代表解锁了。这种简单的加锁和解锁模式可以移植到zookeeper上,通过创建一个路径,来证明该锁已经存在,然后删除路径来释放该锁。而同时zookeeper又能支持对节点的监控,这样一来,我们在多机的情况下就能同时且实时知道锁是存在还是已经解锁了。
如图所示,我们在/lock下创建了/app1 /app2 … /appN n个子目录,用于适用不同的应用, 每个/app* 下面都可以根据业务需求创建锁 /lock. , 而每个机器在获取锁的时候,会在/lock. 下面创建 _0000000* 的自增长临时节点,这个节点上的数字用于表示获取锁的先后顺序。前面说的还是有点抽象,下面举个例子:
一个后台应用(app为back)总共有3台机器在处理事务,其中有一个业务(lock为 biz)同一时间只能有一台机器能处理,其他如果也同时收到处理消息的时候,需要对这个事务加个锁,以保证全局的事务性。三台机器分别表示为 server1, server2和 server3。
对应的,锁的路径就是 /lock/back/biz 。 首先 server1先收到消息,要处理事务biz,那么获取锁,并在/lock/back/biz下创建一个临时节点/lock/back/biz/_00000001 ,这时候判断/lock/back/biz 下的子节点,看最小的那个是不是和自己创建的相等,如果是,说明自己已经获取了锁,可以继续下一步的操作,直到事务完成。
与此同时,server2和server3也收到的消息(稍微慢于server1收到),这时候server2和server3也分别会在/lock/back/biz下面创建临时节点/lock/back/biz/_00000002 和 /lock/back/biz/_00000003,他们这时候也会判断/lock/back/biz下的子节点,发现最小的节点是_00000001,不是自己创建的那个,于是乎就会进入一个wait的操作(通过mutex很容易实现)。
之后,等server1的事务处理完毕,释放server1获取的锁,也就是把 /_00000001删掉。由于zookeeper可以监控节点的变化,删掉/_00000001的时候,zookeeper可以通过节点删除的事件,通知到server1 server2 server3,这时候server2和server3对上面通过mutex block住的区块发送信号量(notify),server2和server3继续进入判断/lock/back/biz下面的子节点以及最小的节点和自己做对比,这时候server2由于创建了节点_00000002,所以轮到他来获取锁:
之后server2开始进入事务处理,然后释放锁,server3开始得到锁,处理事务,释放锁。以此类推。
这样一来,整个事务(biz)的处理就能保证同时只有一台机能处理到了。
整体伪代码如下:
public class LockImpl{ //获取zk实例,最好是单例的或者是共享连接池,不然并发高的时候,容易挂 private Zookeeper zk = getZookeeper(); //用于本地做wait和notify private byte[] mutex = new byte[0]; //节点监控器 private Watcher watcher; //这个锁生成的序列号 private String serial; public LockImpl(){ watcher = new DefaultWatcher(mutex); createIfNotExist(); } private createIfNotExist(){ String path = buildPath(); //创建路径,如果不存在的话 createIfNotExsitPath(path); //注册监控器,才能监控到。 registWatcher(watcher); } public void lock(){ //获取序列号,其实就是创建一个当前path下的临时节点,zk会返回该节点的值 String serial = genSerial(); while(true){ //获取子节点 List<string> children = getChildren(); //按从小到大排序 sort(children); //如果当前节点是第一个,说明被当前进程的线程获取。 if(children.index(serial) == 0){ //you get the lock break; }else{ //否则等待别人删除节点后通知,然后进入下一次循环。 synchronized(mutex){ mutex.wait(); } } } return; } public void unlock(){ //删除子节点就能解锁 deletePath(serial); } public void isLock(){ //判断路径下面是否有子节点即可 return ifHasChildren(); } } //监控器类 public class DefaultWatcher implements Watcher{ private byte[] mutex; public DefaultWatcher(byte[] mutex){ this.mutex = mutex; } public void process(WatchedEvent event){ synchronized (mutex) { mutex.notifyAll(); } } }
至此,一个看起来优雅一点的分布式锁就实现了。这是一个理想状态下的实现,咋看起来没问题,其实里面隐藏了比较严重的问题。就是这里把zookeeper理想化了,认为他是完美的,不会出现问题。这里说的问题倒不是一定是zk的bug,比如网络问题,在分布式系统中,网络问题是一个很常见的问题,很容易就会有异常的情况。如果出现网络问题,会出现什么情况呢?答案是“死锁”。
下面按多种情况来分析这个问题:
当我们向zk发起请求,要求创建一个临时节点的时候,发生了异常(包括网络异常,zk的session失效等)
假设zk服务端收到请求了,但服务端发生异常,没有创建成功,那么我们客户端再次重试一下就可以了。
假设zk服务端收到了请求,子节点创建成功了,但是返回给客户端的时候网络发生了异常。这时候我们要是再做重试,创建的话,就会进入“死锁”。这里的“死锁”跟我们平时理解的死锁不是一个概念,这里的意思不是回路循环等待,而是相当于这个锁就是死掉了,因为前面的异常请求中其实创建了一个节点,然后这个节点没有客户端与他关联,我们可以称为幽灵节点
。这个幽灵节点由于先后顺序,他是优先级最高的,然而没有客户端跟他关联,也就是没有客户端可以释放这个幽灵节点,那么所有的客户端都会进入无限的等待,造成了“死锁”的现象。
假设zk服务端收到请求了,子节点创建成功了,返回给客户端成功了,但是客户端在处理的时发生了异常(这种一般是zk的bug才会出现),这时候我们再做一次重试,也会进入上面的“死锁”现象。
为什么会出现3,4的现象呢?因为我们只是做了简单的重试,并没有对服务端和客户端做验证。就是客户端创建了一个幽灵节点,但是创建者本身甚至都不知道是自己创建的幽灵节点,还是别人创建的。要如何规避这个问题呢?废话,引入验证的流程。就是验证+无限重试。怎么做验证,不要把验证想的太复杂,验证就是你创建的节点里面有你创建的私有的信息,你客户端本身也拥有这个信息,然后两者一对比,就能知道哪个节点是哪个客户端创建的。当然,这个信息必须保证我有你无的,也就是唯一性。简单了,引入UUID就可以搞定这个问题。
比如客户端1,生成了UUID: fd456c28-cc85-4e2f-8d52-bcf7538a2acf, 然后创建了一个临时节点: /lock/back/biz/_fd456c28-cc85-4e2f-8d52-bcf7538a2acf_00000001
这时候服务端返回异常,拿客户端第一件事是先把children捞出来,然后判断这些children里面有没有自己创建的 uuid,如果有的话,说明自己其实是创建成功了,然后就看是不是轮到自己了,解决3的问题。如果返回正常,但是客户端有bug抛异常了,这时客户端仍要进行重试,重试之前也会走前面的流程,可以解决4的问题。
对children排序不能简单的把node的路径进行排序,因为randomUUID是完全随机的,按这个排序可能会导致某些锁请求一直没有被响应,也会有问题。这里因为UUID的长度是固定的,而且也有规律可循,所以很容易从node中分解出 uuid和序列号,然后对序列号进行排序,找出最小的值,再赋予锁,就可以了。
分布式系统中,异常出现很正常,如果你的业务需要觅等操作
(N^m = N)的话,就需要引入验证和重试的机制。分布式锁就是需要一个觅等的操作,所以一个靠谱的分布式锁的实现,验证和重试的机制是少不了的,这就是我想说的。具体要参考实现的话,可以看netflix开源的zk客户端框架curator2, 比直接用zk简单得多也健壮得多。框架里面也实现了一套分布式锁(还有其他各种有用的东西),生产线其实可以直接拿来使用的,非常方便。