新聞中心
Redis穿透:有效解決方案

近年來(lái),Redis已成為了很多互聯(lián)網(wǎng)公司的首選緩存數(shù)據(jù)庫(kù)之一。然而,Redis在承載大量數(shù)據(jù)時(shí),很容易遇到一種被稱為“Redis穿透”問(wèn)題。這種問(wèn)題會(huì)引起Redis服務(wù)器的崩潰,給系統(tǒng)帶來(lái)不小的損失。本文將為大家介紹一些有效的解決Redis穿透問(wèn)題的方案。
Redis穿透問(wèn)題背景
在使用Redis時(shí),我們通常會(huì)將一些重量級(jí)的操作緩存在Redis中,比如數(shù)據(jù)庫(kù)查詢結(jié)果或其他第三方服務(wù)的響應(yīng)結(jié)果。這些操作的結(jié)果是經(jīng)過(guò)計(jì)算和處理的,并且不會(huì)隨著時(shí)間的推移而發(fā)生變化。這種類型的操作結(jié)果使用Redis緩存常常能夠帶來(lái)較大的性能提升,同時(shí)也能減輕其他數(shù)據(jù)庫(kù)的壓力。
但是,在大量數(shù)據(jù)的情況下,如果沒(méi)有采取足夠的對(duì)策,在某些它不應(yīng)該獲取的數(shù)據(jù)的情況下,Redis可能會(huì)無(wú)法承受大量的查詢請(qǐng)求壓力,從而崩潰。
我們假設(shè)你有一個(gè)查詢某個(gè)商品詳細(xì)信息的接口,可以通過(guò)將請(qǐng)求的商品ID作為參數(shù)傳遞給后臺(tái)服務(wù)來(lái)獲取信息。如果黑客向你的服務(wù)器發(fā)送一個(gè)查詢不存在的商品ID的請(qǐng)求,由于Redis緩存中沒(méi)有相應(yīng)數(shù)據(jù),系統(tǒng)將不得不去數(shù)據(jù)庫(kù)查詢。當(dāng)攻擊者重復(fù)發(fā)送此類請(qǐng)求時(shí),即產(chǎn)生了“Redis穿透”問(wèn)題,將會(huì)在短時(shí)間內(nèi)導(dǎo)致大量的緩存不中命中,最終導(dǎo)致內(nèi)存和CPU資源不斷增長(zhǎng),最終將Redis服務(wù)器消耗完。
解決方案
1. 布隆過(guò)濾器
布隆過(guò)濾器是一種經(jīng)典的利用概率算法來(lái)解決Redis穿透問(wèn)題的解決方案。它可以幫助我們快速確定是否一個(gè)請(qǐng)求的ID值是否存在于緩存之中。這種解決方法主要是通過(guò)將所有存在的請(qǐng)求ID以及HASH值放進(jìn)一個(gè)超大的比特?cái)?shù)組中,判斷的時(shí)候通過(guò)比特?cái)?shù)組查詢是否有一段位置被置位,若被置位,則說(shuō)明該ID已經(jīng)處理過(guò)。這種方法雖然效率很高,但有一定的誤差率。
以下是一個(gè)實(shí)現(xiàn)布隆過(guò)濾器的Python 代碼示例:
“`python
import redis
import collections
import hashlib
class BloomFilter(object):
def __init__(self, capacity=10000, error_rate=0.01):
”’
初始化Bloom Filter
”’
self.key = ‘bloomfilter’
self.capacity = capacity
self.error_rate = error_rate
self.pool = redis.ConnectionPool(host=’localhost’, port=6379, db=0)
self.r = redis.Redis(connection_pool=self.pool)
self.hash_count, self.bit_count = self.get_optimal_para()
def get_optimal_para(self):
m = -1 * (self.capacity * math.log(self.error_rate)) / (math.log(2) ** 2)
k = (m / self.capacity) * math.log(2)
return int(math.ceil(k)), int(math.ceil(m))
def is_exists(self, value):
if not value:
return False
exist = True
hash_values = self.generate_hash(value)
for offset in hash_values:
v = self.r.getbit(self.key, offset)
if v == 0:
exist = False
self.r.setbit(self.key, offset, 1)
return exist
def generate_hash(self, value):
offset_list = []
murmur = hashlib.md5()
murmur.update(value.encode())
seed = int(murmur.hexdigest(), 16)
m, k = self.bit_count, self.hash_count
for i in range(k):
offset = hash(‘{}{}’.format(seed, i)) % m
offset_list.append(offset)
return offset_list
2. 緩存空對(duì)象
另一種解決Redis穿透問(wèn)題的方法是當(dāng)你從數(shù)據(jù)庫(kù)中查詢出了一個(gè)不存在的數(shù)據(jù)時(shí),你應(yīng)該將這個(gè)結(jié)果加到Redis緩存當(dāng)中,只不過(guò)這個(gè)結(jié)果的值應(yīng)該是空對(duì)象,也就是說(shuō)返回的結(jié)果可以不是null或者類似的空集合(array,list),而是一個(gè)特殊的標(biāo)記——一個(gè)表示不存在的空對(duì)象。這種空對(duì)象可以是一個(gè)字符串、數(shù)字或其他類型的數(shù)據(jù),只要確保它不會(huì)被返回給客戶端就可以了。
以下是一個(gè)PHP的示例代碼:
```php
function get_user($id)
{
$user_info = $redis->get(sprintf('user:%d', $id));
if (!$user_info || $user_info == 'null') {
$user = $db->get_user($id);
if (!$user) {
$redis->setex(sprintf('user:%s', $id), 300, 'null');
} else {
$redis->setex(sprintf('user:%s', $user['id']), 300, json_encode($user));
}
} else if ($user_info == 'null') {
return NULL;
}
return json_decode($user_info, true);
}
?>
總結(jié)
Redis穿透問(wèn)題是一個(gè)相對(duì)比較麻煩的問(wèn)題。如果我們不采取相應(yīng)的措施,其將會(huì)帶來(lái)很多影響,甚至被攻擊的請(qǐng)求會(huì)導(dǎo)致Redis服務(wù)器崩潰。我們?cè)谑褂肦edis緩存數(shù)據(jù)庫(kù)時(shí)一定要注意處理這種情況。布隆過(guò)濾器是一種相對(duì)有效的方法,即使它有一定的誤差率,并且可以使用空對(duì)象緩存解決Redis穿透問(wèn)題。使用這些方法可以幫助提高Redis服務(wù)器的處理效率,并減輕服務(wù)器的負(fù)擔(dān)。
成都創(chuàng)新互聯(lián)建站主營(yíng):成都網(wǎng)站建設(shè)、網(wǎng)站維護(hù)、網(wǎng)站改版的網(wǎng)站建設(shè)公司,提供成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、成都網(wǎng)站推廣、成都網(wǎng)站優(yōu)化seo、響應(yīng)式移動(dòng)網(wǎng)站開(kāi)發(fā)制作等網(wǎng)站服務(wù)。
新聞名稱:Redis穿透有效解決方案(redis穿透解決辦法)
網(wǎng)頁(yè)路徑:http://www.5511xx.com/article/ccssjod.html


咨詢
建站咨詢
