Skip to content

Latest commit

 

History

History
115 lines (58 loc) · 12.2 KB

File metadata and controls

115 lines (58 loc) · 12.2 KB

说点细节的

想到哪写到哪, 上一回视野有点大了, 这回落个地.

Cache和CAS

前面提到"两次删除技术", 千万别用, 用了千万别说. 这实在是有点"思而不学则殆"的味道.

我当时听到的第一感受是"这小伙真实在, 压箱底的技术都分享出来了..." 后来在考虑怎么让开发人员更容易理解cache脏数据的问题, 以及该怎么处理的时候, 发现了这个页面: http://docs.oracle.com/cd/E13924_01/coh.340/e13819/readthrough.htm#CHDBBJFC . 写得真是太好了, 企业级方案还是有很多值得学习的啊, 几种cache模型, 怎么更新数据, 都讲得很清楚. 这个发现, 简直和上次发现VMware的延迟解决方案一样兴奋.

简单点说, 其实脏数据是必然存在的, 只是"存在时间的长短", 而原则是: 允许短时间的脏读, 绝不允许脏写. 要避免脏写, 一定要用到的技术: CAS(Compare and Set). Cache一般会提供这个操作, Couchbase, Redis都有; 数据库有transaction, 如果写操作还是有CAS的考量, 会更好. CAS的好处就是把并发的修改顺序化, 只有最新的数据才允许修改.

当然这么用的话, 代码会复杂一些. 不要总想交给框架. 当程序要为并发和性能考虑的时候, 只有两条路: 1. 非常熟悉框架的实现; 2. 在关键路径上绕开框架. 我总是觉得, 什么都想交给框架的程序员, 是不大可能去细读框架的实现的, 而且大家都这么忙, 项目都来不及啊. 还不如让框架做简单的事, 自己的代码管好重要的部分. 也是很符合"外包非核心业务"的原则啊.

更新数据库然后删除cache的简单策略已经过时了

这个策略, 行为上很简单明了, 如果是个小网站, 这么做是完全OK的. 如果是大网站, 这个策略是不可以的, 因为并发. 在删除cache的时候, 很容易导致并发读取同一条数据库记录, 会给数据库很大压力. 小大之辩, 不可不察.

NoSQL就是Write-back cache

其实我觉得, NoSQL的数据库, 像redis, 和MySQL的cache的区别, 就是write-back还是write-through的区别. 前者先更新缓存, 后者先更新数据. 放一台机器的时候, 就是write-back和write-through, 系统都搞定了; 分布式的时候, 就得开发来处理了, 或者买上面Oracle的Coherence也不错. PS. 好像apache有个开源实现.

Cache靠前

我后来总结, 觉得挺多人对cache和remote cache的理解, 是没有区分的, 可能是受了J2EE的影响, 全都抽象了. 比如Couchbase, Redis, 其实是remote cache. 当然我们也都叫cache. 传统的cache呢, 就是和application在同一个机器上的, 就叫local cache了, 以示区分.

原则上两者比访问数据库都是快的. 为什么还要分开来说呢? 因为它们的性能影响是不同的. 如果application的并发高了, 想要稳定的请求耗时, 就必须要明白两者的区别:

  1. Remote cache的每次访问都是有网络成本的, 重复访问同一条数据(cache hit), 是低效的行为, 至少1ms.
  2. Local cache不存在网络成本, 重复访问数据, 是高效的行为, 访问速度是纳秒级别, 几乎可以忽略.
  3. Remote cache的访问, 每次都会生成新的对象, 重复访问就是产生大量垃圾对象.
  4. Local cache是直接引用对象, 极大提高memory system的效率.

综上, 如果在remote cache上, 能看到application在访问同一条数据(比如couchbase的top keys), 就说明性能有提高余地--很大的提高余地. 印象里tweet就给redis做了个key访问统计的系统, 虽然我觉得他们做得过了一点, 不得不说还是很厉害的.

开发可能会担心数据不够新, "必须访问最新的数据". 然而事实上, 在分布式并发下, 怎么可能存在"最新"的数据呢? 世上哪有最新的东西啊! 这是分布式系统的原则问题!

