关于redis:这20道Redis经典面试题你还不会就别去面试了

本系列会零碎的整顿MySQL,Redis,SSM框架,算法,计网等面试常问技术栈的面试题,本文次要是整顿分享了Redis相干的面试题,MySQL、Spring、JVM之前曾经更新了,须要的同学也能够去看一下,心愿对正在筹备秋招的你们有所帮忙! 当然集体整顿的所有面试题都无偿分享,只求大伙一个点赞关注转发三连,这些文档都放在文末了,须要的同学能够自取1. 什么是Redis?它次要用来什么的?Redis,英文全称是Remote Dictionary Server(近程字典服务),是一个开源的应用ANSI C语言编写、反对网络、可基于内存亦可长久化的日志型、Key-Value数据库,并提供多种语言的API。 与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度十分快,每秒能够解决超过10万次读写操作。因而redis被广泛应用于缓存,另外,Redis也常常用来做分布式锁。除此之外,Redis反对事务、长久化、LUA 脚本、LRU 驱动事件、多种集群计划。 2.说说Redis的根本数据结构类型大多数小伙伴都晓得,Redis有以下这五种根本类型: String(字符串)Hash(哈希)List(列表)Set(汇合)zset(有序汇合)它还有三种非凡的数据结构类型 GeospatialHyperloglogBitmap2.1 Redis 的五种根本数据类型 String(字符串)简介:String是Redis最根底的数据结构类型,它是二进制平安的,能够存储图片或者序列化的对象,值最大存储为512M简略应用举例: set key value、get key等利用场景:共享session、分布式锁,计数器、限流。外部编码有3种,int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)C语言的字符串是char[]实现的,而Redis应用SDS(simple dynamic string) 封装,sds源码如下: struct sdshdr{ unsigned int len; // 标记buf的长度 unsigned int free; //标记buf中未应用的元素个数 char buf[]; // 寄存元素的坑}SDS 结构图如下: Redis为什么抉择SDS构造,而C语言原生的char[]不香吗? 举例其中一点,SDS中,O(1)工夫复杂度,就能够获取字符串长度;而C 字符串,须要遍历整个字符串,工夫复杂度为O(n)Hash(哈希)简介:在Redis中,哈希类型是指v(值)自身又是一个键值对(k-v)构造简略应用举例:hset key field value 、hget key field外部编码:ziplist(压缩列表) 、hashtable(哈希表)利用场景:缓存用户信息等。留神点:如果开发应用hgetall,哈希元素比拟多的话,可能导致Redis阻塞,能够应用hscan。而如果只是获取局部field,倡议应用hmget。字符串和哈希类型对比方下图: List(列表)简介:列表(list)类型是用来存储多个有序的字符串,一个列表最多能够存储2^32-1个元素。简略实用举例:lpush key value [value ...] 、lrange key start end外部编码:ziplist(压缩列表)、linkedlist(链表)利用场景:音讯队列,文章列表,一图看懂list类型的插入与弹出: list利用场景参考以下: lpush+lpop=Stack(栈) lpush+rpop=Queue(队列) lpsh+ltrim=Capped Collection(无限汇合) lpush+brpop=Message Queue(音讯队列) Set(汇合) 简介:汇合(set)类型也是用来保留多个的字符串元素,然而不容许反复元素简略应用举例:sadd key element [element ...]、smembers key外部编码:intset(整数汇合)、hashtable(哈希表)留神点:smembers和lrange、hgetall都属于比拟重的命令,如果元素过多存在阻塞Redis的可能性,能够应用sscan来实现。利用场景:用户标签,生成随机数抽奖、社交需要。有序汇合(zset)简介:已排序的字符串汇合,同时元素不能反复简略格局举例:zadd key score member [score member ...],zrank key member底层外部编码:ziplist(压缩列表)、skiplist(跳跃表)利用场景:排行榜,社交需要(如用户点赞)。2.2 Redis 的三种非凡数据类型Geo:Redis3.2推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。HyperLogLog:用来做基数统计算法的数据结构,如统计网站的UV。Bitmaps :用一个比特位来映射某个元素的状态,在Redis中,它的底层是基于字符串类型实现的,能够把bitmaps成作一个以比特位为单位的数组3. Redis为什么这么快? ...

July 9, 2022 · 3 min · jiezi

关于redis:缓存

一、缓存的应用即时性、数据一致性要求不高的访问量大且更新频率不高的数据(读多,写少)举例:电商利用,商品分类,商品列表等适宜缓存并加一个生效工夫(依据数据更新频率来定),后盾如果公布一个商品,买家须要5分钟能力看到新的商品个别还是能够承受的二、高并发下缓存生效问题缓存穿透 查问一个不存在的数据,因为缓存是不命中的,将去查询数据库,然而数据库也无此记录,咱们没有将这个此查问的null写入缓存,这将导致这个不存在的数据每次申请都到存储层查问,失去了缓存的意义。 危险:利用不存在的数据进行攻打,数据库刹时压力增大,最终导致解体 解决:null后果缓存,并退出短暂过期工夫缓存雪崩 指在咱们设置缓存时key采纳了雷同的过期工夫,导致缓存在某一时刻同时生效,申请全副转发到DB,DB刹时压力过重雪崩。 解决:原有生效工夫的根底上减少一个随机值,比方1-5分钟随机,这样没有一个缓存的过期工夫的反复率就会升高,就很难引发个体生效事件。缓存击穿 对于设置过期工夫的key,如果这些key可能会在某些工夫点被高并发地拜访,是一种十分"热点"的数据。 如果这个key在大量申请同时进来前正好生效,那么所有对这个key的数据查问都落到DB,咱们称为缓存击穿。 解决:加锁,大量并发只让一个去查,其他人期待,查到后开释锁,其他人获取到锁,先查缓存,就会有数据,不必去DB三、本地锁只有是同一把锁,就能锁住须要这把锁的所有线程,1. synchronized(this),JUC(lock) SpringBoot 所有的组件在容器中都是单例的,一个服务一个容器,一个容器一个实例,每个this是不同的锁, 四、分布式锁基本原理: 能够去一个中央占坑,占到就执行逻辑,否则期待,直到开释锁,"占坑"能够去redis,能够去数据库,能够去任何大家都能拜访到的中央,期待能够自旋的形式。问题 setnx 占好了位,业务异样或者程序在页面过程中宕机,没有执行删除锁逻辑,就造成了死锁,能够通过设置锁的主动过期,即便没有删除,会主动删除,设置过期和加锁要原子操作如果业务工夫长,锁本人过期了,咱们间接删除,有可能把他人正在持有的锁删除了,占锁的时候指定uuid,每个人匹配本人的锁才删除。删除之前判断是否是本人的 String lockValue = stringRedisTemplate.opsForValue().get("lock"); if (uuid.equals(lockValue)) { stringRedisTemplate.delete("lock"); }但这样还是有问题, 从redis取出值,返回的路上,redis过期了,别的线程加锁胜利,这时判断为true,删除就删除了别的线程的锁,所以获取,判断,删除要是原子操作,通过lua脚本来实现。 Redisson 阻塞式期待,默认加锁都是30s工夫锁的主动续期,如果业务超长,运行期间主动给锁续上新的30s,不必放心业务工夫长,锁主动过期被删掉加锁业务只有运行实现,就不会给以后锁续期,即便不手动解锁,锁默认在30秒当前主动删除读写锁 保障肯定能读到最新数据,批改期间,写锁是一个排他锁(互斥锁、独享锁),读锁是一个共享锁 写锁没开释读就必须期待 读 + 读:相当于无锁 写 + 读:期待写锁开释 写 + 写:阻塞形式 读 + 写:有读锁,写也须要期待 只有有写的存在,都必须期待 五、缓存一致性缓存中的数据如何与数据库保持一致(缓存数据一致性) 双写模式 批改了数据库再查下,保留至缓存。两个线程批改同一条数据库,因为卡顿起因,导致写缓存2在最前,写缓存1在前面就呈现不统一。 脏数据问题:这时暂时性脏数据问题,然而在数据稳固,缓存过期后,又能失去最新的正确数据(最终一致性)生效模式 批改数据库,删除缓存无论双写还是生效模式,都会导致缓存不统一问题,即多个实例同时更新会出事,怎么办? 如果是用户维度数据(订单数据、用户数据),这种并发几率小,不必思考这个问题,缓存数据加上过期工夫,每隔一段时间触发读被动更新即可如果是菜单,商品介绍等根底数据,也能够去应用canal订阅binlog的形式。缓存数据+过期工夫也足够解决大部分业务对于缓存的要求通过加锁保障并发读写,写写的时候按程序排好队,读读无所谓,所以适宜读写锁(业务不关心脏数据,容许长期脏数据可疏忽)总结 咱们能放入缓存的数据本就不应该是实时性的,一致性要求超高的,所以缓存数据加上过期工夫,保障每天拿到以后的最新数据即可咱们不应该适度设计,减少零碎的复杂性遇到实时性、一致性要求高到的数据,就应该查数据库,即便慢点

July 9, 2022 · 1 min · jiezi

关于redis:redis-存储结构原理-1

对于 redis 置信大家都不生疏了,之前有从 0 -1 分享过 redis 的根本应用形式,用起来倒是都没有啥问题了,不过还是那句话,会利用之后,咱们必须要究其原理,知其然知其所以然 明天咱们来分享一下对于 redis 的存储构造的原理 redis 的存储构造的原理咱们都晓得 redis 是一个 K-V 内存数据库,相似于 memcache ,那么个别存储这种 K-V 键值对的数据结构是什么呢? 是 红黑树 , 那么咱们对于红黑树的增删改查的工夫复杂度是 O(logN),对于红黑树而言,只有内存足够,那么这个 N 是能够无限大的 这对于 redis 来说是没有方法满足 redis 的需要,那么咱们是否能够将复杂度升高到 O(1) 呢,感兴趣的,咱们能够来摸索一下? hash 表能满足 O(1) 工夫复杂度的数据结构有啥呢?咱们是不是能够想到 hash 表 具体 hash 表是怎么的一种构造,后面有文章曾经分享过一些,redis 基础性的数据结构能够查看历史文章:【Redis 系列】redis 学习四,set 汇合,hash 哈希,zset 有序汇合初步 redis 的 key 反对哪些类型?redis 反对的 key 有: longdoubleintstring - 可见的字符串和二进制字符串,key 都是 string 类型实际上最终到 redis 解决的时候,上述类型,都是对应依照 sring 类型进行存储的 这个 key 是有法则的 key,并且是强随机性的 ...

July 8, 2022 · 1 min · jiezi

关于redis:NET-Redis-Client-又多了一个选择还在被-StackExchangeRedis-Timeout-问题困扰吗

前言.NET 下 RedisClient SDK 抉择挺多,国人罕用收费的有 StackExchange.Redis/CSRedis/Newlife.Redis,免费的有 ServiceStack.Redis。 小弟从接手 CSRedis 代码 2016 年至今保护了6年,起因是初入 .NETCore 坑可选择性少,应用的 StackExchange.Redis 产生 Timeout 问题无奈解决,我的项目首急上线于是应用了 CSRedis,因为作者进行保护一些扩大或性能得不到解决,所以起初间接引入源码到我的项目内改良,减少了 RedisHelper、连接池、集群、以及高版本 Redis-server 的一些命令,最初因为改变太多与原作者开源线路失落,且首次接触不懂开源协定,间接创立了 CSRedisCore 仓储进行了社区开源保护,对此行为给原作者致歉,并且在 CSRedisCore 首页表明致谢话语。 越往后面,小弟发现 CSRedisCore 本人一些谬误的改变,又或者说原作者的代码实现理念难以对 redis-server 高版本反对,又或者会造成破坏性降级,与其这样不如从新写一个 RedisClient,于是 FreeRedis 就这样诞生了。 开源理念FreeRedis 的命名来自,“自在”、“收费”,它和名字与 FreeSql 是一个理念,繁难是他们统一的追寻方向,最低可反对 .NET Framework 4.0 运行环境,反对到 Redis-server 7.0。 感激 Nuget FreeRedis 原拥有者对 FreeRedis 的割爱,他的开源地址:https://gitee.com/LeanCai 现在,FreeRedis 从第一个版本公布至今 20个月,工夫验证了其可靠性,是时候公开给大家,多一个抉择多一条路。FreeRedis 整个源码是零依赖,应用它只会在 bin 目录产生一个 FreeRedis.dll,十分的轻量级,并且其性能十分弱小: <h1> FreeRedis </h1> 基于 .NET 的 Redis 客户端,反对 .NET Core 2.1+、.NET Framework 4.0+ 以及 Xamarin。 ...

July 7, 2022 · 2 min · jiezi

关于redis:Redis常见指令

定义Redis能够简略的了解成由键值对组成的nosql数据库(举个例子,键用id,值用JSON字符串)。 指令具体指令阐明见官网文档 常见的generic指令:KEYS :查问合乎模板要求的所有key相似正则 (因为Redis是单线程,这样在大规模数据的查问中会导致阻塞,所以强烈不举荐在生产环境中应用)DEL :删除一个或多个KEYEXISTS: 判断一个KEY是否存在EXPIRE: 设置KEY的有效期(因为Redis是基于内存的,内存无限)TTL: 查看一个key的有效期(有效期time to live 简称TTL)help [command]查看命令的用法常见的String指令: 常见的Hash指令: 常见的List指令: 常见的Set指令: 常见的SortedSet指令: 参考:bilibili

July 5, 2022 · 1 min · jiezi

关于redis:Redis数据结构与内部编码

概要Redis 对外的数据结构包含:字符串(string)、哈希(hash)、列表(list)、汇合(set)以及有序汇合(zset)。这些数据结构都有对应的 API 能够在客户端应用。如上每一种数据结构在 Redis 外部又对应不只一种外部编码实现。Redis 依据存储的数据抉择比拟适合的外部编码以实现内存与性能之间的均衡。 键对应存储的值的数据类型通过 type 命令查看,外部编码通过 object encoding 命令查看: 127.0.0.1> set a bOK127.0.0.1> type astring 127.0.0.1> object encoding a"embstr"字符串(string)字符串类型的值能够是字符串(简略字符串,简单字符串如(JSON、XML),数字(整数、浮点数),甚至是二进制(图片、视频、音频)),值最大不能超过 512MB。 字符串类型的外部编码有 3 种:int:键值8 个字节的长整型embstr:键值是小于等于 39 个字节的字符串raw:键值是大于 39 个字节的字符串 哈希(hash)哈希类型是指键值自身又是一个键值对,形如:value = {{field1=value1},{field2=value2}} 哈希类型的外部编码有 2 种:ziplist:当哈希类型的元素个数小于 hash-max-ziplist-entries 配置(默认 512 个),同时所有的值都小于 hash-max-ziplist-value 配置(默认 64 字节)时。ziplist 更加节俭内存。hashtable:当不满足 ziplist 条件,ziplist 的读写效率会降落,此时,应用 hashtable 作为外部编码,hashtable 的读写工夫复杂度是 O(1)。 开发揭示:为了节俭内存,能够将一个大的哈希拆分成若干小的哈希来满足 ziplist 条件。列表(list)列表用来存储多个有序的字符串,列表中的每个字符串称之为一个元素,列表中的元素能够反复,一个列表最多能够存储 2^31 - 1 个元素。列表类型的外部编码有 3 种:ziplist:当列表类型的元素个数小于 list-max-zip-entries 配置(默认 512 个),同时列表中的每个元素的值都小于 list-max-ziplist-value 配置(默认 64 字节),ziplist 更加节俭内存。linkedlist:当不满足 ziplist 条件,外部编码应用 linkedlist 实现。quicklist:是一个以 ziplist 为节点的 linkedlist,联合了 ziplist 和 linkedlist 的长处。从 Redis 3.2 版本开始,新增了这种更加优良的列表外部编码实现。 ...

July 4, 2022 · 1 min · jiezi

关于redis:centos-安装redis

办法一:通过yum装置yum -y install redissystemctl start redisvim /etc/redis.conf [root@prometheus-influxdb02 ~]# grep -v "#" /etc/redis.confbind 10.26.5.xxprotected-mode noport 6379tcp-backlog 511timeout 30tcp-keepalive 300daemonize yessupervised nopidfile /var/run/redis_6379.pidloglevel noticelogfile /var/log/redis/redis.logdatabases 16save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename dump.rdbdir /var/lib/redisslave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100requirepass XiaohongshUappendonly noappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yeslua-time-limit 5000slowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yes为了能够使Redis能被近程连贯,须要批改配置文件,门路为/etc/redis.conf ...

July 4, 2022 · 1 min · jiezi

关于redis:解决Redis连接报错ERR-max-number-of-clients-reached

问题产生起因redis应用过程当中个别会由客户端进行连贯资源管理,例如调配连贯、监控连贯状态、回收连接池资源默认设置下,redis不会被动断开连接redis的timeout参数配置项,默认单位是秒,当timeout是0的时候,redis不会被动敞开连贯因为redis默认的最大连接数是10000,然而始终不敞开连贯的话随着时间推移,连贯越积越多,最终导致没有连贯可用最终导致redis客户端连贯的时候报错,显示"ERR max number of clients reached" 获取以后redis配置$ redis-cli127.0.0.1:6379> CONFIG get * # 获取timeout能够执行127.0.0.1:6379> CONFIG get timeout1) "timeout"2) "0"解决方案配置timout参数值 compose.yaml配置如下 redis: image: redis command: redis-server --requirepass testpasswd --timeout 10 container_name: demo-redis restart: always oom_kill_disable: true mem_limit: 2g ports: - 127.0.0.1:6379:6379 healthcheck: test: 'redis-cli ping || exit 1'启动命令redis-server --requirepass testpasswd --timeout 10 --requirepass testpasswd: 配置redis启动命令,设置启用明码连贯,明码设置为testpasswd--timeout 10: 配置当连贯无操作10s之后连贯会断开其余参数解释oom_kill_disable: true 禁止当内存占用太多时候敞开容器mem_limit: 2g 限度最大应用2G内存healthcheck 应用健康检查容器是否服务失常ports 端口凋谢配置127.0.0.1:6379:6379示意不对外部机器凋谢6379端口验证$ docker exec -it demo-redis /bin/sh# redis-cli127.0.0.1:6379> auth testpasswd127.0.0.1:6379> config get timeout1) "timeout"2) "10"# 此处期待10秒之后再次输出命令,发现Broken pipe,这个是失常的,因为以后连贯10s内没有操作,所以redis服务器敞开了该连贯127.0.0.1:6379> CONFIG get timeoutError: Broken pipenot connected> 查看clients连贯 ...

July 2, 2022 · 1 min · jiezi

关于redis:flask-连接redis

pip3 install flask-redisfrom flask import Flaskfrom flask_redis import FlaskRedisimport timeapp = Flask(__name__)app.config['REDIS_URL'] = 'redis://:XxxxU@10.2x.5.36:6x9/0'redis_client = FlaskRedis(app)@app.route('/')def hello_world(): return 'Hello World!'def mark_dyn_data(keys): keys_str = str(keys).encode('utf-8') exists_keys = redis_client.exists(keys_str) print("###exists_keys:", exists_keys) if exists_keys == 1: redis_client.incrby(keys_str, 1) else: expires = int(time.time()) + 3600 p = redis_client.pipeline() p.set(keys_str, 1) p.expireat(keys_str, expires) p.execute()def get_dyn_data(keys): keys_str = str(keys).encode('utf-8') data = redis_client.get(keys_str) if data: return str(data) return None@app.route('/test_redis/<path:keys>')def test_redis1(keys): #mark_dyn_data('icmp_设施无奈ping通告警_icmp_10.26.5.36_2022-06-2517:59:38') mark_dyn_data(keys) data = get_dyn_data(keys) return str(data)if __name__ == '__main__': app.run() ...

June 27, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习十六redis-字典map-及其核心编码结构

redis 是应用 C 语言编写的,然而 C 语言是没有字典这个数据结构的,因而 C 语言本人应用构造体来自定义一个字典构造 typedef struct redisDbsrc\server.h 中的 redis 数据库 数据结构 /* Redis database representation. There are multiple databases identified * by integers from 0 (the default database) up to the max configured * database. The database number is the 'id' field in the structure. */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 */ int id; /* Database ID */ long long avg_ttl; /* Average TTL, just for stats */ unsigned long expires_cursor; /* Cursor of the active expire cycle. */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */} redisDb;redisDb 寄存了 redis 数据库底层的数据结构: ...

June 26, 2022 · 4 min · jiezi

关于redis:redis配置文件中的保护模式protectedmode

1. 保护模式在redis.conf配置文件中有这样一个配置: protected-mode yes下面有一段正文很好的阐明了它的作用 Protected mode is a layer of security protection, in order to avoid that Redis instances left open on the internet are accessed and exploited. When protected mode is on and the default user has no password, the server only accepts local connections from the IPv4 address (127.0.0.1), IPv6 address (::1) or Unix domain sockets. By default protected mode is enabled. You should disable it only if you are sure you want clients from other hosts to connect to Redis even if no authentication is configured. ...

June 24, 2022 · 1 min · jiezi

关于redis:redis

1 .我的项目中为什么应用redis?因为源数据不在咱们本地,须要调用SDK从远端的服务器去获取数据,所以对于那些须要频繁调用接口获取的并且更新不是非常频繁数据咱们能够思考将它们在第一次获取到后缓存在本地,下一次再来获取这些数据的时候能够间接从缓存中获取,只有做好了缓存的一致性等措施,就能够防止因为网络提早等起因导致的调用SDK超时而获取不到数据等等诸如此类的问题。

June 24, 2022 · 1 min · jiezi

关于redis:3大问题Redis缓存异常及处理方案总结

导语 | Redis作为一个高性能的内存中的key-value数据结构存储系统,在咱们日常开发中广泛应用于缓存、计数器、音讯队列、排行榜等场景中,尤其是作为最罕用的缓存形式,在进步数据查问效率、爱护数据库等方面起到了不可磨灭的作用,但理论利用中,可能会呈现一些Redis缓存异样的状况,本文次要对Redis缓存异样及解决计划进行了总结。一、背景Redis是一个齐全开源的、恪守BSD协定的、高性能的key-value数据结构存储系统,它反对数据的长久化,能够将内存中的数据保留在磁盘中,而且不仅仅反对简略的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储,性能非常弱小,Redis还反对数据的备份,即master-slave模式的数据备份,从而进步可用性。当然最重要的还是读写速度快,作为咱们平时开发中最罕用的缓存计划被广泛应用。但在理论利用过程中,它会存在缓存雪崩、缓存击穿和缓存穿透等异常情况,如果漠视这些状况可能会带来灾难性的结果,上面次要对这些缓存异样和常见解决计划进行相应剖析与总结。 二、缓存雪崩(一)是什么一段时间内本应在redis缓存中解决的大量申请,都发送到了数据库进行解决,导致对数据库的压力迅速增大,重大时甚至可能导致数据库解体,从而导致整个零碎解体,就像雪崩一样,引发连锁效应,所以叫缓存雪崩。 (二)为什么呈现上述情况的常见起因次要有以下两点: 大量缓存数据同时过期,导致本应申请到缓存的需从新从数据库中获取数据。redis自身呈现故障,无奈解决申请,那天然会再申请到数据库那里。(三)怎么办 针对大量缓存数据同时过期的状况: 理论设置过期工夫时,该当尽量避免大量key同时过期的场景,如果真的有,那就通过随机、微调、平均设置等形式设置过期工夫,从而防止同一时间过期。增加互斥锁,使得构建缓存的操作不会在同一时间进行。双key策略,主key是原始缓存,备key为拷贝缓存,主key生效时,能够拜访备key,主key缓存生效工夫设置为短期,备key设置为长期。后盾更新缓存策略,采纳定时工作或者音讯队列的形式进行redis缓存更新或移除等。针对redis自身呈现故障的状况: 在预防层面,能够通过主从节点的形式构建高可用的集群,也就是实现主Redis实例挂掉后,能有其余从库疾速切换为主库,持续提供服务。如果事件曾经产生了,那就要为了避免数据库被大量的申请搞解体,能够采纳服务熔断或者申请限流的办法。当然服务熔断绝对粗犷一些,进行服务直到redis服务复原,申请限流绝对温和一些,保障一些申请能够解决,不是一刀切,不过还是看具体业务状况抉择适合的解决计划。三、缓存击穿(一)是什么缓存击穿个别呈现在高并发零碎中,是大量并发用户同时申请到缓存中没有但数据库中有的数据,也就是同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力霎时增大。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。 (二)为什么这种状况其实个别呈现的起因就是某个热点数据缓存过期,因为是热点数据,申请并发量又大,所以过期的时候还是会有大量申请同时过去,来不及更新缓存就全部打到数据库那边了。 (三)怎么办针对这种状况有两种常见的解决计划: 简略粗犷的对热点数据不设置过期工夫,这样不会过期,天然也就不会呈现上述情况了,如果后续想清理,能够通过后盾进行清理。增加互斥锁,即当过期之后,除了申请过去的第一个查问的申请能够获取到锁申请到数据库,并再次更新到缓存中,其余的会被阻塞住,直到锁被开释,同时新的缓存也被更新下来了,后续申请又会申请到缓存上,这样就不会呈现缓存击穿了。四、缓存穿透(一)是什么缓存穿透是指数据既不在redis中,也不在数据库中,这样就导致每次申请过去的时候,在缓存中找不到对应key之后,每次都还要去数据库再查问一遍,发现数据库也没有,相当于进行了两次无用的查问。这样申请就能够绕过缓存间接查数据库,如果这个时候有人想歹意攻打零碎,就能够成心应用空值或者其余不存在的值进行频繁申请,那么就会对数据库造成比拟大的压力。 (二)为什么这种景象的起因其实很好了解,业务逻辑外面如果用户对某些信息还没有进行相应的操作或者解决,那对应的寄存信息的数据库或者缓存中天然也就没有相应的数据,也就容易呈现上述问题。 (三)怎么办针对缓存穿透,个别有以下三种解决计划: 非法申请的限度,次要是指参数校验、鉴权校验等,从而一开始就把大量的非法申请拦挡在外,这在理论业务开发中是必要的伎俩。缓存空值或者默认值,如果从缓存取不到的数据,在数据库中也没有取到,那咱们依然把这个空后果进行缓存,同时设置一个较短的过期工夫。通过这个设置的默认值寄存到缓存,这样第二次到缓存中获取就有值了,而不会持续拜访数据库,能够避免有大量歹意申请是重复用同一个key进行攻打。应用布隆过滤器疾速判断数据是否存在。那什么是布隆过滤器呢,简略来说,就是能够引入了多个互相独立的哈希函数,保障在给定的空间和误判率下,实现元素判重。因为咱们晓得,存在hash碰撞这样一种状况,那如果只应用一个hash函数,则碰撞抵触的概率显著会变大,那为了缩小这种抵触,咱们能够多引入几个hash函数,而布隆过滤器算法的核心思想就是利用多个不同的hash函数来解决这样一种抵触。它的长处是空间效率高,查问工夫短,远超其余算法,而它的毛病就是会存在肯定的误识别率,它不能齐全保障申请过去的key,通过布隆过滤器的校验,就肯定有这个数据,毕竟实践上还是会存在抵触状况,无论概率多小。然而,只有没有通过布隆过滤器的校验,那么这个key就肯定不存在,只有利用这一点其实就曾经能够过滤掉大部分不存在的key的申请了,在失常场景下未然足够了。五、其余除了上述三种常见的Redis缓存异样问题之外,还常常听到的有缓存预热和缓存降级两个名词,与其说是异样问题,不如说是两种的优化解决办法。 (一)缓存预热缓存预热就是零碎上线前后,将相干的缓存数据间接加载到缓存零碎中去,而不依赖用户。这样就能够防止在用户申请的时候,先查询数据库,而后再将数据缓存的问题。用户间接查问当时被预热的缓存数据,这样能够防止那么零碎上线初期,对于高并发的流量,都会拜访到数据库中, 对数据库造成流量的压力。依据数据不同量级,能够有以下几种做法: 数据量不大:我的项目启动的时候主动进行加载。数据量较大:后盾定时刷新缓存。数据量极大:只针对热点数据进行预加载缓存操作。(二)缓存降级缓存降级是指当缓存生效或缓存服务呈现问题时,为了避免缓存服务故障,导致数据库跟着一起产生雪崩问题,所以也不去拜访数据库,但因为一些起因,依然想要保障服务还是根本可用的,尽管必定会是有损服务。因而,对于不重要的缓存数据,咱们能够采取服务降级策略。个别做法有以下两种: 间接拜访内存局部的数据缓存。间接返回零碎设置的默认值。六、总结本文次要对常见的Redis缓存异样及其解决计划进行了总结,能够用下图做个概括: 作者简介:尹哲浩,腾讯经营开发工程师。

June 22, 2022 · 1 min · jiezi

关于redis:游戏的-Redis-选用思考

此处仅是收集材料的一些小结,若有舛误,还望不吝赐教。背景:思考到游戏的高频读写行为,Demo 中将 Redis 作为一级存储,定时向 MySQL 进行二级存储。 游戏体验还不错,于是打算制作正式版,就延展出了不少对于效率和永恒缓存的遐思,这里简略赘述一二。 对于存储构造:String vs Hash这个问题最后不存在,我是间接用 String convert to JSON,而后看到了汉家松鼠的技术总结,他们采纳的是 Hash,就此产生了 diff 这两者的想法。 简略的思考了一下, 也参考了 Redis strings vs Redis hashes to represent JSON: efficiency? 等发问。 Hash 的特点: K-V 式的读取,500kb 的数据里,间接读到你须要的 value,高效!K 须要额定的存储空间。嵌套对象不易存储,问题不大,除非是 MongoDB 爱好者。K 的可用性须要额定保障。String 的特点: 一口气寄存所有数据,会比拟大,对于 500kb 这种,能够拆分为多个局部来存储。须要额定转码——譬如 PHP 的 json_decode 还有优化空间,会有性能思考。无奈定制化的做 TTL。TTL 由转储动作来自行处理,毕竟不是缓存,而是二级数据库,不思考过期。转码性能也须要玩家到一定量级,而且能够配合拆分构造、重写 json decode 来优化。 只管 Hash 仿佛也不错,最终对我并没有特地重大的吸引力,仍放弃 String 来解决。 对于 Redis to MySQL这个问题发现的比拟早,如何稳固、牢靠的让 Redis 数据定期进入 MySQL,不然玩家会丢档。v2ex 上也发问过,技术大佬们提了很多计划: 工夫轮:工夫戳作为 key,玩家数据的快照作为 value,定时器每秒解决本工夫戳的所有 value,将其实例化入 MySQL;玩家信息更新时,将本身的工夫戳 +60s,存入工夫轮。有序列表:每次只需扫描表头,和工夫轮相似,更省事。spacekey:Redis 官网性能,当缓存生效时能够触发一个事件,问题在于,这个事件首先默认不反对集群(但能够手动订阅),其次这个事件在客户端 shutdown 时,会失落在这期间的 message。不是很稳固。MQ 延时队列 & 死信队列:将数据丢进去,延时解决。死信队列从未听过,就简略探索了一下。 ...

June 20, 2022 · 1 min · jiezi

关于redis:红包雨中Redis-和-Lua-的邂逅

