Skip to content

Latest commit

 

History

History
881 lines (719 loc) · 39.1 KB

左耳听风专栏学习笔记.md

File metadata and controls

881 lines (719 loc) · 39.1 KB

[TOC]

01 | 程序员如何用技术变现(上)

没有必要通过打工听人安排而活着,而是反过来通过在公司工作提高自己的技能,让自己可以更为独立和自由地生活。 去研究公司里外那些更为核心更有技术含量的技术了。

25~35 岁是每个人最宝贵的时光,应该用在刀刃上。 并不是社会不尊重程序员,只要你能帮上大忙,就一定会赢得别人的尊重。

02 | 程序员如何用技术变现(下)

如何让自己的技能变现?

  • 千里之行,积于跬步。
  • 关注有价值的东西。
    • 关于市场需求。
    • 关于技术趋势。
  • 找到能体现价值的地方。在一家高速发展的公司中,技术人员的价值可以达到最大化。
  • 动手能力很重要。
  • 关注技术付费点。
    • 一个是,能帮别人“挣钱”的地方;另一个是,能帮别人“省钱”的地方。
  • 提升自己的能力和经历。
  • 找到有价值的信息源。
  • 输出观点和价值观。
  • 朋友圈很重要。

会挣钱的人一定是会投资的人。

03 | Equifax信息泄露始末

04 | 从Equifax信息泄露看数据安全

数据泄露攻击

如何实现攻击?

  • 利用程序框架或库的已知漏洞。
  • 暴力破解密码。
  • 代码注入。
  • 利用程序日志不小心泄露的信息。
  • 社会工程学。

Equifax暴露出的数据管理上的问题

  • 只有一层安全
  • 弱密码
  • 向公网暴露了内部系统
  • 对系统及时打安全补丁
  • 安全日志被暴露
  • 保存了不必要保存的用户数据
  • 密码没有被合理地散列

专家建议

  • 理解你的软件产品中使用了哪些支持性框架和库,它们的版本号分别是多少。时刻跟踪影响这些产品和版本的最新安全性声明。
  • 建立一个流程,来快速地部署带有安全补丁的软件产品发布版
  • 所有复杂的软件都有漏洞。不要基于“支持性软件产品没有安全性漏洞”这样的假设来建立安全策略。
  • 建立多个安全层。
  • 针对公网资源,建立对异常访问模式的监控机制

技术上的安全做法

从技术上来说,安全防范最好是做到连自己内部员工都能防,因为无论是程序的 BUG 还是漏洞,都是为了取得系统的权限而获得数据。

  • 需要把我们的关键数据定义出来,然后把这些关键数据隔离出来,隔离到一个安全级别非常高的地方。
  • 如果业务必需返回用户的数据,一般来说,最终用户可能需要读取自己的数据,那么,对于像信用卡这样的关键数据是死也不能返回全部数据的,只能返回一个被“马赛克”了的数据(隐藏掉部分信息)。就算需要返回一些数据(如用户的地址),那么也需要在传输层上加密返回。
  • 用户加密的算法一定要采用非对称加密的方式,而且还要加上密钥的自动化更换,比如:在外部系统调用 100 次或是第一个小时后就自动更换加密的密钥。
  • 被加密的数据和用于加密的密钥是由不同的人来管理的,有密钥的人没有数据,有数据的人没有密钥
  • 密钥一定要做到随机生成,最好是对于不同用户的数据有不同的密钥,并且时不时地就能自动化更新一下,这样就可以做到内部防范。
  • 每当这些关键信息传到外部系统,需要做通知,最好是通知用户和自己的管理员。

21 分布式系统的冰与火

使用分布式系统的原因:

  • 增大系统容量
  • 加强系统可用

分布式系统优势:

  • 模块化,所以系统模块重用度更高;
  • 软件服务模块被拆分,开发和发布速度可以并行而变得更快
  • 系统扩展性更高
  • 团队协作流程也会得到改善
  • ……

分布式系统劣势:

  • 架构设计变得复杂
  • 部署复杂
  • 吞吐量会变大,但是响应时间会变长
  • 架构复杂导致学习曲线变大。
  • 测试和查错的复杂度增大。
  • 技术多元化,这会带来维护和运维的复杂度。
  • 管理分布式系统中的服务和调度变得困难和复杂。

22 从亚马逊的实践,谈分布式系统的难点

亚马逊分布式服务实践总结:

  • 分布式服务的架构需要分布式的团队架构
  • 分布式服务查错不容易
    • 一旦出现问题,所有相关人士都要在线
  • 没有专职的测试人员,也没有专职的运维人员,开发人员做所有的事情
  • 运维优先,崇尚简化和自动化
  • 内部服务和外部服务一致

分布式系统中需要注意的问题

问题一:异构系统的不标准问题

表现在:

  • 软件和应用不标准
  • 通讯协议不标准。
  • 数据格式不标准。
  • 开发和运维的过程和方法不标准。

问题二:系统架构中的服务依赖性问题

  • 如果非关键业务被关键业务所依赖,会导致非关键业务变成一个关键业务。
  • 服务依赖链中,出现“木桶短板效应”——整个 SLA 由最差的那个服务所决定。 注意: 很多分布式架构在应用层上做到了业务隔离,然而,在数据库结点上并没有。如果一个非关键业务把数据库拖死,那么会导致全站不可用。 —— 最好一个业务线用一套自己的数据库。 —— 系统间不能读取对方的数据库,只通过服务接口耦合。

问题三:故障发生的概率更大

出现故障不可怕,故障恢复时间过长、或者故障影响面过大才可怕。 “防火胜于救火”,我们还要考虑如何防火,这需要我们在设计或运维系统时都要为这些故障考虑,即所谓 Design for Failure。在设计时就要考虑如何减轻故障。如果无法避免,也要使用自动化的方式恢复故障,减少故障影响面。 人管代码,代码管机器,人不管机器!

问题四:多层架构的运维复杂度更大

很多公司都是按技能分工的 —— 没有统一的视图和管理,导致运维被割裂开来,造成更大的复杂度。 分工不是问题,问题是分工后的协作是否统一和规范。

23 分布式系统的技术栈

构建分布式系统的目的是增加系统容量,提高系统的可用性,即完成:

  • 大流量处理。
  • 关键业务保护。

提高架构性能

  • 缓存系统。
  • 负载均衡系统。
  • 异步调用。
  • 数据分区和数据镜像。

提高架构的稳定性

  • 服务拆分。
  • 服务冗余。
  • 限流降级。
  • 高可用运维。

分布式系统的关键技术

  • 服务治理。
  • 架构软件管理。
  • DevOps。
  • 自动化运维。
  • 资源调度管理。
  • 整体架构监控。
  • 流量控制。

分布式系统的“纲”

  • 全栈系统监控;
  • 服务 / 资源调度;
  • 流量调度;
  • 状态 / 数据调度;
  • 开发和运维的自动化。

25 分布式系统关键技术:服务调度

关键点: 服务关键程度 服务依赖关系 服务发现 整个架构的版本管理 服务应用生命周期全管理

流量与数据调度

流量调度关键技术:

  • 高性能
  • 扛流量
  • 业务逻辑
  • 服务化

以上4个特性,其实主要是在说API网关应该具备的特点。

分布式事务一致性问题

数据副本是分布式解决数据丢失异常的唯一解决手段。 解决方案:

  • Master/Slave
  • Master/Master
  • 两阶段与三阶段提交方案
  • Paxos方案

应用层解决事务问题:两阶段提交 数据层解决事务问题:Paxos算法

27 洞悉PaaS平台的本质

首先还是抛出问题吧: 为了构建一个分布式系统,我们面临的主要问题有:

  • 故障是常态,需要运维流程自动化
  • 良好的服务设计,避免单点故障
  • 容量的可伸缩性
  • 老的服务可能是异构的,需要让它们使用标准化的协议
  • 分布式存储使得事务处理变得复杂,事务无法自动恢复时,手工恢复将会很复杂
  • 测试和查错的复杂度增大
  • 系统吞度量会变大,但同时响应时间会变长

为了解决这些问题,了解了如下解决方案:

  • 完善的监控系统
  • 设计服务时要分析其依赖链
  • 重构老的软件,使其服务化
  • 为老的服务编写接口逻辑,以便使用标准协议,或必要时重构老的服务
  • 自动构建服务的依赖地图
  • 使用API网关
  • 事务处理建议在存储层实现;根据业务需求,或是降级使用更简单、吞吐量更大的最终一致性方案,或是通过二阶段提交、paxos、raft、NWR等方案之一,使用吞吐量小的强一致性方案
  • 异步调用;关键服务采用专属硬件资源,优化软件逻辑

41 弹力设计篇之“认识故障和弹力设计”

分布式系统中,可能出现多种问题,不出现故障基本不可能,需要考虑出现故障时如何尽快修复故障。 可能出现的故障主要有:

  • 网络问题
  • 性能问题
  • 安全问题
  • 运维问题
  • 管理问题
  • 硬件问题

故障不可避免,就需要把处理故障的代码当成正常的功能做在架构里写在代码里

左耳朵耗子叔这里说的弹力设计 Resiliency 主要是指:

  • 好的情况下,系统出现故障后,能够自动修复,不需要人为介入
  • 如果修复不了,系统能够自我保护,不让事态变得更糟

42 弹力设计篇之“隔离设计”

隔离设计所要解决的问题:发生故障时使故障隔离,不要导致系统整体不可用,减小影响范围 分离的方式:

  • 以服务的种类来做分离
    • 异步处理,两阶段提交
  • 以用户的请求来做分离
    • 多租户架构

43 弹力设计篇之“异步通讯设计”

系统间通讯,主要有2种:同步,异步

同步调用的问题:

  • 整个同步调用链的性能会由最慢的那个服务所决定。
  • 同步调用会导致调用方一直在等待被调用方完成,如果一层接一层地同步调用下去,所有的参与方会有相同的等待时间。高并发场景下,非常消耗资源。
  • 同步调用只能是一对一的,很难做到一对多。
  • 被调用方失败,调用方也会跟着失败,故障蔓延

异步通讯的方式:

    1. 请求响应式
    • 又分为:发送方定期查询接收方是否完成,以及发送方注册回调、接收方完成后掉回调这两种。
    1. 通过订阅的方式
    • 发送方发送事件,事件驱动
    • 缺点:接收方依赖于发送方,还是有耦合
    1. 通过 Broker 的方式
    • 同样依赖于事件,但发送方、接收方都使用broker通信
    • 2和3都是事件驱动设计

事件驱动

  • 优点
    • 消除了服务间依赖,每个服务都是高度可重用并可被替换的。
    • 开发、测试、运维,以及故障处理都是高度隔离的
    • 服务间是不会相互 block 的
    • 服务间增加一些 Adapter(如日志、认证、版本、限流、降级、熔断等)相当容易。
    • 服务间的吞吐也被解开了,各个服务可以按照自己的处理速度处理。
  • 缺点
    • 业务流程不再那么明显和好管理。整个架构变得比较复杂
    • 事件可能会乱序。
    • 事务处理变得复杂。需要使用两阶段提交来做强一致性,或是退缩到最终一致性。

异步通讯

  • 为何要异步通讯?
    • 解耦服务间的依赖
    • 让各个服务的隔离性更好,避免出故障时故障蔓延
    • 可以获得更大的吞吐量,而且各个服务间的性能不受干扰相对独立
    • 利用 Broker 或队列的方式还可以达到把抖动的吞吐量变成均匀的吞吐量,这就是所谓的“削峰”,这对后端系统是个不错的保护。
    • 部署、扩容和运维上都可以做到独立不受其他服务的干扰。
  • 注意事项:
  • 用于异步通讯的中间件 Broker需要设计成高可用不丢消息的;消息无法保证顺序,架构设计不要依赖于消息的顺序
  • 业务处理流程不那么直观,在 Broker 上需要有相关的服务消息跟踪机制
  • 业务状态最好由一个总控方来管理,用于维护一个业务流程的状态变迁逻辑,便于发生故障时查找问题、以及故障恢复
  • 若消息可能重传,需要处理方有幂等的处理

44 弹力设计篇之“幂等性设计”

保证幂等性的几种方式:

  • 需要有全局ID
    • SnowFlake算法
    • 数据库实现
    • Redis/MongoDB实现 以上算法大同小异
  • 处理流程
    • 实际执行操作时查询一下?
      • 有问题,多数情况不会发生重复,导致很多操作都是白费力,消耗资源
    • 使用id,保存时如果有重复直接抛错

HTTP的幂等性

  • PRG模式

45 弹力设计篇之“服务的状态”

无状态的服务 stateless

  • 为了做成无状态服务,可能依赖于导致这些服务需要耦合第三方有状态的存储服务
    • 比如,不太重要的数据可以放到 Redis 中,重要的数据可以放到 MySQL 中,或是像 ZooKeeper/Etcd 这样的高可用的强一致性的存储中,或是分布式文件系统中。
    • 要求这些存储服务也做成高可用高扩展的方式

有状态的服务 stateful

  • 数据本地化(Data Locality)
    • 有更低的延时,而且对于数据密集型的应用来说,这会更快。
  • 更高的可用性和更强的一致性
    • CAP 原理中的 A 和 C
    • 因为对于有状态的服务,我们需要对于客户端传来的请求,都必需保证其落在同一个实例上,这叫 Sticky Session 或是 Sticky Connection。这样一来,我们完全不需要考虑数据要被加载到不同的结点上去,而且这样的模型更容易理解和实现。
    • Sticky Session
      • 实现方式:
        • 持久化的长连
        • 哈希(hash)算法:uid取模,或是一致性hash
      • 问题:结点的负载和数据并不会很均匀
        • 解决:
          • 有一个元数据索引来映射后端服务实例和请求的对应关键,还需要一个路由结点
          • 使用到 Gossip 协议

服务状态的容错设计

一种方式:采取数据在运行时就复制的方案

46 弹力设计篇之“补偿事务”

CAP 理论:在分布式的服务架构中,一致性(Consistency)、可用性(Availability)、分区容忍性(Partition Tolerance),在现实中不能都满足,最多只能满足其中两个。

ACID 的一个变种 BASE:

  • Basic Availability:基本可用。系统可以出现暂时不可用的状态,而后面会快速恢复。
  • Soft-state:为了提高性能,我们可以让服务暂时保存一些状态或数据,这些状态和数据不是强一致性的。
  • Eventual Consistency:最终一致性

ACID强调的是一致性(CAP中的C),BASE强调的是可用性(CAP中的A)

业务补偿机制需要做到:

  • 清楚地描述出要达到什么样的状态,以及如果其中的条件不满足,那么,我们要回退到哪一个状态。
  • 当整条业务跑起来的时候,我们可以串行或并行地做这些事。
    • 如果达不到,就需要通过补偿机制回滚到之前的状态。这就是所谓的状态拟合。
  • 对于已经完成的事务进行整体修改,可以考虑成一个修改事务。

业务补偿机制设计重点:

  • 服务方支持幂等性,上游有重试机制
  • 最好是一个业务流程的控制方来做这个事,也就是一个工作流引擎。
  • 补偿的业务逻辑和流程不一定非得是严格反向操作。有时候可以并行,有时候,可能会更简单。总之,设计业务正向流程的时候,也需要设计业务的反向补偿流程。
  • 下层的业务方最好提供短期的资源预留机制。

47 弹力设计篇之“重试设计”

重试的场景

并不是所有故障都要重试,故障是暂时的、不是永久的,才有必要重试。 例如:调用超时,被调用端返回了某种可以重试的错误(如繁忙中、流控中、维护中、资源不足等)。

不要重试:业务级的错误(如没有权限、或是非法数据等错误),技术上的错误

重试的策略

Exponential Backoff 指数级退避:每一次重试所需要的休息时间都会成倍增加

Spring的重试策略

  • Spring Retry项目

重试设计的重点

  • 要确定什么样的错误下需要重试
  • 重试的时间和重试的次数
  • 如果超过重试次数,或是一段时间,那么重试就没有意义了。
  • 重试还需要考虑被调用方是否有幂等的设计
  • 重试的代码比较简单也比较通用,完全可以不用侵入到业务代码中。
  • 留意有事务相关的操作

48 弹力设计篇之“熔断设计”

熔断设计

可以使用状态机来实现,内部模拟以下状态:

  • 闭合(close)
  • 断开(open): 在该状态下,对应用程序的请求会立即返回错误响应,而不调用后端的服务。
  • 半开(half-open)允许应用程序一定数量的请求去调用服务,根据这些请求的调用情况来决定后续是切换为断开状态还是闭合状态。

熔断设计重点

  • 错误的类型
  • 日志的监控
  • 测试服务是否可用
  • 手动重置
  • 并发问题
  • 资源分区
  • 重试错误的请求

49 弹力设计篇之“限流设计”

保护系统不会在过载的情况下出现问题,就需要限流。

限流的策略

  • 拒绝服务
  • 服务降级
    • 关停不重要的服务
    • 不再返回全量数据,只返回部分
  • 特权请求
    • 资源不够时,有限的资源分给重要的客户
  • 延时处理
    • 如:使用队列缓冲请求
  • 弹性伸缩
    • 动用自动化运维的方式对相应的服务做自动化的伸缩。
      • 当然,如果是数据库的压力过大,弹性伸缩应用是没什么用的,这个时候还是应该限流。

限流的实现方式

  • 计数器方式
  • 队列算法
    • 变种:
      • 高/低优先级队列
      • 为避免饿死:权重队列
  • 漏斗算法 Leaky Bucket
    • 经常用队列实现
    • 处理请求是以一个常量和恒定的速度处理的
  • 令牌桶算法 Token Bucket
    • 令牌桶算法则是在流量小的时候“攒钱”,流量大的时候,可以快速处理。
  • 基于响应时间的动态限流
    • 典型:TCP协议的拥塞控制的算法 Round Trip Time
      • 需要仔细研究下这个部分

限流的设计要点

  • 在架构的早期考虑
  • 限流模块性能必须好,而且对流量的变化也是非常灵敏的
  • 限流应该有个手动的开关,这样在应急的时候,可以手动操作
  • 当限流发生时,应该有个监控事件通知。
  • 当限流发生时,对于拒掉的请求,我们应该返回一个特定的限流错误码。
  • 限流应该让后端的服务感知到。

50 弹力设计篇之“降级设计”

降级设计(Degradation) 降级时,一般会牺牲掉:

  • 降低一致性:从强一致性变成最终一致性
    • 使用异步简化流程
    • 降低数据的一致性
      • 缓存,或是去掉数据
  • 停止次要功能:
  • 简化功能

降级设计要点