既然最新是不存在的, 那么就必须考虑"能不能使用1秒钟前的数据?", "10秒钟呢?", "一分钟呢?". 我想对很多业务来说, 1秒钟是显然没有问题的, 搞不好慢一点的系统写操作还没完成呢; 1分钟可能不好接受; 然而10秒钟, 可以斟酌一下吧; 或者5秒钟, 真不是太大的问题. 事实上, 有很多数据, 能cache住5秒钟, 对系统的性能提升是非常明显. 假设原来每秒会访问10次重复数据, 就需要10ms, 现在只需要1ms. 是不是值得考虑一下?

秒杀场景

可能有人会说我是秒杀场景, 不能cache. 说实话, 我觉得cache个几秒钟也无所谓, 反正没人知道究竟能不能抢到, 可能体验会比现在再差那么一点, 倒也没到分对错的程度. 不过秒杀场景, cache方式有一点不同: 比如要秒杀10万个手机, 如果用一个计数器, 每次秒掉一个就倒数一个, 那当然是不能cache了. 如果用10万个计数器, 每个计数器都只有1, 那不就可以把这些计数器cache到application server上去了吗?

当然也不用真弄那么多计数器, 毕竟提交记录还是要花时间的. 这只是个概念的demo.

超级并发和假并发

说两个场景.

第一个, 是球赛. 据说访问比分的qps会非常高. 当时业务问我们, 能不能用Redis支撑50wqps的访问. 我的第一反应是"你们想干嘛?" 当然我们得好好说话: "是什么场景啊?". 当知道只是要存一条数据的时候, 我就告诉他们, 可以直接放在内存里. 我还在想着该怎么说服他们这么干, 毕竟要改代码的. 结果还没开口, 他们欢天喜地地回去了! 其实, 有时候大家都只是突然陷在一个想法而已.

第二个, 是要做离线下载那种"进度条效果". 开发问我, 每个用户的每个下载进度都要记录下来, 并且访问频率会很高, 因为离线下载是很快的, 所以要很快更新, 是个数据量大并发又高的场景. 我们讨论了一会儿, 发现其实离线下载的进度条, 大部分只要做个假的就可以了. 因为后台早就下载好了啊, 进度条的作用就是让用户觉得我们下载得很快. 而真正要存下来的只是很小一部分, 并且, 这一部分因为服务端还在下载, 所以进度更新频率很慢. 所以一个大数据量高并发的场景, 变成了小数据量低并发的场景.

有时候, 我觉得技术人员缺乏一些对变动业务自信, 总觉得自己是处理技术问题的, 放弃了对产品的控制力. 技术人员要明白: 我们, 把一个东西做出, 变成计算机能理解的代码, 我们对这个产品的理解, 深入到了另一种语言的程度, 深入到了数字的程度, 深入的一个像素的程度, 没有人能超越. 有地方不合理的时候, 一定要说出来. 别扭的技术需求, 同不合理的场景设计总是如影随形, 一定要说出来.

报警短信洪水

应该很多人都遇到过这个问题. 如果没有就别看了, 我就取了个巧.

原则上说, 处理报警洪水, 要设定更好的报警规则, 做到无误报无漏报. 然而有些事情实在不能控制, 有些, 像Cluster磁盘满了这种, 本身它是应该每台机器都报出来的, 并且也不应该用fakemaster的方式来做, 万一fakemaster上没满呢. 并且在Zabbix里设定很复杂的报警规则还是不大方便的. 于是, 我就想在Zabbix报警之后, 对报警进行分析, 并且做出更智能的报警. 能够在收到报警之后, 再做一次更复杂的分析, 并且合并成一条报警. 在此基础上, 其实还可以做故障的自动化处理. 不过我们的故障率已经降低到了足可以人工处理的阶段, 所以倒也不着急.

我开始想处理两个类型的误报:

  1. 比如大范围网络抖动. 经常一出来就是短信停不下来. 传闻曾经有哥们被干得换号码的.
  2. 多个节点都会报警的集群故障.

于是设计了一个类似processor list的系统, 按优先级一个个alert分析. 写了我一星期, 开始写频率控制, 然而前面的规则, 实在写得很郁闷. 突然灵光一闪: 为什么我不把整个系统直接做成频率控制呢?

重新思考一下重点:

  1. 报警短信洪水. 报警短信是为了及时的通知有故障, 短信洪水其实失去了通知的意义, 因为不会去看.
  2. 正常的故障, 都会是单条的报警. 大量的报警说明故障很严重, 这个时候我并不需要再收报警, 我肯定已经在处理故障了啊, 而且线上有更方便的系统检查故障情况.

所以, 结论是: 第一条短信报警是有意义的, 连续短信报警情况下, 后面的短信, 不管什么报警都是没有意义的.

所以, 防洪水的规则就变得很简单: 控制频率, 合并短信, 而和报警内容无关.

我的规则就是: 每分钟最多3条短信, 每5分钟最多5条短信. 一旦超过阈(yu4)值, 短信内容就会合并成一封邮件, 并且只发一条短信: 收到多少条报警, 详细内容见邮件. 合并在一封邮件里的报警看起来也更容易.

从此手机就安静了.

我觉得这个频率控制规则, 好比垃圾邮件算法: 处理垃圾邮件从来不需要程序理解垃圾邮件. 处理报警短信洪水也不需要知道报警短信内容.

有两个小细节:

  1. 如果系统在合并短信模式下, 连续1分钟没有新的短信, 会把前面已经合并的短信发出去并且切回到立刻转发的模式. 防止delay太久.
  2. 可能有人会觉得, 这个系统delay了报警邮件一段时间. 其实无所谓, 第一条短信发送是很及时的, 中转一下而已. 发到手机上还不一定能立刻看到呢. 当然, 延迟是可以自己调整的.

整个系统, 差不多百来行代码就够了.

安全和责任

这个小节出现在这里, 因为突然想起一个弱密码相关的事. 平时我们收到MySQL弱密码的通知, 就把密码改一下. 有次, 忽然收到通知"我们的密码策略要给一个改进方案出来, 因为密码太弱". 我有点惊讶, 因为我们的MySQL密码策略早就确认过强度. 于是就去看了下那个弱密码: 有大小写字母, 有数字, 有符号. 上网搜了一下也没发现在密码库里.

我就开始问, 这个密码为什么是弱密码? 收到第一个回复: 先给一个改进方案出来. 我更好奇了(竟然比我还拽), 就加了总监继续问. 总监来打了个圆场: "黑客有很多我们不知道的黑科技的, 还是以安全组的要求做". 说实话, 我非常相信安全组在安全这块的专业知识, 生态和工具链上比我强N倍. 然而并不代表我连密码强弱都不能分辨. 于是我继续问: "如果不解释一下这个弱密码是怎么发现的, 我们没有办法改进密码策略". 还好我的老板顶了我一下, 终于得到了答案: "渗透测试的时候, 发现一个excel里记录着这个密码".

从此我对安全组有了改观, 从认为比我强N倍改为强1倍. 当然, 那个什么加强的系统也就不用做了.

后来, 又遇到一个事情. Linux出了个大漏洞, 所有的服务器都要打补丁. 安全组的邮件说有个wiki, 要分几步, 怎么怎么做, 我突然觉得很复杂. 然而如果我都觉得复杂, 那么公司几千台服务器, 那么多人比我还要不懂Linux呢. 我就觉得有问题: 我们只是用服务器, 为什么服务器的安全补丁要我们去打呢? 有安全组, 有IaaS组, 他们多专业. 我不能想象, 让那么多不专业的人士去维护安全是什么样的结果. 毕竟安全问题之所以存在就是因为对这部分不专业的人的存在啊.

于是我就顶了一下: 希望IaaS或者安全组能做这个事情, 因为用户都不专业. 收到个回复: 我们现在是要求用户对自己的服务器负责, 因为他们是owner. 做安全的就是拽, 反正被攻击了都是因为对安全不够重视. 用户是机器的owner, 都不知道你们是怎么大摇大摆地在我的机器上装了agent的, 还能对安全负责.

我对安全的理解是:

  1. 重大的安全问题从来是没法提前预防的. 比如heartbleed. 不是安全公司就别操预防这个心, 积极打补丁就行了.
  2. 大部分的安全隐患, 是不规范造成的. 比如在私有云让每个虚机的owner去维护安全; 比如把密码写在excel里; 比如SQL注入, 弱密码. 把规范做成自动化, 避免低级错误.
  3. 组织架构的自我修养. 比如前面说的excel泄露密码, 传过来就变成"数据库使用了弱密码". 不能准确传递信息的组织, 谈何安全.