温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何解决项目实际应用中redis缓存与数据库一致性问题

发布时间:2021-11-03 11:21:26 来源:亿速云 阅读:180 作者:小新 栏目:编程语言

这篇文章将为大家详细讲解有关如何解决项目实际应用中redis缓存与数据库一致性问题,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

一、       问题解决思路

能否做到先发出的请求一定先执行完成呢?常见的思路是“串行化”

如何解决项目实际应用中redis缓存与数据库一致性问题

上图是一个service服务的上下游及服务内部详细展开,细节如下:

1service的上游是多个业务应用,上游发起请求对同一个数据并发的进行读写操作,上例中并发进行了一个uid=1的余额修改(写)操作与uid=1的余额查询(读)操作

2service的下游是数据库DB,假设只读写一个DB

3)中间是服务层service,它又分为了这么几个部分

3.1)最上层是任务队列

3.2)中间是工作线程,每个工作线程完成实际的工作任务,典型的工作任务是通过数据库连接池读写数据库

3.3)最下层是数据库连接池,所有的SQL语句都是通过数据库连接池发往数据库去执行的

 

工作线程的典型工作流是这样的:

void work_thread_routine(){

    Task t = TaskQueue.pop(); // 获取任务

    // 任务逻辑处理,生成sql语句

    DBConnection c = CPool.GetDBConnection(); // DB连接池获取一个DB连接

    c.execSQL(sql); // 通过DB连接执行sql语句

    CPool.PutDBConnection(c); // DB连接放回DB连接池

}

 

提问:任务队列其实已经做了任务串行化的工作,能否保证任务不并发执行?

答:不行,因为

11个服务有多个工作线程,串行弹出的任务会被并行执行

21个服务有多个数据库连接,每个工作线程获取不同的数据库连接会在DB层面并发执行

 

提问:假设服务只部署一份,能否保证任务不并发执行?

答:不行,原因同上

 

提问:假设1个服务只有1条数据库连接,能否保证任务不并发执行?

答:不行,因为

11个服务只有1条数据库连接,只能保证在一个服务器上的请求在数据库层面是串行执行的

2)因为服务是分布式部署的,多个服务上的请求在数据库层面仍可能是并发执行的

 

提问:假设服务只部署一份,且1个服务只有1条连接,能否保证任务不并发执行?

答:可以,全局来看请求是串行执行的,吞吐量很低,并且服务无法保证可用性

 

完了,看似无望了,

1)任务队列不能保证串行化

2)单服务多数据库连接不能保证串行化

3)多服务单数据库连接不能保证串行化

4)单服务单数据库连接可能保证串行化,但吞吐量级低,且不能保证服务的可用性,几乎不可行,那是否还有解?

 

退一步想,其实不需要让全局的请求串行化,而只需要“让同一个数据的访问能串行化”就行。

在一个服务内,如何做到“让同一个数据的访问串行化”,只需要“让同一个数据的访问通过同一条DB连接执行”就行。

如何做到“让同一个数据的访问通过同一条DB连接执行”,只需要“在DB连接池层面稍微修改,按数据取连接即可”

获取DB连接的CPool.GetDBConnection()【返回任何一个可用DB连接】改为

CPool.GetDBConnection(longid)【返回id取模相关联的DB连接】

 

这个修改的好处是:

1)简单,只需要修改DB连接池实现,以及DB连接获取处

2)连接池的修改不需要关注业务,传入的id是什么含义连接池不关注,直接按照id取模返回DB连接即可

3)可以适用多种业务场景,取用户数据业务传入user-id取连接,取订单数据业务传入order-id取连接即可

这样的话,就能够保证同一个数据例如uid在数据库层面的执行一定是串行的

 

稍等稍等,服务可是部署了很多份的,上述方案只能保证同一个数据在一个服务上的访问,在DB层面的执行是串行化的,实际上服务是分布式部署的,在全局范围内的访问仍是并行的,怎么解决呢?能不能做到同一个数据的访问一定落到同一个服务呢?

 

能否做到同一个数据的访问落在同一个服务上?

 

上面分析了服务层service的上下游及内部结构,再一起看一下应用层上下游及内部结构

如何解决项目实际应用中redis缓存与数据库一致性问题

上图是一个业务应用的上下游及服务内部详细展开,细节如下:

1)业务应用的上游不确定是啥,可能是直接是http请求,可能也是一个服务的上游调用

2)业务应用的下游是多个服务service

3)中间是业务应用,它又分为了这么几个部分

3.1)最上层是任务队列【或许web-server例如tomcat帮你干了这个事情了】

3.2)中间是工作线程【或许web-server的工作线程或者cgi工作线程帮你干了线程分派这个事情了】,每个工作线程完成实际的业务任务,典型的工作任务是通过服务连接池进行RPC调用

3.3)最下层是服务连接池,所有的RPC调用都是通过服务连接池往下游服务去发包执行的

 

工作线程的典型工作流是这样的:

voidwork_thread_routine(){

Task t = TaskQueue.pop(); // 获取任务

// 任务逻辑处理,组成一个网络包packet,调用下游RPC接口

ServiceConnection c = CPool.GetServiceConnection(); // Service连接池获取一个Service连接

c.Send(packet); // 通过Service连接发送报文执行RPC请求

CPool.PutServiceConnection(c); // Service连接放回Service连接池

}

 

似曾相识吧?没错,只要对服务连接池进行少量改动:

获取Service连接的CPool.GetServiceConnection()【返回任何一个可用Service连接】改为

CPool.GetServiceConnection(longid)【返回id取模相关联的Service连接】

这样的话,就能够保证同一个数据例如uid的请求落到同一个服务Service上。

                                                                                

由于数据库层面的读写并发,引发的数据库与缓存数据不一致的问题(本质是后发生的读请求先返回了),可能通过两个小的改动解决:

1)修改服务Service连接池,id取模选取服务连接,能够保证同一个数据的读写都落在同一个后端服务上

2)修改数据库DB连接池,id取模选取DB连接,能够保证同一个数据的读写在数据库层面是串行的

关于“如何解决项目实际应用中redis缓存与数据库一致性问题”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI