在互联网公司,经常面临一个“三高”问题:
- 高并发
- 高性能
- 高可用
一、缓存
- 使用空间换时间的思想
- 代码在访问数据的时候,尽量使用缓存命中率高的方式
- 缓存之所以能够大幅提高系统的性能,关键在于二八定律:「百分之八十的数据访问是集中在 20% 的数据上」
- 缓存分类:1. 本地缓存 2. 分布式缓存(一致性 Hash 算法)
- 适合缓存的场景:1、读多写少 2、计算耗时大,且实时性不高
- 不适合缓存的场景:1、写多读少,频繁更新 2、对数据一致性要求严格 3、数据访问完全随机
- 缓存更新的策略:Cache-Aside 和 Cache-As-SoR
二、预处理和延后处理
- 预处理:比如缓存预热,通过预先处理减少了实时链路上的 RPC 调用,既减少了系统的外部依赖,也极大的提高了系统的吞吐量
- 延后处理:比如中了红包的延期到账,如果去实时的做到账,那么大概率数据库的 TPS(每秒处理的事务数) 会是瓶颈。通过产品提示,将到账操作延后处理,解决了数据库 TPS 瓶颈。延后处理还有一个非常著名的例子,COW(Copy On Write,写时复制)。
三、池化
- 后台开发过程中你一定离不开各种 「池子」: 内存池、连接池、线程池、对象池......
- 内存、连接、线程这些都是资源,创建线程、分配内存、数据库连接这些操作都有一个特征, 那就是创建和销毁过程都会涉及到很多系统调用或者网络 IO。 每次都在请求中去申请创建这些资源,就会增加请求处理耗时,但是如果我们用一个 容器(池) 把它们保存起来,下次需要的时候,直接拿出来使用,避免重复创建和销毁浪费的时间。
四、同步变异步
- 对于处理耗时的任务,如果采用同步的方式,那么会增加任务耗时,降低系统并发度。同步轮训, 这样效率显然太低了。
- 可以通过将同步任务变为异步进行优化。
- 异步,在很多编程语言中有异步编程的库,比如 C++ std::future、Python asyncio 等,但是异步编程往往需要回调函数(Callback function),如果回调函数的层级太深,这就是回调地狱(Callback hell)。回调地狱如何优化又是一个庞大的话题。
五、消息队列
- 异步
- 解耦
- 削峰
六、批量处理
- 通过合并一些频繁请求的小资源可以获得更快的加载速度。
七、数据库
- 索引
- 读写分离
- 分库分表
八、其他
- 零拷贝
mmap
sendfile
......
- 无锁化:锁的代价也是比较高的,锁会导致上线文切换,甚至被挂起直到锁被释放。
基于硬件提供的原子操作 CAS(Compare And Swap) 实现一些高性能无锁的数据结构,比如无锁队列,可以在保证并发安全的情况下,提供更高的性能。
- 序列化与反序列化:序列化解决了对象持久化和跨网络数据交换的问题。
序列化一般按照序列化后的结果是否可读,可分为以下两类:
- 文本类型:
如 JSON、XML,这些类型可读性非常好,是自解释的。也常常用在前后端数据交互上,因为接口调试,可读性高非常方便。但是缺点就是信息密度低,序列化后占用空间大。 - 二进制类型
如 Protocol Buffer、Thrift等,这些类型采用二进制编码,数据组织得更加紧凑,信息密度高,占用空间小,但是带来的问题就是基本不可读。