在设计降级的时候,需要清楚地定义好降级的关键条件,如吞吐量过大、响应时间过慢、失败次数多过,有网络或是服务故障,等等,然后做好相应的应急预案。这些预案最好是写成代码可以快速地自动化或半自动化执行的。

参考文章

-[缓存更新的套路]

51 弹力设计篇之“弹力设计总结”

弹力设计总结:

  • 冗余服务
    • 服务发现
    • 负载均衡
    • 动态路由
    • 健康检查
  • 服务解耦
    • 水平:业务/用户分片分区
    • 垂直:异步通讯机制
    • 服务的编排与聚合:工作流
      • 像 Spring 的 Stream 或 Akka 的 flow 或是 AWS 的 Simple Workflow
    • 一致性问题:业务补偿机制
  • 服务容错
    • 重试机制
    • 熔断,限流,降级

弹力设计开发和运维

  • 一个是像 APM 这样的服务监控
  • 服务调度的系统,如:Docker + Kubernetes。

参考文章

52 管理设计篇之“分布式锁”

常见实现思路:

  • 数据库
  • redis
  • zookeeper

分布式锁的特点:

  • 安全性(Safety):排他性
  • 避免死锁
  • 容错性

分布式锁的一个问题

引入版本号,用于排他

从乐观锁到cas

数据库中也保留着版本号,那么完全可以用数据库来做这个锁服务 使用数据版本(Version)记录机制,即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现的。当读取数据时,将 version 字段的值一同读出,数据每更新一次,对此 version 值加一。 ——fence token 使用版本号/fence token ,就不需要再使用分布式锁了

注意:需要分清楚:我是用来修改某个共享源的,还是用来不同进程间的同步或是互斥的。如果使用 CAS 这样的方式(无锁方式)来更新数据,那么我们是不需要使用分布式锁服务的,而后者可能是需要的。所以,这是我们在决定使用分布式锁服务前需要考虑的第一个问题——我们是否需要?

参考文章

53 管理设计篇之“配置中心”

配置中心的设计

  • 静态配置
  • 动态配置
    • 按不同维度来区分:
      • 按运行环境分:开发、测试、预发、生产环境
      • 按依赖区分:依赖配置,不依赖的配置
      • 按层次分:IaaS, PaaS, SaaS

配置中心的模型

模型:key/value

分成3层:

  • OS层/平台层配置: 专门的运维/架构师负责
    • 最好有模板来初始化全套的参数
    • 外部依赖的配置并不适合放在配置中心里,而最好是由服务发现系统来提供。
  • 注意不同环境中配置的区别
    • 日志:开发/测试环境是Debug级,生产环境是Warning/Error
  • 版本管理
    • 记录每次修改差异
    • 配置管理工具

配置中心的架构

注意这些问题:

  • 为什么不直接 Pub 数据过去,还要订阅方反向拉数据?
  • 配置变更控制器部署在哪里?是在每个服务器上呢,还是在一个中心的地方?
  • 平台层的配置变更,有的参数是在服务启动的命令行上,这个怎么变更呢?
  • 操作系统的配置变更和平台层的配置变更最好模块化掉,就像云服务中的不同尺寸的主机型号一样。
  • 应用服务配置更新的标准化

配置中心的设计重点

笔记略,见原文。

54 管理设计篇之“边车模式”

边车模式设计

边车模式/搭档模式/伴侣模式/跟班模式 ——将控制和逻辑分离和解耦

实现方式:

  • SDK、Lib 或 Framework
    • 对应用有侵入
    • 受应用的编程语言和技术限制
    • 软件包升级时需重新编译打包
  • 通过像 Sidecar 这样的方式,在运维时与真实的应用服务集成起来
    • 对应用服务没有侵入性,并且不用受到应用服务的语言和技术的限制,而且可以做到控制和逻辑的分开升级和部署
    • 增加了每个应用服务的依赖性,也增加了应用的延迟,并且也会大大增加管理、托管、部署的复杂度

55 管理设计篇之“服务网格”

什么是 Service Mesh

  • Service Mesh 是一个基础设施。
  • Service Mesh 是一个轻量的服务通讯的网络代理。
  • Service Mesh 对于应用服务来说是透明无侵入的。
  • Service Mesh 用于解耦和分离分布式系统架构中控制层面上的东西。

Service Mesh 相关的开源软件

  • Istio 和 Linkerd
    • 可以在k8s中集成
  • Conduit
    • 由 Rust 和 Go 写成,比 Linkerd 更快,还轻,更简单

陈皓推荐:用Rust/Go 语言实现的 lstio 和 Conduit

lstio 是目前最主流的解决方案

Service Mesh的设计重点

参考文章

56 管理设计篇之“网关模式”

网关模式设计

