如何實(shí)現(xiàn)頁(yè)面廣告隨時(shí)上下線、過期自動(dòng)下線及到時(shí)自動(dòng)上線

背景引入

最近需要實(shí)現(xiàn)一個(gè)功能须床,關(guān)于頁(yè)面廣告自動(dòng)配置的铐料,如支付寶的支付完成頁(yè)。這篇隨筆是記錄對(duì)這個(gè)需求從分析到實(shí)現(xiàn)以及優(yōu)化的過程豺旬,以免以后忘記钠惩。如果你最近也要做著樣的功能,希望對(duì)你有所啟發(fā)族阅。

需求描述

某些頁(yè)面需要配置廣告或活動(dòng)宣傳圖篓跛,廣告或活動(dòng)需滿足隨時(shí)上下線、過期自動(dòng)下線及到時(shí)自動(dòng)上線坦刀。
如:現(xiàn)在時(shí)間2019-2-22 16:16:13愧沟,要在支付完成頁(yè)面配置領(lǐng)獎(jiǎng)活動(dòng),活動(dòng)要在2019-3-10 00:00:00準(zhǔn)時(shí)上線求泰,在2019-3-30 23:59:59結(jié)束活動(dòng)央渣。
所以要的效果是,在活動(dòng)上線前的任意時(shí)刻配置完活動(dòng)后渴频,頁(yè)面到時(shí)間自動(dòng)上線這個(gè)活動(dòng)芽丹。
也可能會(huì)是其他的多個(gè)活動(dòng)或廣告,每個(gè)頁(yè)面廣告的個(gè)數(shù)可變卜朗,不同上下線時(shí)間可不同拔第,其他頁(yè)面也需要實(shí)現(xiàn)這樣的功能,頁(yè)面與頁(yè)面之間的活動(dòng)不一定一樣场钉。

需求分析

需求簡(jiǎn)單的幾句話蚊俺,那么我們來具體的分析一下。

提取關(guān)鍵點(diǎn)

  • 廣告或活動(dòng)宣傳圖
  • 隨時(shí)上下線逛万、過期自動(dòng)下線及到時(shí)自動(dòng)上線
  • 每個(gè)頁(yè)面廣告的個(gè)數(shù)可變
  • 不同廣告上下線時(shí)間可不同
  • 頁(yè)面與頁(yè)面之間的活動(dòng)不一定一樣

數(shù)據(jù)庫(kù)分析

1泳猬、【廣告或活動(dòng)宣傳圖】
要為不同頁(yè)面設(shè)置不同的廣告,有的頁(yè)面廣告可能一樣宇植,也就是廣告會(huì)復(fù)用得封,所有要有廣告表。
2指郁、【每個(gè)頁(yè)面廣告的個(gè)數(shù)可變】【不同廣告上下線時(shí)間可不同】【頁(yè)面與頁(yè)面之間的活動(dòng)不一定一樣】
頁(yè)面可配置多個(gè)廣告忙上,所有要有頁(yè)面配置表,以及廣告和頁(yè)面的關(guān)系表闲坎,即頁(yè)面廣告表疫粥。
頁(yè)面配置表主要配置頁(yè)面的廣告?zhèn)€數(shù)茬斧,實(shí)現(xiàn)【每個(gè)頁(yè)面廣告的個(gè)數(shù)可變】,頁(yè)面廣告表主要配置頁(yè)面的每個(gè)廣告上下線時(shí)間梗逮,實(shí)現(xiàn)【不同廣告上下線時(shí)間可不同】

簡(jiǎn)單分析后得出如下表結(jié)構(gòu):廣告表adv项秉,頁(yè)面配置表page_config,頁(yè)面廣告表page_adv


表結(jié)構(gòu)

思考

這些頁(yè)面配置的廣告在一段時(shí)間內(nèi)是不會(huì)變的库糠,如果頁(yè)面請(qǐng)求次數(shù)較多伙狐,廣告查詢次數(shù)就會(huì)很頻繁涮毫,對(duì)數(shù)據(jù)庫(kù)造成不必要的壓力瞬欧。所以可以引入緩存,降低數(shù)據(jù)庫(kù)請(qǐng)求次數(shù)罢防,緩解數(shù)據(jù)庫(kù)壓力艘虎。這里使用的Redis。

何時(shí)入緩存咒吐?
可以選擇在服務(wù)啟動(dòng)時(shí)異步把已在上下線時(shí)間區(qū)間內(nèi)的廣告先加載至緩存野建,或選擇在請(qǐng)求時(shí)取緩存,緩存內(nèi)沒有時(shí)再查庫(kù)然后放緩存恬叹。緩存時(shí)間視情況而定候生。

這里選擇的是,項(xiàng)目啟動(dòng)時(shí)異步把符合條件的頁(yè)面廣告配置信息存入Redis绽昼,那些還沒到指定時(shí)間的先不放Redis唯鸭,等到訪問頁(yè)面加載廣告時(shí),先查Redis硅确,若無(wú)則按條件(>=nowtime)查庫(kù)目溉,查到后存Redis。

