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,就有可能读不到或者脏读。所以具体把读写分离放在哪一层,还是要根据业务类型和实际情况来决定。数据库读写分离及问题
标签:rpc subject proxy ati 手机 email 类型 cti 简介