某些頁面需設定廣告或活動宣傳圖,廣告或活動需滿足隨時上下線、過期自動下線及到時自動上線。
如:現在時間2019-2-22 16:16:13,要在付款完成頁面配置領獎活動,活動要在2019-3-10 00:00:00準時上線,在2019- 3-30 23:59:59結束活動。
因此,期望實現的效果是在活動上線前的任何時間配置活動後,頁面會在指定時間自動上線該活動。也可能會是其他的多個活動或廣告,每個頁面廣告的數量可變,不同上線時間可不同,其他頁面也需要實現這樣的功能,頁面與頁面之間的活動不一定一樣。
需求簡單的幾句話,那麼我們來具體的分析一下。
廣告或活動宣傳圖
隨時上線、過期自動離線及到時自動上線
每個頁面廣告的個數可變
#不同廣告上線時間可不同
#頁面與頁面之間的活動不一定一樣
#1、【廣告或活動宣傳圖】
要為不同頁面設定不同的廣告,有的頁面廣告可能一樣,也就是廣告會重複使用,所有要有廣告表。
2、【每個頁面廣告的數量可變】【不同廣告上下線時間可不同】【頁面與頁面之間的活動不一定一樣】
頁面可配置多個廣告,所有要有頁面配置表,以及廣告和頁面的關係表,即頁面廣告表。
頁面配置表主要配置頁面的廣告個數,實現【每個頁面廣告的個數可變】,頁面廣告表主要配置頁面的每個廣告上離線時間,實現【不同廣告上下線時間可不同】
根據簡單的分析,我得到以下表格建構:廣告表格(adv),頁面設定表(page_config)和頁面廣告表(page_adv)
這些頁面配置的廣告在一段時間內是不會改變的,如果頁面請求次數較多,廣告查詢次數就會很頻繁,對資料庫造成不必要的壓力。所以可以引入緩存,降低資料庫請求次數,緩解資料庫壓力。這裡使用的Redis。
何時進入快取?
可以選擇在服務啟動時非同步把已在上下線時間區間內的廣告先載入至緩存,或選擇在請求時取緩存,快取內沒有時再查庫然後放快取。快取時間視情況而定。
這裡選擇的是,專案啟動時非同步把符合條件的頁面廣告設定資訊存入Redis,那些還沒到指定時間的先不放Redis,等到造訪頁面載入廣告時,先查Redis,若無則依條件(>=nowtime)查庫,查到後存Redis。
在介面中拿到廣告配置資訊後,判斷當前時間是否在配置的時間區間內,由於一個頁面配置多個廣告,不同廣告時間也不同,所以要迭代,把符合的返回,有過期的就做標記,然後把整個頁面的設定資訊在Redis裡刪除。 (或不選擇在啟動時加載,就在用戶請求時加入緩存,但是下面的第1步的方法在刷新加載時會用到,故不能刪)
a、查詢所有pageId
SELECT pageId FROM page_config page_adv WHERE nowtime<p>兩個表內連接,得List<pageid>,得到的都是配置的有廣告的且廣告還沒過期的pageId。 </pageid></p><p>b、查詢pegeId對應的廣告圖片及跳轉連結</p><pre class="brush:php;toolbar:false">SELECT 字段名 FROM page_adv adv WHERE begintime<p>然後把查到的設定資訊List<adv>(空時不做動作),以pageId為KEY放入緩存。 </adv></p><h4>步驟2、給前端寫介面查詢頁面廣告</h4><p>按標準的控制層,業務層,資料存取層寫,第一步中的邏輯就是在業務層完成的。 </p><p>控制層:</p><p>控制層接參pageId,呼叫業務層查詢對應頁面配置的廣告訊息,判空,直接回傳狀態碼0,即無廣告前端不展示。 </p><p>不為空就依照業務邏輯處理資料(如img的URL加網域),然後回傳狀態碼1,前端展示廣告。這裡控制層還可以加邏輯,迭代廣告list,把當前時間在廣告起始時間內的返回,不在的不返回,並且只要有一個廣告過期,就把這個頁面的廣告list緩存清掉。這個邏輯是把過期的清掉。 </p><p>業務層:</p><p>先取緩存,沒有再查庫判斷不為空(本頁配置的有廣告),放入快取(pageId為KEY),然後返回。 </p><p>資料存取層:</p><p>SQL:</p><pre class="brush:php;toolbar:false">SELECT 字段名 FROM page_config adv page_adv WHERE begintime<p>三表聯查,根據pageId查詢目前頁面配置的廣告活動資訊(已在廣告活動時間內)</p><h4>第3步、刷新載入</h4><p>為什麼要使用刷新載入? </p><p>因为有这样的场景:给页面A配置了一个广告(当前时间在广告的起始时间内),那么这个页面的广告已经在缓存里了,假如此时A页面要新加一个广告,在后台配置后如果不做其他操作,这个广告不会显示(假设缓存时间较长,为一天),因为库更新了,缓存没有同步更新。</p><p>解决方案</p><p>使用Redis的发布订阅机制实现缓存的刷新加载,使新配置的广告及时能够显示。刷新加载的回调方法即第1步中的方法。</p><h3>进一步优化</h3><p>想一想,目前的实现存在什么问题?</p><p>存在的问题</p><p>假如有页面需要配置广告,但是还没有配(前端已经开发完上线,每次都会调接口查广告信息),那么数据库肯定查不到,缓存也没有。如果这个页面访问量很大,那么缓存没命中就查库,这样对库的压力就会很大,这就是缓存穿透,请求上来了很容易击垮数据库。那怎么办呢?</p><p>解决方案</p><p>当页面没有配置广告时,在缓存存标志,查询时先看标志,在决定是否往下走。</p><p>具体方案</p><p>这时,上面的第1步就要改了。</p><p>1、首先改第1步的步骤a的SQL,把所有的pageId都查询出来。</p><p>使用左连接</p><pre class="brush:php;toolbar:false">SELECT pageId FROM page_config LEFT JOIN page_adv ON ... GROUP BY pageId
或者干脆查page_config
SELECT pageId FROM page_config
目的是把已在page_config表中配置,但关系表中page_adv未配置广告的pageId也查出来,这样才能给未配置广告的pageId在缓存里放标志
2、第1步的步骤b的SQL改为
SELECT 字段名 FROM page_adv adv WHERE nowtime<p>然后把查到的配置信息放入缓存之前判断【为空时的不做操作】改为【为空时存入一个标志】假如这个标志KEY为pageId+"_EMPTY_FLAG",value为"DB_IS_NULL"</p><p>为什么只判断小于结束时间</p><p>因为如果该页面配置的广告开始时间大于当前时间,那么这个是查不到的,会被处理为DATABASE_IS_NULL,如果在这个标志还没失效之前就到了配置的开始时间了,那么这个广告不会被展示。所有要让未到开始时间的也放入缓存,然后让控制层去判断在不在时间区间。</p><p>3、所以要在第2步也修改一下</p><p>在业务层里取缓存中的广告列表之前,先从缓存取pageId+"EMPTY_FLAG"的value判断为"DB_IS_NULL"直接返回空,这样就能避免缓存穿透的问题了。</p><p>继续修改第2步的业务层,查库的SQL同样要改:</p><pre class="brush:php;toolbar:false">SELECT 字段名 FROM page_config adv page_adv WHERE nowtime<p>然后判断为空的话,同上面的黄字那样处理。</p><p>4、最后,第3步的刷新加载调的是第1步的方法,不用改。<br><br>当然这个缓存穿透的优化方案只是其中一种。还可以这样:</p><p>1、控制层拦截:根据pageId查询page_adv表,查不到说明没配置,直接返回。</p><p>2、page_config 表增加字段,表示当前页面已经配置的广告个数,默认0,每配置一个该字段加1,把大于0的pageId缓存起来,调接口时前判断在不在缓存里。</p>
以上是redis怎麼實現頁面即時更新自動上線的詳細內容。更多資訊請關注PHP中文網其他相關文章!