在接口中拿到廣告配置信息后菱农,判斷當(dāng)前時(shí)間是否在配置的時(shí)間區(qū)間內(nèi)缭付,由于一個(gè)頁(yè)面配置多個(gè)廣告,不同廣告時(shí)間也不同循未,所以要迭代陷猫,把符合的返回,有過期的就做標(biāo)記的妖,然后把整個(gè)頁(yè)面的配置信息在Redis里刪除绣檬。
(或者不選擇在啟動(dòng)時(shí)加載,就在用戶請(qǐng)求時(shí)加入緩存羔味,但是下面的第1步的方法在刷新加載時(shí)會(huì)用到河咽,故不能刪)

具體實(shí)現(xiàn)

第1步、查詢頁(yè)面廣告配置信息存入Redis

這一步是為了項(xiàng)目啟動(dòng)時(shí)調(diào)這個(gè)方法赋元,把符合條件的頁(yè)面廣告配置信息存入Redis忘蟹,并且刷新加載時(shí)也回調(diào)這個(gè)方法飒房。

a、查詢所有pageId

SELECT pageId FROM page_config page_adv WHERE nowtime<=endtime AND GROUP BY pageId

兩個(gè)表內(nèi)連接媚值,得List<pageId>狠毯,得到的都是配置的有廣告的并且廣告還沒過期的pageId。
b褥芒、查詢pegeId對(duì)應(yīng)的廣告圖片及跳轉(zhuǎn)鏈接

SELECT 字段名 FROM page_adv adv WHERE begintime<=nowtime<=endtime AND pageId={#pageId}

然后把查到的配置信息List<adv>(為空時(shí)不做操作)嚼松,以pageId為KEY放入緩存。

第2步锰扶、給前端寫接口查詢頁(yè)面廣告

按標(biāo)準(zhǔn)的控制層献酗,業(yè)務(wù)層,數(shù)據(jù)訪問層寫坷牛,第一步中的邏輯就是在業(yè)務(wù)層完成的罕偎。
控制層:
控制層接參pageId,調(diào)用業(yè)務(wù)層查詢對(duì)應(yīng)頁(yè)面配置的廣告信息京闰,判空颜及,直接返回狀態(tài)碼0,即無(wú)廣告前端不展示蹂楣。
不為空就根據(jù)業(yè)務(wù)邏輯處理數(shù)據(jù)(如img的URL加域名)俏站,然后返回狀態(tài)碼1,前端展示廣告痊土。
這里控制層還可以加邏輯肄扎,迭代廣告list,把當(dāng)前時(shí)間在廣告起始時(shí)間內(nèi)的返回施戴,不在的不返回反浓,并且只要有一個(gè)廣告過期,就把這個(gè)頁(yè)面的廣告list緩存清掉赞哗。這個(gè)邏輯是把過期的清掉雷则。
業(yè)務(wù)層:
先取緩存,沒有再查庫(kù)判斷不為空(本頁(yè)面配置的有廣告)肪笋,放入緩存(pageId為KEY)月劈,然后返回。
數(shù)據(jù)訪問層:
SQL:

SELECT 字段名 FROM page_config adv page_adv WHERE begintime<=nowtime<=endtime AND pageId=#{pageId}

三表聯(lián)查藤乙,根據(jù)pageId查詢當(dāng)前頁(yè)面配置的廣告活動(dòng)信息(已在廣告活動(dòng)時(shí)間內(nèi))

第3步猜揪、刷新加載

為什么使用刷新加載?
因?yàn)橛羞@樣的場(chǎng)景:給頁(yè)面A配置了一個(gè)廣告(當(dāng)前時(shí)間在廣告的起始時(shí)間內(nèi))坛梁,那么這個(gè)頁(yè)面的廣告已經(jīng)在緩存里了而姐,假如此時(shí)A頁(yè)面要新加一個(gè)廣告,在后臺(tái)配置后如果不做其他操作划咐,這個(gè)廣告不會(huì)顯示(假設(shè)緩存時(shí)間較長(zhǎng)拴念,為一天)钧萍,因?yàn)閹?kù)更新了,緩存沒有同步更新政鼠。

解決方案
使用Redis的發(fā)布訂閱機(jī)制實(shí)現(xiàn)緩存的刷新加載风瘦,使新配置的廣告及時(shí)能夠顯示。
刷新加載的回調(diào)方法即第1步中的方法公般。

進(jìn)一步優(yōu)化

想一想万搔,目前的實(shí)現(xiàn)存在什么問題?

存在的問題
假如有頁(yè)面需要配置廣告官帘,但是還沒有配(前端已經(jīng)開發(fā)完上線瞬雹,每次都會(huì)調(diào)接口查廣告信息),那么數(shù)據(jù)庫(kù)肯定查不到遏佣,緩存也沒有挖炬。如果這個(gè)頁(yè)面訪問量很大揽浙,那么緩存沒命中就查庫(kù)状婶,這樣對(duì)庫(kù)的壓力就會(huì)很大,這就是緩存穿透馅巷,請(qǐng)求上來了很容易擊垮數(shù)據(jù)庫(kù)膛虫。那怎么辦呢?

