Mysql機能優化案例研討-籠罩索引和SQL_NO_CACHE。本站提示廣大學習愛好者:(Mysql機能優化案例研討-籠罩索引和SQL_NO_CACHE)文章只能為提供參考,不一定能成為您想要的結果。以下是Mysql機能優化案例研討-籠罩索引和SQL_NO_CACHE正文
這篇文章重要引見了應用Redis完成SQL伸縮的辦法,包含講到了鎖和時光序列等方面來晉升傳統數據庫的機能,須要的同伙可以參考下。
減緩行競爭
我們在Sentry開辟的夙興采取的是sentry.buffers。 這是一個簡略的體系,它許可我們以簡略的Last Write Wins戰略來完成異常有用的緩沖計數器。 主要的是,我們借助它完整清除了任何情勢的經久性 (這是Sentry任務的一個異常可接收的方法)。
操作異常簡略,每當一個更新出去我們就做以下幾步:
如今每個時光刻度 (在Sentry中為10秒鐘) 我們要轉儲(dump)這些緩沖區而且扇出寫道(fanout the writes)。 看起來像上面如許:
如今RabbitMQ功課將可以或許讀取和消除哈希表,和“懸而未決”更新曾經彈出了一套。有幾件工作須要留意:
我們有了這個處置成績的模子以後,可以或許確保“年夜部門情形下”每次在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)
歸結以下:
以上就是若何應用Redis完成SQL伸縮的辦法,願望對年夜家的進修有所贊助。