网关需要有如下功能:

  • 请求路由
  • 服务注册
  • 负载均衡
  • 弹力设计
  • 安全方面 此外,还可以做:
  • 灰度发布
  • API 聚合
  • API 编排

Gateway、Sidecar 和 Service Mesh

网关的设计重点

  • 高性能
  • 高可用
    • 集群化
    • 服务化
    • 持续化
  • 高扩展

运维方面,应遵循如下设计原则:

  • 业务松耦合,协议紧耦合
  • 应用监视,提供分析数据
  • 用弹力设计保护后端服务
  • DevOps

安全方面:

  • 加密数据
  • 校验用户的请求
  • 检测异常访问

58 性能设计篇之“缓存”

一般来说,只要小心维护好,数据库四种操作(select、update、insert 和 delete)中的三个写操作 insert、update 和 delete 不太会出现性能问题(insert 一般不会有性能问题,update 和 delete 一般会有主键,所以也不会太慢)。除非索引建得太多,而数据库里的数据又太多,这三个操作才会变慢。

一般来说,缓存有以下三种模式:

  • Cache Aside 更新模式
  • Read/Write Through 更新模式
  • Write Behind Caching 更新模式

Cache Aside 更新模式

最常用的设计模式

  • 失效:应用程序先从 Cache 取数据
  • 命中:应用程序从 Cache 中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效

注意:此处,在更换操作时,并没有在完成更新后将其写入缓存,是怕两个并发的写操作导致脏数据。 参见帖子:Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?

Cache Aside理论上有并发问题: 比如,一个是读操作,但是没有命中缓存,就会到数据库中取数据。而此时来了一个写操作,写完数据库后,让缓存失效,然后之前的那个读操作再把老的数据放进去,所以会造成脏数据

但实际上发生概率非常低。因为这个条件需要发生在读缓存时缓存失效,而且有一个并发的写操作。实际上数据库的写操作会比读操作慢得多,而且还要锁表,读操作必须在写操作前进入数据库操作,又要晚于写操作更新缓存,所有这些条件都具备的概率并不大。

Read/Write Through 更新模式

把更新数据库(repository)的操作由缓存自己代理

  • Read Through
    • 在查询操作中更新缓存. Cache Aside由调用方更新缓存,Read Through缓存服务自己来加载
  • Write Through
    • 在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后由 Cache 自己更新数据库(这是一个同步操作)。

Write Behind Caching 更新模式

Write Behind 又叫 Write Back 在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的 I/O 操作飞快无比(因为直接操作内存嘛)。因为异步,Write Back 还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。 但其带来的问题是,数据不是强一致性的,而且可能会丢失(我们知道 Unix/Linux 非正常关机会导致数据丢失,就是因为这个事)。

软件设计从来都是 trade-off(取舍)

缓存设计的重点

  • 缓存的好坏要看命中率
  • 缓存是通过牺牲强一致性来提高性能的,并不是所有的业务都适合用缓存,要调研好需求
  • 缓存数据的时间周期也需要好好设计,太长太短都不好
  • 使用缓存的时候,一般会使用 LRU 策略
    • 对于 LRU 的缓存系统来说,其需要在 key-value 这样的非顺序的数据结构中维护一个顺序的数据结构,并在读缓存时,需要改变被访问数据在顺序结构中的排位。于是,我们的 LRU 在读写时都需要加锁(除非是单线程无并发),因此 LRU 可能会导致更慢的缓存存取的时间。这点要小心。
  • 要小心爬虫爬网站时,将缓存热点数据挤出去。一般来说,我们需要有一个爬虫保护机制,或是我们引导这些人去使用我们提供的外部 API。在那边,我们可以有针对性地做多租户的缓存系统

59 能设计篇之“异步处理”

异步系统所带来的好处——让系统可以统一调度。

异步处理的设计

事件溯源

Event Sourcing(事件溯源):主要想解决的问题是,我们可以看到数据库中的一个数据的值(状态),但我们完全不知道这个值是怎么得出来的。

异步处理 + 事件溯源的方式,可以很好地让我们的整个系统进行任务的统筹安排、批量处理,可以让整体处理过程达到性能和资源的最大化利用。 Event Sourcing Example

异步处理的分布式事务

要达到最终一致性,我们需要有个交易凭证。注意:

  • 注意凭证的保存
  • 凭证处理的幂等性问题
  • 如果事务完成不了,需要做补偿事务处理

异步处理的设计要点

异步处理中的事件驱动和事件溯源是两个比较关键的技术。

并不是所有的业务都可以用异步的方式,比如一些需要强一致性的业务,使用异步的方式可能就不适合,这里需要我们小心地分析业务。

60 性能设计篇之“数据库扩展”