解決方案
當(dāng)頁(yè)面沒有配置廣告時(shí)钓猬,在緩存存標(biāo)志稍刀,查詢時(shí)先看標(biāo)志,在決定是否往下走敞曹。

具體方案
這時(shí)账月,上面的第1步就要改了。
1澳迫、首先改第1步的步驟a的SQL局齿,把所有的pageId都查詢出來。
使用左連接

SELECT pageId FROM page_config LEFT JOIN page_adv ON ...  GROUP BY pageId

或者干脆查page_config

SELECT pageId FROM page_config

目的是把已在page_config表中配置橄登,但關(guān)系表中page_adv未配置廣告的pageId也查出來抓歼,這樣才能給未配置廣告的pageId在緩存里放標(biāo)志
2、第1步的步驟b的SQL改為

SELECT 字段名 FROM page_adv adv WHERE nowtime<=endtime AND pageId={#pageId}

然后把查到的配置信息放入緩存之前\color{#FFA500}{判斷【為空時(shí)的不做操作】改為【為空時(shí)存入一個(gè)標(biāo)志】假如這個(gè)標(biāo)志KEY為pageId+"EMPTY_FLAG",value為"DB_IS_NULL"}

為什么只判斷小于結(jié)束時(shí)間
因?yàn)槿绻擁?yè)面配置的廣告開始時(shí)間大于當(dāng)前時(shí)間拢锹,那么這個(gè)是查不到的谣妻,會(huì)被處理為DATABASE_IS_NULL,如果在這個(gè)標(biāo)志還沒失效之前就到了配置的開始時(shí)間了卒稳,那么這個(gè)廣告不會(huì)被展示蹋半。所有要讓未到開始時(shí)間的也放入緩存,然后讓控制層去判斷在不在時(shí)間區(qū)間充坑。
3减江、所以要在第2步也修改一下
在業(yè)務(wù)層里取緩存中的廣告列表之前闻蛀,先從緩存取pageId+"EMPTY_FLAG"的value判斷為"DB_IS_NULL"直接返回空,這樣就能解決緩存穿透的問題了您市。
繼續(xù)修改第2步的業(yè)務(wù)層觉痛,查庫(kù)的SQL同樣要改:

SELECT 字段名 FROM page_config adv page_adv WHERE nowtime<=endtime AND pageId=#{pageId} 

然后判斷為空的話,同上面的黃字那樣處理茵休。

4薪棒、最后,第3步的刷新加載調(diào)的是第1步的方法榕莺,不用改俐芯。

當(dāng)然這個(gè)緩存穿透的優(yōu)化方案只是其中一種。還可以這樣:
1钉鸯、控制層攔截:根據(jù)pageId查詢page_adv表吧史,查不到說明沒配置,直接返回唠雕。
2贸营、page_config 表增加字段,表示當(dāng)前頁(yè)面已經(jīng)配置的廣告?zhèn)€數(shù)岩睁,默認(rèn)0钞脂,每配置一個(gè)該字段加1,把大于0的pageId緩存起來捕儒,調(diào)接口時(shí)前判斷在不在緩存里冰啃。

總結(jié):

實(shí)現(xiàn)這個(gè)功能并不是太難,主要用到了Redis的緩存技術(shù)刘莹,Redis發(fā)布訂閱機(jī)制阎毅,關(guān)鍵就是細(xì)節(jié)的把控,以及緩存穿透的處理点弯。


END

著作權(quán)歸作者所有扇调。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處蒲拉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肃拜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雌团,更是在濱河造成了極大的恐慌燃领,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锦援,死亡現(xiàn)場(chǎng)離奇詭異猛蔽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門曼库,熙熙樓的掌柜王于貴愁眉苦臉地迎上來区岗,“玉大人,你說我怎么就攤上這事毁枯〈鹊蓿” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵种玛,是天一觀的道長(zhǎng)藐鹤。 經(jīng)常有香客問我,道長(zhǎng)赂韵,這世上最難降的妖魔是什么娱节? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮祭示,結(jié)果婚禮上肄满,老公的妹妹穿的比我還像新娘。我一直安慰自己质涛,他們只是感情好稠歉,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹂窖,像睡著了一般轧抗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞬测,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音纠炮,去河邊找鬼月趟。 笑死恢口,一個(gè)胖子當(dāng)著我的面吹牛孝宗,可吹牛的內(nèi)容都是我干的耕肩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼猿诸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼婚被!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起址芯,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谷炸,沒想到半個(gè)月后北专,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旬陡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年拓颓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了描孟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啥繁,死狀恐怖青抛,靈堂內(nèi)的尸體忽然破棺而出旗闽,到底是詐尸還是另有隱情蜜另,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布捣辆,位于F島的核電站,受9級(jí)特大地震影響汽畴,放射性物質(zhì)發(fā)生泄漏耸序。R本人自食惡果不足惜忍些,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一罢坝、第九天 我趴在偏房一處隱蔽的房頂上張望搅窿。 院中可真熱鬧嘁酿,春花似錦男应、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)众弓。三九已至,卻和暖如春谓娃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滨达。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锌订,地道東北人画株。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谓传,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子紧卒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容