Redis(四):Key読み書きおよび期限切れポリシー
7198 ワード
DB構造体Redisにはデフォルトで16のデータベースがあり、データを格納する前にSELECT INDEXでDB(デフォルトindexは0、DB構造体はserver.h/redisDb)を指定しなければならない.DBはキー値ペア情報を主に格納し維持する.Redisは現在、現在操作中のライブラリを取得するコマンドがないので、操作するたびにselectを選択するのが良いことに注意してください.
dict辞書はキー空間とも呼ばれ、すべてのキー値ペアが格納され、値は5つのタイプの1つである.expiresは、有効期限が設定されたkeyとその有効期限のキー値のペアを格納する.Key読み書きユーザがKeyを読み出すと、dictにKeyが存在しない場合、miss回数+1がクライアント空に戻る.そうでなければhit回数+1とキーの最後の使用時間が更新され、この値によってkeyのアイドル時間が計算され、アイドル時間の長さは回収ポリシーにも影響し、キーが失効した場合はdictとexpiresのキー値のペアを同時にクリアし、SlaveにDELコマンドを送信します.info statsでhistやmissなどの情報が表示され、Object idletime keyでkeyのアイドル時間が表示されます.キーを書き込むと、dictにキーが既に存在する場合、対応する値の更新が行われ、そうでない場合、新しいキー値ペアが格納されます.有効期限が設定されているKeyへの書き込みはexpires辞書にも格納され、削除時にもexpiresの対応するキー値ペアがクリアされます.Key有効期限設定redisには、4つのコマンドexpire、pexpire、expireat、pexpireatがkeyをバインドできる有効期限があります(persistはバインドを解除できます).expireとpexpireはkeyが生存できる時間を設定し、expireatとpexpireatはkeyが失効する時間点を設定し、前者は秒単位、後者はミリ秒単位で、最初の3つのコマンドの下部はpexpireatを使用します.Key期限切れクリーンアップポリシータイミングクリーンアップ:k/vペアを設定すると、同時にタイマをオンにするか、失効時点をタイマに注入し、有効時間が到着した後にキー値ペアを削除します.この方式は一般的に、消費されたメモリをできるだけ早く解放することができるが、CPUの性能に対する消費は深刻であり、特に大量のデータや大量の連続要求の場合、こんなに多くのCPU資源が消費されず、実際には一般的ではない. 不活性クリーンアップ:クライアントがkeyを要求したときに有効性が検出されます.この方法では、CPUリソースをタイミングクリーンアップのように大量に消費することはありませんが、メモリに対してメモリ漏洩が発生する可能性があります.クライアントが失効したキーを要求しないと、これらのキーはメモリに常駐し、解放されません(flushなどの操作を除きます). 定期清掃:周期的な検出keyの有効性(必ずしも全量keyとは限らない)は、CPU資源を大量に消費することもメモリ漏れを招くこともないが、keyは基本的にタイムリーに清掃されず、実際の清掃のタイミングと周波数は容易に確定しない.
Redisは総合的に不活性クリーンアップと定期クリーンアップの戦略を採用しており、不活性クリーンアップについては上に説明したが、ここでは定期クリーンアップのソースコードを分析し、3.2.8バージョンではソースコードがserverにある.c/activeExpireCycle,この関数は周期的にserverCron()を呼び出す.DatabasesCron()は間接的に呼び出され、周波数はデフォルトで#define CONFIG_DEFAULT_HZ 10 /* Time interrupt calls/sec. */毎秒10回、ソースコードは以下の通りです.
基本的にこの関数の動作パターンは,1)一定数のDBを反復し,最大数16と要約できる.2)各DBから最大20個のkey*1をランダムに抽出する:各keyのttlを取得し、平均ttl*2を統計的に求める:失効したkeyをクリーンアップし、失効したkeyの個数を記録し、失効したkeyの個数が1/4(5個未満)を超えない場合、現在のDBはクリーンアップを必要とせず、次のDBの処理を実行する.*3:長時間のブロックを防止するため、16回の実行ごとに実行時間が経過したかどうかを検出し、実行時間が上限に達した場合、今回のクリーンアップを終了し、次回の周期的な呼び出しを待つ.ただし、DB数が16個未満の場合、このステップは実行されません.この場合、実行時間が長くても、最後のクリーンアップが完了するまで終了しません.RDBとAOFの期限切れKeyに対する処理RDBとAOFの書き換えは本質的に現在のメモリに格納されているデータであるが、RDBはデータファイルとして格納されているが、書き換えAOFはコマンド形式で格納されているが、メモリにすでに失効しているKeyについては両方とも格納されず、RDBにとって利用可能なデータのみを保存する必要がある.ロード後に無効なデータをクリーンアップする必要はなく、AOFにとって最小命令セットを保存するだけで、書き込みと削除の2つの命令を保存する必要もありません.しかし、通常のAOFは失効Keyに対して、Delコマンドをaofファイルに追加します.プライマリスレーブノードの期限切れKeyに対する処理は、スレーブノードのデータがプライマリノードに由来するため、マスターノードとの状態を維持するために、Masterが削除命令を送信するまで、失効したkeyをクリーンアップするためにポリシーは使用されませんが、失効したKeyに対するすべての要求は空に戻ります.空に戻るのは、キーが失効したがプライマリノードdelコマンドが受信または失われていない場合、プライマリノードと一致する応答が必要であるためです.まとめ:
Redisデータベースは主にdictとexpiresの2つの辞書から構成され、dict辞書はキー値のペアを保存し、キーは文字列であり、値は5つのタイプの1つである.expires辞書は、キーと有効期限のペアを保存します.したがって、キーの操作は、辞書操作の上に確立されている である. Redisは、不活性な削除と定期的な削除の2つのポリシーを使用して、期限切れのキー を削除する.失効キーが削除されると、既存のAOFファイルにDELが追加されますが、RDBまたはAOF書き換えには失効キー は含まれません.プライマリノードがキーを削除すると、すべてのスレーブノードにDELコマンドが送信されます.スレーブノードの場合、プライマリノードとの一貫性を維持するために、DELコマンドを受信する前にポリシーを使用して削除することはありませんが、失効keyのすべてのリクエストは空に戻ります.
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
dict辞書はキー空間とも呼ばれ、すべてのキー値ペアが格納され、値は5つのタイプの1つである.expiresは、有効期限が設定されたkeyとその有効期限のキー値のペアを格納する.Key読み書きユーザがKeyを読み出すと、dictにKeyが存在しない場合、miss回数+1がクライアント空に戻る.そうでなければhit回数+1とキーの最後の使用時間が更新され、この値によってkeyのアイドル時間が計算され、アイドル時間の長さは回収ポリシーにも影響し、キーが失効した場合はdictとexpiresのキー値のペアを同時にクリアし、SlaveにDELコマンドを送信します.info statsでhistやmissなどの情報が表示され、Object idletime keyでkeyのアイドル時間が表示されます.キーを書き込むと、dictにキーが既に存在する場合、対応する値の更新が行われ、そうでない場合、新しいキー値ペアが格納されます.有効期限が設定されているKeyへの書き込みはexpires辞書にも格納され、削除時にもexpiresの対応するキー値ペアがクリアされます.Key有効期限設定redisには、4つのコマンドexpire、pexpire、expireat、pexpireatがkeyをバインドできる有効期限があります(persistはバインドを解除できます).expireとpexpireはkeyが生存できる時間を設定し、expireatとpexpireatはkeyが失効する時間点を設定し、前者は秒単位、後者はミリ秒単位で、最初の3つのコマンドの下部はpexpireatを使用します.Key期限切れクリーンアップポリシー
Redisは総合的に不活性クリーンアップと定期クリーンアップの戦略を採用しており、不活性クリーンアップについては上に説明したが、ここでは定期クリーンアップのソースコードを分析し、3.2.8バージョンではソースコードがserverにある.c/activeExpireCycle,この関数は周期的にserverCron()を呼び出す.DatabasesCron()は間接的に呼び出され、周波数はデフォルトで#define CONFIG_DEFAULT_HZ 10 /* Time interrupt calls/sec. */毎秒10回、ソースコードは以下の通りです.
/*
*type=ACTIVE_EXPIRE_CYCLE_FAST ; type=ACTIVE_EXPIRE_CYCLE_SLOW ;
*/
void activeExpireCycle(int type) {
static unsigned int current_db = 0; /* DB */
static int timelimit_exit = 0; /* */
static long long last_fast_cycle = 0; /* . */
int j, iteration = 0;
int dbs_per_call = CRON_DBS_PER_CALL; /* DB :16*/
long long start = ustime(), timelimit;
if (clientsArePaused()) return;
if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
/* , */
if (!timelimit_exit) return;
if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;
last_fast_cycle = start;
}
/* DB */
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
/* : 1000000 * 25/10/100 = 25000 == 25 */
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;
timelimit_exit = 0;
if (timelimit <= 0) timelimit = 1;
/* :1 */
if (type == ACTIVE_EXPIRE_CYCLE_FAST)
timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* in microseconds. */
/* DB */
for (j = 0; j < dbs_per_call; j++) {
int expired;
redisDb *db = server.db+(current_db % server.dbnum);
/* DB 1, DB */
current_db++;
/* DB 20 key
* 1: key ttl, ttl
* 2: key, key , key 1/4( 5 ), DB , DB 。
* 3: 16 , , 。
*/
do {
unsigned long num, slots;
long long now, ttl_sum;
int ttl_samples;
if ((num = dictSize(db->expires)) == 0) {
db->avg_ttl = 0;
break;
}
slots = dictSlots(db->expires);
now = mstime();
if (num && slots > DICT_HT_INITIAL_SIZE &&
(num*100/slots < 1)) break;
expired = 0;
ttl_sum = 0;
ttl_samples = 0;
/* 20 key */
if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;
while (num--) {
dictEntry *de;
long long ttl;
if ((de = dictGetRandomKey(db->expires)) == NULL) break;
ttl = dictGetSignedIntegerVal(de)-now;
if (activeExpireCycleTryExpire(db,de,now)) expired++;/* */
if (ttl > 0) {
ttl_sum += ttl;
ttl_samples++;
}
}
if (ttl_samples) {
long long avg_ttl = ttl_sum/ttl_samples;
if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;
db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);
}
/* 16 */
iteration++;
if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */
long long elapsed = ustime()-start;
latencyAddSampleIfNeeded("expire-cycle",elapsed/1000);
if (elapsed > timelimit) timelimit_exit = 1;
}
/* , . */
if (timelimit_exit) return;
/* >1/4 , DB . */
} while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
}
}
基本的にこの関数の動作パターンは,1)一定数のDBを反復し,最大数16と要約できる.2)各DBから最大20個のkey*1をランダムに抽出する:各keyのttlを取得し、平均ttl*2を統計的に求める:失効したkeyをクリーンアップし、失効したkeyの個数を記録し、失効したkeyの個数が1/4(5個未満)を超えない場合、現在のDBはクリーンアップを必要とせず、次のDBの処理を実行する.*3:長時間のブロックを防止するため、16回の実行ごとに実行時間が経過したかどうかを検出し、実行時間が上限に達した場合、今回のクリーンアップを終了し、次回の周期的な呼び出しを待つ.ただし、DB数が16個未満の場合、このステップは実行されません.この場合、実行時間が長くても、最後のクリーンアップが完了するまで終了しません.RDBとAOFの期限切れKeyに対する処理RDBとAOFの書き換えは本質的に現在のメモリに格納されているデータであるが、RDBはデータファイルとして格納されているが、書き換えAOFはコマンド形式で格納されているが、メモリにすでに失効しているKeyについては両方とも格納されず、RDBにとって利用可能なデータのみを保存する必要がある.ロード後に無効なデータをクリーンアップする必要はなく、AOFにとって最小命令セットを保存するだけで、書き込みと削除の2つの命令を保存する必要もありません.しかし、通常のAOFは失効Keyに対して、Delコマンドをaofファイルに追加します.プライマリスレーブノードの期限切れKeyに対する処理は、スレーブノードのデータがプライマリノードに由来するため、マスターノードとの状態を維持するために、Masterが削除命令を送信するまで、失効したkeyをクリーンアップするためにポリシーは使用されませんが、失効したKeyに対するすべての要求は空に戻ります.空に戻るのは、キーが失効したがプライマリノードdelコマンドが受信または失われていない場合、プライマリノードと一致する応答が必要であるためです.まとめ: