您的位置:js12345金沙官网登入 > 网络编程 > 利用Redis实现SQL伸缩的方法

利用Redis实现SQL伸缩的方法

2020-04-15 04:27

它的逻辑相当直接,如同下面展示的那般:

而锁()内的哨兵利用的memcached的,但绝对没有理由我们不能在其切换到Redis。时间序列数据

那么Redis为您解决什么问题呢?

基本锁

在目前的模型中,我们使用单一的hash map来存储全部时间序列数据。例如,这意味所有数据项在都将同一个哈希键拥有一个数据类型和1秒的生命周期。如下所示:

现在每一个时间刻度 (在Sentry中为10秒钟) 我们要转储(dump)这些缓冲区并且扇出写道(fanout the writes)。 看起来像下面这样:

虽然Redis的是可用性不高,我们的用例锁,使其成为工作的好工具。我们没有使用这些在哨兵的核心了,但一个示例用例是,我们希望尽量减少并发性和简单无操作的操作,如果事情似乎是已经在运行。这对于可能需要执行每隔一段时间类似cron任务非常有用,但不具备较强的协调。在Redis的这样使用SETNX操作是相当简单的:

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) 

缓解行竞争

我们有了这个处理问题的模型之后,能够确保“大部分情况下”每次在SQL中只有一行能够被马上更新,而这样的处理方式减轻了我们能够预见到的锁问题。考虑到将会处理一个突然产生且所有最终组合在一起进入同一个计数器的数据的场景,这种策略对Sentry用处很多。

因此在这种状况,我们需要追踪事件的数目。事件类型映射到枚举类型"1".该判断的时间是1s,因此我们的处理时间需要以秒计。散列最终看起来是这样的:复制代码 代码如下:{ "1:1399958363:0": { "1": 53, "2": 72, }}

近来我们创造一个新的机制在Sentry(包含在sentry.tsdb中) 存储时间序列数据。这是受RRD模型启发,特别是Graphite。我们期望一个快速简单的方式存储短期(比如一个月)时间序列数,以便于处理高速写入数据,特别是在极端情况下计算潜在的短期速率。尽管这是第一个模型,我们依旧期望在Redis存储数据,它也是使用计数器的简单范例。

创建一个绑定到传入实体的哈希键(hash key) 使用HINCRBY使计数器值增加 HSET所有的LWW数据(比如 "最后一次见到的") 用当前时间戳ZADD哈希键(hash key)到一个"挂起" set

在目前的模型中,我们使用单一的hash map来存储全部时间序列数据。例如,这意味所有数据项在都将同一个哈希键拥有一个数据类型和1秒的生命周期。如下所示:

复制代码 代码如下: "1:1399958363:0:1": 53

这篇文章主要介绍了利用Redis实现SQL伸缩的方法,包括讲到了锁和时间序列等方面来提升传统数据库的性能,需要的朋友可以参考下。

我们有了这个处理问题的模型之后,能够确保“大部分情况下”每次在SQL中只有一行能够被马上更新,而这样的处理方式减轻了我们能够预见到的锁问题。考虑到将会处理一个突然产生且所有最终组合在一起进入同一个计数器的数据的场景,这种策略对Sentry用处很多。

"1:1399958363:0:1": 53 

事实上我们最终采取这种策略可以使哨兵追踪不同事件的短期数据。在这种情况下,我们通常对用户数据进行排序以便可以在最短的时间内找到最活跃用户的数据。

我们所阐明的限制速率的方法是 Redis在高速缓存服务上最基本的功能之一:增加空的键字。在高速缓存服务中实现同样的行为可能最终使用这种方法:

速度限制

我们在Sentry开发的早起采用的是sentry.buffers。 这是一个简单的系统,它允许我们以简单的Last Write Wins策略来实现非常有效的缓冲计数器。 重要的是,我们借助它完全消除了任何形式的耐久性 (这是Sentry工作的一个非常可接受的方式)。

我们可以将所有的键设为一次性的 大幅压缩键值,这是相当重要的处理

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) 

简单的选择

以上就是如何利用Redis实现SQL伸缩的方法,希望对大家的学习有所帮助。

此外,离散的数字键允许我们在将虚拟的离散键值映射到固定数目的键值上,并在此分配单一存储区

速度限制

我们在Sentry开发的早起采用的是sentry.buffers。 这是一个简单的系统,它允许我们以简单的Last Write Wins策略来实现非常有效的缓冲计数器。 重要的是,我们借助它完全消除了任何形式的耐久性 (这是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的这样使用SETNX操作是相当简单的:

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)
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 

现在每一个时间刻度 (在Sentry中为10秒钟) 我们要转储(dump)这些缓冲区并且扇出写道(fanout the writes)。 看起来像下面这样:

在下面我们想要只弹出一个设置的数量的例子中我们将使用一组排序(举例来说我们需要那100个旧集合)。 假使我们为了处理一个键值来结束多道排序的作业,这个人会得到no-oped由于另一个已经存在的处理和清空哈希的过程。 该系统能够在许多Redis节点上不断扩展下去仅仅是通过在每个节点上安置把一个'悬置'主键来实现。

本文由js12345金沙官网登入发布于网络编程,转载请注明出处:利用Redis实现SQL伸缩的方法

关键词: