байюнь
Синтаксис команд
Значение команды: удалите значение, соответствующее формату ключевой команды:
DEL [key1 key2 …]
Командуйте реальным боем:
127.0.0.1:6379> del key1 (integer) 1
Возвращаемое значение: количество удаленных ключей
Анализ исходного кода
Сначала мы открываем клиент redis и используем порт сервера redis GDB – P. Поскольку функцией обработки, соответствующей команде Del, является команда del (), точка останова выполняется по команде del, а затем в клиенте redis выполняются следующие команды:
127.0.0.1:6379> set key1 value1 EX 100 OK 127.0.0.1:6379> get key1 "value1" 127.0.0.1:6379> del key1
Сначала установите пару значений ключа как key1-value1 со сроком действия 100 секунд, а затем удалите ее в течение 100 секунд и выполните команду del key1, чтобы удалить ключ, срок действия которого еще не истек. Давайте посмотрим, как работает сервер radius:
Breakpoint 1, delCommand (c=0x7fb46230dec0) at db.c:487 487 delGenericCommand(c,0); (gdb) s delGenericCommand (c=0x7fb46230dec0, lazy=0) at db.c:468 468 void delGenericCommand(client *c, int lazy) { (gdb) n 471 for (j = 1; j < c->argc; j++) { (gdb) p c->argc $1 = 2 (gdb) p c->argv[0] $2 = (robj *) 0x7fb462229800 (gdb) p *$2 $3 = {type = 0, encoding = 8, lru = 8575647, refcount = 1, ptr = 0x7fb462229813}
Мы видим, что delcommand() напрямую вызывает универсальную команду deal (C, 0), которая, по-видимому, является инкапсулированной общей функцией удаления. Давайте войдем и взглянем на внутренний процесс выполнения.
void delGenericCommand(client *c, int lazy) { int numdel = 0, j; for (j = 1; j < c->argc; j++) { Expireifneeded (C - > dB, C - > argv [J]); // check whether the key has expired. Some additional operations are required for expiration Int deleted = lazy? Dbasyncdelete (c > dB, C > argv [J]): dbsyncdelete (c > dB, C > argv [J]); // asynchronous or synchronous deletion if (deleted) { Signalmodifiedkey (c > dB, C > argv [J]); // transaction related notifyKeyspaceEvent(NOTIFY_ Generic, "del", C - > argv [J], C > DB > ID); // publish / subscribe related notifications server.dirty ++; // AOF persistence related Numdel + +; // the number of deleted keys + +, which is the number of deleted keys returned } } addReplyLongLong(c,numdel); }
Прежде всего, мы непосредственно запускаем цикл for, количество циклов равно C > argc, и мы выводим, что оно равно 2. Глядя на имя переменной, мы можем предположить, что это могут быть два параметра команды del и key1. Мы выводим C > argv [0] и обнаруживаем, что это объект redis. Затем мы видим, что соответствующий тип кодировки равен 8, а именно тип emb str, оптимизированная версия формы хранения строк. Чтобы просмотреть его содержимое, наведите указатель на символ *:
(gdb) p *((char *)($3.ptr)) $4 = 100 'd' (gdb) p *((char *)($3.ptr+1)) $6 = 101 'e' (gdb) p *((char *)($3.ptr+2)) $7 = 108 'l' (gdb) p *((char *)($3.ptr+3))
Затем выведите следующий параметр:
(gdb) p *c->argv[1] $10 = {type = 0, encoding = 8, lru = 8575647, refcount = 1, ptr = 0x7fb4622297e3} (gdb) p *(char *)($10.ptr) $11 = 107 'k' (gdb) p *((char *)($10.ptr+1)) $12 = 101 'e' (gdb) p *((char *)($10.ptr+2)) $13 = 121 'y' (gdb) p *((char *)($10.ptr+3)) $14 = 49 '1'
Мы видим, что значения ключа деланда 1 хранятся в массиве C > argv. Однако следующий цикл for начинается с 1 и не содержит имени команды, поэтому для параметра key1 выполняется только один цикл. Затем вызовите функцию expireifneeded():
472 expireIfNeeded(c->db,c->argv[j]); (gdb) s expireIfNeeded (db=0x7fb46221a800, key=0x7fb4622297d0) at db.c:1167 1167 int expireIfNeeded(redisDb *db, robj *key) { (gdb) n 1168 if (!keyIsExpired(db,key)) return 0; (gdb) 1187 } (gdb)
Мы обнаруживаем, что он определяет, истек срок действия ключа или нет, и если нет, он возвращает 0. Таким образом, для команд удаления существует разница между удалениями с истекшим и не истекшим сроком действия. Пропусти здесь и двигайся дальше:
473 int deleted = lazy ? dbAsyncDelete(c->db,c->argv[j]) : dbSyncDelete(c->db,c->argv[j]);
Вот ленивый параметр. Параметр lazy определяет, следует ли вызывать функцию db async delete() или функцию dbsync delete (). Давайте выведем это значение:
(gdb) p lazy $15 = 0
Значение lazy равно 0. Буквально это, по-видимому, означает, что нет необходимости в ленивом удалении. Неактивное удаление соответствует функции db sync delete (), то есть синхронному удалению. Здесь мы можем знать, что не ленивое удаление соответствует синхронному удалению. Мы выполнили удаление dbsync (c > dB, C > argv [J])
int dbSyncDelete(redisDb *db, robj *key) { If (dictsize (DB > expires) > 0) dictdelete (DB > expires, key > PTR); // delete the expiration time dictionary entry corresponding to key1 if (dictDelete(db->dict,key->ptr) == DICT_ OK) {// delete the key1-value1 key value pair in the dictionary if (server.cluster_enabled) slotToKeyDel(key); return 1; } else { return 0; } }
В redis время истечения срока действия и данные хранятся в двух словарях в базе данных redis. Обсуждалась структура словаря. В словаре времени истечения срока действия ключ-это наш ключевой ключ1; словарь пространства ключей хранит пары реальных значений ключей. Поэтому нам нужно удалить время истечения срока действия и значение данных в словаре. У нас нет глубокого понимания конкретной логики удаления. Мы знаем только, что в словаре dict существуют пары время истечения срока действия и значение ключа
typedef struct redisDb { ... Dict * dict; // key space dictionary to save all key value pairs in the database Dict * expires // the expiration time dictionary is used to save the expiration time of the key ... } redisDb;
После успешного удаления значение удаленной переменной равно 1. Будет выполнено следующее if, и две функции будут вызваны для выполнения некоторых операций транзакции и публикации/подписки. Мы не будем подробно объяснять эти две части. Затем мы сохраним удаленную сумму + + в выходной буфер, и выполнение команды будет завершено.
расширять
Ленивое удаление
В предыдущем примере мы вызвали метод dbsync delete() для синхронизации операции удаления. Однако, если значение lazy равно true, то есть если включена политика отложенного удаления, будет вызван метод асинхронного удаления бд()
#define LAZYFREE_THRESHOLD 64 int dbAsyncDelete(redisDb *db, robj *key) { //When to remove an expired key from the expired key dictionary if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr); //Find the location pointer of key in the key space dictionary dictEntry *de = dictUnlink(db->dict,key->ptr); if (de) { //Get the value through the key pointer robj *val = dictGetVal(de); //Calculate and determine whether to delete lazily. If only a very small key is deleted, the lazy deletion strategy is not needed, and it can be deleted synchronously. size_t free_effort = lazyfreeGetFreeEffort(val); //When the current price exceeds the threshold value of 64, the lazy deletion task will be distributed to the background thread to do, without blocking the main process if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) { atomicIncr(lazyfree_ Objects, 1); // number of lazy deleted objects++ bioCreateBackgroundJob(BIO_ LAZY_ Free, Val, null, null); // delete the task lazily dictSetVal(db->dict,de,NULL); } } if (de) { dictFreeUnlinkedEntry(db->dict,de); if (server.cluster_enabled) slotToKeyDel(key); return 1; } else { return 0; } }
Мы видим, что перед отложенным удалением нам нужно рассчитать, подходит ли текущая пара значений ключа для отложенного удаления. Политика отложенного удаления может быть включена только в том случае, если время отложенного удаления превышает пороговое значение 64. Поэтому мы знаем, что он использует асинхронные потоки для задержки восстановления удаленных пар значений ключей. Так зачем же ты это делаешь? Причина в том, что если удаляемая пара значений ключа занимает большой объем памяти, одновременное синхронное удаление занимает много времени, что приведет к блокировке основного процесса и невозможности предоставления услуг внешнему миру. Поэтому, чтобы решить эту проблему, redis выдвигает концепцию инверсии ленивого удаления 4.0, которая решает проблему блокировки удаления большого ключа асинхронным потоком. Асинхронный поток имеет специальное имя, выделенное красным цветом. Это биология. Полное название-фоновый ввод-вывод, что означает, что поток ввода-вывода работает тихо за кулисами. Это носитель и ядро ленивого
справочный материал
[анализ исходного кода redis] краткая история lazy без redis