2018年,王思聪的冲顶大会,西瓜视频的百万英雄,再到映客的芝士超人,直播答题火爆全网。 我服务的一家电商公司也退出了这次热潮,技术团队研发了直播答题性能。答题完结之后,红包会以红包雨的模式落下,用户点击屏幕上落下的红包,若抢到红包,红包会以现金的模式进入用户账户。 红包雨是一个典型的高并发场景,短时间内有海量申请拜访服务端,技术团队为了让零碎运行顺畅,抢红包采纳了基于 Redis + Lua 脚本的设计方案。 1 整体流程咱们剖析下抢红包的整体流程 : 经营系统配置红包雨流动总金额以及红包个数,提前计算出各个红包的金额并存储到 Redis 中;抢红包雨界面,用户点击屏幕上落下的红包,发动抢红包申请;TCP 网关接管抢红包申请后,调用答题零碎抢红包 dubbo 服务,抢红包服务实质上就是执行 Lua 脚本,将后果通过 TCP 网关返回给前端;用户若抢到红包,异步工作会从 Redis 中 获取抢得的红包信息,调用余额零碎,将金额返回到用户账户。2 红包 Redis 设计抢红包有如下规定: 同一流动,用户只能抢红包一次 ;红包数量无限,一个红包只能被一个用户抢到。如下图,咱们设计三种数据类型: 经营预调配红包列表 ; 队列元素 json 数据格式 : { //红包编号 redPacketId : '365628617880842241' //红包金额 amount : '12.21' }用户红包支付记录列表; 队列元素 json 数据格式: { //红包编号 redPacketId : '365628617880842241' //红包金额 amount : '12.21', //用户编号 userId : '265628617882842248'}用户红包防重 Hash 表; 抢红包 Redis 操作流程 : ...

June 19, 2022 · 6 min · jiezi

关于redis:市场上的企业即时通讯软件我们应该如何选择

在信息时代背景下,即时通讯能够说是与咱们生存相干最为亲密的工具了,即时通讯不仅能够帮忙咱们在亲朋好友之间交换通信,也能够为互联网时代的企业员工交换,企业信息互通发明良好的条件。 那么企业即时通讯工具又应该怎么抉择呢?那些企业即时通讯工具是最好用的呢? 企业即时通讯工具是企业工作中帮忙企业办公的重要工具,企业内员工能够通过即时通讯工具进行信息互通,实现各项工作的交换,进步企业办公效率。企业即时通讯工具的抉择有很多思考方面,首先咱们能够来简略理解一下企业即时通讯工具的排行状况。 企业微信能够说是企业即时通讯工具中的龙头老大。在社交通信类软件中腾讯系的位置是不可比较的。而企业微信作为与微信同源的社交软件,则更适宜企业进行工作交换以及工作汇报。以后企业微信能够疾速链接本人的员工,并且通过在线查问等形式,察看员工是否在线,以及员工在线是挪动端还是电脑端登陆状况。企业微信在公司业务性能上极为全面,不仅能够传输文件,还能够进行视频通话、语音通话等等,不便了企业进行信息交换,腾讯的企业微信会议还能够满足企业召开网络会议的作用。理解更多能够登录官网征询 https://www.tokim.cn 谈到企业即时通讯工具,其实更多人思考的是一些具备商务性质的通信软件。而腾讯系列的企业即时通讯工具中企业QQ其实是一款比企业微信更加具备代表性的即时通讯软件。企业QQ作为商务版QQ囊括了企业办公须要的各类型条件,企业能够通过企业QQ进行客户分割,疾速统计客户相干信息,与企业客户进行商务单干和沟通,帮忙企业进行会议交换和信息沟通。 而同为企业即时通讯工具的阿里巴巴旗下的钉钉天然也不落后。以后钉钉在社交网络中占据的地位并不显著,但要说企业办公,钉钉以优越的关上零碎,日志统计等各类型企业专用的工作条件胜利位居了企业即时通讯工具的帮手,也是以后大中小企业在抉择办公类企业即时通讯工具时的首选。 除了腾讯系列和阿里系列的企业即时通讯工具外,当然还有其余类型的企业即时通讯工具,比方网易旗下的易信等等,另外一些即时通讯工具也能够帮忙企业进行通信交换,然而与上文介绍的

June 17, 2022 · 1 min · jiezi

关于redis:redis-cluster

主动将数据进行分片,每个 master 上放一部分数据提供内置的高可用反对,局部 master 不可用时,还是能够持续工作的在 Redis cluster 架构下,每个 Redis 要放开两个端口号,比方一个是 6379,另外一个就是 加1w的端口号,比方 16379。16379 端口号是用来进行节点间通信的,也就是 cluster bus 的货色,cluster bus 的通信,用来进行故障检测、配置更新、故障转移受权。cluster bus 用了另外一种二进制的协定, gossip协定,用于节点间进行高效的数据交换,占用更少的网络带宽和解决工夫。

June 15, 2022 · 1 min · jiezi

关于redis:Redis都有哪些使用场景

本文转自Redis面试大全,汇总PHP、Golang、Redis、MySQL等相干面试题。聊聊Redis现状Redis作为一种内存型的非关系型的数据库,不论在互联网大厂,小厂,大我的项目和小我的项目中,简直都会被应用。为什么Redis会受到如此青眼呢?对于这个问题,可能很多的程序员只是看着他人用而用,不足对Redis一个全面的理解。 Redis应用场景缓存缓存当初简直是所有中大型网站都在用的必杀技,正当的利用缓存不仅可能晋升网站访问速度,还能大大降低数据库的压力。Redis提供了键过期性能,也提供了灵便的键淘汰策略,所以,当初Redis用在缓存的场合十分多。 排行榜很多网站都有排行榜利用的,如京东的月度销量榜单、商品按工夫的上新排行榜等。Redis提供的有序汇合数据类构能实现各种简单的排行榜利用。 计数器什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1,并发量高时如果每次都申请数据库操作无疑是种挑战和压力。Redis提供的incr命令来实现计数器性能,内存操作,性能十分好,十分实用于这些计数场景。 分布式会话集群模式下,在利用不多的状况下个别应用容器自带的session复制性能就能满足,当利用增多绝对简单的零碎中,个别都会搭建以Redis等内存数据库为核心的session服务,session不再由容器治理,而是由session服务及内存数据库治理。 分布式锁在很多互联网公司中都应用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发拜访,如全局ID、减库存、秒杀等场景,并发量不大的场景能够应用数据库的乐观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来管制资源的并发拜访是不太现实的,大大影响了数据库的性能。能够利用Redis的setnx性能来编写分布式的锁,如果设置返回1阐明获取锁胜利,否则获取锁失败,理论利用中要思考的细节要更多。 社交网络点赞、踩、关注/被关注、独特好友等是社交网站的基本功能,社交网站的访问量通常来说比拟大,而且传统的关系数据库类型不适宜存储这种类型的数据,Redis提供的哈希、汇合等数据结构能很不便的的实现这些性能。 最新列表Redis列表构造,LPUSH能够在列表头部插入一个内容ID作为关键字,LTRIM可用来限度列表的数量,这样列表永远为N个ID,无需查问最新的列表,间接依据ID去到对应的内容页即可。 音讯零碎音讯队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等风行的音讯队列中间件,次要用于业务解耦、流量削峰及异步解决实时性低的业务。Redis提供了公布/订阅及阻塞队列性能,能实现一个简略的音讯队列零碎。另外,这个不能和业余的消息中间件相比。 如何应用下面提到了各种应用场景,在这些场景应用中,无非就是对Redis数据类型的操作。就须要对Redis数据类型有所理解。在Redis中有这些数据类型。String、Hash、List、Set、Zset、GEO、Stream、HyperLogLog、BitMap。 数据应用场景String类型String类型是一种字符串类型,相似一种键值对的模式。 个别咱们用String类型用来存储商品数量、用户信息和分布式锁等利用场景。 存储商品数量。set goods:count:1 1233set goods:count:2 100用户信息。set user:1 "{"id":1, "name":"张三", "age": 12}"set user:2 "{"id":2, "name":"李四", "age": 12}"分布式锁。# 设置一个不存在的键名为id:1值为10, 过期工夫为10秒。127.0.0.1:6379> set id:1 10 ex 10 nxOK127.0.0.1:6379> get id:1"10"# 以后的键还未过期,在次设置则不会设置胜利。127.0.0.1:6379> set id:1 10 ex 10 nx(nil)# 当10秒之后去获取,以后的键则为空。127.0.0.1:6379> get id:1(nil)用Redis实现分布式锁的原理,当一个键存在则设置失败。hash类型hash类型是一种相似关系型数据库构造的数据结构。有一个键名,键存的内容是以键值对的模式存在。 利用hash构造,咱们能够用来存储用户信息、对象信息等业务场景。 存用户信息。127.0.0.1:6379> hset user:1 id 1 name zhangsan age 12 sex 1(integer) 4127.0.0.1:6379> hset user:2 id 2 name lisi age 14 sex 0(integer) 4127.0.0.1:6379> hmget user:1 id name age sex1) "1"2) "zhangsan"3) "12"4) "1"存储对象信息。127.0.0.1:6379> hset object:user id public-1 name private-zhangsan(integer) 2127.0.0.1:6379> hmget object:uesr id name1) (nil)2) (nil)127.0.0.1:6379> hget object:user id"public-1"127.0.0.1:6379>这里存储一个user对象,对象外面有两个属性,别离是id和name字,别离存储的是属性的拜访权限和默认值拼接。list类型list类型是一个列表类型的数据结构。用一个键,依照顺序排列数据。 ...

June 15, 2022 · 3 min · jiezi

关于redis:从Redis70发布看Redis的过去与未来

简介: 经验靠近一年的开发、三个候选版本,Redis 7.0终于正式公布,这是Redis历史上扭转最多的一个大版本,它不仅蕴含了50多个新命令,还有大量外围新个性与改良,这些不仅可能解决用户应用中的诸多问题,还进一步拓展了Redis的应用场景。发布会详情:https://developer.aliyun.com/... image.png 前言经验靠近一年的开发、三个候选版本,Redis 7.0终于正式公布,这是Redis历史上扭转最多的一个大版本,它不仅蕴含了50多个新命令,还有大量外围新个性与改良,这些不仅可能解决用户应用中的诸多问题,还进一步拓展了Redis的应用场景。 尽管Redis 7.0做了许多大胆的尝试,然而稳定性仍然是最重要的基石。Redis官网在7.0的GA博文中也强调这一点:“While user-facing features are easy to boast of, the real “unsung heroes” in this version are efforts to make Redis more performant, stable, and lean.”(尽管面向用户的性能更容易失去称誉,但这个版本中的无名英雄是咱们继续一直的对Redis的优化,使其变得更加精简、高效、稳固)。置信从这里能够看出,Redis倒退至今稳定性始终被摆在最重要的地位,因而对于7.0的稳定性担心大可打消。 上面请先追随咱们一起理解Redis7.0的几个外围新个性。 Redis7.0外围新个性概览FunctionFunction是Redis脚本计划的全新实现,在Redis 7.0之前用户只能应用EVAL命令族来执行Lua脚本,然而Redis对Lua脚本的长久化和主从复制始终是undefined状态,在各个大版本甚至release版本中也都有不同的体现。因而社区也间接要求用户在应用Lua脚本时必须在本地保留一份(这也是最为平安的形式),以避免实例重启、主从切换时可能造成的Lua脚本失落,保护Redis中的Lua脚本始终是宽广用户的痛点。 Function的呈现很好的对Lua脚本进行了补充,它容许用户向Redis加载自定义的函数库,一方面绝对于EVALSHA的调用形式用户自定义的函数名能够有更为清晰的语义,另一方面Function加载的函数库明确会进行主从复制和长久化存储,彻底解决了过来Lua脚本在长久化上含糊不清的问题。 那么自7.0开始,Function命令族和EVAL命令族有了各自明确的定义:FUNCTION LOAD会把函数库主动进行主从复制和长久化存储;而SCRIPT LOAD则不会进行长久化和主从复制,脚本仅保留在以后执行节点。并且社区也在打算后续版本中让Function反对更多语言,例如JavaScript、Python等,敬请期待。 总的来说,Function在7.0中被设计为数据的一部分,因而可能被保留在RDB、AOF文件中,也能通过主从复制将Function由主库复制到所有从库,能够无效解决之前Lua脚本失落的问题,咱们也十分倡议大家逐渐将Redis中的Lua脚本替换为Function。 Multi-part AOFAOF是Redis数据长久化的外围解决方案,其本质是一直追加数据批改操作的redo log,那么既然是一直追加就须要做回收也即compaction,在Redis中称为AOF rewrite。 然而AOF rewrite期间的增量数据如何解决始终是个问题,在过来rewrite期间的增量数据须要在内存中保留,rewrite完结后再把这部分增量数据写入新的AOF文件中以保障数据完整性。能够看进去AOF rewrite会额定耗费内存和磁盘IO,这也是Redis AOF rewrite的痛点,尽管之前也进行过屡次改良然而资源耗费的实质问题始终没有解决。 阿里云的Redis企业版在最后也遇到了这个问题,在外部通过屡次迭代开发,实现了Multi-part AOF机制来解决,同时也奉献给了社区并随此次7.0公布。具体方法是采纳base(全量数据)+inc(增量数据)独立文件存储的形式,彻底解决内存和IO资源的节约,同时也反对对历史AOF文件的保留治理,联合AOF文件中的工夫信息还能够实现PITR按工夫点复原(阿里云企业版Tair已反对),这进一步加强了Redis的数据可靠性,满足用户数据回档等需要。 对具体实现感兴趣的同学能够查看本文开端参考资料。 Sharded-pubsubRedis自2.0开始便反对公布订阅机制,应用pubsub命令族用户能够很不便地建设音讯告诉订阅零碎,然而在cluster集群模式下Redis的pubsub存在一些问题,最为显著的就是在大规模集群中带来的播送风暴。 Redis的pubsub是按channel频道进行公布订阅,然而在集群模式下channel不被当做数据处理,也即不会参加到hash值计算无奈按slot散发,所以在集群模式下Redis对用户公布的音讯采纳的是在集群中播送的形式。 那么问题不言而喻,如果一个集群有100个节点,用户在节点1对某个channel进行publish公布音讯,该节点就须要把音讯播送给集群中其余99个节点,如果其余节点中只有多数节点订阅了该频道,那么绝大部分音讯都是有效的,这对网络、CPU等资源造成了极大的节约。 Sharded-pubsub便是用来解决这个问题,意如其名,sharded-pubsub会把channel按分片来进行散发,一个分片节点只负责解决属于本人的channel而不会进行播送,以很简略的办法防止了资源的节约。 Client-evictionRedis反对内存规格配置,maxmemory和maxmemory-policy大家曾经都很相熟了,然而在这里我还是想再解释一下:maxmemory管制的是Redis的整体运行内存而非数据内存,例如client buffer、lua cache、fucntion cache、db metadata等这些都会算在运行内存中,如果运行内存超过了maxmemory就会触发evict删除数据,这也是用户在应用Redis时的一大痛点。 在这些非数据内存应用当中,又以client buffer耗费最大,在大流量场景下client须要缓存很多用户读写数据(设想一下keys的后果须要先缓存在client output buffer中再发送给用户),因为网络流量的内存耗费导致触发eviction删除数据的状况十分之多。尽管Redis很早就反对对client-output-buffer-limit配置项,但其限度的也只是单个连贯维度的output buffer,没有全局统计client应用内存和限度。为了解决这个问题,7.0新增了maxmemory-clients配置项,来对所有client应用的内存做限度,如果超过了这个限度就会筛选内存耗费最大的client开释,用来缓解内存应用的耗费。 Client-eviction并不是起点,还有很多元数据的内存应用会对用户造成困扰,Redis是基于内存的数据库,咱们须要对各个模块的内存进行更准确的统计和管制,让用户可能对数据存储有更为清晰的了解和布局。 Redis历史回顾Redis倒退至今已历经7个大版本,而每个大版本都有大量的新个性产生。例如从3.0开始反对cluster集群模式;4.0开发的lazyfree和PSYNC2解决了Redis短暂的大key删除阻塞问题及同步中断无奈续传的问题;5.0新增了stream数据结构使Redis具备性能残缺的轻量级音讯队列能力;6.0更是公布了诸多企业级个性如threaded-io、TLS和ACL等,大幅晋升了Redis的性能和安全性。 国内开发者与Redis社区建设感激国内开发者,中国区曾经成为Redis社区最大的奉献起源之一阿里云从Redis 4.0开始就深度参加到Redis开源社区当中,向社区奉献了大量能力,目前阿里云在Redis社区共有1名core team member(外围维护者)和2名contributor(外围贡献者),是原作者和Redis Labs以外对Redis社区奉献最大的组织。 ...

June 13, 2022 · 1 min · jiezi

关于redis:即时通讯为企业办公带来了怎样的便利

互联网时代越来越多的生存和办公条件失去了便当,特地是在即时通讯软件方面的倒退,更是让咱们从日常人际交往到工作交换都变得更加便捷。即时通讯为咱们办公发明了怎么的便当条件? 1、即时通讯为办公带来了更加及时的信息反馈。在传统的通信联系形式上,特地是大量数据的剖析和回顾,须要通过电子邮件发送以及面对面交换或者电话通信等形式进行交换,这种沟通形式尽管保障了公务解决的可行性,然而工作效率和成果大打折扣。而即时通讯的产生,在通信联系上进行了更好的改善,可能便于工作对接更加及时和无效。咱们在即时通讯软件上能够与上级领导和上级员工进行交换和沟通,条件须要的状况下能够把整个小组成员拉至同一分组,独特进行问题的探讨,无论间隔多远,只有有手机电脑等工具,就能够进行即时通讯,帮忙咱们更好的进行问题的探讨。 2、即时通讯为办公节约了各种资源。在互联网倒退时代,通信信息相干的资金节约是比拟显著的,尤其是网络通讯遍及和流量费用升高的状况下,即时通讯可能有比电话通信费用等更加实惠的劣势,让咱们在办公中很好的节约通信费用,更好的进行资金节约,将更多的资金运用到研发和生产当中去。 3、 即时通讯为办公发明了更加无效的条件。即时通讯的产生让办公更加高效的同时,也为咱们的工作交换进行了更好的调整,云办公这种办公形式逐步成为互联网时代的新办公形式。咱们能够通过即时通讯的形式缩小日常工作对接中因为见不到人而产生的各种问题,咱们能通过即时通讯让办公气氛趋于良好,相当一部分在办公室中容易呈现的人际交往问题,在即时通讯中容易防止,从而更加无效的进行工作。 4、即时通讯为互联网时代办公提供了良好的条件,这种条件不仅体现在办公形式和模式上的转变,更体现在即时通讯对网络时代办公中工作气氛的扭转。即时通讯条件下的办公是更加简洁高效的办公模式,在即时通讯条件下进行办公也会产生一些其余发展趋势。理解更多能够登录官网征询 https://www.tokim.cn 在信息时代,想要持续倒退即时通讯的高效办公劣势,不仅须要咱们理解即时通讯为办公带来了怎么的便当。更须要咱们深刻的剖析在即时通讯条件下,办公须要什么样的条件,比方咱们常见的网络通讯稳定性,员工在线时长统计,通信揭示的及时和无效等等。 互联网时代中即时通讯是办公中一项重要的沟通条件,在即时通讯的环境下进行会议交换、数据传递以及小组讨论等,都让咱们的日常办公条件更加方便快捷,可能让即时通讯中的工作效率大大提高,即时通讯是互联网技术以及办公倒退的重要趋势。

June 12, 2022 · 1 min · jiezi

关于redis:记一次Redis-On命令拖慢接口

1. 景象某天忽然收到运维告警音讯,反馈产线有接口在某一段时间慢了,随之性能优化的JIRA工作也开过去了,大抵内容如下: API告警 - Base Tomcat Accesslog: 微服务: xxxapi 接口: /xxxx/xxx POST 在 [ 2022/xx/xx 10:42:00 ~ 10:43:00 ] 工夫窗口,RT > 阈值(300ms) 产生 141 次。 公司外部对API的响应工夫要求是低于300ms,超过的都属于有性能问题的接口,接下来就排查吧。 2. 排查2.1. 查监控平台:接口速度首先就去查看公司的API监控平台,确认这个接口确实在这段时间确实在响应速度上,有100屡次超过300ms。 不过发现几个有意思的景象: 在那段时间前后,该接口都很失常,所以应该只是那段时间产生了什么。不只是那一个接口超过300ms了,其实那段时间内,也有个别其余接口超过阈值。只不过因为那个接口的并发比拟高,很显著的被告警机制检测进去。2.2. 查监控平台:全链路日志尽管下面发现,可能不只是那一个接口的问题,但还是要仔细分析一下那个接口的全链路日志。 通过调用链路图,能够显著的发现,次要的耗时都在redis命令的执行阶段。又找了那段时间,其余的几个接口,后果都是一样的。 接下来就应该看看对应接口的代码,是不是执行redis命令时,有什么影响性能的问题。 2.3. 剖析代码找到对应接口的代码,代码中惟一用到redis的命令是 HMGET key field1 [field2] ,剖析后排除该代码的问题: 代码中就那一次的redis调用,HMGET 不是耗时的命令(不在后文 O(n)命令之列),且 field 的数量是可枚举的,最多也就几十个,不会有多耗时。该接口因为之前预计到并发高,所以上线之前通过压测,上万QPS齐全不成问题,不会在这里栽跟头。2.4. 查监控平台:redis连接池排除了代码层面上该接口的问题,那只能从运维侧看看执行redis命令慢的问题了,正好监控平台上有mysql、redis连接池的监控。不看不晓得,一看吓一跳。 在出问题时间段,redis连接数霎时飙升,间接超过了最大闲暇连接数。在工夫节点上,和产线呈现慢响应的工夫齐全吻合。 能够揣测出,这段时间可能执行了某些奇怪的命令,造成了redis阻塞,其余redis申请只能频繁的创立新的连贯,从而导致redis连接池中连接数飙升。 想要揣测那段时间redis经验了什么,只能托付于运维的共事了。 2.5. 运维:查redis慢申请通过从运维共事导出来的,那段时间redis执行过的慢申请日志,果然和料想的一样。那段时间执行了大量的慢申请命令,尤其是一段 lrange 命令,每次执行工夫都超过了300ms,一分钟执行了几百次。 通过具体的redis命令,倒推找到了对应的代码,该代码对应的其实是一段对应用户登录后,通过MQ异步刷新用户权限的逻辑。因为是异步生产,所以就算执行的慢,也并不会因为响应工夫阈值而被告警。 再联合 ELK 中无关该接口的调用日志,能够还原出问题产生的残缺过程。 2.6. 场景还原该 lrange 命令独自执行还行,可那段时间在产线有大批量用户的登录并发,导致该慢申请命令阻塞住了redis,大量redis申请都在阻塞排队,redis连接池中连接数也在一直扩大。 所以说,那段时间所有的redis申请命令,都会因为阻塞而提早执行实现。告警的那个接口属于“躺枪”,因为它调用的并发高,所以体现显著,被告警程序当典型抓住了。 如果没有这一步步排查,很难置信一个API的告警,是因为另外一个毫无关系的API影响的。 3. 解决3.1. 解决lrange 是工夫复杂度为 O(n) 的命令,原则上是不应该被频繁调用的。剖析代码上下文逻辑,其实只是为了全量存储和读取 List 类型数据。齐全能够用redis中的 String 代替 List。 ...

June 5, 2022 · 1 min · jiezi

关于redis:缓存1

1.我的项目中缓存是如何应用的?常识发现平台的数据库信息建设在neo4j和mysql之上,多用户查问常识图谱实体和开掘实体间规定面临高性能和高并发的问题。 2.为什么要用缓存?用缓存,次要有两个用处:高性能、高并发。高性能假如这么个场景,你有个操作,一个申请过去,吭哧吭哧你各种乌七八糟操作mysql,半天查出来一个后果,耗时 600ms。然而这个后果可能接下来几个小时都不会变了,或者变了也能够不必立刻反馈给用户。那么此时咋办?缓存啊,折腾 600ms 查出来的后果,扔缓存里,一个 key 对应一个 value,下次再有人查,别走 mysql 折腾 600ms 了,间接从缓存里,通过一个 key 查出来一个 value,2ms 搞定。性能晋升 300 倍。就是说对于一些须要简单操作耗时查出来的后果,且确定前面不怎么变动,然而有很多读申请,那么间接将查问进去的后果放在缓存中,前面间接读缓存就好。高并发mysql 这么重的数据库,压根儿设计不是让你玩儿高并发的,尽管也能够玩儿,然而人造反对不好。mysql 单机撑持到 2000QPS 也开始容易报警了。所以要是你有个零碎,高峰期一秒钟过去的申请有 1万,那一个 mysql 单机相对会死掉。你这个时候就只能上缓存,把很多数据放缓存,别放 mysql。缓存性能简略,说白了就是 key*value 式操作,单机撑持的并发量轻松一秒几万十几万,撑持高并发 so easy。单机承载并发量是 mysql 单机的几十倍。

June 4, 2022 · 1 min · jiezi

关于redis:Redis的Java客户端比较绝地武士与生菜

绝地武士与生菜:摸索作者: Guy Royse 我是一个真正的探索者,所以当我必须做出技术决定时——比方抉择一个 Redis 客户端——我会去探险。这是我对 Java 客户端押韵组合的摸索: Jedis 和 Lettuce。 我的打算很简略: 在代码中尝试一些简略的事件在代码中尝试一些高级的货色达到某种抉择规范…利润!利润的格言指标,就像内裤一样,始终存在。 但您能够从中受害的局部是抉择规范。这将使咱们可能决定何时 Jedis 是正确的抉择,而 Lettuce 何时才是正确的抉择。这一点十分重要,因为咱们都晓得 抉择工具时任何问题的答案都是“视状况而定”。 一种简略的代码让咱们比拟一下所有练习中最简略的一些代码:从 Redis 的单个实例中设置和获取值。 首先,咱们应用 Jedis 执行此操作: import redis.clients.jedis.Jedis;public class JedisSetGet { private static final String YOUR_CONNECTION_STRING = "redis://:foobared@yourserver:6379/0"; public static void main(String[] args) { Jedis jedis = new Jedis(YOUR_CONNECTION_STRING); jedis.set("foo", "bar"); String result = jedis.get("foo"); jedis.close(); System.out.println(result); // "bar" }}查看托管的 原始JedisSetGet.java 通过 GitHub 看代码,这很简略。创立连贯。用它。敞开它。 接下来咱们将应用生菜来做: import io.lettuce.core.RedisClient;import io.lettuce.core.api.StatefulRedisConnection;import io.lettuce.core.api.sync.RedisCommands;public class LettuceSetGet { private static final String YOUR_CONNECTION_STRING = "redis://:foobared@yourserver:6379/0"; public static void main(String[] args) { RedisClient redisClient = RedisClient.create(YOUR_CONNECTION_STRING); StatefulRedisConnection<String, String> connection = redisClient.connect(); RedisCommands<String, String> sync = connection.sync(); sync.set("foo", "bar"); String result = sync.get("foo"); connection.close(); redisClient.shutdown(); System.out.println(result); // "bar" }}通过 GitHub 查看托管的 原始LettuceSetGet.java ...

May 31, 2022 · 2 min · jiezi

关于redis:Redis秒杀抢购方案

总体架构计划网络拦挡: DNS优化, SLB负载平衡,网关封IP限速 业务拦挡: ID限速, 验证码, 只排汇后面N个申请,前面的全回绝; Redis拦挡: 库存不超发,保障限购 接口拦挡: 尽量减少业务查看,判断黑名单 MQ+MYSQL: 异步解决落库状况; Redis List计划 + Incrby计划从缓存读取出流动信息判断流动开始工夫和完结工夫redis内无库存就间接返回无库存,流动完结读取 UID+流动ID的 参加次数 达到限度就返回限度应用Incrby 写入 UID+流动ID的 参加次数,判断返回的次数是否超过限购次数,超过则返回限度写入DB(或者用MQ推送给DB削峰落库)第四点尽管redis是单线程的,然而客户端是多个的,所以第四点的读取和第五点的应用,两个不能同时具备原子性, 必须以 incrby后果为准; 比方一人限购一个,就算某用户第二个申请进来了,把 UID+流动ID 的值 incrby 为2 ,那么这个人返回的也是 “一人只能够购买一个”,对于业务无损; 如果呈现Redis解体或者Mysql解体等异常情况,期待服务复原后, 能够间接从DB外面的购买记录和库存信息简略同步到redis外面,无需开发人员进行过多操作 Redis List计划反对库存编号模型,如果所有的SKU自身无编号意义或者发货才确定库存,能够应用 Incrby管制库存 + Incrby 管制集体限购数量

May 30, 2022 · 1 min · jiezi

关于redis:redis之lua脚本调试

命令行下非调试模式执行此脚本: 命令行下调试模式执行此脚本:--ldb 非同步模式,会对数据作回滚--ldb-sync-mode 同步模式,数据不作回滚 help:输入所有命令指南。step:运行以后行并进行。next:同step。continue:运行脚本间接下一个断点。list: 列出以后行左近的脚本list [line]:列出line行左近的脚本,line = 0,意味着以后行。list [line] [ctx]:在line行前后列出ctx行脚本。whole:列出所有脚本,list 1 1000000 的别名。print:输入所有局部变量print <var>:输入指定变量。也能够输入全副变量 KEYS ARGVbreak:列出所有断点break <line> [,<line> ...]:给指定行减少断点。break -<line> [,-line> ...]:移掉指定行断点break 0:移掉所有断点trace:show a backtrace。eval <coee>:执行Lua代码。redis <cmd>:执行redis命令maxlen [len]:截断redis命令的响应或lua变量输入至指定长度,len=0示意不限度。abort:进行脚本执行,在同步模式下,脚本不会回滚。 redis.debug():在控制台输入变量redis.breakpoint():遇此代码进行执行,就像下一行有一个断点。

May 28, 2022 · 1 min · jiezi

关于redis:CentOS安装Redis

装置 yum install epel-releaseyum install redis如果要装置最新的redis,须要装置Remi的软件源,官网地址:http://rpms.famillecollet.com/yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm而后能够应用上面的命令装置最新版本的redis:yum --enablerepo=remi install redis -y 装置结束后,即可应用上面的命令启动redis服务systemctl restart redis Redis客户端redis-cli 容许近程拜访vim /etc/redis.conf # bind 127.0.0.1 ::1bind 0.0.0.0# daemonize是守护过程的意思, 改成yesdaemonize yes设置明码 requirepass HtiL9h661DQspm设置开机主动systemctl enable redis.service

May 27, 2022 · 1 min · jiezi

关于redis:LeetCode高频算法面试题-003-无重复字符的最长子串

大家好,我是散步coding, 最近在整顿2022年LeetCode高频算法面试题 ,感觉好的, 能够点赞、珍藏哈。同时有补充的也欢送大家给出反馈。本文首发于公众号: 散步coding 给定一个字符串 s ,请你找出其中不含有反复字符的 最长子串 的长度。 <font color=#FF000 >题目难度: ★★★, 中等</font> 示例 1: 输出: s = "abcabcbb"输入: 3 解释: 因为无反复字符的最长子串是 "abc",所以其长度为 3。示例 2: 输出: s = "bbbbb"输入: 1解释: 因为无反复字符的最长子串是 "b",所以其长度为 1。示例 3: 输出: s = "pwwkew"输入: 3解释: 因为无反复字符的最长子串是 "wke",所以其长度为 3。  请留神,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。提醒: 0 <= s.length <= 5 * 104s 由英文字母、数字、符号和空格组成代码实现tips: 以下代码是应用Go代码实现的不同解法, 文章最初能够看C++、C、Java、Python实现1、滑动窗口1)、滑动窗口由左右两个指针(start, end)组成,其起始地位均为0。2)、窗口挪动时,让左指针star不变,仅挪动右指针end,不断扩大子串。3)、子串中是否存在反复字符,咱们应用map存储字符呈现的地位。4)、<font color=#FF000 >右指针end的下一个字符是否曾经存在于map中,若是,则左指针start右移到该字符呈现地位的下一位(因为从左指针start到这一位两头是必然有反复的);否则反复步骤2。</font>5)、每次刷新窗口时,都要对窗口长度进行计算,若大于原来标记的最大窗口长度,则进行更新。另外,要留神一些边界条件,如字符串长度不能为0,否则将导致数组越界; 这样,通过一次遍历就可能失去后果,工夫复杂度为O(n)。 图解过程 <font color=#FF000 >步骤1、</font> <font color=#FF000 >步骤2、</font> <font color=#FF000 >步骤3、</font> <font color=#FF000 >步骤4、</font> ...

May 26, 2022 · 2 min · jiezi

关于redis:Redis服务支持5000万的QPS有什么好的思路

一、题目剖析1. 仅依据qps计算多少机器是不够的如果一个Redis服务器反对五千万下面的读写申请,那么此题很简略,一个机器,一个Redis过程即可。 但显然没有这样的超级机器,Redis也做不到一个过程就能解决五千万QPS。 那么一个Redis到底能解决多少QPS? 如果是简略的读写,那么一个Redis在一个硬件服务器(只有1个核,CPU为以后支流即可),就能解决Million级别的QPS,即至多百万。 所以,下面的问题如同简略了,咱们只有用50个Redis过程就能反对题目所需的五千万,而后在一个50核的机器上跑这五十个过程,即可。 所以,答案如同很简略:一台五十核机器,五十Redis过程。 然而这样的吗?不是的,因为很可能应用环境并不是只有简略的有限量的key,如果key一直增多,那就会呈现大麻烦。 2. 瓶颈在哪?难点在那个两千万QPS的写入(即20M qps write),我来仔细分析给你看。 假如:每个写入是100字节(即key+value之和均匀在100Bytes,即0.1KB),而且万一这两千万QPS的写入都是新增数据,而且随后的三千万QPS(即30M qps read)还可能查历史的数据,咱们会有什么麻烦? 显然,下一秒将产生 0.1KB 20M qps = 2GB 新增数据,每天将产生 2GB 86400 秒 = 173 TB 新增数据。 没有任何一台机器的内存能够反对一天的新增数据容量,更何况服务运行工夫以年为单位。 所以,咱们只能用到磁盘。 但问题是:如果将数据存盘,Redis是查不到的。 但如果Redis能查到磁盘上的数据,是否能解此题? RedRock反对Redis存储磁盘,那么咱们尝试用RedRock解解此题。 二、首先拆分假如咱们用100台机器,每台跑一个RedRock服务过程。即一个RedRock服务,反对500K qps,其中200K qps write,300K qps read。这样百台机器就能够达到五千万(50M)qps。 首先,每天新增的数据对于每台机器,只有1.7 TB,须要存盘。再加上RedRock采纳压缩存盘,尽管压缩率依据数据的不同而压缩效率不一样,但最差的状况下,咱们也有信念取得至多50%的压缩率(最好能够到10%)。 所以,对于每台机器,每天会新增最多 0.9 TB数据,而后入磁盘。一年的数据能够到300 TB这个量级。 这个是磁盘的理论最大量,古代的机器配置齐全反对一年这个规模的数据存盘。 但理论利用中,很多时候用不到这么大量,可能会有十倍到百倍甚至千倍的缩减,请急躁看上面的剖析。 三、查看磁盘带宽够否下面的剖析还太简略,因为磁盘还有一个瓶颈,就是读写速度是否够? 1. 先看磁盘写对于每台机器,注入的速度是:0.1 KB * 200K qps = 20MB/每秒 的注入速度。 再思考RedRock用了RocksDB写入,个别会有10倍的写放大,所以,磁盘写所消耗的带宽是 200MB/每秒,再思考压缩50%,则只有100MB/每秒的写入。 这个对于以后的SSD的性能要求是足够的。 2. 再看磁盘读30M qps read摊派到100台机器,那么每台机器是300K qps read。 咱们假如90%的读,都是热数据,即都在RedRock的内存中间接读取,那么只有10%的读须要到磁盘上。那么理论磁盘的读的带宽需要是(还需思考50%的压缩): 30K qps read from disk 0.1 KB 50% = 1.5MB read/每秒 ...

May 26, 2022 · 1 min · jiezi

关于redis:Redis-做接口限流一个注解的事

Redis 除了做缓存,还无能很多很多事件:分布式锁、限流、解决申请接口幂等性。。。太多太多了~ 明天想和小伙伴们聊聊用 Redis 解决接口限流,这也是最近的 TienChin 我的项目波及到这个知识点了,我就拎进去和大家聊聊这个话题,前面视频也会讲。 1. 筹备工作首先咱们创立一个 Spring Boot 工程,引入 Web 和 Redis 依赖,同时思考到接口限流个别是通过注解来标记,而注解是通过 AOP 来解析的,所以咱们还须要加上 AOP 的依赖,最终的依赖如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency>而后提前准备好一个 Redis 实例,这里咱们我的项目配置好之后,间接配置一下 Redis 的根本信息即可,如下: spring.redis.host=localhostspring.redis.port=6379spring.redis.password=123好啦,筹备工作就算是到位了。 2. 限流注解接下来咱们创立一个限流注解,咱们将限流分为两种状况: 针对以后接口的全局性限流,例如该接口能够在 1 分钟内拜访 100 次。针对某一个 IP 地址的限流,例如某个 IP 地址能够在 1 分钟内拜访 100 次。针对这两种状况,咱们创立一个枚举类: public enum LimitType { /** * 默认策略全局限流 */ DEFAULT, /** * 依据请求者IP进行限流 */ IP}接下来咱们来创立限流注解: @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RateLimiter { /** * 限流key */ String key() default "rate_limit:"; /** * 限流工夫,单位秒 */ int time() default 60; /** * 限流次数 */ int count() default 100; /** * 限流类型 */ LimitType limitType() default LimitType.DEFAULT;}第一个参数限流的 key,这个仅仅是一个前缀,未来残缺的 key 是这个前缀再加上接口办法的残缺门路,独特组成限流 key,这个 key 将被存入到 Redis 中。 ...

May 17, 2022 · 3 min · jiezi

关于redis:Redis核心技术与实践-03-高性能IO模型为什么单线程Redis能那么快

原文地址:https://time.geekbang.org/col... 集体博客地址:http://njpkhuan.cn/archives/r... 你好,我是蒋德钧。 明天,咱们来探讨一个很多人都很关怀的问题:“为什么单线程的 Redis 能那么快?” 首先,我要和你厘清一个事实,咱们通常说,Redis 是单线程,次要是指 Redis 的网络 IO 和键值对读写是由一个线程来实现的,这也是 Redis 对外提供键值存储服务的次要流程。但 Redis 的其余性能,比方长久化、异步删除、集群数据同步等,其实是由额定的线程执行的。 所以,严格来说,Redis 并不是单线程,然而咱们个别把 Redis 称为单线程高性能,这样显得“酷”些。接下来,我也会把 Redis 称为单线程模式。而且,这也会促使你紧接着发问:“为什么用单线程?为什么单线程能这么快?” 要弄明确这个问题,咱们就要深刻地学习下 Redis 的单线程设计机制以及多路复用机制。之后你在调优 Redis 性能时,也能更有针对性地防止会导致 Redis 单线程阻塞的操作,例如执行复杂度高的命令。 好了,话不多说,接下来,咱们就先来学习下 Redis 采纳单线程的起因。 Redis 为什么用单线程?要更好地了解 Redis 为什么用单线程,咱们就要先理解多线程的开销。 多线程的开销日常写程序时,咱们常常会听到一种说法:“应用多线程,能够减少零碎吞吐率,或是能够减少零碎扩展性。”确实,对于一个多线程的零碎来说,在有正当的资源分配的状况下,能够减少零碎中解决申请操作的资源实体,进而晋升零碎可能同时解决的申请数,即吞吐率。上面的左图是咱们采纳多线程时所期待的后果。 然而,请你留神,通常状况下,在咱们采纳多线程后,如果没有良好的零碎设计,理论失去的后果,其实是右图所展现的那样。咱们刚开始减少线程数时,零碎吞吐率会减少,然而,再进一步减少线程时,零碎吞吐率就增长缓慢了,有时甚至还会呈现降落的状况。 为什么会呈现这种状况呢?一个要害的瓶颈在于,零碎中通常会存在被多线程同时拜访的共享资源,比方一个共享的数据结构。当有多个线程要批改这个共享资源时,为了保障共享资源的正确性,就须要有额定的机制进行保障,而这个额定的机制,就会带来额定的开销。 拿 Redis 来说,在上节课中,我提到过,Redis 有 List 的数据类型,并提供出队(LPOP)和入队(LPUSH)操作。假如 Redis 采纳多线程设计,如下图所示,当初有两个线程 A 和 B,线程 A 对一个 List 做 LPUSH 操作,并对队列长度加 1。同时,线程 B 对该 List 执行 LPOP 操作,并对队列长度减 1。为了保障队列长度的正确性,Redis 须要让线程 A 和 B 的 LPUSH 和 LPOP 串行执行,这样一来,Redis 能够无误地记录它们对 List 长度的批改。否则,咱们可能就会失去谬误的长度后果。这就是多线程编程模式面临的共享资源的并发访问控制问题。 ...

May 17, 2022 · 2 min · jiezi

关于redis:Redis核心技术与实践-02-数据结构快速的Redis有哪些慢操作

原文地址:https://time.geekbang.org/col... 博客地址:http://njpkhuan.cn/archives/r... 你好,我是蒋德钧。 一提到 Redis,咱们的脑子里马上就会呈现一个词:“快。”然而你有没有想过,Redis 的快,到底是快在哪里呢?实际上,这里有一个重要的体现:它接管到一个键值对操作后,能以微秒级别的速度找到数据,并疾速实现操作。 数据库这么多,为啥 Redis 能有这么突出的体现呢?一方面,这是因为它是内存数据库,所有操作都在内存上实现,内存的访问速度自身就很快。另一方面,这要归功于它的数据结构。这是因为,键值对是按肯定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以高效的数据结构是 Redis 疾速解决数据的根底。这节课,我就来和你聊聊数据结构。 说到这儿,你必定会说:“这个我晓得,不就是 String(字符串)、List(列表)、Hash(哈希)、Set(汇合)和 Sorted Set(有序汇合)吗?”其实,这些只是 Redis 键值对中值的数据类型,也就是数据的保留模式。而这里,咱们说的数据结构,是要去看看它们的底层实现。 简略来说,底层数据结构一共有 6 种,别离是简略动静字符串、双向链表、压缩列表、哈希表、跳表和整数数组。它们和数据类型的对应关系如下图所示: 能够看到,String 类型的底层实现只有一种数据结构,也就是简略动静字符串。而 List、Hash、Set 和 Sorted Set 这四种数据类型,都有两种底层实现构造。通常状况下,咱们会把这四种类型称为汇合类型,它们的特点是一个键对应了一个汇合的数据。 看到这里,其实有些问题曾经值得咱们去思考了: 这些数据结构都是值的底层实现,键和值自身之间用什么构造组织?为什么汇合类型有那么多的底层构造,它们都是怎么组织数据的,都很快吗?什么是简略动静字符串,和罕用的字符串是一回事吗?接下来,我就和你聊聊前两个问题。这样,你不仅能够晓得 Redis“快”的基本原理,还能够借此了解 Redis 中有哪些潜在的“慢操作”,最大化 Redis 的性能劣势。而对于简略动静字符串,我会在前面的课程中再和你探讨。 咱们先来看看键和值之间是用什么构造组织的。 键和值用什么构造组织?为了实现从键到值的快速访问,Redis 应用了一个哈希表来保留所有键值对。 一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。所以,咱们常说,一个哈希表是由多个哈希桶组成的,每个哈希桶中保留了键值对数据。 看到这里,你可能会问了:“如果值是汇合类型的话,作为数组元素的哈希桶怎么来保留呢?”其实,哈希桶中的元素保留的并不是值自身,而是指向具体值的指针。这也就是说,不论值是 String,还是汇合类型,哈希桶中的元素都是指向它们的指针。 在下图中,能够看到,哈希桶中的 entry 元素中保留了key和value指针,别离指向了理论的键和值,这样一来,即便值是一个汇合,也能够通过*value指针被查找到。 因为这个哈希表保留了所有的键值对,所以,我也把它称为全局哈希表。哈希表的最大益处很显著,就是让咱们能够用 O(1) 的工夫复杂度来疾速查找到键值对——咱们只须要计算键的哈希值,就能够晓得它所对应的哈希桶地位,而后就能够拜访相应的 entry 元素。 你看,这个查找过程次要依赖于哈希计算,和数据量的多少并没有间接关系。也就是说,不论哈希表里有 10 万个键还是 100 万个键,咱们只须要一次计算就能找到相应的键。 然而,如果你只是理解了哈希表的 O(1) 复杂度和疾速查找个性,那么,当你往 Redis 中写入大量数据后,就可能发现操作有时候会忽然变慢了。这其实是因为你疏忽了一个潜在的危险点,那就是哈希表的抵触问题和 rehash 可能带来的操作阻塞。 为什么哈希表操作变慢了?当你往哈希表中写入更多数据时,哈希抵触是不可避免的问题。这里的哈希抵触,也就是指,两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。 毕竟,哈希桶的个数通常要少于 key 的数量,这也就是说,难免会有一些 key 的哈希值对应到了同一个哈希桶中。 Redis 解决哈希抵触的形式,就是链式哈希。链式哈希也很容易了解,就是指同一个哈希桶中的多个元素用一个链表来保留,它们之间顺次用指针连贯。 ...

May 17, 2022 · 2 min · jiezi

关于redis:聊聊Redis热点key热点问题

阐明缓存穿透、缓存击穿和缓存雪崩是Redis面试当中和理论开发中,常常须要思考的一个问题。很多人对该问题的产生、起因和解决方案还是不够清晰。其实大家针对该三种状况,去仔细分析一个产生的原理就能很好的找到一个好的解决方案。 本文通过定义、案例、危害和解决方案的几个角度,来帮忙你疾速理解该三个问题。 置信大家在网上也看到很多解决这三种问题的解决方案,其中的一些计划是否是一个正确的计划呢?本文也将一一剖析此类计划的优缺点。 下图为本文的内容纲要,文章也是围绕这几点进行剖析与总结。 三者比拟缓存穿透、缓存击穿和缓存雪崩都是因为缓存中数据不存在,导致走数据库去查问数据。因为缓存数据不存在,所有的申请都会走到数据库,因而会导致数据库的压力过大甚至呈现服务解体,导致整个零碎无奈应用。缓存穿透定义:缓存穿透是因为客户端求的数据在缓存中不存在,而后去查询数据库,然而数据库没有客户端要查问的数据,导致每一次申请都会走数据库查问操作。真正的问题在于该数据自身就是不存在的。 举例:客户端申请商品详情信息时,携带一个商品ID,此时该商品ID是不存在的(不论是缓存中还是数据库中)。导致每一次申请该ID商品的数据信息都会走数据库。 危害:因为申请的参数对应的数据基本不存在,会导致每一次都会申请数据库,减少数据库的压力或者服务解体,更有甚至影响到其余的业务模块。常常产生在用户歹意申请的状况下会产生。 解决方案: 依据申请的参数缓存一个null值。并且为该值设置一个过期工夫,能够将工夫设置短暂一点。应用布隆过滤器,首先通过布隆过滤器进行筛选,如果在过滤器中存在则去查询数据库,而后增加到缓存中。如果不存在则间接返回客户端数据不存在。因为缓存穿透可能是用户发动歹意申请,能够将用户ip给记录下来,针对歹意的ip申请进行封禁。计划剖析: 第一种计划,针对不存在的key,会缓存一个空的值。假如这样的申请特地多,是否都会一一去设置一个空值的缓存,此时Redis中就存在大量有效的缓存空值。假如这样的key是商品或者文章类的ID,咱们在设置空值之后,如果后盾增加数据应该去更新ID对应的缓存值,并设置一个正当的过期工夫。第二种计划,也是业界应用最多的一种计划。布隆过滤器的长处在于基于Redis实现,内存操作并且底层的实现也是十分节约内存。对于布隆过滤器的介绍,能够参考该文章。 当后盾增加数据胜利时,将该数据的ID增加到布隆过滤器中,前端在申请时先走布隆过滤器进行验证是否存在。但布隆过滤器也存在一个弊病,就是hash抵触问题。这里的hash抵触是什么意思呢?就是说多个ID在进行hash计算时,失去的hash位都是同一个值,这就导致在验证是否存在时误判。自身是有的,失去的后果是没有。布隆过滤器的一个弊病就是,它说有并不一定有,它说没有就一点是没有的。第三种计划,针对同一用户一段时间内发动大量的申请,触发缓存穿透机制,此时咱们能够显示该客户端的拜访。但攻击者如果是发动DDOS这样的攻打,是没法齐全的防止此类攻打,因而这种计划不是一个很好的解决方案。计划总结: 咱们首先在申请层面减少第3中计划,做一个限流机制、IP黑名单机制,管制一些歹意的申请,如果是误判咱们能够实现IP解封这样的操作。在缓存层则应用第1中计划实现。设置一个正当的缓存工夫。对于能容忍误判的业务场景,能够间接才用第2中计划实现。齐全基于Redis,缩小了零碎的复杂度。缓存击穿定义:缓存击穿是因为某个热点key不存在,导致走数据库查问。减少了数据库的压力。这种压力可能是霎时的,也可能是比拟长久的。真正的问题在于该key是存在,只是缓存中不存在,导致走数据库操作。 举例:有一个热门的商品,用户查看商品详情时携带商品的ID以获取到商品的详情信息。此时缓存中的数据曾经过期了,因而来的所有申请都要走数据库去查问。 危害:绝对缓存穿透而言,该数据在数据库中是存在的,只是因为缓存过期了,导致要走一次数据库,而后在增加到缓存中,下次申请就能失常走缓存。所谓的危害同样的还是针对数据库层面的危害。 解决方案: 加互斥锁。针对第一个申请,发现缓存中没有数据,此时查询数据库增加到缓存外面。这样前面的申请就不须要走数据库查问。减少业务逻辑过期工夫。在设置缓存时,咱们能够增加一个缓存过期工夫。每次去读取的时候,做一个判断,如果这个过期工夫与以后工夫小于一个范畴,触发一个后盾线程,去数据库拉取一下数据,接着更新一下缓存数据和缓存的过期工夫。其实原理就是代码层面给缓存缩短缓存时长。数据预热。实现通过后盾把数据增加到缓存外面。例如秒杀场景开始前,就把商品的库存增加到缓存外面,这样用户申请来了之后,就间接走缓存。永恒不过期。在给缓存设置过期工夫时,让它永恒不过期。后盾独自开启一个线程,来保护这些缓存的过期工夫和数据更新。计划剖析: 互斥锁保障了只有一个申请走数据库,这是一个长处。然而对于分布式的零碎,得才用分布式锁实现,分布式锁的实现自身就有肯定的难点,这样晋升了零碎的复杂度。对于Redis分布式的介绍,能够参考该文章。第2种计划,利用Redis不过期,业务过期的计划实现。保障了每一次申请都能拿到数据,同时也能够做到一个后盾线程去更新数据。毛病在于后盾线程没有更新完数据,此时申请拿到的数据是旧数据,可能对应实时性要求高的业务场景存在弊病。第3种计划,应用缓存预热每次加载都走缓存,与第2种计划差不多。不过也存在热点数据更新问题,因而该计划适宜数据实时性要求不高的数据。第4中计划,和第2、3种计划相似,在此基础上进行了肯定优化,应用后盾异步线程被动去更新缓存数据。难点在于更新的频率管制。计划总结: 对于实时性要求高的数据,举荐应用第1种计划,尽管在技术上有肯定的难度然而能做到数据的实时性解决。如果产生某些申请等待时间久,能够返回异样,让客户端从新发送一次申请。对于实时性要求不高的数据,能够应用第4种计划。缓存雪崩定义:后面在说到缓存击穿,是因为缓存中的某个热点key生效,导致大量申请走数据库。然而缓存雪崩其实也是同样的情理,只不过这个更重大而已,是大部分缓存的key生效,而不是一个或者两个key生效。 举例:在一个电商零碎中,某一个分类下的商品数据在缓存中都生效了。然而以后零碎的很多申请都是该分类上面的商品数据。这样就导致所有的申请都走数据库查问。 危害:因为一瞬间大量的申请涌入,每一个申请都要走数据库进行查问。数据库霎时流量涌入,重大减少数据库累赘,很容易导致数据库间接瘫痪。 解决方案: 缓存工夫随机。因为某一时间,大量的缓存生效,阐明缓存的过期工夫比拟集中。咱们间接将过期的工夫设置为不集中,随机打乱。这样缓存过期工夫绝对不会很集中,就不会呈现同一时刻大量申请走数据库进行查问操作。多级缓存。不单纯的靠Redis来做缓存,咱们也能够应用memcached来做缓存(这里只是举一个例子,其余的缓存服务也能够)。缓存数据时,对Redis做一个缓存,对memcached做一个缓存。如果Redis生效了,咱们能够走memcached。互斥锁。缓存击穿中咱们提到了应用互斥锁来实现,同样咱们也能够用在雪崩的状况下。设置过期标记。其实也能够用到缓存击穿中讲到的永恒不过期。当申请时,判断过期工夫,如果邻近过期工夫则设置一个过期标记,触发一个独立的线程去对这个缓存进行更新。计划剖析: 第1种计划采纳随机数缓存工夫,能保障key的生效工夫扩散。难点在于如何设置缓存工夫,如果对于一些须要设置短缓存工夫并数据量十分大的数据,该计划就须要正当的管制工夫。第2种计划应用多级缓存,能够保障申请全副走缓存数据。但这样减少了零碎的架构难度,以及其余的各种问题,例如缓存多级更新。第3种计划应用互斥锁,在缓存击穿中咱们提到了互斥锁,在雪崩的场景中咱们尽管能应用,然而这样会产生大量的分布式锁。第4种计划应用逻辑缓存工夫,很好的保障了零碎的缓存压力。计划总结:在理论的我的项目中举荐应用第1、2和4种计划试下会更好一些。 总结缓存穿透是因为数据库自身没有该数据。缓存击穿和缓存雪崩是数据库中存在该数据,只是缓存中的数据生效了,导致从新要查问一次数据库再增加到缓存中去。缓存击穿是针对局部热点key,而缓存雪崩是大面积缓存生效。两则原理上其实是一样的,无非就是针对缓存的key的划分不同而已。

May 16, 2022 · 1 min · jiezi

关于redis:Redis-Sentinel-哨兵实现原理之三个定时任务

Redis Sentinel 的根本实现原理,具体蕴含以下几个方面: 三个定时工作主管下线主观下线Sentinel 领导者选举故障转移本节对 三个定时工作 开展阐明。Redis Sentinel 通过三个定时工作实现对各个节点发现和监控: 每隔 10 秒;每隔 2 秒;每隔 1 秒;每隔 10 秒每隔 10 秒,每隔 Sentinel 节点会向主节点和从节点发送 info 命令获取最新的拓扑图。 作用体现通过向主节点执行 info 命令,获取从节点信息,这也是为什么 Sentinel 节点不须要显示配置监控从节点;当有新的从节点退出时能够立刻感知到;节点不可达或故障转移后,能够通过 info 命令实时更新节点拓扑信息;每隔 2 秒每隔 2 秒,每个 Sentinel 节点会向 Redis 数据节点的 __sentinel__:hello 频道发送该 Sentinel 节点对于主节点的判断以及以后 Sentinel 节点的信息,同时每个 Sentinel 节点也会订阅该频道,来理解其余 Sentinel 节点以及它们对主节点的判断。 工作内容发现新的 Sentinel 节点:通过订阅主节点的 __sentinel__:hello 理解其余 Sentinel 节点信息,如果是新退出的 Sentinel 节点,将该 Sentinel 节点信息保存起来,并于该 Sentinel 节点创立连贯。Sentinel 节点之间替换主节点的状态,作为前面主观下线以及领导者选举的根据。 每隔 1 秒心跳检测每隔 1 秒,每个 Sentinel 节点会向主节点、从节点、其余 Sentinel 节点发送一条 ping 命令做一次心跳检测,来确认这些节点以后是否可达。 ...

May 16, 2022 · 1 min · jiezi

关于redis:redis数据结构之集合常用命令

命令用例形容备注saddsadd key-name item [item ...]将一个或多个元素增加到汇合外面,并返回被增加元素当中本来并不存在于汇合外面的元素数量 sremsrem key-name item [item ...]从汇合外面移除一个或多个元素,并返回被移除元素的数量 sismembersismember key-name item查看元素item是否存在于汇合key-name里 scardscard key-name返回汇合蕴含的元素数量 smemberssmembers key-name返回汇合蕴含的所有元素 srandmembersrandmember key-name [count]从汇合外面随机地返回一个或多个元素。当count为负数时,命令返回的随机元素不会反复;当count为正数时,命令返回的随机元素可能会呈现反复 spopspop key-name count随机地移除汇合中的一个或多个元素,并返回被移除的元素 smovesmove source-key dest-key item如果汇合source-key蕴含元素item,那么从汇合source-key外面移除元素item,并将元素item增加到汇合dest-key中;如果item被胜利移除,那么汇合返回1,否则返回0 sdiffsdiff key-name [key-name ...]返回那些存在于第一个汇合,但不存在于其余汇合中的元素(差集) sdiffstoresdiffstore dest-key key-name [key-name ...]将那些存在于第一个汇合,但不存在于其余汇合中的元素存储到dest-key外面 sintersinter key-name [key-name ...]返回那些同时存在于所有汇合中的元素(交加) sinterstoresinterstore dest-key key-name [key-name ...]将那些同时存在于所有汇合的元素存储到dest-key键外面 sunionsunion key-name [key-name ...]返回那些至多存在于一个汇合中的元素(并集) sunionstoresunionstore dest-key key-name [key-name ...]将那些至多存在于一个汇合中的元素存储到dest-key键外面 smismembersmismember key-name item [item ...]查看一群元素是否存在于汇合key-name里Available since: 6.2.0sintercardsintercard numkeys key [key ...] [LIMIT limit]绝对于sinter的后果返回一个交加,sintercard返回的是交加的数量,numkeys示意前面汇合的数量有几个;limit默认值为0,示意不限度;如果非0,寻找到交加的数量达到limit,命令将进行,并返回Available since: 7.0.0sscansscan key cursor [match pattern] [count count-value]迭代汇合

May 16, 2022 · 1 min · jiezi

关于redis:Redis核心技术与实践-01-基本架构一个键值数据库包含什么

原文地址:https://time.geekbang.org/col... 集体博客地址:http://njpkhuan.cn/archives/r... 你好,我是蒋德钧。 咱们晓得,Redis 是典型的键值数据库,所以明天,我筹备手把手地带你构建一个简略的键值数据库。为啥要这么做呢? 还记得我在开篇词说过吗?Redis 自身比较复杂,如果咱们一上来就间接钻研一个个具体的技术点,比方“单线程”“缓存”等,尽管能够间接学习到具体的内容,甚至立马就能解决一些小问题,然而这样学,很容易迷失在细枝末节里。 从我本人的教训来看,更好的学习形式就是先建设起“零碎观”。这也就是说,如果咱们想要深刻了解和优化 Redis,就必须要对它的总体架构和要害模块有一个全局的认知,而后再深刻到具体的技术点。这也是咱们这门课保持的一种讲课形式。 我置信,通过这样一个过程,咱们在实践中定位和解决问题时,就会轻松很多,而且你还能够把这个学习形式迁徙到其余的学习流动上。我心愿你能彻底把握这个学习思路,让本人的学习、工作效率更高。 说远了,还是回到咱们明天的课程主题上。明天,在结构这个简略的键值数据库时,咱们只须要关注整体架构和外围模块。这就相当于医学上在正式解剖人体之前,会先解剖一只小白鼠。咱们通过分析这个最简略的键值数据库,来迅速抓住学习和调优 Redis 的要害。 我把这个简略的键值数据库称为 SimpleKV。须要留神的是,GitHub 上也有一个名为 SimpleKV 的我的项目,这跟我说的 SimpleKV 不是一回事,我说的只是一个具备要害组件的键值数据库架构。 好了,你是不是曾经筹备好了,那咱们就一起来结构 SimpleKV 吧。 开始结构 SimpleKV 时,首先就要思考外面能够存什么样的数据,对数据能够做什么样的操作,也就是数据模型和操作接口。它们看似简略,实际上却是咱们了解 Redis 常常被用于缓存、秒杀、分布式锁等场景的重要根底。 了解了数据模型,你就会明确,为什么在有些场景下,原先应用关系型数据库保留的数据,也能够用键值数据库保留。例如,用户信息(用户 ID、姓名、年龄、性别等)通常用关系型数据库保留,在这个场景下,一个用户 ID 对应一个用户信息汇合,这就是键值数据库的一种数据模型,它同样能实现这一存储需要。 然而,如果你只晓得数据模型,而不理解操作接口的话,可能就无奈了解,为什么在有些场景中,应用键值数据库又不适合了。例如,同样是在下面的场景中,如果你要对多个用户的年龄计算均值,键值数据库就无奈实现了。因为它只提供简略的操作接口,无奈反对简单的聚合计算。 那么,对于 Redis 来说,它到底能做什么,不能做什么呢?只有先搞懂它的数据模型和操作接口,咱们能力真正把“这块好钢用在刀刃上”。 接下来,咱们就先来看能够存哪些数据。 能够存哪些数据?对于键值数据库而言,根本的数据模型是 key-value 模型。 例如,“hello”: “world”就是一个根本的 KV 对,其中,“hello”是 key,“world”是 value。SimpleKV 也不例外。在 SimpleKV 中,key 是 String 类型,而 value 是根本数据类型,例如 String、整型等。 然而,SimpleKV 毕竟是一个简略的键值数据库,对于理论生产环境中的键值数据库来说,value 类型还能够是简单类型。 不同键值数据库反对的 key 类型个别差别不大,而 value 类型则有较大差异。咱们在对键值数据库进行选型时,一个重要的思考因素是它反对的 value 类型。例如,Memcached 反对的 value 类型仅为 String 类型,而 Redis 反对的 value 类型包含了 String、哈希表、列表、汇合等。Redis 可能在理论业务场景中失去宽泛的利用,就是得益于反对多样化类型的 value。 ...

May 16, 2022 · 2 min · jiezi

关于redis:redis使用场景

应用场景最近联系人列表保护长度为 100 的最近联系人列表 如果指定的联系人已在列表中,则从列表中移除 (lrem) lrem list 1 mobile将指定的联系人增加到列表最后面 (lpush) lpush list mobile如果增加实现后,列表长度超过 100 ,则对列表进行修剪,仅保留列表后面的 100 个联系人 (ltrim) ltrim list 0 99限度短信频率,比方同一手机号60分钟之内只能发送3条限度短信频率,同一手机号60分钟之内只能发送3条 精华版采纳队列实现 local lKey = KEYS[1] -- 列表键,比方间接应用手机号local maxCount = tonumber(ARGV[1]) --肯定工夫内短信最多发送条数-- 以后工夫戳,毫秒local currentTime = tonumber(ARGV[2])-- 一段时间:一分钟、一小时、一天,毫秒local period = tonumber(ARGV[3])-- 查看以后队列大小。local lSize = redis.call("llen", lKey)-- 阐明一段时间内队列未满,间接压入队列,返回 1,示意能够发送短信local flag = falseif lSize < maxCount then -- 记录短信的发送工夫(毫秒) redis.call("lpush", lKey, currentTime) flag = trueelse -- 取队列当中最早一条短信的发送工夫,拿以后工夫减去此工夫,如果超过“一段”工夫,则能够移掉,并压入以后短信工夫。 local oldestTime = tonumber(redis.call("lindex", lKey, -1)) local result = currentTime - oldestTime -- 以后工夫与第一条短信的发送工夫距离大于“周期”,才可持续发送 if result > period then redis.call("rpop", lKey) -- 弹出队列中最早一条短信发送工夫 redis.call("lpush", lKey, currentTime) -- 压入最新一条短信的发送工夫 flag = true endend-- 刷新键的过期工夫为“一段”工夫。redis.call("expire", lKey, period/1000)-- flag: true 示意能够发送短信,false 则不能发送短信if flag then return 1endreturn 0简陋版 ...

May 15, 2022 · 1 min · jiezi

关于redis:redis数据结构之列表常用命令和使用场景

常用命令介绍:命令用例介绍linkrpushrpush key-name value [value ...]将一个或多个值推入到列表的右端 lpushlpush key-name value [value ...]将一个或多个值推入到列表的左端 rpoprpop key-name移除并返回列表最右端的元素 lpoplpop key-name移除并返回列表最左端的元素 lindexlindex key-name offset返回列表中偏移量为offset的元素 lrangelrange key-name start end返回列表从start偏移量到end偏移量范畴内的所有元素,其中偏移量为start和偏移量为end的元素也会蕴含在被返回的元素之内 ltrimltrim key-name start end对列表进行修剪,只保留从start偏移量到end偏移量范畴内的元素,其中偏移量为start和偏移量为end的元素也会被保留 blpopblpop key-name [key-name ...] timeout从第一个非空列表中弹出位于最左端的元素,或者在timeout秒之内阻塞并期待可弹出的元素呈现 brpopbrpop key-name [key-name ...]从第一个非空列表中弹出位于最右端的元素,或者在timeout秒之内阻塞并期待可弹出的元素呈现 rpoplpushrpoplpush source-key dest-key从source-key列表中弹出位于最右端的元素,而后将这个元素推入dest-key列表的最左端,并向用户返回这个元素 brpoplpushbrpoplpush source-key dest-key timeout从source-key列表中弹出位于最右端的元素,而后将这个元素推入dest-key列表的最左端,并向用户返回这个元素;如果source-key为空,那么在timeout秒之内阻塞并期待可弹出的元素呈现 lremlrem key count element从列表中删除一些(count)等于element的元素,count = 0,删掉列表中所有等于element的元素, count > 0,从左到右删除指定数量个等于element的元素, count < 0,从右到左删除指定数量个等于elemnt的元素https://redis.io/commands/lrem/linsertlinsert key BEFORE或AFTER pivot element在列表某个元素(pivot)的后面或前面插入元素element。如果pivot有多个,定位到列表左起第一个;如果pivot不存在,返回-1,否则返回列表长度https://redis.io/commands/lin...llenllen key返回列表的长度。如果列表不存在,返回0。https://redis.io/commands/llen/lsetlset key index element给列表指定地位设置成元素elementhttps://redis.io/commands/lset/rpushxrpushx key-name value [value ...]在列表存在的状况,从左边插入多个值。如果列表不存在,没有任何影响https://redis.io/commands/rpu...lpushxlpushx key-name value [value ...]在列表存在的状况,从右边插入多个值。如果列表不存在,没有任何影响https://redis.io/commands/lpu...LMPOPnumkeys key [key ...] LEFT 或 RIGHT [COUNT count]Pop elements from a listhttps://redis.io/commands/lmpop/BLMPOPnumkeys key [key ...] LEFT 或 RIGHT [COUNT count]Pop elements from a list, or block until one is availablehttps://redis.io/commands/blm...LMOVELMOVE source destination LEFT 或 RIGHT LEFT 或 RIGHTPop an element from a list, push it to another list and return it BLMOVEsource destination LEFT 或 RIGHT LEFT 或 RIGHT timeoutPop an element from a list, push it to another list and return it; or block until one is available LPOSLPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]Return the index of matching elements on a list 应用场景最近联系人列表保护长度为 100 的最近联系人列表 ...

May 14, 2022 · 2 min · jiezi

关于redis:redis如何在执行lua脚本的时候输出日志

Use a Lua Table定义一个函数和一个table,把日志记录在table中,并返回 local logtable = {} local function log(msg) logtable[#logtable+1] = msgend log("hello")log("world") return logtable$ redis-cli --eval log.lua1) "hello"2) "world" Use a Redis Listlocal loglist = KEYS[1]redis.pcall("del", loglist) local function log(msg) redis.pcall("rpush", loglist, msg)end log("hello")log("world") return 'ok' .. 'ok'$redis-cli --eval log.lua mylist"okok" lrange mylist 0 -11) "hello"2) "world"Use Redis’ Pub/Sublocal function log(msg) redis.pcall("publish", 'log', msg)end log("hello")log("world") return 'ok' .. 'ok'执行脚本客户端$ redis-cli --eval log.lua"okok" 日志订阅者客户端 subscribe logReading messages... (press Ctrl-C to quit)1) "subscribe"2) "log"3) (integer) 11) "message"2) "log"3) "hello"1) "message"2) "log"3) "world"参考:https://redis.com/blog/5-6-7-... ...

May 12, 2022 · 1 min · jiezi

关于redis:redis如何执行lua脚本附lua部分基本语法

在客户端命令行执行Lua脚本$ cat /tmp/script.luareturn redis.call('SET',KEYS[1],ARGV[1])$ redis-cli --eval /tmp/script.lua location:hastings:temp , 23OKnote:命令行中应用逗号分隔KEYS和ARGV,KEYS和ARGV如果存在多个,两头应用一个和多个空格分隔。留神:逗号前后,必须含有至多一个空格。场景一,前后都有空格: redis-cli --eval /tmp/script.lua location:hastings:temp , 23KEYS={"location:hastings:temp"} ARGV={"23"} 场景二,前面有空格redis-cli --eval /tmp/script.lua location:hastings:temp, 23KEYS={"location:hastings:temp,","23"} ARGV={} 场景三,后面有空格redis-cli --eval /tmp/script.lua location:hastings:temp ,23KEYS={"location:hastings:temp"; ",23"} ARGV={} 应用redis命令执行lua脚本evaleval script(脚本内容) numkeys(key个数) key [key ...](key列表) arg [arg ...](参数列表) eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world"hello redisworld"evalsha加载脚本:script load命令能够将脚本内容加载到Redis内存中,例如下面将test.lua加载到Redis中,失去SHA1$redis-cli script load "$(cat test.lua)""c40bee28ef52798c0a894fc857070c93bd107c40" script exists sha1 [sha1 ...] 用于判断sha1是否曾经加载到Redis内存中 script exists c40bee28ef52798c0a894fc857070c93bd107c40 aa1) (integer) 12) (integer) 0script flush于革除Redis内存曾经加载的所有Lua脚本 ...

May 12, 2022 · 2 min · jiezi

关于redis:深入Go底层原理重写Redis中间件实战网盘分享

download:深刻Go底层原理,重写Redis中间件实战【网盘分享】揭秘 Kotlin 1.6.20 重磅功能 Context Receivers 这篇文章咱们一起来聊一下 Kotlin 1.6.20 的新功能 Context Receivers,来看看它为咱们解决了什么问题。 通过这篇文章将会学习到以下内容: 扩大函数的局限性什么是 Context Receivers,以及如何使用Context Receivers 解决了什么问题引入 Context Receivers 会带来新的问题,咱们如何解决Context Receivers 利用范畴及注意事项扩大函数的局限性在 Kotlin 中承受者只能利用在扩大函数或者带承受者 lambda 表达式中, 如下所示。 class Context { var density = 0f}// 扩大函数inline fun Context.px2dp(value: Int): Float = value.toFloat() / density复制代码承受者是 fun 关键字之后和点之前的类型 Context,这里藏匿了两个学识点。 咱们可能像调用外部函数一样,调用扩大函数 px2dp(),通常拆散 Kotlin 作用域函数 with , run , apply 等等一起使用。with(Context()) { px2dp(100)}复制代码在扩大函数外部,咱们可能使用 this 关键字,或者藏匿关键字隐式拜访外部的成员函数,然而咱们不能拜访公有成员扩大函数使用起来很便利,咱们可能对系统或者第三方库进行扩大,然而也有局限性。 只能定义一个承受者,因此限度了它的可组合性,如果有多个承受者只能当做参数传送。比如咱们调用 px2dp() 方法的同时,往 logcat 和 file 中写入日志。class LogContext { fun logcat(message: Any){}}class FileContext { ...

May 12, 2022 · 2 min · jiezi

关于redis:Redis单线程模型

前言Redis6.0引入了多线程模型,那么在Redis6.0之前,Redis是单线程模型,那么单线程模型的Redis的底层模型是什么,为什么单线程模型还能那么快,本篇文章将对Redis的单线程模型进行学习。 注:Redis是单线程仅指Redis服务端的网络IO是单线程的,Redis的集群数据同步,长久化等都是多线程的。 注释一. 筹备常识学习Redis中的单线程模型,会波及一些操作系统相干的常识,所以先对这些常识进行一个简略介绍。 1. linux操作系统中的I/O在linux操作系统中,所有事物对象都是文件,linux执行任何模式的I/O操作时,都是对一个文件描述符进行读取或写入,留神这里的文件描述符不单会关联传统意义上的文件,还可能会关联管道,键盘,显示器或者网络连接。 2. socket概念既然linux操作系统中的任何模式的I/O都是对一个文件描述符的读取或写入,那么网络I/O也不例外,通过socket()函数能够创立网络连接,其返回的socket就是文件描述符,通过socket就能够像操作文件那样来操作网络通信,例如应用read()函数来读取对端计算机传来的数据,应用write()函数来向对端计算机发送数据。 socket又叫套接字,其将不同主机上的过程或者雷同主机上的不同过程进行通信做了一层形象,也能够将socket了解为应用层到传输层的一层形象。下表给出了两种罕用的socket。 socket类型阐明流式socket用于TCP通信,提供牢靠的,面向连贯的通信。数据报socket用于UDP通信,提供不牢靠,无连贯的通信。在进行网络通信的时候,须要一对socket,一个运行于客户端,一个运行于服务端,下图进行一个简略示意。 那么整个通信流程能够进行如下概括。 服务端运行后,会在服务端创立listen-socket,listen-socket会绑定服务端的ip和port,而后服务端进入监听状态;客户端申请服务端时,客户端创立connect-socket,connect-socket形容了其要连贯的服务端的listen-socket,而后connect-socket向listen-socket发动连贯申请;connect-socket与listen-socket胜利连贯后(TCP三次握手胜利),服务端会为已连贯的客户端创立一个代表该客户端的client-socket,用于后续和客户端进行通信;客户端与服务端通过socket进行网络I/O操作,此时就实现了客户端和服务端中的不同过程的通信。3. 用户过程缓冲区与内核缓冲区用户过程拜访系统资源(磁盘,网卡,键盘等)时,须要切换到内核态(Kernel Mode),拜访完结后,又须要从内核态切换为用户态(User Mode),这种切换非常耗时,所以用户过程会在用户过程空间中开拓一块缓冲区域,叫做用户过程缓冲区,用户过程如果是读系统资源,则会将读到的系统资源写入用户过程缓冲区,后续读就读用户过程缓冲区的内容,用户过程如果是写数据到系统资源,则会将写的数据先写入用户过程缓冲区,而后再将用户过程缓冲区的内容写到系统资源。所以用户过程缓存区会缩小用户过程在用户态和内核态之间的切换次数,从而升高切换的工夫。 用户过程拜访系统资源实际上须要借助操作系统内核实现,所以与系统资源产生I/O的理论是操作系统内核,操作系统内核为了缩小与系统资源理论的I/O的次数,也有一个缓冲区叫做内核缓冲区,如果是对系统资源的读,则先将系统资源数据读取并写入内核缓冲区中,而后再将内核缓冲区的内容写入用户过程缓冲区,如果是对系统资源的写,则先将用户过程缓冲区的内容写入内核缓冲区, 而后再将内核缓冲区的内容写到系统资源。这样能够无效升高操作系统内核与系统资源的理论I/O次数,升高I/O带来的工夫耗费。 上面以一个服务端解决一个客户端申请为例,对用户过程缓冲区与内核缓冲区进行一个更直观的阐明。(注:理论的网络申请比上面的图示更为简单,上面的图示只是一个大抵流程的体现,目标是帮忙领会用户过程缓冲区与内核缓冲区的作用) 那么上述过程能够概括如下。 操作系统内核通过与网卡(系统资源)进行网络I/O读取客户端申请数据到内核缓冲区中;服务端用户过程将内核缓冲区内容写入用户过程缓冲区中,随后用户过程读取用户过程缓冲区的内容并解决业务;服务端用户过程将响应内容写入用户过程缓冲区,而后再将用户过程缓冲区的内容写入内核缓冲区;操作系统内核通过与网卡进行网络I/O,将内核缓冲区的内容写到网卡。4. 同步阻塞I/O,同步非阻塞I/O和I/O多路复用同步阻塞I/O是用户过程调用read时发动的I/O操作,此时用户过程由用户态转换到内核态,只有在内核态中将I/O操作执行完后,才会从内核态切换回用户态,这期间用户过程会始终阻塞。同步阻塞I/O(Blocking IO,BIO)示意图如下。 同步非阻塞I/O是用户过程调用read时,用户过程由用户态转换到内核态后,此时如果没有系统资源数据可能被读取到内核缓冲区中,返回read失败,并从内核态切换回用户态。也就是用户过程发动I/O操作后会立刻失去一个操作后果,同时用户过程须要在read失败时始终反复的发动read,直至read胜利。同步非阻塞I/O(Non-Blocking IO,NIO)示意图如下。 I/O多路复用是一个用户过程中对多个文件描述符进行监控,一旦有文件描述符能够进行I/O操作,内核会告诉用户过程对相应的文件描述符进行I/O操作。最简略的实现是应用select操作来实现对多个文件描述符的监控,具体做法如下。 在用户过程中将文件描述符注册到select的文件描述符列表中;执行select操作,此时用户过程由用户态转换到内核态,而后内核会查找出select的文件描述符列表中所有能够进行I/O操作的文件描述符,并返回,此时内核态转换到用户态;用户过程在select操作返回前会始终阻塞,直至select操作返回,此时用户过程取得了能够I/O的文件描述符列表;用户过程取得了能够I/O操作的文件描述符列表后,会对列表中每个文件描述符发动I/O操作。I/O多路复用(IO Multiplexing)能够用下图进行示意。 5. 单Reactor单线程模型有了下面1-4点的根底,当初来介绍单Reactor单线程模型。已知在客户端与服务端通信的过程中,呈现了三种socket,如下所示。 listen-socket,是服务端用于监听客户端建设连贯的socket;connect-socket,是客户端用于连贯服务端的socket;client-socket,是服务端监听到客户端连贯申请后,在服务端生成的与客户端连贯的socket。(注:上述中的socket,能够被称为套接字,也能够被称为文件描述符。) 那么先看一下如下的客户端申请服务端的模型。 上图中的Server主线程中创立了listen-socket用于监听客户端的连贯申请,当Client1创立connect1-socket并发动connect操作时,Server主线程会从accept操作返回并失去代表Client1的client1-socket,随后Server在主线程中解决Client1的申请,此时Client2创立connect2-socket并发动connect操作,因为Server主线程正在解决Client1的申请,所以Server此时不会立刻与Client2建设连贯,等到Server主线程中解决完了Client1的申请并断开与Client1的连贯后,此时Server才会再与Client2建设连贯。上述的客户端申请服务端的模型,实质就是同步阻塞I/O模型,对于服务端来说,这种模型有两个问题,如下所示。 服务端是单线程的,同一时间只能在服务端主线程中监听到一个客户端建设连贯的申请,并且只会在解决完以后建设了连贯的客户端的申请后,才会持续与下一个客户端建设连贯;服务端的listen-socket的accept操作是阻塞的,服务端与客户端建设连贯后,client-socket的read和write操作是阻塞的,换句话说,服务端要么阻塞在listen-socket的accept操作上,要么阻塞在client-socket的read和write操作上。那么单Reactor单线程模型在引入了多路复用I/O后,对下面第二个问题进行了优化:服务端主线程中应用select或者epoll等操作,来同时监督listen-socket和client-socket。以select举例,服务端主线程中一开始会在select的文件描述符列表中增加listen-socket,随后调用select进入监督状态(此时主线程阻塞在select上),此时如果客户端的connect-socket发动了connect操作,服务端主线程就会从select上返回,并且判断是listen-socket准备就绪,所以会失去代表客户端的client-socket,该client-socket会被退出到select的文件描述符列表中,而后服务端主线程又调用select进入监督状态,此时是同时监督listen-socket和client-socket,后续主线程从select返回后,判断如果是listen-socket准备就绪,则将失去的client-socket退出select的文件描述符列表,如果是client-socket准备就绪,则解决对应的客户端的申请。单Reactor单线程模型能够用下图进行示意。 单Reactor单线程模型中,只有一个Reactor,负责调用select来监督listen-socket和client-socket,当有socket准备就绪时,称有事件产生,如果是listen-socket准备就绪,则产生了连贯事件,如果是client-socket准备就绪,则产生了读写事件,不同的事件由dispatch来散发到不同的模块进行解决,连贯事件由Acceptor来获取client-socket并退出到select的文件描述符列表,读写事件由Handler来解决即执行客户端的申请并响应。 单Reactor单线程模型的单线程体现在上述的操作均都是产生在主线程中,即当同时有连贯事件和读写事件准备就绪时,单Reactor单线程模型会串行的解决连贯事件和读写事件,该模型的长处就是简略且没有并发问题,毛病就是通常解决连贯事件很快然而解决读写事件会较慢从而造成CPU资源被节约,假若解决读写事件也很快,那么单Reactor单线程模型会是一个优良的抉择,恰好在Redis中,因为数据都是存储在内存中,Redis服务端响应客户端的读写事件的速度是很快的,所以,Redis中的单线程模型,理论就是单Reactor单线程模型。 二. Redis中的文件事件处理器Redis服务端是通过listen-socket来获取客户端连贯,通过client-socket来解决客户端申请,listen-socket和client-socket可连贯,可读或者可写时都会产生事件,称为文件事件,即文件事件是Redis服务端对socket的操作的形象。Redis有一个文件事件处理器来解决文件事件,示意图如下所示。 Redis服务端会在I/O多路复用器中将socket准备就绪的操作入队列,所以准备就绪的操作会作为文件事件有序的被文件事件分派器分派到事件处理器的不同模块解决。 Redis中的文件事件处理器就是一个单Reactor单线程模型,并且单线程是体现在事件处理器解决不同的事件时是单线程的。 上面给出客户端申请Redis服务端的流程示意图。 最初对上图做如下几点阐明。 如果客户端的connect-socket执行connect操作,或者客户端向Redis发动写申请,那么对应的socket会产生AE_READABLE事件;如果客户端向Redis发动读申请,那么对应的socket会产生 AE_WRITABLE事件;上述流程图中,编号雷同示意同一个申请的解决步骤。总结Redis的单线程模型,就是单Reactor单线程模型,Redis应用I/O多路复用,在单线程中轮询socket,并将对Redis库的建设连贯,敞开连贯,读数据和写数据申请都转换成了文件事件,最初Redis还应用其实现的文件事件分派器和事件处理器来解决不同的事件,整体执行效率高,还节俭了多线程的开销。

May 11, 2022 · 1 min · jiezi

关于redis:redis缓存穿透解决方案布隆过滤器的实现

布隆过滤器1.背景个别应用布隆过滤器来解决一个理论问题:缓存穿透。缓存穿透:绕过 Redis 服务器,间接进入后盾数据库查问的攻击方式,咱们就称之为缓存穿透。缓存穿透攻打,是指歹意用户在短时内大量查问不存在的数据,导致大量申请被送达数据库进行查问,当申请数量超过数据库负载下限时,使零碎响应呈现高提早甚至瘫痪的攻击行为,就是缓存穿透攻打。而解决缓存穿透的计划通常有两种:1.缓存空对象从缓存上取不到数据,在数据库中也取不到,这时能够把key-value键值对写成key-null键值对,并且设置无效工夫(设置短一些)。这样能够避免带有歹意的用户频繁地用一个值来攻打数据库。 2.布隆过滤器 2. 介绍过滤器,顾名思义,就是不让某些申请通过,不放行某些歹意的申请。在缓存中,采纳布隆过滤器,将可能存在的数据哈希到足够大的bitmap上,如果客户端申请的数据不存在,布隆过滤器会回绝客户端的申请,如果存在则会放行申请,再去查问缓存,按步骤执行前面的操作。布隆过滤器宗旨是采纳一个很长的二进制数组,通过一系列的 Hash 函数来确定该数据是否存在。布隆过滤器实质上是一个 n 位的二进制数组。你也晓得二进制只有 0 和 1 来示意,针对于以后咱们的场景。这里我模仿了一个二进制数组,其每一位它的初始值都是 0。 3. 原理咱们举个例子来感受一下其中原理。 咱们提到作为以后的商城,假如有 1000 个商品编号,从 1~1000。作为布隆过滤器,在初始化的时候,实际上就是对每一个商品编号进行若干次 Hash 来确定它们的地位。 “1”号商品计算: 比如说针对于以后的“1”编号,咱们对其执行了三次 Hash。所谓 Hash 函数就是将数据代入当前确定一个具体的地位。 Hash 1 函数:它会定位到二进制数组的索引为 1 上,并将其数值从 0 改为 1;Hash 2 函数:它定位到索引为 5 的地位,并将从 0 改为 1;Hash 3 函数:定位到索引为 99 的地位上,将其从 0 改为 1。 如图:“2”号商品计算: 那 1 号商品计算完当前,该轮到 2 号商品。2 号商品通过三次 Hash 当前,别离定位到索引为 1、3 以及 98 号地位上。 留神:原始数据中 1 号位因为方才曾经变成了 1,当初它不变;而 3 号位和 98 号位原始数据从 0 变为 1。 这里又衍生出一个 Hash 新规定:如果在 Hash 后,原始位它是 0 的话,将其从 0 变为 1;如果自身这一位就是 1 的话,则放弃不变。“1000”号商品计算: 此时 2 号商品也解决完了,咱们持续向后 3、4、5、6、7、8 直到编号达到了最初一个 1000,当商品编号 1000 解决完后,他将索引为 3、6、98 设置为 1。计算结束了,该怎么用呢?逻辑怎么呢? ...

May 10, 2022 · 1 min · jiezi

关于redis:MongoDB-入门教程系列之二使用-Spring-Boot-操作-MongoDB

本教程的前一篇文章:[MongoDB 入门教程系列之一:开发环境搭建以及 Node.js 和 Java 的读写访问](),咱们首先介绍了 MongoDB 本地环境的搭建,接着举了两个具体的例子,展现了如何应用 Node.js 和 Java 拜访 MongoDB 存储的数据。 本教程持续介绍如何应用业界风行的开发工具来操作 MongoDB. Spring Boot 是一个轻量级框架,能够实现基于 Spring 的应用程序的大部分配置工作。Spring Boot 的目标是提供一组工具,以便疾速构建容易配置的 Spring 应用程序,为利用开发人员省去大量传统 Spring 我的项目的繁琐配置。 本教程第一篇文章曾经介绍过,MongoDB 是一个基于分布式文件存储的数据库,由 C++ 语言编写,旨在为 WEB 利用提供可扩大的高性能数据存储解决方案。 本文介绍如何应用 Spring Boot 操作 MongoDB,从而通过 Java 代码在 MongoDB 里插入数据。 首先依照本教程前一篇文章的介绍,在本地搭建好 MongoDB 的开发环境。 新建一个 Java 我的项目,pom.xml 的内容如下: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.springframework</groupId><artifactId>gs-rest-service</artifactId><version>0.1.0</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mongodb</groupId><artifactId>mongodb-driver</artifactId><version>3.6.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency></dependencies><properties><java.version>1.8</java.version></properties><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><repositories><repository><id>spring-releases</id><url>https://repo.spring.io/libs-release</url></repository></repositories><pluginRepositories><pluginRepository><id>spring-releases</id><url>https://repo.spring.io/libs-release</url></pluginRepository></pluginRepositories></project> 其中上面这个 dependency 的作用是为 SpringBoot 利用提供操作 MongoDB 的性能: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>而这个 dependency 能让您的 Spring Boot 利用反对 junit: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>在 src/main/test 文件夹下创立一个以 Tests 结尾的 .java 文件,我的例子里是ApplicationTests.java: ...

May 10, 2022 · 1 min · jiezi

关于redis:首个彻底保证缓存一致性的开源方案

概述大量的理论的我的项目中,都会引入 Redis 缓存来缓解数据库的查问压力,此时因为一个数据在 Redis 和数据库两处进行了存储,就会有数据一致性的问题。目前业界尚未见到成熟的可能确保最终一致性的计划,特地是当如下场景产生时,会间接导致缓存数据与数据库数据不统一,可能给利用带来较大问题。 dtm-labs 致力于解决数据一致性问题,在剖析了行业的现有做法后,提出了新解决方案dtm-labs/dtm+dtm-labs/rockscache,彻底解决了上述问题。另外作为一个成熟计划,该计划还能够防缓存穿透,防缓存击穿,防缓存雪崩,同时也可利用于要求数据强统一的场景。 对于治理缓存的现有计划,本文不再赘述,不太理解的同学能够参考上面这两篇文章 这篇通俗易懂些:聊聊数据库与缓存数据一致性问题这篇更加深刻:携程最终统一和强一致性缓存实际乱序产生的不统一在上述这个时序图中,因为服务1产生了过程暂停(例如因为GC导致),因而当它往缓存当中写入v1时,笼罩了缓存中的v2,导致了最终的不统一(DB中为v2,缓存中为v1)。 对于上述这类问题该当如何解决?目前现存的计划,全都没有彻底解决该问题,个别都是通过设定稍短的过期工夫兜底。咱们实现的缓存提早删除计划,可能彻底解决这个问题,确保缓存与数据库之间的数据保持一致。解决原理如下: 缓存中的数据是一个hash,外面有以下几个字段: value: 数据自身lockUtil: 数据锁定到期工夫,当某个过程查问缓存无数据,那么先锁定缓存一小段时间,而后查问DB,而后更新缓存owner: 数据锁定者uuid查问缓存时: 如果数据为空,且被锁定,则睡眠1s后,从新查问如果数据为空,且未被锁定,同步执行"取数据",返回后果如果数据不为空,那么立刻返回后果,并异步执行"取数据"其中"取数据"的操作定义为: 判断是否须要更新缓存,上面两个条件满足其一,则须要更新缓存 数据为空,并且未被锁定数据的锁定已过期如果须要更新,则锁定缓存,查问DB,校验锁持有者无变动,写入缓存,解锁缓存当DB数据更新时,通过dtm确保数据更新胜利时,将缓存提早删除(将在前面一节开展具体解说) 提早删除会将数据过期工夫设定为10s,将锁设置为已过期,触发下一次查问缓存时的“取数据”在上述的策略下:如果最初写入数据库的版本为Vi,最初写入到缓存的版本为V,写入V的uuid为uuidv,那么肯定存在以下事件序列: 数据库写入Vi -> 缓存数据被标记为删除 -> 某个查问锁定数据并写入uuidv -> 查询数据库后果V -> 缓存中的锁定者为uuidv,写入后果V 在这个序列中,V的读取产生在写入Vi之后,所以V等于Vi,保障了缓存的数据的最终一致性。 dtm-labs/rockscache曾经实现了上述办法,可能确保缓存数据的最终一致性。 Fetch函数实现了后面的查问缓存DelayDelete函数实现了提早删除逻辑感兴趣的同学,能够参考dtm-cases/cache,外面有具体的例子 DB与缓存操作的原子性对于缓存的治理,个别业界会采纳写完数据库后,删除/更新缓存数据的策略。因为保留到缓存和保留到数据库两个操作之间不是原子的,肯定会有时间差,因而这两个数据之间会有一个不统一的工夫窗口,通常这个窗口不大,影响较小。然而两个两头可能产生宕机,也可能产生各种网络谬误,因而就有可能产生实现了其中一个,然而未实现另一个,导致数据会呈现长时间不统一。 举一个场景来阐明上述不统一的状况,数据用户将数据 A 批改为 B ,利用批改完数据库之后,再去删除/更新缓存,如果未产生异样,那么数据库和缓存的数据是统一的,没有问题。然而分布式系统中,可能会产生过程crash、宕机等事件,因而如果更新完数据库,尚未删除/更新缓存时,呈现过程crash,那么数据库和缓存的数据就可能呈现长时间的不统一。 面对这里的长时间不统一的状况,想要彻底解决,并不是一件容易的事,咱们上面分各种利用状况来介绍解决方案。 计划一:较短的缓存工夫这个计划,是最简略的计划,适宜并发量不大利用。如果利用的并发不高,那么整个缓存零碎,只须要设置了一个较短的缓存工夫,例如一分钟。这种状况下数据库须要承当的负载是:大概每一分钟,须要将拜访到的缓存数据全副生成一遍,在并发量不大的状况下,这种策略是可行的。 上述这种策略非常简单,易于了解和实现,缓存零碎提供的语义是,大多数状况下,缓存和数据库之间不统一的工夫窗口是很短的,在较低概率产生过程crash的状况下,不统一的工夫窗口会达到一分钟。 利用在上述束缚下,须要将一致性要求不高的数据读取,从缓存读取;而将一致性要求较高的读,不走缓存,间接从数据库查问。 计划二:音讯队列保障统一如果利用的并发量很高,缓存过期工夫须要比一分钟更长,而且利用中的大量申请不可能容忍较长时间的不统一,那么这个时候,能够通过应用音讯队列的形式,来更新缓存。具体的做法是: 更新数据库时,同时将更新缓存的音讯写入本地表,随着数据库更新操作的提交而提交。写一个轮询工作,一直轮询这部分音讯,发给音讯队列。生产音讯队列中的音讯,更新/删除缓存这种做法能够保障数据库更新之后,缓存肯定会被更新。但这种这种架构计划很重,这几个局部开发保护老本都不低:音讯队列的保护;高效轮询工作的开发与保护。 计划三:订阅 binlog这个计划实用场景与计划二十分相似,原理又与数据库的主从同步相似,数据库的主从同步是通过订阅binlog,将主库的更新利用到从库上,而这个计划则是通过订阅binlog,将数据库的更新利用到缓存上。具体做法是: 部署并配置阿里开源的 canal ,让它订阅数据库的binlog通过 canal等工具 监听数据更新,同步更新/删除缓存这种计划也能够保障数据库更新之后,缓存肯定会被更新,然而这种架构计划跟后面的音讯队列计划一样,也十分重。一方面 canal 的学习保护老本不低,另一方面,开发者可能只须要大量数据更新缓存,通过订阅所有的 binlog 来做这个事件,节约了很多资源。 计划四: dtm 二阶段音讯计划dtm 里的二阶段音讯模式,非常适合这里的批改数据库之后更新/删除缓存,次要代码如下: msg := dtmcli.NewMsg(DtmServer, gid). Add(busi.Busi+"/UpdateRedis", &Req{Key: key1})err := msg.DoAndSubmitDB(busi.Busi+"/QueryPrepared", db, func(tx *sql.Tx) error { // update db data with key1})这段代码,DoAndSubmitDB会进行本地数据库操作,进行数据库的数据批改,批改实现后,会提交一个二阶段音讯事务,音讯事务将会异步调用 UpdateRedis。如果本地事务执行之后,就立即产生了过程 crash 事件,那么 dtm 会进行回查调用 QueryPrepared ,保障本地事务提交胜利的状况下,UpdateRedis 会被起码胜利执行一次。 ...

May 9, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习十五redis-sds数据结构和底层设计原理

redis 是 C 语言写的,那么咱们思考一下 redis 是如何示意一个字符串的?redis 的数据结构和 C 语言的数据结构是一样的吗? 咱们能够看到 redis 源码中的 sds 库函数,和 sds 的具体实现,别离有如下 2 个文件: sds.hsds.c具体门路是:deps/hiredis/sds.h , deps/hiredis/sds.c sds.h 中波及如下数据结构: SDSredis 中 SDS simple Dynamic string 简略动静字符串 C 语言中示意字符串的形式是字符数组,例如: char data[]="xiaomotong"如果 C 语言须要扩容的话须要重新分配一个再大一点的内存,寄存新的字符串,若每次都要重新分配字符串,对于效率和性能必然会大大降低,并且若某一个字符串是 “xiaomo\0tong” 这个时候,实际上 C 中 遇到 ‘\0’ 就完结了,因而理论 “xiaomo\0tong” 只会读取到xiaomo ,字符串长度就是 6 因而 redis 中的 sds 数据结构是这样设计的,是通过一个成员来标记字符串的长度: SDS: free:0 len:6 char buf[]="xiaomo" 若这个时候,咱们须要在字符串前面追加字符串, sds 就会进行扩容,例如在前面加上 “tong” , 那么 sds 的数据结构中的值会变成如下: free:10 len:10 char buf[]="xiaomotong" 最初的 "xiaomotong" 也是带有 \0 的,这也放弃了 C 语言的规范,redis 中对于 sds 数据结构扩容是成倍增加的,然而到了肯定的级别,例如 1M 的时候,就不会翻倍的扩容,而是做加法 例如 1M 变成 2M , 2M 变成 3M 等等 ...

May 6, 2022 · 3 min · jiezi

关于redis:2022年Redis最新面试题第9篇-Redis运维和部署

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第9篇 - Redis运维和部署。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署运维和部署Redis 如何设置明码及验证明码?Redis 如何做内存优化?Redis 如何设置明码及验证明码?呈现概率: ★★★ redis没有实现访问控制这个性能,然而它提供了一个轻量级的认证形式,能够编辑redis.conf配置来启用认证。 1、初始化Redis明码: 在配置文件中有个参数: requirepass 这个就是配置redis拜访明码的参数; 比方 requirepass test123;(Ps:需重启Redis能力失效) redis的查问速度是十分快的,内部用户一秒内能够尝试多达150K个明码;所以明码要尽量长(对于DBA 没有必要必须记住明码); 2、不重启Redis设置明码: 在配置文件中配置requirepass的明码(当redis重启时明码仍然无效)。 redis 127.0.0.1:6379> config set requirepass test123查问明码: redis 127.0.0.1:6379> config get requirepass(error) ERR operation not permitted明码验证: redis 127.0.0.1:6379> auth test123OK再次查问: redis 127.0.0.1:6379> config get requirepass1) "requirepass"2) "test123"PS:如果配置文件中没增加明码 那么redis重启后,明码生效; 3、在Redis集群中应用认证明码 如果Redis服务器,应用了集群。除了在master中配置明码外,也须要在slave中进行相应配置。在slave的配置文件中找到如下行,去掉正文并批改与master雷同的明码即可: # masterauth master-passwordRedis 如何做内存优化?呈现概率: ★★★ 在思考如何做内存优化之前, 咱们先看看redisObject对象的构造 type 代表数据类型,比方 list, set, hash, 因为一共是 4 bits, 所以 redis 最多能反对 16 种encoding 代表底层编码类型,比方 ziplist, intset, EMBSTR, raw 等等refcount 援用计数,对象创立时为 1, 当值为 0 时开释ptr 指向实在数据,尽管是个指针,但有时为了高效应用内存,间接存储整型对象有如下个性: ...

May 6, 2022 · 1 min · jiezi

关于redis:Redis知识点面试题总结

热衷学习,热衷生存! 积淀、分享、成长,让本人和别人都能有所播种! 一、简略的介绍一下Redis简略的说Redis就是一个应用C语言开发的一个数据库,不过与传统数据库不同的是Redis的数据是存在内存中的,它是内存数据库,所以读写速度十分快,所以Redis被广泛应用于缓存方向。 另外,Redis除了做缓存之外,也还常常用于做分布式锁,甚至是音讯队列。 Redis提供了多种数据类型来反对不同的业务场景。Redis还反对事务、长久化、Lua脚本、多种集群计划。 二、Redis长久化机制Redis是一个反对长久化的内存数据库,通过长久化机制把内存中的数据同步到硬盘文件来保证数据长久化。当Redis重启后通过把硬盘文件从新加载到内存,就能达到复原数据的目标。 实现:独自创立一个fork()子过程,将以后父线程的数据库文件复制到子过程的内存中,而后由子过程写入到临时文件中,长久化的过程就完结了,再用这个临时文件替换上次的快照文件,而后子过程退出,内存开释。 RDB(Redis DataBase)是Redis默认的长久化形式。依照肯定的工夫周期策略把内存的数据以快照的模式保留到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。 AOF(Append Only Field)形式:Redis会将每一个写命令都通过write函数追加到文件最初,相似MySQL的binlog,当重启Redis会加载appendonly.aof文件复原数据。 三、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级问题缓存雪崩缓存雪崩能够了解为:因为原有缓存生效,新缓存未到期间,而造成一些列连锁反应,而造成整个零碎解体。 举个栗子: 咱们设置缓存采纳了雷同的过期工夫,在同一时刻呈现大面积的缓存过期,而后所有的申请都去拜访数据库了,对数据库CPU和存在造成了微小压力,重大的时候造成数据库宕机。 解决办法:大多数零碎设计者思考用加锁或者队列的形式保障不会有大量的线程对数据库一次性进行读写,从而防止生效时大量的并发申请落到底层存储系统上。还有一个简略计划就时讲缓存生效工夫扩散开。 缓存穿透缓存穿透是指用户查询数据库,在数据库没有,天然在缓存中也不会有,这样就导致用户在查问的时候,在缓存中找不到,每次都要去数据库再查问一遍,而后返回空,相当于两次无用的查问,这样申请就间接绕过缓存间接查询数据库,这也是常常说的缓存命中问题。 解决办法:最罕用的就是采纳布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,不存的数据会被这个bitmap拦挡掉,从而防止了对底层存储系统的查问压力。 另外一个更为简略粗犷的办法,如果一个查问返回的数据为空(不论是数据不存在,还是系统故障),咱们依然把这个空后果进行缓存,但它的过期工夫会很短,不会超过五分钟,通过这个间接设置的默认值寄存到缓存,这样子二次到缓存中获取就有值了。 缓存预热缓存预热就是零碎上线后,将相干的缓存数据间接加载到缓存零碎。这样子就能够防止在用户申请的时候,先查询数据库,而后再将数据缓存的问题!用户间接查问当时被预热的缓存数据! 解决思路: 间接写个缓存页面,上线时手工操作下。数据量不大,能够在我的项目启动时主动进行加载。定时刷新缓存。缓存更新除了缓存服务器自带的缓存生效策略之外(Redis默认的有6中策略可供选择),咱们还能够依据具体的业务需要进行自定义的缓存淘汰,常见的策略有两种:(1)定时去清理过期的缓存;(2)当有用户申请过去时,再判断这个申请所用到的缓存是否过期,过期的话就去底层零碎失去新数据并更新缓存。两者各有优劣,第一种的毛病是保护大量缓存的key是比拟麻烦的,第二种的毛病就是每次用户申请过来都要判断缓存生效,逻辑绝对比较复杂!具体用哪种计划,能够依据本人的利用场景来衡量。 缓存降级当访问量剧增、服务呈现问题或非核心服务影响到外围流程的性能时依然须要保障服务还是可用的,即便是有损服务。零碎能够依据一些要害数据进行主动降级,也能够配置开关实现人工降级。 降级的最终目标是保障外围服务可用,即便是有损的。而且有些服务是无奈降级的(如退出购物车、结算)。以参考日志级别设置预案:(1)个别:比方有些服务偶然因为网络抖动或者服务正在上线而超时,能够主动降级;(2)正告:有些服务在一段时间内成功率有稳定(如在95~100%之间),能够主动降级或人工降级,并发送告警;(3)谬误:比方可用率低于90%,或者数据库连接池被打爆了,或者访问量忽然猛增到零碎能接受的最大阀值,此时能够依据状况主动降级或者人工降级;(4)严重错误:比方因为非凡起因数据谬误了,此时须要紧急人工降级。 服务降级的目标是为了避免Redis服务故障,导致数据库跟着一起产生雪崩问题。因而,对于不重要的缓存数据,能够采取服务降级策略,例如一个比拟常见的做法就是,Redis呈现问题,不去数据库查询,而是间接返回默认值给用户。 四、单线程的Redis为什么这么快纯内存操作。单线程操作,防止了频繁的上下文切换。采纳非阻塞I/O多路复用机制。五、Redis的数据类型,以及每种数据类型的应用场景stringstring数据结构是简略的key-value类型,value能够是字符串也能够是数字。 个别用于做一下简单的计数性能,比方用户的拜访次数、点赞性能等等。 listlist即是链表,实现了一个双向链表,能够反对反向查找、遍历,更不便操作,然而带来了额定的内存开销。 罕用于公布和订阅或者音讯队列、慢查问。 hashhash相似JDK1.8前的HashMap,内存实现也差不过是数组+链表。特地适宜用于贮存对象,后续操作的时候,你能够间接仅仅批改这个对象中的某个字段的值。 罕用于零碎中对象数据的存储。 setset相似于Java中的HashSet,是一个无序且不反复的汇合。能够基于 set 轻易实现交加、并集、差集的操作。 罕用于须要寄存的数据不能反复以及须要获取多个数据源交加和并集等场景。 sorted set和set相比,减少了一个权重参数score,是的汇合中的元素可能依照score进行有序排列,还能够通过 score 的范畴来获取元素的列表。 罕用于须要对数据依据某个权重进行排序的场景。比方:各种排行榜、TOP N。 bitmapbitmap存储的是间断的二进制数字(0和1),通过bitmap,只需一个bit位来示意某个元素对应的值或者状态,key就是对应元素自身。 罕用于须要保留状态信息(比方是否签到、是否登陆)并须要进一步对这些信息进行剖析的场景。比方用户签到状况、沉闷用户状况、用户行为统计。 六、Redis的过期策略以及内存淘汰机制redis采纳的是定期删除+惰性删除策略。 为什么不必定时删除策略? 定时删除用一个定时器来负责监督key,过期则主动删除。尽管内存及时开释,然而非常耗费CPU资源。 在大量并发申请下,CPU要将工夫用在解决申请,而不是用来删除key。 定期删除+惰性删除是怎么工作的呢? 定期删除,redis默认每隔100ms查看是否有过期的key,有的话就删除。须要阐明是redis不是每隔100ms将所有的key查看一次,而是随机抽取进行查看,因而如果只采纳定期删除策略会导致很多过期的key没有删除,这个时候惰性删除就派上用场了,获取某个key的时候,redis会检查一下这个key是否过期了,如果过期了就删除。 采纳定期删除+惰性删除就没其余问题了吗? 不是的,如果定期删除没删除的key,也没有申请去获取,这个时候惰性删除就不会失效,redis的内存会越来越高,这个时候就要用到内存淘汰机制了。 在redis.conf中有一行配置: maxmemory-policy volatile-lru该配置就是配置内存淘汰策略,次要有以下策略: volatile-re:从已设置过期的数据集中筛选最近起码应用的数据淘汰。volatile-ttl:从已设置过期的数据集中筛选将要过期的数据淘汰。volatile-random:从已设置过期的数据集中随机抉择数据淘汰。allkeys-lru:从数据集中筛选最近起码应用的数据淘汰。allkeys-random:从数据集中随机筛选数据淘汰。no-enviction:禁止淘汰数据,新写入操作会报错。ps:如果没有设置expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上统一。 七、Redis为什么是单线程的官网FAQ示意,因为redis是基于内存操作,CPU不是redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络宽带。既然单线程容易实现,而且CPU不会成为瓶颈。那就牵强附会地采纳单线程的计划了(毕竟采纳多线程会有很多麻烦!) Redis利用队列技术将并发拜访变为串行拜访。 绝大部分申请是纯正的内存操作。采纳单线程能够防止不必要的上下文切换和竞争条件。Redis采纳了非阻塞I/O技术。八、为什么Redis的操作是原子性的,怎么保障原子性的?对Redis而言,命令的原子性指的是:一个操作的不能够再分,操作要么执行,要么不执行。 Redis的操作之所以是原子性的,是因为Redis是单线程的。 ...

May 6, 2022 · 1 min · jiezi

关于redis:2022年Redis最新面试题第8篇-Redis缓存问题

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第8篇 - Redis缓存问题。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署缓存问题分布式缓存Redis缓存雪崩Redis缓存击穿Redis缓存穿透缓存预热缓存降级Redis缓存雪崩呈现概率: ★★★★★ 这个在Redis面试的题目中算是出镜率特地高的问题了, 倡议认真消化一下。 1)、缓存雪崩是指<font color=#FF000 >大量的缓存key</font>无奈在Redis缓存中进行解决,紧接着,<font color=#FF000 >短时间大量申请间接打到数据库层</font>,导致数据库层的压力激增, 可能霎时就会导致数据库宕机。 缓存雪崩个别是由两个起因导致的,应答计划也有所不同,咱们一个个来看。 第一个起因是:缓存中有大量数据同时过期,比如说一些APP首页缓存数据,都设置了当天无效, 凌晨过期, 在第二天流量顶峰时, 大量用户拜访的申请, 没有换成从数据库中读取数据。如果利用的并发申请量很大,那么数据库的压力也忽然变得很大,可能会造成缓存雪崩。 这种相似的问题解决思路也根本简略, 打散过期工夫, 比方设置过期工夫为 24小时之后 + 随机N分钟。 另外一个起因是Redis宕机, 这个就须要剖析Redis宕机的起因了, 是因为磁盘满了还是内存满了, 依据状况进行解决,或者升配置什么的。如果是内存满了, 也须要思考是不是缓存过期回收策略的问题。 Redis缓存击穿呈现概率: ★★★★ 缓存击穿跟缓存雪崩有点相似,缓存雪崩是大规模的key生效,而<font color=#FF000 >缓存击穿是某个热点的key生效</font>,大并发集中对其进行申请,就会造成大量申请读缓存没读到数据,从而导致高并发拜访数据库,引起数据库压力剧增。这种景象就叫做缓存击穿。 解决方案: a)、在缓存生效后,通过互斥锁或者队列来管制读数据写缓存的线程数量,比方某个key只容许一个线程查问数据和写缓存,其余线程期待。这种形式会阻塞其余的线程,此时零碎的吞吐量会降落 b)、热点数据缓存永远不过期。 永不过期理论蕴含两层意思: 物理不过期,针对热点key不设置过期工夫 逻辑过期,把过期工夫存在key对应的value里,如果发现要过期了,通过一个后盾的异步线程进行缓存的构建 Redis缓存穿透呈现概率: ★★★★ 缓存穿透是指用户申请的数据在<font color=#FF000 >缓存中不存在即没有命中,同时在数据库中也不存在</font>,导致用户每次申请该数据都要去数据库中查问一遍。 如果短时间大量相似申请落在数据库上,造成数据库压力过大,可能导致数据库承受不住而宕机解体。 解决办法: a)、将有效的key寄存进Redis中: 当呈现Redis查不到数据,数据库也查不到数据的状况,咱们就把这个key保留到Redis中,设置value="null",并设置其过期工夫极短,前面再呈现查问这个key的申请的时候,间接返回null,就不须要再查询数据库了。但这种解决形式是有问题的,如果传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。 b)、应用布隆过滤器: 如果布隆过滤器断定某个 key 不存在布隆过滤器中,那么就肯定不存在,如果断定某个 key 存在,那么很大可能是存在(存在肯定的误判率)。于是咱们能够在缓存之前再加一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查问Redis前先去布隆过滤器查问 key 是否存在,如果不存在就间接返回,不让其拜访数据库,从而防止了对底层存储系统的查问压力。 ...

May 5, 2022 · 1 min · jiezi

关于redis:Redis内存机制

前言Redis的数据都存储在内存中,所以本篇文章将学习Redis的内存机制,以帮忙定位Redis的内存相干问题。 注释一. 查看Redis中的内存Redis提供了info memory指令来查看Redis的内存状况,然而在查看Redis中的内存之前,先通过一段代码来造成Redis的OOM,代码片段如下。 long count = 0L;while (true) { byte[] bytes = new byte[1024]; RBucket<byte[]> curBucket = redissonClient.getBucket(String.valueOf(count++)); curBucket.set(bytes);}因为当时曾经将Redis的最大内存设置为了10MB,所以上述代码片段运行一小会儿就会造成Redis产生OOM,Redisson抛出的OOM异样如下。 此时应用info memory指令查看Redis内存状况,如下所示。 下表是对上图的字段的阐明。 字段阐明used_memoryRedis应用的内存总量。蕴含对象内存,缓冲内存,虚拟内存和本身内存,不蕴含内存碎片。used_memory_rssRedis过程占用的内存总量。蕴含对象内存,缓冲内存,本身内存和内存碎片,不蕴含虚拟内存。used_memory_peakused_memory达到过的峰值。used_memory_luaLua引擎占用的内存。maxmemory用户配置的Redis的最大内存。maxmemory_policyRedis达到最大内存时的淘汰策略。mem_fragmentation_ratioused_memory_rss / used_memory。代表Redis内存的碎片化率,值越大,示意Redis的内存碎片越多。mem_allocatorRedis应用的内存分配器。默认为jemalloc。注:以_human结尾的字段是增加了单位的可读形式显示。 二. used_memory详解info memory指令的后果中的used_memory示意Redis理论应用的内存,通常由对象内存,缓冲内存和本身内存组成,能够由下图进行概括。 1. 对象内存已知Redis中存储数据时应用的键和值均为对象,Redis中的对象共五种(详见Redis-对象类型),当存储数据时,会依据存储场景将数据存储为不同的对象,此时Redis的内存分配器会为这些对象分配内存空间。 2. 缓冲内存Redis中的缓冲区次要有:客户端缓冲区,AOF缓冲区和复制积压缓冲区,这些缓冲区的内存耗费能够概括如下。 客户端缓冲区:又分为客户端输出缓冲区和客户端输入缓冲区。客户端输出缓冲区用于暂存客户端输出的指令,当一次性写入大量指令,或者服务端负载过高时,客户端输出缓冲区会继续增高,客户端输出缓冲区的最大容量为1GB。客户端输入缓冲区用于保留指令执行后的返回后果;AOF缓冲区:Redis进行AOF长久化时会先将Redis指令写入缓冲区中,而后再依据AOF的写磁盘策略在适合的工夫点将缓存内容刷到磁盘文件中;复制积压缓冲区:用于主从同步。对于主从同步,会在后续的文章中介绍。3. 本身内存次要指进行RDB或者AOF长久化时,Redis创立子过程的内存耗费。 三. mem_fragmentation_ratio详解mem_fragmentation_ratio示意Redis内存的碎片化率,所谓内存碎片,就是Redis占用着然而又没有用于存储数据的内存。mem_fragmentation_ratio = used_memory_rss / used_memory,对于mem_fragmentation_ratio的值,存在如下的含意。 mem_fragmentation_ratio大于1时,示意Redis存在内存碎片,mem_fragmentation_ratio越大,内存碎片越多;mem_fragmentation_ratio小于1时,示意Redis的局部内存替换到了磁盘(应用了虚拟内存),值越小,Redis替换到磁盘的内存就越多,此时Redis的速度就越慢。失常状况下,mem_fragmentation_ratio的值须要大于1并管制在1.2以内。 Redis产生内存碎片是因为Redis的默认内存分配器jemalloc的内存分配机制导致的,jemalloc分配内存时,会依照如下规定。 调配的内存空间满足2的幂次方;调配的内存空间满足大于等于须要调配的内存空间。所以如果须要调配100byte,那么jemalloc会调配128byte的内存空间,此时Redis过程占用的这128byte的内存理论只有100byte被应用,未应用的28byte就是内存碎片。同时如果一个128byte的内存空间中只有局部数据被删除,那么这128byte的内存是不会被回收的,此时也产生了内存碎片。 四. Redis的淘汰策略如果Redis应用的内存将超过maxmemory时,Redis会依据maxmemory_policy即淘汰策略来决定将哪些数据淘汰掉。Redis反对的淘汰策略如下表所示。 淘汰策略阐明noeviction默认策略。应用内存达到maxmemory时,如果增加新数据,不会删除任何旧数据,而是间接报错。volatile-random对设置了过期工夫(expire)的数据失效。随机删除一部分数据,直到有足够的内存空间调配给新数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。volatile-ttl对设置了过期工夫(expire)的数据失效。优先删除过期工夫最小的数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。volatile-lru对设置了过期工夫(expire)的数据失效。应用LRU算法删除数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。volatile-lfu对设置了过期工夫(expire)的数据失效。应用LFU算法删除数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。allkeys-random对所有数据失效。随机删除一部分数据,直到有足够的内存空间调配给新数据,如果没有数据可供删除且还未有足够空间调配给新数据,此时报错。allkeys-lru对所有数据失效。应用LRU算法删除数据,如果没有数据可供删除且还未有足够空间调配给新数据,此时报错。allkeys-lfu对所有数据失效。应用LFU算法删除数据,如果没有数据可供删除且还未有足够空间调配给新数据,此时报错。下面提到的LRU(Least Recently Used)算法其实就是将最近被拜访间隔以后最久的数据删除,Redis会记录每个数据最初一次被拜访的工夫,在须要删除数据开释空间时,会依据每个数据最初一次被拜访的工夫抉择出最“旧”的数据进行删除,所以LRU(Least Recently Used)算法有一个毛病,就是一个很少被拜访然而最近被拜访过的数据不会优先被删除,所以Redis4.0引入了LFU(Least Frequently Used)算法,即须要删除数据开释空间时,依据数据的拜访频次筛选出起码被拜访的数据进行删除,如果两条数据的拜访频次雷同,此时再依据数据最初一次被拜访工夫来决定删除哪条数据。 在第一节的例子中,能够看到Redis的淘汰策略是noeviction,所以在应用内存达到maxmemory后,Redis报了OOM的谬误。 五. Redis的过期策略上一节提到在Redis中能够应用expire指令为存储的数据设置过期工夫,那么某条数据过期后,Redis会依据过期策略来删除这些过期数据,Redis中的过期策略如下所示。 过期策略阐明定时删除每个设置了过期工夫的数据都有一个定时器,一旦数据过期,该数据会立刻被删除。长处:过期数据能够及时被删除;毛病:过期数据多时定时器会占用较多CPU资源。惰性删除应用数据的时候才去判断该数据是否过期,如果过期就删除该数据。长处:过期数据被拜访时才会被删除,删除过期数据不会占用过多CPU资源;毛病:有些曾经过期然而没有被拜访的数据会长期得不到删除。定期删除每隔一段时间扫描过期数据并删除。长处:通过管制扫描的间隔时间和执行工夫,能够缩小删除过期数据的操作对CPU资源的占用;毛病:扫描的间隔时间和执行工夫难以确定一个正当值。在Redis中是采纳惰性删除加定期删除来解决过期数据的,同时Redis在进行定期删除时,是将设置了过期工夫的key寄存在字典中,默认状况下是每秒对字典进行10次扫描,每次扫描应用贪婪策略遍历局部key来判断对应的数据是否过期,贪婪策略如下。 步骤1:从字典中随机抉择20个key;步骤2:删除这20个key中过期key对应的数据;步骤3:如果20个key中过期key占比超过25%,则反复步骤1-3。总结Redis将数据寄存在内存中,所以理解Redis的内存机制对于应用Redis很有帮忙。info memory指令可能列出Redis以后的内存情况和内存策略,能够通过maxmemory配置项来配置Redis的最大内存,也能够通过maxmemory-policy配置项来配置当内存达到最大值时的数据淘汰策略。Redis提供了共8种淘汰策略,能够仅针对设置了过期工夫的数据失效,也能够针对所有数据失效,如果不配置淘汰策略,那么Redis采纳的默认淘汰策略为不淘汰,此时内存达到最大值时Redis会报OOM谬误。

May 5, 2022 · 1 min · jiezi

关于redis:Redis-内存优化在-vivo-的探索与实践

作者:vivo 互联网服务器团队- Tang Wenjian一、 背景应用过 Redis 的同学应该都晓得,它基于键值对(key-value)的内存数据库,所有数据寄存在内存中,内存在 Redis 中表演一个外围角色,所有的操作都是围绕它进行。 咱们在理论保护过程中常常会被问到如下问题,比方数据怎么存储在 Redis 外面能节约老本、晋升性能?Redis内存告警是什么起因导致? 本文次要是通过剖析 Redis内存构造、介绍内存优化伎俩,同时联合生产案例,帮忙大家在优化内存应用,疾速定位 Redis 相干内存异样问题。 二、 Redis 内存治理本章具体介绍 Redis 是怎么治理各内存构造的,而后次要介绍几个占用内存可能比拟多的内存构造。首先咱们看下Redis 的内存模型。 内存模型如图: 【used_memory】:Redis内存占用中最次要的局部,Redis分配器调配的内存总量(单位是KB)(在编译时指定编译器,默认是jemalloc),次要蕴含本身内存(字典、元数据)、对象内存、缓存,lua内存。 【本身内存】:本身保护的一些数据字典及元数据,个别占用内存很低。 【对象内存】:所有对象都是Key-Value型,Key对象都是字符串,Value对象则包含5品种(String,List,Hash,Set,Zset),5.0还反对stream类型。 【缓存】:客户端缓冲区(一般 + 主从复制 + pubsub)以及aof缓冲区。 【Lua内存】:次要是存储加载的 Lua 脚本,内存使用量和加载的 Lua 脚本数量无关。 【used\_memory\_rss】:Redis 主过程占据操作系统的内存(单位是KB),是从操作系统角度失去的值,如top、ps等命令。 【内存碎片】:如果对数据的更改频繁,可能导致redis开释的空间在物理内存中并没有开释,但redis又无奈无效利用,这就造成了内存碎片。 【运行内存】:运行时耗费的内存,个别占用内存较低,在10M内。 【子过程内存】:次要是在长久化的时候,aof rewrite或者rdb产生的子过程耗费的内存,个别也是比拟小。 2.1 对象内存对象内存存储 Redis 所有的key-value型数据类型,key对象都是 string 类型,value对象次要有五种数据类型String、List、Hash、Set、Zset,不同类型的对象通过对应的编码各种封装,对外定义为RedisObject构造体,RedisObject都是由字典(Dict)保留的,而字典底层是通过哈希表来实现的。通过哈希表中的节点保留字典中的键值对,构造如下: (起源:书籍《Redis设计与实现》) 为了达到极大的进步 Redis 的灵活性和效率,Redis 依据不同的应用场景来对一个对象设置不同的编码,从而优化某一场景下的效率。  各类对象抉择编码的规定如下: string (字符串) 【int】:(整数且数字长度小于20,间接记录在ptr*外面)【embstr】: (间断调配的内存(字符串长度小于等于44字节的字符串))【raw】: 动静字符串(大于44个字节的字符串,同时字符长度小于 512M(512M是字符串的大小限度))list (列表) 【ziplist】:(元素个数小于hash-max-ziplist-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64个字节))【linkedlist】:(当列表类型无奈满足ziplist的条件时,Redis会应用linkedlist作为列表的外部实现)【quicklist】:(Redis 3.2 版本引入了 quicklist 作为 list 的底层实现,不再应用 linkedlist 和 ziplist 实现)set (汇合) ...

