跳至内容 跳至搜索

Redis 缓存存储

部署说明:注意使用专用的 Redis 缓存,而不是将其指向持久性 Redis 服务器(例如,用作 Active Job 队列的服务器)。Redis 无法很好地处理混合使用模式,并且默认情况下不会使缓存条目过期。

Redis 缓存服务器设置指南:redis.io/topics/lru-cache

  • 支持原生 Redis、hiredis 和 Redis::Distributed

  • 使用 Redis::Distributed 支持跨 Redis 的类 Memcached 分片。

  • 容错。如果 Redis 服务器不可用,则不会引发异常。 Cache 获取全部失败,写入全部丢失。

  • 本地缓存。块/中间件范围内热门的内存中主缓存。

  • 支持 Redis mget/mset 的 read_multiwrite_multi。使用 Redis::Distributed 4.0.1+ 支持分布式 mget。

  • 支持 Redis KEYS 全局的 delete_matched

方法
C
D
I
N
R
S

常量

DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) do if logger logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" } end ActiveSupport.error_reporter&.report( exception, severity: :warning, source: "redis_cache_store.active_support", ) end
 
DEFAULT_REDIS_OPTIONS = { connect_timeout: 1, read_timeout: 1, write_timeout: 1, }
 
MAX_KEY_BYTESIZE = 1024
 

如果键超过 1kB,则使用 Active Support 哈希对其进行截断

属性

[R] max_key_bytesize
[R] redis

类公共方法

new(error_handler: DEFAULT_ERROR_HANDLER, **redis_options)

创建一个新的 Redis 缓存存储。

有四种方式提供缓存使用的 Redis 客户端::redis 参数可以是 Redis 实例或返回 Redis 实例的块,或者 :url 参数可以是字符串或字符串数组,这些字符串或数组将用于创建 Redis 实例或 Redis::Distributed 实例。

Option  Class       Result
:redis  Proc    ->  options[:redis].call
:redis  Object  ->  options[:redis]
:url    String  ->  Redis.new(url: …)
:url    Array   ->  Redis::Distributed.new([{ url: … }, { url: … }, …])

默认情况下不设置命名空间。如果 Redis 缓存服务器与其他应用共享,则提供一个命名空间:namespace: 'myapp-cache'

默认情况下启用压缩,并设置 1kB 阈值,因此大于 1kB 的缓存值将自动压缩。通过传递 compress: false 禁用压缩,或通过传递 compress_threshold: 4.kilobytes 更改阈值。

默认情况下不设置缓存项的过期时间。Redis 预期配置有驱逐策略,当达到最大内存时,该策略将自动删除最近最少使用或最不经常使用的键。有关缓存服务器设置,请参阅 redis.io/topics/lru-cache

默认情况下不设置竞争条件 TTL。当热门缓存项过期时,这可用于避免“惊群”缓存写入。有关更多信息,请参阅 ActiveSupport::Cache::Store#fetch

设置 skip_nil: true 将不会缓存 nil 结果

cache.fetch('foo') { nil }
cache.fetch('bar', skip_nil: true) { nil }
cache.exist?('foo') # => true
cache.exist?('bar') # => false
# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 149
def initialize(error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
  universal_options = redis_options.extract!(*UNIVERSAL_OPTIONS)

  if pool_options = self.class.send(:retrieve_pool_options, redis_options)
    @redis = ::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) }
  else
    @redis = self.class.build_redis(**redis_options)
  end

  @max_key_bytesize = MAX_KEY_BYTESIZE
  @error_handler = error_handler

  super(universal_options)
end

supports_cache_versioning?()

宣传缓存版本控制支持。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 63
def self.supports_cache_versioning?
  true
end

实例公共方法

cleanup(options = nil)

Cache Store API 实现。

删除过期的项。由 Redis 最近最少/最不经常使用过期时间本机处理,因此不支持手动清理。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 276
def cleanup(options = nil)
  super
end

clear(options = nil)

清除所有 Redis 服务器上的整个缓存。如果缓存是命名空间的,则在共享服务器上使用时是安全的。

故障保护:引发错误。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 284
def clear(options = nil)
  failsafe :clear do
    if namespace = merged_options(options)[:namespace]
      delete_matched "*", namespace: namespace
    else
      redis.then { |c| c.flushdb }
    end
  end
end

decrement(name, amount = 1, options = nil)

使用 Redis decrby 原子运算符递减缓存的整数值。返回更新后的值。

如果键未设置或已过期,它将被设置为 -amount

cache.decrement("foo") # => -1

要设置特定值,请调用 write 传递 raw: true

cache.write("baz", 5, raw: true)
cache.decrement("baz") # => 4

递减非数字值或未用 raw: true 编写的值将失败并返回 nil

故障保护:引发错误。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 262
def decrement(name, amount = 1, options = nil)
  instrument :decrement, name, amount: amount do
    failsafe :decrement do
      options = merged_options(options)
      key = normalize_key(name, options)
      change_counter(key, -amount, options)
    end
  end
end

delete_matched(matcher, options = nil)

Cache Store API 实现。

支持 Redis KEYS glob 模式

h?llo matches hello, hallo and hxllo
h*llo matches hllo and heeeello
h[ae]llo matches hello and hallo, but not hillo
h[^e]llo matches hallo, hbllo, ... but not hello
h[a-b]llo matches hallo and hbllo

如果要逐字匹配特殊字符,请使用 \ 转义它们。

有关更多信息,请参阅 redis.io/commands/KEYS

故障保护:引发错误。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 198
def delete_matched(matcher, options = nil)
  instrument :delete_matched, matcher do
    unless String === matcher
      raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}"
    end
    redis.then do |c|
      pattern = namespace_key(matcher, options)
      cursor = "0"
      # Fetch keys in batches using SCAN to avoid blocking the Redis server.
      nodes = c.respond_to?(:nodes) ? c.nodes : [c]

      nodes.each do |node|
        begin
          cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
          node.del(*keys) unless keys.empty?
        end until cursor == "0"
      end
    end
  end
end

increment(name, amount = 1, options = nil)

使用 Redis incrby 原子运算符递增缓存的整数值。返回更新后的值。

如果键未设置或已过期,它将被设置为 amount

cache.increment("foo") # => 1
cache.increment("bar", 100) # => 100

要设置特定值,请调用 write 传递 raw: true

cache.write("baz", 5, raw: true)
cache.increment("baz") # => 6

递增非数字值或未用 raw: true 编写的值将失败并返回 nil

故障保护:引发错误。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 236
def increment(name, amount = 1, options = nil)
  instrument :increment, name, amount: amount do
    failsafe :increment do
      options = merged_options(options)
      key = normalize_key(name, options)
      change_counter(key, amount, options)
    end
  end
end

inspect()

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 164
def inspect
  "#<#{self.class} options=#{options.inspect} redis=#{redis.inspect}>"
end

read_multi(*names)

Cache Store API 实现。

一次读取多个值。返回请求键的哈希 -> 获取的值。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 172
def read_multi(*names)
  return {} if names.empty?

  options = names.extract_options!
  instrument_multi(:read_multi, names, options) do |payload|
    read_multi_entries(names, **options).tap do |results|
      payload[:hits] = results.keys
    end
  end
end

stats()

从 redis 服务器获取信息。

# File activesupport/lib/active_support/cache/redis_cache_store.rb, line 295
def stats
  redis.then { |c| c.info }
end