跳至內容 跳至搜尋

Active 支援快取儲存

一種抽象快取儲存類別。有多種快取儲存實作,各實作具有不同的附加功能。請參閱 ActiveSupport::Cache 模組下的類別,例如 ActiveSupport::Cache::MemCacheStoreMemCacheStore 目前是大型製作網站最受歡迎的快取儲存。

有些實作可能不支援基礎快取方法以外的所有方法,例如 fetchwritereadexist? 以及 delete

ActiveSupport::Cache::Store 可以儲存任何由其 coderdumpload 方法所支援的 Ruby 物件。

cache = ActiveSupport::Cache::MemoryStore.new

cache.read('city')   # => nil
cache.write('city', "Duckburgh") # => true
cache.read('city')   # => "Duckburgh"

cache.write('not serializable', Proc.new {}) # => TypeError

金鑰會永遠轉換成字串且區分大小寫。當指定物件為金鑰,而且已定義 cache_key 方法時,這個方法就能呼叫來定義金鑰。否則,就會呼叫 to_param 方法。哈希和陣列也可以用來當作金鑰。這些元素將會以斜線分隔,而且會依金鑰排序 Hash 內的元素,以確保一致性。

cache.read('city') == cache.read(:city)   # => true

可以快取空值。

如果您的快取是在共享基礎設施中,您可以為快取條目定義命名空間。如果已定義命名空間,它將會字首附加至每個金鑰。命名空間可以是靜態值或 Proc。如果是 Proc,每次評估各個金鑰時都會呼叫它,以便您可使用應用邏輯來取消金鑰的效度。

cache.namespace = -> { @last_mod_time }  # Set the namespace to a variable
@last_mod_time = Time.now  # Invalidate the entire cache by changing namespace
方法
C
D
E
F
I
K
M
N
R
S
W

屬性

[R] 選項
[R] 靜音
[R] 靜音?

類別公開方法

new(options = nil)

建立新的快取。

選項

:namespace

設定快取的命名空間。如果您的應用程式與其他應用程式共用快取,特別可以使用此選項。

:serializer

快取值序列化器。必須對 dumpload 有反應。

根據快取格式版本,預設序列化器的不同(當使用 Rails 時,請透過 config.active_support.cache_format_version 設定)。每個格式版本的預設序列化器包含一個中繼機制,用於從任何格式版本取消序列化的值。此行為讓您可以在格式版本之間輕鬆執行遷移,而不會使整個快取失效。

您也可以指定 serializer: :message_pack 來使用基於 ActiveSupport::MessagePack 的預先設定序列化器。 :message_pack 序列化器包含相同的序列化中繼機制,讓您可以輕鬆從(或轉換至)預設序列化器。 :message_pack 序列化器可能會提升效能,但它需要 msgpack 這個 gem。

:compressor

序列化快取值後用於壓縮的壓縮器。必須對 deflateinflate 有反應。

預設壓縮器為 Zlib。若要定義一個自訂壓縮器,它也會解壓縮舊的快取條目,您可以檢查壓縮值的 Zlib "\x78" 簽章碼

module MyCompressor
  def self.deflate(dumped)
    # compression logic... (make sure result does not start with "\x78"!)
  end

  def self.inflate(compressed)
    if compressed.start_with?("\x78")
      Zlib.inflate(compressed)
    else
      # decompression logic...
    end
  end
end

ActiveSupport::Cache.lookup_store(:redis_cache_store, compressor: MyCompressor)
:coder

用於序列化和(選擇性)壓縮快取條目的編碼器。必須回應 dumpload

預設編碼器組成序列化器和壓縮器,並包含一些效能最佳化。如果您只需要覆寫序列化器或壓縮器,您應改為指定 :serializer:compressor 選項。

如果儲存體可以直接處理快取條目,您還可以指定 coder: nil 省略序列化器、壓縮器和編碼器。例如,如果您正在使用 ActiveSupport::Cache::MemoryStore,並且可以保證快取值不會異動,您可以指定 coder: nil,以避免防止異動所產生的負擔。

:coder 選項與 :serializer:compressor 選項互斥。若同時指定,會產生 ArgumentError

指定的任何其他選項都被視為相關快取操作的預設選項,例如 readwritefetch

# File activesupport/lib/active_support/cache.rb, line 295
def initialize(options = nil)
  @options = options ? validate_options(normalize_options(options)) : {}

  @options[:compress] = true unless @options.key?(:compress)
  @options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT

  @coder = @options.delete(:coder) do
    legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
    serializer = @options.delete(:serializer) || default_serializer
    serializer = Cache::SerializerWithFallback[serializer] if serializer.is_a?(Symbol)
    compressor = @options.delete(:compressor) { Zlib }

    Cache::Coder.new(serializer, compressor, legacy_serializer: legacy_serializer)
  end

  @coder ||= Cache::SerializerWithFallback[:passthrough]

  @coder_supports_compression = @coder.respond_to?(:dump_compressed)