May 5, 2022 · 2 min · jiezi

关于redis:Redis三种常用的缓存读写策略

热衷学习,热衷生存! 积淀、分享、成长,让本人和别人都能有所播种! 一、Redis三种罕用的缓存读写策略Redis有三种读写策略别离是:旁路缓存模式策略、读写穿透策略、异步缓存写入策略。 这三种缓存读写策略各有劣势,不存在最佳,须要咱们依据理论的业务场景抉择最合适的。 二、旁路缓存模式(Cache Aside Pattern)旁路缓存模式是咱们平时应用比拟多的一个缓存读写模式,比拟适宜读申请比拟多的场景。 旁路缓存模式中服务端须要同时保护DB和Cache,并且是以DB的后果为准。 读写步骤写:先更新DB。而后间接删除cache。如下图: 读:从cache中读取数据,读取到就间接返回。cache中读取不到的话,就从DB读取返回。再把数据写到cache中。如下图: 自我思考思考这样子的一个问题:“如果在写数据的过程中,能够先删除cache,再更新DB吗?” 答案:答案必定是不行的,因为这样子可能造成数据库和缓存数据不统一的问题,比方这个时候有一个数据在DB和缓存都为100,申请1须要将这个数据更新写成200,如果先删除换出再更新数据库的话,在申请1曾经删除缓存然而数据库还没写完的时候,有一个申请2读取数据,首先去缓存读取,发现缓存被删除了,而后去数据库读取失去100(这个时候申请1还没写完)再写入缓存,这个时候申请1写完了,这个时候数据库里数据为200,缓存里为100,不统一。 能够简略形容为: 申请1先把cache中的数据删除 -> 申请2从DB中读取数据 -> 申请1再把DB中的数据更新紧接着思考:“在写数据的过程中,如果先写BD,再删除cache就不会造成数据不统一了吗?” 答案:实践上来说还是会呈现数据不统一的问题,不过概率很小,因为缓存的写入速度是比数据库写入速度快很多。 比方申请1先读数据A,申请2随后写数据A,并且数据A不在缓存中存在的话就会去数据库读取,读取完申请2再更新完并删除缓存,而后申请1把数据A写入缓存,这个时候数据库和缓存就不统一了。 这个过程能够简略的形容为: 申请1从DB读取数据A -> 申请2写更新数据A到数据库再删除cache中的A数据 -> 申请1将数据A写入缓存毛病首次申请的数据肯定不在cache的问题 解决办法:能够将热点数据提前写入cache中。 写操作比拟频繁的话导致cache中的数据会被频繁的删除,这样会影响缓存命中率。 解决办法: 数据库和缓存强始终场景:更新DB的时候同样更新cache,不过须要加一个锁/分布式锁来保障更新cache的时候不存在线程平安问题。能够短暂的容许数据库和缓存数据不统一的场景:更新DB的时候同样更新cache,然而给缓存加一个比拟短的过期工夫,这样的话就能够保障即便数据不统一的话影响也比拟小。三、读写穿透(Read/Write Through Pattern)读写穿透中服务端把cache视为次要数据存储,从中读取数据并将数据写入其中。cache服务负责将此数据读取和写入DB,从而加重应用程序的职责。 读写步骤写:先查cache,cache中不存在,间接更新DB。cache中存在,则先更新cache,而后cache服务本人更新DB(同时更新DB和cache)。如下图: 读:先从cache中读取数据,读取到间接返回。从cache中读取不到,则先从DB加载写入到cache后返回响应。如下图: 读写穿透理论是在旁路缓存之上进行了封装。在旁路缓存下,产生读申请的时候,如果cache中不存在对应的数据,是由客户端本人负责把数据写入cache,而读写穿透则是cache服务本人来写入缓存,这对客户端是通明的。 和旁路缓存一样,读写穿透也存在首次申请数据肯定不在cache中的问题,对于热点数据能够提前写入缓存中。 四、异步缓存写入(Write Behind Pattern)异步缓存写入和读写穿透很类似,两者都是由cache服务来负责cache和DB的读写。 两者最大的不同点就是:读写穿透是同步更新DB和cache,而异步缓存写入则是只更新cache,不间接更新DB,而是改为异步批量的形式更新DB。 很显著,这种形式对数据一致性带来了更大的挑战,比方cache数据可能还没异步更新DB,cache服务可能就挂了。 这种策略在咱们平时开发过程中也十分少见,然而不代表它的利用场景少,比方音讯队列中音讯的异步写入磁盘、MySQL的InnoDB Buffer Pool机制都用到了这种策略。 异步缓存写入的写性能十分高,非常适合写数据常常变动又对数据一致性要求没那么高的场景下应用,比方浏览量、点赞量等。 参考:https://javaguide.cn/database...

