常见的设计思路

设计并无定规,但是成功的设计总有值得借鉴的地方。
计算机科学博大精深,其中包含了大量优秀的设计理念,近期学习偶有所获,在此记下。

缓存

缓存在IT技术中非常常见,它的定义是什么?
我认为缓存技术的初衷是解决了两个问题:

  1. 减小计算机CPU运算速度与存储介质IO速度的巨大差异
  2. 存放中间运算结果
    除了最初的cpu与内存间的缓存,陆续又涌现出内存与硬盘间、客户端与浏览器端的缓存。
    但是作用无外乎这两种:
  3. 减小不同部件之间的处理速度差异
  4. 暂时存放结果
    而目的只有一个:加快处理速度,减少响应时间。
    原理则是空间换时间,在IO速度快的存储介质上保存冗余的数据备份,就可以避免读低速的存储介质或者重复复杂的运算。
    缓存有2个基本问题需要解决:
  5. 缓存容量有限
    缓存一般采用IO快的存储介质,容量相对较小,一般存不了所有数据,缓存满了怎么办?很简单,清除掉部分数据就可以了,但是清除哪些比较合适呢?
    常见的策略有:
    • FIFO:先进先出,淘汰年龄最大的数据 (队列)
    • LRU:Least Recently Used,淘汰最近没用过的 (链表)
    • LFU:Least Frequently Used,淘汰使用频率最低的 (SkipList)
      根据实际场景选择适合的策略。
  6. 数据的时效性
    数据可能会变更,导致缓存失效。相应的,缓存有失效策略:
    • 过期失效
      设置过期时间,不使用过期数据,比如浏览器缓存、Redis
    • 缓存更新
      • Cache Aside 更新操作完成后,使缓存失效
      • Read/Write Through 缓存代理数据读写
      • Write Behind 异步批量更新数据库,性能高,逻辑复杂,牺牲强一致性

并发情况下可能出现的问题:

  1. 缓存并发
    防止并发未命中时重复建立缓存。
    思路: 缓存未命中时,tryLock(key),上锁成功的线程通过查询or计算更新缓存,否则block。
    java中可缓存future对象,防止重复运算。
  2. 缓存穿透
    防止反复查询不存在的值。可缓存空值
  3. 缓存批量过期导致雪崩

    锁解决的问题:保证并发情况下数据一致性。
    应用程序通常采用 CAS或锁机制保证数据一致性,CAS底层采用系统命令,硬件层锁内存总线。
    JVM通过Compare and Swap修改对象头中的锁状态实现加锁。
    数据库中将锁分为共享锁和互斥锁,实现读写分离。

池的作用:管理并重复利用资源

  1. 资源创建、销毁开销比较大
  2. 资源无限制的创建会导致系统崩溃
    常见的有:线程池、数据连接池

管道

负载均衡