end

實體公開方法

cleanup(options = nil)

透過移除過期的條目,清理快取。

選項傳遞給底層快取實作。

部分實作可能不支援此方法。

# File activesupport/lib/active_support/cache.rb, line 749
def cleanup(options = nil)
  raise NotImplementedError.new("#{self.class.name} does not support cleanup")
end

clear(options = nil)

清除整個快取。使用此方法時要小心,如果使用共用快取,這可能會影響其他程序。

選項 hash 傳遞給底層快取實作。

部分實作可能不支援此方法。

# File activesupport/lib/active_support/cache.rb, line 759
def clear(options = nil)
  raise NotImplementedError.new("#{self.class.name} does not support clear")
end

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

遞減快取中的整數值。

選項傳遞給底層快取實作。

部分實作可能不支援此方法。

# File activesupport/lib/active_support/cache.rb, line 740
def decrement(name, amount = 1, options = nil)
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
end

delete(name, options = nil)

刪除快取中的條目。如果刪除條目,傳回 true,否則傳回 false

選項傳遞給底層快取實作。

# File activesupport/lib/active_support/cache.rb, line 676
def delete(name, options = nil)
  options = merged_options(options)
  key = normalize_key(name, options)

  instrument(:delete, key, options) do
    delete_entry(key, **options)
  end
end

delete_matched(matcher, options = nil)

刪除與鍵值模式相符的所有輸入項。

選項傳遞給底層快取實作。

部分實作可能不支援此方法。

# File activesupport/lib/active_support/cache.rb, line 722
def delete_matched(matcher, options = nil)
  raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
end

delete_multi(names, options = nil)

刪除快取中的多個輸入項。傳回已刪除輸入項的數量。

選項傳遞給底層快取實作。

# File activesupport/lib/active_support/cache.rb, line 689
def delete_multi(names, options = nil)
  return 0 if names.empty?

  options = merged_options(options)
  names.map! { |key| normalize_key(key, options) }

  instrument_multi(:delete_multi, names, options) do
    delete_multi_entries(names, **options)
  end
end

exist?(name, options = nil)

如果快取含有給定鍵值的輸入項,則傳回 true

選項傳遞給底層快取實作。