May 3, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习十四sortedset-初步探究梳理

sorted_set 是什么?sorted_set 就是 zset ,是 redis 外面的数据之一,有序汇合 有序汇合是汇合的一部分,有序汇合给每个元素多设置了一个分数,相当于多了一个维度,redis 也是利用这个维度进行排序的 理论利用redis-cli 连贯上 redis-server ,应用 help @sorted_set 查看有序联合反对的命令 # redis-cli -p 6379127.0.0.1:6379> pingPONG127.0.0.1:6379>127.0.0.1:6379> help @sorted_set BZPOPMAX key [key ...] timeout summary: Remove and return the member with the highest score from one or more sorted sets, or block until one is available since: 5.0.0.... summary对这个命令的概括 since这个命令从 redis 哪一个版本就开始提供了 举个例子在 sorted_set 中增加一个 key,这个key 外面有 3 个成员 ,3 个成员对应的分支如下: 成员分值pig9dog2cat6 127.0.0.1:6379> zadd k1 9 pig 2 dog 6 cat(integer) 3获取有序汇合的所有值,默认是依照无效到大的形式来展现,因为数据存入到 redis 内存中,物理内存的后果是从左到右,一一递增的 ...

May 3, 2022 · 2 min · jiezi

关于redis:Golang对Redis的使用

