zoukankan      html  css  js  c++  java
  • 数据库读写分离及问题(转)

      对于数据存储层高并发问题,最先想到的可能就是读写分离,在网站访问量大并且读写不平均的情况下,将存储分为master,slave两台,所有的写都路由到master上,所有的读都路由到slave上,然后master和slave同步。如果一台salve不够,可以加多台,比如一台master,3台slave。对于什么是读写分离,以及读写分离有什么好处,这里不再叙述,有兴趣的可以参考这里 。 
    在设计读写分离的时候,有几种解决方案:
        1. 将读写分离放在dao层,在dao层, 所有的insert/update/delete都访问master库,所有的select 都访问salve库,这样对于业务层是透明的。 
        2. 将读写分离放在ORM层,比如mybatis可以通过mybatis plus拦截sql语句,所有的insert/update/delete都访问master库,所有的select 都访问salve库,这样对于dao层都是透明。 
        3. 放在代理层,比如MySQL-Proxy,这样针对整个应用程序都是透明的。 
    对于绝大多数情景,读写分离都适用,但是读写分离有一个问题是master slave同步,这个同步是会有一定延迟,因此有几个场景还是要注意的:
    考虑下面一个用户注册场景。 
     
    复制代码

    boolean addUserSuccess = userDao.addUser(registUser);
    if (addUserSuccess) {
    cachedThreadPool.execute(()
    -> {
    EmailService.SendActivateEmail(userId);
    });
    }

    复制代码
    复制代码
    public static void SendActivateEmail(int userId) {
    User user
    = userDao.getUser(userId);
    String userName
    = user.getUserName();
    String userEmail
    = user.getUserEmail();
    String subject
    = "...";
    String body
    = "...";
    //调用邮件服务发邮件

    }

    复制代码
    如上,当新用户注册,在注册完成后,会发一封激活邮件,新增用户是insert,发邮件获取用户是select ,如果master slave存在延迟,有可能在这个时候获取不到用户。
    对于分布式服务系统,都会有一些独立的子服务,比如用户服务,订单服务,这些服务通过http或者rpc访问, 如下是用户服务下的两个接口。 
    userservice.xx.com/user/userinfo       post请求,用户新增或者更新用户
    userservice.xx.com/user/userinfo/1    get请求,用于获取用户信息
    这是两个接口服务,一个用于更新用户数据,一个用户获取数据,如果手机APP有一个操作是修改用户名,在调用更新用户接口修改用户名后,紧接着调用获取用户信息的接口,两个请求间隔短,而如果同步延迟,就有可能读取到脏数据。 
    对于上面第一种情况
    1. 我们在可以在dao的方法中,再加一个参数,比如:
    public User getUser(int userId, boolean isMaster)
    业务层决定要在哪个库操作。
    2. 根据具体的业务类型,将读写标志位放到线程上下文中。比如对于注册用户的操作,可以在开始处理的时候,在线程上下文中放入一个标志位master,在所有的dao 方法内,判断该标志位,如果是master,则从master读取。这样读写分离是由具体的业务场景决定的。 
    对于上面第二种情况
    1. 简单的方法,就是在请求参数中再加一个参数,服务端根据参数决定要在哪个库操作, 这样增加了前端的一些工作量。
    2. 复杂一点的,可以在服务端处理,当修改了用户信息后,可以在redis或者memcache中新增一条Id记录,5秒过期,每次请求的时候,先到memcache中判断一下,对应的id是否存在,如果存在读master, 否则slave。只是这样无形之中增加了服务端的开销。 
    其实数据延迟没有那么严重,基本都是秒级的,对于上面第二个场景,可能两个请求来回,数据就已经同步好。不会出现脏读的情况,但是在一些特殊的场景下,比如网络抖动,新加字段,可能数据同步延迟会变大,此时master slave的数据会出现不一致,而如果业务上出现上面的两种情景,即insert/update/delete后立刻select,就有可能读不到或者脏读。所以具体把读写分离放在哪一层,还是要根据业务类型和实际情况来决定。
     
  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    block的是发送信号的线程,又不是处理槽函数的线程
  • 原文地址:https://www.cnblogs.com/jpfss/p/8116466.html
Copyright © 2011-2022 走看看