当前位置: 首页 > 网络学院 >

如何利用 Redis 实现 SQL 伸缩

新客网 XKER.COM 时间:2015-06-29 12:55:26来源:脚本之家  评论:

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

缓解行竞争

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

操作非常简单,每当一个更新进来我们就做如下几步:

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

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

  • 使用ZRANGE获取所有的key
  • 为每一个挂起的key发起一个作业到RabbitMQ

现在RabbitMQ作业将能够读取和清除哈希表,和“悬而未决”更新已经弹出了一套。有几件事情需要注意:

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

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

速度限制

出于哨兵的局限性,我们必须终结持续的拒绝服务攻击。我们通过限制连接速度来应对这种问题,其中一项是通过Redis支持的。这无疑是在sentry.quotas范围内更直接的实现。

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

  1. def incr_and_check_limit(user_id, limit): 
  2.   key = '{user_id}:{epoch}'.format(user_id, int(time() / 60)) 
  3.    
  4.   pipe = redis.pipeline() 
  5.   pipe.incr(key
  6.   pipe.expire(key, 60) 
  7.   current_rate, _ = pipe.execute() 
  8.    
  9.   return int(current_rate) > limit 

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

  1. def incr_and_check_limit_memcache(user_id, limit): 
  2.   key = '{user_id}:{epoch}'.format(user_id, int(time() / 60)) 
  3.    
  4.   if cache.add(key, 0, 60): 
  5.     return False 
  6.    
  7.   current_rate = cache.incr(key
  8.    
  9.   return current_rate > limit 

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

基本锁

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

在Redis的这样使用SETNX操作是相当简单的:

  1. from contextlib import contextmanagerr = Redis()@contextmanagerdef lock(key, nowait=True): 
  2.   while not r.setnx(key'1'): 
  3.     if nowait: 
  4.       raise Locked('try again soon!'
  5.     sleep(0.01) 
  6.    
  7.   # limit lock time to 10 seconds 
  8.   r.expire(key, 10) 
  9.    
  10.   # do something crazy 
  11.   yield 
  12.    
  13.   # explicitly unlock 
  14.   r.delete(key

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

如果您喜欢本文请分享给您的好友,谢谢!感谢本文来源方:脚本之家

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)