Luaスクリプトがクラスターノードの非ローカルキーにアクセスしようとしました問題解決

1.問題の説明

最近、最適化企業は、さまざまなビジネスシステムのキャッシュツールに標準化されたソリューションを提供する必要があります。各ビジネスシステムは、キャッシュされたデータをマップ構造を介して保存し、これらのマップをキャッシュシステムで取得して、redisデータベースに保存します。テクニカルマネージャーが考える最善の解決策は、マップコレクションをredisのハッシュテーブルに直接保存することです。ただし、ハッシュテーブルの各要素のキャッシュ時間を設定する必要があります。

問題があります:
1)ハッシュテーブルにデータを追加する場合、hmsetコマンドを使用できますが、このコマンドは有効期限を設定できません;
2)ハッシュ内の各キーとキーと値のペアは有効期限を設定できません時間;

2つ、ソリューションのアイデア

私が最初に考えたのは、プログラム内のマップコレクションをトラバースして各キー値を取得し、次に各キー値をredisに追加してから、有効期限を設定することでした。しかし、テクニカルマネージャーは、マップセットに大量のデータが含まれている場合、このパフォーマンスは確実に機能しないと述べました。だから私は他の方法しか考えられません。

話し合いの結果、当面は2つの解決策が思い浮かびました。

  • 解決策1:2つのハッシュ(hash_aとhash_b)を定義し、hash_aはキャッシュされたデータを格納するために使用され、hash_bは各キーの有効期限を格納するために使用されます。クエリを実行するときは、最初にhash_b内の各キーの有効期限をクエリします。有効期限が現在の時間よりも大きい場合は、キーの有効期限が切れていることを意味し、直接戻ります。有効期限が切れていない場合は、hash_aを介してクエリを実行します。キーを押し、キャッシュされたデータをユーザーに返します

  • 解決策2:ハッシュテーブルからキャッシュされたデータを読み取るLuaスクリプトを記述し、setexメソッドを呼び出して各キー値をredisに再度追加し、キャッシュ時間を指定します。luaスクリプトはredisでローカルに実行されるため、スクリプトの実行パフォーマンスは高くなります。
    ここに画像の説明を挿入
    オプション3:パイプラインを使用して達成します。最初にバックグラウンドマップデータを読み取り、次にsetexコマンドを使用して各マップ要素をパイプラインに追加します。追加が完了したら、すべてのコマンドをredisに送信して、一度にバッチ実行します。

包括的に考えると、スキーム1の操作は比較的面倒であり、同時に操作するにはさらに2つのハッシュが必要です。ソリューション3Redisクラスターはパイプラインの使用をサポートしていないため、最終的にソリューション2を選択して実装します。

3、コードの実装

redisに次のデータがあるとします。

# 添加数据
hmset human name zhongliwen age 30

プラン2によると、luaスクリプトによって実現される機能は、名前と年齢がある各属性を読み取ることです。次に、setexコマンドを実行して、それらをredisに追加し直します。

这个脚本比较简单。但是如果是刚学lua的同学,可能在编写时候遇到各种问题,需要有耐心慢慢调试。这里简单说明一下上面的lua代码:
```lua
# 定义变量,用于存储hash对象的所有属性名称。
local result = redis.call('hkeys', KEYS[1])

# 循环遍历result,k代表table的下标,v代表hash对象每个属性名称。
for k, v in ipairs(result) 
do 
  # 获取hash对象指定属性的值
  local val = redis.call('hget', KEYS[1], (v))
   
  # 将属性添加到redis中,并指定ARGV[1],ARGV[1]代表过期时间。
  redis.call('setex', v, ARGV[1], val) 
end

上記はluaスクリプトのすべてのコードです。記述後、redisクラスターの任意のノードでスクリプトをコンパイルします。

script load "local result = redis.call('hkeys', KEYS[1]) for k, v in ipairs(result) do local val = redis.call('hget', KEYS[1], (v)) redis.call('setex', v, ARGV[1], val) end"

script loadこのコマンドは、スクリプトコードをキャッシュに追加するために使用されます。コマンドを実行すると、数字の文字列が表示されます。この数字の文字列を介して、スクリプトコードをredisでローカルに実行することも、クライアント側で実行することもできます。
ここに画像の説明を挿入
スクリプトを呼び出すコード:

evalsha cd1e27f9d2b4844f55a1a94586cc46ee351f36f1 1 human 30

cd1e27f9d2b4844f55a1a94586cc46ee351f36f1は、実行されるluaスクリプトハッシュです。数字の1は、背面にパラメーターが1つしかないことを意味します。人間は、luaスクリプトに渡されるパラメーターです。数字の1が前面に指定されている場合、パラメーターは1つだけです。数字の30は、追加のパラメーターを表します。

ただし、上記のスクリプトを実行すると問題が発生しました。

ERR Error running script (call to cd1e27f9d2b4844f55a1a94586cc46ee351f36f1): @user_script:1: @user_script: 1: Lua script attempted to access a non local key in a cluster node

この問題の理由は、redisクラスター環境でluaスクリプトを使用してredisキーを操作する場合、これらのキーが同じスロットにあることを確認する必要があるためです。スタンドアロン環境の場合、この問題は発生しません。

解決策は、データを保存するときにキーを処理することです。Redis{...}は、キーの一部を使用してスロットの位置を計算することを規定してますしたがって、{...}同じスロットに格納されるように、各キーに同じものを追加できますハッシュ構造を使用する場合は、キーとフィールドが同じスロットに格納されていることを確認してください。

したがって、hmsetコマンドを次のように実行します。

hmset human{human} name{human} zhongliwen age{human} 30

上記のキーとフィールドはすべて追加され{human}、redisはそれ{human}使用してどのスロットに格納するかを計算します。これにより、luaスクリプトによって操作されるキーがすべて同じスロットに保存されます。

おすすめ

転載: blog.csdn.net/zhongliwen1981/article/details/108737045