Redis 罕用的数据类型: 字符串 哈希 链表 汇合 有序汇合 bitmap hyperlog geo 应用Lua 脚本实现多个命令原子性操作 分布式锁 package redisimport ( "fmt" "github.com/go-redis/redis" "time")type clientRedis struct { *redis.Client}/* 获取一个*redis.Client */func NewRds(addr, password string, DB, poolSize int) (client *clientRedis, err error) { client = &clientRedis{redis.NewClient(&redis.Options{ Addr: addr, Password: password, DB: DB, PoolSize: poolSize, })} pong, err := client.Ping().Result() if err != nil { fmt.Println(err) return } fmt.Println(pong) return}/* string set操作 对照命令 set key value */func (cr *clientRedis) StringSet(expire time.Duration, key string, value string) (err error) { err = cr.Client.Append(key, value).Err() if err != nil { // todo error info return } cr.Expire(key, expire) return}/* string get操作 对照命令 get key */func (cr *clientRedis) StringGet(key string) (res string, err error) { res, err = cr.Get(key).Result() if err != nil { //todo error info return } return}/* list lpush操作 对照命令 lpush key value*/func (cr *clientRedis) ListLPush(expire time.Duration, key string, value ...string) (err error) { err = cr.LPush(key, value).Err() if err != nil { // todo error info return } cr.Expire(key, expire) return}/* list rpush操作 对照命令 rpush key value*/func (cr *clientRedis) ListRPush(expire time.Duration, key string, value ...string) (err error) { err = cr.RPush(key, value).Err() if err != nil { // todo error info return } cr.Expire(key, expire) return}/* list lpop操作 对照命令 lpop key*/func (cr *clientRedis) ListLPop(key string) (res string, err error) { res, err = cr.LPop(key).Result() if err != nil { //todo error info return } return}/* list rpop操作 对照命令 rpop key*/func (cr *clientRedis) ListRPop(key string) (res string, err error) { res, err = cr.RPop(key).Result() if err != nil { //todo error info return } return}/* set add操作 对照命令 sadd key value */func (cr *clientRedis) SetAdd(expire time.Duration, key string, value ...string) (err error) { err = cr.SAdd(key, value).Err() if err != nil { // todo error info return } cr.Expire(key, expire) return}/* set members操作 对照命令 smembers key */func (cr *clientRedis) SetMembers(key string) (res []string, err error) { res, err = cr.SMembers(key).Result() if err != nil { //todo error info return } return}

May 1, 2022 · 2 min · jiezi

关于redis:2022年Redis最新面试题第7篇-Redis分布式锁

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第7篇 - Redis分布式锁。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署你晓得实现实现分布式锁有哪些计划?呈现概率: ★★★★★ 分布式锁无论是在平时开发中还是面试中都是很常见的问题, 所以倡议本人多梳理一下。知其然, 且知其所以然。 1、在开始之前首先要晓得什么是分布式锁 ? 分布式锁其实就是管制分布式系统不同过程独特访问共享资源的一种锁的实现。如果不同的零碎或同一个零碎的不同主机之间共享了某个临界资源,往往须要互斥来避免彼此烦扰,以保障一致性。 在分布式锁方面, Redis有广泛应用, 日常开发中分布式锁的一些常见常见有秒杀下单、抢红包等等。 图片2、分布式锁的特点如下: 互斥性:和咱们本地锁一样互斥性是最根本,然而分布式锁须要保障在不同节点的不同线程的互斥。 可重入性:同一个节点上的同一个线程如果获取了锁之后那么也能够再次获取这个锁。 锁超时:和本地锁一样反对锁超时,避免死锁。 高效,高可用:加锁和解锁须要高效,同时也须要保障高可用避免分布式锁生效,能够减少降级。 反对阻塞和非阻塞:和 ReentrantLock 一样反对 lock 和 trylock 以及 tryLock(long timeOut)。 反对偏心锁和非偏心锁(可选):偏心锁的意思是依照申请加锁的程序取得锁,非偏心锁就相同是无序的。这个一般来说实现的比拟少。 3、一些比拟常见的分布式锁计划 SETNX + EXPIRE SETNX + value值是(零碎工夫+过期工夫) SET EX PX NX + 校验惟一随机值,再开释锁 多机实现的分布式锁Redlock ZooKeeper实现分布式锁 应用数据库实现分布式锁 1)、计划一: SETNX和EXPIRE 伪代码如下: if (setnx(key, 1) == 1){ expire(key, 10)try { //TODO 业务逻辑} finally { del(key)}}这种计划的长处是长处是实现简略,通过批改过期工夫能够反对锁重入,锁超时主动开释; 毛病:因为上述命令是分两步执行,如果第二步执行失败,将造成无奈解锁, 很容易导致死锁。 2)、计划二: SETNX + value值是(零碎工夫+过期工夫) ...

May 1, 2022 · 2 min · jiezi

关于redis:2022年Redis最新面试题第6篇-Redis淘汰策略

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第6篇 - Redis淘汰策略。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署概要Redis过期键的删除策略?你能够简略聊聊Redis内存淘汰机制(回收策略)Redis 过期键的删除策略?呈现概率: ★★★★ Redis过期键的删除策略是:定期删除+惰性删除。 1)、对于定期删除, Redis默认会每隔100ms就随机选取一些曾经过期了的key,查看其是否过期,如果曾经过期就删除。 不过假如Redis里放了100w个key,而且都设置了过期工夫,你每隔几百毫秒,就查看100w个key,那 Redis基本上就卡死了,cpu负载也会很高的,根本都耗费在查看过期key上了。 留神,这里可不是每隔100ms就遍历所有的设置过期工夫的key,那样就是一场性能上的劫难。理论状况是每次随机选取一些key进行检查和删除的。 但因为随机会带来不确定性,可能会导致很多过期key到了工夫并没有被删除掉,那应该怎么办呢?所以就须要惰性删除了。 对于定期删除源码解析:每当 Redis 服务器的周期性操作 redis.c/serverCron 函数执行时,redis.c/activeExpireCycle 会被调用。activeExpireCycle 函数在规定的工夫内,分屡次遍历服务器中的各个数据库,从数据库的 expires 字典中随机查看一部分键的过期工夫,并删除其中的过期键。 current\_db 记录以后查看的数据库,如果函数 activeExpireCycle 以后正在解决 2 号数据库,工夫超限,返回后,下次查看时,会从 3 号数据库开始查看。所有数据库查看一遍后,current\_db 重置为 0,而后再次开始一轮的查看工作。 2)、对于惰性删除, 当用户获取某个key的时候,Redis会检查一下这个key是不是设置了过期工夫, 并且是否过期了?如果过期了此时就会删除,不会给用户返回任何货色。 惰性删除有一个问题, 依赖用户的被动调用,那如果一些用户就长时间没有拜访怎么办, 会导致大量过期 key沉积在内存里,进而导致Redis 内存块耗尽了,咋整? 答案就是:触发Redis内存淘汰机制。(上面会讲到) 你能够简略聊聊Redis内存淘汰机制(回收策略)呈现概率: ★★★★ 1)、Redis内存淘汰机制有以下几个: noeviction: 当内存不足以包容新写入数据时,新写入操作会报错。(这个有点过于暴力, 不举荐)allkeys-lru:当内存不足以包容新写入数据时,在键空间中,移除最近起码应用的 key(这个是最罕用的)。allkeys-random:当内存不足以包容新写入数据时,在键空间中,随机移除某个 key。volatile-lru:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,移除最近起码应用的 key(这个个别不太适合)。volatile-random:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,随机移除某个 key。volatile-ttl:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,有更早过期工夫的 key 优先移除。2)、Redis默认的过期策略是noeviction, 最暴力那个, 如果内存满了那就是一场“富丽”的故事了。 127.0.0.1:6379> config get maxmemory-policy1) "maxmemory-policy"2) "noeviction"127.0.0.1:6379> 3)、redis.conf 中的过期淘汰配置如下: ...

April 30, 2022 · 2 min · jiezi

关于redis:2022年Redis最新面试题第5篇-Redis集群

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第5篇 - Redis集群。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署概要Redis 是单过程单线程的?是否应用过 Redis 集群,集群的原理是什么?能够简略说说你对Redis Sentinel的了解Redis Sentinal和Redis Cluster的区别Redis 的同步机制理解么?Redis 集群最大节点个数是多少?Redis 是单过程单线程的?呈现概率: ★★★★ 大家所熟知的 Redis 的确是单线程模型,指的是执行 Redis 命令的外围模块是单线程的,而不是整个 Redis 实例就一个线程,Redis 其余模块还有各自模块的线程的。 上面这个解释比拟好: 同时Redis 采纳网络 I/O 多路复用技术,来保障在多连贯的时候零碎的高吞吐量。对于 I/O 多路复用(又被称为“事件驱动”),首先要了解的是,操作系统为你提供了一个性能,当你的某个 socket 可读或者可写的时候,它能够给你一个告诉。这样当配合非阻塞的 socket 应用时,只有当零碎告诉我哪个描述符可读了,我才去执行 read 操作,能够保障每次 read 都能读到无效数据而不做纯返回 -1 和 EAGAIN 的无用功,写操作相似。 目前多路复用次要有三种技术:select,poll,epoll。epoll 是最新的也是目前最好的多路复用技术。 采纳多路 I/O 复用技术能够让单个线程高效的解决多个连贯申请(尽量减少网络 I/O 的工夫耗费),且 Redis 在内存中操作数据的速度十分快,也就是说内存内的操作不会成为影响 Redis 性能的瓶颈,基于这两点 Redis 具备很高的吞吐量。 是否应用过 Redis 集群,集群的原理是什么?呈现概率: ★★★ Redis Cluster 是 在 3.0 版本正式推出的高可用集群计划,相比Redis Sentinel,Redis Cluster计划不须要额定部署Sentinel集群,而是通过集群外部通信实现集群监控,故障时主从切换;同时,反对外部基于哈希实现数据分片,反对动静程度扩容。 ...

April 29, 2022 · 1 min · jiezi

关于redis:Reids-源码导读

本 README 只是一个疾速入门文档。你能够在redis.io找到更具体的文档。 什么是 Redis?Redis 通常被称为数据结构服务器。这意味着 Redis 通过一组命令提供对可变数据结构的拜访,这些命令应用带有 TCP 套接字和简略协定的服务器-客户端模型发送。因而不同的过程能够以共享的形式查问和批改雷同的数据结构。Redis 中实现的数据结构有一些非凡的属性: 尽管 Redis 通常被当做内存数据库,读写操作都是在内存中进行的,然而 Redis 也提供了将数据同步到磁盘的性能。这意味着 Redis 速度很快,但它也是非易失的。Redis 数据结构的实现强调内存效率(工夫&空间),因而与应用高级编程语言实现的雷同数据结构相比,Redis 外部的数据结构可能会应用更少的内存。Redis 提供了许多数据库软件常见个性,例如复制、可调整级别的持久性、集群和高可用性。另一个很好的例子是将 Redis 视为更简单的 memcached 版本,其中的操作不仅是 SET 和 GET,而且还实用于简单数据类型(如 List、Set、有序数据结构等)的操作。 如果您想理解更多信息,能够参阅上面链接: Redis 数据类型简介。 https://redis.io/topics/data-...间接在浏览器中试用 Redis。 https://try.redis.ioRedis 命令的残缺列表。 https://redis.io/commandsRedis 官网文档中还有更多内容。 https://redis.io/documentation 构建 RedisRedis 能够在 Linux、OSX、OpenBSD、NetBSD、FreeBSD 上编译和应用。咱们反对大端和小端架构,以及 32 位和 64 位零碎。Redis 能够在 Solaris 派生零碎(例如 SmartOS)上编译,但并不保障 Redis 在这些零碎上和在 Linux、OSX 、 *BSD 上工作得一样好。 它很简略:% make要应用 TLS 反对进行构建,您须要 OpenSSL 开发库(例如 Debian/Ubuntu 上的 libssl-dev)并运行: % make BUILD_TLS=yes要应用 systemd 反对构建,您须要 systemd 开发库(例如 Debian/Ubuntu 上的 libsystemd-dev 或 CentOS 上的 systemd-devel)并运行: ...

April 29, 2022 · 4 min · jiezi

关于redis:得物技术浅谈深入浅出的Redis分布式锁

一、什么是分布式锁1.1 分布式锁介绍分布式锁是管制不同零碎之间访问共享资源的一种锁实现,如果不同的零碎或同一个零碎的不同主机之间共享了某个资源时,往往须要互斥来避免彼此烦扰来保障一致性。 1.2 为什么须要分布式锁在单机部署的零碎中,应用线程锁来解决高并发的问题,多线程访问共享变量的问题达到数据一致性,如应用synchornized、ReentrantLock等。然而在后端集群部署的零碎中,程序在不同的JVM虚拟机中运行,且因为synchronized或ReentrantLock都只能保障同一个JVM过程中保障无效,所以这时就须要应用分布式锁了。这里就不再赘述synchornized锁的原理。 1.3 分布式锁须要具备的条件分布式锁须要具备互斥性、不会死锁和容错等。互斥性,在于不论任何时候,应该只能有一个线程持有一把锁;不会死锁在于即便是持有锁的客户端意外宕机或产生过程被kill等状况时也能开释锁,不至于导致整个服务死锁。容错性指的是只有大多数节点失常工作,客户端应该都能获取和开释锁。 二、分布式锁的实现形式目前支流的分布式锁的实现形式,基于数据库实现分布式锁、基于Redis实现分布式锁、基于ZooKeeper实现分布式锁,本篇文章次要介绍了Redis实现的分布式锁。 2.1 由单机部署到集群部署锁的演变一开始在redis设置一个默认值key:ticket 对应的值为20,并搭建一个Spring Boot服务,用来模仿多窗口卖票景象,配置类的代码就不一一列出了。 2.1.1 单机模式解决并发问题一开始的时候在redis预设置的门票值ticket=20,那么当一个申请进来之后,会判断是否余票是否是大于0,若大于0那么就将余票减一,再从新写入Redis中,假使库存小于0,那么就会打印谬误日志。 @RestController@Slf4jpublic class RedisLockController { @Resource private Redisson redisson; @Resource private StringRedisTemplate stringRedisTemplate; @RequestMapping("/lock") public String deductTicket() throws InterruptedException { String lockKey = "ticket"; int ticketCount = Integer.parseInt(stringRedisTemplate.opsForValue().get(lockKey)); if (ticketCount > 0) { int realTicketCount = ticketCount - 1; log.info("扣减胜利,残余票数:" + realTicketCount + ""); stringRedisTemplate.opsForValue().set(lockKey, realTicketCount + ""); } else { log.error("扣减失败,余票有余"); } return "end"; } }代码运行剖析: 这里显著有一个问题,就是以后若有两个线程同时申请进来,那么两个线程同时申请这段代码时,如图thread 1 和thread 2同时,两个线程从Redis拿到的数据都是20,那么执行实现后thread 1 和thread 2又将减完后的库存ticket=19从新写入Redis,那么数据就会产生问题,实际上两个线程各减去了一张票数,然而理论写进就减了一次票数,就呈现了数据不统一的景象。 ...

April 27, 2022 · 7 min · jiezi

关于redis:Redis对象类型

前言Redis是一种key-value型的数据库,key和value都是应用对象示意。执行SET message HelloWorld时,key是一个蕴含了字符串message的对象,value是一个蕴含了字符串HelloWorld的对象。本篇文章将对Redis中的对象类型进行学习。 注释一. Redis中的五种对象简介Redis中的对象叫做redisObject,其构造如下所示。 typedef struct redisObject { //对象类型 unsigned type:4; //对象底层应用的编码 unsigned encoding:4; //对象最初一次被应用的工夫 unsigned lru:22; //援用计数 int refcount; //指向对象底层数据结构的指针 void *ptr;} robj;redisObject中的type字段示意以后对象的类型,type字段的取值共五种,表明Redis中共有五种对象,如下表所示。 type阐明REDIS_STRING字符串REDIS_LIST列表REDIS_HASH哈希REDIS_SET汇合REDIS_ZSET有序汇合每种类型的Redis对象在不同状况下会由不同的底层数据结构实现。Redis对象底层数据结构共八种,如下表所示。 encoding阐明REDIS_ENCODING_INTlong类型的整数REDIS_ENCODING_EMBSTRembstr编码的简略动静字符串REDIS_ENCODING_RAWraw编码的简略动静字符串REDIS_ENCODING_HT字典REDIS_ENCODING_LINKEDLIST双端链表REDIS_ENCODING_ZIPLIST压缩列表REDIS_ENCODING_INTSET整数汇合REDIS_ENCODING_SKIPLIST跳表type和encoding的对应关系能够由下表示意。 二. 字符串对象字符串对象与底层数据结构对应关系如下。 如果一个字符串内容能够转换为long类型,则该字符串对象的底层数据结构为long类型的整数;如果字符串不能转换为long类型,并且字符串长度小于等于44字节,则该字符串对象的底层数据结构为embstr编码的简略动静字符串;如果字符串长度大于等于45字节,则该字符串对象的底层数据结构为raw编码的简略动静字符串。embstr编码在分配内存时只须要调配一次,在开释内存时也只须要开释一次,与之作为比照的raw须要两次。同时embstr编码的字符串应用间断内存,能够躲避伪共享的产生从而能够更好的利用缓存劣势。 三. 列表对象列表对象与底层数据结构对应关系如下。 列表对象存储的元素长度小于64字节,元素个数小于512个,则该列表对象底层数据结构应用压缩列表;其它状况,该列表对象底层数据结构应用双端链表。上面对压缩列表进行剖析。压缩列表是一种内存紧凑型的数据结构,因为应用间断的内存空间,能够躲避伪共享的产生从而能够更好的利用缓存劣势,压缩列表在元素长度不大或者元素个数不多时,会被列表对象采纳为底层数据结构。压缩列表的示意图能够示意如下。 struct ziplist<T> { //整个压缩列表占用字节数 int32 zlbytes; //最初一个entry间隔压缩列表起始地位的字节数 int32 zltail_offset; //entry个数 int16 zllength; //entry数组 T[] entries; //压缩列表的完结标记,0xFF int8 zlend;}借助压缩列表的zlbytes,zltail_offset和zllength字段,定位到压缩列表的第一个entry和最初一个entry的工夫复杂度是O(1),然而查找entry的工夫复杂度是O(N),所以压缩列表的元素个数不宜太多。entry数据结构如下所示。 struct entry { //前一个entry的字节 int<var> prevlen; //entry的内容的类型和长度 int<var> encoding; //entry的内容 optional byte[] content;}entry中的prevlen字段和encoding字段都是可变长度,它们依照如下规定变动。 如果前一个entry的长度小于254字节,则prevlen字段长度为1字节;如果前一个entry的长度大于等于254字节,则prevlen字段长度为5字节;如果以后entry的内容是整数,则encoding字段长度为1字节;如果以后entry的内容是字符串,则依据字符串的大小的不同,encoding字段长度为1字节,2字节或5字节。插入数据时,依据插入数据的类型和大小的不同,prevlen字段和encoding字段会动态变化,这就是Redis应用压缩列表来节约空间的思维。然而相应的会产生如下的问题。 往压缩列表插入,更新和删除数据时,可能会导致某些entry的prevlen字段发生变化,极其状况下这个变动会向后逐级传递,造成压缩列表的级联更新;压缩列表级联更新时,会屡次从新分配内存,使得压缩列表的性能好转。因而鉴于压缩列表的个性,Redis中的列表对象在存储元素小于64字节,元素个数小于512个时才会应用压缩列表作为底层数据结构,为的就是防止级联更新的产生。 四. 哈希对象哈希对象与底层数据结构对应关系如下。 ...

April 27, 2022 · 1 min · jiezi

关于redis:2022年Redis最新面试题第4篇-Redis数据持久化

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第4篇 - Redis数据长久化。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署概要为什么 Redis 须要把所有数据放到内存中?Redis如何做长久化的?Redis key 的过期工夫和永恒无效别离怎么设置?为什么 Redis 须要把所有数据放到内存中?呈现概率: ★★★ <font color=#FF000 >Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的形式将数据写入磁盘</font>。所以Redis具备疾速和数据长久化的个性。如果不将数据放到内存中,磁盘的I/O速度会重大影响redis的性能。在内存越来越便宜的明天,redis将会越来越受欢迎。不过也能够设置了最大应用的内存, 则数据已有记录数达到内存限值后将不能持续插入新值。 Redis如何做长久化的?呈现概率: ★★★★★ <font color=#FF000 >bgsave做镜像全量长久化,AOF做增量长久化</font>。因为bgsave会消耗较长时间,不够实时,在停机的时候会导致大量失落数据,所以须要AOF来配合应用。在redis实例重启时,优先应用AOF来复原内存的状态,如果没有AOF日志,就会应用RDB文件来复原。 如果再问AOF文件过大复原工夫过长怎么办?你通知面试官,Redis会定期做AOF重写,压缩AOF文件日志大小。如果面试官不够称心,再拿出杀手锏答案,Redis4.0之后有了混合长久化的性能,将bgsave的全量和AOF的增量做了交融解决,这样既保证了复原的效率又兼顾了数据的安全性。这个性能甚至很多面试官都不晓得,他们必定会对你另眼相看。 如果对方诘问那如果忽然机器掉电会怎么?取决于AOF日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会失落数据。然而在高性能的要求下每次都sync是不事实的,个别都应用定时sync,比方1s1次,这个时候最多就会失落1s的数据。 1)、RDB工作原理 既然说RDB是Redis中数据集的工夫点快照,在Redis内实现RDB长久化的办法有<font color=#FF000 >rdbSave</font>和<font color=#FF000 >rdbSaveBackground</font>两个函数办法(源码文件rdb.c中),先简略说下两者差异: rdbSave:是同步执行的,办法调用后就会立即启动长久化流程。因为Redis是单线程模型,长久化过程中会阻塞,Redis无奈对外提供服务;rdbSaveBackground:是后盾(异步)执行的,该办法会fork出子过程,真正的长久化过程是在子过程中执行的(调用rdbSave),主过程会持续提供服务;RDB长久化的触发必然离不开以上两个办法,触发的形式分为手动和主动。手动触发容易了解,是指咱们通过Redis客户端人为的对Redis服务端发动长久化备份指令,而后Redis服务端开始执行长久化流程,这里的指令有save和bgsave。 整个长久化的过程中,主过程不进行任何 io 操作,全程都有子过程来实现,这就确保了极高的性能。如果须要进行大规模的数据恢复,且对数据恢复的完整性不是十分敏感,那么 rdb 形式要比 AOF 形式更加的高效,rdb 的毛病是最初一次长久化的数据可能会失落。 2)、AOF 工作原理 <font color=#FF000 >AOF 长久化全称 append only file</font>,以日志模式记录每个写操作,将 redis 执行过得所有写操作指令记录下来(读操作不记录)。只许追加文件但不能够改写文件,redis 启动之初会读取该文件从新构建数据,换言之,redis 重启的话就依据日志文件的内容将写操作指令从前到后执行一次以实现数据的复原工作。 AOF 默认保留的是 appendonly.AOF 文件,此文件具备可读性。 AOF 的工作原理其实相似于 mysql 的 binlog 日志语句复制。是以日志的模式记录服务器所解决的每一个写,删除操作,查问操作不会记录,以文本的形式进行记录,该文件具备可读性。 数据同步有三种同步策略:批改同步、每秒同步、不被动调用 fsync 同步。 ...

April 27, 2022 · 1 min · jiezi

关于redis:故障分析-bgsave-导致-redis-定期卡顿案例一则

作者:任坤 现居珠海,先后负责专职 Oracle 和 MySQL DBA,当初次要负责 MySQL、mongoDB 和 Redis 保护工作。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 1、背景线上有套 redis 主从,版本4.0,开发埋怨说常常会呈现周期性卡顿。 利用日志显示每隔10多分钟呈现一次,一个一般调用就须要执行1s左右,而后主动复原,get/set 均受影响 2、诊断查看 redis qps 和 cpu 监控,均未发现有用线索。 登录 redis 查看 slowlog ,也没有吻合工夫点的慢查问。 evicted_keys 指标始终是 0 ,expired_keys 数量尽管很多,然而始终没有显著稳定,不太可能是驱赶过期键导致。 经组里共事揭示留神到 latest_fork_usec 指标,执行一次靠近1s左右,大概每15分钟触发一次 bgsave ,和利用呈现慢查问的频率大抵吻合,当初初步认定是 redis 实例定期 bgsave 导 致的利用卡顿。 在相当长的一段时间内,我始终认为 redis 的 bgsave 衍生出1个子过程并且采纳 copy-on- write 机制,不会对 redis 自身有太多影响,顶多落盘时占用点 IO 资源而已。 潜在瓶颈点呈现fork()调用上 Under Linux, fork() is implemented using copy‐on‐write pages, so the only penalty that it incurs is the time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child. 如果父过程的页表比拟大,fork()耗时就会相应缩短,且 redis 采纳了单工作过程模型, fork()执行期间会阻塞所有用户申请。 ...

April 26, 2022 · 1 min · jiezi

关于redis:2022年Redis最新面试题第3篇-Redis事务

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第3篇 - Redis事务。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署事务怎么了解 Redis 事务?Redis事务执行过程Redis事务的一些应用场景Redis事务与Redis pipeline的区别集群模式下Redis事务如何保障原子性怎么了解 Redis 事务?呈现概率: ★★★★ <font color=#FF000 >Redis事务的实质是一组命令的汇合</font>。事务反对一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会依照程序串行的执行队列中的命令,其余客户端提交的命令申请不会插入到事务执行命令序列中。 总结说:Redis事务就是一次性、程序性、排他性的执行一个队列中的一系列命令。 1)、Redis事务相干命令和应用 MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务相干的命令。 MULTI: 开启事务,redis会将后续的命令一一放入队列中,而后应用EXEC命令来原子化执行这个命令系列。EXEC: 执行事务中的所有操作命令。DISCARD: 勾销事务,放弃执行事务块中的所有命令。WATCH: 监督一个或多个key,如果事务在执行前,这个key(或多个key)被其余命令批改,则事务被中断,不会执行事务中的任何命令。UNWATCH: 勾销WATCH对所有key的监督。example: 127.0.0.1:6379> MULTIOK127.0.0.1:6379> SET name '散步coding'QUEUED127.0.0.1:6379> SET brief '一个专一于算法、数据库、职场的公众号'QUEUED127.0.0.1:6379> GET nameQUEUED127.0.0.1:6379> EXEC1) OK2) OK3) "散步coding"画重点, <font color=#FF000 >Redis事务不反对Rollback</font> 事实上Redis命令在事务执行时可能会失败,但仍会继续执行残余命令而不是Rollback(事务回滚)。如果你应用过关系数据库,这种状况可能会让你感到很奇怪。然而针对这种状况Redis官网也给出了解释: Redis命令可能会执行失败,仅仅是因为谬误的语法被调用(命令排队时检测不进去的谬误),或者应用谬误的数据类型操作某个Key: 这意味着,实际上失败的命令都是编程谬误造成的,都是开发中可能被检测进去的,生产环境中不应该存在。(这番话,彻底甩锅,“都是你们本人编程谬误,与咱们无关”。) 因为不用反对Rollback,Redis外部简洁并且更加高效。 Redis事务执行过程呈现概率: ★★★ 一个Redis事务从开始到执行会经验以下三个阶段: 1)开始事务。2)命令放入Queue。3)执行事务。1)开始事务 MULTI命令的执行标记着事务的开始: 127.0.0.1:6379> MULTIOK这个命令惟一做的就是, 将客户端的 REDIS_MULTI 选项关上, 让客户端从非事务状态切换到事务状态。 ...

April 24, 2022 · 1 min · jiezi

关于redis:2022年Redis最新面试题第2篇-Redis数据结构

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第2篇 - Redis数据结构。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署数据结构Redis的数据类型有哪些?说说 Redis 哈希槽的概念?Hash如何实现O(1)的查问和设置速度, 以及扩容原理布隆过滤器Redis的数据类型有哪些?呈现概率: ★★★★★ 这个在面试的过程呈现的概率特地高了。 <font color=#FF000 >Redis 反对五种罕用的数据类型:string( 字符串),hash( 哈希), list( 列表), set( 汇合) 及 zsetsorted set:有序汇合)</font>。 redis 的根本数据结构对应的底层实现如下图所示: 1)、Redis 的字符串是<font color=#FF000 >动静字符串</font>,是能够批改的字符串,内部结构实现上相似于 Java 的 ArrayList,采纳<font color=#FF000 >预调配冗余空间</font>的形式来缩小内存的频繁调配,如图所示: len 是以后字符串理论长度,capacity 是为字符串调配的可用空间,当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。字符串最大长度为 512M。 2)、hash Redis Hash通过<font color=#FF000 >分桶的形式</font>解决 hash 抵触。它是无序字典。外部实现构造是同样的<font color=#FF000 >数组 + 链表二维构造</font>。第一维 hash 的数组地位碰撞时,就会将碰撞的元素应用链表串接起来。第一维是数组,第二维是链表。数组中存储的是第二维链表的第一个元素的指针。 3)、list Redis 的列表相当于 Java 语言中的 LinkedList,留神<font color=#FF000 >它是链表而不是数组</font>。这意味着 list 的插入和删除操作十分快,工夫复杂度为 O(1),然而<font color=#FF000 >索引定位很慢</font>,工夫复杂度为 O(n)。 ...

April 23, 2022 · 1 min · jiezi

关于redis:2022年Redis最新面试题第1篇-Redis基础知识

大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第1篇 - Redis基础知识。 本文首发于公众号: 散步coding 2022年Redis最新面试题目录Redis基础知识Redis数据结构Redis事务Redis数据长久化Redis集群Redis淘汰策略Redis分布式锁Redis缓存问题运维和部署Redis基础知识什么是 Redis, 有哪些优缺点?Redis 最适宜的场景, 能够简略的说说吗?Redis 相比 Memcached 有哪些劣势?一个字符串类型的值能存储最大容量是多少?Redis 读写拆散什么是 Redis, 有哪些优缺点?呈现概率: ★★★★ Redis是一个非关系性数据库, 开源的、应用C语言编写、反对网络、可基于内存亦可长久化的日志型、key-value(键值对)数据库,是目前分布式架构中不可或缺的一环。 Redis服务器程序是<font color=#FF000 >单过程模型</font>,也就是在一台服务器上能够同时启动多个Redis过程,而Redis的理论处理速度则齐全依附于主过程的的执行效率。若在服务器上只运行一个Redis过程,当多个客户端同时拜访时,服务器的解决能力会有肯定水平的降落,若在同一台服务器上开启多个Redis过程,Redis在进步并发解决能力的同时会给服务器的CPU造成很大压力。也就是说,在理论生产环境中,须要依据理论的需要来决定开启多少个Redis过程。若对高并发要求更高一些,可能会思考在同一台服务器上开启多个过程。若CPU资源比拟缓和,采纳单过程即可。 <font color=#FF000 >Redis长处</font>: 1)、性能极高, 读写性能优异,从内存当中进行IO读写速度快。 2)、反对数据的长久化(反对<font color=#FF000 >AOF</font>和<font color=#FF000 >RDB</font>两种长久化形式),对数据的更新采纳<font color=#FF000 >Copy-on-write</font>技术(写拷贝),能够异步的保留在磁盘上 因为Redis的数据都寄存在内存中,如果没有配置长久化,redis重启后数据就全失落了,于是须要开启redis的长久化性能,将数据保留到磁 盘上,当redis重启后,能够从磁盘中复原数据。 redis提供两种形式进行长久化,一种是RDB长久化:指在指定的工夫距离内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子过程,先将数据集写入临时文件,写入胜利后,再替换之前的文件,用二进制压缩存储。 还有一种是AOF长久化:以日志的模式记录服务器所解决的每一个写、删除操作,查问操作不会记录,以文本的形式记录,能够关上文件看到具体的操作记录。 3)、反对主从复制,主机会主动将数据同步到从机,能够进行读写拆散。 4)、数据结构丰盛:除了反对string类型的value外还反对string、hash、set、sortedset、list等数据结构。 5)、原子性:多个操作通过MULTI和EXEC指令反对事务 <font color=#FF000 >Redis毛病</font>: 1)、主从同步,如果主机宕机,宕机前有一部分数据没有同步到从机,会导致数据不统一。 2)、主从同步,数据同步会有提早。 3)、读写拆散,主机写的负载量太大,也会导致主机的宕机 4)、数据库容量受到物理内存的限度,不能用作海量数据的高性能读写 Redis 最适宜的场景, 能够简略的说说吗?呈现概率: ★★★★ 1、会话缓存(Session Cache)最罕用的一种应用Redis的情景是会话缓存(session cache), Redis缓存会话比其余存储(如Memcached)的劣势在于:Redis提供长久化。 2、排行榜/计数器 Redis在内存中对数字进行递增或递加的操作实现的十分好。汇合(Set)和有序汇合(Sorted Set)也使得咱们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。 3、公布/订阅 Redis的公布/订阅性能。公布/订阅的应用场景的确十分多。我已看见人们在社交网络连接中应用,还可作为基于公布/订阅的脚本触发器,甚至用Redis的公布/订阅性能来建设聊天零碎! 4、缓存热数据 能够缓存一些高频读, 低频写的内容, 比方app首页一些设置等。 5、利用BitMap统计用户签到、统计沉闷用户、用户在线状态等 ...

April 22, 2022 · 1 min · jiezi

关于redis:Redis-的五种数据结构分析

Redis自身是一个Map,其中所有的数据都是采纳key:value的模式存储这里的数据类型次要是指存储的,也即是value的数据类型,key的数据类型永远都是Stringredis中value应用的数据结构有: String:字符串类型List:列表类型Hash:哈希表类型Set:无序汇合类型sorted set:有序汇合类型 上面咱们来一个一个别离来理解一下:一、String:字符串类型redis是应用C语言开发,但C中并没有String类型,只能应用指针或字符数组的模式示意一个字符串,所以redis设计了一种简略动静字符串(SDS[Simple Dynamic String])作为底层实现。这个SDS的内部结构更像是一个ArrayList,外部保护着一个字节数组,并且在其外部预调配了肯定的空间,以缩小内存的频繁调配。Redis的内存分配机制是这样:当字符串的长度小于 1MB时,每次扩容都是加倍现有的空间。如果字符串长度超过 1MB时,每次扩容时只会扩大 1MB 的空间。这样既保证了内存空间够用,还不至于造成内存的节约,字符串最大长度为 512MB。 上图就是字符串的根本构造,其中 content 外面保留的是字符串内容,0x\0作为完结字符不会被计算len中。SDS的数据结构: capacity 和 len两个属性都是泛型,为什么不间接用int类型?因为Redis外部有很多优化计划,为更正当的应用内存,不同长度的字符串采纳不同的数据类型示意,且在创立字符串的时候 len 会和 capacity 一样大,不产生冗余的空间,所以String值能够是字符串、数字(整数、浮点数) 或者二进制。redis中SDS和C语言字符串比照:1)C语言中的字符串,遇到'\0'则结尾,用长度N+1的数组保护长度为N的字符串。Redis的SDS是:len示意字符串的长度;free示意闲暇的,未调配的空间;buffer数组是真正的字符串,并且以'\0'结尾。2)C 字符串并不记录本身的长度信息,获取一个C字符串的长度,必须遍历整个字符串,对遇到的字符进行计数,直到遇到代表字符串结尾的空字符为止,复杂度为O(n)SDS 在len属性中记录了SDS的自身长度,复杂度为O(1)3)C字符串不记录本身长度容易造成缓冲区溢出SDS 的空间调配策略齐全杜绝了产生缓冲区的可能性:当SDS API 须要对SDS进行批改时,API会先查看SDS的空间是否满足批改所需的要求,如果不满足的话,API会主动将SDS的空间扩大至执行批改所需的大小,而后才执行理论的批改操作,所以应用SDS既不须要批改SDS的空间大小,也不会呈现后面所说的缓冲区溢出问题4)缩小批改字符串时带来的内存重调配次数C字符串的长度和底层数组的长度之间存在这种关联性,所以每次增长或者缩短一个C字符串,总要对保留C字符串的数组进行一次内存重调配操作在SDS中,buf数组的长度不肯定就是字符数量加一,数组外面能够蕴含为应用的字节,而这些字节的数量就由SDS的free属性记录。通过未应用空间,SDS实现了空间预调配和惰性空间开释两种优化策略5)二级制平安C字符串必须合乎某种编码,并且除了字符串的开端之外,字符串外面不能蕴含空字符SDS的API都是二进制平安的,所有SDS API都会以解决二进制的形式来解决SDS寄存在buf数组里的数据6)C兼容所有字符串函数SDS兼容局部C字符串函数 String类型的利用1、能够存储base64的图片数据2、作为缓存性能,升高mysql数据库的申请3、做一些短时间的谬误限度管制二、List:列表类型Redis中的list实质是链表构造list 的实现在3.2版本之前有两种形式:压缩列表ziplist双向链表linkedlist在3.2版本之后引入了:疾速列表quicklist因为双向链表linkedlist占用的内存比压缩列表ziplist要多, 所以当创立新的列表键时,列表会优先思考应用压缩列表ziplist, 并且在有须要的时候, 才从压缩列表ziplist实现转换到双向链表linkedlist实现。而后续引入的quicklist能够看作是linkedlist和ziplist的结合体。这三种列表外部应用哪一种类型是通过编码来辨别的: linkedlistlinkedlist是一个双向列表,每个节点都会存储指向上一个节点和指向下一个节点的指针。linkedlist因为每个节点的空间是不间断的,所以可能会造成过多的空间碎片。linkedlist的存储构造,链表中每一个节点都是一个listNode对象(源码adlist.h内),不过须要留神的是,列表中的value其实也是一个字符串对象。 而后会将其再进行封装成为一个list对象(源码adlist.h内): Redis中对linkedlist的拜访是以NULL值为起点的,因为head节点的prev节点为NULL,tail节点的next节点为NULL。所以,在Redis3.2之前咱们能够失去如下简图: ziplistziplist是为了节俭内存而开发的一种压缩列表数据结构,哈希数据类型底层也用到了ziplist。ziplist是由一系列非凡编码的间断内存块组成的程序型数据结构,一个ziplist能够蕴含任意多个entry,而每一个entry又能够保留一个字节数组或者一个整数值。ziplist和linkedlist最大的区别是ziplist不存储指向上一个节点和下一个节点的指针,存储的是上一个节点的长度和以后节点的长度,就义了局部读写性能来换取高效的内存利用率,是一种工夫换空间的思维。ziplist实用于字段个数少和字段值少的场景。ziplist的组成构造: 对于列表抉择应用ziplist编码进行存储,必须满足上面两个条件:1)列表对象保留的所有字符串元素的长度都小于64字节。2)列表对象保留的元素数量小于512个。一旦不满足这两个条件中的任意一个,则会应用linkedlist编码来进行存储这两个条件能够通过参数list-max-ziplist-value和list-max-ziplist-entries进行批改从新设定quicklist在Redis3.2之后,对立用quicklist来存储列表对象,quicklist存储了一个双向列表,每个列表的节点是一个ziplist,所以实际上quicklist就是linkedlist和ziplist的联合。quicklist外部存储构造,quicklist中每一个节点都是一个quicklistNode对象,其数据结构定义为: 而后各个quicklistNode就形成了一个列表,quicklist: 依据下面两个结构图,能够失去Redis3.2之后列表的简图: quicklist和原始两种列表的比照quicklist同样采纳了linkedlist的双端列表个性,而后quicklist中的每个节点又是一个ziplist,所以quicklist就是综合均衡思考了空间碎片和读写性能两个维度。应用quicklist须要留神以下2点:1、如果ziplist中的entry个数过少,极其状况就是只有1个entry,此时就相当于进化成了一个一般的linkedlist。2、如果ziplist中的entry过多,那么也会导致一次性须要申请的内存空间过大,而且因为ziplist自身的就是以工夫换空间,所以会过多entry也会影响到列表对象的读写性能。ziplist中的entry个数能够通过参数list-max-ziplist-size来管制:list-max-ziplist-size 1这个参数能够配置负数也能够配置正数。负数示意限度每个节点中的entry数量,如果是正数则只能为-1~-5-1:每个ziplist最多只能为4KB-2:每个ziplist最多只能为8KB-3:每个ziplist最多只能为16KB-4:每个ziplist最多只能为32KB-5:每个ziplist最多只能为64KBRedis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。其中 0 示意列表的第一个元素,1 示意列表的第二个元素,以此类推。也能够应用正数下标,以 -1 示意列表的最初一个元素, -2 示意列表的倒数第二个元素,以此类推。List数据类型利用:timeline:例如微博的时间轴,有人公布微博,用lpush退出时间轴,展现新的列表信息。三、Hash:哈希表类型Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特地适宜用于存储对象。Redis 中每个 hash 能够存储 2^32 - 1 键值对(40多亿)Redis的哈希对象的底层存储能够应用ziplist(压缩列表)和hashtable。当hash对象能够同时满足以下两个条件时,哈希对象应用ziplist编码1.哈希对象保留的所有键值对的键和值的字符串长度都小于64字节2.哈希对象保留的键值对数量小于512个常见操作命令:所有hash的命令都是h结尾的 hget、hset、hdel等Hash数据类型利用:1、缓存:能直观,相比string更节俭空间的保护缓存信息,如:用户信息,视频信息等。四、Set:无序汇合类型Redis 的 Set 是 String 类型的无序汇合。汇合成员是惟一的,这就意味着汇合中不能呈现反复的数据,将一个反复的元素增加到set中将会被疏忽。汇合对象的编码能够是 intset 或者 hashtable。Redis 中汇合是通过哈希表实现的,所以增加,删除,查找的复杂度都是 O(1)。汇合中最大的成员数为 2^32 - 1 (4294967295, 每个汇合可存储40多亿个成员)汇合类型也是用来保留多个字符串的元素,但和列表不同的是,汇合中: ...

