前言
看了很多操作系统和软件中间件之类的知识之后发现,软件和硬件在大的方面有时候解决的是一样的问题
一、CPU vs 分布式系统
如果咱们把多核CPU和分布式系统进行类比,发现有一些共同点。
1 CPU
每个CPU有自己的cache(level1和level2的cache都是每个核私有的),那么每个CPU对于私有cache的操作,是不是就很像是分布式系统
1 分布式服务
对于分布式服务,仔细考究起来,其实比CPU对于cache的处理还要简单。
一主多从:限制master才可以写,follower都是进行数据同步而已
多主服务:多主服务一般都是操作各自的数据,不管是Redis集群还是mysql的分库,只有当多主节点进行变化时才需要进行数据处理。Redis集群是重新处理hash槽的分布,mysql麻烦点,可能涉及到id的变化
二、一致性
一致性问题需要解决,最大的需要解决的问题就是顺序性
1.CPU
- CPU的MESI解决了cache的一致性问题,但是其实并没有明确要求顺序性,只需要遵循状态流转就行。
- 拿Intel的芯片架构举例,为了加快cache一致性,硬件层面添加了storeBuffer,来进行异步通知
- MESI解决的cache一致性,并不包括storeBuffer里的数据
- storeBuffer可以理解为cache line的缓存,所以相当于缓存的缓存又是各自维护,不一致;
- 即便是Java的增强型volatile,通过内存屏障也只是达到了cache的可见性:具体实现方式是通过执行lock前缀指令,而Intel芯片架构对于lock前缀指令的执行会将store buffer强制和cache刷进内存,见这一篇博客,所以可以发现volatile的最佳使用场景是一写多读,单例模式的懒汉方式
- volatile之所以不是线程安全的,也就是volatile只保证了可见性,写操作并发写的时候,线程就不安全的,当利用JIT编译之后查看汇编语言可以发现,变量的写操作,一般是先把变量复制到寄存器,然后在寄存器里更改,再写回cache line(volatile变量会先写回storeBuffer)。也就是写操作是个RMW(read-modify-write)的过程,所以要保证线程安全的话,必须得通过锁,将RMW变为原子操作
2.分布式服务
- 为了避免多核CPU的缓存一致性问题,多数中间件使用的方式都是只有leader支持写,follower只支持同步,剩下需要解决的问题就是要满足强一致性还是最终一致性
- 如果是多主节点的集群,譬如Redis,通过分配hash槽(16384个),来进行数据隔离,但是可能因此带来的问题就是当发生multiGet请求的时候,会生成IO放大的问题;mysql的多主集群的话,一般也会隔离数据,当然也会有双向同步的情况,不过一般会有中心库作为总数据备份
3. SET化服务
- 现在开始流行服务set化,不再是存储层进行分布式或者叫计算层服务分布式,而是集群之间并行,集群之间互不影响,集群内部,分布式。这样的服务更加弹性化。对于LBS服务效果明显。
总结
- 软件层面需要解决的问题,其实在硬件层面早就解决过,在软件层面进行技术架构时可以进行参考。
- 不过硬件层面比软件层面又好在没有网络因素在里面,所以需要对于具体的业务或者功能有相应的取舍