In diesem Artikel erfahren Sie 21 Punkte, die Sie bei der Verwendung von Redis wissen müssen. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.
1. wird der Schlüssel vorangestellt und durch einen Doppelpunkt getrennt, um ein Überschreiben von Schlüsselkonflikten zu verhindern. Zum Beispiel live:rank:1Um sicherzustellen, dass die Semantik des Schlüssels klar ist, sollte die Länge des Schlüssels weniger als 30 Zeichen betragen.
key darf keine Sonderzeichen wie Leerzeichen, Zeilenumbrüche, einfache und doppelte Anführungszeichen und andere Escape-Zeichen enthalten. Redis-Schlüssel sollten so weit wie möglich auf TTL eingestellt werden, um sicherzustellen, dass nicht verwendete Schlüssel rechtzeitig gelöscht oder entfernt werden können.
1.2. Wichtige Punkte der Wertspezifikation
- Der Wert von Redis kann nicht nach Belieben festgelegt werden.
- Der erste Punkt
- ist, dass es Probleme geben wird, wenn bigKey in großen Mengen gespeichert wird, was zu langsamen Abfragen, übermäßigem Speicherwachstum usw. führt.
Wenn es sich um einen String-Typ handelt, sollte die Größe eines einzelnen Werts innerhalb von 10 KB kontrolliert werden. Wenn es sich um einen Hash-, Listen-, Set- oder Zset-Typ handelt, überschreitet die Anzahl der Elemente im Allgemeinen 5000 nicht.
Der zweite Punkt ist die Auswahl des geeigneten Datentyps. Viele Freunde verwenden nur den String-Typ von Redis, hauptsächlich Set und Get. Tatsächlich bietet Redis reichhaltige Datenstrukturtypen. In einigen Geschäftsszenarien eignet es sich besser für andere Datenergebnisse wie hash und zset
. [Verwandte Empfehlungen: Redis-Video-Tutorial]
Positiv Beispiel
- Gegenbeispiel:
set user:666:name jay set user:666:age 18
hmset user:666 name jay age 18
1.3. Legen Sie die Ablaufzeit für den Schlüssel fest, achten Sie auf die Schlüssel verschiedener Unternehmen und versuchen Sie, die Ablaufzeit so weit wie möglich zu verteilenhash、zset
等其他数据结果。【相关推荐:Redis视频教程】
反例:
keys key前缀*
正例
if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁 expire(key_resource_id,100); //设置过期时间 try { do something //业务请求 }catch(){ } finally { jedis.del(key_resource_id); //释放锁 } }
1.3. 给Key设置过期时间,同时注意不同业务的key,尽量过期时间分散一点
如果大量的key在某个时间点集中过期,到过期的那个时间点,Redis可能会存在卡顿,甚至出现缓存雪崩现象,因此一般不同业务的key,过期时间应该分散一些。有时候,同业务的,也可以在时间上加一个随机值,让过期时间分散一些。
1.4.建议使用批量操作提高效率
我们日常写SQL的时候,都知道,批量操作效率会更高,一次更新50条,比循环50次,每次更新一条效率更高。其实Redis操作命令也是这个道理。
Redis客户端执行一次命令可分为4个过程:1.发送命令-> 2.命令排队-> 3.命令执行-> 4. 返回结果。1和4 称为RRT(命令执行往返时间)。 Redis提供了批量操作命令,如mget、mset等,可有效节约RRT。但是呢,大部分的命令,是不支持批量操作的,比如hgetall,并没有mhgetall存在。Pipeline 则可以解决这个问题。
Pipeline是什么呢?它能将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端.
我们先来看下没有使用Pipeline执行了n条命令的模型:
使用Pipeline执行了n次命令,整个过程需要1次RTT,模型如下:
2、Redis 有坑的那些命令
2.1. 慎用<span style="font-size: 18px;">O(n)</span>
复杂度命令,如<span style="font-size: 18px;">hgetall</span>
、<span style="font-size: 18px;">smember</span>
,<span style="font-size: 18px;">lrange</span>
🎜O (n)🎜
🎜Komplexitätsbefehle, wie 🎜🎜hgetall🎜
🎜, 🎜🎜smember🎜
🎜, 🎜🎜lrange🎜🎜Warten🎜🎜🎜🎜Weil Redis Befehle in einem einzelnen Thread ausführt. Die zeitliche Komplexität von Befehlen wie hgetall und smember beträgt O(n), wenn n weiter zunimmt, führt dies dazu, dass die Redis-CPU weiter ansteigt und die Ausführung anderer Befehle blockiert. 🎜<blockquote>hgetall、smember,lrange等这些命令不是一定不能使用,需要综合评估数据量,明确n的值,再去决定。 比如hgetall,如果哈希元素n比较多的话,可以优先考虑使用<strong>hscan</strong>。</blockquote>
<p><strong><span style="font-size: 18px;">2.2 慎用Redis的monitor命令</span></strong></p>
<p>Redis Monitor 命令用于实时打印出Redis服务器接收到的命令,如果我们想知道客户端对redis服务端做了哪些命令操作,就可以用Monitor 命令查看,但是它一般<strong>调试</strong>用而已,尽量不要在生产上用!因为<strong>monitor命令可能导致redis的内存持续飙升。</strong></p>
<blockquote>monitor的模型是酱紫的,它会将所有在Redis服务器执行的命令进行输出,一般来讲Redis服务器的QPS是很高的,也就是如果执行了monitor命令,Redis服务器在Monitor这个客户端的输出缓冲区又会有大量“存货”,也就占用了大量Redis内存。</blockquote>
<p><img src="https://img.php.cn/upload/image/778/919/256/16165561671008321%20Punkte,%20auf%20die%20Sie%20bei%20der%20Verwendung%20von%20Redis%20achten%20m%C3%BCssen%20(Zusammenfassung)" title="16165561671008321 Punkte, auf die Sie bei der Verwendung von Redis achten müssen (Zusammenfassung)" alt="21 Punkte, auf die Sie bei der Verwendung von Redis achten müssen (Zusammenfassung)"></p>
<p><strong><span style="font-size: 18px;">2.3、生产环境不能使用 keys指令</span></strong></p>
<p>Redis Keys 命令用于查找所有符合给定模式pattern的key。如果想查看Redis 某类型的key有多少个,不少小伙伴想到用keys命令,如下:</p>
<pre class="brush:php;toolbar:false">keys key前缀*</pre>
<p>但是,redis的<code>keys
是遍历匹配的,复杂度是O(n)
,数据库数据越多就越慢。我们知道,redis是单线程的,如果数据比较多的话,keys指令就会导致redis线程阻塞,线上服务也会停顿了,直到指令执行完,服务才会恢复。因此,一般在生产环境,不要使用keys指令。官方文档也有声明:
Warning: consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases. This command is intended for debugging and special operations, such as changing your keyspace layout. Don't use KEYS in your regular application code. If you're looking for a way to find keys in a subset of your keyspace, consider using sets.
其实,可以使用scan指令,它同keys命令一样提供模式匹配功能。它的复杂度也是 O(n),但是它通过游标分步进行,不会阻塞redis线程;但是会有一定的重复概率,需要在客户端做一次去重。
scan支持增量式迭代命令,增量式迭代命令也是有缺点的:举个例子, 使用 SMEMBERS 命令可以返回集合键当前包含的所有元素, 但是对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证 。
2.4 禁止使用flushall、flushdb
- Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。
- Flushdb 命令用于清空当前数据库中的所有 key。
这两命令是原子性的,不会终止执行。一旦开始执行,不会执行失败的。
2.5 注意使用del命令
删除key你一般使用什么命令?是直接del?如果删除一个key,直接使用del命令当然没问题。但是,你想过del的时间复杂度是多少嘛?我们分情况探讨一下:
O(1)
,可以直接del。O(n)
, n表示元素个数。因此,如果你删除一个List/Hash/Set/ZSet类型的key时,元素越多,就越慢。当n很大时,要尤其注意,会阻塞主线程的。那么,如果不用del,我们应该怎么删除呢?
- 如果是List类型,你可以执行
lpop或者rpop
,直到所有元素删除完成。- 如果是Hash/Set/ZSet类型,你可以先执行
hscan/sscan/scan
查询,再执行hdel/srem/zrem
依次删除每个元素。
2.6 避免使用SORT、SINTER等复杂度过高的命令。
执行复杂度较高的命令,会消耗更多的 CPU 资源,会阻塞主线程。所以你要避免执行如SORT、SINTER、SINTERSTORE、ZUNIONSTORE、ZINTERSTORE
等聚合命令,一般建议把它放到客户端来执行。
3、项目实战避坑操作
3.1 分布式锁使用的注意点
分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。秒杀下单、抢红包等等业务场景,都需要用到分布式锁。我们经常使用Redis作为分布式锁,主要有这些注意点:
3.1.1 两个命令SETNX + EXPIRE分开写(典型错误实现范例)
if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁 expire(key_resource_id,100); //设置过期时间 try { do something //业务请求 }catch(){ } finally { jedis.del(key_resource_id); //释放锁 } }
如果执行完setnx
加锁,正要执行expire设置过期时间时,进程crash或者要重启维护了,那么这个锁就“长生不老”了,别的线程永远获取不到锁啦,所以一般分布式锁不能这么实现。
3.1.2 SETNX + value值是过期时间 (有些小伙伴是这么实现,有坑)
long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间 String expiresStr = String.valueOf(expires); // 如果当前锁不存在,返回加锁成功 if (jedis.setnx(key_resource_id, expiresStr) == 1) { return true; } // 如果锁已经存在,获取锁的过期时间 String currentValueStr = jedis.get(key_resource_id); // 如果获取到的过期时间,小于系统当前时间,表示已经过期 if (currentValueStr != null && Long.parseLong(currentValueStr) <p>这种方案的<strong>缺点</strong>:</p><blockquote><ul> <li>过期时间是客户端自己生成的,分布式环境下,每个客户端的时间必须同步</li> <li>没有保存持有者的唯一标识,可能被别的客户端释放/解锁。</li> <li>锁过期的时候,并发多个客户端同时请求过来,都执行了<code>jedis.getSet()</code>,最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。</li> </ul></blockquote><p><strong>3.1.3: SET的扩展命令(SET EX PX NX)(注意可能存在的问题)</strong></p><pre class="brush:php;toolbar:false">if(jedis.set(key_resource_id, lock_value, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { jedis.del(key_resource_id); //释放锁 } }
这个方案还是可能存在问题:
3.1.4 SET EX PX NX + 校验唯一随机值,再删除(解决了误删问题,还是存在锁过期,业务没执行完的问题)
if(jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { //判断是不是当前线程加的锁,是才释放 if (uni_request_id.equals(jedis.get(key_resource_id))) { jedis.del(lockKey); //释放锁 } } }
在这里,判断是不是当前线程加的锁和释放锁不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。
一般也是用lua脚本代替。lua脚本如下:
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end;
3.1.5 Redisson框架 + Redlock算法 解决锁过期释放,业务没执行完问题+单机问题
Redisson 使用了一个Watch dog
解决了锁过期释放,业务没执行完问题,Redisson原理图如下:
以上的分布式锁,还存在单机问题:
如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。
针对单机问题,可以使用Redlock算法。有兴趣的朋友可以看下我这篇文章哈,七种方案!探讨Redis分布式锁的正确使用姿势
3.2 缓存一致性注意点
有兴趣的朋友,可以看下我这篇文章哈:并发环境下,先操作数据库还是先操作缓存?
3.3 合理评估Redis容量,避免由于频繁set覆盖,导致之前设置的过期时间无效。
我们知道,Redis的所有数据结构类型,都是可以设置过期时间的。假设一个字符串,已经设置了过期时间,你再去重新设置它,就会导致之前的过期时间无效。
Redis setKey
源码如下:
void setKey(redisDb *db,robj *key,robj *val) { if(lookupKeyWrite(db,key)==NULL) { dbAdd(db,key,val); }else{ dbOverwrite(db,key,val); } incrRefCount(val); removeExpire(db,key); //去掉过期时间 signalModifiedKey(db,key); }
实际业务开发中,同时我们要合理评估Redis的容量,避免频繁set覆盖,导致设置了过期时间的key失效。新手小白容易犯这个错误。
3.4 缓存穿透问题
先来看一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。
Cache-Penetration: bezieht sich auf das Abfragen von Daten, die nicht vorhanden sein dürfen. Da der Cache nicht vorhanden ist, müssen sie aus der Datenbank abgefragt werden. Dies wird nicht der Fall sein Dies führt dazu, dass die nicht vorhandenen Daten jedes Mal angefordert werden, um sie abzufragen, wodurch die Datenbank unter Druck gesetzt wird.
Um es einfach auszudrücken: Wenn auf eine Leseanforderung zugegriffen wird, haben weder der Cache noch die Datenbank einen bestimmten Wert, was dazu führt, dass jede Abfrageanforderung für diesen Wert in die Datenbank eindringt. Dies ist eine Cache-Penetration. Cache-Penetration wird im Allgemeinen durch die folgenden Situationen verursacht:Wie vermeide ich das Eindringen in den Cache? Generell gibt es drei Methoden.
3.5 Cache-Snowrun-Problem
Cache-Snowrun: bezieht sich auf die Ablaufzeit einer großen Datenmenge im Cache, die Abfragedaten sind riesig und die Anforderungen greifen direkt auf die Datenbank zu, was zu übermäßigem Druck führt auf die Datenbank und sogar Ausfallzeiten.
Es gibt zwei Lösungen: 1. Verwenden Sie das Mutex-Sperrschema
. Wenn der Cache fehlschlägt, laden Sie die Datenbankdaten nicht sofort, sondern verwenden bei erfolgreicher Rückkehr zunächst einige atomare Operationsbefehle, z. B. (Setnx von Redis), um den Vorgang durchzuführen. Laden Sie bei Erfolg die Datenbankdatenbankdaten und richten Sie den Cache ein. Versuchen Sie andernfalls erneut, den Cache abzurufen.2. „Lässt nie ab“
bedeutet, dass keine Ablaufzeit festgelegt ist, aber wenn die Hotspot-Daten bald ablaufen, aktualisiert der asynchrone Thread die Ablaufzeit und legt sie fest.Die von den Benutzern verbrauchten Daten sind viel größer als die produzierten Daten, wie z. B. Flash-Sales, aktuelle Nachrichten und andere Szenarien, in denen mehr gelesen und weniger geschrieben wird. Das Anforderungs-Sharding ist konzentriert, was die Leistung eines einzelnen Redi-Servers übersteigt. Wenn beispielsweise der Schlüssel mit dem festen Namen und der Hash auf denselben Server fallen, ist der Umfang des sofortigen Zugriffs enorm, übersteigt den Maschinenengpass und verursacht Hot Schlüsselprobleme.
Wie erkennt man also Hotkeys in der täglichen Entwicklung?
Bestimmen Sie, welche Hotkeys auf Erfahrung basieren.
- Client-Statistikberichte;
- Reporting an die Service-Agent-Ebene.
- Redis-Cluster-Erweiterung: Fügen Sie Shard-Kopien hinzu, um den Leseverkehr auszugleichen; Shards können Sie zufällig auf eines der N Backups zugreifen, um den Leseverkehr weiter zu teilen.
- Verwenden Sie den Second-Level-Cache, also den lokalen JVM-Cache, um Redis-Leseanforderungen zu reduzieren.
4. Redis-Konfiguration und -Betrieb
4.1 Verwenden Sie lange Verbindungen anstelle von kurzen Verbindungen und konfigurieren Sie den Verbindungspool des Clients ordnungsgemäß TCP jedes Mal Drei Handshakes und vier Wellen erhöhen die benötigte Zeit. Bei einer langen Verbindung wird die Verbindung jedoch einmal hergestellt und der Redis-Befehl kann für immer verwendet werden. Jiangzi kann die Zeit zum Herstellen einer Redis-Verbindung verkürzen. Der Verbindungspool kann mehrere Verbindungen auf dem Client herstellen, ohne sie freizugeben. Wenn eine Verbindung benötigt wird, muss nicht jedes Mal eine Verbindung erstellt werden, was Zeit spart. Allerdings müssen die Parameter entsprechend eingestellt werden, wenn Redis längere Zeit nicht betrieben wird, müssen Verbindungsressourcen rechtzeitig freigegeben werden.
Die Redis-Standalone-Architektur verbietet die Verwendung von Nicht-db0. Es gibt zwei Gründe:
- 4.2 verwendet nur db0
Bei einer Verbindung führt Redis den Befehl select 0 und select 1 aus, um zu wechseln, was verbraucht wird neue Energie. Redis Cluster unterstützt nur db0 und die Migration ist sehr kostspielig
Um zu verhindern, dass sich der Speicherrückstand vergrößert. Wenn beispielsweise das Geschäftsvolumen zunimmt, werden häufig Redis-Schlüssel verwendet, der Speicher reicht nicht aus und der Betriebs- und Wartungsmitarbeiter vergisst, den Speicher zu erhöhen. Könnte Redis einfach so auflegen? Daher müssen Sie die Maxmemory-Richtlinie (Richtlinie zur maximalen Speicherbeseitigung) auswählen und die Ablaufzeit basierend auf dem tatsächlichen Geschäft festlegen. Insgesamt gibt es 8 Speichereliminierungsstrategien:
- 4.3 Legen Sie maxmemory + geeignete Eliminierungsstrategie fest.
volatile-lru: Wenn der Speicher nicht ausreicht, um neu geschriebene Daten aufzunehmen, wird der LRU-Algorithmus (zuletzt verwendet) verwendet, um Schlüssel mit festgelegter Ablaufzeit zu eliminieren; lru: Wenn der Speicher nicht ausreicht, um die neu geschriebenen Daten aufzunehmen, wird der LRU-Algorithmus (zuletzt verwendet) verwendet, um alle Schlüssel zu entfernen. volatile-lfu: Neu hinzugefügt in Version 4.0: Wenn der Speicher nicht ausreicht, um neu geschriebene Daten aufzunehmen, wird der LFU-Algorithmus verwendet, um Schlüssel unter abgelaufenen Schlüsseln zu löschen.
allkeys-lfu: Neu in Version 4.0: Wenn der Speicher nicht ausreicht, um die neu geschriebenen Daten aufzunehmen, wird der LFU-Algorithmus verwendet, um alle Schlüssel zu entfernen.
volatile-random: Wenn der Speicher nicht ausreicht, um die neu geschriebenen Daten aufzunehmen Neu geschriebene Daten, aus dem Schlüssel mit festgelegter Ablaufzeit, werden die Daten zufällig gelöscht;.
- allkeys-random: Wenn der Speicher nicht ausreicht, um die neu geschriebenen Daten aufzunehmen, werden Daten zufällig aus allen Schlüsseln entfernt.
- volatile-ttl: Wenn der Speicher nicht ausreicht, um die neu geschriebenen Daten aufzunehmen, wird der Schlüssel mit einer festgelegten Ablaufzeit entsprechend der Ablaufzeit gelöscht, und die zuvor abgelaufenen werden zuerst gelöscht;
- noeviction: Der Standardstrategie: Wenn der Speicher nicht ausreicht, um neu geschriebene Daten aufzunehmen, meldet der neue Schreibvorgang einen Fehler.
- 4.4 Lazy-Free-Mechanismus aktivieren
- Die Redis4.0+-Version unterstützt den Lazy-Free-Mechanismus. Wenn Ihr Redis noch über so etwas wie bigKey verfügt, wird empfohlen, den Lazy-Free-Mechanismus zu aktivieren. Wenn es aktiviert ist und Redis einen Bigkey löscht, wird der zeitaufwändige Vorgang der Speicherfreigabe in einem Hintergrundthread ausgeführt, wodurch die blockierende Auswirkung auf den Hauptthread verringert wird.
Weitere Programmierkenntnisse finden Sie unter: Programmiervideo! !
Das obige ist der detaillierte Inhalt von21 Punkte, auf die Sie bei der Verwendung von Redis achten müssen (Zusammenfassung). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!