数据库扩展方法

  • 读写分离
    • 写库有单点故障问题,并且数据库同步不实时,需要强一致性的读写操作还是需要落在写库上
  • CQRS
    • Command and Query Responsibility Segregation
    • 命令与查询职责分离:一个应用的操作可以分成两种,一种是 Command 也就是我们的写操作(增,删,改),另一种是 Query 操作(查)
      • 命令 Command 不会返回结果数据,只会返回执行状态,但会改变数据。
      • 查询 Query 会返回结果数据,但是不会改变数据,对系统没有副作用。
  • 分库分表 Sharding
    • 关于分库的策略
    • 关于数据访问层
      • 数据路由
      • 分片策略一般采用:
        • 按多租户的方式
        • 按数据的种类来分
        • 通过范围来分
        • 通过哈希散列算法来分
      • 应考虑应用程序的业务要求及其数据使用模式
        • 数据库分片必须考虑业务,从业务的角度入手,而不是从技术的角度入手
        • 请只考虑业务分片。请不要走哈希散列的分片方式

在一个单体的库上做读写分离或是做分片都是一件治标不治本的事,真正治本的方法就是要和服务一起拆解。

61 性能设计篇之“秒杀”

秒杀的技术挑战

大量请求涌入

  • 带宽
  • 所有请求集中在同一条数据库记录中

秒杀的解决方案

使用CDN

CDN节点按照一定概率,截留大部分请求,直接返回秒杀结束 剩下的请求再发给数据中心,进一步抢

可以使用 CDN 的边缘结点来扛流量,然后过滤用户请求(限流用户请求),来保护数据中心的系统

对于双十一的场景

双十一:尽可能多地收订单,但又不能超过库存 需要认认真真地做高并发的架构和测试了,需要各个系统把自己的性能调整上去,还要小心地做性能规划,更要把分布式的弹力设计做好,最后是要不停地做性能测试,找到整个架构的系统瓶颈,然后不断地做水平扩展,以解决大规模的并发。

12306的场景:

  • 分时间段放票
  • 预售,也是一个思路

62 边缘计算

边缘计算的适用场景:

  • 处理一些实时响应的业务
  • 处理一些简单的业务逻辑
  • 收集并结构化数据
  • 实时设备监控
  • P2P 的一些去中心化的应用
  • 云资源调度
  • 云资源聚合

边缘计算的关键技术:

  • API Gateway
  • Serverless/FaaS

95 | 高效学习:端正学习态度

我的感触: 1、学习要不断坚持 2、纸上得来终觉浅,绝知此事要躬行。尤其编程,更是如此。

97 | 高效学习:深度,归纳和坚持实践

学习模板

左耳朵耗子学新技术时的学习模板:

  1. 这个技术出现的背景、初衷和要达到什么样的目标或是要解决什么样的问题。
  2. 这个技术的优势和劣势分别是什么,或者说,这个技术的 trade-off 是什么。
  3. 这个技术适用的场景
  4. 技术的底层原理和关键实现
  5. 已有的实现和它之间的对比

举一反三

  1. 联想能力
  2. 抽象能力
  3. 自省能力

98 | 高效学习:如何学习和阅读代码

书和文档是人对人说的话,代码是人对机器说的话(注:代码中有一部份逻辑是控制流程的逻辑,不是业务逻辑)。所以:

  1. 如果你想知道人为什么要这么搞,那么应该去看书(像 Effective C++、Code Complete、Design Pattern、Thinking in Java 等),看文档。
  2. 如果你要知道让机器干了什么?那你应该看代码!(就像 Linus 去看 zlib 的代码来找性能问题。)

关键在于目的:

  1. 如果你想了解一种思想,一种方法,一种原理,一种思路,一种经验,恐怕,读书和读文档会更有效率一些
  2. 如果你想了解的就是具体细节,比如某协程的实现,某个模块的性能,某个算法的实现,那么你还是要去读代码的

99 | 高效学习:面对枯燥和量大的知识

如何面对大量的知识

在学习时,一定不要学在表面上,一定要学到本质,学到原理上,那些东西是不容易变的 带着问题去学习 把你学习的心得、过程、笔记、代码分享出来

认真阅读文档

其他学习方式

  • 用不同的方式来学习同一个东西
  • 不要被打断
  • 总结压缩信息
  • 把未知关联到已知
  • 用教的方式来学习
  • 学以致用
  • 不要记忆
  • 多犯错误

100 | 高效沟通:Talk和Code同等重要

有效的沟通是事业成功的必要条件。 沟通要有反馈,保证双方理解一致,并达成共识。 可以事先约定一些术语,帮助沟通。

103 | 高效沟通:沟通技术

逻辑

逻辑能力一定要强

信息

信息要全面、准确。

维度

在和人争论时,如果要反驳,那一定是低维度反驳,越细节越好。而在说服对方时,则要在高维度说服对方,越宏观越好,比如从公司的大目标出发。高维度讲究的是求同存异。你跟别人相同的东西一定是高维度的,这就是大同,而你跟别人不同的一定是非常细节的东西。大同的东西,更容易让人产生共鸣,从而容易达成默契和共识。

能够站在更高的维度来沟通是我们需要努力的目标。

共同

共情,共享,共利,共识以及换位思考。

104 | 高效沟通:好老板要善于提问

引导,用提问的方式,“倒逼”员工找到答案,从而提高员工的参与感和成就感。 倾听,心态平和,毫无偏见,全面接收和理解对方的信息,而不是只听自己想听的信息。 共情,换位思考,站在对方立场设身处地思考和处理问题,动之以情,晓之以理。 高维,提升自己的格局观,能从全局利益、长远利益思考问题,解决问题。 反馈,建立反馈机制,及时发现问题、解决问题,形成正向循环。

05 | 何为技术领导力?

什么是技术领导力?

技术领导力是:

  • 尊重技术,追求核心基础技术。
  • 追逐自动化的高效率的工具和技术,同时避免无效率的组织架构和管理。
  • 解放生产力,追逐人效的提高。
  • 开发抽象和高质量的可以重用的技术组件。
  • 坚持高于社会主流的技术标准和要求。

如何拥有技术领导力?

  • 能够发现问题。
  • 能够提供解决问题的思路和方案,并能比较这些方案的优缺点。
  • 能够做出正确的技术决定。
  • 能够用更优雅,更简单,更容易的方式来解决问题。
  • 能够提高代码或软件的扩展性、重用性和可维护性。
  • 能够用正确的方式管理团队。
  • 创新能力。

怎样让自己拥有领导力?

  • 扎实的基础技术;
  • 非同一般的学习能力;
  • 坚持做正确的事;
  • 不断提高对自己的要求标准;

06 | 如何才能拥有技术领导力?

  • 第一,你要吃透基础技术。基础技术是各种上层技术共同的基础。
    • 编程和系统。
  • 第二,提高学习能力。所谓学习能力,就是能够很快地学习新技术,又能在关键技术上深入的能力。
    • 学习的信息源。
    • 与高手交流。
    • 举一反三的思考。
    • 不怕困难的态度。
    • 开放的心态。
  • 第三,坚持做正确的事。做正确的事,比用正确的方式做事更重要,因为这样才始终会向目的地靠拢。
    • 提高效率的事。
    • 自动化的事。
    • 掌握前沿技术的事。
    • 知识密集型的事。
    • 技术驱动的事。
  • 第四,高标准要求自己。只有不断地提高标准 ,你才可能越走越高,所以,要以高标准要求自己,不断地反思、总结和审视自己,才能够提升自己。
    • Google 的自我评分卡。
    • 敏锐的技术嗅觉。
    • 强调实践,学以致用。
    • Lead by Example。

09 | 答疑解惑:渴望、热情和选择

加班太严重完全没有时间学习,怎么办? 时间一定是能找得到的,关键还是看你的渴望程度和热情。只要你真心想把事儿做成,你就一定能想出各种各样的招儿来挤出时间。

为什么你能够写出这么多东西? 第一个阶段,是学习的阶段。 第二个阶段,是有利益驱动的阶段。 第三个阶段,是记录自己观点打自己脸的阶段。 第四个阶段,是与他人交互的阶段。

陈皓的建议:

  1. 客观地审视自己。
  2. 确定自己想要什么。
  3. 注重长期的可能性,而不是短期的功利。
  4. 尽量关注自己会得到的东西,而不是自己会失去的东西。
  5. 不要和大众的思维方式一样。

10 | 如何成为一个大家愿意追随的Leader?

Leader 和 Boss 的不同

Boss 是驱动员工,Leader 是指导员工。 Boss 制造畏惧,Leader 制造热情。 Boss 面对错误喜欢使用人事惩罚的手段,而 Leader 面对错误喜欢寻找解决问题的技术或管理方法。 Boss 只是知道怎么做,而 Leader 则是展示怎么做。 Boss 是用人,而 Leader 是发展人。 Boss 从团队收割成绩,而 Leader 则是给予团队成绩。 Boss 喜欢命令和控制( Command + Control ),而 Leader 喜欢沟通和协作( Communication + Cooperation )。 Boss 喜欢说“给我上”,而 Leader 喜欢说“跟我上”。

如何成为众人愿意追随的 Leader

帮人解决问题。 被人依赖。

一些比较关键的除了技术领导力之外的一个 Leader 需要的素质:

  • 赢得他人的信任。
  • 开放的心态 + 倾向性的价值观。
  • Lead by Example。
  • 保持热情和冲劲。
  • 能够抓住重点,看透事物的本质。
  • 描绘令人激动的方向,提供令人向住的环境。
  • 甘当铺路石,为他人创造机会。