使用 Redis 實現 SQL 伸縮

來源:互聯網
上載者:User

使用 Redis 實現 SQL 伸縮

我喜歡Redis。這是目前的技術當中唯一讓你奇怪為什麼需要這麼長時間編譯它的技術。可預測的,高效能並且適應性強,這是我過去幾年越來越多使用它的原因。Sentry主要在PostgreSQL上運行已經不是秘密(儘管目前它還依賴於一系列其它技術)

 

一個多星期前,我在 Python Nordeste 上作了主題演講。某種程度上而言我只能作一些快速的總結,我決定去找一些駭客來探討大量使用Sentry,特別是Redis技術。這篇文章是一個5分鐘討論的擴充。

緩解行之間的爭奪

 

我們採用了早在哨兵發展的東西是什麼成為著名的sentry.buffers。這是一個簡單的系統,使我們能夠實現非常有效緩衝計數器,一個簡單的上次寫入贏的策略。重要的是要注意,我們完全與此幾乎杜絕任何形式的耐用性(這是非常可以接受的哨兵的工作方式)。

該操作是相當簡單的,每當一個更新進來,我們做到以下幾點:

1.建立綁定到給定實體散列鍵

2.增量'反'使用HINCRBY

3.HEST各種不同LWW資料(如“最後一次見到”)

4.ZADD散列鍵到'掛起'使用目前時間戳設定

現在,每個刻度(在哨兵的情況下,這是10秒),我們要轉儲這些緩衝區和扇出的寫入。這看起來像下面這樣:

1.開始使用ZRANGE所有鍵

2. 火了一個作業分成RabbitMQ的每一個懸而未決的關鍵

3. ZREM給定的鍵

現在RabbitMQ作業將能夠讀取和清除雜湊表,和“懸而未決”更新已經彈出了一套。有幾件事情需要注意:

  • 在下面我們想要只彈出一個設定的數量的例子中我們將使用一組排序(舉例來說我們需要那100箇舊集合)。

  • 假使我們為了處理一個索引值來結束多道排序的作業,這個人會得到no-oped由於另一個已經存在的處理和清空雜湊的過程。

  • 該系統能夠在許多Redis節點上不斷擴充下去僅僅是通過在每個節點上安置把一個'懸置'主鍵來實現。

我們有了這個處理問題的模型之後,能夠確保“大部分情況下”每次在SQL中只有一行能夠被馬上更新,而這樣的處理方式減輕了我們能夠預見到的鎖問題。考慮到將會處理一個突然產生且所有最終組合在一起進入同一個計數器的資料的情境,這種策略對Sentry用處很多。

速度限制

出於哨兵的局限性,我們必須終結持續的拒絕服務的攻擊。我們通過限制連線速度來應對這種問題,其中一項是通過Redis支援的。這無疑是在sentry.quotas範圍內更直接的實現。

它的邏輯相當直接,如同下面展示的那般:

def incr_and_check_limit(user_id, limit): key = '{user_id}:{epoch}' .format(user_id, int(time() / 60 ))   pipe = redis.pipeline() pipe.incr(key) pipe.expire(key, 60 ) current_rate, _ = pipe.execute()   return int(current_rate) > limit

我們所闡明的限制速率的方法是 Redis在快取服務上最基本的功能之一:增加空的鍵字。在快取服務中實現同樣的行為可能最終使用這種方法:

def incr_and_check_limit_memcache(user_id, limit): key = '{user_id}:{epoch}' .format(user_id, int(time() / 60 ))   if cache.add(key, 0 , 60 ): return False   current_rate = cache.incr(key)   return current_rate > limit

事實上我們最終採取這種策略可以使哨兵追蹤不同事件的短期資料。在這種情況下,我們通常對使用者資料進行排序以便可以在最短的時間內找到最活躍使用者的資料。

基本鎖

雖然Redis的是可用性不高,我們的用例鎖,使其成為工作的好工具。我們沒有使用這些在哨兵的核心了,但一個樣本用例是,我們希望盡量減少並發性和簡單無操作的操作,如果事情似乎是已經在運行。這對於可能需要執行每隔一段時間類似cron任務非常有用,但不具備較強的協調。
在Redis的這樣使用SETNX操作是相當簡單的:

from contextlib import contextmanagerr = Redis()@contextmanagerdef lock(key, nowait = True ): while not r.setnx(key, '1' ): if nowait: raise Locked( 'try again soon!' ) sleep( 0.01 )   # limit lock time to 10 seconds r.expire(key, 10 )   # do something crazy yield  # explicitly unlock r.delete(key)

而鎖()內的哨兵利用的memcached的,但絕對沒有理由我們不能在其切換到Redis。

時間序列資料

近來我們創造一個新的機制在Sentry(包含在sentry.tsdb中)儲存時間序列資料。這是受RRD模型啟發,特別是Graphite。我們期望一個快速簡單的方式儲存短期(比如一個月)時間序列數,以便於處理高速寫入資料,特別是在極端情況下計算潛在的短期速率。儘管這是第一個模型,我們依舊期望在Redis儲存資料��它也是使用計數器的簡單範例。

在目前的模型中,我們使用單一的hash map來儲存全部時間序列資料。例如,這意味所有資料項目在都將同一個雜湊鍵擁有一個資料類型和1秒的生命週期。如下所示:

{ "<type enum>:<epoch>:<shard number>" : { "<id>" : <count> }}

因此在這種狀況,我們需要追蹤事件的數目。事件類型映射到枚舉類型"1".該判斷的時間是1s,因此我們的處理時間需要以秒計。散列最終看起來是這樣的:

{ "1:1399958363:0" : { "1" : 53, "2" : 72, }}

一個可修改模型可能僅使用簡單的鍵並且僅在儲存區上增加一些增量寄存器。

   "1:1399958363:0:1": 53

我們選擇雜湊映射模型基於以下兩個原因:

  • 我們可以將所有的鍵設為一次性的(這也可能產生負面影響,但是目前為止是穩定的)

  • 大幅壓縮索引值,這是相當重要的處理

此外,離散的數字鍵允許我們在將虛擬離散索引值映射到固定數目的索引值上,並在此分配單一儲存區(我們可以使用64,映射到32個物理結點上)

現在通過使用Nydus和它的map()(依賴於一個工作區)(),資料查詢已經完成。這次操作的代碼是相當健壯的,但幸好它並不龐大。

def get_range( self , model, keys, start, end, rollup = None ): """ To get a range of data for group ID=[1, 2, 3]: Start and end are both inclusive. >>> now = timezone.now() >>> get_keys(tsdb.models.group, [1, 2, 3], >>> start=now - timedelta(days=1), >>> end=now) """ normalize_to_epoch = self .normalize_to_epoch normalize_to_rollup = self .normalize_to_rollup make_key = self .make_key   if rollup is None : rollup = self .get_optimal_rollup(start, end)   results = [] timestamp = end with self .conn. map () as conn: while timestamp > = start: real_epoch = normalize_to_epoch(timestamp, rollup) norm_epoch = normalize_to_rollup(timestamp, rollup)   for key in keys: model_key = self .get_model_key(key) hash_key = make_key(model, norm_epoch, model_key) results.append((real_epoch, key, conn.hget(hash_key, model_key)))   timestamp = timestamp - timedelta(seconds = rollup)   results_by_key = defaultdict( dict ) for epoch, key, count in results: results_by_key[key][epoch] = int (count or 0 )   for key, points in results_by_key.iteritems(): results_by_key[key] = sorted (points.items()) return dict (results_by_key)

歸結如下:

  • 產生所必須的鍵。

  • 使用工作區,提取所有串連操作的最小結果集(Nydus負責這些)。

  • 給出結果,並且基於指定的時間間隔內和給定的索引值將它們映射到當前的儲存區內。

簡單的選擇

我是一個喜歡用簡單的方案解決問題的人,在這個範疇裡使用Redis無疑是很適合的。它的文檔是那樣讓人驚訝,那是因為(閱讀)其文檔的門檻非常的低。雖然他也有折衷(主要是如果你使用持久化),但是他們工作地很好並且比較直觀。

那麼Redis為您解決什麼問題呢?

Ubuntu 14.04下Redis安裝及簡單測試

Redis叢集明細文檔

Ubuntu 12.10下安裝Redis(圖文詳解)+ Jedis串連Redis

Redis系列-安裝部署維護篇

CentOS 6.3安裝Redis

Redis安裝部署學習筆記

Redis設定檔redis.conf 詳解

Redis 的詳細介紹:請點這裡
Redis 的:請點這裡

本文永久更新連結地址:

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.