Cet article partagera avec vous 21 points que vous devez connaître lorsque vous utilisez Redis. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.
1. Spécifications d'utilisation de Redis
1.1.
Lorsque nous concevons la clé Redis, nous devons prêter attention aux points suivants :
- Préfixez la clé avec le nom de l'entreprise et utilisez un deux points Séparés pour éviter la couverture des conflits clés. Par exemple, live:rank:1
- Pour garantir que la sémantique de la clé est claire, la longueur de la clé doit être aussi courte que 30 caractères.
- key ne doit pas contenir de caractères spéciaux, tels que des espaces, des nouvelles lignes, des guillemets simples et doubles et d'autres caractères d'échappement.
- Les clés Redis doivent être définies sur ttl autant que possible pour garantir que les clés inutilisées puissent être effacées ou éliminées à temps.
1.2. Points de valeur standard
La valeur de Redis ne peut pas être définie à volonté.
Le premier point est qu'il y aura des problèmes si un grand nombre de bigKeys sont stockés, ce qui entraînera des requêtes lentes, une croissance excessive de la mémoire, etc.
- S'il s'agit d'un type String, la taille d'une seule valeur doit être contrôlée dans les 10k.
- S'il s'agit de type hash, list, set, zset, le nombre d'éléments ne dépasse généralement pas 5000.
Le deuxième point est de choisir le type de données approprié. De nombreux amis n'utilisent que le type String de Redis, principalement set et get. En fait, Redis fournit des types de structures de données riches, et dans certains scénarios commerciaux, il est plus adapté à d'autres résultats de données tels que hash、zset
. [Recommandations associées : Tutoriel vidéo Redis]
Contre-exemple :
set user:666:name jay set user:666:age 18
Exemple positif
hmset user:666 name jay age 18
1.3. Définissez le délai d'expiration de la clé, faites attention aux clés des différentes entreprises et essayez d'étaler le moins possible le délai d'expiration
Si un grand nombre de clés expirent à un certain moment, Redis peut être bloqué à ce moment-là, voire même un phénomène d'avalanche de cache, donc c'est généralement différent Le délai d’expiration des clés professionnelles doit être étalé. Parfois, si vous êtes dans le même métier, vous pouvez également ajouter une valeur aléatoire au délai pour étaler le délai d'expiration.
1.4. Il est recommandé d'utiliser des opérations par lots pour améliorer l'efficacité
Lorsque nous écrivons du SQL tous les jours, nous savons tous que les opérations par lots seront être plus efficace, une fois La mise à jour de 50 éléments est plus efficace que de boucler 50 fois et de mettre à jour un élément à chaque fois. En fait, le même principe s'applique aux commandes d'opération Redis.
L'exécution d'une commande sur le client Redis peut être divisée en 4 processus : 1. Envoyer la commande-> 2. Mise en file d'attente des commandes-> 3. Exécution de la commande-> 1 et 4 sont appelés RRT (Command Execution Round Trip Time). Redis fournit commandes d'opération par lots, telles que mget, mset , etc., qui peuvent efficacement enregistrer le RRT. Cependant, la plupart des commandes ne prennent pas en charge les opérations par lots, telles que hgetall, et mhgetall n'existe pas. Pipeline peut résoudre ce problème.
Qu'est-ce que Pipeline ? Il peut assembler un ensemble de commandes Redis, les transmettre à Redis via un RTT, puis renvoyer les résultats d'exécution de cet ensemble de commandes Redis au client dans l'ordre.
Regardons d'abord le modèle qui exécute n commandes sans utiliser Pipeline :
Exécuter n commandes avec Pipeline L'ensemble du processus nécessite 1 RTT. 🎜>
2. Commandes avec pièges dans Redis
2.1. O(n)
<span style="font-size: 18px;">O(n)</span>
Commandes de complexité, telles que <code><span style="font-size: 18px;">hgetall</span>
hgetall,<span style="font-size: 18px;">smember</span>
<span style="font-size: 18px;">smembre</span>
<span style="font-size: 18px;">lrange</span>
, lrange
hgetall、smember,lrange等这些命令不是一定不能使用,需要综合评估数据量,明确n的值,再去决定。 比如hgetall,如果哈希元素n比较多的话,可以优先考虑使用hscan。
2.2 慎用Redis的monitor命令
Redis Monitor 命令用于实时打印出Redis服务器接收到的命令,如果我们想知道客户端对redis服务端做了哪些命令操作,就可以用Monitor 命令查看,但是它一般调试用而已,尽量不要在生产上用!因为monitor命令可能导致redis的内存持续飙升。
monitor的模型是酱紫的,它会将所有在Redis服务器执行的命令进行输出,一般来讲Redis服务器的QPS是很高的,也就是如果执行了monitor命令,Redis服务器在Monitor这个客户端的输出缓冲区又会有大量“存货”,也就占用了大量Redis内存。
2.3、生产环境不能使用 keys指令
Redis Keys 命令用于查找所有符合给定模式pattern的key。如果想查看Redis 某类型的key有多少个,不少小伙伴想到用keys命令,如下:
keys key前缀*
但是,redis的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 缓存穿透问题
先来看一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。
Pénétration du cache : fait référence à l'interrogation d'une donnée qui ne doit pas exister. Puisque le cache n'est pas atteint, il doit être interrogé à partir de la base de données. Si la donnée est introuvable, elle ne sera pas écrite. au cache, ce qui entraînera des données inexistantes. Chaque fois que les données sont demandées, la base de données doit être interrogée, ce qui exerce une pression sur la base de données.
Pour faire simple, lors de l'accès à une requête de lecture, ni le cache ni la base de données n'ont une certaine valeur, ce qui fera que chaque requête de requête pour cette valeur pénétrera dans la base de données.
La pénétration du cache est généralement causée par les situations suivantes :
Comment éviter la pénétration du cache ? Généralement, il existe trois méthodes.
Principe du filtre Bloom : Il se compose d'un tableau bitmap avec une valeur initiale de 0 et de N fonctions de hachage. Effectuez N algorithmes de hachage sur une clé pour obtenir N valeurs. Hachez ces N valeurs dans le tableau de bits et définissez-les sur 1. Puis lors de la vérification, si ces positions spécifiques sont toutes à 1, alors filtrage Bloom Le serveur détermine que la clé existe. .
3.5 Problème de snowrun du cache
Snowrun du cache : fait référence au délai d'expiration de gros lots de données dans le cache , et La quantité de données de requête est énorme et toutes les requêtes accèdent directement à la base de données, provoquant une pression excessive sur la base de données et même des temps d'arrêt.
3.6 Problème de panne de cache
Panne de cache : fait référence à la touche de raccourci à un certain moment de time Lorsqu'elle expire, il y a un grand nombre de requêtes simultanées pour cette clé à ce moment-là, et un grand nombre de requêtes sont envoyées à la base de données.
La panne du cache semble un peu similaire. En fait, la différence entre eux est que les chutes de neige dans le cache signifient que la base de données est soumise à une pression excessive, voire en panne, et qu'elle est simplement due à un grand nombre de requêtes simultanées vers la base de données. niveau. On peut considérer que la panne est un sous-ensemble du cache snowrun. Certains articles estiment que la différence entre les deux réside dans le fait que la panne vise un certain cache de touches de raccourci, tandis que Xuebeng cible de nombreuses clés.
Il existe deux solutions :
3.7. Problème de touche de raccourci du cache
Dans Redis, nous appelons les touches à fréquence d'accès élevée comme touches de raccourci. Si une demande pour une certaine clé de point d'accès est envoyée à l'hôte du serveur, le volume de la demande est extrêmement important, ce qui peut entraîner des ressources hôte insuffisantes ou même des temps d'arrêt, affectant ainsi les services normaux.
Comment la clé du hotspot est-elle générée ? Il y a deux raisons principales :
- Les données consommées par les utilisateurs sont bien supérieures aux données produites, comme les ventes flash, les actualités brûlantes et d'autres scénarios où il y a plus de lecture et moins d'écriture.
- Le partage des requêtes est concentré, ce qui dépasse les performances d'un seul serveur Redi. Par exemple, si la clé de nom fixe et le hachage tombent sur le même serveur, la quantité d'accès instantané est énorme, dépassant le goulot d'étranglement de la machine. , et provoquant des problèmes de touches de raccourci.
Alors, comment identifier les touches de raccourci dans le développement quotidien ?
- Déterminez quelles touches de raccourci sont basées sur l'expérience ;
- Rapports de statistiques sur les clients
- Rapports de couche proxy de service
Comment résoudre le problème des touches de raccourci ?
- Extension du cluster Redis : ajoutez des copies de fragments pour équilibrer le trafic de lecture ;
- Hachez les touches de raccourci, telles que la sauvegarde d'une clé en tant que clé1, clé2... cléN, la même chose. N sauvegardes des données, et les N sauvegardes sont distribuées sur différentes partitions. Lors de l'accès, l'une des N sauvegardes est accessible de manière aléatoire pour partager davantage le trafic de lecture
- Utilisez le cache de deuxième niveau, c'est-à-dire ; , le cache local JVM, pour réduire les requêtes de lecture Redis.
4. Configuration et fonctionnement de Redis
4.1 Utiliser des connexions longues au lieu de connexions courtes , et configurez correctement le pool de connexions du client
4.2 Utiliser uniquement db0
L'architecture autonome de Redis interdit l'utilisation de non-db0 Il y a deux raisons
4.3 Définir la mémoire maximale + une stratégie d'élimination appropriée.
Afin d'éviter que l'arriéré de mémoire ne s'étende. Par exemple, parfois, lorsque le volume d'activité augmente, les clés Redis sont fortement utilisées, la mémoire n'est tout simplement pas suffisante et le responsable de l'exploitation et de la maintenance oublie d'augmenter la mémoire. Redis pourrait-il raccrocher comme ça ? Par conséquent, vous devez sélectionner la stratégie maxmemory (politique d'élimination maximale de la mémoire) et définir le délai d'expiration en fonction de l'activité réelle. Il existe un total de 8 stratégies d'élimination de mémoire :
4.4 Activer le mécanisme sans paresseux
La version Redis4.0+ prend en charge le mécanisme sans paresseux, si votre Redis l'a toujours Il existe bigKey, et il est recommandé de l'activer sans paresseux. Lorsqu'il est activé, si Redis supprime une bigkey, l'opération fastidieuse de libération de mémoire sera exécutée dans un thread d'arrière-plan, réduisant ainsi l'impact du blocage sur le thread principal.
Pour plus de connaissances liées à la programmation, veuillez visiter : Vidéo de programmation ! !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!