Skip to content

性能优化-数据读写分离

About 769 wordsAbout 3 min

性能新书

2026-08-24

5.17 数据读写分离

大部分业务系统的数据都是写少读多的场景,为了保证并发下的读写数据安全,通常会对这些数据加锁。常见的方式是使用读写锁。另外一种提供更高效的读取数据方式采用无锁方式,即当数据更新的时候,复制一份完整数据,在复制数据上更新,最后替换为原来的数据。比如Java的CopyOnWriteArrayList 就是这种实现,如下是add操作

public boolean add(E e) {
    synchronized (lock) {
        Object[] es = getArray();
        int len = es.length;
        es = Arrays.copyOf(es, len + 1);
        es[len] = e;
        setArray(es);
        return true;
    }
}

代码3行getArray返回当前数据的引用,并在代码5行,复制一份新的数据,其容量比原来数据增加1,代码6行设置新的数据,代码7行,将原有数据的引用替换成现在的数据es。

注意到尽管代码2行加锁,但这是同步并发写操作。此处加锁不影响读操作。其get方法如下:

public E get(int index) {
    return elementAt(getArray(), index);
}
static <E> E elementAt(Object[] a, int index) {
    return (E) a[index];
}

读写分离使得高性能数据同步维护比较简单,假设你的业务系统有多种数据需要缓存,比如角色,权限,和字典表。假定设定每1小时刷新,你如何更新这些数据呢?

需要考虑,如果缓存更新过程中,用户访问这些数据,会造成数据不一致,比如后台管理系统新增角色后,角色缓存更新完毕,但权限数据还未来得及更新,那么用户访问时候读取缓存,可能出现角色信息无对应的权限信息。

比较简单的方法采用读写分离,使用新的缓存代替旧的缓存,如下,定义一个CacheManager,维护最新的缓存

public class CacheManager{
    Cache cache;
    public Cache getCache(){return cache;}
    public void setNewCache(Cache cache){this.cache=cache}
}


public class Cache{
   HashMap roleInfo =...;
   HashMap permissionInfo =...; 
   HashMap dictInfo = ......
   public void init(){}
   public HashMap getRoles(){return roleInfo;}
}

当触发缓存更新时候,可以重新创建一个Cache,并替换原有Cache

//定时调度
Cache cache = new Cache();
cache.init();
cacheManager.setNewCache(cache);

在分布式系统架构中,读写分离以并结合数据分片,是实现大流量的分布式系统高可用,高性能的战术。比如电商系统,通常把商品,库存信息存放在一个固定64分片的Redis里。如果单分片的读访问量大,负载高,则可以配置Redis从节点,读操作能路由到Redis从节点

物联网中,查询设备的状态信息,也不在查询设备的连接的网关,这是因为查询设备信息的流量非常大,设备网关应该专注于设备连接。因此把设备状态维护到Redis里,其他系统访问Redis获取设备信息

知行合一