記一次廣告線上問題

關鍵詞:redis slave spire 獲取過期數據

週六晚會直播,在值班的時候發現原來十分鐘不重複出廣告的策略c長時間的限制了第二次廣告的觸發;

廣告播放的標記存儲在redis中,expire設置爲600,按理10分鐘後標記清除,廣告系統獲取不到播放標記會給用戶再次下發廣告。但是當晚有一些用戶看過第一廣告後,長時間無法第二次播放廣告。
經過查詢相應用戶後臺日誌,發現是10分鐘不重複觀看策略導致的。

也就是說redis存在expire過期數據仍可被讀取的情況。

經過一番查證,redis曾發起Issue Improve expire consistency on slaves,以下摘錄說明了這個情況

In order for Redis to ensure consistency between a master and its slaves, eviction of keys with an expire are managed by the master, which sends an explicit DEL to its slaves when the key gets actually removed.

爲了保證redis主、從一致性,expire數據的刪除由master來進行,當expire數據刪除的時候,master會向slave發送刪除命令


This means that slaves are not able to directly expire keys, even if these keys are logically expired on the master side. So a GET that will return null in the master side, may return a stale value in the slave side.

這意味着,即使這些expire數據從邏輯上應該被master端刪除,slaves也不會直接刪除expire數據。在master獲取這些過期數據將會獲取null,而在slave端可能仍能獲取到舊的數據

我們項目對redis cluster的readonly字段配置爲1,查看golang redis.v5源碼

func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) {
	if state == nil {
		node, err := c.nodes.Random()
		return 0, node, err
	}

	cmdInfo := c.cmds[cmd.name()]
	firstKey := cmd.arg(cmdFirstKeyPos(cmd, cmdInfo))
	slot := hashtag.Slot(firstKey)

	if cmdInfo != nil && cmdInfo.ReadOnly && c.opt.ReadOnly {
		if c.opt.RouteByLatency {
			node, err := state.slotClosestNode(slot)
			return slot, node, err
		}

		node, err := state.slotSlaveNode(slot)
		return slot, node, err
	}

	node, err := state.slotMasterNode(slot)
	return slot, node, err
}

發現c.opt.ReadOnly成立是將會使用slaveNode,否則使用masterNode,而使用slaveNode則可能引發上面的問題。

解決方案:

  1. 查看我司服務器redis版本是redis_version:3.0.7-m,這個問題在Redis 3.2 中得到解決,升級redis可以解決問題。
  2. 將readonly字段配置爲0,將使用masterNode節點。但是要注意這種方案將會增大master的壓力,酌情考慮。
  3. 除此之外也有同學提出了另外的解決途徑
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章