# File activesupport/lib/active_support/cache.rb, line 703
def exist?(name, options = nil)
  options = merged_options(options)
  key = normalize_key(name, options)

  instrument(:exist?, key) do |payload|
    entry = read_entry(key, **options, event: payload)
    (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
  end
end

fetch(name, options = nil, &block)

使用給定的鍵值,從快取中擷取資料。如果快取中含有給定鍵值的資料,則傳回該資料。

如果快取中沒有此資料(快取遺漏),則傳回 nil。不過,如果已傳入區段,則會將該區段傳遞鍵值,並在快取遺漏時執行。區段的傳回值會以給定的快取鍵值寫入快取,而且會傳回該傳回值。

cache.write('today', 'Monday')
cache.fetch('today')  # => "Monday"

cache.fetch('city')   # => nil
cache.fetch('city') do
  'Duckburgh'
end
cache.fetch('city')   # => "Duckburgh"

選項

在內部,fetch 會在快取遺漏時呼叫 read_entry,並呼叫 write_entry。因此,fetch 支援與 readwrite 相同的選項。此外,fetch 支援下列選項

  • force: true - 強制進行快取「遺漏」,意即我們會將快取值視為遺漏狀態,即使它存在。在 force 為 true 時,傳遞區段是必須的,因此這樣總是會進行快取寫入作業。

    cache.write('today', 'Monday')
    cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
    cache.fetch('today', force: true) # => ArgumentError
    

    在您呼叫其他方法來詢問是否應該強制進行快取寫入作業時,:force 選項很有用。否則,只呼叫 write 會更為明確。

  • skip_nil: true - 避免快取 nil 結果

    cache.fetch('foo') { nil }
    cache.fetch('bar', skip_nil: true) { nil }
    cache.exist?('foo') # => true
    cache.exist?('bar') # => false
    
  • :race_condition_ttl - 指定一個時間,在這個時間內,過期的值可在新的值產生時重新使用。這可以用來避免快取輸入項過期時的競爭情況,方法是避免多個處理同時重新產生相同的輸入項(也稱為狗群效應)。

    當處理會遇到快取輸入項的情況,而該輸入項是在不到 :race_condition_ttl 秒前過期的,它會在產生新值前將到期時間延長 :race_condition_ttl 秒。在此延伸時間視窗中,在處理產生新值時,其他處理會繼續使用舊值。在第一個處理寫入新值之後,其他處理接著就會使用它。

    如果第一個處理在產生新值時發生錯誤,則其他處理可以在延伸時間視窗經過後,試著產生新值。

    # Set all values to expire after one minute.
    cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
    
    cache.write("foo", "original value")
    val_1 = nil
    val_2 = nil
    p cache.read("foo") # => "original value"
    
    sleep 1 # wait until the cache expires
    
    t1 = Thread.new do
      # fetch does the following:
      # 1. gets an recent expired entry
      # 2. extends the expiry by 2 seconds (race_condition_ttl)
      # 3. regenerates the new value
      val_1 = cache.fetch("foo", race_condition_ttl: 2) do
        sleep 1
        "new value 1"
      end
    end
    
    # Wait until t1 extends the expiry of the entry
    # but before generating the new value
    sleep 0.1
    
    val_2 = cache.fetch("foo", race_condition_ttl: 2) do
      # This block won't be executed because t1 extended the expiry
      "new value 2"
    end
    
    t1.join
    
    p val_1 # => "new value 1"
    p val_2 # => "original value"
    p cache.fetch("foo") # => "new value 1"
    
    # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
    # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1
    # more second to see the entry expire.
    sleep 1
    
    p cache.fetch("foo") # => nil
    

動態選項

在某些情況下,可能有必要根據快取值動態計算選項。為支援此功能,會傳遞 ActiveSupport::Cache::WriteOptions 執行個體,做為區段的第二個引數。例如

cache.fetch("authentication-token:#{user.id}") do |key, options|
  token = authenticate_to_service
  options.expires_at = token.expires_at
  token
end
# File activesupport/lib/active_support/cache.rb, line 444
def fetch(name, options = nil, &block)
  if block_given?
    options = merged_options(options)
    key = normalize_key(name, options)

    entry = nil
    unless options[:force]
      instrument(:read, key, options) do |payload|
        cached_entry = read_entry(key, **options, event: payload)
        entry = handle_expired_entry(cached_entry, key, options)
        if entry
          if entry.mismatched?(normalize_version(name, options))
            entry = nil
          else
            begin
              entry.value
            rescue DeserializationError
              entry = nil
            end
          end
        end
        payload[:super_operation] = :fetch if payload
        payload[:hit] = !!entry if payload
      end
    end

    if entry
      get_entry_value(entry, name, options)
    else
      save_block_result_to_cache(name, key, options, &block)
    end
  elsif options && options[:force]
    raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
  else
    read(name, options)
  end
end

fetch_multi(*names)

使用給定的金鑰從快取中擷取資料。如果快取中有使用給定金鑰的資料,則會傳回該資料。否則,會針對沒有資料的每個金鑰呼叫提供的區塊,而結果會寫入快取並傳回。因此,您需要傳遞一個區塊來傳回要寫入快取的資料。如果您不希望在找不到快取時寫入快取,請使用 read_multi

傳回具有每個名稱資料的雜湊。例如

cache.write("bim", "bam")
cache.fetch_multi("bim", "unknown_key") do |key|
  "Fallback value for key: #{key}"
end
# => { "bim" => "bam",
#      "unknown_key" => "Fallback value for key: unknown_key" }

您也可以透過 options 參數指定其他選項。請參閱 fetch 以了解詳細資料。其他選項會傳遞給基礎快取實作。例如

cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
  "buzz"
end
# => {"fizz"=>"buzz"}
cache.read("fizz")
# => "buzz"
sleep(6)
cache.read("fizz")
# => nil
# File activesupport/lib/active_support/cache.rb, line 595
def fetch_multi(*names)
  raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
  return {} if names.empty?

  options = names.extract_options!
  options = merged_options(options)
  keys    = names.map { |name| normalize_key(name, options) }
  writes  = {}
  ordered = instrument_multi :read_multi, keys, options do |payload|
    if options[:force]
      reads = {}
    else
      reads = read_multi_entries(names, **options)
    end

    ordered = names.index_with do |name|
      reads.fetch(name) { writes[name] = yield(name) }
    end
    writes.compact! if options[:skip_nil]

    payload[:hits] = reads.keys.map { |name| normalize_key(name, options) }
    payload[:super_operation] = :fetch_multi

    ordered
  end

  write_multi(writes, options)

  ordered
end

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

在快取中增加整數值。

選項傳遞給底層快取實作。

部分實作可能不支援此方法。

# File activesupport/lib/active_support/cache.rb, line 731
def increment(name, amount = 1, options = nil)
  raise NotImplementedError.new("#{self.class.name} does not support increment")
end

mute()

在區塊內靜音記錄器。

# File activesupport/lib/active_support/cache.rb, line 322
def mute
  previous_silence, @silence = @silence, true
  yield
ensure
  @silence = previous_silence
end

read(name, options = nil)

使用給定的金鑰從快取中讀取資料。如果快取中有使用給定金鑰的資料,則會傳回該資料。否則,會傳回 nil

請注意,如果資料是用 :expires_in:version 選項寫入的,則在傳回資料前,這兩個條件都會套用。

選項

  • :namespace - 替換此呼叫的儲存空間命名空間。

  • :version - 為快取項目指定版本。如果快取版本與請求版本不符,則讀取將被視為快取遺漏。此功能用來支援可重複使用的快取金鑰。

其他選項會由特定的快取儲存空間實作處理。

# File activesupport/lib/active_support/cache.rb, line 498
def read(name, options = nil)
  options = merged_options(options)
  key     = normalize_key(name, options)
  version = normalize_version(name, options)

  instrument(:read, key, options) do |payload|
    entry = read_entry(key, **options, event: payload)

    if entry
      if entry.expired?
        delete_entry(key, **options)
        payload[:hit] = false if payload
        nil
      elsif entry.mismatched?(version)
        payload[:hit] = false if payload
        nil
      else
        payload[:hit] = true if payload
        begin
          entry.value
        rescue DeserializationError
          payload[:hit] = false
          nil
        end
      end
    else
      payload[:hit] = false if payload
      nil
    end
  end
end

read_multi(*names)

一次從快取中讀取多個值。選項可以在最後一個參數中傳遞。

有些快取實作可能會最佳化此方法。

傳回將提供的名稱對應到找到值的雜湊。

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

  options = names.extract_options!
  options = merged_options(options)
  keys    = names.map { |name| normalize_key(name, options) }

  instrument_multi :read_multi, keys, options do |payload|
    read_multi_entries(names, **options, event: payload).tap do |results|
      payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
    end
  end
end

silence!()

靜音記錄器。

# File activesupport/lib/active_support/cache.rb, line 316
def silence!
  @silence = true
  self
end

write(name, value, options = nil)

使用金鑰將值寫入緩存。coderdumpload 方法必須支援這個值。

傳回 true 表示寫入成功,nil 表示與緩存後端溝通發生錯誤,或者 false 表示寫入失敗的原因不同。

預設情況下,會壓縮大小超過 1kB 的快取項目。壓縮功能讓更多資料可以使用相同的記憶體空間,降低快取驅逐頻率並提升命中率。

選項

  • compress: false - 關閉快取項目的壓縮功能。

  • :compress_threshold - 壓縮門檻,以位元組指定。大於此門檻的快取項目會壓縮。預設值為 1.kilobyte

  • :expires_in - 設定快取項目的相對到期時間,以秒指定。:expire_in:expired_in:expires_in 的別名。

    cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
    cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
    
  • :expires_at - 設定快取項目的絕對到期時間。

    cache = ActiveSupport::Cache::MemoryStore.new
    cache.write(key, value, expires_at: Time.now.at_end_of_hour)
    
  • :version - 指定快取項目的版本。從快取讀取時,如果快取版本與要求的版本不符,讀取將視為快取未命中。此功能用於支援可回收快取金鑰。

其他選項會由特定的快取儲存空間實作處理。

# File activesupport/lib/active_support/cache.rb, line 662
def write(name, value, options = nil)
  options = merged_options(options)
  key = normalize_key(name, options)

  instrument(:write, key, options) do
    entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
    write_entry(key, entry, **options)
  end
end

write_multi(hash, options = nil)

Cache 儲存 API,可一次寫入多個值。

# File activesupport/lib/active_support/cache.rb, line 551
def write_multi(hash, options = nil)
  return hash if hash.empty?

  options = merged_options(options)
  normalized_hash = hash.transform_keys { |key| normalize_key(key, options) }

  instrument_multi :write_multi, normalized_hash, options do |payload|
    entries = hash.each_with_object({}) do |(name, value), memo|
      memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
    end

    write_multi_entries entries, **options
  end
end

實例私人方法

key_matcher(pattern, options)

將選項中定義的命名空間加入配對金鑰的樣式中。支援 delete_matched 的實作應呼叫此方法,以將配對名稱的樣式轉譯為配對帶命名空間金鑰的樣式。

# File activesupport/lib/active_support/cache.rb, line 779
def key_matcher(pattern, options) # :doc:
  prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
  if prefix
    source = pattern.source
    if source.start_with?("^")
      source = source[1, source.length]
    else
      source = ".*#{source[0, source.length]}"
    end
    Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
  else
    pattern
  end
end