Redis和数据库的结合
使用 Redis 可以?xún)?yōu)化性能,但是存在 Redis 的數(shù)據(jù)和數(shù)據(jù)庫(kù)同步的問(wèn)題,這是我們需要關(guān)注的問(wèn)題。假設(shè)兩個(gè)業(yè)務(wù)邏輯都是在操作數(shù)據(jù)庫(kù)的同一條記錄,而 Redis 和數(shù)據(jù)庫(kù)不一致。
Redis 和數(shù)據(jù)庫(kù)不一致
在圖中,T1 時(shí)刻以鍵 key1 保存數(shù)據(jù)到 Redis,T2 時(shí)刻刷新進(jìn)入數(shù)據(jù)庫(kù),但是 T3 時(shí)刻發(fā)生了其他業(yè)務(wù)需要改變數(shù)據(jù)庫(kù)同一條記錄的數(shù)據(jù),但是采用了 key2 保存到 Redis 中,然后又寫(xiě)入了更新數(shù)據(jù)到數(shù)據(jù)庫(kù)中,此時(shí)在 Redis 中 key1 的數(shù)據(jù)是臟數(shù)據(jù),和數(shù)據(jù)庫(kù)的數(shù)據(jù)并不一致。
而上圖只是數(shù)據(jù)不一致的一個(gè)可能的原因,實(shí)際情況可能存在多種,比如數(shù)據(jù)庫(kù)的事務(wù)是完善的,而對(duì)于 Redis 的事務(wù),通過(guò)學(xué)習(xí)應(yīng)該清楚它并不是那么嚴(yán)格的,如果發(fā)生異常回滾的事件,那么 Redis 的數(shù)據(jù)可能就和數(shù)據(jù)庫(kù)不太一致了,所以要保存數(shù)據(jù)的一致性是相當(dāng)困難的。
但是不用沮喪,因?yàn)榛ヂ?lián)網(wǎng)系統(tǒng)顯示給用戶(hù)的信息往往并不需要完全是“最新的”,有些數(shù)據(jù)允許延遲。舉個(gè)例子,一個(gè)購(gòu)物網(wǎng)站會(huì)有一個(gè)用戶(hù)購(gòu)買(mǎi)排名榜,如果做成實(shí)時(shí)的,每一筆投資都會(huì)引發(fā)重新計(jì)算,那么網(wǎng)站的性能就存在極大的壓力,但是這個(gè)排名榜卻沒(méi)有太大的意義。
同樣,商品的總數(shù)有時(shí)候只需要去實(shí)現(xiàn)一個(gè)非實(shí)時(shí)的數(shù)據(jù)。這些在互聯(lián)網(wǎng)系統(tǒng)中也是十分常見(jiàn)的,一般而言,可以在某段時(shí)間進(jìn)行刷新(比如以一個(gè)小時(shí)為刷新間隔),排出這段時(shí)間的最新排名,這就是延遲性的更新。但是對(duì)于一些內(nèi)容則需要最新的,尤其是當(dāng)前用戶(hù)的交易記錄、購(gòu)買(mǎi)時(shí)商品的數(shù)量,這些需要實(shí)時(shí)處理,以避免數(shù)據(jù)的不一致,因?yàn)檫@些都是對(duì)于企業(yè)和用戶(hù)重要的記錄。
我們會(huì)考慮讀/寫(xiě)以數(shù)據(jù)庫(kù)的最新記錄為主,并且同步寫(xiě)入 Redis,這樣數(shù)據(jù)就能保持一致性了,而對(duì)于一些常用的只需要顯示的,則以查詢(xún) Redis 為主。。
Redis 和數(shù)據(jù)庫(kù)讀操作
數(shù)據(jù)緩存往往會(huì)在 Redis 上設(shè)置超時(shí)時(shí)間,當(dāng)設(shè)置 Redis 的數(shù)據(jù)超時(shí)后,Redis 就沒(méi)法讀出數(shù)據(jù)了,這個(gè)時(shí)候就會(huì)觸發(fā)程序讀取數(shù)據(jù)庫(kù),然后將讀取的數(shù)據(jù)庫(kù)數(shù)據(jù)寫(xiě)入 Redis(此時(shí)會(huì)給 Redis 重設(shè)超時(shí)時(shí)間),這樣程序在讀取的過(guò)程中就能按一定的時(shí)間間隔刷新數(shù)據(jù)了。
讀取數(shù)據(jù)的流程
下面寫(xiě)出這個(gè)流程的代碼:
public DataObject readMethod(args) {// 嘗試從Redis中讀取數(shù)據(jù)DataObject data = getRedis(key);if(data != null) {// 讀取數(shù)據(jù)返回為空,失敗// 從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)data = getFromDataBase();// 重新寫(xiě)入Redis,以便以后讀出writeRedis(key,data);// 設(shè)置Redis的超時(shí)時(shí)間為5分鐘setRedisExpire(key,5);}return data; }上面的代碼完成了圖中所描述的過(guò)程。這樣每當(dāng)讀取 Redis 數(shù)據(jù)超過(guò) 5 分鐘,Redis 就不能讀到超時(shí)數(shù)據(jù)了,只能重新從 Redis 中讀取,保證了一定的實(shí)時(shí)性,也避免了多次訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)造成的系統(tǒng)性能低下的情況。
Redis 和數(shù)據(jù)庫(kù)寫(xiě)操作
寫(xiě)操作要考慮數(shù)據(jù)一致的問(wèn)題,尤其是那些重要的業(yè)務(wù)數(shù)據(jù),所以首先應(yīng)該考慮從數(shù)據(jù)庫(kù)中讀取最新的數(shù)據(jù),然后對(duì)數(shù)據(jù)進(jìn)行操作,最后把數(shù)據(jù)寫(xiě)入 Redis 緩存中。
寫(xiě)入數(shù)據(jù)的流程
寫(xiě)入業(yè)務(wù)數(shù)據(jù),先從數(shù)據(jù)庫(kù)中讀取最新數(shù)據(jù),然后進(jìn)行業(yè)務(wù)操作,更新業(yè)務(wù)數(shù)據(jù)到數(shù)據(jù)庫(kù)后,再將數(shù)據(jù)刷新到 Redis 緩存中,這樣就完成了一次寫(xiě)操作。
下面寫(xiě)出這個(gè)流程的代碼:
public DataObject writeMethod(args) {//從數(shù)據(jù)庫(kù)里讀取最新數(shù)據(jù)DataObject dataObject = getFromDataBase(args);//執(zhí)行業(yè)務(wù)邏輯ExecLogic(dataObject);//更新數(shù)據(jù)庫(kù)數(shù)據(jù)updateDataBase(dataObject);//刷新Redis緩存updateRedisData(dataObject); }上面的代碼完成了圖中所描述的過(guò)程。首先,從數(shù)據(jù)庫(kù)中讀取最新的數(shù)據(jù),以規(guī)避緩存中的臟數(shù)據(jù)問(wèn)題,執(zhí)行了邏輯,修改了部分業(yè)務(wù)數(shù)據(jù)。然后,把這些數(shù)據(jù)保存到數(shù)據(jù)庫(kù)里,最后,刷新這些數(shù)據(jù)到 Redis 中。
總結(jié)
以上是生活随笔為你收集整理的Redis和数据库的结合的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MySQL 正则表达式查询
- 下一篇: Jsoup设置元素的文本内容