April 21, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习十三Redis-常问简单面试题

咱们一起来看看 redis 常问罕用的面试题 Redis 是个啥?http://www.redis.cn/ redis 中文网给了很明确且清晰的定义 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统Redis 能够用作数据库、缓存和消息中间件反对的数据结构有 8 种 字符串(strings), 散列(hashes), 列表(lists), 汇合(sets), 有序汇合(sorted sets), bitmaps, hyperloglogs 和 geospatial 应用 redis 有什么好处?存取的数据都十分快,redis 的数据是间接在内存中操作的个性丰盛:可用于缓存,音讯队列,等等反对 8 种 数据类型Redis 为什么这么快嘞?首先 redis 是单线程的,很多人认为单线程就肯定慢,多线程就肯定快,这是一个误会 redis 应用多路 I/O 复用模型,非阻塞 的 IO采纳单线程,就不会有上下文切换的耗费,不必去思考加锁和解锁的事件,没有竞争资源呈现的性能耗费数据结构很简略,上述 8 种 数据类型,一学就会操作 redis 的数据,根本是在妥妥的操作内存,必须快Redis 能够长久化吗?能够的话,长久化的形式有哪些?能够长久化,有 2 种形式, RDB 和 AOF , redis 默认是反对 RDB 长久化 RDB 长久化形式在指定工夫距离内将内存中的数据集快照写入到磁盘中,这就是快照 snapshot ,复原快照的时候,是把快照文件读入到内存中。 redis 通过 fork 的形式创立一个子过程来专门做长久化的动作 劣势 适宜大规模的数据恢复对数据的完整性要求不高劣势 须要在肯定的工夫距离进行操作,如果 redis 意外宕机,最初一次写入的数据就会失落fork 子过程的时候须要占用肯定的空间AOF 长久化形式AOF 是 redis 的另外一种长久化形式,以日志的模式记录每一个写操作,将 redis 执行过的写操作全副记录下来,只容许追加文件,不容许改写文件 ...

April 20, 2022 · 2 min · jiezi

关于redis:在Redis中使用Pipelining提升查询速度

Redis是一个client-server模式的TCP服务,也被称为Request/Response协定的实现。 这意味着通常一个申请的实现是遵循上面两个步骤: Client发送一个操作命令给Server,从TCP的套接字Socket中读取Server的响应值,通常来说这是一种阻塞的形式Server执行操作命令,而后将响应值返回给Client举个例子: Client: INCR XServer: 1Client: INCR XServer: 2Client: INCR XServer: 3Client: INCR XServer: 4复制代码Clients和Servers是通过网络进行连贯。网络连接可能会很快(比方本机回环网络),也可能会很慢(比方两个主机之间存在多条网络)。不论网络怎么样,一个数据包从Client到Server,而后相应值又从Server返回Client都须要肯定的工夫。 这个工夫被称为RTT(Round Trip Time)。当一个Client须要执行多个间断申请(比方增加许多个元素到一个list中,或者清掉Redis中许多个键值对),那么RTT是怎么影响到性能,这个也是很不便去计算的。如果RTT的工夫为250ms(假如互联网连贯速度很常慢),即便Server能够每秒解决100k个申请,那么最多也只能承受每秒4个申请。 如果是本地回环网络,RTT将会特地的短(比方作者的localhost,RTT的响应工夫为40ms),然而对于执行间断屡次写操作时,也是一笔不小的耗费。 其实咱们有其余方法来升高这种场景的耗费。 Redis Pipelining在一个Request/Response形式的服务中有一个个性:即便Client没有收到之前的响应值,也能够持续发送新的申请。这种个性咱们能够不必期待Server的响应,率先发送许多操作命令给Server,再一次性读取Server的所有响应值。 这种形式被称为Pipelining技术,该技术近几十年来被宽泛的应用。比方多POP3协定的实现就反对这个个性,大大的晋升了从server端下载新的邮件的速度。 Redis在很早的时候就反对该项技术,所以不论你运行的是什么版本,你都能够应用pipelining技术,比方这里有一个应用 netcat 工具的: $ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379+PONG+PONG+PONG复制代码当初咱们不须要为每一次申请付出RTT的耗费了,而是一次性发送三个操作命令。为了便于直观的了解,还是拿之前的例子阐明,应用pipelining技术该例子的实现程序如下: Client: INCR XClient: INCR XClient: INCR XClient: INCR XServer: 1Server: 2Server: 3Server: 4复制代码当client应用pipelining发送操作命令时,server端将强制应用内存来排列响应后果。所以在应用pipelining发送大量的操作命令的时候,最好确定一个正当的命令条数,一批一批的发送给Server端,比方发送10000个操作命令,读取响应后果,再发送10000个操作命令,以此类推…尽管说耗时近乎雷同,然而额定的内存耗费将是这10000操作命令的排列响应后果所需的最大值。为避免内存耗尽,尽量抉择一个正当的值。 It’s not just a matter of RTTPipelining不是缩小因为 RTT 造成耗费的惟一形式,但它的确帮忙咱们极大的晋升每秒的执行命令数量。事实的假相是:从拜访相应的数据结构并且生成回答后果的状况来看,不应用pipelining的确代价很低;然而从套接字socket I/O的状况来看,恰恰相反。因为这波及到了read()和write()调用,须要从用户状态切换到内核状态。这种高低切换会特地损耗工夫。 一旦应用了pipelining技术,很多操作命令将会从同一个read()调用中执行读操作,大量的回答后果将会被散发到同一个write()调用中执行写操作。基于此,随着管道的长度减少,每秒执行的查问数量最开始简直呈直线型减少,直到不应用pipelining技术的基准的10倍,如下图所示: Some real world code example不翻译,基本上就是说应用了pipelining晋升了5倍性能。 Pipelining VS ScriptingRedis Scripting(2.6+版本可用),通过应用在Server端实现大量工作的脚本Scripting,能够更加高效的解决大量pipelining用例。应用脚本Scripting的最大益处就是在读和写的时候耗费更少的性能,使得像读、写、计算这样的操作更加疾速。(当client须要写操作之前获取读操作的响应后果时,pepelining就显得相形见拙。) 有时候,利用可能须要在应用pipelining时,发送 EVAL 或者 EVALSHA 命令,这是可行的,并且Redis明确反对这么这种SCRIPT LOAD命令。(它保障可能够调用 EVALSHA 而不会有失败的危险)。 ...

April 19, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习十二redis-缓存穿透缓存击穿缓存雪崩

尽管咱们在应用 redis 缓存的时候十分的爽,它大大的进步了咱们应用程序的性能和效率,尤其是数据查问方面,咱们不必间接去长久化的数据库中查问数据,而是到内存中查问数据即可 事物总是有两面的,用的爽的同时,也必须面对它带来的问题,就是数据一致性的问题,这个问题,是一个权衡利弊的问题,咱们接着看 redis 缓存和一些长久化的数据库配合应用的时候,会呈现一些高可用的问题,如: 缓存穿透缓存击穿缓存雪崩咱们可能解决上述问题,那就解决了一部分服务器高可用的问题 什么是缓存穿透咱们先学习一部分,对于底层原理和理论源码剖析,咱们之后再一起看 缓存穿透,就是用户想要查问一个数据,在 redis 中查问不到,即没有在缓存中命中,那么就会间接去长久化的 mysql 中进行查问,发现也没有这个数据,那么本次查问就失败了 当用户巨多的时候,查问缓存都没有查问到,那么这些全副都去查问长久化的 mysql 数据库,压力全部打到 mysql 下面,这就是缓存穿透 解决方案有个别有 2 种形式: 应用布隆过滤器缓存空的对象应用布隆过滤器布隆过滤器是一种数据结构,对所有可能查问到的参数都是以 hash 的形式存储,会先在管制层进行校验,不合乎的话,则抛弃,这就防止了对底层存储系统的压力 布隆过滤器部署在 redis 的后面,去拦挡数据,缩小对 redis 的冲击,进而减小对 长久化层的冲击 缓存空的对象缓存空对象,就是当咱们在长久化的数据库中没有查问到咱们冀望的数据时,那么就返回一个空对象,并且将这个空对象缓存起来,再对其设置一个过期工夫 那么之后再有拜访这个对象的申请时,缓存间接拜访空对象即可,这就能够爱护长久化数据层,缩小对他的冲击压力 通过上述缓存空对象的形式,貌似也能解决问题,然而应用长久上来,会发现 key 值对应的空对象越来越多,会呈现上面 2 个问题: 十分多的空对象被缓存起来,那么对应就很多的 key 占用 内存空间,占用资源,内存压力直线回升如果空对象的过期工夫到了,那么申请的压力还是会打到长久化数据库下面,这会影响数据的一致性业务什么是缓存击穿 呈现缓存击穿的状况是数据量太大,或者是缓存过期了 当某个 key 在过期的霎时,有大量的申请这个 key 的数据,这种数据是热点数据,因为在缓存过期的霎时,申请会同时拜访到长久化的数据库来查问数据,并且会将数据会写到缓存中,此时就会导致数据库霎时的压力过大,导致击穿 此处能够了解 击穿和穿透的区别: 击穿,是一个 key 十分热点,大量的拜访都打在这个 key 下面,在 key 生效的霎时,所有申请打在数据库上,就打出一个洞,击穿了 而穿透更多的是拜访的数据不存在的状况,大量的申请拜访的都是不存在的数据 缓存击穿的解决方案 将热点数据设置不过期,不设置过期工夫,就不会呈现热点 key 过期的霎时造成问题加上分布式锁,保障对于每一个 key ,同时只有一个服务进行拜访,其余的服务没有获取到锁,就不能拜访 redis 的这个 key,那么就须要期待获取锁 ...

April 17, 2022 · 1 min · jiezi

关于redis:Redis的所有操作都是原子性

1、redis 介绍Redis 是齐全开源的,是一个高性能的 key-value 数据库。redis 的劣势 性能高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。丰盛的数据类型 – Redis反对二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。原子 – Redis的所有操作都是原子性的,意思就是要么胜利执行要么失败齐全不执行。单个操作是原子性的。多个操作也反对事务,即原子性,通过MULTI和EXEC指令包起来。丰盛的个性 – Redis还反对 publish/subscribe, 告诉, key 过期等等个性。。 2、在我的项目中的利用在游戏服务器中的利用次要有上面几个场景:基于redis 的高性能,所以咱们当做跨服的数据存储应用基于redis的公布订阅性能,咱们当做服务器之间的音讯公布应用,统领所有的服务器基于redis 的排序功能,咱们当做跨服的排行榜应用。 3、代码展现在我的项目中退出上面的依赖 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId></dependency>如果你应用springboot搭建的我的项目,能够退出上面到pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>1、数据存储数据的存储比较简单,惯例的应用就行 @Componentpublic class RedisUtil { public static RedisUtil util; public RedisUtil(@Autowired JedisPool jedisPool) { this.jedisPool = jedisPool; RedisUtil.util = this; } public static RedisUtil getInstance(){ return util; } @Autowired private JedisPool jedisPool; /** * 向Redis中存值,永恒无效 */ public String set(String key, String value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); return jedis.set(key, value); } catch (Exception e) { return "0"; } finally { jedis.close(); } } /** * 依据传入Key获取指定Value */ public String get(String key) { Jedis jedis = null; String value; try { jedis = jedisPool.getResource(); value = jedis.get(key); } catch (Exception e) { return "0"; } finally { jedis.close(); } return value; } /** * 校验Key值是否存在 */ public Boolean exists(String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); return jedis.exists(key); } catch (Exception e) { return false; } finally { jedis.close(); } } /** * 删除指定Key-Value */ public Long del(String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); return jedis.del(key); } catch (Exception e) { return 0L; } finally { jedis.close(); } } /** * 分布式锁 * @param key * @param value * @param time 锁的超时工夫,单位:秒 * * @return 获取锁胜利返回"OK",失败返回null */ public String getDistributedLock(String key,String value,int time){ Jedis jedis = null; String ret = ""; try { jedis = jedisPool.getResource(); ret = jedis.set(key, value, new SetParams().nx().ex(time)); return ret; } catch (Exception e) { return null; } finally { jedis.close(); } } public void pub(){ jedisPool.getResource().publish("test","msg"); } }

April 16, 2022 · 2 min · jiezi

关于redis:Redis在项目中的应用总结

1、数据缓存一个详情页蕴含了比拟多的信息,这些信息大多是动态的,为了进步详情页的加载速度,将这些信息缓存到Redis中。 如何解决缓存和数据库的数据不统一问题? 我的项目中应用的是先更新数据库,后删除缓存,在非并发读写状况下,简直不存在问题,在并发读写状况,当线程W更新完数据库,删除缓存后,R申请此时读取缓存,R申请读取缓存不存在,此时查询数据库,主从同步还没有实现或者产生了主从同步提早,R申请从读库上获取了旧的数据,而后写入到缓存中,那么缓存和数据库就会产生不统一。 下面这种状况,能够通过提早再删除的办法解决,W申请删除缓存后,发送一条提早音讯,音讯的生产端依据理论状况再次删除缓存,生产端比拟缓存中的数据版本和数据库中数据的版本,如果不统一,那么再次删除缓存,如果统一,无需操作。 对于数据库和缓存一致性更加具体的剖析参考这篇文章 2、数据统计聚合统计所谓的聚合统计,就是指统计多个汇合元素的聚合后果,包含:统计多个汇合的共有元素(交加统计);把两个汇合相比,统计其中一个汇合独有的元素(差集统计);统计多个汇合的所有元素(并集统计)。 统计用户 App 每天的新增用户数用一个汇合记录所有登录过 App 的用户 ID,同时,用另一个汇合记录每一天登录过 App 的用户 ID。而后,再对这两个汇合做差集统计。 咱们能够应用 Set 类型,把 key 设置为 user:id,示意记录的是用户 ID,value 就是一个 Set 汇合,外面是所有登录过 App 的用户 ID,咱们能够把这个 Set 叫作累计用户 Set,如下图所示: 把每一天登录的用户 ID,记录到一个新汇合中,咱们把这个汇合叫作每日用户 Set,它有两个特点:1、key 是 user:id 以及当天日期,例如 user20200803;2、value 是 Set 汇合,记录当天登录的用户 ID。 在统计每天的新增用户时,咱们只用计算每日用户 Set 和累计用户 Set 的差集就行,伪代码如下: Set difference = getRedisTemplate().opsForSet().difference("user:id", "user:id:20200803");而后,咱们计算累计用户 Set 和 user20200803 Set 的并集后果,后果保留在 user:id 这个累计用户 Set 中,伪代码如下: Set union = getRedisTemplate().opsForSet().union("user:id","user:id:20200803");此时,user:id 这个累计用户 Set 中就有了 8 月 3 日的用户 ID。等到 8 月 4 日再统计时,咱们把 8 月 4 日登录的用户 ID 记录到 user20200804 的 Set 中。接下来,计算累计用户 Set 和 user20200804 Set 的差集就是8月4日的新增用户 ...

April 14, 2022 · 3 min · jiezi

关于redis:删库到跑路还得看这篇Redis数据库持久化与企业容灾备份恢复实战指南

本章目录0x00 数据长久化 1.RDB 形式2.AOF 形式如何抉择 RDB OR AOF?0x01 备份容灾一、备份 1.手动备份redis数据库2.迁徙Redis指定db-数据库3.Redis集群数据备份与迁徙二、复原 1.零碎Redis用户被删除后配置数据恢复流程2.Kubernetes中单实例异样数据迁徙复原实际3.当Redis集群中呈现从节点slave,fail,noaddr问题进行解决复原流程。前置常识学习补充Redis数据库根底入门介绍与装置 - https://blog.weiyigeek.top/20... Redis数据库根底数据类型介绍与应用 - https://blog.weiyigeek.top/20... Redis根底运维之原理介绍和主从配置 - https://blog.weiyigeek.top/20... Redis根底运维之哨兵和集群装置配置 - https://blog.weiyigeek.top/20... Redis根底运维之在K8S中的装置与配置 - https://blog.weiyigeek.top/20... Redis数据库性能测试及优化配置 - https://blog.weiyigeek.top/20... Redis数据库客户端操作实际及入坑出坑 - https://blog.weiyigeek.top/20... 0x00 数据长久化形容: Redis 是将数据存储在内存之中所以其读写效率十分高,然而往往事物都不是那么美妙,当因为某些不可抗力导致机器宕机、redis服务进行此时您在内存中的数据将齐全失落; 所以为了使 Redis 在异样重启后仍能保证数据不失落, 咱们就须要对其进行设置长久化存储,使其将内存的数据通过某种形式存入磁盘中,当Redis服务端重启后便会从该磁盘中进行读取数据进而复原Redis中的数据, 所以Redis数据长久化是容灾复原必备条件; Redis反对两种长久化形式: (1) RDB 长久化(默认反对): 该机制是指在指定的工夫距离内将内存中数据集写入到磁盘;(2) AOF 长久化: 该机制将以日志的模式记录服务器所解决的每一个写操作,同时在Redis服务器启动之初会读取该文件来从新构建数据库,以保障启动后数据库中的数据残缺;(3) 无长久化: 将 Redis 作为一个长期缓存,并将数据寄存到memcached之中;Tips: 为保证数据安全性,咱们能够设置 Redis 同时应用RDB和AOF长久化形式,来保障重启后Redis服务器中的数据残缺; 1.RDB 形式形容: Redis 将某一时刻的快照(备份的数据库数据)保留成一种称为RDB格局的文件中,这种格局是通过压缩的二进制文件,数据库的保留和复原文件如下图所示。 <br/> 保留RDB数据的两种形式: 1、save命令:save命令会阻塞redis服务器的过程,直到RDB文件创建完,在该期间redis不能解决任何的命令申请,这就是save命令最大的缺点。2、bgsave命令:与save命令不同的是 bgsave在生成RDB文件时,会派生出一个子过程,子过程负责创立RDB文件,在此期间,主过程和子过程是同时存在的,因而不会阻塞redis服务器过程。 (举荐形式)阐明:可用lastsave命令查看生成RDB文件是否胜利 <br/> 劣势 ...

April 14, 2022 · 14 min · jiezi

关于redis:如何保证Redis数据库性能与安全看这篇Redis性能测试及安全优化配置指南就够了

本章目录0x00 Redis 性能指标监控 (1) 性能指标 1.根本流动指标:Basic activity2.性能指标:Performance3.内存指标: Memory4.持久性指标: Persistence5.谬误指标:Error6.其余指标阐明 (2) 性能测试工具 1.redis-benchmark 命令2.redisbench 工具3.rdb 内存剖析工具 (3) 基准测试实际 3.1 K8s中单实例redis测试 0x01 Redis 平安优化 1.Security 非特权运行文件权限接口绑定更改默认服务端口认证配置禁用特定命令日志记录防备字符串本义和 NoSQL 注入防备由内部客户端精心筛选的输出触发的攻打防火墙限度拜访禁止redis中存储敏感的明文数据Redis 平安配置总结示例 2.Performance Optimization 要害优化项Redis 性能优化总结示例 前置常识学习补充1.Redis数据库根底入门介绍与装置 - https://blog.weiyigeek.top/20... 2.Redis数据库根底数据类型介绍与应用 - https://blog.weiyigeek.top/20... 3.Redis根底运维之原理介绍和主从配置 - https://blog.weiyigeek.top/20... 4.Redis根底运维之哨兵和集群装置配置 - https://blog.weiyigeek.top/20... 5.Redis根底运维之在K8S中的装置与配置 - https://blog.weiyigeek.top/20... 7.Redis数据库容灾备份企业实战 - https://blog.weiyigeek.top/20... 8.Redis数据库客户端操作实际及入坑出坑 - https://blog.weiyigeek.top/20... 0x00 Redis 性能指标监控(1) 性能指标Redis 服务端常见指标参数: redis-cli -a password info > redis-Performance.txt # 咱们能够将redis服务端info相干信息导出到文件之中 # 2.clients: # 3.memory: # 4.persistence: # 5.stats:通用统计数据 # 6.Replication: # 7.CPU:CPU应用状况 # 8.cluster: # 9.Keypass:键值对统计数量信息 10.20.172.108:6379> info # (1) Redis 服务端信息交互式查看# Server 服务器运行的环境参数redis_version:6.2.5redis_git_sha1:00000000redis_git_dirty:0redis_build_id:cb556a016f8668dredis_mode:standaloneos:Linux 5.11.0-25-generic x86_64arch_bits:64multiplexing_api:epollatomicvar_api:c11-builtingcc_version:9.3.0process_id:187640process_supervised:norun_id:97838216d4fe0de4739e7814b5a2e1d0d32d0982tcp_port:6379server_time_usec:1630241617439942uptime_in_seconds:10930uptime_in_days:0hz:10configured_hz:10lru_clock:2851665executable:/opt/databases/redis-6.2.5/src/./redis-serverconfig_file:/home/weiyigeek/redis/6379/redis-6379.confio_threads_active:0# Clients 客户端相干信息connected_clients:7cluster_connections:0maxclients:10000client_recent_max_input_buffer:32client_recent_max_output_buffer:0blocked_clients:0tracking_clients:0clients_in_timeout_table:0# Memory 服务器运行内存统计数据used_memory:2050432used_memory_human:1.96Mused_memory_rss:5140480used_memory_rss_human:4.90Mused_memory_peak:2253512used_memory_peak_human:2.15Mused_memory_peak_perc:90.99%used_memory_overhead:1982152used_memory_startup:810376used_memory_dataset:68280used_memory_dataset_perc:5.51%allocator_allocated:2204376allocator_active:2555904allocator_resident:5230592total_system_memory:12442619904total_system_memory_human:11.59Gused_memory_lua:37888used_memory_lua_human:37.00Kused_memory_scripts:0used_memory_scripts_human:0Bnumber_of_cached_scripts:0maxmemory:0maxmemory_human:0Bmaxmemory_policy:noevictionallocator_frag_ratio:1.16allocator_frag_bytes:351528allocator_rss_ratio:2.05allocator_rss_bytes:2674688rss_overhead_ratio:0.98rss_overhead_bytes:-90112mem_fragmentation_ratio:2.59mem_fragmentation_bytes:3153776mem_not_counted_for_evict:124mem_replication_backlog:1048576mem_clients_slaves:0mem_clients_normal:123000mem_aof_buffer:128mem_allocator:jemalloc-5.1.0active_defrag_running:0lazyfree_pending_objects:0lazyfreed_objects:0# Persistence 长久化数据相干信息loading:0current_cow_size:0current_cow_size_age:0current_fork_perc:0.00current_save_keys_processed:0current_save_keys_total:0rdb_changes_since_last_save:3rdb_bgsave_in_progress:0rdb_last_save_time:1630230687rdb_last_bgsave_status:okrdb_last_bgsave_time_sec:-1rdb_current_bgsave_time_sec:-1rdb_last_cow_size:0aof_enabled:1aof_rewrite_in_progress:0aof_rewrite_scheduled:0aof_last_rewrite_time_sec:0aof_current_rewrite_time_sec:-1aof_last_bgrewrite_status:okaof_last_write_status:okaof_last_cow_size:462848module_fork_in_progress:0module_fork_last_cow_size:0aof_current_size:150aof_base_size:92aof_pending_rewrite:0aof_buffer_length:0aof_rewrite_buffer_length:0aof_pending_bio_fsync:0aof_delayed_fsync:0# Stats 通用统计数据信息total_connections_received:25total_commands_processed:50482instantaneous_ops_per_sec:4total_net_input_bytes:2758703total_net_output_bytes:22330756instantaneous_input_kbps:0.23instantaneous_output_kbps:0.55rejected_connections:0sync_full:0sync_partial_ok:0sync_partial_err:0expired_keys:0expired_stale_perc:0.00expired_time_cap_reached_count:0expire_cycle_cpu_milliseconds:0evicted_keys:0keyspace_hits:1keyspace_misses:0pubsub_channels:1pubsub_patterns:0latest_fork_usec:310total_forks:1migrate_cached_sockets:0slave_expires_tracked_keys:0active_defrag_hits:0active_defrag_misses:0active_defrag_key_hits:0active_defrag_key_misses:0tracking_total_keys:0tracking_total_items:0tracking_total_prefixes:0unexpected_error_replies:0total_error_replies:8dump_payload_sanitizations:0total_reads_processed:48899total_writes_processed:97139io_threaded_reads_processed:0io_threaded_writes_processed:0# Replication 主从相干指标信息role:masterconnected_slaves:0master_failover_state:no-failovermaster_replid:32da05299a5a36de431b4c05122f7d2b93eca169master_replid2:3e15749ad586d60bd0d1c93854f6f719a22316cemaster_repl_offset:8915second_repl_offset:829repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:8915# CPU 处理器模式占用信息used_cpu_sys:12.012184used_cpu_user:9.453505used_cpu_sys_children:0.002158used_cpu_user_children:0.000000used_cpu_sys_main_thread:11.802969used_cpu_user_main_thread:9.286577# Modules 模块加载状况# Errorstats 谬误状态信息errorstat_NOAUTH:count=6errorstat_READONLY:count=1errorstat_WRONGPASS:count=1# Cluster 集群信息cluster_enabled:0# Keyspace 库id与键数量相干信息db0:keys=1,expires=0,avg_ttl=0 ...

April 14, 2022 · 11 min · jiezi

关于redis:Redis-缓存过期处理与内存淘汰机制

已过期的key如何解决?设置了expire的key缓存过期了,然而服务器的内存还是会被占用,这是因为redis所基于的两种删除策略redis有两种策略: (被动)定时删除定时随机的查看过期的key,如果过期则清理删除。(每秒查看次数在redis.conf中的hz配置)(被动)惰性删除当客户端申请一个曾经过期的key的时候,那么redis会查看这个key是否过期,如果过期了,则删除,而后返回一个nil。这种策略敌对,不会有太多的损耗,然而内存占用会比拟高。所以,尽管key过期了,然而只有没有被redis清理,那么其实内存还是会被占用着的。那么如果内存被Redis缓存占用慢了咋办?内存占满了,能够应用硬盘,来保留,然而没意义,因为硬盘没有内存快,会影响redis性能。所以,当内存占用满了当前,redis提供了一套缓存淘汰机制:MEMORY MANAGEMENTmaxmemory :当内存已使用率达到,则开始清理缓存 * noeviction:旧缓存永不过期,新缓存设置不了,返回谬误* allkeys-lru:革除起码用的旧缓存,而后保留新的缓存(举荐应用)* allkeys-random:在所有的缓存中随机删除(不举荐)* volatile-lru:在那些设置了expire过期工夫的缓存中,革除起码用的旧缓存,而后保留新的缓存* volatile-random:在那些设置了expire过期工夫的缓存中,随机删除缓存* volatile-ttl:在那些设置了expire过期工夫的缓存中,删除行将过期的

April 13, 2022 · 1 min · jiezi

关于redis:Redis的持久化机制-AOF

RDB会失落最初一次备份的rdb文件,然而其实也无所谓,其实也能够忽略不计,毕竟是缓存,丢了就丢了,然而如果谋求数据的完整性,那就的思考应用AOF了AOF特点 以日志的模式来记录用户申请的写操作。读操作不会记录,因为写操作才会存存储。文件以追加的模式而不是批改的模式。redis的aof复原其实就是把追加的文件从开始到结尾读取执行写操作。劣势AOF更加耐用,能够以秒级别为单位备份,如果产生问题,也只会失落最初一秒的数据,大大增加了可靠性和数据完整性。所以AOF可一次,应用fsync操作。以log日志模式追加,如果磁盘满了,会执行 redis-check-aof 工具当数据太大的时候,redis能够在后盾主动重写aof。当redis持续把日志追加到老的文件中去时,重写也是十分平安的,不会影响客户端作。AOF 日志蕴含的所有写操作,会更加便于redis的解析复原。劣势雷同的数据,同一份数据,AOF比RDB大针对不同的同步机制,AOF会比RDB慢,因为AOF每秒都会备份做写操作,这样绝对与RDB来说就略低。 每秒备份fsync没故障,然而的每次写入就做一次备份fsync的话,那么redis的性能就会降落。AOF产生过bug,就是数据恢复的时候数据不残缺,这样显得AOF会比拟软弱,容易呈现bug,因为AOF没有RDB那么简略,然而呢为的产生,AOF就不会依据旧的指令去重构,而是依据过后缓存中存在的数据指令去做重构,这样就更加强壮和牢靠了。AOF的配置# AOF 默认敞开,yes能够开启appendonly no# AOF 的文件名appendfilename "appendonly.aof"# no:不同步# everysec:每秒备份,举荐应用# always:每次操作都会备份,平安并且数据残缺,然而慢性能差appendfsync everysec# 重写的时候是否要同步,no能够保障数据安全no-appendfsync-on-rewrite no# 重写机制:防止文件越来越大,主动优化压缩指令,会fork一个新的过程去实现重写动作,新过程里的内存数据会被重写,此时# 以后AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb到底采纳RDB还是AOF呢? 如果你能承受一段时间的缓存失落,那么能够应用RDB如果你对实时性的数据比拟care,那么就用AOF

April 13, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习十一redis-的哨兵模式详解和实战

上一次咱们说到的主从复制是这样搭建的 主机能够读,能够写 从机只能读,不能写 想一想,那么咱们是不是也能够这样呢? 多个 redis-server 首尾相连 那么咱们部署的时候就是 6379 – 6380 – 6381 此时,若主机 6379 宕机掉,6380 会不会变成主机呢? 127.0.0.1:6379> info replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6380,state=online,offset=0,lag=1master_failover_state:no-failovermaster_replid:f1e3db9e5e438f5d98e4cad23f684b12d790ae56master_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:0咱们能够看到 6379 有一个从机,6380 本身也是作为从机(尽管 6380 是 6381 的主机) 此时将 6379 宕机掉,发现 6380 依然是 slave 127.0.0.1:6379> shudownnot connected> 咱们人为的将 reids 6380 的服务器批改为主机,看看 6379 redis-server 起来之后,是否能够把 master 抢回去 应用 slaveof no one 能够将本人设置成 master 启动 6379 redis-server redis-server /usr/local/redis/redis-6.2.5/6379.conf发现 6380 依然是主机,6379 成为了光杆司令 理论我的项目中,咱们必定不会采取下面和上一次文章说到的部署形式,他们抵挡危险的能力太低了 因为理论生产环境中,主机宕机了,若从机没有方法成为主机的话,岂不是在主机回复之前再也不能做写入操作了吗?这是很重大的问题 上面咱们来具体看看 哨兵模式是如何解决上述问题的 哨兵模式主动选举 master 的模式 ...

April 10, 2022 · 3 min · jiezi

关于redis:Redis-基础学习

什么是 Redis?Redis is in the family of databases called key-value stores.Redis ( Remote DIctionary Server ) 通常被称为数据结构服务器。键值存储的实质The essence of a key-value store is the ability to store some data, called a value, inside a key. This data can later be retrieved only if we know the exact key used to store it.键值存储的实质是在键中存储一些数据(被称为值)的能力。仅当咱们晓得用于存储它的键时,能力在稍后检索此数据。所以 Redis 的惯例函数都是围绕键和值开展。 Redis反对的数据类型?Redis 能够存储键和不同类型的值之间的映射。 键的类型只能为字符串。 值常见有五种数据类型: 字符串(string)。列表(list)。汇合(set)。哈希表(hash)。有序汇合。Redis keys 命令删改查。 删: DEL key [key ...]改: RENAME key newkey查: ...

April 6, 2022 · 3 min · jiezi

关于redis:Redigo-ScanStruct匿名指针字段的解析

Redigo issue 487How to scan struct with nested fields?#487为了更好的了解本篇文章,倡议先浏览issue原文 一、问题是什么将HGETALL 命令返回的数据,解析到对应的构造体UserInfo中,然而构造体中的*LiteUser字段的数据未能胜利解析。 如果将 *LiteUser 改为 LiteUser 就能够了。 二、复现问题copy issue 中的代码,go module 装置 redigo,再筹备一台redis服务。go.mod 中的 redigo中的版本设置为issue未修改前的版本:v1.8.1。运行代码,会复现此issue的问题,*LiteUser字段的数据未能胜利解析。试试最新版的代码,运行下来的状况: go.mod 中的 redigo 中的版本设置为issue最新版本:v1.8.8。运行代码,问题没有呈现。留神,为了在最新版本下复现问题,需在示例代码大概 73行上面,退出如下代码(前面再回过头来看看这个问题):...var newUser UserInfonewUser.LiteUser = &LiteUser{}...三、怎么解决的具体内容详见 pr 490 在看如何解决之前,先梳理一下执行流程: 3.1 解析数据到构造体变量当执行HGETALL从 Redis 中拿到了数据后,须要将数据解析到构造体的成员变量上,就像从 MySQL 拿进去数据,解析到构造体成员变量上是一个意思。 Redigo 提供好了一个办法,将数据和构造体变量传进去,数据就会解析到newUser构造体上: redis.ScanStruct(v, &newUser)3.2 ScanStruct接下来,看下redis.ScanStruct()都做了些什么。我梳理总结了一下过程中调用的办法: // 将数据解析到structSpecForType返回的构造体成员上func ScanStruct(src []interface{}, dest interface{}) error { //获取变量指针 d := reflect.ValueOf(dest) //获取指针指向的变量 d = d.Elem() structSpecForType(d.Type()) ...}// 依据传入的reflect.Type,先去缓存中查找是否解析过,如果没有调用compileStructSpecfunc structSpecForType(t reflect.Type) *structSpec { ... compileStructSpec(t, make(map[string]int), nil, ss) ...}3.3 compileStructSpeccompileStructSpec办法实现的就是类型解析,问题其实就出在了这。 ...

April 4, 2022 · 2 min · jiezi

关于redis:Redis面试题

Redis面试题redis快的起因, redis的字典和hashmap区别 Kafka和redis作用,如何避免音讯失落 redis实现长久化的原理 Redis缓存和MySQL数据库一致性保障? redis集群部署形式?主从和哨兵的区别? redis数据结构,string底层实现,跳表复杂度? redis应用过程中呈现过变慢的状况吗? redis个别怎么用的?为什么抉择用redis?为什么redis快呢? redis几种数据类型?redis string的底层实现? 分布式锁用过吗?说说怎么用的?用的哪个命令? redis 底层 hash 表扩容机制说一下? redis解决客户端session共享信息怎么解决的。 redis分布式锁解决了什么问题? redis为什么能反对分布式锁?应用形式有哪些? Redis的其余利用场景? 看过Redis源码吗? 你罕用过有哪些Redis的数据类型? List是用什么数据结构实现的? Redis的一个长久化策略理解吗?说一下 说出5个你罕用的Redis命令 Redis的数据结构? 五种根底类型&几个高级数据结构 场景题:省市区这种类型的数据,用Redis的哪种数据结构存储比拟好? Redis速度快的起因? redis 对于缓存击穿的解决方案 Redis为什么这么快 Redis的key过期策略 缓存穿透和缓存雪崩 分布式锁的作用是什么? 分布式锁什么实现的? 分布式锁是怎么避免死锁?过期工夫 分布式锁设置过期工夫有啥考究? 万一业务解决工夫超过过期工夫了,怎么办? 分布式锁 延时队列 布隆过滤器 缓存 计数器 Zset底层实现? 缓存穿透 缓存击穿 缓存雪崩是什么意思? 缓存穿透怎么避免?

April 4, 2022 · 1 min · jiezi

关于redis:Redigo-ScanStruct匿名指针字段的解析

Redigo issue 487How to scan struct with nested fields?#487为了更好的了解本篇文章,倡议先浏览issue原文 一、问题是什么将HGETALL 命令返回的数据,解析到对应的构造体UserInfo中,然而构造体中的*LiteUser字段的数据未能胜利解析。 如果将 *LiteUser 改为 LiteUser 就能够了。 二、复现问题copy issue 中的代码,go module 装置 redigo,再筹备一台redis服务。go.mod 中的 redigo中的版本设置为issue未修改前的版本:v1.8.1。运行代码,会复现此issue的问题,*LiteUser字段的数据未能胜利解析。试试最新版的代码,运行下来的状况: go.mod 中的 redigo 中的版本设置为issue最新版本:v1.8.8。运行代码,问题没有呈现。留神,为了在最新版本下复现问题,需在示例代码大概 73行上面,退出如下代码(前面再回过头来看看这个问题):...var newUser UserInfonewUser.LiteUser = &LiteUser{}...三、怎么解决的具体内容详见 pr 490 在看如何解决之前,先梳理一下执行流程: 3.1 解析数据到构造体变量当执行HGETALL从 Redis 中拿到了数据后,须要将数据解析到构造体的成员变量上,就像从 MySQL 拿进去数据,解析到构造体成员变量上是一个意思。 Redigo 提供好了一个办法,将数据和构造体变量传进去,数据就会解析到newUser构造体上: redis.ScanStruct(v, &newUser)3.2 ScanStruct接下来,看下redis.ScanStruct()都做了些什么。我梳理总结了一下过程中调用的办法: // 将数据解析到structSpecForType返回的构造体成员上func ScanStruct(src []interface{}, dest interface{}) error { //获取变量指针 d := reflect.ValueOf(dest) //获取指针指向的变量 d = d.Elem() structSpecForType(d.Type()) ...}// 依据传入的reflect.Type,先去缓存中查找是否解析过,如果没有调用compileStructSpecfunc structSpecForType(t reflect.Type) *structSpec { ... compileStructSpec(t, make(map[string]int), nil, ss) ...}3.3 compileStructSpeccompileStructSpec办法实现的就是类型解析,问题其实就出在了这。 ...

April 3, 2022 · 2 min · jiezi

关于redis:redis后台线程条件变量源码解析

定义条件变量容许一个线程就某个共享变量(或其余共享资源)的状态变动告诉其余线程,并让其余线程期待(阻塞于)这一告诉。 标记变量有时候线程之间不必条件变量和锁, 用一个标记变量会看起来很简略,很吸引人。例如,期待代码: while (ready == 0) ; // spin相干的发信号代码看起来像这样: ready = 1;千万不要这么做。因为: 首先,少数状况下性能差(长时间的自旋节约 CPU)。其次,容易出错。这些不正规的同步办法半数以上都是有问题的。条件变量API采纳了条件变量( condition variable),这一问题就迎刃而解:容许一个线程休眠(等待)直至接获另一线程的告诉(收到信号)去执行某些操作(例如,呈现一些“状况”后,期待者必须立刻做出响应)。线程阻塞时不占用CPU。 #include <pthread.h>int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); pthread_cond_signal/pthread_cond_broadcast函数 pthread_cond_signal()和 pthread_cond_broadcast()之间的差异在于,二者对阻塞于 pthread_cond_wait()的多个线程解决形式不同。 pthread_cond_signal()函数只保障唤醒至多一条受到阻塞的线程,而 pthread_cond_broadcast()则会唤醒所有遭阻塞的线程。 只有当仅需唤醒一条(且无论是其中哪条)期待线程来解决共享变量的状态变动时,才应应用 pthread_cond_signal()。利用这种形式的典型状况是,所有期待线程都在执行完全相同的工作。同理,函数 pthread_cond_broadcast()所解决的状况是:处于期待状态的所有线程执行的工作不同(即各线程关联于条件变量的断定条件不同)。 pthread_cond_wait条件变量总是要与一个互斥量相干。将这些对象通过函数参数传递给 pthread_cond_wait(),后者执行如下操作步骤。 解锁互斥量 mutex。梗塞调用线程,直至另一线程就条件变量 cond 发出信号。从新锁定 mutex。通常状况下代码会以如下形式访问共享变量: pthread_mutex_lock(&mtx);while(/* Check that shared variable is not in state we want */) pthread_cond_wait(&cond, &mtx);pthread_mutex_unlock(&mtx);代码执行流程如下: 线程在筹备查看共享变量状态时锁定互斥量。查看共享变量的状态。如果共享变量未处于预期状态,线程应在期待条件变量并进入休眠前解锁互斥量(以便其余线程能拜访该共享变量)。当线程因为条件变量的告诉而被再度唤醒时, 必须对互斥量再次加锁, 因为在典型状况下,线程会立刻访问共享变量。函数 pthread_cond_wait()会主动执行最初两步中对互斥量的解锁和加锁动作。第 3 步中互斥量的开释与陷入对条件变量的期待同属于一个原子操作。换句话说,在函数pthread_cond_wait()的调用线程陷入对条件变量的期待之前,其余线程不可能获取到该互斥量,也不可能就该条件变量发出信号。 测试条件变量的判断条件( predicate)必须由一个 while循环,而不是 if 语句,来管制对pthread_cond_wait()的调用。这是因为,当代码从 pthread_cond_wait()返回时,并不能确定判断条件的状态,所以应该立刻从新查看判断条件,在条件不满足的状况下持续休眠期待。 ...

April 2, 2022 · 2 min · jiezi

关于redis:Redis数据结构详解3redis中的排序好手跳表skiplist

前提常识自从通过博客开始记录学习内容、整顿常识,整个人变得比以前更踊跃了,虽说实质是为了记录和整顿,但未免对各大博客网站的阅读数和点赞评论数关怀(尽管到当初还少得可怜哈哈哈),有的博客网站还有本人专属的积分值,甚至还有排行榜,我偶然也会点开看看,空想本人能呈现在下面~(嘻嘻~梦里什么都有) 问:那么这个排行榜应该怎么实现呢?(强行应题!)答:简略!数据库用一个表来保护,按积分值字段大小排序不就行了~的确可行,但因为网站的并发量高,须要疾速响应,就要借助缓存来实现,而redis中刚好有一个根本数据结构合乎这个要求,那就是Sorted set(有序汇合),它跟Set(汇合)一样不能有反复的元素,然而多了排序的性能,而且是主动排序,不须要保护的,也就是你增加或更新元素,底层主动就帮你排序了。所以当你须要一个有序且不反复的汇合列表,那么Sorted set将是一个不错的抉择。 Sorted set的底层实现用到了属于redis用到比拟有代表性的数据结构——跳表skiplist,也是咱们明天要说的次要内容,它是如何在redis中起到了排序的作用,又是怎么实现的呢?咱们从头梳理一下~ 中华文化博大精深咱们常常说到这句话:中华文化博大精深;然而有时在了解意思不当也会产生反成果。比方跳表,全称是跳跃列表(摘自百科),如果你没有看到跳表的英文单词skiplist,可能要误会,此跳跃(skip)非彼跳跃(jump),不是双脚离地的跳起,而是“跳过、省略”的意思,所以我集体感觉翻译为“跳越列表”可能更适宜一点,不过也不用纠结这个,置信明确了跳表skiplist的英语原意,会更好地帮你持续往下理解跳表。 排序,你会想到什么?咱们先从最根底的数据结构来想: 数组 ❎数组是在内存上间断的,如果你对要保护排序的元素有新增、更新、删除的操作,那么将“牵一发而动全身”,势必须要做大量的内存删除或者挪动操作;所以除非是要保护一个固定不变的元素汇合,其余状况咱们就不必思考数组了,所以pass。 链表 ✅链表就不存在下面的状况,而且链表的特点就是用来存储线性表的数据结构;新增、更新、删除等操作无非也就是扭转指针指向的地址,是个不错的抉择。 哈希表 ❎哈希表其实不是有序的,在哈希表上只能做单个key的查找,不适宜做范畴查找,所以也pass掉。 树 ✅树其实也是一个用来排序的优质抉择,但从后果来看,redis并没有取用它,起因咱们先按下不表,前面会具体解释。 一般链表能满足需要吗?跳表对它做了什么优化?咱们先来看一个一般的有序链表:如果我当初要增加一个 新元素18 那么我只能先从头结点开始,通过next指针找到第一个 元素2,而后再通过next指针找到下一个 元素3,如此重复,直到咱们找到第一个大于等于要增加的元素,即 元素19,它比18大,所以咱们应该把 元素18 增加在 元素19 之前,通过8个指针(简略为数红箭头个数)寻址到最初后果。由此可见:链表越长,插入的地位越靠后,那么消耗的工夫就更多,工夫复杂度为O(n)。 总感觉能够优化呢!又要提到这句话了: 家喻户晓,redis是一个谋求性能的程序。 这里插入一个小常识: 在30多年前的1990年,威廉·普创造了跳跃列表(跳表),从百科摘下发明者威廉·普对跳表的评估: 跳跃列表是在很多利用中有可能代替均衡树而作为实现办法的一种数据结构。跳跃列表的算法有同均衡树一样的渐进的预期工夫边界,并且更简略、更疾速和应用更少的空间。奈斯!通过这个评估咱们不光对跳表有个大抵的理解,如同还顺便牵扯了一道常见的相干面试题。 作者说跳表简略?哪里简略?你说说看!跳表跳表,之所以叫跳表,就是因为它能够跳~(不是是因为跳表能够通过节点的跃迁更快地找到目标值。咱们沿用下面的数据,然而间接换用跳表的数据结构举个栗子:直观来看,一些元素仿佛多了一层,但咱们不晓得干嘛的,不过先别急,咱们依照这个构造试着增加 新元素18之前咱们是从第一层开始的,但这次咱们从第二层,也就是 L2节点开始,通过next指针跳过了 元素2 间接指向 元素3,再指向 元素7,始终到比 元素18 大的 元素19,而后通过backward指针找到前一个 元素17,元素18 又大于它,所以咱们最终把元素增加到这里,通过5个指针(简略为数红箭头个数)寻址到最初后果。 相比之前简略链表要通过8个指针来寻址,跳表的复杂度低了一点。不过咱们还不满足,咱们这次把局部元素的层数减少到3层再看一下。 这次又少了,通过4个指针(简略为数红箭头个数)就能够寻址到最初后果。 跳表skiplist的寻址逻辑能够简略地概括为:从最高层开始寻址,以后节点的next指针如果指向null的话就降落一层,next指针指向的下一个元素值如果小于查找值,就持续走,如果大于的话就调用backward后退指针找前一个元素再比拟,直到找到对应的地位。 那元素分值score会不会雷同呢?会,这时候就会按数据obj的字典序排序,能够简略了解优先级为0~9、A~Z、a~z。你有没有发现,下面的过程有点相似于二分查找,或者说n分查找,现实状态下能够把工夫复杂度从原来的O(n)升高到O(log n)。 跳跃表节点详解首先咱们看下跳跃表节点构造定义: typedef struct zskiplistNode { // 后退指针 struct zskiplistNode *backward; // 分值 double score; // 成员对象 robj *obj; // 层 struct zskiplistLevel { //后退指针 struct zskiplistNode *forward; //跨度 unsigned int span; } level[];} zskiplistNode;看到具体的跳跃表节点构造,咱们就晓得多的层是哪里来的了。 ...

April 1, 2022 · 2 min · jiezi

关于redis:Redis一原理与基本使用

@[toc] 一、Redis的外围概念概念 Redis就是分布式缓存,也能够了解成过程外的缓存。 如图: 二、Redis的利用场景利用场景 次要是利用在集群零碎中。 单体我的项目就没必要用分布式缓存,应用本地缓存就能够;如图: 当客户端发动申请到零碎,零碎先去到本地缓存查问数据,没有查问到数据则到数据库查问,将查到的数据保留到本地缓存在返回到客户端;当第二次申请到零碎,零碎先去到本地缓存查问数据,则将缓存中的数据返回到客户端【第一次申请曾经将数据保留到本地缓存中】;那他本地缓存的命中率为 50%;应用本地缓存做分布式会有缓存命中率降落缺点;如图: 客户端发动申请到Nginx,Nginx将申请代理到零碎1,零碎1查看本地缓存没有符合条件的数据,再到数据库获取数据存到本地缓存再返回到客户端;当客户端在一次发动申请到Nginx,Nginx将申请代理到零碎2,零碎2查看本地缓存没有符合条件的数据,再到数据库获取数据存到本地缓存再返回到客户端,那么发动了两次申请都没有到本地缓存中获取到数据,那它缓存命中率为 0/2=0;缓存命中率降落那就表明整体查问性能降落。解决缓存命中率的计划 将缓存数据集中到一起,应用Redis分布式缓存,如图: 三、Redis的我的项目落地条件 Demo我的项目Redis 【在linux中部署】 Linux装置 #装置 wget 命令yum -y install wget#下载wget http://download.redis.io/releases/redis-6.2.6.tar.gz#解压tar xzf redis-6.2.6.tar.gz#进入解压文件夹cd redis-6.2.6#装置 gccyum install gcc#编译make#进入解压后的src目录,运行服务src/redis-serverDemo 我的项目配置 步骤 装置Redis Nuget包 StackExchange.Redis定义一个扩大类 public static IServiceCollection AddRedis(this IServiceCollection serviceCollection) { ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect("IP:端口"); serviceCollection.AddSingleton(connectionMultiplexer); return serviceCollection; }StateUp类注册【写了个扩大办法】 services.AddRedis();Redis 存储和获取 //注入public readonly ConnectionMultiplexer connectionMultiplexer; public HomeController(ConnectionMultiplexer _connectionMultiplexer){ connectionMultiplexer = _connectionMultiplexer;}// 获取Redis值obj = connectionMultiplexer.GetDatabase(0).StringGet(Key).ToString();//存储Redis值 Key:ValueconnectionMultiplexer.GetDatabase(0).StringSet(Key, Value);Redis 存储汇合 ...

March 31, 2022 · 2 min · jiezi

关于redis:基于Redis实现一套支持排队等待的限流器

一、背景因为我的项目中调用了一个政府官网零碎,前段时间失去政府告诉说咱们调用频率太高,目前给咱们凋谢的接口调用频率是每秒一次。而后还发过来一个咱们申请通过与超频的比例报告,显示失败率高达80%,也就是说咱们百分之80的申请都被拦挡了,这里应该会有有搭档疑难80%的异样申请你们零碎怎么开展业务的。实际上咱们零碎外部有做缓存,而后后盾有被动和被动两种形式去刷新缓存,所以这80%失败申请中绝大多数都是后盾刷新缓存的申请,并非客户端用户的申请所以呢对咱们的业务也没有实质性的影响。基于此我方也须要做申请限度,不然政府方面会思考以申请失败率过高而把咱们的接口权限下掉。 二、调研对于限流的经典算法漏斗和令牌通这里就不多说了,这类算法介绍网上曾经很多内容了。我这里整顿下目前市面上开源的限流工具以及为什么咱们没抉择应用开源工具而要本人造轮子的起因。 Google Guava首先就是谷歌的Guava工具类库,该类库提供很多比拟好用的工具类,其中就包含基于令牌通算法实现的限流器RateLimiter // 1、申明一个qps最大为1的限流器RateLimiter limiter = RateLimiter.create(1);// 2、尝试阻塞获取令牌limiter.acquire();Alibaba Sentinel而后就是阿里巴巴的Sentinel,这个就比拟弱小了,应该是目前市面上限流方面做的最全面的开源我的项目了。不仅反对流量管制,同时还反对分布式限流,熔断降级,系统监控等,还有比拟灵便的限流策略配置反对。这里我也没用过,可能须要花些工夫能力把握吧。 Redisson RRateLimiter最初要介绍的是基于令牌通算法实现的RRateLimiter, 它是Redisson类库的限流工具,反对分布式限流,应用起来也相当的不便 // 1、 申明一个限流器RRateLimiter rateLimiter = redissonClient.getRateLimiter(key); // 2、 设置速率,5秒中产生3个令牌rateLimiter.trySetRate(RateType.OVERALL, 3, 5, RateIntervalUnit.SECONDS); // 3、试图获取一个令牌,获取到返回truerateLimiter.tryAcquire(1)选型 首先我的需要是限流器必须要反对分布式,那Guava首先能够排除了。而后Sentinel对于咱们的需要来说有些轻便,太过于分量所以也排除了。最初RRateLimiter尽管反对分布式,应用也比较简单,然而如同它不反对偏心排队(不太确定)。三、造轮子基于以上我决定本人手撸一个反对偏心排队的分布式限流器。实现计划是基于Redis Lua脚本而后配合业务层代码反对,间接上菜 限流器的主体代码public class RedisRateLimiter { public static final GenericToStringSerializer argsSerializer = new GenericToStringSerializer<>(Object.class); public static final GenericToStringSerializer resultSerializer = new GenericToStringSerializer<>(Long.class); @Resource private RedisTemplate<String, Object> redisTemplate; @Resource private RedisUtil redisUtil; public static final int DEFAULT_MAX_PERMIT_COUNT = 1; public static final float DEFAULT_INTERVAL_SECONDS = 1.3f; public static final int DEFAULT_TIMEOUT_SECONDS = 5; // TODO 目前不反对自定义该值 /** * 一个周期内的最大许可数量 */ private int maxPermitCount; public RedisRateLimiter() { this.maxPermitCount = DEFAULT_MAX_PERMIT_COUNT; } public static DefaultRedisScript<Long> redisScript; static { redisScript = new DefaultRedisScript<>(); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua"))); redisScript.setResultType(Long.class); } /** * * @param redisKey * @param intervalSeconds 距离几秒创立一个许可 * @param timeoutSeconds 获取许可超时工夫 * @return * @throws InterruptedException */ public boolean tryAcquire(String redisKey, float intervalSeconds, long timeoutSeconds) throws InterruptedException { try { if (redisKey == null) { throw new BusinessException(BusinessExceptionCode.REQUEST_PARAM_ERROR, "redisKey不能为空!"); } Preconditions.checkArgument(intervalSeconds > 0.0 && !Double.isNaN(intervalSeconds), "rate must be positive"); long intervalMillis = (long) (intervalSeconds * 1000); long timeoutMillis = Math.max(TimeUnit.SECONDS.toMillis(timeoutSeconds), 0); long pttl = redisTemplate.execute(redisScript, argsSerializer, resultSerializer, Arrays.asList(redisKey), maxPermitCount, intervalMillis, timeoutMillis); if (pttl == 0) { log.info("----------------无需排队,间接通过, 以后许可数量={}", redisUtil.get(redisKey)); return true; }else if(pttl < timeoutMillis) { Thread.sleep(pttl); log.info("----------------排队{}毫秒后,通过, 以后许可数量={}", pttl, redisUtil.get(redisKey)); return true; }else { // 间接超时 log.info("----------------需排队{}毫秒,间接超时, 以后许可数量={}", pttl, redisUtil.get(redisKey)); return false; } }catch (Exception e) { log.error("限流异样", e); } return true; }}外围Lua脚本文件: rateLimiter.lualocal limit = tonumber(ARGV[1])local interval = tonumber(ARGV[2])local timeout = tonumber(ARGV[3])local count = tonumber(redis.call('get', KEYS[1]) or "0")local pttl = tonumber(redis.call('pttl', KEYS[1]) or "0")if pttl < 0 then pttl = 0end-- 这个代表已被预约的令牌数local currentCount = count - math.max(math.floor((count*interval - pttl)/interval), 0)-- 新增一个令牌local newCount = currentCount + 1-- 所有令牌总的生效毫秒local newPTTL = pttl + intervalif newCount <= limit then --无需排队间接通过 redis.call("PSETEX", KEYS[1], newPTTL, newCount) return 0elseif pttl < timeout then --排队pttl毫秒后可通过 redis.call("PSETEX", KEYS[1], newPTTL, newCount)else -- 超时 redis.call("PSETEX", KEYS[1], pttl, currentCount)end-- 返回需期待毫秒数return pttl

March 31, 2022 · 2 min · jiezi

关于redis:redis客户端发送键消息流程

redis客户端启动流程用户键入shell命令启动redis客户端:./redis-cli -h ...shell fork一个子过程来运行这个程序内核将redis客户端程序src/redis-cli可执行文件从磁盘载入内存子过程通过execvp零碎函数来执行与父过程不同的程序,即src/redis-cli程序。 execvp()会从可执行程序中加载代码和静态数据,并用它覆写本人(子过程)的代码段(以及静态数据),堆、栈及其他内存空间也会被从新初始化。而后操作系统就执行该程序,将参数通过 argv 传递给该过程。因而,它并谁有创立新过程,而是间接将以后运行的程序(以前的 shell)替换为不同的运行程序(redis-cli)调用redis-cli.c中的main函数程序在子过程中运行直到完结 构建RESP格局音讯redis命令行应用的是第三方代码库linenoise,期待命令行输出。linenoise是代替readline 性能的命令行工具,其源代码在deps\linenoise目录下。它能够独立于 Redis src 目录下的性能源码进行编译。 main()函数首先与向服务器发送connect(),申请建设tcp连贯: int main(int argc, char **argv) { ...... cliConnect(0); repl(); ...... }等到tcp通过三次握手建好连贯后,客户端通过linenoise()函数期待命令行输出: static void repl(void) { while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL) { ...... }}(5) repl()中调用hiredis接口redisAppendCommandArgv()依据命令行参数构建RESP格局音讯 #0 redisAppendCommandArgv () at hiredis.c:965#1 cliSendCommand () at redis-cli.c:1148#2 issueCommandRepeat () at redis-cli.c:1583#3 repl () at redis-cli.c:1764#4 main () at redis-cli.c:7158// 依据命令行构建resp音讯int redisFormatSdsCommandArgv(){ sds cmd; cmd = sdsempty(); cmd = sdsMakeRoomFor(cmd, totlen); /* Construct command */ cmd = sdscatfmt(cmd, "*%i\r\n", argc); for (j=0; j < argc; j++) { len = argvlen ? argvlen[j] : strlen(argv[j]); cmd = sdscatfmt(cmd, "$%u\r\n", len); cmd = sdscatlen(cmd, argv[j], len); cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); } }发送redis键命令音讯给服务器发送接口:调用cliReadReply函数发送数据到服务端并读取服务端的返回数据cliReadReply() ---> redisGetReply() ---> redisBufferWrite() ---> write() ...

March 30, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习九Redis-的发布和订阅是咋玩的

Redis 公布订阅Redis 公布订阅(pub / sub)是一种音讯通信模式 发送者发送音讯 pub接受者订阅音讯 sub例如微信,微博这样的关注零碎 Redis 的客户端能够订阅任意数量的频道,不受限制 来看看图示音讯发布者音讯订阅者频道 这里的音讯发布者,和音讯订阅者都是 redis 客户端, 订阅者订阅某个频道,发布者在该频道中公布相干信息,例如文章,例如沸点,等等,音讯订阅者就能实时收到方才发布者发送的内容了 如下图中,频道 channel1 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系: 当有新音讯通过 PUBLISH 命令发送给频道 channel1 时 这个音讯就会被发送给订阅它的三个客户端: 常用命令下表列出了 redis 公布订阅常用命令: 序号命令及形容1PSUBSCRIBE pattern [pattern ...]订阅一个或多个合乎给定模式的频道。2PUBSUB subcommand [argument [argument ...]查看订阅与公布零碎状态。3PUBLISH channel message将信息发送到指定的频道。4[PUNSUBSCRIBE [pattern [pattern ...]退订所有给定模式的频道。5SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。6UNSUBSCRIBE [channel [channel ...] 指退订给定的频道。理论测试和验证subscribe channel [channel ...]订阅一个或者多个通道 PUBLISH channel message向频道中发送音讯 接收端: 接收端订阅 xiaomotong 频道,只有发送端有 publish 音讯到频道中,接收端就能马上收到 127.0.0.1:6379> subscribe xiaomotongReading messages... (press Ctrl-C to quit)1) "subscribe"2) "xiaomotong"3) (integer) 11) "message"2) "xiaomotong"3) "hellowrold"1) "message"2) "xiaomotong"3) "hello_redis"1) "message"2) "xiaomotong"3) "xiaozhupeiqi"发送端: ...

March 30, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习八redis-持久化-RDB-和-AOF

Redis 长久化redis 是内存数据库,如果不将内存中数据库保留到磁盘上,那么服务器一旦宕机,或者 redis 过程退出,不仅数据会被失落,服务器中的数据库状态也会被失落 因而 redis 提供了长久化的性能 redis 的长久化分为 RDB 和 AOF RDB (Redis DatabBase)在主从复制中,rdb文件都作为备用的,放在从机下面 在指定工夫距离内将内存中的数据集快照写入到磁盘中,这就是快照 snapshot ,复原快照的时候,是把快照文件读入到内存中。 redis 通过 fork 的形式创立一个子过程来专门做长久化的动作, 先将数据写入到一个临时文件中,待长久化过程完结,再用这个临时文件替换上一次的长久化好的文件整个过程中,主过程是不进行工作 IO 操作的,这就保障了极高的性能 如果须要进行大规模的数据恢复,且对于数据的完整性要求不那么敏感和严格,抉择 RDB 的长久化形式比 AOF 的长久化形式更优,更加高效。 RDB 尽管性能高,然而在 最初一次长久化后的数据可能会被失落,redis 默认就是应用的 RDB 长久化形式,个别状况下也不须要批改 save 60 3# The filename where to dump the DBdbfilename dump.rdbdir ./ 咱们设置 60s 内若 有操作 redis 3 次,那就做一次长久化 127.0.0.1:6379> pingPONG127.0.0.1:6379> config get dir1) "dir"2) "/root"127.0.0.1:6379> set k1 v1OK127.0.0.1:6379> set k2 v2OK127.0.0.1:6379> set k3 v3OKdump.db 文件是生成在 dir 目录下的,咱们这里的 dir 目录是 /root ...

March 30, 2022 · 3 min · jiezi

关于redis:Yii20-redis的配置和使用

第一步:当初本地环境中下载对应的压缩包,地址:https://github.com/MicrosoftA...。 第二步:window在DOS下进入解压后的目录,而后运行redis-server.exe 呈现启动胜利的窗口第三步:呈现以上界面的时候阐明启动胜利,而后另开一个DOS窗口进入目录下运行redis-cli.exe启动用户端服务,运行命令注:以上曾经能够失常运行redis命令了,然而这个时候的第一个窗口不能敞开门,敞开后就不能应用了。 Redis是能够装置成windows服务的,开机自启动,命令如下: redis-server --service-install redis.windows.conf装置完之后,就可看到Redis曾经作为windows服务了。 然而装置好之后,Redis并没有启动,启动命令如下: redis-server --service-start进行命令: redis-server --service-stop本地装置好redis服务后,那么该如何和Yii框架联合起来应用,请持续往下看: 一、composer装置 composer require --prefer-dist yiisoft/yii2-redis二、 手动装置 点击下载:yii2.0-redis扩大 把下载的扩大文件放到vendor/yiisoft/下,命名为yii2-redis 批改vender/yiisoft/下的extensions.php,退出redis扩大会在vender下生产文件夹vendor\yiisoft\yii2-redis,而后在common\config\main.php中增加redis配置在页面中的话就能够应用以下形式调用, $redis = Yii::$app->redis;$redis->set('aa','111a');echo $redis->get('aa');好了,这样的话就可能在Yii中失常应用redis了。

March 30, 2022 · 1 min · jiezi

关于redis:墨天轮访谈-华为云温云博从客户视角出发GaussDBfor-Redis究竟香在哪里

分享嘉宾:温云博华为云数据库NoSQL团队研发工程师整顿:墨天轮社区导读GaussDB(for Redis)采纳云原生分布式架构,齐全兼容Redis协定,反对丰盛数据类型。 提供数据实时长久化、多正本强统一保障,以及实时监控、弹性伸缩、主动备份等一站式服务。明天想和大家分享的内容是,从客户视角登程,GaussDB(for Redis)能带来哪些价值。 GaussDB(for Redis)介绍1、GaussDB(for Redis)是什么首先,我想要传递的观点是:GaussDB(for Redis)就是Redis。 目前在业界应用Redis的用户有很大的存量,置信他们在应用的过程中领会到各种各样的痛点,而咱们的GaussDB(for Redis)能与开源redis兼容,实现业务0搬迁革新。GaussDB(for Redis) 采纳云原生分布式架构,齐全兼容Redis协定,反对丰盛数据类型,同时提供数据实时长久化、多正本强统一保障,以及实时监控、弹性伸缩、主动备份等一站式服务。 对于GaussDB(for Redis)更多产品材料:https://bbs.huaweicloud.com/blogs/248875图1 GaussDB(for Redis) 劣势 2、GaussDB(for Redis)软件架构华为云在 存储、数据库、大数据三个畛域有很多的子产品,比方大数据畛域的Hadoop、Hbase、Hive等。其实它们的底层都会基于一个高性能分布式存储池实现存算拆散的架构,通过这样的架构带来企业级的能力。 GaussDB(for Redis)领有先进的存算拆散架构: 计算层:提供可弹性伸缩的吞吐性能。ELB+Proxy架构,用单机模式客户端即可疾速接入业务。存储层:提供高牢靠的数据存储能力。数据三正本、强统一存储。扩容秒级实现,业务0感知。同时最要害的特点是,数据并不保留在计算层中,数据是全量下沉在存储池中。对于存储池自身,领有数据长久化能力,以三正本模式存储,数据长久化能力比拟高。 图2 GaussDB(for Redis) 软件架构 GaussDB(for Redis)解决的痛点1、解决老本痛点首先体现在实例价格上。GaussDB(for Redis)比开源节俭20%-70%,同时容量越大,性价比越高,标配可扩容至12TB 其次体现在数据压缩上。逻辑压缩+物理压缩联合,比开源Redis更省空间,省空间就是降老本。依据理论业务测试,string、hash等罕用构造在GaussDB(for Redis)实例中,存储空间占用仅为开源Redis的70%~85% 最初体现在前期扩容上。不同于开源Redis各节点本地内存装载数据分片,GaussDB(for Redis)可能实现存储独立扩容,不用为过剩算力买单。同时容量依照1GB的力度调整,按需应用也是省老本的一个路径。 综上所述,老本的痛点在这里是能够齐全解决的。 图3 GaussDB(for Redis) 在老本上的解决方案 2、解决稳定性痛点稳定性指标稳定性作为一种隐性的指标,容易影响开发与运维的效率 图4 GaussDB(for Redis) 与开源Redis 在稳定性上的指标比照 跨Region容灾跨Region容灾是实现一个RsyncServer过程,负责集群下各个节点 间的数据复制,以此为根底实现逻辑齐备的灾备计划。 高斯Redis除了提供上述3AZ的强统一计划以外,还提供跨Region级别的容灾,也就是两个实例间的异步容灾。在计划里减少了一个Rsync-Server的模块,用来订阅主实例上新增的日志,再把日志反解编码成相应的格局,转发给对端的备实例,由备实例回放即可。这套计划,能够实现双向同步、断点续传、抵触解决等等。 图5 GaussDB(for Redis) 跨Region容灾 3、解决扩容痛点业务持续扩容时,开源Redis只能加分片,从而导致扩容折腾、数据无奈及时平衡、老本大涨等问题。而GaussDB(for Redis) 在扩容方面可能实现三点价值,灵便、不停服、省老本。 图6 GaussDB(for Redis) 在扩容上的解决方案 GaussDB(for Redis)案例分享1、案例一:大型手游跨服对战在某大型手游跨服对战的案例中,GaussDB(for Redis)数据库解决全局缓存和跨服缓存,帮忙轻松应答跨服多人对战流量顶峰。 高稳定性,轻松应答业务的顶峰变动弹性伸缩,实现秒级无损扩容,在开服首日对战顶峰时做到实时扩容并熟能生巧,对业务0影响;提供三正本容灾计划,从底层保证数据一致性,从源头防止脏数据呈现,无效帮助客户简化下层业务逻辑架构。图7 GaussDB(for Redis) 在大型手游跨服对战的利用案例 2、案例二:商品库存零碎在商品库存零碎的场景上,GaussDB(for Redis)的解决方案可能实现强统一,实现无脏读、不超卖。 电商的零碎常常波及到高并发拜访,客户原先应用开源redis,不强壮的主从构造会存在丢数据的危险,导致超卖的景象时有发生。切到GaussDB(for Redis)后,利用强统一能力,数据在存储池当中三正本统一,不存在数据不统一或者失落。库存零碎不论是在线上、或者是秒杀都不会呈现数据不统一的景象,防止超卖。 ...

March 29, 2022 · 1 min · jiezi

关于redis:Redis持久化之AOF

AOF(Append Only File):增量长久化形式与RDB长久化通过保留数据库中的键值对来记录数据库状态不同,AOF长久化是通过保留Redis服务器所执行的写命令来记录数据库状态的,如下图: 被写入AOF文件的所有命令都是以Redis的命令申请协定格局(RESP协定REdis Serialization Protocol)保留的,RESP2是纯文本格式。 1、AOF长久化的实现AOF长久化性能的实现能够分为命令追加(append)、文件写入、文件同步(sync)三个步骤。(1)命令追加当AOF长久化性能处于关上状态时,服务器在执行完一个写命令之后,会以RESP协定格局将被执行的写命令追加到服务器状态的aof_buf缓冲区的开端: struct redisServer { sds aof_buf; // 应用sds数据结构实现AOF缓冲区}命令追加在执行完命令之后,表明AOF是写后日志,采纳写后日志的益处: I) 为了防止额定的查看开销,Redis 在向 AOF 外面记录日志的时候,并不会先去对这些命令进行语法查看。所以,如果先记日志再执行命令的话,日志中就有可能记录了谬误的命令,Redis 在应用日志复原数据时,就可能会出错。而写后日志这种形式,就是先让零碎执行命令,只有命令能执行胜利,才会被记录到日志中,否则,零碎就会间接向客户端报错。所以,Redis 应用写后日志这一形式的一大益处是,能够避免出现记录谬误命令的状况。 II) 除此之外,AOF 还有一个益处:它是在命令执行后才记录日志,所以不会阻塞以后的写操作。但会阻塞之后的写操作。 (2)AOF文件的写入与同步Redis的服务器主过程就是一个事件循环: Redis的命令追加在主循环(aeMain函数)中的每次解决完写命令的执行后,通过propagate函数触发。 propagate办法将以后命令的内容append到redisServer对象的aof_buf变量中。主循环在下一个迭代进入多路复用的select办法前,Redis会通过flushAppendOnlyFile办法将aof_buf的内容write到AOF对应的文件中。但write操作只是将数据写到缓存中,什么时候从缓存真正落地到磁盘上,取决于操作系统。只有显示调用fsync()办法能力强制地让操作系统落地数据到磁盘。 AOF写入与同步的语义写入AOF文件指的是:调用write()写到操作系统缓存同步AOF文件指的是:调用fsync()强制缓存落地到磁盘文件flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项决定: always:主循环的每个迭代的flushAppendOnlyFile函数中都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且间接触发fsync办法同步AOF文件,强制数据落地到磁盘。因为每个命令都在写入磁盘后才返回,故容错能力最高,即便呈现故障宕机,也只会失落一个事件循环中所产生的命令数据。everysec:服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且每隔一秒异步地触发一次fsync办法。fsync办法的执行者是bio线程池中的某个线程。flushAppendOnlyFile函数只是作为生产者将fsync工作放入队列,由bio线程生产并执行。no:将aof_buf缓冲区中的所有内容写入到AOF文件,但不显示调用fsync,由操作系统决定什么时候落地磁盘。这种模式下,Redis无奈决定增量的落地工夫,因而容错能力不可控。综上,以上3种策略将aof_buf缓冲区中的所有内容写入到AOF文件的机会都一样,不同的是fsync强制落盘的机会。由名字能够看出,appendfsync是同步AOF文件策略。 2、AOF文件的载入与数据还原Redis会创立一个不带网络连接的伪客户端(fake client):因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所应用的命令间接来源于AOF文件而不是网络连接,所以服务器应用了一个没有网络连接的伪客户端来执行AOF文件保留的写命令。 3、AOF重写(1)为了解决AOF文件体积收缩的问题 AOF 文件过大会带来性能问题。这里的“性能问题”,次要在于以下三个方面: 一是,文件系统自身对文件大小有限度,无奈保留过大的文件;二是,如果文件太大,之后再往里面追加命令记录的话,效率也会变低;三是,如果产生宕机,AOF 中记录的命令要一个个被从新执行,用于故障复原,如果日志文件太大,整个复原过程就会十分迟缓,这就会影响到 Redis 的失常应用。尽管Redis将生成新的AOF文件替换旧AOF文件的性能命名为“AOF文件重写”,但实际上,AOF文件重写并不需要对现有的AOF文件进行任何读取、剖析或者写入操作,这个性能是通过读取服务器以后的数据器状态来实现的。因为生成的新AOF文件只蕴含还原以后数据库状态所必须的命令,所以新AOF文件不会节约任何硬盘空间。 (2)作为一种辅助性的保护伎俩,Redis不心愿AOF重写造成服务器无奈解决申请,所以Redis决定将AOF重写程序放到子过程里执行,这样做能够同时达到两个目标: 子过程进行AOF重写期间,服务器过程(父过程)能够持续解决命令申请。子过程带有服务器过程的数据正本,应用子过程而不是线程,能够在防止应用锁的状况下,保证数据的安全性。(3)因为子过程在进行AOF重写期间,服务器过程还须要持续解决命令申请,这就可能导致重写后的AOF文件和服务器的以后数据库状态不统一。为了解决此问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创立子过程之后开始应用,当Redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区,如下图: 这也就是说,在子过程执行AOF重写期间,服务器过程须要执行以下三个工作: 执行客户端发来的命令将执行后的写命令追加到AOF缓冲区将执行后的写命令追加到AOF重写缓冲区这样一来,从创立子过程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区外面。 (4)当子过程实现AOF重写工作后,它会向父过程发送一个信号,父过程在接到该信号之后,会调用一个信号处理函数,并执行以下工作: 将AOF重写缓冲区中的所有内容写入到新AOF文件中,这时新AOF文件所保留的数据库状态将和服务器以后的数据库状态统一。对新的AOF文件进行改名,原子地笼罩现有的AOF文件,实现新旧两个AOF文件的替换。4、何时触发AOF重写?手动发送“bgrewriteaof”指令;有两个配置项在管制AOF重写的触发机会:1)auto-aof-rewrite-min-size: 示意运行AOF重写时文件的最小大小,默认为64MB 2)auto-aof-rewrite-percentage: 这个值的计算方法是:以后AOF文件大小和上一次重写后AOF文件大小的差值,再除以上一次重写后AOF文件大小。也就是以后AOF文件比上一次重写后AOF文件的增量大小,和上一次重写后AOF文件大小的比值。AOF文件大小同时超出下面这两个配置项时,会触发AOF重写。参考资料1、redis设计与实现2、redis深度历险

March 29, 2022 · 1 min · jiezi

关于redis:Redis数据结构详解2redis中的字典dict

前提常识字典,又被称为符号表(symbol table)或映射(map),其实简略地能够了解为键值对key-value。比方Java的常见汇合类HashMap,就是用来存储键值对的。字典中的键(key)都是惟一的,因为这个个性,咱们能够依据键(key)查找到对应的值(value),又或者进行更新和删除操作。 字典dict的实现Redis的字典应用了哈希表作为底层实现,一个哈希表外面能够有多个哈希表节点,每个节点也保留了对应的键值对。Redis的字典dict构造如下: typedef struct dict { //类型特定函数 //是一个指向dictType构造的指针,能够使dict的key和value可能存储任何类型的数据 dictType *type; //公有数据 //公有数据指针,不是探讨的重点,暂疏忽 void *privdata; //哈希表 dictht ht[2]; //rehash 索引 //当 rehash 不在进行时,值为 -1 int rehashidx;}咱们重点关注两个属性就能够: ht 属性:能够看到ht属性是一个 size为2 的 dictht哈希表数组,在平时状况下,字典只用到 ht[0],ht[1] 只会在对 ht[0] 哈希表进行rehash时才会用到。 rehashidx 属性:它记录了rehash目前的进度,如果当初没有进行rehash,那么它的值为-1,能够了解为rehash状态的标识。 下图就是一个一般状态下的字典:理论的数据在 ht[0] 中存储;ht[1] 起辅助作用,只会在进行rehash时应用,具体作用包含rehash的内容咱们会在前面进行具体介绍。 哈希算法定位索引PS:如果你有HashMap的相干常识,晓得如何计算索引值,那么你能够跳过这一部分。 如果咱们当初模仿将 hash值从0到5的哈希表节点 放入 size为4的哈希表数组 中,也就是将蕴含键值对的哈希表节点放在哈希表数组的指定索引上。对应索引的计算公式:index = hash & ht[x].sizemask 看不懂没关系,能够简略的了解为hash值对哈希表数组的size值求余;比方下面 hash值为0的节点,0 % 4 = 0,所以放在索引0的地位上, hash值为1的节点,1 % 4 = 1,所以放在索引1的地位上, hash值为5的节点,5 % 4 = 1,也等于1,也会被调配在索引1的地位上,并且因为dictEntry节点组成的链表没有指向链表表尾的指针,所以会将新节点增加在链表的表头地位,排在已有节点的后面。 咱们把下面索引雷同从而造成链表的状况叫键抵触,而且因为造成了链表!那么就意味着查找等操作的复杂度变高了!例如你要查找hash=1的节点,你就只能先依据hash值找到索引为1的地位,而后找到hash=5的节点,再通过next指针能力找到最初的后果,也就意味着键抵触产生得越多,查找等操作破费的工夫也就更多。 ...

March 28, 2022 · 1 min · jiezi

关于redis:高颜值多平台功能强大的redis客户端

写在后面对于后端开发而言,日常工作中会常常接触到Redis。然而,常常应用命令行操作redis,效率绝对于低下。一款好的redis客户端,可能帮忙你疾速的剖析业务问题。   明天和大家分享一款Mac上优良的Redis客户端,可视化界面操作。 反对 Redis集群连贯Sentinel树视图自定义字体家族隐形键/值格局SSH私钥中文界面是一款颜值、性能兼备的Redis数据库客户端! 一眼看的见的服务器信息  连贯完服务器之后,能够一眼看到整个服务器的次要信息。 次要分为五大版块: 服务器:以后应用的redis版本、应用的os、redis启动的过程ID内存占用:已用内存、内存占用峰值、Lua占用内存 状态:目前客户端连接数、历史连接数、历史命令数 键值统计:以后连贯下db库的个数、应用到的keys、行将有多少个key过期以及残缺的redis info命令信息采集,具体的能够看:  https://www.redis.com.cn/comm...   右上角有个定时刷新的性能,有时候咱们须要实时监测服务器的信息,这个按钮会变得很有用。可见开发者的用心。 redis操作更敌对,便捷对于redis的各种类型的数据,查看、编辑操作相当敌对。同时也能够间接进行编辑。这里我以hash类型的数据进行截图。 对于数据,咱们常常会序列化成json格局。对于json格局,显示的也很敌对。 对于数据的搜寻,这里基于MATCH 命令进行搜寻,同时,对于数据的显示依据自定义分隔符进行了显示。 其余细节  redis客户端的所有操作日志,都看的见。对于,应用redis也有肯定的帮忙。 当然,你还是能够间接应用redis的命令来进行操作哦 同时,反对对每个连贯进行色彩标记。这样,各个连贯通过色彩能够更好的辨别开来。   软件反对深浅色模式的切换,同时反对连贯配置导出 写在最初所有的黑科技均在github、gitee同步进行开源了,感兴趣的小伙伴,能够点个star。 gitee:https://gitee.com/xiaoyi-codi...github:https://github.com/xiaoyi-cod...关注公众号(萧逸coding),后续更新告诉更及时。

March 28, 2022 · 1 min · jiezi

关于redis:Redigo-Envoy代理Redis场景下的错误提示

Redigo issue 579reply.go sliceHelper does not handle Redis errors within the slice#579 一、问题是什么在应用了 Envoy 代理 Redis 的场景下,执行MGET获取数据,其中某台 Redis 服务呈现了问题,Envoy返回了如下的谬误提醒upstream failure 而通过 redigo 返回来的却是 redigo: unexpected element type for ByteSlices, got type redis.Error显然封装的有问题,导致谬误提醒不够间接。 二、怎么解决的详见 pr 580 2.1 ByteSlices 与 MGET咱们看 issue 579 的示例代码,产生问题的调用就在这里: values, err := redis.ByteSlices(conn.Do("MGET", params...))那么问题本源出在哪里呢? 这就须要看下 redis.ByteSlices() 都做了什么。 我间接总结一下:遍历MGET返回的所有数据,进行类型断言,如果断言失败,返回错误信息。 那么问题就在于如下两点: 当 Envoy 代理 Redis 后,在 redis 没有响应或者其它状况下,Envoy 返回自定义的错误信息。(Envoy文档-upstream failure谬误)Redigo 粗犷的判断,对返回的值进行类型断言 v.([]byte), 切片断言失败,返回了自定义的谬误提醒,且谬误提醒中,没有带着 Envoy 返回的内容。2.2 switch 与 v.(type)if v.([]byte) 类型断言出是否为字节切片革新为 switch v := v.(type) 、 case []byte: 、 case Error: ...

March 24, 2022 · 1 min · jiezi

关于redis:Redigo-Envoy代理Redis场景下的错误提示

Redigo issue 579reply.go sliceHelper does not handle Redis errors within the slice#579 一、问题是什么在应用了 Envoy 代理 Redis 的场景下,执行MGET获取数据,其中某台 Redis 服务呈现了问题,Envoy返回了如下的谬误提醒upstream failure 而通过 redigo 返回来的却是 redigo: unexpected element type for ByteSlices, got type redis.Error显然封装的有问题,导致谬误提醒不够间接。 二、怎么解决的详见 pr 580 2.1 ByteSlices 与 MGET咱们看 issue 579 的示例代码,产生问题的调用就在这里: values, err := redis.ByteSlices(conn.Do("MGET", params...))那么问题本源出在哪里呢? 这就须要看下 redis.ByteSlices() 都做了什么。 我间接总结一下:遍历MGET返回的所有数据,进行类型断言,如果断言失败,返回错误信息。 那么问题就在于如下两点: 当 Envoy 代理 Redis 后,在 redis 没有响应或者其它状况下,Envoy 返回自定义的错误信息。(Envoy文档-upstream failure谬误)Redigo 粗犷的判断,对返回的值进行类型断言 v.([]byte), 切片断言失败,返回了自定义的谬误提醒,且谬误提醒中,没有带着 Envoy 返回的内容。2.2 switch 与 v.(type)if v.([]byte) 类型断言出是否为字节切片革新为 switch v := v.(type) 、 case []byte: 、 case Error: ...

March 24, 2022 · 1 min · jiezi

关于redis:如何使Codis存储成本降低90个推使用Pika做到了

作为一家数据智能公司,个推不仅领有海量的关系型数据,也积攒了丰盛的key-value等非关系型数据资源。个推采纳Codis保留大规模的key-value数据,随着公司kv类型数据的一直减少,应用原生的Codis搭建的集群所破费的老本越来越高。 在一些对性能响应要求不高的场景中,个推打算采纳新的存储和治理计划以无效兼顾老本与性能。通过选型,个推引入了360开源的存储系统Pika作为Codis的底层存储,以替换老本较高的codis-server,治理分布式kv数据集群。 将Pika接入到Codis的过程并非一帆风顺,为了更好地满足业务场景需要,个推进行了系列设计和革新工作。 本文是“大数据降本提效”专题的第四篇,为大家分享个推如何完满联合Pika和Codis,最终节俭90%大数据存储老本的实战经验。 Codis的四大组件在理解具体的迁徙实战之前,须要先初步意识下Codis的根本架构。Codis 是一个分布式 Redis解决方案,由codis-fe、codis-dashboard、codis-proxy、codis-server等四个组件形成。 其中,codis-server是Codis中最外围和根底的组件。基于Redis 3版本,codis-server进行了性能扩大,但其本质上还是依赖于高性能的Redis提供服务。codis-server扩大了基于slot的key存储性能(为了实现slot这个性能,codis-server会额定占用超出存储数据所需的内存),并可能在Codis集群的不同Group之间进行slot数据热迁徙。codis-fe则提供对运维比拟敌对的治理界面,不便对立治理多套的codis-dashboard。codis-dashboard负责管理slot、codis-proxy和ZooKeeper(或者etcd)等组件的数据一致性,整个集群的运维状态,数据的扩容缩容和组件的高可用,相似于k8s的api-server性能。codis-proxy次要提供给业务层面应用的拜访代理,负责解析申请路由并将key的路由信息路由到对应的后端group下面。此外,codis-proxy还有一个很重要的性能,即在通过codis-fe进行集群的扩缩容时,codis-proxy会依据group对应的slot的迁徙状态触发key迁徙的流程,可能实现在不中断业务服务的状况下热迁徙数据,以确保业务的可用性。Pika接入Codis的挑战咱们引入Pika次要是用来替换codis-server。作为360开源的类Redis存储系统,Pika底层选用RocksDB,它齐全兼容Redis协定,并且支流版本提供Codis的接入能力。但在引入Pika以及将数据迁徙到Codis的过程中,咱们发现Pika和Codis的联合并非设想中完满。 问题一:语法不对立在接入之前,咱们深刻查阅并比照了Pika和Codis源码,发现Pika实现的命令绝对较少,将Pika接入到Codis之后有些性能还是否失常应用有待察看。 位于pika_command.h头文件中的Pika (3.4.0版本) 源码: //Codis Slotsconst std::string kCmdNameSlotsInfo = "slotsinfo";const std::string kCmdNameSlotsHashKey = "slotshashkey";const std::string kCmdNameSlotsMgrtTagSlotAsync = "slotsmgrttagslot-async";const std::string kCmdNameSlotsMgrtSlotAsync = "slotsmgrtslot-async";const std::string kCmdNameSlotsDel = "slotsdel";const std::string kCmdNameSlotsScan = "slotsscan";const std::string kCmdNameSlotsMgrtExecWrapper = "slotsmgrt-exec-wrapper";const std::string kCmdNameSlotsMgrtAsyncStatus = "slotsmgrt-async-status";const std::string kCmdNameSlotsMgrtAsyncCancel = "slotsmgrt-async-cancel";const std::string kCmdNameSlotsMgrtSlot = "slotsmgrtslot";const std::string kCmdNameSlotsMgrtTagSlot = "slotsmgrttagslot";const std::string kCmdNameSlotsMgrtOne = "slotsmgrtone";const std::string kCmdNameSlotsMgrtTagOne = "slotsmgrttagone";codis-server反对的命令如下: {"slotsinfo",slotsinfoCommand,-1,"rF",0,NULL,0,0,0,0,0}, {"slotsscan",slotsscanCommand,-3,"rR",0,NULL,0,0,0,0,0}, {"slotsdel",slotsdelCommand,-2,"w",0,NULL,1,-1,1,0,0}, {"slotsmgrtslot",slotsmgrtslotCommand,5,"w",0,NULL,0,0,0,0,0}, {"slotsmgrttagslot",slotsmgrttagslotCommand,5,"w",0,NULL,0,0,0,0,0}, {"slotsmgrtone",slotsmgrtoneCommand,5,"w",0,NULL,0,0,0,0,0}, {"slotsmgrttagone",slotsmgrttagoneCommand,5,"w",0,NULL,0,0,0,0,0}, {"slotshashkey",slotshashkeyCommand,-1,"rF",0,NULL,0,0,0,0,0}, {"slotscheck",slotscheckCommand,0,"r",0,NULL,0,0,0,0,0}, {"slotsrestore",slotsrestoreCommand,-4,"wm",0,NULL,0,0,0,0,0}, {"slotsmgrtslot-async",slotsmgrtSlotAsyncCommand,8,"ws",0,NULL,0,0,0,0,0}, {"slotsmgrttagslot-async",slotsmgrtTagSlotAsyncCommand,8,"ws",0,NULL,0,0,0,0,0}, {"slotsmgrtone-async",slotsmgrtOneAsyncCommand,-7,"ws",0,NULL,0,0,0,0,0}, {"slotsmgrttagone-async",slotsmgrtTagOneAsyncCommand,-7,"ws",0,NULL,0,0,0,0,0}, {"slotsmgrtone-async-dump",slotsmgrtOneAsyncDumpCommand,-4,"rm",0,NULL,0,0,0,0,0}, {"slotsmgrttagone-async-dump",slotsmgrtTagOneAsyncDumpCommand,-4,"rm",0,NULL,0,0,0,0,0}, {"slotsmgrt-async-fence",slotsmgrtAsyncFenceCommand,0,"rs",0,NULL,0,0,0,0,0}, {"slotsmgrt-async-cancel",slotsmgrtAsyncCancelCommand,0,"F",0,NULL,0,0,0,0,0}, {"slotsmgrt-async-status",slotsmgrtAsyncStatusCommand,0,"F",0,NULL,0,0,0,0,0}, {"slotsmgrt-exec-wrapper",slotsmgrtExecWrapperCommand,-3,"wm",0,NULL,0,0,0,0,0}, {"slotsrestore-async",slotsrestoreAsyncCommand,-2,"wm",0,NULL,0,0,0,0,0}, {"slotsrestore-async-auth",slotsrestoreAsyncAuthCommand,2,"sltF",0,NULL,0,0,0,0,0}, {"slotsrestore-async-select",slotsrestoreAsyncSelectCommand,2,"lF",0,NULL,0,0,0,0,0}, {"slotsrestore-async-ack",slotsrestoreAsyncAckCommand,3,"w",0,NULL,0,0,0,0,0},此外,codis-server和Pika反对的语法也有所不同。例如,如果要查看某一节点上slot 1的详细信息,Codis与Pika执行的命令别离如下: 也就是说,咱们必须在codis-fe层命令调度与治理性能方面加上对Pika语法格局的反对。 ...

March 22, 2022 · 1 min · jiezi

关于redis:我用-Redis-干掉了一摞简历

如果你做后端,面试八成会被问到 Redis,尤其在一些大厂面试里更常见,而且他们不仅要求面试者能简略应用 Redis,还要深刻了解其底层实现原理,具备解决常见问题的能力。不夸大地说,后端开发的必备技能之一就是做到纯熟应用 Redis。 但我发现,在工作或面试时,大家还是存在不少各种对于实践、实战或源码方面的问题,比方:如何用 Redis 实现分布式锁?Redis 怎么解决过期键?缓存雪崩、穿透、热点问题怎么解决?长久化、集群计划怎么抉择?如何优雅地给 Redis 做键值剖析?等等。 这里,分享给你一张 Redis 问题画像图和一张 源码全景图,别离能做到帮你疾速定位问题对应的 Redis 主线模块及相干技术点和带你了解代码逻辑,从整体性把握 redis 源码架构,最终高效浏览源码。     第一张来自《Redis 核心技术与实战》,专栏不仅原理讲得透彻,实战性也强。深刻问题的本源,会带着大家敲代码测试和剖析了源码细节,晋升你对 Redis 的了解。订阅曾经22,000+ 了。 第二张来自《Redis 源码分析与实战》,总结了一条高效的 Redis 源码学习门路,会带你吃透 Redis 零碎设计思路,并把握计算机系统设计思维,和经典 C 语言的开发技巧。难得的是,作者还解析了不少 Redis 高频面试题目,为通过大厂的后端面试加码。 这两个专栏的作者都是蒋德钧,他是中科院计算所副研究员,长期致力于 Redis 钻研,与阿里、蚂蚁金服、百度、华为、中兴等公司发展了多种我的项目单干,具备 Redis 实战经验十分丰盛,还申请了 NVM(非易失内存)相干专利二十多项。 置信大多数人都是带着具体问题学 Redis 的,这些问题诚然重要,但如果只关注零散技术点,没有建设起残缺的常识框架,你的应用能力很难失去质的晋升。 所以,蒋老师在第一季专栏中高度总结出了“两大维度,三大主线”:前者指零碎维度和利用维度,后者就是高性能、高牢靠和高可扩大。  只有将零碎维度和利用维度两相结合,按 “利用场景驱动”和“典型案例驱动”两种形式学习,一个是“面”的梳理,一个是“点”的把握,能力透彻了解 Redis,建设起结构化的常识体系,疾速找到引发问题的关键因素,甚至整顿成 Checklist,作为遇到问题时信手拈来的“神机妙算”。 更难得的是,第一季专栏更新完,蒋老师还写了好几篇加餐,不仅分享了一些好用的运维工具,还解说了定制化客户端的开发方法,分享了一些经典学习材料。 当然,作为蒋老师时隔一年的回归之作,第二季 Redis 源码对咱们而言等同重要,因为其不仅是一份优良的 C 语言编程学习素材,能帮你把握编码标准和技巧,Redis 还是一个十分经典的内存数据库,设计与实现时,会波及单机键值数据库和分布式系统的关键技术,十分有助于学习和把握计算机系统设计思维,实现职业能力进阶。 另外,在数据类型上 Redis 也很丰盛,参加构建主从集群、切片集群,能够别离晋升 Redis 应用的可靠性和可扩展性。因而,针对 Redis 性能个性,蒋老师把源码课分成以下五大模块:● 第一个模块:数据结构。对于 Redis 次要数据结构的设计思维和实现,包含字符串的实现办法、内存紧凑型构造的设计、哈希表性能优化设计,以及 ziplist、quicklist、listpack、跳表的设计与实现等等。● 第二个模块:网络通信与执行模型。这里有 Redis server 的启动流程、高性能网络通信设计与实现、事件驱动框架的设计与实现、Redis 线程类型的设计和优化等等。● 第三个模块:缓存。你将理解常见缓存替换算法如何从原理转变为代码。● 第四个模块:可靠性保障。你将把握 RDB、AOF 的具体实现,分布式系统中 Raft一致性协定的设计实现,故障切换的要害代码实现等等。● 第五个模块:切片集群。你将学习到 Redis 切片集群中要害机制的设计与实现,包含Gossip通信协议、申请重定向、数据迁徙等等。在学习这五类模块中的要害源码外,蒋老师还介绍了对应的计算机系统设计思维,和 Redis 源码中应用的一些编程技巧,这样不便你利用到本人的程序开发中。 目前两个专栏的留言品质都很高,不仅蒋老师会解答大家的问题,读者本人也会互相切磋,光看评论区都能学到不少,口碑天然不错,截了一些供你参考:  ...

March 21, 2022 · 1 min · jiezi

关于redis:Redis数据结构详解1redis中的字符串SDS

前提常识咱们先从百科上摘下Redis的解释: Redis是一个应用 ANSI C 编写的开源、反对网络、基于内存、分布式、可选持久性的键值对存储数据库。(不必过多在意ANSI,它只是一个规范,你能够了解为晚期民间版本很多,起初对立了规范,大学课程里包含当初在用的都是标准化后的C语言版本) 没错!Redis的底层是由 C语言 实现的!大学不论是什么业余应该都有这个课,然而不论大家还有没有它的记忆,都不影响咱们接下来的学习哈哈哈~ redis第一步,字符串是根底回想当初学习Java,第一个学习的数据类型应该是根本类型,但redis里最根底的构造其实是字符串,简直哪里都有它。在mysql定义字段类型时,个别老哥会很中意varchar这个类型,因为万物皆可varchar(不探讨性能的状况下),其实redis就有点这个意思,很多构造的根底都是字符串。 下面提到Redis的底层是由C语言实现的,那岂不是间接拿C语言的字符串(以空字符结尾的字符数组,以下简称C字符串)来用就行? 我先答复了:不行,因为C语言作为晚期的编程语言,C字符串是有些“有余”或者说是须要补充的,尤其是像redis这样对速度要求严苛,字符串可能会被频繁批改的服务,于是redis在C字符串的根底上,本人结构了名为 简略动静字符串(simple dynamic string,SDS)的形象类型,用作redis本人的默认字符串。 (redis也不是就不必C字符串了,只会使用在一些无需对字符串值做批改的中央,例如打印日志时) C字符串简略介绍,嗯,简略。首先上图就是一个值为"yikun"的C字符串。不理解的小伙伴可能不晓得为什么前面多了个'\0',其实这个字符是空字符,也能够了解为C语言的“字符串终止符”。长度为N的字符串,会用长度为N+1的字符数组来示意,最初多进去的1长度就是专门用来存储空符'\0'的。 而后没了。C语言的字符串就是这么简略。不急,咱们持续往后说~ 那么C字符串有哪些“有余”呢?俗话说得好:知己知彼,屡战屡败。咱们得晓得C语言的“有余”,能力晓得redis为了补救这种状况,在SDS中做了什么措施。 1.C字符串并没有记录本身长度。2.会依据空字符'\0'来判断字符串是否完结。3.只能依据空字符'\0'来判断字符串是否完结。 2和3如同差不多,但意思其实是有细微差别的。(果然中华文化博大精深~) C字符串因为下面的“有余”会引起什么问题? 1.获取字符串长度复杂度高因为 有余1 和 有余3,所以C字符串只能通过遍历字符来获取长度。咱们这里用Java的for循环(只是用Java举例,理论是C语言实现的)简略阐明下C语言如何获取字符串长度。可见字符串越长,这个操作就越耗时,复杂度为O(N)。 2.缓冲区溢出 和 内存泄露因为 有余2,C字符串可能会在批改字符串时呈现问题,因为大家晓得内存是间断的,如果我要在字符串前面拼接新的内容,我首先要通过 内存重调配 来扩大底层数组的空间大小。 比方我要在字符串'yikun'后拼接'lucky',但当初前面紧挨存储的是'happy'字符串,所以我首先要扩大数组能力拼接'lucky',不然就把'happy'给笼罩掉,造成缓存区溢出了。 那如果我要缩短'yikun',改成'yi',倒是不必扩大数组。但我空的这部分又造成内存透露,所以还是须要进行 内存重调配。而 内存重调配 是个非常耗时的操作,如果下面的字符串批改频繁产生的话,对于性能的影响就会很大。 3.没方法存储二进制数据因为 有余3,如果咱们的数据自身就蕴含空字符串'\0',而代码逻辑是大公无私的,会认为是字符串终止的意思;而像图片、音频、视频、压缩文件这样的二进制数据是会蕴含空字符串'\0'的,所以可想而知,数据会呈现问题,可能只能辨认到后面的局部数据,而失落前面的内容。 所以针对下面的问题,redis的SDS的构造是怎么设计的?废话不多说,咱们间接来看下SDS的构造:直观来看,SDS多保护了 free 属性和 len 属性。len 属性用来记录buf数组中已应用字节的数量,同时也等于SDS所保留字符串的长度。而属性 free 是用来记录buf数组中未应用字节的数量。 SDS是如何依附引入两个属性值len、free来解决具体问题呢? 1.获取字符串长度当初能够间接拜访 len 属性来获取字符串长度,就好比Java属性的get办法,复杂度由原来的O(N)一下子降到了O(1)。而且这个属性是SDS在执行操作时主动实现的,咱们无需进行任何保护。 2.空间预调配 和 惰性空间开释下面咱们说内存重调配操作耗时,所以在须要对SDS进行空间扩大的时候,会调配额定的未应用空间。 上面是额定调配空间数量的公式:1.如果SDS批改之后,SDS的长度(len属性的值)小于1MB,那么就会调配和len属性同样大的未应用空间,也就是free属性会和len属性雷同,相当于将原数组长度double。2.如果SDS批改之后,SDS的长度(len属性的值)大于或等于1MB,那么就会调配固定的1MB未应用空间。 而free属性就决定了是不是须要进行额定的内存重调配操作,如果free为7,而你须要拼接的字符串长度只有5,那就不须要进行内存重调配操作,间接存储就能够。 free:太好了,我这空座就是给老弟你留的,快坐快坐~ 所以针对拼接操作来说,原本N次 肯定须要N次 的内存重调配操作,当初 最多只须要N次。(比方你第一次拼接后,free就大于后续要拼接的字符串长度之和了,那其实就只有1次内存重调配操作,所以说最多N次) 缩短操作也是同理,删掉N个字符后,我free就减少N,我先不做内存重调配操作,就先给你留着呗,万一你前面又做拼接是不是。所以总结:拼接时咱们做 空间预调配,缩短时咱们做 惰性空间开释,都是为了 缩小内存重调配操作。 ...

March 18, 2022 · 1 min · jiezi

关于redis:MOor搭建Redis可视化工具与监控

一、引言本篇文章次要介绍以下三点: 基于Linux零碎应用Docker装置RedisMod基于Windows零碎搭建Redis可视化工具RedisInsight基于Linux零碎应用Docker搭建Redis可视化监控二、装置RedisMod2.1 RedisMod简介首先介绍下RedisMod这个货色,它是一系列Redis的加强模块。有了RedisMod的反对,Redis的性能将变得十分弱小。目前RedisMod中蕴含了如下加强模块: RediSearch:一个功能齐全的搜索引擎;RedisJSON:对JSON类型的原生反对;RedisTimeSeries:时序数据库反对;RedisGraph:图数据库反对;RedisBloom:概率性数据的原生反对;RedisGears:可编程的数据处理;RedisAI:机器学习的实时模型治理和部署。2.2 RedisMod装置这里咱们是在Linux下,通过Docker快捷装置RedisMod,即装置的是增强版的Redis 应用如下命令下载RedisMod的镜像:docker pull redislabs/redismod:preview应用如下命令在容器中运行RedisMod服务:docker run -p 6379:6379 --name redismod \-v /mydata/redismod/data:/data \-d redislabs/redismod:preview到这里RedisMod就曾经下载并装置结束了,接下来就是搭建可视化工具了 三、搭建RedisInsight可视化工具3.1 RedisInsight简介RedisInsight是Redis官网出品的可视化管理工具(GUI),可用于交互、治理、监控你的Redis数据库。反对深色和浅色两种主题,界面十分炫酷!可反对String、Hash、Set、List、JSON等多种数据类型的治理,同时反对近程应用CLI性能,性能十分弱小! 上面是RedisInsight的一张深色主题应用效果图,Cool~ 3.2 RedisInsight装置下载RedisInsight安装包,下载地址:RedisInsight下载实现后间接装置即可,装置实现后在主界面点击 增加Redis数据库而后抉择手动增加,填写Redis的IP、端口、别名及相应信息3.3 RedisInsight应用关上方才增加的别名链接,即可操作数据库了,右上角会显示曾经装置的Redis加强模块当初咱们就能够操作Redis了,这里比方增加一个JSON键值对操作JSON也很不便,能够在界面上间接批改或新增数据主题目前提供了深色和浅色两种主题,都很Cool~如果界面操作还是不够满足需要,RedisInsight也提供了 CLI 命令操作RedisInsight还贴心的提供了命令查找帮忙,Command HelperRedisInsight还提供 Profiler 性能,开启后能够显示执行日志,比如说这里通过界面增加一个String键值对四、搭建Redis可视化监控4.1 Grafana简介Grafana 是用于可视化大型测量数据的开源程序,他提供了弱小和优雅的形式去创立、共享、浏览数据,界面好看,提供了丰盛的仪表盘和图表编辑的指标剖析,更偏差于展现数据,短少更多的告警与互动查问性能,可联合上面的 Prometheus 增强监控性能 4.2 Prometheus简介Prometheus(普罗米修斯) 是由 SoundCloud 开发的开源监控报警零碎和时序列数据库(TSDB),次要用于抓取数据和存储时序数据,监控、汇总、上报数据,查问、告警告诉治理等 4.3 Grafana、Prometheus装置下载Grafana的Docker镜像: docker pull grafana/grafana下载实现后运行Grafana: docker run -p 3000:3000 --name grafana \-d grafana/grafana而后下载Prometheus的Docker镜像: docker pull prom/prometheus在/mydata/prometheus/目录下创立Prometheus的配置文件prometheus.yml: global:scrape_interval: 5s运行Prometheus,把宿主机中的配置文件prometheus.yml挂载到容器中去: docker run -p 9090:9090 --name prometheus \-v /mydata/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \-d prom/prometheus进入grafana容器并装置redis-datasource插件: docker exec -it grafana /bin/bashgrafana-cli plugins install redis-datasourceredis-datasource插件装置实现后须要重启grafana服务: ...

March 17, 2022 · 1 min · jiezi

关于redis:Redis学习总结

一、数据类型Redis反对五种数据类型: 二、主从复制机制Redis反对主从复制,Redis的主从构造能够采纳一主多从或者级联构造 Redis主从复制能够依据是否是全量分为全量同步和增量同步。 1. 全量同步Redis全量复制个别产生在Slave初始化阶段,这时Slave须要将Master上的所有数据都复制一份。具体步骤如下: 1)从服务器连贯主服务器,发送SYNC命令;2)主服务器接管到SYNC命名后,开始执行BGSAVE命令生成RDB文件并应用缓冲区记录尔后执行的所有写命令;3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间持续记录被执行的写命令;4)从服务器收到快照文件后抛弃所有旧数据,载入收到的快照;5)主服务器快照发送结束后开始向从服务器发送缓冲区中的写命令;6)从服务器实现对快照的载入,开始接管命令申请,并执行来自主服务器缓冲区的写命令;实现下面几个步骤后就实现了从服务器数据初始化的所有操作,从服务器此时能够接管来自用户的读申请。 2. 增量同步Redis增量复制是指Slave初始化后开始失常工作时主服务器产生的写操作同步到从服务器的过程。 增量复制的过程次要是主服务器每执行一个写命令就会向从服务器发送雷同的写命令,从服务器接管并执行收到的写命令。 3. 主从同步策略主从刚刚连贯的时候,进行全量同步;全同步完结后,进行增量同步。当然,如果有须要,slave 在任何时候都能够发动全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不胜利,要求从机进行全量同步。 Redis 2.8 当前提供了PSYNC优化了断线重连的效率 三、集群计划四种支流的redis架构计划 客户端分片Redis ClusterTwemproxyProxy + Redis Cluster四、长久化形式redis提供了两种长久化的形式,别离是RDB(Redis DataBase)和AOF(Apend Only File)。 1. RDB概念:是一种快照式的长久化办法,将某一时刻的数据长久化到磁盘中。过程如下 redis在进行数据长久化的过程中,会先将数据写入到一个临时文件中,待长久化过程都完结了,才会用这个临时文件替换上次长久化好的文件。正是这种个性,让咱们能够随时来进行备份,因为快照文件总是残缺可用的。对于RDB形式,redis会独自创立(fork)一个子过程来进行长久化,而主过程是不会进行任何IO操作的,这样就确保了redis极高的性能。如果须要进行大规模数据的复原,且对于数据恢复的完整性不是十分敏感,那RDB形式要比AOF形式更加的高效。优缺点: 长处:最大化redis的性能,复原大的数据集时速度更快;毛病:意外宕机时,会失落数据(取决于配置的save工夫点) 2. AOFAOF形式是将执行过的写指令记录下来,在数据恢复时依照丛前到后的程序再将指令执行一遍。 AOF命令以redis协定追加保留每次写的操作到文件开端.Redis还能对AOF文件进行后盾重写,使得AOF文件的体积不至于过大.默认的AOF长久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种状况下,redis依然能够放弃很好的解决性能,即便redis故障,也只会失落最近1秒钟的数据。如果在追加日志时,恰好遇到磁盘空间满、inode满或断电等状况导致日志写入不残缺,也没有关系,redis提供了redis-check-aof工具,能够用来进行日志修复。因为采纳了追加形式,如果不做任何解决的话,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留能够复原数据的最小指令集。举个例子或者更形象,如果咱们调用了100次INCR指令,在AOF文件中就要存储100条指令,但这显著是很低效的,齐全能够把这100条指令合并成一条SET指令,这就是重写机制的原理。在进行AOF重写时,依然是采纳先写临时文件,全副实现后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性。优缺点 长处:毛病:体积较大,数据长久慢五、内存回收机制1. 过期删除策略定时删除:对每一个设置了过期工夫的key都会创立一个定时器,一旦达到过期工夫就立刻删除。 长处:可立刻革除过期的数据,对内存敌对毛病:占用了大量CPU资源去解决过期数据,会影响Redis的吞吐量和响应工夫。惰性删除:当拜访一个key时,才判断是否过期,过期则删除。 长处:最大限度地节俭CPU资源。毛病:对内存非常不敌对,极其状况会呈现大量过期key没有被再次拜访,因而不会被革除,导致占用了大量的内存。定期删除:每隔一段时间,扫描redis中过期key字段,并革除局部过期的key。 该策略是前两者的一个折中计划,还能够通过调整定时扫描的工夫距离和每次扫描的限定耗时,在不同状况下使得CPU和内存资源达到最优的均衡成果2. 内存淘汰策略no-enviction(默认):禁止驱赶数据。不会删除任何数据,回绝所有写入操作并返回客户端错误信息,此时Redis只响应读操作。allkeys-lru:当内存不足以包容新写入数据时,在键空间(server.db[i].dict)中,移除最近起码应用的 key。allkeys-random:在键空间(server.db[i].dict)中,随机移除某个 key。volatile-lru:在设置了过期工夫的键空间(server.db[i].expires)中,移除最近起码应用的 key。volatile-random:在设置了过期工夫的键空间(server.db[i].expires)中,随机移除某个 key。volatile-ttl:在设置了过期工夫的键空间(server.db[i].expires)中,有更早过期工夫的 key 优先移除。 注: lru:Least Recently Used,最近起码应用lfu:Least Frequently Used,最不常常应用六、分布式锁数据库乐观锁;基于Redis的分布式锁;基于ZooKeeper的分布式锁。七、缓存穿透、雪崩、击穿1. 缓存穿透缓存穿透是指查问一个肯定不存在的数据,因为缓存不命中时须要从数据库查问,查不到数据则不写入缓存,这将导致这个不存在的数据每次申请都要到数据库去查问,造成缓存穿透。解决办法: 对所有可能查问的参数以hash模式存储,在管制层先进行校验,不合乎则抛弃。还有最常见的则是采纳布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个肯定不存在的数据会被这个bitmap拦挡掉,从而防止了对底层存储系统的查问压力。也能够采纳一个更为简略粗犷的办法,如果一个查问返回的数据为空(不论是数 据不存在,还是系统故障),咱们依然把这个空后果进行缓存,但它的过期工夫会很短,最长不超过五分钟。 2. 缓存雪崩如果缓存集中在一段时间内生效,产生大量的缓存穿透,所有的查问都落在数据库上,造成了缓存雪崩。 解决办法:数据预热、缓存不过期、做二级缓存,或者双缓存策略 3. 缓存击穿缓存在某个工夫点过期的时候,恰好在这个工夫点对这个Key有大量的并发申请过去,这些申请发现缓存过期个别都会从后端DB加载数据并回设到缓存,这个时候大并发的申请可能会霎时把后端DB压垮。 解决办法:应用互斥锁等

March 13, 2022 · 1 min · jiezi

关于redis:Redis-replication-中的探活

redis 在运行过程中须要一些探活机制来保障对另一端的感知能力。 slave 重建 replication 阶段当因为网络或其余起因导致主从 link 断开后,slave 会尝试重建 replication 。在这个过程中,slave 的复制状态机 repl_state 变量会通过一系列流传,最终为 REPL_STATE_CONNECTED 状态。 repl_state 在很多状态的停留时间都有超时设定,以便出错后尽早是否资源。server.repl_transfer_lastio 变量起到了计时器的作用,它记录了slave 上一次从 master 进行 io 交互(即读写事件)的工夫。 REPL_STATE_CONNECTING 超时在 REPL_STATE_CONNECTING 阶段,slave 会主从 connet master,该过程应用了非阻塞 IO,在replicationCron 函数里周期性查看是否超时。 /* Non blocking connection timeout? */if (server.masterhost && (server.repl_state == REPL_STATE_CONNECTING || slaveIsInHandshakeState()) && (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout){ serverLog(LL_WARNING,"Timeout connecting to the MASTER..."); cancelReplicationHandshake();}REPL_STATE_TRANSFER 超时在 REPL_STATE_TRANSFER 阶段,slave 会从 master 接管 rdb 文件,通常不会在一次 read 里实现,所以须要在 replicationCron 函数里周期性查看该过程是否超时。 /* Bulk transfer I/O timeout? */if (server.masterhost && server.repl_state == REPL_STATE_TRANSFER && (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout){ cancelReplicationHandshake();}如果 rdb 数据量过大,可能须要做一些额定解决,上面进行阐明。 ...

March 13, 2022 · 3 min · jiezi

关于redis:小白也能看懂的缓存雪崩穿透击穿

大家好,我是七淅(xī)。 作为后端开发,我想缓存是大家再相熟不过的货色了。 本文会介绍呈现缓存雪崩、穿透和击穿的业务背景、解决方案和对业务可靠性解决。当时阐明,最佳解决方案肯定须要结合实际业务调整,不同业务的解决不完全相同 其实我在网上也看过不少对于缓存雪崩、穿透、击穿介绍,不晓得是不是大家所做业务的不同,发现有不少小伙伴有以下疑难,比方: 加随机工夫过期后,如果拜访工夫刚好就是加了随机工夫后的数据,这样岂不是白加了随机工夫?热点数据不过期,那岂不是有越来越多的脏数据?就以上问题,我都会在文中一一解释,以下说的缓存都指 Redis。 我争取把这一高频面试题讲明确,如果大家看后能在这块内容和面试官背后谈笑自若,那你就是最靓的仔。 上面,我就开始进入正题啦。 1. 缓存雪崩即缓存同一时间大面积的生效,这个时候来了一大波申请,都怼到数据库上,最初数据库解决不过去崩了。 1.1 业务场景举例APP 首页有大量热点数据,在某大型流动期间,针对不同时间段须要展现不同的首页数据。 比方在 0 点时须要替换新的首页数据,此时旧首页数据过期,新首页数据刚开始加载。 而 0 点正在有个小流动开始,少量申请涌入。因为新数据刚开始加载,申请少数没有命中缓存,申请到了数据库,最初就把数据库打挂了。 1.2 解决方案再强调一下,所谓的解决方案是须要依据理论业务调整,不同业务的解决不完全相同 1.2.1 办法一常见形式就是给过期工夫加个随机工夫。 留神这个随机工夫不是几秒哈,能够长达几分钟。因为如果数据量很大,依照上述例子,加上 Redis 是单线程解决数据的。那么几秒的缓冲不肯定可能保障新数据都被加载实现。 所以过期工夫宁愿设置长一点,也好过短一点。反正最初都是会过期掉,最终成果是一样的。 而且过期工夫范畴加大,key 会更加扩散,这样也是肯定水平缩短 Redis 在过期 key 时候的阻塞工夫。 而至于文章结尾说的:「如果拜访工夫刚好就是加了随机工夫后的数据,这样岂不是白加了随机工夫」。 当初你联合上例流动的例子,它还会是一个问题吗?联合业务,肯定要联合业务。 1.2.2 办法二加互斥锁,但这个计划会导致吞吐量显著降落。所以还是要看理论业务,像上述例子就不合实用 1.2.3 办法三热点数据不设置过期。不过期的话,失常业务申请天然就不会打到数据库了。 那新的问题又来了,不过期有脏数据,怎么办? 很简略,流动整体完结后再删除嘛。 那像上述例子,能够怎么解决呢?—— 抉择办法一;或者提前把 0 点须要的新数据加载进 Redis,不用等到 0 点才去加载,这样也是能够的 2. 缓存击穿缓存击穿是指一个热点 key 过期或被删除后,导致线上本来能命中该热点 key 的申请,霎时大量地打到数据库上,最终导致数据库被击垮。 有种千里之堤,溃于蚁穴的感觉。 2.1 业务场景举例呈现状况个别是误操作,比方设置错了过期工夫、误删除导致的。 谁还没误操作过呢,删库跑路理解一下。反正我误删过测试库的数据,幸好人没事,狗头保命。2.2 解决方案办法一代码问题,该 review 的 review。 热点数据到底要不要过期,什么时候过期要明确 既然是热点数据,大概率是外围流程。那么该保障的外围性能还是须要保障的,缩小犯错机会。万一出问题,那就是用户的一波输入了。 办法二线上误操作的事件,该增强权限治理的增强,特地是线上权限,肯定须要审核,以防手抖。 PS:若有帮忙心愿大家能够点赞、在看、转发轻易来一份激励吧,这对我真得很重要,非常感谢~3. 缓存穿透缓存穿透是指:客户端申请缓存和数据库中不存在的数据,导致所有的申请都打到数据库上。如果申请很多,数据库依旧会挂得明明白白。 3.1 业务场景举例数据库主键 id 都是负数,而后客户端发动了 id = -1 的查问一个查问接口,有一个状态字段 status,其实 0 示意开始、1 示意完结。后果有申请始终发 status=3 的申请过去3.2 解决方案3.2.1 办法一做好参数校验,对于不合理的参数要及时 return 完结 ...

March 8, 2022 · 1 min · jiezi