关于redis:为什么LRU算法原理和代码实现不一样

(1) Redis里缓存有哪些淘汰策略内存淘汰策略解释备注noeviction不进行数据淘汰 allkeys-random在所有key里随机筛选数据 allkeys-lru在所有key里筛选最近最久未应用的数据 allkeys-lfu在所有key里筛选最近起码应用的数据Redis 4.0 新增volatile-ttl在有过期工夫key里依据过期工夫的先后筛选 volatile-random在有过期工夫key里随机筛选数据 volatile-lru在有过期工夫key里筛选最近最久未应用的数据 volatile-lfu在有过期工夫key里筛选最近起码应用的数据Redis 4.0 新增lru (Least Recently Used) 最近最久未应用lfu (Least Frequently Used) 最近起码应用 在redis3.0之前,默认淘汰策略是volatile-lru;在redis3.0及之后(包含3.0),默认淘汰策略是noeviction。 在3.0及之后的版本,Redis 在应用的内存空间超过 maxmemory 值时,并不会淘汰数据。 对应到 Redis 缓存,也就是指,一旦缓存被写满了,再有写申请来时,Redis 不再提供服务,而是间接返回谬误。 (1.1) Redis内存淘汰机制如何启用Redis 的内存淘汰机制是如何启用近似 LRU 算法的 和Redis配置文件redis.conf中的两个配置参数无关: maxmemory,该配置项设定了 Redis server 能够应用的最大内存容量,一旦 server 应用的理论内存量超出该阈值时,server 就会依据 maxmemory-policy 配置项定义的策略,执行内存淘汰操作; maxmemory-policy,该配置项设定了 Redis server 的内存淘汰策略,次要包含近似 LRU 算法、LFU 算法、按 TTL 值淘汰和随机淘汰等几种算法。 (2) 缓存淘汰算法/页面置换算法原理(2.1) LRULRU 算法背地的想法十分奢侈:它认为刚刚被拜访的数据,必定还会被再次拜访。 抉择最近最久未被应用的数据进行淘汰。 长处: 简略、高效有余: 可能造成缓存净化。 缓存净化:在一些场景下,有些数据被拜访的次数非常少,甚至只会被拜访一次。当这些数据服务完拜访申请后,如果还持续留存在缓存中的话,就只会白白占用缓存空间。 典型场景:全表扫描,对所有数据进行一次读取,每个数据都被读取到了, (2.2) LFU记录数据被拜访的频率,抉择在最近应用起码的数据进行淘汰。 (3) Redis里缓存淘汰算法实现(3.1) Redis-LRULRU 算法在理论实现时,须要用链表治理所有的缓存数据,这会带来额定的空间开销。 ...

January 30, 2023 · 17 min · jiezi

关于redis:从代码实现看分布式锁的原子性保证

理解一下Redis命令处理过程 及 Redis分布式锁 (1) Redis实现分布式锁通过Redis SET key value NX 能够简略地实现分布式锁。 127.0.0.1:6379> SET key_lock value1 NXOK127.0.0.1:6379>127.0.0.1:6379> SET key_lock value1 NX(nil)127.0.0.1:6379>在解锁时能够先判断key是否存在,而后对比值是否相等,相等后再删除key,开释锁。 有没有想过一个问题,Redis SET key value NX 是怎么保障分布式锁的原子性的? (2) Redis命令的处理过程Redis Server 和客户端建设连贯后,就会在事件驱动框架中注册可读事件,这就对应了客户端的命令申请。而对于整个命令解决的过程来说,我认为次要能够分成五个阶段,它们别离对应了 Redis 源码中的不同函数。 接管申请,对应 acceptTcpHandler 函数命令读取,对应 readQueryFromClient 函数;命令解析,对应 processInputBuffer 函数;命令执行,对应 processCommand 函数;后果返回,对应 addReply 函数;(2.1) 接管申请阶段-acceptTcpHandler()// file: src/networking.c /* * 接管tcp处理器 * * @param *el * @param fd * @param *privdata * @param mask */void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { // ... // 接管tcp申请 cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); // ... // 接管通用解决 acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip);} acceptTcpHandler -> anetTcpAccept -> acceptCommonHandler -> createClient -> readQueryFromClient(2.2) 命令读取阶段-readQueryFromClient()readQueryFromClient 函数会从客户端连贯的 socket 中,读取最大为 readlen 长度的数据,readlen 值大小是宏定义 PROTO_IOBUF_LEN。该宏定义是在server.h文件中定义的,默认值为 16KB。 ...

January 29, 2023 · 6 min · jiezi

关于redis:Redis的序列化方式

1.JdkSerializationRedisSerializer用JdkSerializationRedisSerializer序列化,被序列化的对象必须实现Serializable接口,否则就会抛出异样。在存储内容时,除了属性外,还会存其余内容,占用空间大,且不易浏览 2.Jackson2JsonRedisSerializerJackson2JsonRedisSerializer将对象序列化为json格局的字符串,存储字节少,容易浏览。不须要实现Serializable接口,个别应用它序列化须要保留的对象 3.StringRedisSerializer即可个别key,value都是String类型,应用StringRedisSerializer即可 1.Redis中StringRedisTemplate默认应用的是StringRedisSerializer, RedisTemplate默认应用的是JdkSerializationRedisSerializer

January 29, 2023 · 1 min · jiezi

关于redis:Redis缓存的主要异常及解决方案

作者:京东物流 陈昌浩 1 导读Redis 是以后最风行的 NoSQL数据库。Redis次要用来做缓存应用,在进步数据查问效率、爱护数据库等方面起到了关键性的作用,很大水平上进步零碎的性能。当然在应用过程中,也会呈现一些异样情景,导致Redis失去缓存作用。 2 异样类型异样次要有 缓存雪崩 缓存穿透 缓存击穿。 2.1 缓存雪崩2.1.1 景象缓存雪崩是指大量申请在缓存中没有查到数据,间接拜访数据库,导致数据库压力增大,最终导致数据库解体,从而波及整个零碎不可用,如同雪崩一样。 2.1.2 异样起因缓存服务不可用。缓存服务可用,然而大量KEY同时生效。2.1.3 解决方案1.缓存服务不可用 redis的部署形式次要有单机、主从、哨兵和 cluster模式。 单机 只有一台机器,所有数据都存在这台机器上,当机器出现异常时,redis将生效,可能会导致redis缓存雪崩。主从 主从其实就是一台机器做主,一个或多个机器做从,从节点从主节点复制数据,能够实现读写拆散,主节点做写,从节点做读。 长处:当某个从节点异样时,不影响应用。 毛病:当主节点异样时,服务将不可用。哨兵 哨兵模式也是一种主从,只不过减少了哨兵的性能,用于监控主节点的状态,当主节点宕机之后会进行投票在从节点中从新选出主节点。 长处:高可用,当主节点异样时,主动在从节点当中抉择一个主节点。 毛病:只有一个主节点,当数据比拟多时,主节点压力会很大。cluster模式 集群采纳了多主多从,依照肯定的规定进行分片,将数据别离存储,肯定水平上解决了哨兵模式下单机存储无限的问题。 长处:高可用,配置了多主多从,能够使数据分区,去中心化,减小了单台机子的累赘. 毛病:机器资源应用比拟多,配置简单。小结 从高可用得角度思考,应用哨兵模式和cluster模式能够避免因为redis不可用导致的缓存雪崩问题。2.大量KEY同时生效 能够通过设置永不生效、设置不同生效工夫、应用二级缓存和定时更新缓存生效工夫 设置永不生效 如果所有的key都设置不生效,不就不会呈现因为KEY生效导致的缓存雪崩问题了。redis设置key永远无效的命令如下: PERSIST key 毛病:会导致redis的空间资源需要变大。设置随机失效工夫 如果key的生效工夫不雷同,就不会在同一时刻生效,这样就不会呈现大量拜访数据库的状况。 redis设置key无效工夫命令如下: Expire key 示例代码如下,通过RedisClient实现/*** 随机设置小于30分钟的生效工夫* @param redisKey* @param value*/private void setRandomTimeForReidsKey(String redisKey,String value){//随机函数Random rand = new Random();//随机获取30分钟内(30*60)的随机数int times = rand.nextInt(1800);//设置缓存工夫(缓存的key,缓存的值,生效工夫:单位秒)redisClient.setNxEx(redisKey,value,times);}应用二级缓存 二级缓存是应用两组缓存,1级缓存和2级缓存,同一个Key在两组缓存里都保留,然而他们的生效工夫不同,这样1级缓存没有查到数据时,能够在二级缓存里查问,不会间接拜访数据库。 示例代码如下:public static void main(String[] args) {CacheTest test = new CacheTest();//从1级缓存中获取数据String value = test.queryByOneCacheKey("key");//如果1级缓存中没有数据,再二级缓存中查找if(StringUtils.isBlank(value)){value = test.queryBySecondCacheKey("key");//如果二级缓存中没有,从数据库中查找if(StringUtils.isBlank(value)){value =test.getFromDb();//如果数据库中也没有,就返回空if(StringUtils.isBlank(value)){System.out.println("数据不存在!");}else{//二级缓存中保留数据test.secondCacheSave("key",value);//一级缓存中保留数据test.oneCacheSave("key",value);System.out.println("数据库中返回数据!");}}else{//一级缓存中保留数据test.oneCacheSave("key",value);System.out.println("二级缓存中返回数据!");}}else {System.out.println("一级缓存中返回数据!");}}异步更新缓存工夫 每次拜访缓存时,启动一个线程或者建设一个异步工作来,更新缓存工夫。 示例代码如下:public class CacheRunnable implements Runnable {private ClusterRedisClientAdapter redisClient;/*** 要更新的key*/public String key;public CacheRunnable(String key){this.key =key;}@Overridepublic void run() {//更细缓存工夫redisClient.expire(this.getKey(),1800);}public String getKey() {return key;}public void setKey(String key) {this.key = key;}}public static void main(String[] args) {CacheTest test = new CacheTest();//从缓存中获取数据String value = test.getFromCache("key");if(StringUtils.isBlank(value)){//从数据库中获取数据value = test.getFromDb("key");//将数据放在缓存中test.oneCacheSave("key",value);//返回数据System.out.println("返回数据");}else{//异步工作更新缓存CacheRunnable runnable = new CacheRunnable("key");runnable.run();//返回数据System.out.println("返回数据");}}3.小结 下面从服务不可用和key大面积生效两个方面,列举了几种解决方案,下面的代码只是提供一些思路,具体实施还要思考到现实情况。当然也有其余的解决方案,我这里举例是比拟罕用的。毕竟现实情况,变幻无穷,没有最好的计划,只有最实用的计划。 ...

January 29, 2023 · 2 min · jiezi

关于redis:Redis-IO多线程

在2020年5月推出的 Redis 6.0 版本中,Redis 在执行模型中应用了多线程来解决 IO 工作,这样设计的目标,是为了充分利用以后服务器的多核个性,应用多核运行多线程,让多线程帮忙减速数据读取、命令解析以及数据写回的速度,晋升 Redis 整体性能。 那么,这些多线程具体是在什么时候启动,又是通过什么形式来解决 IO 申请的呢? (1) IO多线程Redis里的IO多线程是指Redis Server读取客户端申请或者向客户端写数据时,应用多个线程,利用CPU资源,放慢整体读写速度。 (2) IO多线程的原理IO多线程的原理是在CPU资源、内存资源利用不充沛的状况下,开启多个线程能够充分利用CPU资源,放慢整体读写速度。 (3) 源码解析// file: server.c/** * main办法 */int main(int argc, char **argv) { // InitServerLast();}// file: server.c/** * 服务器初始化的一些步骤须要最初实现(在加载模块之后)。 * 具体来说,因为 ld.so 中的竞争谬误导致线程创立,其中线程本地存储初始化与 dlopen 调用发生冲突。 * see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */void InitServerLast() { // bioInit(); // 初始化IO线程 initThreadedIO(); set_jemalloc_bg_thread(server.jemalloc_bg_thread); server.initial_memory_usage = zmalloc_used_memory();}// file: networking.c/** * 初始化线程 I/O 所需的数据结构。 */void initThreadedIO(void) { // io_threads_active 初始化为 0,示意 IO 线程还没有被激活 server.io_threads_active = 0; /* We start with threads not active. */ // 如果用户抉择了单个线程,则不要生成任何线程:咱们将间接从主线程解决 I/O。 // 只有1个主 IO 线程 if (server.io_threads_num == 1) return; // 最多有128个IO线程 if (server.io_threads_num > IO_THREADS_MAX_NUM) { serverLog(LL_WARNING,"Fatal: too many I/O threads configured. " "The maximum number is %d.", IO_THREADS_MAX_NUM); exit(1); } // 生成并初始化 I/O 线程。 for (int i = 0; i < server.io_threads_num; i++) { // 咱们为包含主线程在内的所有线程所做的事件。 // 链表 io_threads_list[i] = listCreate(); if (i == 0) continue; /* Thread 0 is the main thread. */ // 咱们只为额定的线程做的事件。 pthread_t tid; // 初始化io_threads_mutex数组 pthread_mutex_init(&io_threads_mutex[i],NULL); // 初始化io_threads_pending数组 io_threads_pending[i] = 0; pthread_mutex_lock(&io_threads_mutex[i]); /* Thread will be stopped. */ // 调用pthread_create函数创立IO线程,线程运行函数为IOThreadMain if (pthread_create(&tid,NULL,IOThreadMain,(void*)(long)i) != 0) { serverLog(LL_WARNING,"Fatal: Can't initialize IO thread."); exit(1); } // 初始化io_threads数组,设置值为线程标识 io_threads[i] = tid; }}// file: networking.c/** * */void *IOThreadMain(void *myid) { /* The ID is the thread number (from 0 to server.iothreads_num-1), and is * used by the thread to just manipulate a single sub-array of clients. */ long id = (unsigned long)myid; char thdname[16]; snprintf(thdname, sizeof(thdname), "io_thd_%ld", id); redis_set_thread_title(thdname); redisSetCpuAffinity(server.server_cpulist); makeThreadKillable(); while(1) { /* Wait for start */ for (int j = 0; j < 1000000; j++) { if (io_threads_pending[id] != 0) break; } /* Give the main thread a chance to stop this thread. */ if (io_threads_pending[id] == 0) { pthread_mutex_lock(&io_threads_mutex[id]); pthread_mutex_unlock(&io_threads_mutex[id]); continue; } serverAssert(io_threads_pending[id] != 0); if (tio_debug) printf("[%ld] %d to handle\n", id, (int)listLength(io_threads_list[id])); /* Process: note that the main thread will never touch our list * before we drop the pending count to 0. */ listIter li; // 链表节点 listNode *ln; // 获取IO线程要解决的客户端列表 listRewind(io_threads_list[id],&li); // 遍历链表 while((ln = listNext(&li))) { // 获取链表节点的值 也就是一个客户端 client *c = listNodeValue(ln); if (io_threads_op == IO_THREADS_OP_WRITE) { // 写操作 writeToClient(c,0); // 将数据写到客户端 } else if (io_threads_op == IO_THREADS_OP_READ) { // 读操作 readQueryFromClient(c->conn); // 取数据 } else { serverPanic("io_threads_op value is unknown"); } } // 解决完所有客户端后,清空链表 listEmpty(io_threads_list[id]); // 将该线程的待处理工作数量设置为0 io_threads_pending[id] = 0; if (tio_debug) printf("[%ld] Done\n", id); }}每一个IO线程运行时,都会一直查看是否有期待它解决的客户端。如果有,就依据操作类型,从客户端读取数据或是将数据写回客户端。 这也是为什么咱们把这些线程称为IO线程的起因。 ...

January 28, 2023 · 10 min · jiezi

关于redis:Redis的设计与实现6压缩列表

压缩列表 (ziplist) 是列表键和哈希键的底层实现之一. 当一个列表键只蕴含大量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比拟短的字符串, 那么 Redis 就会应用压缩列表来做列表键的底层实现.当一个哈希键只蕴含大量键值对, 并且每个键值对的键和值要么就是小整数值, 要么就是长度比拟短的字符串, 那么 Redis 就会应用压缩列表来做哈希键的底层实现.1. 压缩列表的形成压缩列表是 Redis 为了节约内存而开发的, 由一系列非凡编码的间断内存块组成的程序型 (sequential) 数据结构.一个压缩列表能够蕴含任意多个节点 (entry) , 每个节点能够保留一个字节数组或者一个整数值. <!--more--> 压缩列表的整体布局: <zlbytes><zltail><zllen><entry><entry><zlend> zlbytes uint32_t 4 字节 记录整个压缩列表占用的内存字节数: 在对压缩列表进行内存重调配, 或者计算 zlend 的地位时应用. zltail uint32_t 4 字节 记录压缩列表表尾节点间隔压缩列表的起始地址有多少字节: 通过这个偏移量,程序毋庸遍历整个压缩列表就能够确定表尾节点的地址. zllen uint16_t 2 字节 记录了压缩列表蕴含的节点数量: 当这个属性的值小于 UINT16_MAX (65535)时, 这个属性的值就是压缩列表蕴含节点的数量; 当这个值等于 UINT16_MAX 时, 节点的实在数量须要遍历整个压缩列表能力计算得出. entryX 列表节点 不定 压缩列表蕴含的各个节点,节点的长度由节点保留的内容决定. zlend uint8_t 1 字节 非凡值 0xFF (十进制 255 ),用于标记压缩列表的末端. 2 压缩列表节点的形成每个压缩列表节点能够保留一个字节数组或者一个整数值, 其中, 字节数组能够是以下三种长度的其中一种: ...

January 28, 2023 · 2 min · jiezi

关于redis:Redis执行模型Redis是单线程的吗

Redis的执行模型,是指 Redis 运行时应用的过程、子过程和线程的个数,以及它们各自负责的工作工作。 咱们常常会听到一个问题: Redis 到底是不是一个单线程的程序? 先来看 Redis server 启动时的过程运行。 (1) Redis过程创立在启动 Redis 实例时 ./redis-server ../redis.conf这个命令后,它理论会调用fork零碎调用函数,最终会调用Redis Server的main函数,来新建一个过程。 运行 Redis server 后,咱们会看到 Redis server 启动后的日志输入会打印到终端屏幕上 [weikeqin@bogon src]$ ./redis-server77405:C 27 Jan 2023 22:11:02.194 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo77405:C 27 Jan 2023 22:11:02.195 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=77405, just started77405:C 27 Jan 2023 22:11:02.195 # Warning: no config file specified, using the default config. In order to specify a config file use ./redis-server /path/to/redis.conf77405:M 27 Jan 2023 22:11:02.197 * Increased maximum number of open files to 10032 (it was originally set to 256). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.0.9 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 77405 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'77405:M 27 Jan 2023 22:11:02.203 # Server initialized77405:M 27 Jan 2023 22:11:02.203 * Loading RDB produced by version 6.0.977405:M 27 Jan 2023 22:11:02.203 * RDB age 284987 seconds77405:M 27 Jan 2023 22:11:02.203 * RDB memory usage when created 0.96 Mb77405:M 27 Jan 2023 22:11:02.204 * DB loaded from disk: 0.001 seconds77405:M 27 Jan 2023 22:11:02.204 * Ready to accept connectionsRedis 过程创立开始运行后,它就会从 main 函数开始执行。 ...

January 27, 2023 · 5 min · jiezi

关于redis:Redis的设计与实现5整数集合

整数汇合(intset)是汇合键的底层实现之一: 当一个汇合只蕴含整数值元素, 并且这个汇合的元素数量不多时, Redis 就会应用整数汇合作为汇合键的底层实现. 整数汇合 (intset) 是 Redis 用于保留整数值的汇合形象数据结构, 它能够保留类型为 int16_t , int32_t 或者 int64_t 的整数值, 并且保障汇合中不会呈现反复元素. 1. 整数汇合的定义每个 intset.h/intset 构造示意一个整数汇合: typedef struct intset { // 编码方式 uint32_t encoding; // 汇合蕴含的元素数量 uint32_t length; // 保留元素的数组 int8_t contents[];} intset;<!--more--> contents 数组是整数汇合的底层实现: 整数汇合的每个元素都是 contents 数组的一个数组项 (item) , 各个项在数组中按值的大小从小到大有序地排列, 并且数组中不蕴含任何反复项. length 属性记录了整数汇合蕴含的元素数量, 也即是 contents 数组的长度. 尽管 intset 构造将 contents 属性申明为 int8_t 类型的数组, 但实际上 contents 数组并不保留任何 int8_t 类型的值 -- contents 数组的真正类型取决于 encoding 属性的值: ...

January 27, 2023 · 2 min · jiezi

关于redis:Redis有哪些事件

(1) Redis里的事件Redis事件驱动框架是如何以事件模式,解决 server 运行过程中面临的申请操作和多种工作的。 (1.1) 事件循环构造体// file: src/ae.h/** * 基于事件的程序的状态 * State of an event based program */typedef struct aeEventLoop { int maxfd; // 以后注册的最大文件描述符 int setsize; // 跟踪的最大文件描述符数 long long timeEventNextId; time_t lastTime; /* Used to detect system clock skew */ aeFileEvent *events; // 注册事件数组的指针 指向aeFileEvent数组 aeFiredEvent *fired; // 就绪事件数组的指针 指向aeFiredEvent数组 aeTimeEvent *timeEventHead; // 工夫事件 int stop; void *apidata; // 指向aeApiState构造体 创立的epoll对象就在aeApiState->epfd aeBeforeSleepProc *beforesleep; // 在事件处理前执行的函数 aeBeforeSleepProc *aftersleep; // 在事件处理后执行的函数 int flags;} aeEventLoop;// file: src/ae.h /** * 文件事件构造 * File event structure */typedef struct aeFileEvent { int mask; // 标记 可读/可写/屏障 aeFileProc *rfileProc; // 写事件回调 aeFileProc *wfileProc; // 读事件回调 void *clientData; // 扩大数据} aeFileEvent;(1.2) 事件对象的初始化//file: src/server.cvoid initServer(void) { // 2.1 创立 epoll server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR); // 省略局部代码}事件的个数对应入参里的 server.maxclients+CONFIG_FDSET_INCR, server.maxclients 变量的值大小,能够在 Redis 的配置文件 redis.conf 中进行定义,默认值是 1000。 CONFIG_FDSET_INCR 的大小 = 32 + 96 ...

January 26, 2023 · 4 min · jiezi

关于redis:Redis的设计与实现4跳跃表

跳跃表 (skiplist) 是一种有序数据结构, 它通过在每个节点中维持多个指向其余节点的指针, 从而达到快速访问节点的目标. 跳跃表反对均匀 O(log N) 最坏 O(N) 复杂度的节点查找, 还能够通过程序性操作来批量解决节点. 在大部分状况下, 跳跃表的效率能够和均衡树相媲美, 并且因为跳跃表的实现比均衡树要来得更为简略, 所以有不少程序都应用跳跃表来代替均衡树. Redis 应用跳跃表作为有序汇合键的底层实现之一: 如果一个有序汇合蕴含的元素数量比拟多, 又或者有序汇合中元素的成员 (member) 是比拟长的字符串时, Redis 就会应用跳跃表来作为有序汇合键的底层实现. 和链表, 字典等数据结构被宽泛地利用在 Redis 外部不同, Redis 只在两个中央用到了跳跃表, 一个是实现有序汇合键, 另一个是在集群节点中用作外部数据结构, 除此之外, 跳跃表在 Redis 外面没有其余用处. 1. 跳跃表节点跳跃表节点由 redis.h/zskiplistNode 构造定义: typedef struct zskiplistNode { // 后退指针 struct zskiplistNode *backward; // 分值 double score; // 成员对象 robj *obj; // 层 struct zskiplistLevel { // 后退指针 struct zskiplistNode *forward; // 跨度 unsigned int span; } level[];} zskiplistNode;1.1 层跳跃表节点的 level 数组能够蕴含多个元素, 每个元素都蕴含一个指向其余节点的指针, 程序能够通过这些层来放慢拜访其余节点的速度, 一般来说, 层的数量越多, 拜访其余节点的速度就越快. ...

January 26, 2023 · 2 min · jiezi

关于redis:Redis事件驱动框架

常常会遇到这样一道问题:Redis的网络框架是实现了 Reactor 模型吗? (1) Reactor模型是什么Reactor模型是网络服务器端用来解决高并发网络IO申请的一种编程模型。 Reactor的核心思想:Reactor模式 也叫 Dispatcher 模式,将关注的IO事件注册到多路复用器上,一旦有IO事件触发,将事件散发到事件处理器中,执行就绪IO事件对应的处理函数中。 (1.1) 角色Reactor模型中定义的三种角色: 角色职责Reactor负责监听和调配事件,将I/O事件分派给对应的Handler。Acceptor解决客户端新连贯,并分派申请到处理器链中。Handler将本身与事件绑定,执行非阻塞读/写工作,实现channel的读入,实现解决业务逻辑后,负责将后果写出channel。可用资源池来治理。 (1.2) 事件Reactor 模型解决的是客户端和服务器端的交互过程,而这三类事件正好对应了客户端和服务器端交互过程中,不同类申请在服务器端引发的待处理事件: 事件类型解释备注连贯事件客户端和服务器建设连贯对应connect()函数写事件连贯建设后,客户端会给服务器端发送读申请数据;\n 或 服务器端解决完客户端申请后写数据。对应write()函数读事件服务器端读取申请数据。对应 read()函数(1.3) Reactor解决申请的流程连贯事件由 acceptor 来解决,负责接管连贯;acceptor 在接管连贯后,会创立 handler,用于网络连接上对后续读写事件的解决;读写事件由 handler 解决;在高并发场景中,连贯事件、读写事件会同时产生,须要有一个角色专门监听和调配事件,这就是 reactor 角色。当有连贯申请时,reactor 将产生的连贯事件交由 acceptor 解决;当有读写申请时,reactor 将读写事件交由 handler 解决。1、单 Reactor 单线程;2、单 Reactor 多线程;3、主从 Reactor 多线程。 (2) 为什么要用Reactor模型(2.1) BIO模型的优缺点 长处:1、应用简略,容易编程2、在多核零碎下,可能充分利用了多核CPU的资源。 毛病:该模式的实质问题在于重大依赖线程,随着并发访问量的减少,线程数量的一直收缩将服务端的性能将急剧下降。1、线程的创立与销毁都须要调用零碎函数,开销比拟高。 2、资源耗费大。大量闲暇的线程会占用许多内存;并发量大时,线程资源争抢重大,CPU性能可能会降落。3、线程的切换老本高。操作系统产生线程切换的时候,须要保留线程的上下文,而后执行零碎调用。线程数过高,会带来许多无用的上下文切换,可能导致执行线程切换的工夫甚至会大于线程执行的工夫,这时候带来的体现往往是零碎负载偏高、CPU sy(零碎CPU)使用率特地高。4、客户端和服务器端的连贯会始终保留着,可能会存在大量线程在大量工夫内都处于空置状态的状况。 (2.2) Reactor模型的优缺点Reactor模型具备如下的长处: 响应快,不用为单个同步工夫所阻塞,尽管Reactor自身仍然是同步的;编程绝对简略,能够最大水平的防止简单的多线程及同步问题,并且防止了多线程/过程的切换开销;可扩展性,能够不便地通过减少Reactor实例个数来充分利用CPU资源;可复用性,Reactor模型自身与具体事件处理逻辑无关,具备很高的复用性。 (3) 如何依照Reactor模型实现的Redis 为了实现事件驱动框架,相应地定义了事件的数据结构、框架主循环函数、事件捕捉散发函数、事件和 handler 注册函数。 (4) 源码解析(4.1) 事件的数据结构Redis 的事件驱动框架定义了两类事件:IO事件和工夫事件,别离对应了客户端发送的网络申请和 Redis本身的周期性操作。 // file: ae.h /** 文件事件构造 */typedef struct aeFileEvent { int mask; // 标记 可读/可写/屏障 aeFileProc *rfileProc; // 写事件函数 aeFileProc *wfileProc; // 读事件函数 void *clientData; // } aeFileEvent;// file: ae.h /* 工夫事件构造 */typedef struct aeTimeEvent { long long id; /* time event identifier. */ long when_sec; /* seconds */ long when_ms; /* milliseconds */ aeTimeProc *timeProc; aeEventFinalizerProc *finalizerProc; void *clientData; struct aeTimeEvent *prev; struct aeTimeEvent *next; int refcount; /* refcount to prevent timer events from being * freed in recursive time event calls. */} aeTimeEvent;(4.2) 事件执行入口-main函数// file: src/ae.c /** * 循环处理事件 * * @param *eventLoop */ void aeMain(aeEventLoop *eventLoop) { eventLoop->stop = 0; // 循环处理事件 while (!eventLoop->stop) { // 处理事件 aeProcessEvents(eventLoop, AE_ALL_EVENTS| AE_CALL_BEFORE_SLEEP| AE_CALL_AFTER_SLEEP); }}(4.3) 获取就绪事件并散发// file: src/ae.c /** * 处理事件 * * @param *eventLoop * @param flags */ int aeProcessEvents(aeEventLoop *eventLoop, int flags){ // 省略局部细节... // 调用多路复用API获取就绪事件 numevents = aeApiPoll(eventLoop, tvp); // 解决写事件 fe->wfileProc(eventLoop,fd,fe->clientData,mask); // 解决读事件 fe->rfileProc(eventLoop,fd,fe->clientData,mask); }// file: src/ae_poll.c /** * 获取就绪事件 * * @param *eventLoop * @param *tvp */ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { // 期待事件 aeApiState *state = eventLoop->apidata; int retval, numevents = 0; // 调linux epoll_wait函数来获取已就绪socket retval = epoll_wait(state->epfd,state->events,eventLoop->setsize, tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); // ... return numevents;}(4.4) 事件注册// file: src/ae.c/** * @param *eventLoop * @param fd * @param mask 0:未注册事件 1:描述符可读时触发 2:描述符可写时触发 3: * @param *proc aeFileProc类型 入参传的是 acceptTcpHandler函数 回调时会用到这个函数 * @param *clientData */ int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData){ if (fd >= eventLoop->setsize) { errno = ERANGE; return AE_ERR; } // 从aeFileEvent事件数组里取出一个文件事件构造 aeFileEvent *fe = &eventLoop->events[fd]; // 监听指定fd的指定事件 if (aeApiAddEvent(eventLoop, fd, mask) == -1) return AE_ERR; // 设置文件事件类型 以及事件的处理器 fe->mask |= mask; if (mask & AE_READABLE) fe->rfileProc = proc; // 设置读事件函数 if (mask & AE_WRITABLE) fe->wfileProc = proc; // 设置写事件函数 // 公有数据 fe->clientData = clientData; if (fd > eventLoop->maxfd) eventLoop->maxfd = fd; return AE_OK;}Redis高性能IO模型 https://weikeqin.com/2022/01/... ...

January 25, 2023 · 2 min · jiezi

关于redis:Redis的设计与实现3字典

Redis 的数据库应用字典实现, 对数据库的增, 删, 查, 改也是构建在对字典的操作之上的. 字典是哈希键的底层实现之一: 当一个哈希键蕴含的键值对比拟多, 又或者键值对中的元素都是比拟长的字符串时, Redis 将会应用字典作为哈希键的底层实现. 1. 哈希表Redis 的字典应用哈希表作为底层实现, 一个哈希表外面能够有多个哈希表节点, 而每个哈希表节点就保留了字典中的一个键值对. Redis 字典所应用的哈希表由 dict.h/dictht 构造定义: typedef struct dictht { // 哈希表数组 dictEntry **table; // 哈希表大小 unsigned long size; // 哈希表大小掩码,用于计算索引值 // 总是等于 size - 1 unsigned long sizemask; // 该哈希表已有节点的数量 unsigned long used;} dictht;<!--more--> table 属性是一个数组, 数组中的每个元素都是一个指向 dict.h/dictEntry 构造的指针, 每个 dictEntry 构造保留着一个键值对. size 属性记录了哈希表的大小, 也即是 table 数组的大小, 而 used 属性则记录了哈希表目前已有节点(键值对)的数量. sizemask 属性的值总是等于 size - 1 , 这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引下面. ...

January 25, 2023 · 4 min · jiezi

关于redis:Redis的设计与实现2链表

链表在 Redis 中的利用十分宽泛, 比方列表键的底层实现之一就是链表: 当一个列表键蕴含了数量比拟多的元素, 又或者列表中蕴含的元素都是比拟长的字符串时, Redis 就会应用链表作为列表键的底层实现. 除了链表键之外, 公布与订阅, 慢查问, 监视器等性能也用到了链表, Redis 服务器还应用链表保留多个客户端的状态信息, 以及应用链表来构建客户端输入缓冲区(output buffer). 1. 链表的定义每个链表节点应用一个 adlist.h/listNode 构造来示意: typedef struct listNode { // 前置节点 struct listNode *prev; // 后置节点 struct listNode *next; // 节点的值 void *value;} listNode;应用 adlist.h/list 来操作链表会更不便 typedef struct list { // 表头节点 listNode *head; // 表尾节点 listNode *tail; // 链表所蕴含的节点数量 unsigned long len; // 节点值复制函数 void *(*dup)(void *ptr); // 节点值开释函数 void (*free)(void *ptr); // 节点值比照函数 int (*match)(void *ptr, void *key);} list;可见, 它是一个双向链表. ...

January 24, 2023 · 2 min · jiezi

关于redis:Redis事件驱动框架IO模型

Redis 作为一个 Client-Server 架构的数据库,其源码中少不了用来实现网络通信的局部。 为了实现高并发的网络通信,咱们罕用的 Linux 操作系统,就提供了 select、poll 和 epoll 三种编程模型,而在 Linux 上运行的 Redis,通常就会采纳其中的 epoll 模型来进行网络通信。 (1) 为什么 Redis 不应用根本的 Socket 编程模型?应用 Socket 模型实现网络通信时,须要通过 创立Socket、监听端口、解决连贯 和 读写申请等多个步骤 须要留神的是,accept 函数是阻塞函数,也就是说,如果此时始终没有客户端连贯申请,那么,服务器端的执行流程会始终阻塞在 accept 函数。 尽管它可能实现服务器端和客户端之间的通信,然而程序每调用一次 accept 函数,只能解决一个客户端连贯。 这个显然不满足高并发的要求 IO模型 https://weikeqin.com/2020/06/... 参考IO模型,要想满足高并发,个别都应用IO多路复用 (2) RedisServer网络申请解决流程 (2.1) 绑定地址并监听套接字(bind listen)// file: src/server.c // 监听端口/** * @param port * @param *fds * @param *count */int listenToPort(int port, int *fds, int *count) { // ... for (j = 0; j < server.bindaddr_count || j == 0; j++) { // ... 省略 绑定IPV6 IPV4的细节 anetTcp6Server anetTcpServer // 绑定 fds[*count] = anetTcpServer(server.neterr,port,NULL, server.tcp_backlog); // ... } return C_OK;}Redis 是反对开启多个端口的,所以在 listenToPort 中咱们看到是启用一个循环来调用 anetTcpServer。 在 anetTcpServer 中,逐渐会开展调用,直到执行到 bind 和 listen 零碎调用。 ...

January 24, 2023 · 16 min · jiezi

关于redis:Redis启动会做哪些操作

Redis server启动后会做哪些操作? Redis 是典型的 Client-Server 架构,一旦 Redis 实例开始运行,Redis server 也就会启动,而 main 函数其实也会负责 Redis server 的启动运行。 Redis 针对以下三个问题的实现思路: Redis server 启动后具体会做哪些初始化操作?Redis server 初始化时有哪些要害配置项?Redis server 如何开始解决客户端申请? (1) Redis Server 启动源码地址 https://github.com/redis/redi... //file: src/server.c// #L5297 5297行int main(int argc, char **argv) { // 省略局部代码... // 1. 启动RedisServer时 初始化配置及资源 initServer(); // 2. 循环解决申请及事件,直到服务器敞开为止 aeMain(server.el); }(1.1) 启动RedisServer初始化配置及资源在 initServer 这个函数内,Redis 做了这么三件重要的事件。 1、创立一个epoll对象2、对配置的监听端口进行listen3、把 listen socket 让 epoll 给治理起来 void initServer(void) { // 1. 创立epoll server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR); // 2. 监听端口 listenToPort(server.port,server.ipfd,&server.ipfd_count) // 3. 注册accept事件处理器 (只是注册 前面会用到) for (j = 0; j < server.ipfd_count; j++) { aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL) }}(1.2) 循环解决申请// file: src/ae.c /** * 循环处理事件 * * @param *eventLoop */ void aeMain(aeEventLoop *eventLoop) { eventLoop->stop = 0; // 循环处理事件 while (!eventLoop->stop) { // 处理事件 aeProcessEvents(eventLoop, AE_ALL_EVENTS| AE_CALL_BEFORE_SLEEP| AE_CALL_AFTER_SLEEP); }}// file: src/ae.c /** * 处理事件 * * @param *eventLoop * @param flags */ int aeProcessEvents(aeEventLoop *eventLoop, int flags){ // 省略局部细节... // 调用多路复用API获取就绪事件 numevents = aeApiPoll(eventLoop, tvp); // 解决写事件 fe->wfileProc(eventLoop,fd,fe->clientData,mask); // 解决读事件 fe->rfileProc(eventLoop,fd,fe->clientData,mask); }其实整个 Redis 的工作过程,就只须要了解分明 main 函数中调用的 initServer 和 aeMain 这两个函数就足够了。 ...

January 23, 2023 · 3 min · jiezi

关于redis:Redis的设计与实现1SDS简单动态字符串

当初在高铁上, 赶着春节回家过年, 无座站票, 电脑只能放行李架上, 面对着行李架撸键盘--看过<Redis的设计与实现>这本书, 忽然想起, 便整顿下SDS的内容, 绝对前面的章节, 算是比较简单的~大多数状况下, Redis应用SDS(Simple Dynamic String, 简略动静字符串)作为字符串示意, 比起C字符串, SDS具备以下长处: 常数复杂度获取字符串长度;杜绝缓冲区溢出;缩小批改字符串时带来的内存重调配次数;二进制平安;兼容局部C字符串函数.以上列举的长处, 也算是这篇文章的次要内容. 先从定义说起吧: 1. SDS的定义SDS构造定义如下(sds.h/sdshdr): struct sdshdr { // 记录buf数组中已应用字节的数量, 等于SDS所保留字符串的长度 int len; // 记录buf数组中未应用字节的数量 int free; // 字节数组, 用于保留字符串 char buf[];}len属性记录了已应用的字节数量(字符串长度);free属性的值为0, 示意这个SDS没有未应用的空间;free属性的值为5, 示意这个SDS保留了一个5字节长的字符串;buf属性是一个char类型的数组, 数组的最初一个字节保留了空字符\0.SDS遵循C字符串以空字符串结尾的常规, 空字符不计入SDS的len属性, 即额定为空字符调配了1字节的空间, 并且增加空字符到字符串开端均由SDS函数主动实现, 对使用者齐全通明. 该个性带来的益处是, SDS能够间接复用C字符串函数库的局部函数. 2. SDS与C字符串的区别2.1 常数复杂度获取字符串长度因为C字符串不记录本身长度, 所以获取长度时须要遍历整个字符串, 直到遇到空字符\0为止, 该操作的复杂度为O(N);因为SDS在len属性中记录了SDS自身的长度, 所以获取一个SDS的长度的复杂度为O(1).Redis应用SDS, 将获取字符串长度所需的复杂度从O(N)升高到O(1), 确保获取字符串长度的工作不会成为Redis的性能瓶颈. 2.2 杜绝缓冲区溢出因为C字符串不记录本身长度, 以函数strcat来说, 当执行该函数时, 都是认为曾经为dest调配了足够的内存包容src字符串, 但如果该假如不成立, 就会产生缓冲区溢出. 然而, SDS的字符串空间调配策略, 从根本上杜绝了缓冲区溢出的可能性: 当SDS-API须要对SDS进行批改时, API会先查看SDS的空间是否满足批改所需的需要, 如果不满足的话, API会主动扩容至所需大小, 再执行批改操作. 所以, SDS无需手工保护SDS的空间大小, 也不会产生缓冲区溢出的问题. ...

January 23, 2023 · 2 min · jiezi

关于redis:Redis-quicklist设计

quicklist 的设计,其实是联合了链表和 ziplist 各自的劣势。简略来说,一个 quicklist 就是一个链表,而链表中的每个元素又是一个 ziplist。咱们来看下 quicklist 的数据结构,这是在quicklist.h文件中定义的,而 quicklist 的具体实现是在quicklist.c文件中。首先,quicklist 元素的定义,也就是 quicklistNode。因为 quicklist 是一个链表,所以每个 quicklistNode 中,都蕴含了别离指向它前序和后序节点的指针prev和next。同时,每个 quicklistNode 又是一个 ziplist,所以,在 quicklistNode 的构造体中,还有指向 ziplist 的指针*zl。此外,quicklistNode 构造体中还定义了一些属性,比方 ziplist 的字节大小、蕴含的元素个数、编码格局、存储形式等。上面的代码显示了 quicklistNode 的构造体定义 quicklist 的设计,其实是联合了链表和 ziplist 各自的劣势。简略来说,一个 quicklist 就是一个链表,而链表中的每个元素又是一个 ziplist。 typedef struct quicklistNode { struct quicklistNode *prev; //前一个quicklistNode struct quicklistNode *next; //后一个quicklistNode unsigned char *zl; //quicklistNode指向的ziplist unsigned int sz; //ziplist的字节大小 unsigned int count : 16; //ziplist中的元素个数 unsigned int encoding : 2; //编码格局,原生字节数组或压缩存储 unsigned int container : 2; //存储形式 unsigned int recompress : 1; //数据是否被压缩 unsigned int attempted_compress : 1; //数据是否被压缩 unsigned int extra : 10; //预留的bit位} quicklistNode;quicklist 作为一个链表构造,在它的数据结构中,是定义了整个 quicklist 的头、尾指针,这样一来,咱们就能够通过 quicklist 的数据结构,来疾速定位到 quicklist 的链表头和链表尾。 ...

January 21, 2023 · 1 min · jiezi

关于redis:Redis有序集合

有序汇合(Sorted Set)是Redis中的一种对象,它自身是汇合类型,同时也能够反对汇合中的元素带有权重,并按权重排序。 为什么 Sorted Set 能同时提供以下两种操作接口,以及它们的复杂度别离是 O(logN)+M 和 O(1) 呢? ZRANGEBYSCORE:依照元素权重返回一个范畴内的元素。ZSCORE:返回某个元素的权重值。 实际上,这个问题背地的实质是:为什么 Sorted Set 既能反对高效的范畴查问,同时还能以 O(1) 复杂度获取元素权重值? 127.0.0.1:6379> zadd zset_user_1 1 mysql(integer) 1127.0.0.1:6379> zadd zset_user_1 2 redis(integer) 1127.0.0.1:6379> zadd zset_user_1 3 es(integer) 1127.0.0.1:6379> zadd zset_user_1 4 hbase(integer) 1127.0.0.1:6379> zadd zset_user_1 5 clickhouse(integer) 1127.0.0.1:6379>127.0.0.1:6379> zrange zset_user_1 0 101) "mysql"2) "redis"3) "es"4) "hbase"5) "clickhouse"127.0.0.1:6379>127.0.0.1:6379> zrange zset_user_1 0 10 withscores 1) "mysql" 2) "1" 3) "redis" 4) "2" 5) "es" 6) "3" 7) "hbase" 8) "4" 9) "clickhouse"10) "5"127.0.0.1:6379>127.0.0.1:6379>127.0.0.1:6379> zrangebyscore zset_user_1 2 41) "redis"2) "es"3) "hbase"127.0.0.1:6379>127.0.0.1:6379> zrangebyscore zset_user_1 2 4 withscores1) "redis"2) "2"3) "es"4) "3"5) "hbase"6) "4"127.0.0.1:6379>127.0.0.1:6379>127.0.0.1:6379> zscore zset_user_1 mysql"1"127.0.0.1:6379>127.0.0.1:6379> zscore zset_user_1 es"3"127.0.0.1:6379>127.0.0.1:6379>这其实就和 Sorted Set 底层的设计实现无关了。 Sorted Set 能反对范畴查问,这是因为它的外围数据结构设计采纳了跳表, 而它能以常数复杂度获取元素权重,这是因为它同时采纳了哈希表进行索引。 ...

January 20, 2023 · 7 min · jiezi

关于redis:Redis对象处理机制

在Redis的命令中,用于对键(key)进行解决的命令占了很大一部分, 而对于键所保留的值的类型(后简称"键的类型"),键能执行的命令又各不相同。 比如说,LPUSH 和 LLEN 只能用于列表键,如果用错了会提醒 (error) WRONGTYPE Operation against a key holding the wrong kind of value 比方在string类型上用了 llen 就会提醒这个谬误,那么redis是怎么做到发现类型谬误并提醒WRONGTYPE的? 127.0.0.1:6379> set key1 value1OK127.0.0.1:6379> get key1"value1"127.0.0.1:6379>127.0.0.1:6379> llen key1(error) WRONGTYPE Operation against a key holding the wrong kind of value127.0.0.1:6379>实际上,Redis每个键都带有类型信息,使得程序能够查看键的类型,并为它抉择适合的解决形式。 另外,Redis 的每一种数据类型,比方字符串、列表、有序集, 它们都领有不只一种底层实现(Redis外部称之为编码,encoding),这阐明,每当对某种数据类型的键进行操作时,程序都必须依据键所采取的编码,进行不同的操作。 (1) Redis对象零碎为了应答不同的对象类型(type)和对象编码(encoding),Redis构建了本人的类型零碎,这个零碎的次要性能包含: redisObject 对象。基于 redisObject 对象的类型查看。基于 redisObject 对象的显式多态函数。对 redisObject 进行调配、共享和销毁的机制。Redis 的 key 是 String 类型,但 value 能够是很多类型(String/List/Hash/Set/ZSet等),所以 Redis 要想存储多种数据类型,就要设计一个通用的对象进行封装,这个对象就是 redisObject。 redisObject 是Redis类型零碎的外围, 数据库中的每个键、值,以及Redis自身解决的参数,都示意为这种数据类型。 (1.1) redisObject构造定义Redis 应用的根本数据对象构造体 redisObject ...

January 19, 2023 · 5 min · jiezi

关于redis:Redis源码剖析与实战-学习笔记-Day3-如何实现一个性能优异的Hash表

哈希表是一种十分要害的数据结构,在计算机系统中施展着重要作用。 它的底层是数组+链表,通过哈希计算,能以 O(1) 的复杂度疾速依据key查问到数据。 (1) 数据结构-哈希表假如让咱们本人实现一个哈希表,咱们要思考哪些方面? 哈希表提供的性能哈希表操作的工夫复杂度为O(1)哈希表的容量与扩容(1.1) 提供的性能新建哈希表、新增数据、批改数据、删除数据、查问数据 (1.2) 工夫复杂度O(1)要想使工夫复杂度为O(1),要通过高效的定位办法,比方hash函数 然而hash函数有个问题,可能会哈希抵触,抵触后有多种办法 链地址法、 凋谢定位法、 再哈希法,这里咱们应用链地址法实现。 /** 哈希函数 */ int hash(Object key) { int h; // h = key.hashCode() 为第一步 取hashCode值 // h ^ (h >>> 16) 为第二步 高位参加运算 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }链式哈希的链不能太长,否则会升高 哈希表性能。 链表哈希的链表太长时能够转换为红黑树进步查问性能。 (1.3) 哈希表容量与扩容这里咱们在创立哈希表的时候反对指定容量 扩容时容量是原来的2倍 public void resize(){ //todo }如果容量特地大,内存不够怎么办? (2) Redis里的哈希表对于 Redis 键值数据库来说,哈希表既是键值对中的一种值类型,同时,Redis 也应用一个全局 哈希表来保留所有的键值对,从而既满足利用存取 Hash 构造数据需要,又能提供疾速查问性能。 ...

January 18, 2023 · 10 min · jiezi

关于redis:Redis源码剖析与实战-学习笔记-Day2-键值对中字符串的实现用char还是结构体

Redis绝大部分操作都会波及到key,应用特地宽泛,所以须要尽量满足以下三个要求: 能反对丰盛且高效的字符串操作,比方字符串追加、拷贝、比拟、获取长度等;能保留二进制数据,比方byte[]等;节俭内存开销。从零碎设计的角度来看,咱们该如何设计实现字符串呢? Redis 设计了简略动静字符串(Simple Dynamic String)的构造,用来示意字符串。相比于 C 语言中的字符串实现,SDS 这种字符串的实现形式,会晋升字符串的操作效率,并且能够用来保留二进制数据。 (1) 为什么Redis里的字符串不必char*?Redis是用c语言编写的,为什么Redis的key不间接应用char* ? 来看看 char* 字符数组的构造。 char字符数组的构造比较简单,用一块间断的内存空间,顺次寄存了字符串中的每一个字符数组构造,结尾地位就用"\0"示意,示意完结。 不应用char*的起因 操作效率低:获取字符串长度需遍历整个字符数组,O(N)复杂度二进制不平安:无奈存储蕴含 \0 的数据批改字符串遗记分配内存容易造成缓冲区溢出 (buffer overflow) 。批改字符串须要从新分配内存,波及零碎调用。SDS劣势 操作效率高:获取长度无需遍历,O(1)复杂度二进制平安:因独自记录长度字段,所以可存储蕴含 "\0" 的数据兼容 C 字符串函数,可间接应用字符串 API另外 Redis 在操作 SDS 时,为了防止频繁操作字符串时,每次「申请、开释」内存的开销,还做了这些优化: 内存预调配:SDS 扩容,会多申请一些内存(小于 1MB 翻倍扩容,大于 1MB 按 1MB 扩容)多余内存不开释:SDS 缩容,不开释多余的内存,下次应用可间接复用这些内存(2) SDS设计思维SDS 构造里蕴含了一个字符数组 buf[],用来保留理论数据。 同时,SDS构造里还蕴含了三个元数据,别离是字符数组现有长度 len、调配给字符数组的空间长度 alloc,以及 SDS 类型 flags。 (2.1) SDS定义redis 6.0版本源码 https://github.com/redis/redi... Redis里SDS定义,这里以sdshdr64为例 typedef char *sds;struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; // 字符数组已应用长度 /* used */ uint64_t alloc; // 字符数组的已调配空间,不包含构造体和完结字符 /* excluding the header and null terminator */ unsigned char flags; /* SDS类型 占用一字节 理论应用了3bit,5bit未应用 */ /* 3 lsb of type, 5 unused bits */ char buf[]; /** 理论存储的数据 */};类型 sds 是 char* 的别名(alias) 构造 sdshdr 则保留了 len 、 alloc flags和 buf[] 四个属性 ...

January 17, 2023 · 5 min · jiezi

关于redis:技术分享-Redis-持久化之-RDB-与-AOF

作者:贲绍华 爱可生研发核心工程师,负责我的项目的需要与保护工作。其余身份:柯基铲屎官。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 一、RDB(Redis Database)简介RDB长久化形式可能在指定的工夫距离内(N秒内有M次改变时),对实例的数据进行快照存储,也就是全备的意思。 二、RDB - 个性2.1 长处繁多文件,不便传输,适宜灾备;复原大数据集时效率会比AOF快一些;备份时会由fork出的子过程操作,父过程不须要其余IO操作,性能绝对AOF来说占优。2.2 毛病在距离其间发生意外宕机,会造成数据大量失落;数据量十分大时fork子过程十分耗时,可能会影响业务失常响应。三、RDB - 策略RDB的备份触发形式有两种类型,五种触发条件,别离为: 3.1 主动触发依据conf内配置的save规定进行保留;执行FLUSHALL(删除所有数据库外面的所有数据)命令会触发;被动退出Redis会触发。3.2 手动触发SAVE「同步执行」(保留数据至磁盘);BGSAVE「异步执行」(保留数据至磁盘)。3.3 操作流程1.fork一个子过程,创立子过程时并不会产生数据复制,进步了复制速度升高了所需空间大小(内核级的零碎调用:fork()); 2.子过程取得所有数据指向地址的指针; 3.此时如有数据持续减少则触发写时复制,父过程指向新值地址,子过程仍旧指向原值地址(COW(copy-on-write 写时复制)); 4.将指针指向的值写入备份; 5.备份实现。 四、RDB - 配置| 配置项 | 阐明 | | --- | --- | | save | N秒内有M次改变时保留(触发的是BGSAVE异步执行) | | stop-writes-on-bgsave-error | 快照出错时是否禁止写入操作 || rdbcompression | 是否压缩RDB文件 | | rdbchecksum | 是否开启RC64校验 | | dbfilenameRDB | 文件保留名称 | | dirRDB | 文件保留目录 | 五、RDB - 其余5.1 时点性当Redis在指定工夫点触发全备时如果尔后数据库仍然有批改,则值还是会保留在未修改前的工夫点,这样保障了不会产生时点凌乱。 5.2 阈值倡议倡议Redis应用内存管制在10-15G以内,过大的话会影响RDB落盘的速度。 5.3 RDB文件损坏该怎么办在Redis的装置目录内,提供了redis-check-rdb工具用于对损坏的备份文件进行修复。 ...

January 17, 2023 · 1 min · jiezi

关于redis:Redis源码剖析与实战-学习笔记-Day1-Redis源码整体概览

Redis代码的整体架构,就相当于给 Redis代码画了张全景图。 有了这张图,咱们再去学习 Redis 不同功能模块的设计与实现时,就能够从图上疾速查找和定位这些功能模块对应的代码文件。 庖丁解牛 代码的目录构造和作用划分 了解 Redis 代码的整体架构,以及所蕴含的代码性能类别; 零碎功能模块与对应代码文件 目标是理解 Redis 实例提供的各项性能及其相应的实现文件,以便后续深刻学习。 (1) Redis目录构造在学习一个大型系统软件的代码时,要想疾速地对代码有个初步认知,理解零碎源码的整体目录构造就是一个卓有成效的办法。 对于 Redis 来说,在它的源码总目录下,一共蕴含了deps、src、tests、utils四个子目录,这四个子目录别离对应了 Redis 中施展不同作用的代码,上面咱们具体来看看。 (1.1) deps目录这个目录次要蕴含了 Redis 依赖的第三方代码库,包含 Redis 的 C 语言版本客户端代码 hiredis、jemalloc 内存分配器代码、readline 性能的代替代码 linenoise,以及 lua 脚本代码。 (1.2) src目录这个目录外面蕴含了 Redis 所有功能模块的代码文件,也是 Redis 源码的重要组成部分。 (1.3) tests目录功能模块测试和单元测试的代码。而在 Redis 的代码目录中,就将这部分代码用一个 tests 目录对立治理了起来。 Redis 实现的测试代码能够分成四局部,别离是单元测试(对应 unit 子目录),Redis Cluster 功能测试(对应 cluster 子目录)、哨兵功能测试(对应 sentinel 子目录)、主从复制功能测试(对应 integration 子目录)。这些子目录中的测试代码应用了 Tcl 语言(通用的脚本语言)进行编写,次要目标就是不便进行测试。 (1.4) utils目录在Redis 开发过程中,还有一些性能属于辅助性性能,包含用于创立 Redis Cluster 的脚本、用于测试 LRU 算法成果的程序,以及可视化 rehash 过程的程序。在 Redis 代码构造中,这些性能代码都被归类到了 utils 目录中对立治理。 ...

January 16, 2023 · 2 min · jiezi

关于redis:带读-Redis-设计与实现英文名The-Design-and-Implementation-of-Redis

Why Redis?Redis是一种运行速度很快,并发很强,跑在内存上的NoSql数据库,反对键到五种数据类型的映射。依据Redis官网提供的benchmark测试数据,redis读的速度是110000次/s,写的速度是81000次/s,并且试验证实,数据没在缓存的时候,雷同条件下用Jmeter进行压测,redis的申请处理速度比MySQL高了7倍。另外,在工夫局部性原理很强的场景下,Redis能够从缓存迅速响应,而不是去数据库查sql,尤其是曾经放入缓存的数据,这体现出了Redis的高性能;Redis是单线程的,防止了多线程上下文的切换和锁竞争的工夫开销。(在Redis6.0之后在网络读写时已替换成多线程,但执行命令依然是单线程)Redis指令是原子性的,高并发时不会产生数据异样,Redis 应用 I/O 多路复用技术,能够解决高并发的连贯(非阻塞I/O),这意味着可能让一个计算单元来解决来自多个客户端的流申请实现高并发。想要深刻理解Redis,这本《Redis 设计与实现》就无奈绕开了。 第一局部 数据结构与对象简略动静字符串 SDSSDS对标的是C语言的String,通过在底层实现数组buf的根底上减少len和free字段,实现了 常数复杂度获取字符串长度杜绝缓冲区溢出缩小批改字符串时带来的内存重调配次数 实现的原理也巨简略,,,C的String不记录len,每次都只能遍历String工夫复杂度O(n),SDS空间换工夫用一个len记录一下,工夫复杂度间接降到O(1),这就能解释下面的1和2,至于3就是free这个字段的作用了,redis应用了空间预调配和惰性空间开释策略,说白了就是SDS批改之后如果变长了,理论调配的空间不仅仅是len,会依据SDS长度定,如果新的SDS小于1M,新的SDS大小就在原根底上double+1,为啥double,因为这里会多调配一倍给free,这就是空间预调配;为啥+1byte,因为最初一个字节保留空字符,为啥保留空字符,为了更好的兼容C语言对字符串的操作,对的,没有看错,Redis底层就是用的规范C语言实现的。如果新的SDS大于1M,free就等于1M,新的SDS就等于旧的SDS+1M+1byte。 C语言中对String进行的各种操作,SDS天然也有对应的API,详见http://redisbook.com/preview/... 链表因为C语言没有将链作为内置数据结构,Redis用ListNode和指针实现了双向链表 typedef struct listNode{ struct listNode *prev; struct listNode *next; void *value;}listNode; 未完待续。 参考: 黄健宏. Redis 设计与实现[M]. Ji xie gong ye chu ban she, 2014. https://zhuanlan.zhihu.com/p/...https://youle.zhipin.com/ques...

January 14, 2023 · 1 min · jiezi

关于redis:Redis核心技术2630

26 缓存异样缓存雪崩、缓存击穿和缓存穿透,这三个问题一旦产生,会导致大量的申请积压到数据库层,导致数据库宕机或故障。 缓存雪崩缓存雪崩是指大量的利用申请无奈在 Redis 缓存中进行解决,紧接着,利用将大量申请发送到数据库层,导致数据库层的压力激增。 如何发现:监测 Redis 缓存所在机器和数据库所在机器的负载指标,例如每秒申请数、CPU 利用率、内存利用率等。如果咱们发现 Redis 缓存实例宕机了,而数据库所在机器的负载压力忽然减少(例如每秒申请数激增),此时,就产生缓存雪崩了。 起因一:大量数据同时过期。解决方案: 过期工夫加随机数服务降级:暂停非核心业务拜访,间接返回预约义信息;外围数据容许持续查问起因二:Redis实例宕机解决方案: 服务熔断或限流: 服务熔断:暂停拜访,间接返回限流:申请入口设置每秒申请数量,超出间接回绝配置高可用集群缓存击穿缓存击穿是指,针对某个拜访十分频繁的热点数据的申请,无奈在缓存中进行解决,紧接着,拜访该数据的大量申请,一下子都发送到了后端数据库,导致了数据库压力激增,会影响数据库解决其余申请。 缓存击穿的状况,常常在热点数据过期生效时产生。 解决方案:热点数据不设置过期工夫。 缓存穿透缓存穿透是指要拜访的数据既不在 Redis 缓存中,也不在数据库中,导致申请在拜访缓存时,产生缓存缺失,再去拜访数据库时,发现数据库中也没有要拜访的数据。 起因: 业务层误操作,数据被删除歹意攻打,拜访数据库中没有的数据解决方案: 缓存空值或缺省值布隆过滤器疾速判断前端过滤歹意申请布隆过滤器布隆过滤器由一个初值都为 0 的 bit 数组和 N 个哈希函数组成,能够用来疾速判断某个数据是否存在。 数据写入时标记: 应用 N 个哈希函数,别离计算这个数据的哈希值,失去 N 个哈希值。们把这 N 个哈希值对 bit 数组的长度取模,失去每个哈希值在数组中的对应地位。把对应地位的 bit 位设置为 1,这就实现了在布隆过滤器中标记数据的操作。查问时执行标记过程,并比照bit 数组中这 N 个地位上的 bit 值。只有这 N 个 bit 值有一个不为 1,这就表明布隆过滤器没有对该数据做过标记,所以,查问的数据肯定没有在数据库中保留。 27 缓存净化在一些场景下,有些数据被拜访的次数非常少,甚至只会被拜访一次。当这些数据服务完拜访申请后,如果还持续留存在缓存中的话,就只会白白占用缓存空间。这种状况,就是缓存净化。 缓存净化会导致大量不再拜访的数据滞留在缓存中,当缓存空间占满,再写入新数据时,把这些数据淘汰须要额定的操作工夫开销,影响利用性能。 解决方案: 晓得数据被再次拜访的状况,依据拜访工夫设置过期工夫:volatile-ttlLFU缓存策略扫描式单次查问:对大量的数据进行一次整体读取,每个数据都会被读取,而且只会被读取一次。 因为这些被查问的数据刚刚被拜访过,所以 lru 字段值都很大。在应用 LRU 策略淘汰数据时,这些数据会留存在缓存中很长一段时间,造成缓存净化。 LFU缓存策略LFU 缓存策略是在 LRU 策略根底上,为每个数据减少了一个计数器,来统计这个数据的拜访次数。 当应用 LFU 策略筛选淘汰数据时,首先会依据数据的拜访次数进行筛选,把拜访次数最低的数据淘汰出缓存。如果两个数据的拜访次数雷同,LFU 策略再比拟这两个数据的拜访时效性,把间隔上一次拜访工夫更久的数据淘汰出缓存。扫描式单次查问的数据因为不会被再次拜访,所以它们的拜访次数不会再减少。因而,LFU 策略会优先把这些拜访次数低的数据淘汰出缓存。这样一来,LFU 策略就能够防止这些数据对缓存造成净化了。 ...

January 11, 2023 · 2 min · jiezi

关于redis:Redis核心技术笔记2125

21 缓冲区缓冲区就是用一块内存空间来临时寄存命令数据,免得呈现因为数据和命令的处理速度慢于发送速度而导致的数据失落和性能问题。 缓冲器溢出:缓冲区空间无限,当写入速度继续大于读取速度,占用内存超出设定下限时,产生缓冲区溢出。 应用场景: 在客户端和服务端通信时,暂存客户端命令数据,和服务端返回后果在主从同步时,用来暂存主节点接管的写命令和数据客户端服务器端给每个连贯的客户端都设置了一个输出缓冲区和输入缓冲区,咱们称之为客户端输出缓冲区和输入缓冲区。 输出缓冲区会先把客户端发送过去的命令暂存起来,Redis 主线程再从输出缓冲区中读取命令,进行解决。2.当 Redis 主线程解决完数据后,会把后果写入到输入缓冲区,再通过输入缓冲区返回给客户端。输出缓冲区Redis 的客户端输出缓冲区大小的下限阈值,在代码中就设定为了 1GB,无奈调整。 溢出起因: 写入了 bigkey,比方一下子写入了多个百万级别的汇合类型数据;服务器端解决申请的速度过慢,例如,Redis 主线程呈现了间歇性阻塞,无奈及时处理失常发送的申请,导致客户端发送的申请在缓冲区越积越多。查看应用状况: CLIENT LISTid=5 addr=127.0.0.1:50487 fd=9 name= age=4 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=clientqbuf,示意输出缓冲区曾经应用的大小。qbuf-free,示意输出缓冲区尚未应用的大小。输入缓冲区Redis 为每个客户端设置的输入缓冲区也包含两局部: 一部分,是一个大小为 16KB 的固定缓冲空间,用来暂存 OK 响应和出错信息;另一部分,是一个能够动静减少的缓冲空间,用来暂存大小可变的响应后果。溢出起因: 服务器端返回 bigkey 的大量后果;执行了 MONITOR 命令;缓冲区大小设置得不合理。MONITOR 命令是用来监测 Redis 执行的,输入会继续占用缓冲区,不要在生产环境应用: MONITOR设置缓冲区大小:client-output-buffer-limit 配置项 client-output-buffer-limit normal 0 0 0normal 示意以后设置的是一般客户端,第 1 个 0 设置的是缓冲区大小限度,第 2 个 0 和第 3 个 0 别离示意缓冲区继续写入量限度和继续写入工夫限度。 客户端类型: ...

January 10, 2023 · 3 min · jiezi

关于redis:Redis核心技术笔记1620

16 阻塞式操作影响 Redis 性能的 5 大方面的潜在因素: Redis 外部的阻塞式操作;CPU 核和 NUMA 架构的影响;Redis 要害系统配置;Redis 内存碎片;Redis 缓冲区。实例阻塞点客户端:网络IO,键值对增删改查,数据库操作;磁盘:生成RDB快照,记录AOF日志,AOF日志重写;主从节点:主库生成、传输RDB文件,从库接管RDB文件、清空数据库、加载RDB文件;切片集群实例:向其余实例传输哈希槽信息,数据迁徙客户端阻塞点网络IO:Redis采纳IO多路复用机制,网络IO不是阻塞点键值对操作:复杂度高O(N)的增删改查操作必定会阻塞redis。 汇合全量查问和聚合操作汇合本身删除操作(开释内存后零碎会插入闲暇内存链表用于治理,内存过大操作工夫会减少)清空数据库:删除和开释所有键值对磁盘交互阻塞点AOF日志回写:Redis 间接记录 AOF 日志时,会依据不同的写回策略对数据做落盘保留。一个同步写磁盘的操作的耗时大概是 1~2ms,如果有大量的写操作须要记录在 AOF 日志中,并同步写回的话,就会阻塞主线程了。 主从节点阻塞点在主从集群中,主库须要生成 RDB 文件,并传输给从库。主库在复制的过程中,创立和传输 RDB 文件都是由子过程来实现的,不会阻塞主线程。 对于从库来说,它在接管了 RDB 文件后,须要应用 FLUSHDB 命令清空以后数据库,造成阻塞从库在清空以后数据库后,还须要把 RDB 文件加载到内存,这个过程的快慢和 RDB 文件的大小密切相关,RDB 文件越大,加载过程越慢,造成阻塞集群交互阻塞如果你应用了 Redis Cluster 计划,而且同时正好迁徙的是 bigkey 的话,就会造成主线程的阻塞,因为 Redis Cluster 应用了同步迁徙。 异步执行Redis 主线程启动后,会应用操作系统提供的 pthread_create 函数创立 3 个子线程,别离由它们负责 AOF 日志写操作、键值对删除以及文件敞开的异步执行。 主线程通过一个链表模式的工作队列和子线程进行交互。 写入 AOF 日志当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个工作,也放到工作队列中。后台子线程读取工作后,开始自行写入 AOF 日志,这样主线程就不必始终期待 AOF 日志写完了。 惰性删除lazy free当收到键值对删除和清空数据库的操作时,主线程会把这个操作封装成一个工作,放入到工作队列中,而后给客户端返回一个实现信息,表明删除曾经实现。 但实际上,这个时候删除还没有执行,等到后台子线程从工作队列中读取工作后,才开始理论删除键值对,并开释相应的内存空间。 ...

January 9, 2023 · 3 min · jiezi

关于redis:Redis核心技术笔记1115

11 String为什么String类型内存开销大RedisObject 构造体 + SDS: 当保留64位有符号整数时,String会存为一个8字节的Long类型整数,称为int编码方式当数据蕴含字符串时,String类型会用简略动静字符串(SDS)构造体来保留 buf:字节数组,保留理论数据。为了示意字节数组的完结,Redis 会主动在数组最初加一个“\0”,这就会额定占用 1 个字节的开销。len:占 4 个字节,示意 buf 的已用长度。alloc:也占个 4 字节,示意 buf 的理论调配长度,个别大于 len。RedisObjectRedis 的数据类型有很多,而且,不同数据类型都有些雷同的元数据要记录(比方最初一次拜访的工夫、被援用的次数等),所以,Redis 会用一个 RedisObject 构造体来对立记录这些元数据,同时指向理论数据。 一个 RedisObject 蕴含了 8 字节的元数据和一个 8 字节指针,这个指针再进一步指向具体数据类型的理论数据所在。 编码方式保留 Long 类型整数时,RedisObject 中的指针就间接赋值为整数数据了,只占用8字节。保留小于等于 44 字节的字符串时,RedisObject 中的元数据、指针和 SDS 是一块间断的内存区域,能够防止内存碎片。这种布局形式也被称为 embstr 编码方式。保留大于44字节的字符串时,Redis会给SDS独立的空间,用指针指向SDS构造,称为raw编码方式。内存调配库jemallocRedis 会应用一个全局哈希表保留所有键值对,哈希表的每一项是一个 dictEntry 的构造体,用来指向一个键值对。dictEntry 构造中有三个 8 字节的指针,别离指向 key、value 以及下一个 dictEntry,三个指针共 24 字节。 jemalloc 在分配内存时,会依据咱们申请的字节数 N,找一个比 N 大,然而最靠近 N 的 2 的幂次数作为调配的空间,这样能够缩小频繁调配的次数。 申请 24 字节空间,jemalloc 则会调配 32 字节。所以,在咱们刚刚说的场景里,dictEntry 构造就占用了 32 字节。 ...

January 5, 2023 · 4 min · jiezi

关于redis:Reids的BigKey和HotKey

1.什么是BigKey和HotKey1.1.Big KeyRedis big key problem,实际上不是大Key问题,而是Key对应的value过大,因而严格来说是Big Value问题,Redis value is too large (key value is too large)。 到底多大的value会导致big key问题,并没有对立的规范。 例如,对于String类型的value,有时候超过5M属于big key,有时候稳当起见,超过10K就能够算作Bigey。 Big Key会导致哪些问题呢? 1、因为value值很大,序列化和反序列化工夫过长,网络时延也长,从而导致操作Big Key的时候耗时很长,升高了Redis的性能。 2、在集群模式下无奈做到负载平衡,导致负载歪斜到某个实例上,单实例的QPS会比拟高,内存占用比拟多。 3、因为Redis是单线程,如果要对这个大Key进行删除操作,被操作的实例可能会被block住,从而导致无奈响应申请。 Big Key是如何产生的呢? 个别是程序设计者对于数据的规模意料不当,或设计思考脱漏导致的Big Key的产生。 在某些业务场景下,很容易产生Big Key,例如KOL或者流量明星的粉丝列表、投票的统计信息、大批量数据的缓存,等等。 1.2.Hot KeyHot Key,也叫Hotspot Key,即热点Key。如果某个特定Key忽然有大量申请,流量集中到某个实例,甚至导致这台Redis服务器因为达到物理网卡上线而宕机,这个时候其实就是遇到了热点Key 问题。 热点key会导致很多零碎问题: 1、流量适度集中,无奈施展集群劣势,如果达到该实例解决下限,导致节点宕机,进而冲击数据库,有导致缓存雪崩,让整个零碎挂掉的危险。 2、因为Redis是单线程操作,热点Key会影响所在示例其余Key的操作。 2.如何发现BigKey和HotKey2.1.发现BigKey1、通过Redis命令查问BigKey。 以下命令能够扫描Redis的整个Key空间不同数据类型中最大的Key。-i 0.1 参数能够在扫描的时候每100次命令执行sleep 0.1 秒。 Redis自带的bigkeys的命令能够很不便的在线扫描大key,对服务的性能影响很小,单毛病是信息较少,只有每个类型最大的Key。 $ redis-cli -p 999 --bigkeys -i 0.12、通过开源工具查问BigKey。 应用开源工具,长处在于获取的key信息具体、可选参数多、反对定制化需要,后续解决不便,毛病是须要离线操作,获取后果工夫较长。 比方,redis-rdb-tools 等等。 $ git clone https://github.com/sripathikrishnan/redis-rdb-tools $ cd redis-rdb-tools$ sudo python setup.py install $ rdb -c memory dump-10030.rdb > memory.csv2.2.发现HotKey1、hotkeys 参数 ...

January 5, 2023 · 2 min · jiezi

关于redis:Redis核心技术笔记0910

09 切片集群Redis响应慢问题排查:应用INFO命令查看latest_fork_usec(最近一次fork)耗时起因:fork 在执行时会阻塞主线程。数据量越大,fork 操作造成的主线程阻塞的工夫越长。 数据存储计划Redis 应答数据量增多的两种计划:纵向扩大(scale up)和横向扩大(scale out)。 纵向扩大:降级单个 Redis 实例的资源配置,包含减少内存、磁盘、CPU。横向扩大:减少以后Redis实例数。分布式治理数据切片后,在多个实例之间如何散布?客户端怎么确定想要拜访的数据在哪个实例上?切片和实例对应关系Redis 3.0 开始,官网提供了一个名为 Redis Cluster 的计划,用于实现切片集群。 Redis Cluster 计划采纳哈希槽(Hash Slot,接下来我会间接称之为 Slot),来解决数据和实例之间的映射关系。在 Redis Cluster 计划中,一个切片集群共有 16384 个哈希槽,这些哈希槽相似于数据分区,每个键值对都会依据它的 key,被映射到一个哈希槽中。 具体过程: 依据键值对的 key,依照CRC16算法计算一个 16 bit 的值;用这个 16bit 值对 16384 取模,失去 0~16383 范畴内的模数,每个模数代表一个相应编号的哈希槽。调配计划: 部署 Redis Cluster 计划时,能够应用 cluster create 命令创立集群,此时,Redis 会主动把这些槽均匀散布在集群实例上。例如,如果集群中有 N 个实例,那么,每个实例上的槽个数为 16384/N 个。也能够应用 cluster meet 命令手动建设实例间的连贯,造成集群,再应用 cluster addslots 命令,指定每个实例上的哈希槽个数。 redis-cli -h 172.16.19.3 –p 6379 cluster addslots 0,1redis-cli -h 172.16.19.4 –p 6379 cluster addslots 2,3redis-cli -h 172.16.19.5 –p 6379 cluster addslots 4留神:手动调配哈希槽时,须要把 16384 个槽都调配完,否则 Redis 集群无奈失常工作。 ...

January 4, 2023 · 2 min · jiezi

关于redis:Redis核心技术笔记0708

哨兵机制如果主库挂了,咱们就须要运行一个新主库,比如说把一个从库切换为主库,把它当成主库。在 Redis 主从集群中,哨兵机制是实现主从库主动切换的要害机制,它无效地解决了主从复制模式下故障转移的这三个问题。 根本工作监控:主库真的挂了吗? a. 采纳多实例组成的集群模式进行部署,这也被称为哨兵集群。 b. 引入多个哨兵实例一起来判断,选主:抉择哪个从库作为主库? a. 通过在线状态、网络情况筛选掉一部分从库 b. 再依据优先级、复制进度、从库的id进行打分 c. 得分最高的就是新的主库告诉:把新主库的相干信息告诉给从库和客户端监控哨兵过程在运行时,周期性地给所有的主从库发送 PING 命令,检测它们是否依然在线运行。如果从库没有在规定工夫内响应哨兵的 PING 命令,哨兵就会把它标记为“下线状态”;同样,如果主库也没有在规定工夫内响应哨兵的 PING 命令,哨兵就会断定主库下线,而后开始主动切换主库的流程。选出新主库主库挂了当前,哨兵就须要从很多个从库里,依照肯定的规定抉择一个从库实例,把它作为新的主库。 告诉哨兵会把新主库的连贯信息发给其余从库,让它们执行 replicaof 命令,和新主库建设连贯,并进行数据复制。同时,哨兵会把新主库的连贯信息告诉给客户端,让它们把申请操作发到新主库上。主观下线和主观下线哨兵过程会应用 PING 命令检测它本人和主、从库的网络连接状况,用来判断实例的状态。 如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”。 从库响应超时,哨兵能够间接标记为“主观下线”主库须要哨兵集群一起判断,只有大多数的哨兵实例,都判断主库曾经“主观下线”了,主库才会被标记为“主观下线”主观下线规范: 当有 N 个哨兵实例时,最好要有 N/2 + 1 个实例判断主库为“主观下线”,能力最终断定主库为“主观下线”。管理员可自行设定。从新选主在多个从库中,先依照肯定的筛选条件,把不符合条件的从库去掉。而后再依照肯定的规定,给剩下的从库一一打分,将得分最高的从库选为新主库。筛选条件在选主时,除了要查看从库的以后在线状态,还要判断它之前的网络连接状态。 判断网络连接状态:down-after-milliseconds 是咱们认定主从库断连的最大连贯超时工夫。如果在 down-after-milliseconds 毫秒内,主从节点都没有通过网络分割上,咱们就能够认为主从节点断连了。如果产生断连的次数超过了 10 次,就阐明这个从库的网络情况不好,不适宜作为新主库。 三轮打分依照从库优先级、从库复制进度以及从库 ID 号进行打分。只有在某一轮中,有从库得分最高,那么它就是主库了,选主过程到此结束。如果没有呈现得分最高的从库,那么就持续进行下一轮。优先级:slave-priority 配置项复制进度:slave_repl_offset 最靠近 master_repl_offsetID号:ID号最小的从库得分最高小结Redis 的哨兵机制主动实现了以下三大性能,从而实现了主从库的主动切换,能够升高 Redis 集群的运维开销: 监控主库运行状态,并判断主库是否主观下线;在主库主观下线后,选取新主库;选出新主库后,告诉从库和客户端。问题:哨兵在操作主从切换的过程中,客户端是否失常地进行申请操作?答复:如果客户端应用了读写拆散,那么读申请能够在从库上失常执行,不会受到影响。然而因为此时主库曾经挂了,而且哨兵还没有选出新的主库,所以在这期间写申请会失败,失败继续的工夫 = 哨兵切换主从的工夫 + 客户端感知到新主库 的工夫。 08 哨兵集群配置哨兵信息 sentinel monitor <master-name> <ip> <redis-port> <quorum> 集群组成哨兵实例之间能够互相发现,要归功于 Redis 提供的 pub/sub 机制,也就是公布 / 订阅机制。 ...

January 4, 2023 · 1 min · jiezi

关于redis:Redis核心技术笔记0506

05 内存快照把内存中的数据在某一时刻的状态以文件的模式写到磁盘上就是快照,这个快照文件就称为RDB(Redis DataBase)文件。和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,咱们能够间接把 RDB 文件读入内存,很快地实现复原。 Redis RDB快照是全量快照,会将所有数据保留到磁盘中。 对于Redis的单线程模型,要尽量避免所有会阻塞主线程的操作。 生成形式Redis 提供了两个命令来生成 RDB 文件,别离是 save 和 bgsave: save:在主线程中执行,会导致阻塞;bgsave:创立一个子过程,专门用于写入 RDB 文件,防止了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。写时复制如果快照执行期间数据不能被批改,会给业务服务造成微小的影响。Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,失常解决写操作。 原理: bgsave 子过程是由主线程 fork 生成的,能够共享主线程的所有内存数据。bgsave 子过程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。如果主线程要批改一块数据(例如键值对 C),那么,这块数据就会被复制一份,生成该数据的正本(键值对 C’)。而后,主线程在这个数据正本上进行批改。同时,bgsave 子过程能够持续把原来的数据(键值对 C)写入 RDB 文件。这既保证了快照的完整性,也容许主线程同时对数据进行批改,防止了对失常业务的影响。 快照开销增大磁盘带宽压力fork创立过程自身会阻塞主线程,而且主线程的内存越大,阻塞工夫越长。增量快照Redis 4.0 中提出了一个混合应用 AOF 日志和内存快照的办法:设置的参数是: aof-use-rdb-preamble yes 内存快照以肯定的频率执行在两次快照之间,应用 AOF 日志记录这期间的所有命令操作。小结数据不能失落时,内存快照和 AOF 的混合应用是一个很好的抉择;如果容许分钟级别的数据失落,能够只应用 RDB;如果只用 AOF,优先应用 everysec 的配置选项,因为它在可靠性和性能之间取了一个均衡。问题:2核4G服务器Redis数据大小2G,读写比2:8的场景,用RDB做长久化的危险?答案:a、内存资源危险:长久化过程中,“写时复制”会重新分配整个实例80%的内存正本,大概1.6GB内存,如果此时父过程又有大量新key写入,很快机器内存就会被吃光,如果机器开启了Swap机制,那么Redis会有一部分数据被换到磁盘上,当Redis拜访这部分在磁盘上的数据时,性能会急剧下降,曾经达不到高性能的规范(能够了解为文治被废)。如果机器没有开启Swap,会间接触发OOM,父子过程会面临被零碎kill掉的危险。b、CPU资源危险:尽管子过程在做RDB长久化,但生成RDB快照过程会耗费大量的CPU资源 06 主从统一高可靠性数据尽量少失落:AOF和RDB服务尽量少中断:减少正本冗余量,将一份数据同时保留在多个实例上读写拆散Redis 提供了主从库模式,以保证数据正本的统一,主从库之间采纳的是读写拆散的形式。 读操作:主库、从库都能够接管;写操作:首先到主库执行,而后,主库将写操作同步给从库。主从同步启动多个 Redis 实例时,它们互相通过 replicaof 命令造成主库和从库的关系,之后会依照三个阶段实现数据的第一次同步。 replicaof 172.16.19.3 63791. 建设连贯,协商同步从库给主库发送 psync 命令,示意要进行数据同步,主库依据这个命令的参数来启动复制。psync 命令蕴含了主库的 runID 和复制进度 offset 两个参数: ...

January 4, 2023 · 1 min · jiezi

关于redis:如何用-Redis-统计用户访问量

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构等外围知识点,欢送star~ Github地址:https://github.com/Tyson0314/... Gitee地址:https://gitee.com/tysondai/Ja... 拼多多有数亿的用户,那么对于某个网页,怎么应用Redis来统计一个网站的用户拜访数呢? 1、Hash 哈希是Redis的一种根底数据结构,Redis底层保护的是一个开散列,会把不同的key映射到哈希表上,如果是遇到关键字抵触,那么就会拉出一个链表进去。 当一个用户拜访的时候,如果用户登陆过,那么咱们就应用用户的id,如果用户没有登陆过,那么咱们也可能前端页面随机生成一个key用来标识用户 当用户拜访的时候,咱们能够应用HSET命令,key能够抉择URI与对应的日期进行拼凑,field能够应用用户的id或者随机标识,value能够简略设置为1。 当咱们要统计某一个网站某一天的访问量的时候,就能够间接应用HLEN来失去最终的后果了。 长处:简略,容易实现,查问也是十分不便,数据准确性十分高。 毛病:占用内存过大。随着key的增多,性能也会降落。网站访问量不高还行,拼多多这种数亿PV的网站必定顶不住。 2、Bitset 咱们晓得,对于一个32位的int,如果咱们只用来记录id,那么只可能记录一个用户,但如果咱们转成2进制,每位用来示意一个用户,那么咱们就可能一口气示意32个用户,空间节俭了32倍! 对于有大量数据的场景,如果咱们应用bitset,那么能够节俭十分多的内存。 对于没有登陆的用户,咱们也能够应用哈希算法,把对应的用户标识哈希成一个数字id。bitset十分的节俭内存,假如有1亿个用户,也只须要100000000/8/1024/1024约等于12兆内存。 Redis曾经为咱们提供了SETBIT的办法,应用起来十分的不便,咱们能够看看上面的例子。 咱们在item页面能够不停地应用SETBIT命令,设置用户曾经拜访了该页面,也能够应用GETBIT的办法查问某个用户是否拜访。最初咱们通过BITCOUNT能够统计该网页每天的拜访数量。 长处: 占用内存更小,查问不便,能够指定查问某个用户,数据可能略有瑕疵,对于非登陆的用户,可能不同的key映射到同一个id,否则须要保护一个非登陆用户的映射,有额定的开销。 毛病: 如果用户十分的稠密,那么占用的内存可能比办法一更大。 3、概率算法 对于拼多多这种多个页面都可能十分多访问量的网站,如果所须要的数量不必那么精确,能够应用概率算法。 事实上,咱们对一个网站的UV的统计,1亿跟1亿零30万其实是差不多的。 在Redis中,曾经封装了HyperLogLog算法,他是一种基数评估算法。这种算法的特色,个别都是数据不存具体的值,而是存用来计算概率的一些相干数据。 当用户拜访网站的时候,咱们能够应用PFADD命令,设置对应的命令,最初咱们只有通过PFCOUNT就能顺利计算出最终的后果,因为这个只是一个概率算法,所以可能存在0.81%的误差。 长处: 占用内存极小,对于一个key,只须要12kb。对于拼多多这种超多用户的特地实用。 毛病: 查问指定用户的时候,可能会出错,毕竟存的不是具体的数据。总数也存在肯定的误差。 下面就是常见的3种实用Redis统计网站用户拜访数的办法了。 最初给大家分享一个Github仓库,下面有大彬整顿的300多本经典的计算机书籍PDF,包含C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,能够star一下,下次找书间接在下面搜寻,仓库继续更新中~ Github地址:https://github.com/Tyson0314/...

January 4, 2023 · 1 min · jiezi

关于redis:Redis核心技术笔记0304

03 单线程Redis的单线程指的是网络IO和键值对读写是由一个线程来实现的;其余性能如长久化、异步删除、集群同步等是由额定的线程执行的。注:Redis6.0网络IO已改为多线程执行,以解决单线程的性能瓶颈。多线程的开销并发访问控制问题:零碎中通常会存在被多线程同时拜访的共享资源,比方一个共享的数据结构。当有多个线程要批改这个共享资源时,为了保障共享资源的正确性,就须要有额定的机制进行保障,而这个额定的机制,就会带来额定的开销。 Redis高效起因Redis应用单线程模型能达到每秒十万级的解决能力,起因有: 大部分操作在内存上实现高效的数据结构,如哈希表和跳表多路复用机制,使其在网络IO操作中能并发解决大量的客户端申请,实现高吞吐 IO模型和阻塞点Redis工作流程: 网络IO解决: 监听客户端申请:bind/listen和客户端建设连贯:accept从socket中读取申请:recv解析客户端发送申请:parse键值数据读写:get网络IO解决:send阻塞点:accept和recv当 Redis 监听到一个客户端有连贯申请,但始终未能胜利建设起连贯时,会阻塞在 accept() 函数这里,导致其余客户端无奈和 Redis 建设连贯。当 Redis 通过 recv() 从一个客户端读取数据时,如果数据始终没有达到,Redis 也会始终阻塞在 recv()。 非阻塞模型套接字简略的说就是通信的两方的一种约定,用套接字中的相干函数来实现通信过程。次要有3个参数:通信的目标IP地址、应用的传输层协定(TCP或UDP)和应用的端口号。 socket模型在 socket 模型中,不同操作调用后会返回不同的套接字类型。socket() 办法会返回被动套接字,而后调用 listen() 办法,将被动套接字转化为监听套接字,此时,能够监听来自客户端的连贯申请。最初,调用 accept() 办法接管达到的客户端连贯,并返回已连贯套接字。 非阻塞模式当 Redis 调用 accept() 但始终未有连贯申请达到时,Redis 线程能够返回解决其余操作,而不必始终期待。Redis 调用 recv() 后,如果已连贯套接字上始终没有数据达到,Redis 线程同样能够返回解决其余操作。 IO多路复用机制Linux 中的 IO 多路复用机制是指一个线程解决多个 IO 流,就是咱们常常听到的 select/epoll 机制。简略来说,在 Redis 只运行单线程的状况下,该机制容许内核中,同时存在多个监听套接字和已连贯套接字。内核会始终监听这些套接字上的连贯申请或数据申请。一旦有申请达到,就会交给 Redis 线程解决,这就实现了一个 Redis 线程解决多个 IO 流的成果。Redis 网络框架调用 epoll 机制,让内核监听这些套接字。此时,Redis 线程不会阻塞在某一个特定的监听或已连贯套接字上,也就是说,不会阻塞在某一个特定的客户端申请解决上。正因为此,Redis 能够同时和多个客户端连贯并解决申请,从而晋升并发性。为了在申请达到时能告诉到 Redis 线程,select/epoll 提供了基于事件的回调机制,即针对不同事件的产生,调用相应的处理函数。事件回调机制select/epoll 一旦监测到 FD 上有申请达到时,就会触发相应的事件。所有增加到epoll中的事件都会与设施(网卡)驱动程序建设回调关系,也就是说,一旦监测到fd上有申请达到时,就会触发相应的事件,当相应的事件产生时会调用回调办法。这个回调办法在内核中叫ep_poll_callback,它会将产生的事件增加到rdlist双链表中。这些事件会被放进一个事件队列,Redis 单线程对该事件队列一直进行解决。 Redis 无需始终轮询是否有申请理论产生,这就能够防止造成 CPU 资源节约。Redis 在对事件队列中的事件进行解决时,会调用相应的处理函数,这就实现了基于事件的回调。因为 Redis 始终在对事件队列进行解决,所以能及时响应客户端申请,晋升 Redis 的响应性能。04 AOF日志AOF(Append Only File)是写后日志,Redis先执行命令把数据写入内存,而后才记录日志。 ...

December 29, 2022 · 1 min · jiezi

关于redis:Redis核心技术笔记0102

01 KV数据库构造能够存哪些数据对于键值数据库来说,根本的数据模型是key-value模型。咱们对于KV数据库选项时,一个重要的思考因素是它反对的value类型:Memcached仅反对stringRedis反对String、哈希表、列表、汇合等 数据操作PUT/SET:新写入或更新一个kv对GET:依据key获取对应valueDELETE:依据key删除整个kv对SACN:依据一段key范畴返回相应value 数据库内部结构拜访框架索引模块:定位键值对地位操作模块存储模块 拜访框架函数库调用网络框架:socket通信+申请解析(Memcached、Redis) 索引模块Memcached 和 Redis 采纳哈希表作为 key-value 索引起因:键值数据保留在内存中,内存的高性能随机拜访个性能够很好地与哈希表 O(1) 的操作复杂度相匹配。操作模块不同操作的具体逻辑: GET/SCAN:依据 value 的存储地位返回 valuePUT:为键值对分配内存空间DELETE:删除键值对,并开释相应的内存空间,这个过程由分配器实现。 存储模块重启后疾速提供服务: 内存分配器长久化(AOF/RDB) Redis其余个性高可用集群:主从、哨兵高可扩大集群:数据分片 02 底层数据结构底层数据结构一共有 6 种,别离是简略动静字符串、双向链表、压缩列表、哈希表、跳表和整数数组。键和值构造组织:哈希表String:简略动静字符串(sds)List:双向链表(quicklist),压缩列表(ziplist)hash:压缩列表,hash表zset(sortset):hash表,跳表(skiplist)set:hash表,整数数组 哈希表哈希表就是数组,数组的每个元素称为一个哈希桶,保留指向具体值的指针。长处:能够用 O(1) 的工夫复杂度来疾速查找到键值对。危险:哈希抵触和rehash可能带来的操作阻塞。 哈希抵触两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。解决办法:链式哈希,即同一个哈希桶中的多个元素用一个链表来保留,它们之间顺次用指针连贯。rehash减少现有的哈希桶数量,让逐步增多的 entry 元素能在更多的桶之间扩散保留,缩小单个桶中的元素数量,从而缩小单个桶中的抵触。起因:哈希抵触链上的元素只能通过指针逐个查找再操作,会导致某些哈希抵触链过长,进而导致这个链上的元素查找耗时长,效率升高。备注:Redis 默认应用了两个全局哈希表。执行过程:给哈希表 2 调配更大的空间,例如是以后哈希表 1 大小的两倍;把哈希表 1 中的数据从新映射并拷贝到哈希表 2 中(渐进式rehash);开释哈希表 1 的空间,哈希表 1留作下一次 rehash 扩容备用。 渐进式rehashRedis 依然失常解决客户端申请,每解决一个申请时,从哈希表 1 中的第一个索引地位开始,顺带着将这个索引地位上的所有 entries 拷贝到哈希表 2 中;等解决下一个申请时,再顺带拷贝哈希表 1 中的下一个索引地位的 entries。问题1:渐进式 rehash在解决拷贝的时候,还在解决客户端申请,这个时候怎么保障客户端申请的数据不会落在之前曾经拷贝过了的索引上?或者说如果落在之前的索引上了怎么再回去拷贝到表2中?答:因为在进行渐进式rehash的过程中,字典会同时应用ht[0]和ht[1]两个哈希表,所以在渐进式rehash进行期间,字典的删除(delete)、查找(find)、更新(update)等操作会在两个哈希表上进行。例如,要在字典外面查找一个键的话,程序会先在ht[0]外面进行查找,如果没找到的话,就会持续到ht[1]外面进行查找,诸如此类。另外,在渐进式rehash执行期间,新增加到字典的键值对一律会被保留到ht[1]外面,而ht[0]则不再进行任何增加操作,这一措施保障了ht[0]蕴含的键值对数量会只减不增,并随着rehash操作的执行而最终变成空表。 问题2:一次申请一个entrys,那后续如果再也没有申请来的时候,余下的entrys是怎么解决的呢?是就留在hash1中了还是有定时工作后盾更新过来呢答案: 渐进式rehash执行时,除了依据键值对的操作来进行数据迁徙,Redis自身还会有一个定时工作在执行rehash,如果没有键值对操作时,这个定时工作会周期性地(例如每100ms一次)搬移一些数据到新的哈希表中,这样能够缩短整个rehash的过程。 汇合数据操作效率一个汇合类型的值,第一步是通过全局哈希表找到对应的哈希桶地位,第二步是在汇合中再增删改查。与汇合的底层数据结构无关: 应用哈希表实现的汇合,比应用链表实现的汇合拜访效率更高。操作自身的执行特点无关:读写一个元素的操作要比读写所有元素的效率高 底层数据结构汇合类型的底层数据结构次要有 5 种:整数数组、双向链表、哈希表、压缩列表和跳表。整数数组和双向链表程序读写,通过数组下标/链表指针一一元素拜访,操作复杂度根本是 O(N),操作效率比拟低; 压缩列表压缩列表构造: 相似于一个数组,数组中的每一个元素都对应保留一个数据。表头有三个字段 zlbytes、zltail 和 zllen,别离示意列表长度、列表尾的偏移量和列表中的 entry 个数;表尾还有一个 zlend,示意列表完结。查问效率:第一个元素和最初一个元素,能够通过表头三个字段的长度间接定位,复杂度是 O(1)其余元素只能一一查找,此时的复杂度就是 O(N) ...

December 28, 2022 · 1 min · jiezi

关于redis:如何理解-redis-持久化有几种方式

其实redis就是一种高级的以键值对模式存储数据的数据库,而它的益处就是他能够反对数据的长久化,其实redis之所以会有这样的长处,次要是因为,redis的数据都是寄存在内存中的,如果不配置长久化,那么在redis进行重启的时候,就会造成数据的失落,于是redis开启了数据的长久化性能,将所有的数据保留到磁盘中,当redis重启之后,就能够间接从磁盘中复原数据,所以redis的长久化性能,次要就是为了避免服务器宕机而造成的数据失落。 redis也提供了两种不同的长久化形式: 第一种是:RDB长久化,RDB是Redis DataBase的缩写,redis默认的长久化形式,简略来讲就是将redis在内存中的数据库记录依照指定的工夫转存到磁盘当中,其实就是肯定工夫距离内对你的数据进行一个快照存储,在默认的状况下,redis在实现快照存储后就会将这些数据保留在一个dump.rdb的文件中,当redis运行的时候,RDB就会将内存中的数据集存储到磁盘当中,在redis进行重启的时候,就能够通过载入RDB文件到RDB程序进行数据的同步复原。 长处:因为服务区在执行保留dump.rdb文件时,首先须要redis去调用forks()时,就会同时领有父过程和子过程,而子过程其实就是将这些数据写入到一个长期的RDB文件当中,当子过程实现写入后,redis就会用一个新的RDB文件替换掉旧的RDB文件,并且将旧的RDB文件删除,所以因为这样的工作形式,RDB长久化形式就只有一个dump.rdb文件,十分不便长久化,而且由子过程实现写的操作,让主过程能够持续解决命令,这样能够使得IO最大化,应用独自的子过程来进行长久化,主过程就不会进行任何的IO操作,这样能够保障redis的高性能。 毛病:因为RDB是依照指定的工夫每隔一段时间就要进行一次长久化,如果在长久化的过程中redis产生故障,那么仍然会产生数据失落,所以个别都在数据要求不太谨严的时候应用这种形式。 第二种:AOF长久化,AOF是Append Only File的缩写,默认AOF是不开启的,须要在redis.conf配置文件中手动开启,这种长久化形式就是将redis执行的每一次命令记录到独自的日志文件当中,当还原数据时,只须要将这些备份的指令再从新执行一遍即可。redis的配置文件中存在三种不同的AOF长久化形式,别离是: appendfsync always:每次有数据批改产生时都会写入AOF文件,这样会重大升高Redis的速度 appendfsync everysec:每秒钟同步一次,将多个写命令同步到硬盘 appendfsync no:让操作系统决定何时进行同步 而且因为AOF长久化对日志文件的写入操作采纳的是append模式,应用这种模式的益处就是,即便在写入的过程中呈现宕机景象,也不会毁坏日志文件中曾经存在的内容,然而如果咱们本次操作只写入了一半数据就呈现了零碎解体问题,也不必放心,在redis下一次启动之前,咱们能够通过redis-check-aof工具来帮忙解决这种数据一致性的问题。 长处:数据安全,因为AOF有写回策略机制,比方:always,意思就是能够在每个执行命令执行完之后,立即同步的将日志写回磁盘,而且AOF长久化快,可能缩小数据失落的量,在配置了everysec的状况下最多只会失落秒级数据。 毛病:在等同数据量的状况下,AOF文件的大小要比RDB文件大很多,如果应用它进行内存的复原须要肯定的工夫。 针对以上论述,在抉择长久化形式上,一般来说,如果想要达到足以媲美关系型数据库的数据安全性,那么就应该同时应用两种长久化性能(redis4.0开始反对RDB和AOF混合长久化),在这种状况下,当redis重启的时候就会优先载入AOF文件来复原原始数据,因为通常这种状况下,AOF文件保留数据集要比RDB文件保留数据集更加残缺,如果你十分关怀你的数据,然而依然能够接受一些放弃在分钟之内的数据失落,那么你能够只抉择应用RDB长久化,因为它能够更快,然而会有一些分钟之内的数据失落是它的毛病。 有很多人都只应用AOF长久化,但其实不太举荐,因为定时生成RDB快照十分不便于进行数据库的备份,并且RDB复原数据集的速度也要比AOF复原的速度快,除此之外,应用RDB也能够无效躲避AOF程序的bug,当然如果你只心愿你的数据在服务器运行的时候存在,也能够不抉择应用任何长久化形式。 以上就是针对该问题做的简要论述,心愿可能小小的帮忙到大家。

December 26, 2022 · 1 min · jiezi

关于redis:redis解析

redis面试数据结构(数据类型和数据结构:sds,zipList,quickList,skipList) StringString:String是redis最根本的类型,一个key对应一个value。redis的string类型能够蕴含任何数据,比方jpg图片或者序列化的对象。string类型的值最大能存储512M。String底层采纳了SDS设计。 SDS 结构设计: len:SDS 所保留的字符串长度。 alloc:调配给字符数组的空间长度。批改字符串的时候,能够通过 alloc - len 计算 察看是否须要对空间扩容。 flags:数据类型。buf[]:保留理论数据。SDS理论益处: 1、SDS保留了数据长度,所以工夫复杂度就是O(1)。 2、空间预调配:SDS 被批改后,程序不仅会为 SDS 调配所须要的必须空间,还会调配额定的未应用空间。3、惰性空间开释:当数据进行缩短操作,多余空间不会被回收,前面须要增长时,就不必额定去拓展空间。ListList:是一个链表。一个key对应一个链表。先入先出,能够通过索引查问,不便新增和删除。List底层数据结构又两种: ZipList(压缩表):列表对象保留的所有字符串元素的长度都小于 64 字节并且保留的元素数量小于 512 个。 QuickList(疾速表):应用quicklist,它是一个双向链表,而且是一个基于ziplist的双向链表,quicklist的每个节点都是一个ziplist。ZipList数据结构: zlbytes,记录整个压缩列表占用对内存字节数; zltail,记录压缩列表「尾部」节点间隔起始地址由多少字节,也就是列表尾的偏移量; zllen,记录压缩列表蕴含的节点数量; zlend,标记压缩列表的完结点,非凡值 OxFF(十进制255)。Entry节点形成: prevlen,记录了前一个节点的长度; encoding,记录了以后节点理论数据的类型以及长度; data,记录了以后节点的理论数据;压缩表长处:将长度较短的数据用压缩链表,可能时内存更紧凑,节约内存。 为什么数据量较大和数据长度较长不适宜应用ZipList?压缩表每个Entry的prevlen都记录了上一个节点的长度。如果前一个节点的长度小于 254 字节,那么 prevlen 属性须要用 1 字节的空间来保留这个长度值;如果前一个节点的长度大于等于 254 字节,那么 prevlen 属性须要用 5 字节的空间来保留这个长度值;假如呈现了间断多个节点长度在250~253字节的数据,忽然新增了也该字节长度超过了254数据,会导致后续节点的prevlen都从原来的1个字节拓展到4个字节,该节点就须要拓展空间。而后又导致了该节点的后续节点的prevlen长度变更......而后始终继续上来,这种就叫连锁更新。 quickList长处:quicklist 是 ziplist 和 linkedlist 的混合体,它将 linkedlist 按段切分,每一段应用 ziplist 来紧凑存储,多个 ziplist 之间应用双向指针串接起来。对于quickList曾经将ZipList分成了很多小片区,在小片区内产生连锁更新也是能够承受的。 Hash相似Java的HashMap,数组+链表。1:当Hash抵触时将具备雷同哈希值的数据链接起来,以便这些数据在表中依然能够被查问到。 2:当Hash抵触过多,导致数据链表太长就会产生rehash. 渐进式 Rehash: 为了防止 rehash 在数据迁徙过程中,因拷贝数据的耗时,影响 Redis 性能的状况,所以 Redis 采纳了渐进式 rehash,也就是将数据的迁徙的工作不再是一次性迁徙实现,而是分屡次迁徙。渐进式 rehash 步骤如下: ...

December 23, 2022 · 1 min · jiezi

关于redis:聊聊如何利用redis实现多级缓存同步

前言前阵子加入业务部门的技术计划评审,故事的背景是这样:业务部门上线一个专为公司高管应用的零碎。这个零碎技术架构形如下图 按理来说这个零碎因为受众很小,能够说基本上没并发,业务也没很简单,但就是这么一个零碎,间断2次呈现数据库宕机,而导致系统无奈失常运行。因为这几次事变,业务部门负责人组织这次技术计划评审,主题如何防止再次出现相似这种故障? 过后有个比拟资深的技术,他提出当数据库呈现宕机时,能够切换到redis,redis外面缓存热点数据,另外一个技术说他同意这个计划,但他提出不须要用到redis,间接用本地缓存即可。因为tomcat是集群部署,就等于本地缓存也具备了集群能力。而如果切换成redis,redis也可能会挂景象。 而后那个说用redis的技术又说,用本地缓存,如果数据变更,其余集群的本地缓存如何感知数据曾经发生变化,他感觉还是用redis靠谱,首先redis容量必定是比本地缓存高,而且redis也能够部署集群,可用性能够失去保障,利用redis集中存储,当数据产生变更,其余集群也能够感知到。 在他们争论不休的状况下,有人提出不然就同时应用,当数据库挂了,切换到redis,redis挂了,应用本地缓存。这个计划失去不少人的批准,包含这两个争论不休的技术。但应用这种计划,就得思考多级缓存数据如何同步。 铺垫了那么多,才刚要说明天的主题,多级缓存数据如何进行同步 多级缓存数据同步1、计划一:应用MQ或者canal进行同步计划如下图 如果是应用MQ来同步,实现计划大抵如下,数据产生变更,业务零碎发送变更数据到MQ,其余零碎从MQ生产。 如果是应用canal,实现计划大抵如下,数据产生变更,canal会接到到变更的binlog,业务零碎编写canal tcp客户端,和canal进行交互获取变更数据 2、计划二:利用redis6提供的客户端缓存机制计划如下图 redis6客户端缓存实现机制原理,官网有具体文档介绍,感兴趣大家能够查看如下链接https://redis.io/docs/manual/client-side-caching/ 这边就讲下如何应用 如何应用redis6客户端缓存前置条件:redis服务端版本必须是>=6。lettuce版本>=6 目前java的redis客户端找了一圈,貌似只有lettuce 6反对,其余客户端预计前期会反对 1、我的项目中pom引入lettuce GAV <dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>6.1.8.RELEASE</version> </dependency>2、利用lettuce6提供的ClientSideCaching进行实现 /** * 客户端缓存同步 * */ public String getClientCacheValue(Map<String,String> clientCache,String key){ StatefulRedisConnection<String, String> connect = redisClient.connect(); // Map<String,String> clientCache = new ConcurrentHashMap<>(); CacheFrontend<String,String> frontend = ClientSideCaching.enable(CacheAccessor.forMap(clientCache), connect, TrackingArgs.Builder.enabled().noloop()); return frontend.get(key); }3、测试 @Override public void run(ApplicationArguments args) throws Exception { while(true){ System.out.println(lettuceRedisTemplate.getClientCacheValue("zhangsan")); TimeUnit.SECONDS.sleep(1); } }redis外面的zhangsan数据未产生变更时, 控制台输入的数据为咱们将redis zhangsan的明码改成9999, 看本地缓存是否立马捕捉到 ...

December 20, 2022 · 1 min · jiezi

关于redis:如何用Redis实现分布式锁

在开始提到Redis分布式锁之前,我想跟大家聊点Redis的基础知识。说一下Redis的两个命令:SETNX key value setnx 是SET if Not eXists(如果不存在,则 SET)的简写。 用法如图,如果不存在set胜利返回int的1,这个key存在了返回0。SETEX key seconds value复制代码将值 value 关联到 key ,并将 key 的生存工夫设为 seconds (以秒为单位)。如果 key 曾经存在,setex命令将覆写旧值。有小伙伴必定会纳闷万一set value 胜利 set time失败,那不就傻了么,这啊Redis官网想到了。setex是一个原子性(atomic)操作,关联值和设置生存工夫两个动作会在同一时间内实现。 我设置了10秒的生效工夫,ttl命令能够查看倒计时,负的阐明曾经到期了。跟大家讲这两个命名也是有起因的,因为他们是Redis实现分布式锁的要害。注释开始前还是看看场景: 我仍然是创立了很多个线程去扣减库存inventory,不出意外的库存扣减程序变了,最终的后果也是不对的。单机加synchronized或者Lock这些惯例操作我就不说了好吧,后果必定是对的。 我先实现一个简略的Redis锁,而后咱们再实现分布式锁,可能更不便大家的了解。还记得下面我说过的命令么,实现一个单机的其实比较简单,你们先思考一下,别往下看。setnx 能够看到,第一个胜利了,没开释锁,前面的都失败了,至多程序问题问题是解决了,只有加锁,缩放前面的拿到,开释如此循环,就能保障依照程序执行。然而你们也发现问题了,还是一样的,第一个仔set胜利了,然而忽然挂了,那锁就始终在那无奈失去开释,前面的线程也永远得不到锁,又死锁了。所以....setex晓得我之前说这个命令的起因了吧,设置一个过期工夫,就算线程1挂了,也会在生效工夫到了,主动开释。我这里就用到了nx和px的联合参数,就是set值并且加了过期工夫,这里我还设置了一个过期工夫,就是这工夫内如果第二个没拿到第一个的锁,就退出阻塞了,因为可能是客户端断连了。 加锁整体加锁的逻辑比较简单,大家基本上都能看懂,不过我拿到以后工夫去减开始工夫的操作感觉有点笨, System.currentTimeMillis()耗费很大的。/** * 加锁 * * @param id * @return */public boolean lock(String id) {    Long start = System.currentTimeMillis();    try {        for (; ; ) {            //SET命令返回OK ,则证实获取锁胜利            String lock = jedis.set(LOCK_KEY, id, params);            if ("OK".equals(lock)) {                return true;            }            //否则循环期待,在timeout工夫内仍未获取到锁,则获取失败            long l = System.currentTimeMillis() - start;            if (l >= timeout) {                return false;            }            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    } finally {        jedis.close();    }} System.currentTimeMillis耗费大,每个线程进来都这样,我之前写代码,就会在服务器启动的时候,开一个线程一直去拿,调用方间接获取值就好了,不过也不是最优解,日期类还是有很多好办法的。@Servicepublic class TimeServcie {    private static long time;    static {        new Thread(new Runnable(){            @Override            public void run() {                while (true){                    try {                        Thread.sleep(5);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    long cur = System.currentTimeMillis();                    setTime(cur);                }            }        }).start();    }     public static long getTime() {        return time;    }     public static void setTime(long time) {        TimeServcie.time = time;    }} 解锁解锁的逻辑更加简略,就是一段Lua的拼装,把Key做了删除。你们发现没,我下面加锁解锁都用了UUID,这就是为了保障,谁加锁了谁解锁,要是你删掉了我的锁,那不乱套了嘛。LUA是原子性的,也比较简单,就是判断一下Key和咱们参数是否相等,是的话就删除,返回胜利1,0就是失败。/** * 解锁 * * @param id * @return */public boolean unlock(String id) {    String script =            "if redis.call('get',KEYS[1]) == ARGV[1] then" +                    "   return redis.call('del',KEYS[1]) " +                    "else" +                    "   return 0 " +                    "end";    try {        String result = jedis.eval(script, Collections.singletonList(LOCK_KEY), Collections.singletonList(id)).toString();        return "1".equals(result) ? true : false;    } finally {        jedis.close();    }} 验证咱们能够用咱们写的Redis锁试试成果,能够看到都依照程序去执行了 思考大家是不是感觉完满了,然而下面的锁,有不少瑕疵的,我没思考很多点,你或者能够思考一下,源码我都开源到我的GItHub了。而且,锁个别都是须要可重入行的,下面的线程都是执行完了就开释了,无奈再次进入了,进去也是从新加锁了,对于一个锁的设计来说必定不是很正当的。我不打算手写,因为都有现成的,他人帮咱们写好了。redissonredisson的锁,就实现了可重入了,然而他的源码比拟艰涩难懂。应用起来很简略,因为他们底层都封装好了,你连贯上你的Redis客户端,他帮你做了我下面写的所有,而后更完满。简略看看他的应用吧,跟失常应用Lock没啥区别。ThreadPoolExecutor threadPoolExecutor =        new ThreadPoolExecutor(inventory, inventory, 10L, SECONDS, linkedBlockingQueue);long start = System.currentTimeMillis();Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");final RedissonClient client = Redisson.create(config);final RLock lock = client.getLock("lock1"); for (int i = 0; i <= NUM; i++) {    threadPoolExecutor.execute(new Runnable() {        public void run() {            lock.lock();            inventory--;            System.out.println(inventory);            lock.unlock();        }    });}long end = System.currentTimeMillis();System.out.println("执行线程数:" + NUM + "   总耗时:" + (end - start) + "  库存数为:" + inventory); 下面能够看到我用到了getLock,其实就是获取一个锁的实例。RedissionLock也没做啥,就是相熟的初始化。public RLock getLock(String name) {    return new RedissonLock(connectionManager.getCommandExecutor(), name);} public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {    super(commandExecutor, name);    //命令执行器    this.commandExecutor = commandExecutor;    //UUID字符串    this.id = commandExecutor.getConnectionManager().getId();    //外部锁过期工夫    this.internalLockLeaseTime = commandExecutor.                getConnectionManager().getCfg().getLockWatchdogTimeout();    this.entryName = id + ":" + name;} 加锁有没有发现很多跟Lock很多类似的中央呢?尝试加锁,拿到以后线程,而后我结尾说的ttl也看到了,是不是一切都是那么相熟?public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {        //以后线程ID    long threadId = Thread.currentThread().getId();    //尝试获取锁    Long ttl = tryAcquire(leaseTime, unit, threadId);    // 如果ttl为空,则证实获取锁胜利    if (ttl == null) {        return;    }    //如果获取锁失败,则订阅到对应这个锁的channel    RFuture<RedissonLockEntry> future = subscribe(threadId);    commandExecutor.syncSubscription(future);     try {        while (true) {            //再次尝试获取锁            ttl = tryAcquire(leaseTime, unit, threadId);            //ttl为空,阐明胜利获取锁,返回            if (ttl == null) {                break;            }            //ttl大于0 则期待ttl工夫后持续尝试获取            if (ttl >= 0) {                getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);            } else {                getEntry(threadId).getLatch().acquire();            }        }    } finally {        //勾销对channel的订阅        unsubscribe(future, threadId);    }    //get(lockAsync(leaseTime, unit));} 获取锁获取锁的时候,也比较简单,你能够看到,他也是一直刷新过期工夫,跟我下面一直去拿以后工夫,校验过期是一个情理,只是我比拟毛糙。private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) {     //如果带有过期工夫,则依照一般形式获取锁    if (leaseTime != -1) {        return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);    }        //先依照30秒的过期工夫来执行获取锁的办法    RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(        commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),        TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);            //如果还持有这个锁,则开启定时工作一直刷新该锁的过期工夫    ttlRemainingFuture.addListener(new FutureListener<Long>() {        @Override        public void operationComplete(Future<Long> future) throws Exception {            if (!future.isSuccess()) {                return;            }             Long ttlRemaining = future.getNow();            // lock acquired            if (ttlRemaining == null) {                scheduleExpirationRenewal(threadId);            }        }    });    return ttlRemainingFuture;} 底层加锁逻辑你可能会想这么多操作,在一起不是原子性不还是有问题么?大佬们必定想得到呀,所以还是LUA,他应用了Hash的数据结构。次要是判断锁是否存在,存在就设置过期工夫,如果锁曾经存在了,那比照一下线程,线程是一个那就证实能够重入,锁在了,然而不是以后线程,证实他人还没开释,那就把剩余时间返回,加锁失败。是不是有点绕,多了解一遍。<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit,                                 long threadId, RedisStrictCommand<T> command) {         //过期工夫        internalLockLeaseTime = unit.toMillis(leaseTime);         return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,                  //如果锁不存在,则通过hset设置它的值,并设置过期工夫                  "if (redis.call('exists', KEYS[1]) == 0) then " +                      "redis.call('hset', KEYS[1], ARGV[2], 1); " +                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +                      "return nil; " +                  "end; " +                  //如果锁已存在,并且锁的是以后线程,则通过hincrby给数值递增1                  "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +                      "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +                      "return nil; " +                  "end; " +                  //如果锁已存在,但并非本线程,则返回过期工夫ttl                  "return redis.call('pttl', KEYS[1]);",        Collections.<Object>singletonList(getName()),                 internalLockLeaseTime, getLockName(threadId));    } 解锁锁的开释次要是publish开释锁的信息,而后做校验,一样会判断是否以后线程,胜利就开释锁,还有个hincrby递加的操作,锁的值大于0阐明是可重入锁,那就刷新过期工夫。如果值小于0了,那删掉Key开释锁。是不是又和AQS很像了?AQS就是通过一个volatile润饰status去看锁的状态,也会看数值判断是否是可重入的。所以我说代码的设计,最初就万剑归一,都是一样的。public RFuture<Void> unlockAsync(final long threadId) {    final RPromise<Void> result = new RedissonPromise<Void>();        //解锁办法    RFuture<Boolean> future = unlockInnerAsync(threadId);     future.addListener(new FutureListener<Boolean>() {        @Override        public void operationComplete(Future<Boolean> future) throws Exception {            if (!future.isSuccess()) {                cancelExpirationRenewal(threadId);                result.tryFailure(future.cause());                return;            }            //获取返回值            Boolean opStatus = future.getNow();            //如果返回空,则证实解锁的线程和以后锁不是同一个线程,抛出异样            if (opStatus == null) {                IllegalMonitorStateException cause =                     new IllegalMonitorStateException("                        attempt to unlock lock, not locked by current thread by node id: "                        + id + " thread-id: " + threadId);                result.tryFailure(cause);                return;            }            //解锁胜利,勾销刷新过期工夫的那个定时工作            if (opStatus) {                cancelExpirationRenewal(null);            }            result.trySuccess(null);        }    });     return result;} protected RFuture<Boolean> unlockInnerAsync(long threadId) {    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, EVAL,                //如果锁曾经不存在, 公布锁开释的音讯            "if (redis.call('exists', KEYS[1]) == 0) then " +                "redis.call('publish', KEYS[2], ARGV[1]); " +                "return 1; " +            "end;" +            //如果开释锁的线程和已存在锁的线程不是同一个线程,返回null            "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +                "return nil;" +            "end; " +            //通过hincrby递加1的形式,开释一次锁            //若剩余次数大于0 ,则刷新过期工夫            "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +            "if (counter > 0) then " +                "redis.call('pexpire', KEYS[1], ARGV[2]); " +                "return 0; " +            //否则证实锁曾经开释,删除key并公布锁开释的音讯            "else " +                "redis.call('del', KEYS[1]); " +                "redis.call('publish', KEYS[2], ARGV[1]); " +                "return 1; "+            "end; " +            "return nil;",    Arrays.<Object>asList(getName(), getChannelName()),         LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId)); } 总结这个写了比拟久,然而不是因为简单什么的,是因为集体工作的起因,最近事件很多嘛,还是那句话,程序员才是我的本职写文章只是个喜好,不能轻重倒置了。大家会发现,你学懂一个技术栈之后,学新的会很快,而且也能发现他们的设计思维和技巧真的很奇妙,也总能找到类似点,和让你惊叹的点。就拿Doug Lea写的AbstractQueuedSynchronizer(AQS)来说,他写了一行代码,你可能看几天才能看懂,大佬们的思维是真的牛。我看源码有时候也头疼,然而去谷歌一下,本人了解一下,忽然豁然开朗的时候感觉所有又很值。学习就是一条时而郁郁寡欢,时而开环大笑的路,大家加油,咱们成长路上一起共勉。一个在互联网得过且过的工具人。注:如果本篇博客有任何谬误和倡议,欢送人才们留言,你快说句话啊!

December 14, 2022 · 1 min · jiezi

关于redis:万字好文带你入门-redis

引言|本文将会从:Redis 应用场景与介绍 -> 数据结构与简略应用 -> 小性能大用处 -> 长久化、主从同步与缓存设计 -> 常识拓展 来书写,初学的童鞋只有能记住 Redis 是用来干嘛,各性能的应用场景有哪些,而后对 Redis 有个大略的意识就好啦,剩下的当前有须要的时候再来查看和实际吧! 一、Redis 介绍 Redis 是什么?Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它能够用作数据库、缓存和消息中间件。Redis 反对多种类型的数据结构,如 字符串(strings),散列(hashes), 列表(lists), 汇合(sets), 有序汇合(sorted sets) ,范畴查问, bitmaps, hyperloglogs 和 天文空间(geospatial) 索引半径查问。Redis 内置了复制(replication),LUA 脚本(Lua scripting),LRU驱动事件(LRU eviction),事务(transactions)和不同级别的 磁盘长久化(persistence),Redis 通过 哨兵(Sentinel) 和主动分区(Cluster)提供高可用性(high availability)。 Redis 个性速度快单节点读 110000次/s,写81000次/s数据寄存内存中用C语言实现,离操作系统更近单线程架构,6.0开始反对多线程(CPU、IO读写负荷)长久化数据的更新将异步地保留到硬盘(RDB和 AOF)多种数据结构不仅仅反对简略的 key-value 类型数据,还反对:字符串、hash、列表、汇合、有序汇合,反对多种编程语言功能丰富HyperLogLog、GEO、公布订阅、Lua脚本、事务、Pipeline、Bitmaps,key 过期简略稳固源码少、单线程模型主从复制Redis 反对数据的备份(master-slave)与集群(分片存储),以及领有哨兵监控机制。Redis 的所有操作都是原子性的,同时 Redis 还反对对几个操作合并后的原子性执行。Redis 典型应用场景缓存: 计数器: 音讯队列: 排行榜: 社交网络: Redis 高并发原理Redis是纯内存数据库,个别都是简略的存取操作,线程占用的工夫很多,工夫的破费次要集中在IO上,所以读取速度快Redis应用的是非阻塞 IO,IO 多路复用,应用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,缩小了线程切换时上下文的切换和竞争。Redis采纳了单线程的模型,保障了每个操作的原子性,也缩小了线程的上下文切换和竞争。Redis存储构造多样化,不同的数据结构对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,应用有序的数据结构放慢读取的速度。Redis采纳本人实现的事件分离器,效率比拟高,外部采纳非阻塞的执行形式,吞吐能力比拟大。Redis 装置这里只提供 linux 版本的装置部署 ...

December 1, 2022 · 12 min · jiezi

关于redis:Redis系列之如何高效使用

本文将从避免阻塞和内存节约两个方面介绍如和高效应用Reids。应用Redis时,咱们须要联合具体业务和Redis个性两方面来思考如何设计应用计划。须要两个从两个方面思考: 避免阻塞节约内存上面,咱们将就下面两个点开展阐明如何高效正当应用Redis。 避免阻塞从阻塞章节咱们晓得,引起Redis阻塞可能的起因有内因和外因两方面。 内因躲避缩小简单命令的应用,或者有节制的应用。上面这些命令能够看做简单命令(工夫复杂度为O(N)或者更高):SETRANGE, GETRANGE, MSET, MGET, MSETNX, HMSET, HMGET, HKEYS,HVALS, HGETALL, HSCAN, LTRIM, LINDEX, SMEMBERS, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, ZUNIONSTORE, ZINTERSTORE, SINTER, SINTERSTORE。这些命令当操作的key或者field过多时将会导致Redis过程阻塞。举例来说,对一个蕴含上十万甚至百万个field的hash执行hgetall操作,hgetall命令的工夫复杂度为O(N),此时N页特地大(上十万甚至百万)必然耗时很长。从这个例子,咱们能够发现至多两个不合理的中央: 这种有大量元素的数据不应该存在,因为,咱们并不能确定什么时候咱们对它执行了简单命令。如果真的不可躲避超多元素的状况,在获取多个元素或者全量元素时,务必应用scan之类命令,且确保每次获取元素数量在肯定范畴,比方50等。防止频繁生成RDB和AOF重写,尤其是高峰期。失常状况下,Redis比拟时候缓存类型数据,当然为了保证数据不失落,能够进行导出RDB和从新AOF。但须要确保一下几点: 不要执行save等同步命令;尽量不要在高峰期进行长久化操作;尽量在从实例上做长久化操作;如果必须频繁长久化,须要确保如下几点: 保障CPU、内存短缺,倡议CPU和内存留出肯定的buffer不要绑定CPU防止和CPU密集型服务混布如果多个Redis实例部署在同一台机器,留神布局好系统资源,能够思考错峰长久化,防止同时长久化导致系统资源开销霎时突增零碎尽量不要开启HugePage,避免复制内存页过大而拖慢执行工夫,且会导致长久化期间内存耗费增长防止单Redis实例负载过高。Redis是单线程服务,当负载过大必然影响整体性能,能够通过如下计划进步读写能力: 能够通过读写拆散,从实例承接局部读申请,来升高主实例压力;如果读写压力都很大的话,须要思考集群计划。外因躲避通常,引起服务的外因无外乎CPU、内存和网络,导致Redis阻塞的起因同样也须要从这几方面去思考。CPU竞争导致Redis阻塞的问题起因在阻塞章节曾经具体介绍过,对于解决方案,能够通过以下伎俩来躲避: 过程CPU资源竞争,倡议不要和其余多线程CPU密集型服务混布,尤其是线上环境。另外,如果流量趋势有稳定的服务,比方有早晚顶峰,倡议不要把流量稳定统一的服务混布。绑定CPU,绑定CPU(设置CPU亲和力affinity)是为了升高Redis过程在不同CPU来回切换导致缓存命中率降落等引起的性能问题,然而,过程的CPU亲和力会继承给子过程,Redis过程fork出的子过程也共享该CPU。因而,如果须要频繁长久化的Redis不倡议绑定CPU。节约内存系统优化缩小内存碎片,失常的碎片率(mem_fragmentation_ratio)在1.03左右。然而当存储的数据长短差别较大时,以下场景容易呈现高内存碎片问题: 频繁做更新操作,例如频繁对已存在的键执行append、setrange等更新操作。大量过期键删除,键对象过期删除后,开释的空间无奈失去充分利用,导致碎片率回升。呈现高内存碎片问题时常见的解决形式如下: 数据对齐:在条件容许的状况下尽量做数据对齐,比方数据尽量采纳数字类型或者固定长度字符串等,然而这要视具体的业务而定,有些场景无奈做到。平安重启:重启节点能够做到内存碎片重新整理,因而能够利用高可用架构,如Sentinel或Cluster,将碎片率过高的主节点转换为从节点,进行平安重启。RDB生成和AOF重写会fork子过程,进而导致内存耗费。总结如下: 失常状况下Redis产生的子过程并不需要耗费1倍的父过程内存,理论耗费依据期间写入命令量决定,然而仍然要预留出一些内存避免溢出。须要设置sysctl vm.overcommit_memory=1容许内核能够调配所有的物理内存,避免Redis过程执行fork时因零碎残余内存不足而失败。排查以后零碎是否反对并开启THP,如果开启倡议敞开,避免copy-onwrite期间内存适度耗费。用户优化减小键值字符串长度key能够通过字符串缩减来缩小长度value能够通过序列化和压缩来缩小存储,也能够能够通过业务侧优化缩小不必要的字段尽量应用set而非append因为字符串(SDS)存在预分配机制,日常开发中要小心预调配带来的内存节约。 表-2 set & append 比照测试 操作数据量key大小value大小used_memory_humanused_memory_rss_humanmem_fragmentation_ratio阐明set100w20B100B176.66M180.19M1.02--set100w20B200B283.47M287.66M1.01 set && append100w20B100B+100B497.10M503.19M1.01先set,value大小为100B,随后append大小100B的数据从下面的试验能够看出,同样存储100w条key大小为20B,value大小为200B的数据,通过set和append操作实现的和间接应用set实现多了近75% 的存储耗费。 字符串重构字符串重构:指不肯定把每份数据作为字符串整体存储,像json这样的数据能够应用hash构造,这样做有如下收益: 应用二级构造存储也能帮咱们节俭内存。同时能够应用hmget、hmset命令反对字段的局部读取批改,而不必每次整体存取。留神,这样样做的一个前提是json key-value对中value绝对较小,上面是一个测试例子。 {"id" : "12345678","title" : "redis-memory-optimization","chinese_url" : "http://www.redis.cn/topics/memory-optimization.html","english_url" : "https://redis.io/topics/memory-optimization"}代码-2 一个json实例 表-3 hash优化测试 数据量数据结构编码keyvalue配置used_memory_humanused_memory_rss_humanmem_fragmentation_ratio阐明100wstringraw20Bjson字符串默认252.95M258.04M1.02 100whashhashtbale20Bkey-valuehash-max-ziplist-value 50474.21M484.27M1.02 100whashziplist20Bkey-valuehash-max-ziplist-value 64252.95M258.09M1.02 依据测试构造,hash-max-ziplist-value 50配置下应用hash类型,内存耗费岂但没有升高反而比字符串存储多出2倍,而调整hash-max-ziplist-value 64之后内存升高为252.95M。因为json的chinese_url属性长度是51,调整配置后hash类型外部编码方式变为ziplist,相比字符串在内存应用上至多持平且反对属性的局部操作。intset编码:intset编码是汇合(set)类型编码的一种,外部体现为存储有序、不反复的整数集。当汇合只蕴含整数且长度不超过set-max-intset-entries配置时被启用。 typedef struct intset {uint32_t encoding;uint32_t length;int8_t contents[];} intset;代码-3 intset构造 ...

November 30, 2022 · 1 min · jiezi

关于redis:Redis系列之初识Redis

本文将大略介绍Redis的一些个性、应用场景。个性Redis是始终基于键值对的NoSQL数据库;Redis反对5种次要数据结构:string、hash、list、set、zset以及bitmaps、hyperLoglog、GEO等特化的数据结构;Redis是内存数据库,因而它有足够好的读写性能;Redis反对长久化,redis反对AOF和RDB两种长久化形式,确保了内存中的数据不会“失落”;Redis的sentinel和复制性能保障了Redis的高可用;Redis反对key维度的数据过期;Redis反对公布订阅、“事务”、pipeline、Lua脚本等附加性能。 应用场景Redis 适宜做什么缓存,Redis自身是内存数据库,注定有极高的读写速度和吞吐,加上数据过期性能以及欠缺的数据淘汰策略使得Redis领有与生俱来的缓存潜质。排行榜零碎,Redis提供了zset、list等简单数据结构,以及极佳的性能,能够做出工夫、数量等各种维度的排行榜零碎。计数器零碎,对于视频(音乐)网站的视频播放量、网页浏览量等高频操作,传统的关系型数据库不可能满足需要,Redis自身晓得incr、incrby等命令很好的反对了这计数性能。社交网络,Redis反对多种简单数据结构,比方一个用户有本人的粉丝,同时也会关注其他人,这些多能够应用set来存储,如果须要有序,能够应用zset来存储,这些简单的数据结构传统的关系型数据库并不能很好的反对,同时,因为社交网络网站自身访问量比拟大,传统数据在性能上也是不可能满足的。音讯队列,Redis提供了音讯队列性能,可能满足个别的音讯队列需要。分布式锁,Redis提供了SET key value [EX seconds] [PX milliseconds] [NX|XX]命令,以及Lua脚本性能,基于此可能很好的实现分布式锁性能。 Redis 不适宜做什么每种产品都有本人的特定的应用领域。Redis也不是万能的。 Redis是内存数据库,相比磁盘类型的数据库老本要高不少,注定了Redis不能用于存储大规模的数据(土豪疏忽)。Redis有足够高的性能,因而对于热数据可能很好满足需要,但如果冷数据存在Redis里未免过于节约(土豪疏忽)。Redis数据存储在内存中,对于可用性要求极高,且须要永恒保留的数据不倡议放在Redis中,尽管Redis提供长久化、复制等性能保证数据落盘,但长久化、复制等也存在时间差,这段时间的数据也不是可能齐全保障不失落的。Redis是单线程的,对于数据比拟大的数据的读写操作会阻塞整个数据库,因而Redis不适宜存储单个value比拟大的数据。 深探入微作为最佳实际本章将会把次要的关注点放和在Redis用户相干的一些Redis基本知识,这部分常识是Redis用户须要理解并在理论应用Redis过程中要思考的。比方,如果不思考Redis单线程的个性可能会遇到申请阻塞导致性能急剧下降的问题;不理解Redis的数据结构,可能导致应用存储收缩、大value、热key的问题等等。 单线程不论是单线程或者多线程都是为了晋升Redis的性能,Redis作为一个基于内存的数据库,不仅仅须要进行数据的读写操作,还要解决大量的内部的网络申请,这就不可避免的要进行屡次IO。Redis抉择了IO多路复用+单线程的架构来解决申请。简略说: 应用 I/O 多路复用机制同时监听多个文件描述符的可读和可写状态。一旦受到网络申请就会在内存中疾速解决,因为绝大多数的操作都是纯内存的,所以解决的速度会十分地快。总之,单线程模式下,即便连贯的网络解决很多,因为有IO多路复用,仍然能够在高速的内存解决中失去疏忽,所以抉择Redis抉择单线程的架构。当然,Redis在6.0之后退出了多线程,因为读写网络的read/write零碎调用在Redis执行期间占用了大部分CPU工夫,如果把网络读写做成多线程的形式对性能会有很大晋升。 数据结构相比其余KV数据库,Redis提供了丰盛的数据结构来满足用户的不同需要。同时,能够说Redis在内存应用是斤斤计较。为了最大可能的节约内存,Redis的每一种数据结构都领有2~3种(截止Redis6.0,后续可能会更多)的底层实现。比方,在list、hash、set、zset等简单数据结构在数据量较小的状况下都会应用ziplist这种数据结构等,因为ziplist是间断空间,不影响指针等附加耗费,在数据量较小的时候读写速度劣势也并不显著,然而能够节约不少存储,尤其是在理论应用场景种往往小数据占比拟大的状况下内存节约更为显著。表-1 Redis数据结构与编码 类型编码决定条件阐明stringint8 个字节的带符号长整型(2^63-1)整数编码stringembstr小于等于 44 个字节的字符串,3.0.0 是 39 个字节优化内存调配的字符串编码,和StringObject间断,一起调配一起开释,别离缩小一次内存操作stringraw大于 44 个字节的字符串动静字符串编码listziplistvalue最大字节数 <= list-max-ziplist-value 且 链表长度 <= list-max-ziplist-entries listlinkedlistvalue最大字节数 > list-max-ziplist-value 或者 链表长度 > list-max-ziplist-entries listquicklist3.2版本之后新增。废除list-max-ziplist-value 和 list-max-ziplist-value。应用新配置项:list-max-ziplist-size示意最大压缩空间或者ziplist长度,取值范畴为-5, -1默认是-2(8kb);list-compress-depth 示意压缩深度,默认是0,不压缩思考到链表附加空间绝对太高,应用quicklist代替ziplist 和 linklisthashziplistvalue最大字节数 <= hash-max-ziplist-value 且 field个数 <= hash-max-ziplist-entries hashhashtablevalue最大字节数 > hash-max-ziplist-value 或者 field个数 > hash-max-ziplist-entries setintset元素必须是整型,且汇合长度<=set-max-inset-entries sethashtable汇合长度>set-max-inset-entries zsetziplistvalue最大字节数 <= zset-max-ziplist-value 且 链表长度 <= zset-max-ziplist-entries zsetskiplistvalue最大字节数 > zset-max-ziplist-value 或者 链表长度 > zset-max-ziplist-entries streamrax + listpack listpack 是 ziplist 优化版本,目前(Redis 5.0.0)只应用在 stream 中表1 Redis数据结构外部实现 ...

November 29, 2022 · 1 min · jiezi

关于redis:Bigkey问题的解决思路与方式探索

作者:vivo 互联网数据库团队- Du Ting在Redis运维过程中,因为Bigkey 的存在,会影响业务程序的响应速度,重大的还会造成可用性损失,DBA也始终和业务开发方强调 Bigkey 的躲避办法以及危害。 一、背景在Redis运维过程中,因为Bigkey的存在,会影响业务程序的响应速度,重大的还会造成可用性损失,DBA也始终和业务开发方强调 Bigkey 的躲避办法以及危害,然而Bigkey始终没有完全避免。全网Redis集群有2200个以上,实例数量达到4.5万以上,在以后阶段进行一次全网 Bigkey查看,预计须要以年为工夫单位,十分耗时。咱们须要新的思路去解决Bigkey问题。 二、Bigkey 介绍2.1、什么是 Bigkey在Redis中,一个字符串类型最大能够到512MB,一个二级数据结构(比方hash、list、set、zset等)能够存储大概40亿个(2^32-1)个元素,但实际上不会达到这么大的值,个别状况下如果达到上面的状况,就能够认为它是Bigkey了。 【字符串类型】: 单个string类型的value值超过1MB,就能够认为是Bigkey。【非字符串类型】:哈希、列表、汇合、有序汇合等, 它们的元素个数超过2000个,就能够认为是Bigkey。2.2 Bigkey是怎么产生的咱们遇到的Bigkey个别都是因为程序设计不当或者对于数据规模意料不分明造成的,比方以下的状况。 【统计】:遇到一个统计类的key,是记录某网站的拜访用户的IP,随着工夫的推移,网站拜访的用户越来越多,这个key的元素数量也会越来越大,造成Bigkey。【缓存】: 缓存类key个别是这样的逻辑,将数据从数据库查问进去序列化放到Redis里,如果业务程序从Redis没有拜访到,就会查询数据库并将查问到的数据追加到Redis缓存中,短时间内会缓存大量的数据到Redis的key中,造成Bigkey。【队列】:把Redis当做队列应用,解决工作,如果生产呈现不及时状况,将导致队列越来越大,造成Bigkey。这三种状况,都是咱们理论运维中遇到的,须要审慎应用,正当优化。 2.3 Bigkey 的危害咱们在运维中,遇到Bigkey的状况下,会导致一些问题,会触发监控报警,重大的还会影响Redis实例可用性,进而影响业务可用性,在须要程度扩容时候,可能导致程度扩容失败。 2.3.1内存空间不平均内存空间不平均会不利于集群对内存的对立治理,有数据失落危险。下图中的三个节点是同属于一个集群,它们的key的数量比拟靠近,但内存容量相差比拟多,存在Bigkey的实例占用的内存多了4G以上了。 能够应用应用Daas平台“工具集-操作项治理”,抉择对应的slave实例执行剖析,找出具体的Bigkey。 2.3.2 超时阻塞Redis是单线程工作的,艰深点讲就是同一时间只能解决一个Redis的拜访命令,操作Bigkey的命令通常比拟耗时,这段时间Redis不能解决其余命令,其余命令只能阻塞期待,这样会造成客户端阻塞,导致客户端拜访超时,更重大的会造成master-slave的故障切换。造成阻塞的操作不仅仅是业务程序的拜访,还有key的主动过期的删除、del删除命令,对于Bigkey,这些操作也须要审慎应用。 超时阻塞案例 咱们遇到一个这样超时阻塞的案例,业务方反映程序拜访Redis集群呈现超时景象,hkeys拜访Redis的均匀响应工夫在200毫秒左右,最大响应工夫达到了500毫秒以上,如下图。 hkeys是获取所有哈希表中的字段的命令,剖析应该是集群中某些实例存在hash类型的Bigkey,导致hkeys命令执行工夫过长,产生了阻塞景象。 1.应用Daas平台“服务监控-数据库实例监控”,抉择master节点,抉择Redis响应工夫监控指标“redis.instance.latency.max”,如下图所示,从监控图中咱们能够看到 (1)失常状况下,该实例的响应工夫在0.1毫秒左右。 (2)监控指标下面有很多突刺,该实例的响应工夫到了70毫秒左右,最大到了100毫秒左右,这种状况就是该实例会有100毫秒都在解决Bigkey的拜访命令,不能解决其余命令。 通过查看监控指标,验证了咱们剖析是正确的,是这些监控指标的突刺造成了hkeys命令的响应工夫比拟大,咱们找到了具体的master实例,而后应用master实例的slave去剖析下Bigkey状况。 2.应用Daas平台“工具集-操作项治理”,抉择slave实例执行剖析,剖析后果如下图,有一个hash类型key有12102218个fields。 3. 和业务沟通,这个Bigkey是间断寄存了30天的业务数据了,倡议依据二次hash形式拆分成多个key,也可把30天的数据依据分钟级别拆分成多个key,把每个key的元素数量管制在5000以内,目前业务正在排期优化中。优化后,监控指标的响应工夫的突刺就会隐没了。 2.3.3 网络阻塞Bigkey的value比拟大,也意味着每次获取要产生的网络流量较大,假如一个Bigkey为10MB,客户端每秒访问量为100,那么每秒产生1000MB的流量,对于一般的千兆网卡(依照字节算是128MB/s)的服务器来说几乎是灭顶之灾。而且咱们当初的Redis服务器是采纳单机多实例的形式来部署Redis实例的,也就是说一个Bigkey可能会对同一个服务器上的其余Redis集群实例造成影响,影响到其余的业务。 2.3.4 迁徙艰难咱们在运维中常常做的变更操作是程度扩容,就是减少Redis集群的节点数量来达到扩容的目标,这个程度扩容操作就会波及到key的迁徙,把原实例上的key迁徙到新扩容的实例上。当要对key进行迁徙时,是通过migrate命令来实现的,migrate实际上是通过dump + restore + del三个命令组合成原子命令实现,它在执行的时候会阻塞进行迁徙的两个实例,直到以下任意后果产生才会开释:迁徙胜利,迁徙失败,期待超时。如果key的迁徙过程中遇到Bigkey,会长工夫阻塞进行迁徙的两个实例,可能造成客户端阻塞,导致客户端拜访超时;也可能迁徙工夫太长,造成迁徙超时导致迁徙失败,程度扩容失败。 迁徙失败案例 咱们也遇到过一些因为Bigkey扩容迁徙失败的案例,如下图所示,是一个Redis集群程度扩容的工单,须要进行key的迁徙,当工单执行到60%的时候,迁徙失败了。 1. 进入工单找到失败的实例,应用失败实例的slave节点,在Daas平台的“工具集-操作项治理”进行Bigkey剖析。 2. 通过剖析找出了hash类型的Bigkey有8421874个fields,正是这个Bigkey导致迁徙工夫太长,超过了迁徙工夫限度,导致工单失败了。 3.和业务沟通,这些key是记录用户拜访零碎的某个功能模块的ip地址的,拜访该功能模块的所有ip都会记录到给key外面,随着工夫的积攒,这个key变的越来越大。同样是采纳拆分的形式进行优化,能够思考依照工夫日期维度来拆分,就是一段时间段的拜访ip记录到一个key中。 4.Bigkey优化后,扩容的工单能够重试,实现集群扩容操作。 三、Bigkey的发现Bigkey首先须要重源头治理,避免Bigkey的产生;其次是须要可能及时的发现,发现后及时处理。剖析Bigkey的办法不少,这里介绍两种比拟罕用的办法,也是Daas平台剖析Bigkey应用的两种形式,别离是Bigkeys命令分析法、RDB文件分析法。 3.1 scan命令剖析Redis4.0及以上版本提供了--Bigkeys命令,能够剖析出实例中每种数据结构的top 1的Bigkey,同时给出了每种数据类型的键值个数以及均匀大小。执行--Bigkeys命令时候须要留神以下几点: 倡议在slave节点执行,因为--Bigkeys也是通过scan实现的,可能会对节点造成阻塞。倡议在节点本机执行,这样能够缩小网络开销。如果没有从节点,能够应用--i参数,例如(--i 0.1 代表100毫秒执行一次)。--Bigkeys只能计算每种数据结构的top1,如果有些数据结构有比拟多的Bigkey,是查找不进去的。Daas平台集成了基于原生--Bigkeys代码实现的查问Bigkey的形式,这个形式的毛病是只能计算每种数据结构的top1,如果有些数据结构有比拟多的Bigkey,是查找不进去的。该形式绝对比拟平安,曾经凋谢进去给业务开发同学应用。 3.2 RDB文件剖析借助开源的工具,比方rdb-tools,剖析Redis实例的RDB文件,找出其中的Bigkey,这种形式须要生成RDB文件,须要留神以下几点: 倡议在slave节点执行,因为生成RDB文件会影响节点性能。须要生成RDB文件,会影响节点性能,尽管在slave节点执行,然而也是有可能造成主从中断,进而影响到master节点。Daas平台集成了基于RDB文件剖析代码实现的查问Bigkey的形式,能够依据理论需要自定义填写N,剖析的top N个Bigkey。该形式绝对有肯定危险,只有DBA有权限执行剖析。 3.3 Bigkey 巡检通过巡检,能够暴露出隐患,提前解决,防止故障的产生,进行全网Bigkey的巡检,是防止Bigkey故障的比拟好的办法。因为全网Redis实例数量十分大,剖析的速度比较慢,应用以后的分析方法很难实现。为了解决这个问题,存储研发组分布式数据库同学打算开发一个高效的RDB解析工具,而后通过大规模解析RDB文件来剖析Bigkey,能够进步剖析速度,实现Bigkey的巡检。 ...

November 22, 2022 · 1 min · jiezi

关于redis:Redis-哈希槽及Redis为什么这么快

Redis 哈希槽的概念Redis 集群中内置了 16384 个哈希槽,当须要在 Redis 集群中搁置一个 key-value时,redis 先对 key 应用 crc16 算法算出一个后果,而后把后果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会依据节点数量大抵均等的将哈希槽映射到不同的节点。 Redis 集群没有应用一致性hash, 而是引入了哈希槽的概念 Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定搁置哪个槽.集群的每个节点负责一部分hash槽。这种构造很容易增加或者删除节点,并且无论是增加删除或者批改某一个节点,都不会造成集群不可用的状态。 应用哈希槽的益处就在于能够不便的增加或移除节点: 当须要减少节点时,只须要把其余节点的某些哈希槽挪到新节点就能够了;当须要移除节点时,只须要把移除节点上的哈希槽挪到其余节点就行了; 在这一点上,咱们当前新增或移除节点的时候不必先停掉所有的 redis 服务。 用了哈希槽的概念,而没有用一致性哈希算法,不都是哈希么?这样做的起因是为什么呢?Redis Cluster是本人做的crc16的简略hash算法,没有用一致性hash。Redis的作者认为它的crc16(key) mod 16384的成果曾经不错了,尽管没有一致性hash灵便,但实现很简略,节点增删时解决起来也很不便。 为了动静增删节点的时候,不至于失落数据么?节点增删时不失落数据和hash算法没什么关系,不失落数据要求的是一份数据有多个正本。 还有集群总共有2的14次方-16384个哈希槽,那么每一个哈希槽中存的key 和 value是什么?当你往Redis Cluster中退出一个Key时,会依据crc16(key) mod 16384计算这个key应该散布到哪个hash slot中,一个hash slot中会有很多key和value。你能够了解成表的分区,应用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster当前会主动为你生成16384个分区表,你insert数据时会依据下面的简略算法来决定你的key应该存在哪个分区,每个分区里有很多key。 Redis到底有多快Redis采纳的是基于内存的采纳的是单过程单线程模型的 KV 数据库,由C语言编写,官网提供的数据是能够达到100000+的QPS(每秒内查问次数)。这个数据不比采纳单过程多线程的同样基于内存的 KV 数据库 Memcached 差!有趣味的能够参考官网的基准程序测试《How fast is Redis?》(https://redis.io/topics/bench...) 横轴是连接数,纵轴是QPS。 Redis为什么这么快1、齐全基于内存,绝大部分申请是纯正的内存操作,十分疾速。数据存在内存中,相似于HashMap,HashMap的劣势就是查找和操作的工夫复杂度都是O(1); 2、数据结构简略,对数据操作也简略,Redis中的数据结构是专门进行设计的; 3、采纳单线程,防止了不必要的上下文切换和竞争条件,也不存在多过程或者多线程导致的切换而耗费 CPU,不必去思考各种锁的问题,不存在加锁开释锁操作,没有因为可能呈现死锁而导致的性能耗费; 4、应用多路非阻塞I/O复用模型 5、应用底层模型不同,它们之间底层实现形式以及与客户端之间通信的利用协定不一样,Redis间接本人构建了VM 机制 ,因为个别的零碎调用零碎函数的话,会节约肯定的工夫去挪动和申请; 以上几点都比拟好了解,下边咱们针对多路 I/O 复用模型进行简略的探讨: (1)多路 I/O 复用模型 多路I/O复用模型是利用 select、poll、epoll 能够同时监察多个流的 I/O 事件的能力,在闲暇的时候,会把以后线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正收回了事件的流),并且只顺次程序的解决就绪的流,这种做法就防止了大量的无用操作。 ...

November 20, 2022 · 1 min · jiezi

关于redis:redis主从复制常见的问题

一、读写拆散的问题 1.数据复制的提早 读写拆散时,master会异步的将数据复制到slave,如果这是slave产生阻塞,则会提早master数据的写命令,造成数据不统一的状况 解决办法:能够对slave的偏移量值进行监控,如果发现某台slave的偏移量有问题,则将数据读取操作切换到master,但自身这个监控开销比拟高,所以对于这个问题,大部分的状况是能够间接应用而不去思考的。   2.读到过期的数据 产生起因: redis的从库是无奈被动的删除曾经过期的key的,所以如果做了读写拆散,就很有可能在从库读到脏数据 例子重现: 主Redis setex test 20 1+OKget test$11ttl test:18从Redis get test$11ttl test:7以上都没问题,然而过几秒再看从Redis ttl test:-1get test$11test这个key曾经过期了,然而还是能够获取到test的值。 在应用Redis做锁的时候,如果间接取读从库的值,这就有大问题了。  二、为什么从库不删除数据?redis删除过期数据有以下几个策略: 惰性删除:当读/写一个曾经过期的key时,会触发惰性删除策略,间接删除掉这个过期key,很显著,这是被动的! 定期删除:因为惰性删除策略无奈保障冷数据被及时删掉,所以 redis 会定期被动淘汰一批已过期的key。(在第二节中会具体阐明) 被动删除:以后已用内存超过maxMemory限定时,触发被动清理策略。被动设置的前提是设置了maxMemory的值 int expireIfNeeded(redisDb *db, robj *key) { time_t when = getExpire(db,key); if (when < 0) return 0; /* No expire for this key */ /* Don't expire anything while loading. It will be done later. */ if (server.loading) return 0; /* If we are running in the context of a slave, return ASAP: * the slave key expiration is controlled by the master that will * send us synthesized DEL operations for expired keys. * * Still we try to return the right information to the caller, * that is, 0 if we think the key should be still valid, 1 if * we think the key is expired at this time. */ if (server.masterhost != NULL) { return time(NULL) > when; } /* Return when this key has not expired */ if (time(NULL) <= when) return 0; /* Delete the key */ server.stat_expiredkeys++; propagateExpire(db,key); return dbDelete(db,key); }通过以上源码发现,4行:没有设置超时工夫,则不删;7行:在”loading”时不删;16行:非主库不删;21行未到期不删。25行同步从库和文件。 ...

November 20, 2022 · 2 min · jiezi

关于redis:redis-主从复制常见问题

一、读写拆散的问题 1.数据复制的提早 读写拆散时,master会异步的将数据复制到slave,如果这是slave产生阻塞,则会提早master数据的写命令,造成数据不统一的状况 解决办法:能够对slave的偏移量值进行监控,如果发现某台slave的偏移量有问题,则将数据读取操作切换到master,但自身这个监控开销比拟高,所以对于这个问题,大部分的状况是能够间接应用而不去思考的。   2.读到过期的数据 产生起因: redis的从库是无奈被动的删除曾经过期的key的,所以如果做了读写拆散,就很有可能在从库读到脏数据 例子重现: 主Redis setex test 20 1+OKget test$11ttl test:18从Redis get test$11ttl test:7以上都没问题,然而过几秒再看从Redis ttl test:-1get test$11test这个key曾经过期了,然而还是能够获取到test的值。 在应用Redis做锁的时候,如果间接取读从库的值,这就有大问题了。  二、为什么从库不删除数据?redis删除过期数据有以下几个策略: 惰性删除:当读/写一个曾经过期的key时,会触发惰性删除策略,间接删除掉这个过期key,很显著,这是被动的! 定期删除:因为惰性删除策略无奈保障冷数据被及时删掉,所以 redis 会定期被动淘汰一批已过期的key。(在第二节中会具体阐明) 被动删除:以后已用内存超过maxMemory限定时,触发被动清理策略。被动设置的前提是设置了maxMemory的值 int expireIfNeeded(redisDb *db, robj *key) { time_t when = getExpire(db,key); if (when < 0) return 0; /* No expire for this key */ /* Don't expire anything while loading. It will be done later. */ if (server.loading) return 0; /* If we are running in the context of a slave, return ASAP: * the slave key expiration is controlled by the master that will * send us synthesized DEL operations for expired keys. * * Still we try to return the right information to the caller, * that is, 0 if we think the key should be still valid, 1 if * we think the key is expired at this time. */ if (server.masterhost != NULL) { return time(NULL) > when; } /* Return when this key has not expired */ if (time(NULL) <= when) return 0; /* Delete the key */ server.stat_expiredkeys++; propagateExpire(db,key); return dbDelete(db,key); }通过以上源码发现,4行:没有设置超时工夫,则不删;7行:在”loading”时不删;16行:非主库不删;21行未到期不删。25行同步从库和文件。 ...

November 20, 2022 · 2 min · jiezi

关于redis:几率大的Redis面试题

Redis 长久化机制Redis是一个反对长久化的内存数据库,通过长久化机制把内存中的数据同步到硬盘文件来保证数据长久化。当Redis重启后通过把硬盘文件从新加载到内存,就能达到复原数据的目标。 实现:独自创立fork()一个子过程,将以后父过程的数据库数据复制到子过程的内存中,而后由子过程写入到临时文件中,长久化的过程完结了,再用这个临时文件替换上次的快照文件,而后子过程退出,内存开释。 RDB是Redis默认的长久化形式。依照肯定的工夫周期策略把内存的数据以快照的模式保留到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照能够是其所示意的数据的一个正本,也能够是数据的一个复制品。) AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最初,相似于MySQL的binlog。当Redis重启是会通过从新执行文件中保留的写命令来在内存中重建整个数据库的内容。 当两种形式同时开启时,数据恢复Redis会优先选择AOF复原。 缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题一、缓存雪崩 咱们能够简略的了解为:因为原有缓存生效,新缓存未到期间(例如:咱们设置缓存时采纳了雷同的过期工夫,在同一时刻呈现大面积的缓存过期),所有本来应该拜访缓存的申请都去查询数据库了,而对数据库CPU和内存造成微小压力,重大的会造成数据库宕机。从而造成一系列连锁反应,造成整个零碎解体。 解决办法: 大多数零碎设计者思考用加锁( 最多的解决方案)或者队列的形式保障来保障不会有大量的线程对数据库一次性进行读写,从而防止生效时大量的并发申请落到底层存储系统上。还有一个简略计划就时讲缓存生效工夫扩散开。 二、缓存穿透 缓存穿透是指用户查问数据,在数据库没有,天然在缓存中也不会有。这样就导致用户查问的时候,在缓存中找不到,每次都要去数据库再查问一遍,而后返回空(相当于进行了两次无用的查问)。这样申请就绕过缓存间接查数据库,这也是常常提的缓存命中率问题。 解决办法:最常见的则是采纳布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个肯定不存在的数据会被这个bitmap拦挡掉,从而防止了对底层存储系统的查问压力。 另外也有一个更为简略粗犷的办法,如果一个查问返回的数据为空(不论是数据不存在,还是系统故障),咱们依然把这个空后果进行缓存,但它的过期工夫会很短,最长不超过五分钟。通过这个间接设置的默认值寄存到缓存,这样第二次到缓冲中获取就有值了,而不会持续拜访数据库,这种方法最简略粗犷。 5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解决?如果是64bit的呢? 对于空间的利用达到了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。 Bitmap: 典型的就是哈希表 毛病是,Bitmap对于每个元素只能记录1bit信息,如果还想实现额定的性能,恐怕只能靠就义更多的空间、工夫来实现了。 布隆过滤器(举荐) 就是引入了k(k>1)k(k>1)个互相独立的哈希函数,保障在给定的空间、误判率下,实现元素判重的过程。 它的长处是空间效率和查问工夫都远远超过个别的算法,毛病是有肯定的误识别率和删除艰难。 Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“抵触”。Hash存在一个抵触(碰撞)的问题,用同一个Hash失去的两个URL的值有可能雷同。为了缩小抵触,咱们能够多引入几个Hash,如果通过其中的一个Hash值咱们得出某元素不在汇合中,那么该元素必定不在汇合中。只有在所有的Hash函数通知咱们该元素在汇合中时,能力确定该元素存在于汇合中。这便是Bloom-Filter的根本思维。 Bloom-Filter个别用于在大数据量的汇合中断定某元素是否存在。 受揭示补充:缓存穿透与缓存击穿的区别三、缓存击穿 缓存击穿:是指一个key十分热点,在不停的扛着大并发,大并发集中对这一个点进行拜访,当这个key在生效的霎时,继续的大并发就穿破缓存,间接申请数据。 解决方案:在拜访key之前,采纳SETNX(set if not exists)来设置另一个短期key来锁住以后key的拜访,拜访完结再删除该短期key。 增:给一个我公司解决的案例:背景双机拿token,token在存一份到redis,保证系统在token过期时都只有一个线程去获取token;线上环境有两台机器,故应用分布式锁实现。 四、缓存预热 缓存预热这个应该是一个比拟常见的概念,置信很多小伙伴都应该能够很容易的了解,缓存预热就是零碎上线后,将相干的缓存数据间接加载到缓存零碎。这样就能够防止在用户申请的时候,先查询数据库,而后再将数据缓存的问题!用户间接查问当时被预热的缓存数据! 解决思路: 1、间接写个缓存刷新页面,上线时手工操作下;2、数据量不大,能够在我的项目启动的时候主动进行加载;3、定时刷新缓存; 五、缓存更新 除了缓存服务器自带的缓存生效策略之外(Redis默认的有6中策略可供选择),咱们还能够依据具体的业务需要进行自定义的缓存淘汰,常见的策略有两种: (1)定时去清理过期的缓存;(2)当有用户申请过去时,再判断这个申请所用到的缓存是否过期,过期的话就去底层零碎失去新数据并更新缓存。 两者各有优劣,第一种的毛病是保护大量缓存的key是比拟麻烦的,第二种的毛病就是每次用户申请过去都要判断缓存生效,逻辑绝对比较复杂!具体用哪种计划,大家能够依据本人的利用场景来衡量。 六、缓存降级 当访问量剧增、服务呈现问题(如响应工夫慢或不响应)或非核心服务影响到外围流程的性能时,依然须要保障服务还是可用的,即便是有损服务。零碎能够依据一些要害数据进行主动降级,也能够配置开关实现人工降级。降级的最终目标是保障外围服务可用,即便是有损的。而且有些服务是无奈降级的(如退出购物车、结算)。 以参考日志级别设置预案: 个别:比方有些服务偶然因为网络抖动或者服务正在上线而超时,能够主动降级;正告:有些服务在一段时间内成功率有稳定(如在95~100%之间),能够主动降级或人工降级,并发送告警;谬误:比方可用率低于90%,或者数据库连接池被打爆了,或者访问量忽然猛增到零碎能接受的最大阀值,此时能够依据状况主动降级或者人工降级;严重错误:比方因为非凡起因数据谬误了,此时须要紧急人工降级。服务降级的目标,是为了避免Redis服务故障,导致数据库跟着一起产生雪崩问题。因而,对于不重要的缓存数据,能够采取服务降级策略,例如一个比拟常见的做法就是,Redis呈现问题,不去数据库查问,而是间接返回默认值给用户。 热点数据和冷数据是什么热点数据,缓存才有价值 对于冷数据而言,大部分数据可能还没有再次拜访到就曾经被挤出内存,不仅占用内存,而且价值不大。频繁批改的数据,看状况思考应用缓存 对于下面两个例子,寿星列表、导航信息都存在一个特点,就是信息批改频率不高,读取通常十分高的场景。 对于热点数据,比方咱们的某IM产品,生日祝愿模块,当天的寿星列表,缓存当前可能读取数十万次。再举个例子,某导航产品,咱们将导航信息,缓存当前可能读取数百万次。 数据更新前至多读取两次,缓存才有意义。这个是最根本的策略,如果缓存还没有起作用就生效了,那就没有太大价值了。 那存不存在,批改频率很高,然而又不得不思考缓存的场景呢?有!比方,这个读取接口对数据库的压力很大,然而又是热点数据,这个时候就须要思考通过缓存伎俩,缩小数据库的压力,比方咱们的某助手产品的,点赞数,珍藏数,分享数等是十分典型的热点数据,然而又一直变动,此时就须要将数据同步保留到Redis缓存,缩小数据库压力。 Memcache与Redis的区别都有哪些?存储形式 Memecache把数据全副存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,redis能够长久化其数据数据反对类型 memcached所有的值均是简略的字符串,redis作为其替代者,反对更为丰盛的数据类型 ,提供list,set,zset,hash等数据结构的存储应用底层模型不同 它们之间底层实现形式 以及与客户端之间通信的利用协定不一样。 Redis间接本人构建了VM 机制 ,因为个别的零碎调用零碎函数的话,会节约肯定的工夫去挪动和申请。value 值大小不同:Redis 最大能够达到 512M;memcache 只有 1mb。redis的速度比memcached快很多Redis反对数据的备份,即master-slave模式的数据备份。单线程的redis为什么这么快纯内存操作单线程操作,防止了频繁的上下文切换采纳了非阻塞I/O多路复用机制redis的数据类型,以及每种数据类型的应用场景答复:一共五种 String这个其实没啥好说的,最惯例的set/get操作,value能够是String也能够是数字。个别做一些简单的计数性能的缓存。 hash这里value寄存的是结构化的对象,比拟不便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期工夫,能很好的模拟出相似session的成果。 ...

November 19, 2022 · 2 min · jiezi

关于redis:rebloom-k8s-报错

docker 下的 redis.cnf version: "3"services: redis: container_name: rebloom image: redislabs/rebloom:latest ports: - "6379:6379" volumes: - ./volumes:/data # /usr/local/redis/data 是你宿主机的门路;/data 是容器内的门路,容器内的 redis 会把须要长久化的数据都保留到 /data 目录下 - ./redis.conf:/etc/redis/redis.conf # redis.conf 这个文件曾经筹备好了,能够放到这个门路,也能够本人批改,比方放到我的项目门路中 restart: always# 这个文件的地址,和你的 docker-compose.yaml 中的 /usr/local/redis/redis.conf:/etc/redis/redis.conf 冒号右边的要对应起来# redis 反对两者长久化机制:RDB&AOF# https://juejin.cn/post/6844903716290576392appendonly yes#default: 长久化文件appendfilename "appendonly.aof"#default: 每秒同步一次appendfsync everysecport 6379# 绑定端口,不指定外网可能连不上服务器bind 0.0.0.0复刻到 k8s 中却跑不通,会报错 解决办法: apiVersion: v1kind: ConfigMapmetadata: name: crawler-bloom-redis-config namespace: xxxxxdata: redis-config: | maxmemory 100mb appendonly yes appendfilename "appendonly.aof" appendfsync everysec loadmodule "/usr/lib/redis/modules/redisbloom.so"退出 loadmodule "/usr/lib/redis/modules/redisbloom.so" 即可,至于为什么 docker 下不必加,k8s 中却要加,我就不晓得了。晓得的人能够贴到评论区 ...

November 17, 2022 · 1 min · jiezi

关于redis:virtualbox-Linux-配置Redis无中心集群三主三从

本文指标 在一台virtualbox创立的虚拟机上配置Redis集群,三个主节点,三个从节点。本文有余 因virtualbox创立的虚拟机端口映射有些问题,所以没有在三台虚拟机上别离部署主节点和从节点,然而思路是一样的,依然有参考价值1.如果没有wget,请先装置 yum -y install wget2.在Linux根目录下顺次执行以下命令 mkdir /datayum -y install gcc automake autoconf libtool makewget https://download.redis.io/releases/redis-6.2.0.tar.gztar xzvf redis-6.2.0.tar.gz -C /data/cd /data/mv redis-6.2.0/ rediscd redis/makemkdir /data/redis/datamkdir clustercd cluster/mkdir 7001 7002 7003 7004 7005 7006cp /data/redis/redis.conf 7001/3.批改配置文件办法能够应用传统做法,用vim关上,而后编辑保留 cd 7000/vim redis.conf也能够间接用一些ssh工具去批改,比拟不便先找到文件,而后双击批改实现后ctrl+s保留即可 4.配置文件批改内容 bind 192.168.188.1 #每个实例的配置文件批改为对应节点的ip地址port 7001 #监听端口,运行多个实例时,须要指定布局的每个实例不同的端口号daemonize yes #redis后盾运行pidfile /var/run/redis_7001.pid #pid文件,运行多个实例时,须要指定不同的pid文件logfile /var/log/redis_7001.log #日志文件地位,运行多实例时,须要将文件批改的不同。dir /data/redis/data #存放数据的目录appendonly yes #开启AOF长久化,redis会把所接管到的每一次写操作申请都追加到appendonly.aof文件中,当redis重新启动时,会从该文件复原出之前的状态。appendfilename "appendonly.aof" #AOF文件名称appendfsync everysec #示意对写操作进行累积,每秒同步一次以下为关上正文并批改cluster-enabled yes #启用集群cluster-config-file nodes-7001.conf #集群配置文件,由redis自动更新,不须要手动配置,运行多实例时请注批改为对应端口cluster-node-timeout 5000 #单位毫秒。集群节点超时工夫,即集群中主从节点断开连接工夫阈值,超过该值则认为主节点不能够,从节点将有可能转为mastercluster-replica-validity-factor 10 #在进行故障转移的时候全副slave都会申请申请为master,然而有些slave可能与master断开连接一段时间了导致数据过于古老,不应该被晋升为master。该参数就是用来判断slave节点与master断线的工夫是否过长。(计算方法为:cluster-node-timeout * cluster-replica-validity-factor,此处为:5000 * 10 毫秒)cluster-migration-barrier 1 #一个主机将放弃连贯的最小数量的从机,以便另一个从机迁徙到不再被任何从机笼罩的主机cluster-require-full-coverage yes #集群中的所有slot(16384个)全副笼罩,能力提供服务6.复制到其它节点 ...

November 6, 2022 · 1 min · jiezi

关于redis:使用redis-stream实现im

https://github.com/lloydzhou/... 应用redis stream实现im利用redis stream存储最近音讯同时监听stream实现音讯实时推送应用redis-module实现,以达到更好性能能够间接从stream拿到音讯构建出最近聊天列表以及好友名单前端应用indexedDB长久化音讯,能够增量同步音讯redis keyu:<uid> hash, 寄存用户信息,例如name/avatar等s:<uid> stream, 这个队列用于接管/发送用户音讯c:<uid> sorted set, 用于寄存用户联系人(包含群组)gm:<gid> set, 用于寄存群用户idgs:<gid> stream, 用于接管/发送群音讯gi:<gid> hash, 寄存群信息,例如name,还有一些配置信息(例如加群是否须要验证?)redis moduleredis module实现IMIM.RECIVEIM.RECIVE [uid] BLOCK [ms] COUNT [count] START [start] 获取历史音讯,以及监听新的音讯IM.MESSAGEIM.RECIVE [GROUP | USER] [uid | gid] [mid] 获取单条音讯IM.SENDIM.SEND [uid] [tuid] [field value] [field value ... ] 发送单聊音讯IM.GSENDIM.GSEND [uid] [gid] [field value] [field value ... ] 发送群聊音讯IM.USERIM.USER [uid] (get user info) 获取用户信息 IM.USER [uid] [field value] [field value ... ] (create user or update user info)IM.GROUPIM.GROUP [gid] (get group info) IM.GROUP [gid] [uid] [field value] [field value ... ] (create group or update group info)IM.LINKIM.LINK [uid] [tuid] (add to user contact list)IM.UNLINKIM.UNLINK [uid] [tuid] (remove from user contact list)IM.JOINIM.LINK [uid] [gid] (add to user group)IM.QUITIM.QUIT [uid] [gid] (remove from user group)GUIusing tornado WebSocketHandler to create websocket.using svelte create simple ui to send message (using localStorage to save message)dockerdocker run --rm --name=redisim -p 8888:8888 lloydzhou/redisim

November 5, 2022 · 1 min · jiezi

关于redis:Redis-知识点全面击破多图警告

什么是 Redis?Redis(REmote DIctionary Service)是一个开源的键值对数据库服务器。 Redis 更精确的形容是一个数据结构服务器。Redis 的这种非凡性质让它在开发人员中很受欢迎。 Redis不是通过迭代或者排序形式解决数据,而是一开始就依照数据结构形式组织。晚期,它的应用很像 Memcached,但随着 Redis 的改良,它在许多其余用例中变得可行,包含公布-订阅机制、流(streaming)和队列。 次要来说,Redis 是一个内存数据库,用作另一个“实在”数据库(如 MySQL 或 PostgreSQL)后面的缓存,以帮忙进步应用程序性能。它通过利用内存的高速访问速度,从而加重外围应用程序数据库的负载,例如: 不常常更改且常常被申请的数据工作关键性较低且常常变动的数据上述数据的示例能够包含会话或数据缓存以及仪表板的排行榜或汇总剖析。 然而,对于许多用例场景,Redis 都能够提供足够的保障,能够将其用作成熟的主数据库。再加上 Redis 插件及其各种高可用性(HA)设置,Redis 作为数据库对于某些场景和工作负载变得十分有用。 另一个重要方面是 Redis 含糊了缓存和数据存储之间的界线。这里要了解的重要一点是,相比于应用 SSD 或 HDD 作为存储的传统数据库,读取和操作内存中数据的速度要快得多。 最后,Redis 最常被比作 Memcached,后者过后不足任何非易失性长久化。 这是以后两个缓存之间的性能细分。 尽管当初领有多种配置形式将数据长久化到磁盘,但过后首次引入长久化时,Redis 是应用快照形式,通过异步拷贝内存中的数据形式来做长久化。可怜的是,这种机制的毛病是可能会在快照之间失落数据。 Redis 自 2009 年成立到当初曾经变的很成熟。咱们将介绍它的大部分架构和拓扑,以便你能够将 Redis 增加到你的数据存储系统库中。 Redis 架构在开始探讨 Redis 内部结构之前,让咱们先讨论一下各种 Redis 部署及其衡量取舍。 咱们将次要关注以下这些设置: 单个 Redis 实例Redis 高可用性Redis 哨兵Redis 集群依据你的用例和规模,决定应用哪一种设置。 单个 Redis 实例 单个 Redis 实例是最间接的 Redis 部署形式。它容许用户设置和运行小型实例,从而帮忙他们疾速倒退和减速服务。然而,这种部署并非没有毛病。例如,如果此实例失败或不可用,则所有客户端对 Redis 的调用都将失败,从而升高零碎的整体性能和速度。 ...

October 31, 2022 · 2 min · jiezi

关于redis:Redis基础二高性能IO模型为什么单线程Redis能那么快

Redis的高性能IO模型明天,咱们来探讨一个很多人都很关怀的问题:“为什么单线程的 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 长度的批改。否则,咱们可能就会失去谬误的长度后果。这就是多线程编程模式面临的共享资源的并发访问控制问题。 ...

October 28, 2022 · 2 min · jiezi

关于redis:redis渐进式rehash

引言简略温习一下,redis有哪些数据结构和对象。 数据结构间断内存类 SDS 简略动静字符串整数汇合intset压缩链表ziplist随机内存类 list和listnodezskiplist和zskiplistnode间断+随机:dict、dictht和dictEntry对象总共有5种: stringlisthashsetzset其中,hash对象的外部实现是redis的字典,也就是dict构造,dict构造有两个字段和rehash无关: 两个hash表(hash表是dictht构造)rehashidx,记录rehash的进度渐进式rehash须要渐进式rehash的起因:随着client对hash对象的增删数据操作,hash对象里的元素会相应的减少或缩小,导致内存利用率变低或减少hash碰撞几率,因而须要对其底层的哈希表进行扩缩容。扩缩容的规范是底层哈希表的负载因子;扩缩容,须要相应地从新计算key在新的哈希表中的地位,这是一个耗费cpu算力的操作;这就是渐进式rehash呈现的起因,rehash的需要加上一次性rehash的性能问题。 渐进式rehash触发的条件分为扩容和缩容: 扩容,分为有没有正在执行bgsave(生成RDB文件)或bgrewriteaof命令(重写AOF) 正在执行,判断负载因子>=5没有在执行,判断负载因子>=1已知生成RDB文件或重写AOF也是比拟耗费cpu算力的操作,因而对这两个数值的不同,就能够了解了。 缩容:简略粗犷,负载因子<=0.1附上负载因子计算公式:used / size (哈希表已应用的键数量/容量) 渐进式rehash的过程参考:https://tech.meituan.com/2018... 在没有rehash时,dict构造外面的两个dichht构造,有一个的table指向的是空指针,即,这个样纸: 基于此状态形容渐进式rehash(扩容状况下)的过程: 为ht[1]调配一块新的内存,内存空间大小依照DICT_HT_INITIAL_SIZE * 2的n次方 >= h[0].used,这里>=的意思是第一个大于h[0].used的值;(在rehash过程中会始终维持这个内存耗费);在rehash过程中,所有对h[0]的删改查操作,都会触发h[1]同样的操作,同时进行一次rehash,把h[0]的中h[0].table[rehashidx]地位的数据挪到h[1],而后给rehashidx+1;新增操作则会间接放到h[1]中;每次rehash函数最初会判断h[0].used==0,if true,完结渐进式rehash过程。参考:https://blog.csdn.net/weixin_...

October 27, 2022 · 1 min · jiezi

关于redis:阿里技术专家压箱底好货Redis深度历险笔记

前言Redis,是互联网技术架构在存储系统中应用最为宽泛的中间件,它也是工程师技术面试中最喜爱问的技能之一,特地是那些优良的、竞争强烈的大型互联网公司(比方 Twitter、新浪微博、阿里云、腾讯云、淘宝、知乎等),通常要求面试者不仅仅把握Redis根底应用,更要求深层了解Redis外部实现的细节原理。毫不夸大地说,能把Redis的知识点全副吃透,你的半只脚就曾经踏进心仪公司的大门! 所以小编给大家整顿了这份《Redis深度历险》文档,并且将从目录,前言,次要内容,这三个局部大家解说这本文档,同时心愿对各位大哥敌人们有点作用,也心愿你们会喜爱!最初,有须要这篇《Redis深度历险》文档的敌人们【间接点击此处】即可获取~ 先来看看这份Redis目录: 次要内容这篇《Redis深度历险:外围原理和利用实际》,次要分为五个局部,为利用篇,原理篇,集群篇,拓展篇,源码篇,所以接下来,小编就每篇认真的开展来具体的为大家解说一下这本书的知识点! 开篇和根底篇开篇:授人以鱼不如授人以渔——Redis能够用来做什么?根底:万丈高楼平地起——Redis根底数据结构 开篇 根底 利用篇利用1:千帆竞发——分布式锁利用2:金蝉脱壳——延时队列利用3:省吃俭用——位图利用4:四两拨千斤—— HyperLogLog利用5:层峦叠嶂——布隆过滤器利用6:断尾求生——简略限流利用7:爱财如命——漏斗限流利用8:近水楼台——GeoHash利用9:海底捞针——Scan 局部知识点: 利用1:千帆竞发——分布式锁 利用4:四两拨千斤—— HyperLogLog 这一节咱们能够学会应用HyperLogLog数据结构来进行估数,它十分有价值,能够解决很多精确度不高的统计需要。 利用5:层峦叠嶂——布隆过滤器 利用7∶爱财如命——漏斗限流 漏斗限流是最罕用的限流办法之一,顾名思义,这个算法的灵感源于漏斗(funnel)的构造。 利用8∶近水楼台—— GeoHash Redis在3.2版本当前减少了地理位置GEO模块,意味着咱们能够应用Redis来实现摩拜单车「左近的Mobike」、美团和饿了么「左近的餐馆」这样的性能了。 原理篇原理1:鞭辟入里——线程IO模型原理2:窃窃私语——通信协议原理3:防患未然——长久化理4:雷厉风行——管道原理5:风雨同舟——事务原理6:小道消息—-PubSub原理7:开源节流——小对象压缩原理8:有恃无恐——主从同步 局部知识点: 原理3∶防患未然——长久化 Redis的数据全副在内存里,如果忽然宕机,数据就会全副失落,因而必须有一种机制来保障Redis的数据不会因为故障而失落,这种机制就是Redis的长久化机制。 原理4∶雷厉风行——管道 大家始终以来对Redis管道有一个误会,他们认为这是Redis服务器提供的一种特地的技术,有了这种技术就能够减速Redis的存取效率。然而实际上Redis管道(Pipelinc)自身并不是Redis服务器间接提供的技术,这个技术实质上是由客户端提供的,跟服务器没有什么间接的关系。上面咱们对这块做一个深刻探索。 原理6:小道消息—PubSub 后面咱们讲了 Redis 音讯队列的应用办法,然而没有提到Redis音讯队列的不足之处,那就是它不反对音讯的多提机制。 原理8∶有恃无恐——主从同步 很多企业都没有应用到Redis的集群,然而至多都做了主从。有了主从,当master挂掉的时候,运维让从库过去接管,服务就能够持续,否则master须要通过数据恢复和重启的过程,这就可能会拖很长的工夫,影响线上业务的继续服务。 集群篇集群1:李代桃僵——Sentinel集群2:分而治之——Codis集群3:万众一心——Cluster 局部知识点: 集群2:分而治之——Codis 集群3∶万众一心——Cluster 拓展篇拓展1:耳听八方——Stream拓展2:无所不知—— Info指令拓展3:拾脱漏补——再谈分布式锁拓震4:朝生暮死——过期策略拓展5:优胜劣汰——LRU拓震6:平波缓进———懈怠删除拓展7:妙手仁心——优雅地应用Jedis拓展8:居安思危——爱护Redis拓展9:隔墙有耳——Redis平安通信 局部知识点: 拓展1:耳听八方——Stream Redis5.0 被作者Antirez忽然放了进去,减少了很多新的特色性能。而Redis5.0最大的新个性就是多出了一个数据结构Stream,它是一个新的弱小的反对多播的可长久化的音讯队列,作者坦言Redis Strcam狠狠地借鉴了Kafka的设计。 拓展3:拾脱漏补——再谈分布式锁 在第三节,咱们粗疏解说了分布式锁的原理,它的应用非常简单,一条指令就能够实现加锁操作。不过在集群环境下,这种形式是有缺点的,它不是相对平安的。 拓展6:平波缓进———懈怠删除 始终以来咱们认为Redis是单线程的,单线程为Redis带来了代码的简洁性和丰盛多样的数据结构。不过Redis 外部实际上并不是只有一个主线程,它还有几个异步线程专门用来解决一些耗时的操作。 ...

October 26, 2022 · 1 min · jiezi

关于redis:这次彻底读透-Redis

0. Redis 根底如果对 Redis 还不理解的同学能够先看一下这篇 Redis 根底文章 ,这外面介绍了 Redis 是什么,以及怎么用 1. Redis 管道咱们通常应用 Redis 的形式是,发送命令,命令排队,Redis 执行,而后返回后果,这个过程称为Round trip time(简称RTT, 往返工夫)。然而如果有多条命令须要执行时,须要耗费 N 次 RTT,通过 N 次 IO 传输,这样效率显著很低。 于是 Redis 管道(pipeline)便产生了,一次申请/响应服务器能实现解决新的申请即便旧的申请还未被响应。这样就能够将多个命令发送到服务器,而不必期待回复,最初在一个步骤中读取该回答。这就是管道(pipelining),缩小了 RTT,晋升了效率 重要阐明: 应用管道发送命令时,服务器将被迫回复一个队列回答,占用很多内存。所以,如果你须要发送大量的命令,最好是把他们依照正当数量分批次的解决,例如10K的命令,读回复,而后再发送另一个10k的命令,等等。这样速度简直是雷同的,然而在回复这10k命令队列须要十分大量的内存用来组织返回数据内容。 2. Redis 公布订阅公布订阅是一种音讯模式,发送者(sub)发送音讯,订阅者(pub)接管音讯 如上图所示,公布订阅基于频道实现的,同一个频道能够有多个订阅者,多个发布者。其中任意一个发布者公布音讯到频道中,所以订阅者都能够收到该音讯。 公布订阅 Redis 演示 我在服务器上启动了 4 个 Redis 客户端,2 个订阅者,2 个发布者,订阅者订阅的频道为 channel01 第一步:发布者 1 往 channel01 中发送音讯 "wugongzi" 订阅者 1 收到音讯: 订阅者 2 收到音讯: 第二步:发布者 2 往频道中公布音讯 "hello-redis" 订阅者 1 收到音讯: ...

October 26, 2022 · 6 min · jiezi

关于redis:Redis基础一Redis的数据结构

Redis的数据结构为什么 Redis 能有这么突出的体现呢?一方面,这是因为它是内存数据库,所有操作都在内存上实现,内存的访问速度自身就很快。另一方面,这要归功于它的数据结构。这是因为,键值对是按肯定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以高效的数据结构是 Redis 疾速解决数据的根底。 简略来说,Redis底层数据结构一共有 6 种,别离是简略动静字符串、双向链表、压缩列表、哈希表、跳表和整数数组。它们和数据类型的对应关系如下图所示: String 类型的底层实现只有一种数据结构,也就是简略动静字符串。而 List、Hash、Set 和 Sorted Set 这四种数据类型,都有两种底层实现构造。通常状况下,咱们会把这四种类型称为汇合类型,它们的特点是一个键对应了一个汇合的数据。 键和值的构造组织为了实现从键到值的快速访问,Redis 应用了一个哈希表来保留所有键值对。一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。每个哈希桶中保留了键值对数据。哈希桶中的元素保留的并不是值自身,而是指向具体值的指针。这也就是说,不论值是 String,还是汇合类型,哈希桶中的元素都是指向它们的指针。 哈希桶中的 entry 元素中保留了key和value指针,别离指向了理论的键和值,这样一来,即便值是一个汇合,也能够通过*value指针被查找到。 因为这个哈希表保留了所有的键值对,所以,我也把它称为全局哈希表。哈希表的最大益处很显著,就是让咱们能够用 O(1) 的工夫复杂度来疾速查找到键值对——咱们只须要计算键的哈希值,就能够晓得它所对应的哈希桶地位,而后就能够拜访相应的 entry 元素。你看,这个查找过程次要依赖于哈希计算,和数据量的多少并没有间接关系。也就是说,不论哈希表里有 10 万个键还是 100 万个键,咱们只须要一次计算就能找到相应的键。然而,如果你只是理解了哈希表的 O(1) 复杂度和疾速查找个性,那么,当你往 Redis 中写入大量数据后,就可能发现操作有时候会忽然变慢了。这其实是因为你疏忽了一个潜在的危险点,那就是哈希表的抵触问题和 rehash 可能带来的操作阻塞。 哈希抵触当你往哈希表中写入更多数据时,哈希抵触是不可避免的问题。这里的哈希抵触,也就是指,两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。毕竟,哈希桶的个数通常要少于 key 的数量,这也就是说,难免会有一些 key 的哈希值对应到了同一个哈希桶中。 Redis 解决哈希抵触的形式,就是链式哈希。链式哈希也很容易了解,就是指同一个哈希桶中的多个元素用一个链表来保留,它们之间顺次用指针连贯。 rehash 操作然而下面仍然存在一个问题,哈希抵触链上的元素只能通过指针逐个查找再操作。如果哈希表里写入的数据越来越多,哈希抵触可能也会越来越多,这就会导致某些哈希抵触链过长,进而导致这个链上的元素查找耗时长,效率升高。对于谋求“快”的 Redis 来说,这是不太能承受的。 所以,Redis 会对哈希表做 rehash 操作。rehash 也就是减少现有的哈希桶数量,让逐步增多的 entry 元素能在更多的桶之间扩散保留,缩小单个桶中的元素数量,从而缩小单个桶中的抵触。那具体怎么做呢? 其实,为了使 rehash 操作更高效,Redis 默认应用了两个全局哈希表:哈希表1和哈希表2。一开始,当你刚插入数据时,默认应用哈希表 1,此时的哈希表 2 并没有被调配空间。随着数据逐渐增多,Redis 开始执行 rehash,这个过程分为三步: 1)给哈希表2调配更大的空间,例如是以后哈希表1大小的两倍。2)把哈希表1中的数据从新映射并拷贝到哈希表2中。3)开释哈希表1的空间。 到此,咱们就能够从哈希表 1 切换到哈希表 2,用增大的哈希表 2 保留更多数据,而原来的哈希表 1 留作下一次 rehash 扩容备用。 ...

October 25, 2022 · 2 min · jiezi

关于redis:Redis数据结构一Redis的数据存储及String类型的实现

1 引言Redis作为基于内存的非关系型的K-V数据库。因读写响应疾速、原子操作、提供了多种数据类型String、List、Hash、Set、Sorted Set、在我的项目中有着宽泛的应用,明天咱们来探讨下下Redis的数据结构是如何实现的。 2 数据存储2.1 RedisDBRedis将数据存储在redisDb中,默认0~15共16个db。每个库都是独立的空间,不用放心key抵触问题,可通过select命令切换db。集群模式应用db0 typedef struct redisDb {dict *dict; /* The keyspace for this DB */dict *expires; /* Timeout of keys with a timeout set */...} redisDb;dict:数据库键空间,保留着数据库中的所有键值对expires:键的过期工夫,字典的键为键,字典的值为过期事件UNIX工夫戳2.2 Redis哈希表实现2.2.1 哈希字典dict K-V存储咱们最先想到的就是map,在Redis中通过dict实现,数据结构如下: typedef struct dict { dictType *type; void *privdata; dictht ht[2]; long rehashidx; /* rehashing not in progress if rehashidx == -1 */ unsigned long iterators; /* number of iterators currently running */} dict;type:类型特定函数是一个指向dictType构造的指针,每个dictType构造保留了一簇用于操作特定类型键值对的函数,Redis会为用处不同的字典设置不同的类型特定函数。privdata:公有数据保留了须要传给那些类型特定函数的可选参数ht[2]:哈希表一个蕴含两个项的数组,数组中的每个项都是一个dictht哈希表,个别状况下,字典只应用ht[0] 哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时应用rehashidx:rehash 索引,当rehash不在进行时,值为 -1hash数据存在两个特点: 任意雷同的输出肯定能失去雷同的数据不同的输出,有可能失去雷同的输入针对hash数据的特点,存在hash碰撞的问题,dict通过dictType中的函数可能解决这个问题 ...

October 25, 2022 · 2 min · jiezi

关于redis:说说-Redis-pipeline

更多技术文章,请关注我的集体博客 www.immaxfang.com 和小公众号 Max的学习札记。Redis 客户端和服务端之间是采纳 TCP 协定进行通信的,是基于 Request/Response 这种一问一答的模式,即申请一次响应一次。 一般模式咱们先来看下一般模式下,一条 Redis 命令的简要执行过程: 客户端发送一条命令给 redis-server,阻塞期待 redis-server 应答redis-server 接管到命令,执行命令redis-server 将后果返回给客户端上面咱们来简要理解下一个残缺申请的交互过程。 客户端调用 write() 将音讯写入操作系统为 socket 调配的 send buffer 中操作系统将 send buffer 中的内容发送到网卡,网卡通过网关路由把内容发送到服务器网卡服务器网卡将承受到的音讯写入操作系统为 socket 调配的 recv buffer服务器过程调用 read() 从 recv buffer 中读取音讯进行解决解决实现之后,服务器调用 write() 将响应内容发送的 send buffer 中服务器将 send buffer 中的内容通过网卡,发送到客户端客户端操作系统将网卡中的内容放入 recv buffer 中客户端过程调用 read() 从 recv buffer 中读取音讯 一般模式的问题咱们来想一下,这种状况下可能导致什么问题。如果同时执行大量的命令,那对于每一个命令,都要按下面的流程走一次,以后的命令须要期待上一条命令执行应答结束之后,才会执行。这个过程中会有屡次的 RTT ,也还会随同着很多的 IO 开销,发送网络申请等。每条命令的发送和接管的过程都会占用两边的网络传输。简略的来说,每个命令的执行工夫 = 客户端发送耗时 + 服务器解决耗时 + 服务器返回耗时 + 一个网络来回耗时。在这里,一个 网络来回耗时(RTT) 是不好管制的,也是不稳固的。它的影响因素很多,比方客户端到服务器的网络线路是否拥挤,通过了多少跳。还有就是 IO 零碎调用也是耗时的,一个 read 零碎调用,须要从用户态,切换到内核态。上文咱们讲述一个命令的申请过程时屡次降到 read 和 write 零碎调用。能够说一个命令的执行工夫,很大水平上受到它们的限度。 ...

October 23, 2022 · 1 min · jiezi

关于redis:认识-Redis-clientoutputbufferlimit-参数与源码分析

更多技术文章,请关注我的集体博客 www.immaxfang.com 和小公众号 Max的学习札记。概述Redis 的 client-output-buffer-limit 能够用来强制断开无奈足够快从 redis 服务器端读取数据的客户端。爱护机制规定如下: [hard limit] 大小限度,当某一客户端缓冲区超过设定值后,间接敞开连贯。[soft limit] 持续时间限度,当某一客户端缓冲区继续一段时间占用过大空间时敞开连贯。该参数个别用在以下几类客户端中: 一般 client,包含 monitor主从同步时的 slave clientPub/Sub 模式中的 client 配置介绍与剖析该参数的配置语法: client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>配置实例: # 一般client buffer限度 client-output-buffer-limit normal 0 0 0# slave client buffer限度client-output-buffer-limit slave 256mb 64mb 60# pubsub client buffer限度client-output-buffer-limit pubsub 32mb 8mb 60client-output-buffer-limit normal 0 0 0将 hard limit 和 soft limit 同时设置为 0,则示意敞开该限度。 client-output-buffer-limit slave 256mb 64mb 60该配置示意,对于 slave 客户端来说,如果 output-buffer 占用内存达到 256M 或者超过 64M 的工夫达到 60s,则敞开客户端连贯。 ...

October 19, 2022 · 4 min · jiezi

关于redis:Redis入门及基本操作

1.Redis简介Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官网提供的数据是能够达到100000+的QPS(每秒内查问次数)。它存储的value类型比拟丰盛,也被称为结构化的NoSql数据库。 特点:1.基于内存存储,读写性能高2.适宜存储热点数据(热点商品、资讯、新闻等)3.企业应用宽泛 简而言之,Redis个别用来存热点数据(微博热搜这些)因为用户要频繁拜访。 2.Redis罕用数据类型Redis存储的是key-value构造的数据,其中key是字符串类型,value有5种罕用的数据类型:1.字符串2.哈希hash3.列表4.汇合set5.有序汇合sorted set 3.Redis常用命令1.字符串操作SET key value设置指定key的值 GET key获取指定key值 SETEX key seconds value设置指定key的值,并将key的过期工夫设为seconds秒 SETNX key value只有在key不存在时设置key的值 2.哈希 hash 操作命令HSET key field value 将哈希表 key 中的字段 field 的值设为 valueHGET key field 获取存储在哈希表中指定字段的值HDEL key field 删除存储在哈希表中的指定字段HKEYS key 获取哈希表中所有字段HVALS key 获取哈希表中所有值HGETALL key 获取在哈希表中指定 key 的所有字段和值 3. 列表List操作常用命令LPUSH key value1 [value2] 将一个或多个值插入到列表头部LRANGE key start stop 获取列表指定范畴内的元素RPOP key 移除并获取列表最初一个元素LLEN key 获取列表长度BRPOP key1 [key2] timeout 移出并获取列表的最初一个元素,如果列表没有元素会阻塞列表直到期待超时或发现可弹出元素为止 4.汇合set操作常用命令Redis set汇合成员是惟一的,这就意味着汇合中不能呈现反复的数据,常用命令:SADD key member1 [member2] 向汇合增加一个或多个成员SMEMBERS key 返回汇合中的所有成员SCARD key 获取汇合的成员数SINTER key1 [key2] 返回给定所有汇合的交加SUNION key1 [key2] 返回所有给定汇合的并集SDIFF key1 [key2] 返回给定所有汇合的差集SREM key member1 [member2] 移除汇合中一个或多个成员 ...

October 15, 2022 · 3 min · jiezi

关于redis:原生Redis跨数据中心双向同步优化实践

一、背景 公司基于业务倒退以及战略部署,须要实现在多个数据中心单元化部署,一方面能够实现多数据中心容灾,另外能够晋升用户申请访问速度。须要保障多数据中心容灾或者实现用户就近拜访的话,须要各个数据中心领有统一的全量数据,如果真正实现用户就近读写,也就是实现真正的业务异地多活,数据同步是异地多活的根底,这就须要多数据中心间数据可能双向同步。 二、原生redis遇到的问题 1、不反对双主同步 原生redis并没有提供跨机房的主主同步机制,仅反对主从同步;如果仅利用redis的主从数据同步机制,只能将主节点与从节点部署在不同的机房。当主节点所在机房呈现故障时,从节点能够降级为主节点,利用能够继续对外提供服务。但这种模式下,若要写数据,则只能通过主节点写,异地机房无奈实现就近写入,所以不能做到真正的异地多活,只能做到备份容灾。而且机房故障切换时,须要运维手动染指。因而,想要实现主主同步机制,须要同步工具模仿从节点形式,将本地机房中数据同步到其余机房,其余机房亦如此。同时,应用同步工具实现跨数据中心数据同步,会遇到以下一些问题。(1)数据回环数据回环的意思是,A机房就近写入的数据,通过同步工具同步到B机房后,而后又通过B机房同步工具同步回A机房了。所以在同步的过程中须要辨认本地就近写入的数据还是其余数据中心同步过去的数据,只有本地就近写入的数据须要同步到其余数据中心。(2)幂等性同步过程中的命令可能因断点续传等起因导致反复同步了,此时须要保障同一命令屡次执行保障幂等。(3)多写抵触以双写抵触为例,如下图所示:  DC1写入set a 1,同时DC2写入set a 2,当这两条命令通过同步工具同步到对方机房时,导致最终DC1中保留的a为2,DC2中保留的a为1,也就是说两个机房最终数据不统一。 2、断点续传 针对刹时的断开重连、从节点重启等场景,redis为了进步该场景下的主从同步效率,在主节点中减少了环形复制缓冲区,主节点往从节点写数据的同时也往复制缓冲区中也写入一份数据,当从节点断开重连时,则只须要通过复制缓冲区把断开期间新增的增量数据发送给从节点即可,防止了全量同步,晋升了这些场景下的同步效率。 然而,该内存复制缓冲区一般来说不会太大,生产目前默认设置为64M,跨数据中心同步场景下,网络环境简单,断线的频率和时长可能比同机房更频繁和更长;同时,跨数据中心同步数据也是为了机房级故障容灾,所以要求可能反对更长时间的断点续传,有限增大内存复制缓冲区大小显然不是一个好主见。上面来看看咱们反对redis跨数据中心同步的优化工作。 三、redis节点革新 为了反对异地多活场景,咱们对原生redis代码进行了优化革新,次要包含以下几个方面: 1、对RESP协定进行扩大 为了反对更高效的断点续传,以及为了解决数据回环问题,咱们在redis主节点中对每条须要同步给从节点的命令(大部分为写命令)减少了id,并且扩大了RESP协定,在每条相干命令的头部减少了形如#{id}\r\n模式的协定。本地业务客户端写入的数据仍然遵循原生RESP协定,主节点执行完命令后,同步到从节点的写命令在同步前会进行协定扩大,减少头部id协定;非本地业务客户端(即来自其余数据中心同步)写入的数据均应用扩大的RESP协定。 2、写命令实时写日志为了反对更长时间的断点续传,容忍长时间的机房级故障,本地业务客户端写入的写命令在进行协定扩大后,会程序写入日志文件,同时生成对应的索引文件;为了缩小日志文件大小,以及进步通过日志文件断点续传的效率,来自其余数据中心同步过去的数据不写入日志文件中。 3、同步流程革新原生redis数据同步分为全量同步和局部同步,并且每个主节点有一个内存环形复制缓冲区;首次同步应用全量同步,断点续传时应用局部同步,即先尝试从主节点环形复制缓冲区中进行同步,同步胜利的话则同步完缓冲区中的数据后即可进行增量数据同步,如果不胜利,则依然须要先进行全量同步再增量同步。因为全量同步须要生成一个子过程,并且在子过程中生成一个RDB文件,所以对主节点性能影响比拟大,咱们应该尽量减少全量同步的次数。为了缩小全量同步的次数,咱们对redis同步流程进行革新,当局部同步中无奈应用环形复制缓冲区实现同步时,减少先尝试应用日志rlog进行同步,如果同步胜利,则同步完日志中数据后即可进行增量同步,否则须要先进行全量同步。 四、rLog日志设计分为索引文件与日志文件,均采纳程序写的形式,进步性能,经测试与原生redis开启aof长久化性能统一;然而rlog会定期删除,原生redis为了避免aof文件有限收缩,会定期通过子过程执行aof文件重写,这个对主节点性能比拟大,所以本质上rlog对redis的性能绝对于aof会更小。索引文件和日志文件文件名均为文件中保留的第一条命令的id。索引文件与日志文件均先写内存缓冲区,而后批量写入操作系统缓冲区,并每秒定期刷新操作系统缓冲区真正落入磁盘文件中。相比拟于aof文件缓冲区,咱们对rlog缓冲区进行了预调配优化,达到晋升性能目标。 1、索引文件格式索引文件格式如下所示,每条命令对应的索引数据蕴含三局部: pos:该条命令第一个字节在对应的日志文件中绝对于该日志文件起始地位的偏移 len:该条命令的长度 offset:该条命令第一个字节在主节点复制缓冲区中累积的偏移  2、日志文件拆分 为了避免单个文件有限收缩,redis在写文件时会定期对文件进行拆分,拆分根据两个维度,别离是文件大小和工夫。默认拆分阈值别离为,当日志文件大小达到128M或者每隔一小时同时并且日志条目数大于10w时,写新的日志文件和索引文件。在每次循环解决中,当内存缓冲区的数据全副写入文件时,判断是否满足日志文件拆分条件,如果满足,加上一个日志文件拆分标记,下一次循环解决中,将内存缓冲区数据写入文件之前,先敞开以后的索引文件和日志,同时新建索引文件和日志文件。 3、日志文件删除为了避免日志文件数量有限增长并且耗费磁盘存储空间,以及因为未做日志重写、通过过多的文件进行断点续传效率低下、意义不大,所以redis定期对日志文件和相应的索引文件进行删除。默认日志文件最多保留一天,redis定期删除一天以前的日志文件和索引文件,也就是最多容忍一天工夫的机房级故障,否则须要进行机房间数据全量同步。在断点续传时,如果须要从日志文件中同步数据,在同步开始前会长期禁止日志文件删除逻辑,待同步实现后恢复正常,避免出现在同步的数据被删除的状况。 五、redis数据同步 1、断点续传 如前所述,为了容忍更长时间的机房级故障,进步跨数据中心容灾能力,提升机房间故障复原效率,咱们对redis同步流程进行革新,当局部同步中无奈应用环形复制缓冲区实现同步时,减少先尝试应用日志rlog进行同步,流程图如下所示:  首先,同步工具连贯上主节点后,除了发送认证外,须要先通过replconf capa命令告知主节点具备通过rlog断点续传的能力。 从节点先发送psync runId offset,如果是第一次启动,则先发送psync ? -1,主节点会返回一个runId和offset 如果可能通过复制缓冲区同步,主节点给从节点返回 +CONTINUE runId 如果不可能通过复制缓冲区同步,主节点给从节点返回 +LPSYNC 如果从节点收到+CONTINUE,则持续接管增量数据即可,并持续更新offset和命令id 如果从节点收到+LPSYNC,则从节点持续给主节点发送 LPSYNC runId id 主节点收到LPSYNC命令后,如果可能通过rlog持续同步数据,则给从节点发送 +LCONTINUE runId; 从节点收到+LCONTINUE后,能够把offset设置为LONG_LONG_MIN,或者后续数据不更新offset;持续接管通过rlog同步的增量数据即可; 通过rlog同步的增量数据传输结束后,主节点会给从节点发送 lcommit offset命令; 从节点在解析数据的过程中,收到lcommit命令时,更新本地offset,后续的增量数据持续减少offset,同时lcommit命令无需同步到对端(通过id<0辨认即可,所有id<0的命令均无需同步到对端) 如果不能,此时主节点给从节点返回 +FULLRESYNC runId offset;后续进行全量同步; 2、幂等性 迁徙工具为了进步性能,并不是实时往zk保留同步偏移offset和id,而是定期(默认每秒)向zk进行同步,所以当断点续传时,迁徙工具从zk获取断线前同步的偏移,尝试向主节点持续同步数据,这两头可能会有局部数据反复发送,所以为了保证数据一致性,须要保障命令屡次执行具备幂等性。为了保障redis命令具备幂等性,对redis中局部非幂等性命令进行了革新,具体设计革新的命令如下所示: 注:list类型命令暂未革新,不具备幂等性 3、数据回环解决 数据回环次要是指,当同步工具从A机房redis读取的数据,通过MQ同步到B机房写入后,B机房的同步工具又获取到,再次同步到A机房,导致数据循环复制问题。对于同步到从节点以及迁徙工具的数据,会在头部增加id字段,针对不同起源的数据或者无需同步到远端的数据通过id来标识辨别;本地业务客户端写入的数据须要同步到远端数据中心,调配id大于0;来源于其余数据中心的数据调配id小于0;一些仅用于主从心跳交互的命令数据调配id也小于0。同步工具解析完数据后,过滤掉id小于0的命令,只须要向远端写入id大于0的数据,即本地业务客户端写入的数据。来源于其余数据中心的数据均不回写到远端数据中心。 4、过期与淘汰数据 目前过期与淘汰均由各数据中心redis节点分别独立解决,由过期与淘汰删除的数据不进行同步;即由过期与淘汰产生的删除命令其id调配为小于0,并由同步工具过滤掉。(1)同步产生的问题为什么不同步过来?因为在内存中hash表外面保留的数据没有标记数据中心起源,过期与淘汰的数据有可能来自于其余数据中心,如果来自于其余数据中心的数据被过期或淘汰并且又同步到远端其余数据中心,就会呈现数据双写抵触的场景。双写抵触可能会导致数据不统一。(2)不同步产生的问题对于过期数据来说,不同步删除可能会导致不同数据中心数据显示不统一,然而肯定会最终统一,且不会呈现脏读;对于淘汰数据来说,目前的不同步删除的计划,如果呈现淘汰,会导致不同数据中心数据不统一;目前只有通过运维伎俩,比方短缺预调配、及时关注内存使用率告警,来躲避淘汰数据景象产生。 5、数据迁徙 在redis集群模式中,个别是在产生横向扩容减少集群主节点数时,须要进行槽以及数据的迁徙。redis集群中数据迁徙以槽为维度进行迁徙,将槽中所有数据从源节点迁徙到指标节点,而后将槽号标记为由新的指标节点负责,同时每迁徙完一个Key,会在源节点中进行删除,将migrate命令替换为del命令;同时迁徙数据是在源节点中给指标节点发送restore命令实现。咱们数据迁徙的策略仍然是,各个数据中心独立的实现扩容与数据迁徙工作,迁徙过程产生的del和restore命令不进行跨数据中心同步;把替换后的del命令和发送给指标节点的restore命令都调配小于0的id,于是同步过程中会由同步工具进行过滤掉。 六、redis性能 ...

September 28, 2022 · 1 min · jiezi

关于redis:Lua脚本在Redis事务中的应用实践

应用过Redis事务的应该分明,Redis事务实现是通过打包多条命令,独自的隔离操作,事务中的所有命令都会按程序地执行。事务在执行的过程中,不会被其余客户端发送来的命令申请所打断。事务中的命令要么全副被执行,要么全副都不执行(原子操作)。但其中有命令因业务起因执行失败并不会阻断后续命令的执行,且也无奈回滚曾经执行过的命令。如果想要实现和MySQL一样的事务处理能够应用Lua脚本来实现,Lua脚本中可实现简略的逻辑判断,执行停止等操作。 1 初始Lua脚本Lua是一个玲珑的脚本语言,Redis 脚本应用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌反对 Lua 环境。执行脚本的常用命令为 EVAL。编写Lua脚本就和编写shell脚本一样的简略。Lua语言具体教程参见 示例: --[[ version:1.0 检测key是否存在,如果存在并设置过期工夫 入参列表: 参数个数量:1 KEYS[1]:goodsKey 商品Key 返回列表code: +0:不存在 +1:存在--]]local usableKey = KEYS[1]--[ 判断usableKey在Redis中是否存在 存在将过期工夫缩短1分钟 并返回是否存在后果--]local usableExists = redis.call('EXISTS', usableKey)if (1 == usableExists) then redis.call('PEXPIRE', usableKey, 60000)endreturn { usableExists }示例代码中redis.call(), 是Redis内置办法,用与执行redis命令if () then end 是Lua语言根本分支语法KEYS 为Redis环境执行Lua脚本时Redis Key 参数,如果应用变量入参应用ARGV接管“—”代表单行正文 “—[[ 多行正文 —]]”2 实际利用2.1 需要剖析经典案例需要:库存量扣减并检测库存量是否短缺。 根底需要剖析:商品以后库存量>=扣减数量时,执行扣减。商品以后库存量<扣减数量时,返回库存有余 实现计划剖析: 1)MySQL事务实现: 利用DB行级锁,锁定要扣减商品库存量数据,再判断库存量是否短缺,短缺执行扣减,否则返回库存有余。执行库存扣减,再判断扣减后后果是否小于0,小于0阐明库存有余,事务回滚,否则提交事务。2)计划优缺点剖析: 长处:MySQL人造反对事务,实现难度低。毛病:不思考热点商品场景,当业务量达到一定量级时会达到MySQL性能瓶颈,单库无奈反对业务时扩大问题成为难点,分表、分库等计划对性能开发、业务运维、数据运维都须要有针对于分表、分库计划所配套的零碎或计划。对于零碎革新实现难度较高。Redis Lua脚本事务实现:将库存扣减判断库存量最小原子操作逻辑编写为Lua脚本。 从DB中初始化商品库存数量,利用Redis WATCH命令。判断商品库存量是否短缺,短缺执行扣减,否则返回库存有余。执行库存扣减,再判断扣减后后果是否小于0,小于0阐明库存有余,反向操作减少缩小库存量,返回操作后果计划优缺点剖析: 长处:Redis命令执行单线程个性,毋庸思考并发锁竟争所带来的实现复杂度。Redis人造反对Lua脚本,Lua语言学习难度低,实现与MySQL计划难度相当。Redis同一时间单位反对的并发量比MySQL大,执行耗时更小。对于业务量的增长能够扩容Redis集群分片。毛病:暂无 2.2 Redis Lua脚本事务计划实现初始化商品库存量: //利用Watch 命令乐观乐个性,缩小锁竞争所损耗的性能 public boolean init(InitStockCallback initStockCallback, InitOperationData initOperationData) { //SessionCallback 会话级Rdis事务回调接口 针对于operations所有操作将在同一个Redis tcp连贯上实现List<Object> result = stringRedisTemplate.execute(new SessionCallback<List<Object>>() { public List<Object> execute(RedisOperations operations) { Assert.notNull(operations, "operations must not be null");//Watch 命令用于监督一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其余命令所改变,那么事务将被打断//当出前并发初始化同一个商品库存量时,只有一个能胜利 operations.watch(initOperationData.getWatchKeys()); int initQuantity; try {//查问DB商品库存量 initQuantity = initStockCallback.getInitQuantity(initOperationData); } catch (Exception e) { //异样后开释watch operations.unwatch(); throw e; }//开启Reids事务 operations.multi();//setNx设置商品库存量 operations.opsForValue().setIfAbsent(initOperationData.getGoodsKey(), String.valueOf(initQuantity));//设置商品库存量 key 过期工夫 operations.expire(initOperationData.getGoodsKey(), Duration.ofMinutes(60000L));///执行事事务 return operations.exec(); } });//判断事务执行后果 if (!CollectionUtils.isEmpty(result) && result.get(0) instanceof Boolean) { return (Boolean) result.get(0); } return false; }库存扣减逻辑 ...

September 23, 2022 · 2 min · jiezi

关于redis:Redis开发与运维之内存优化

内存优化咱们都晓得 Redis 的数据都存储在内存中,而内存又是十分贵重的资源,本文将解说如何进行内存优化。 redisObject 对象首先须要理解什么是 redisObject,在 Redis 中存储的所有值对象在外部都被定义为redisObject,构造如下。 type:示意以后对象应用的数据类型,次要就是 string、hash、list、set、zset 五种。4 示意占 4 个 bit 位。 :应用 type [key] 命令能够查看对象的所属类型,返回的是值对象的类型,键都为 string 类型。encoding:示意外部编码的类型,代表以后对象应用哪种数据结构实现。了解外部编码类型对于内存优化十分重要。lru:记录对象最初一次被拜访的工夫,当配置了 maxmemory 和 maxmemory-policy=volatile-lru 或者 allkeys-lru 时,用于辅助 LRU 算法删除键数据。 :应用 object idletime [key] 在不更新 lru 字段时间的状况下查看以后键的闲暇工夫。 应用 scan + object idletime 命令能够批量查问出那些键长时间未被拜访并进行清理,升高内存占用。 refcount:记录以后对象被援用的次数,当 refcount=0 的时候能够平安的进行对象空间回收。 :应用 object recount [key] 获取以后对象援用。当对象值在[0-9999]的时候,redis 会应用共享对象池来节俭内存。* ptr:如果存的是整数,则间接存储数据,否则存储指向数据的指针。当值对象为字符串并且<=44 字节时候,外部编码为 embstr 类型,当>44 字节时候,应用 raw 类型。缩减键值对象缩减 key 和 value 的长度能够无效缩小 Redis 内存应用状况,比方将 key 进行缩写等。 value 比较复杂,如果是将业务对象进行序列化为二进制数组,能够去掉不必要的属性,其次在序列化工具上能够抉择更高效的如:protostuff、kryo 等。除了二进制数组外,咱们也会存入 json、xml 等字符串,在内存缓和状况下,咱们能够应用压缩算法来压缩 json、xml 再存入 redis。 ...

September 22, 2022 · 2 min · jiezi

关于redis:Redis数据倾斜与JD开源hotkey源码分析揭秘

1 前言之前旁边的小伙伴问我热点数据相干问题,在给他粗略地解说一波redis数据歪斜的案例之后,本人也顺道回顾了一些对于热点数据处理的方法论,同时也想起去年所学习JD开源我的项目hotkey——专门用来解决热点数据问题的框架。在这里联合两者所关联到的知识点,通过几个小图和局部粗略的解说,来让大家理解相干方法论以及hotkey的源码解析。 2 Redis数据歪斜2.1 定义与危害先说说数据歪斜的定义,借用百度词条的解释: 对于集群零碎,个别缓存是分布式的,即不同节点负责肯定范畴的缓存数据。咱们把缓存数据分散度不够,导致大量的缓存数据集中到了一台或者几台服务节点上,称为数据歪斜。一般来说数据歪斜是因为负载平衡施行的成果不好引起的。 从下面的定义中能够得悉,数据歪斜的起因个别是因为LB的成果不好,导致局部节点数据量十分集中。 那这又会有什么危害呢? 如果产生了数据歪斜,那么保留了大量数据,或者是保留了热点数据的实例的解决压力就会增大,速度变慢,甚至还可能会引起这个实例的内存资源耗尽,从而解体。这是咱们在利用切片集群时要防止的。 2.2 数据歪斜的分类2.2.1 数据量歪斜(写入歪斜) 1.图示 如图,在某些状况下,实例上的数据分布不平衡,某个实例上的数据特地多。 2.bigkey导致歪斜 某个实例上正好保留了 bigkey。bigkey 的 value 值很大(String 类型),或者是 bigkey 保留了大量汇合元素(汇合类型),会导致这个实例的数据量减少,内存资源耗费也相应减少。 应答办法 在业务层生成数据时,要尽量避免把过多的数据保留在同一个键值对中。如果 bigkey 正好是汇合类型,还有一个办法,就是把 bigkey 拆分成很多个小的汇合类型数据,扩散保留在不同的实例上。3.Slot调配不均导致歪斜 先简略的介绍一下slot的概念,slot其实全名是Hash Slot(哈希槽),在Redis Cluster切片集群中一共有16384 个 Slot,这些哈希槽相似于数据分区,每个键值对都会依据它的 key,被映射到一个哈希槽中。Redis Cluster 计划采纳哈希槽来解决数据和实例之间的映射关系。 一张图来解释,数据、哈希槽、实例这三者的映射散布状况。 这里的CRC16(city)%16384能够简略的了解为将key1依据CRC16算法取hash值而后对slot个数取模,失去的就是slot地位为14484,他所对应的实例节点是第三个。 运维在构建切片集群时候,须要手动调配哈希槽,并且把16384 个槽都调配完,否则 Redis 集群无奈失常工作。因为是手动调配,则可能会导致局部实例所调配的slot过多,导致数据歪斜。 应答办法 应用CLUSTER SLOTS 命令来查 看slot分配情况,应用CLUSTER SETSLOT,CLUSTER GETKEYSINSLOT,MIGRATE这三个命令来进行slot数据的迁徙,具体内容不再这里细说,感兴趣的同学能够自行学习一下。 4.Hash Tag导致歪斜 Hash Tag 定义 :指当一个key蕴含 {} 的时候,就不对整个key做hash,而仅对 {} 包含的字符串做hash。假如hash算法为sha1。对user:{user1}:ids和user:{user1}:tweets,其hash值都等同于sha1(user1)。Hash Tag 劣势 :如果不同 key 的 Hash Tag 内容都是一样的,那么,这些 key 对应的数据会被映射到同一个 Slot 中,同时会被调配到同一个实例上。Hash Tag 劣势 :如果不合理应用,会导致大量的数据可能被集中到一个实例上产生数据歪斜,集群中的负载不平衡。2.2.2 数据拜访歪斜(读取歪斜-热key问题) ...

September 20, 2022 · 14 min · jiezi

关于redis:redis学习之安装

有任何问题都能够留言征询。 装置步骤1、到官网下载要装置的压缩包(https://redis.io/download/) 2、通过rz上传到服务器的机器上 3、解压 (tar -zxvf dist.tar.gz) 4、cd到解压目录 5、执行make 文章不易,请关注公众号 毛毛虫的小小蜡笔,多多反对,谢谢。 启动1、cd到src目录,执行./redis-server

September 20, 2022 · 1 min · jiezi

关于redis:如何在windows上部署多个Redis实例

前言Redis因为其单线程的特点,只会占用机器一个CPU,无奈发现机器多个CPU的劣势。咱们在应用的过程中,经常会部署多个Redis在一台机器上。 在学习Redis的复制、集群等性能的时候,须要多个Redis实例能力实现。这就须要晓得如何在windows上部署多个Redis实例,上面我给大家讲一下如何进行相干操作,心愿大家和我一起提高,一起进步! 大家感觉有用的话,点个赞再走呗! 文件复制大家找到Redis的装置目录,目录外面是蕴含图片外面的这些文件。 把整个文件夹复制一份,对文件夹进行重命名。能够采纳redis-端口号的模式。比方我新建了2个Redis实例的文件夹,别离是Redis-7000和Redis-7001。 其中,Redis-x64-3.2.100是我刚装置Redis的文件夹,也是我筹备作为Redis主节点的实例。 批改Redis端口接下来,咱们进入刚刚复制的文件夹,找到其中的redis.windows.conf和redis.windows-service.conf,两个文件都应用编辑器进行编辑。因为Redis的默认端口是6379,咱们进行搜寻,批改6379的端口号即可。比方咱们进入的是7000的那个文件夹,就把端口号批改为7000。进入7001的文件夹,就把端口号批改为7001 装置和启动Redis服务端口批改实现后,咱们进入别离进入两者的文件夹。输出cmd敲回车进入命令栏窗口。顺次输出: //装置Redis服务redis-server --service-install redis.windows.conf --service-name redis-7001 --loglevel verbose//启动Redis服务redis-server --service-start --service-name redis-7001 查看是否失效进入Windows的服务治理窗口,能够发现有3个Redis实例正在启动运行中。 进入Redis连贯的客户端,比方RedisDesktopManager,用新的端口号进行连贯就能够啦。

September 18, 2022 · 1 min · jiezi

关于redis:redis常见知识点整理

1、缓存穿透大量的申请不存在的key2、缓存击穿key刚过期,申请过去,导致申请到mysql数据库3、缓存雪崩缓存雪崩是指缓存大量生效,导致大量的申请都间接向数据库获取数据,造成数据库的压力。

September 15, 2022 · 1 min · jiezi

关于redis:深入浅出redis缓存应用

0.1、索引https://blog.waterflow.link/articles/1663169309611 1、只读缓存只读缓存的流程是这样的: 当查问申请过去时,先从redis中查问数据,如果有的话就间接返回。如果没有的话,就从数据库查问,并写入到缓存中。 当删改申请过去时,会间接从数据库中删除批改数据,并把redis中保留的数据删除。 这样做的益处是,所有最新的数据都在数据库中,而数据库是有数据可靠性保障的。 2、读写缓存读写缓存的流程是这样的: 当查问申请过去时,先从redis中查问数据,如果有的话就间接返回。如果没有的话,就从数据库查问,并写入到缓存中。当增删改申请过去时,得益于Redis的高性能拜访个性,数据的增删改操作能够在缓存中疾速实现,处理结果也会疾速返回给业务利用,这就能够晋升业务利用的响应速度。然而和只读缓存不同的是,最新的数据都是在redis中,一旦呈现掉电宕机,因为redis的长久化机制,最新的数据有可能会失落,就会给业务带来危险。所以,依据业务利用对数据可靠性和缓存性能的不同要求,咱们会有同步直写和异步写回两种策略。其中,同步直写策略优先保证数据可 靠性,而异步写回策略优先提供疾速响应。 2.1、同步直写当增删改申请过去时,申请到redis的同时,也会申请mysql,等到redis和mysql都写完数据才会返回数据。 这样,即便缓存宕机或产生故障,最新的数据依然保留在数据库中,这就提供了数据可靠性保障。 然而也会升高缓存的使用性能,因为写缓存很快,然而写数据库就要慢很多,整个的响应工夫就会减少。 2.2、异步写回异步写回优先思考了响应速度,写到缓存会立刻响应客户端。等到数据要从redis中淘汰时,再同步到mysql。 然而如果产生掉电,数据还是没有写到mysql,还是有失落的危险。 3、如何抉择如果须要对写申请进行减速,咱们抉择读写缓存;如果写申请很少,或者是只须要晋升读申请的响应速度的话,咱们抉择只读缓存。4、对于一致性对于读写缓存的异步写回,因为是只写redis,淘汰时才会写入mysql,如果产生宕机不能保障一致性对于读写缓存的同步写回,因为redis和mysql是同时写,须要退出事物机制,要么都执行要么都不执行,能够保障一致性。(问题:如何保障原子性?当有并发写过来时即便都执行了也可能会不统一,这是就要引入锁保障互斥性)对于只读缓存,如果产生删改操作,利用既要更新数据库,也要在缓存中删除数据。因为redis和mysql是同时操作,须要退出事物机制,要么都执行要么都不执行,能够保障一致性。(问题:如何保障原子性?)4.1、对于只读缓存的一致性问题先删除缓存,再更新数据库 如果缓存删除胜利,然而数据库更新失败,那么,利用再拜访数据时,缓存中没有数据,就会产生缓存缺失。而后,利用再拜访数库,然而数据库中的值为旧值,利用就拜访到旧值了。如果线程A都胜利了,然而同时另一个线程B在线程A的这俩个申请两头过去。这个时候缓存曾经删除,然而数据库还是旧值,线程B发现没有缓存,就从数据库读读取了旧值更新到redis中,而后线程A把新值更新到数据库。此时redis中是旧值,mysql中是新值。先更新数据库,再删除缓存中的值 如果利用先实现了数据库的更新,然而,在删除缓存时失败了,那么,数据库中的值是新值,而缓存中的是旧值,这必定是不统一的。这个时候,如果有其余的并发申请来拜访数据,依照失常的缓存拜访流程,就会先在缓存中查问,但此时,就会读到旧值了。如果线程A删除了数据库中的值,但还没来得及删除缓存值,线程B就开始读取数据了,那么此时,线程B查问缓存时,发现缓存命中,就会间接从缓存中读取旧值。不过,在这种状况下,如果其余线程并发读缓存的申请不多,那么,就不会有很多申请读取到旧值。而且,线程A个别也会很快删除缓存值,这样一来,其余线程再次读取时,就会产生缓存缺失,进而从数据库中读取最新值。所以,这种状况对业务的影响较小。(能够了解为最终一致性,读到旧数据只是临时的,最终都会读到新数据)所以个别我的项目中应用只读缓存,先更新数据库,再删除缓存。这样的代价是最小的,而且尽量保障了一致性。 5、缓存异样5.1、缓存雪崩缓存雪崩是指,大量的申请无奈在redis中解决(redis没拦住),间接打到了mysql,导致数据库压力激增,甚至服务解体。 redis无奈解决的起因有两种: 缓存中大量数据同时过期 解决方案: 给过期工夫减少一个较小的随机数,过期的数据通过工夫去摊派服务降级,间接返回错误信息Redis缓存实例产生故障宕机了,无奈解决申请,这就会导致大量申请一下子积压到数据库层 解决方案: 服务熔断或者申请限流,redis客户端间接返回,不会申请到redis服务,然而影响范畴比拟大构建redis集群,进步可用性5.2、缓存击穿缓存击穿是指,拜访某个热点数据,无奈在缓存中解决,大量申请打到mysql,导致数据库压力激增,甚至服务解体。 解决方案: 对于频繁拜访的热点数据不设置过期工夫5.3、缓存穿透缓存穿透是指,要拜访的数据既不在redis中,也不在mysql中。申请redis发现数据不存在,持续拜访mysql发现数据还是不存在,而后也无奈写回缓存,下次持续申请的时候还是会打到mysql。 解决方案: 缓存空值或者缺省值应用布隆过滤器布隆过滤器 布隆过滤器由一个初值都为0的bit数组和N个哈希函数组成,能够用来疾速判断某个数据是否存在(精确说是判断不存在,如果布隆过滤器不存在数据库中肯定不存在,如果布隆过滤器判断存在,数据库不肯定存在,这是布隆过滤器的机制决定的)。当咱们想标记某个数据存在时(例如,数据已被写入数据库),布隆过滤器会通过三个操作实现标记: 首先,应用N个哈希函数,别离计算这个数据的哈希值,失去N个哈希值。而后,咱们把这N个哈希值对bit数组的长度取模,失去每个哈希值在数组中的对应地位。最初,咱们把对应地位的bit位设置为1,这就实现了在布隆过滤器中标记数据的操作。如果数据不存在(例如,数据库里没有写入数据),咱们也就没有用布隆过滤器标记过数据,那么,bit数组对应bit位的值依然为0。 所以当咱们写入数据库时,应用布隆过滤器做个标记。当缓存缺失后,利用查询数据库时,能够通过查问布隆过滤器疾速判断数据是否存在。如果不存在,就不必再去数据库中查问了。 6、利用场景咱们看下go-zero中是如何应用缓存的,go-zero中应用的只读缓存,当数据有更新删除操作的时候,redis中的对应Primary记录和查问条件记录会同步删除。go-zero中对某行的缓存,会缓存主键到行记录的缓存,和查问条件(惟一索引)到主键的缓存 咱们看下查问的逻辑(针对的是单行的记录): 通过查问条件查问某条记录时,如果没有查问条件到主键的缓存通过查问条件到mysql查问行记录,而后把主键到行记录的缓存,和查问条件(惟一索引)到主键的缓存更新到redis(前者的过期工夫会多余后者几秒工夫)持续回到1,如果有查问条件到主键的缓存,如果没有主键到记录的缓存,通过主键到mysql查问并写入redis上面看下go-zero源码: // v - 须要读取的数据对象// key - 缓存key// query - 用来从DB读取残缺数据的办法// cacheVal - 用来写缓存的办法func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // singleflight一批申请过去,只容许一个去真正拜访数据,避免缓存击穿 val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // 从cache里读取数据 if err := c.doGetCache(key, v); err != nil { // 如果是事后放进来的placeholder(用来避免缓存穿透)的,那么就返回预设的errNotFound // 如果是未知谬误,那么就间接返回,因为咱们不能放弃缓存出错而间接把所有申请去申请DB, // 这样在高并发的场景下会把DB打挂掉的 if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // 申请DB // 如果返回的error是errNotFound,那么咱们就须要在缓存里设置placeholder,避免缓存穿透 if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // 统计DB失败 c.stat.IncrementDbFails() return nil, err } // 把数据写入缓存 if err = cacheVal(v); err != nil { logx.Error(err) } } // 返回json序列化的数据 return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // 把数据写入到传入的v对象里 return jsonx.Unmarshal(val.([]byte), v)}从下面代码咱们能够看到: ...

September 14, 2022 · 2 min · jiezi

关于redis:一文了解Redis常用命令

前言本文包含Redis中罕用的一些命令,包含针对所有的键相干的命令,以及5种罕用数据类型:字符串、哈希、列表、汇合以及有序汇合的一些命令。 鉴于集体程度无限,文章中若有不对之处,烦请大家留言斧正。 键相干查看所有的键keys会遍历所有的键,它的工夫复杂度是O(n),因而当Redis保留了大量的键时,这个命令会十分耗时,因而生产环境下禁止应用该命令。 keys *//初始化数据study:0>set zhangsan 20"OK"study:0>get zhangsan"20"study:0>set lisi 25"OK"study:0>set wangwu 30"OK"//应用study:0>keys * 1) "lisi" 2) "zhangsan" 3) "wangwu"keys命令也反对含糊匹配,比方咱们想获取所有以zhang结尾的键,能够这样写: keys zhang*study:0>keys zhang* 1) "zhangsan"获取键的总数dbsizestudy:0>dbsize"3"dbsize命令在计算键总数时不会遍历所有的键,而是间接获取Redis内置的键总数变量,所以dbsize命令的工夫复杂度是O(1) 查看键是否存在exists key这个命令返回1或者0,1示意键存在,0示意键不存在 study:0>exists zhangsan"1"study:0>exists dalao"0"删除键del key [key2, key3....]这个命令返回删除胜利的键总数,如果删除的键都不存在,则返回0 study:0>del zhangsan"1"study:0>del dalao"0"设置键的过期工夫Redis反对给键设置过期工夫,当键过期后,该键会在Redis中被主动删除。 //初始化键study:0>set hello world"OK"//给键设置50秒的过期工夫study:0>expire hello 50"1"设置过期工夫之后,咱们也能够通过ttl命令返回键的残余过期工夫,ttl命令有3种返回值: 大于等于0的整数:键的残余过期工夫-1示意键没有设置过期工夫-2示意键不存在(有可能是最开始就不存在,有可能是键的过期工夫到了被主动删除了)study:0>ttl hello"10"study:0>ttl hello"3"study:0>ttl hello"-2"键的数据类型type keystudy:0>set hello world"OK"study:0>type hello"string"若key的类型是字符串类型,则返回string。若key的类型是列表类型,则返回list。 若key不存在,则返回none study:0>type abc"none"字符串类型设置值set key value [ex seconds] [px milliseconds] [nx|xx]study:0>set me water76016"OK"set命令的选项阐明 ex seconds:为键设置秒级过期工夫px milliseconds:为键设置毫秒级过期工夫nx:键必须不存在能力设置胜利,用于新增xx:键必须存在能力设置胜利,用于更新其余set命令 Redis提供了setnx和setex两个命令,作用和ex和nx参数是一样的。 //命令格局setex key seconds valuesetnx key value//setex应用范例(设置键为hello,20秒的过期工夫)study:0>setex hello 20 world"OK"//setnx应用范例(第一次键不存在设置胜利,第二次键曾经存在设置失败)study:0>setnx me water76016"1"study:0>setnx me water76016"0"获取值get key如果获取的键不存在,则会返回null(空) ...

September 13, 2022 · 3 min · jiezi

关于redis:聊聊Redis的数据热点问题

 前两天,咱们应用的某云厂商服务挂了,而且一挂就是挂大半天,咱们的服务强依赖于他们,所以咱们也跟着一起挂。然而咱们却无能为力,只能等他们复原。事变起因中听他们提到Redis有个热key,正好我在上家公司负责过部门Redis集群,也解决过很多起Redis数据热点的问题,接下来就一起聊聊什么是Redis热点?Redis热点问题为什么会极大地影响整个集群的性能?如何防止Redis数据热点?热点问题如何排查?热点问题如何解决? 什么是Redis热点? 我曾在我过往的博文中屡次提到了局部性一词(对于局部性能够看下我之前的博文 局部性原理),数据热点就是数据拜访局部性的体现,具体表现就是Redis中某个Key的拜访频次远大于其余残余的Key,咱们也有一句俗语来形容这种景象旱的旱死,涝的涝死。 为什么Redis会有热点问题?这就得从Redis的原理说起了。家喻户晓,Redis中存储的是K-V数据,在集群模式下,Redis会将所有数据按Key的CRC64值调配到16384个数据槽(slot)中,并将这16384个数据槽调配到集群中各个机器上,尽可能实现数据在各个机器上的平均存储。但平均存储并不意味着平均拜访,有时候某个Key的申请会占到总申请的很大一部分,这就会导致申请集中在某个Redis实例上,将该Redis实例的承载能力耗尽,所有存储在这个实例上的其余数据也就无奈失常拜访了,这就意味着所有依赖于这些数据的服务都会出问题。 这里的出问题并不是说Redis会间接宕机,家喻户晓Redis的外围流程是单线程模式,这就意味着Redis是串行解决所有申请的,当申请过多时,申请就会拥挤起来,从应用层的视角来看,就是申请Redis的耗时会特地特地高。因为应用层应用Redis都是作为缓存,都是同步申请,所以会间接导致应用层的申请解决也会特地耗时,从而导致应用层申请也逐步拥挤,最终整体不可用。 咱们来举个简略的例子,置信大家都在微博上吃过瓜,当有大瓜呈现时,微博上会迅速涌入一批用户检索相干信息,疯狂拜访同一份微博(数据),这种状况下这份数据就是热点数据,如果数据太“热”最终会导致微博挂掉,实际上微博挂掉的状况曾经呈现很屡次了,不是因为微博技术不行,而是因为热点问题太可怕。 Redis热点是如何拖垮其余服务的? Redis的热点并不仅仅会导致单个服务异样,而是会导致所有依赖于此Redis集群的所有服务异样。上图中Server2、Server、Server3频繁拜访XXX_KEY,导致RedisServer2实例不可用,因为Server4依赖于RedisServer2上的Key7,即使是RedisServer1 3 4 5 都失常服务,Server4也无奈对外提供失常的服务。 是不是有人会问RedisServer2挂了,不能把它下掉,换一个新的机器下来吗? 其实在Redis集群模式下,某个实例宕机Redis集群会主动将其替换。但当初的状况是,即使是替换了新实例,大量的申请也会一下子涌进来将其压挂。所以面对Redis热点问题,重启之类的伎俩是有效的,只能从申请端解决问题。 其实这就是一个很显著的 木桶模型, 一个木桶所能装水的多少取决于木桶上最短的那块木板,当利用应用Redis集群时,Redis集群的性能下限并不齐全等于单实例下限乘以实例个数。 但当Redis集群中任一实例有问题,下层感知到的就是整个Redis集群有问题。 Redis热点如何防止? 上文也说到,热点问题其实是局部性问题,而局部性问题的防止其实十分难,任何分布式系统简直都会受局部性的影响。面对这种问题,说实话没有相对能够防止的形式,只能提前通过剖析数据的个性,做好相应的措施。说白了就是靠教训,不晓得大家有啥其余好的思路,能够在评论区探讨下。 热点问题如何排查? Redis的热点的问题其实算是很好查的,就是靠监控数据,监控Redis各个实例的CPU使用率、QPS 数据,如果你看到redis集群中某些实例负载和QPS特地高,但其余实例负载很低,不必问必定是呈现热点问题了,接下来你须要做的就是找出具体的热点key,并且找出数据拜访的起源。 找出热点Key其实也很简略,抓局部拜访日志,而后统计下很容易就看进去了。但比拟难的是找出数据拜访的起源,像我之前所在的公司,同一个Redis集群是被很多业务所共享的,但Redis的拜访并为被纳入到全链路监控的数据中,所以找出拜访起源最间接的形式就是在群外面问,听起挺原始的,但也没有其余办法。 热点问题如何解决? 尽管解决问题最好的形式是防止问题的产生,但我方才也说了,Redis热点其实很难防止,任何业务中热点肯定有,但不肯定会造成劫难而已。 热点的发现不肯定非得是事变引出的,咱们也能够在日常工作中定期巡检,一有发现热点的苗头,间接将其扼杀。 对于发现热点后如何解决,我这里提供两个我的解决思路,大家能够探讨下: 应用层Cache 罕用的实现形式就是在利用里实现本地缓存(LocalCache),就相当于是对Redis数据又加了一层Cache,对于那些十分热的热点数据,应用层有极大的概率能在本地缓存中找到数据,只有极小局部LocalCache数据过期时的申请会漏到上面,这样热点数据的申请在应用层外部就能消化,从而极大缩小对Redis的压力。 这种实现办法的话,仅须要数据读取端做革新,数据写入端齐全不须要革新。然而毛病的话也很显著: 须要各端自行实现,会减少应用层开发和保护老本。会额定节约各端的存储空间。须要针对性开发,不适宜大范畴推广。减少数据正本 既然热点问题是因为某个Key被大量拜访导致的,那咱们将这个Key的申请做下拆分不就行了。例如,原始的热点Key叫做XXX_KEY,咱们在数据写入的时候,能够用不同的Key反复写10份,比方 XXX_KEY_01, XXX_KEY_02……XXX_KEY_10, 拜访的时候在原始Key上随机凭接一个1-10之间的后缀即可,这样就能实现数据申请的扩散,如果想让申请更扩散,能够存储更多的正本。 这种计划的长处就是数据读取端实现老本较低(也不是齐全没有),但对数据写入端的要求就高多了。不仅要写入多份,而且还得思考数据写入后一致性的问题。这种办法要求两端都得改,貌似更麻烦了,你是不是感觉还不如第一种计划?实则不然,一般来说读取端会很多且很扩散,革新的老本会十分高,频繁变动更是不太可能,所以有些工作不得不搁置到比拟集中的端上。 以上两种计划其实都是通过存储来换性能,次要差别点就在于由谁来做而已。前者是客户端来做,后者是服务端来做,各有优缺点。 有没有可能对外提供纯正的Redis协定,但能够解决数据热点的问题?。鲁迅……David Wheeler已经说过,计算机科学的如何问题都能够通过减少一层来解决,热点的问题也不例外。咱们能够在利用和Redis之间加一层中间层,这层中间层能够是实在的服务,比方数据对立拜访层。也能够是特制的Redis客户端。 中间层能够对特定的Key加本地Cache,就能够保障热点不会呈现在Redis上。至于对哪些Key加本地Cache,中间层能够实时去剖析近期申请热点数据,自行决定。其实最简略的形式就是开个LRU或者LFU的Cache。 另外,像第二种减少数据正本的计划,也齐全能够由中间层去实现。当咱们发现有数据热点时,让中间层被动将热点数据复制,拦挡并改写所有对热点数据的申请,将其分散开来。 当然,如果中间层更智能的化,这些齐全都能够实现自动化,从热点的发现到解决,齐全不须要人参加。 明天的文章就到这了,大家感觉有用请点赞,喜爱请关注。对于Redis热点的问题,大家有啥认识或者教训能够在评论区留言探讨。

September 11, 2022 · 1 min · jiezi

关于redis:Centos安装Redis极速安装

下载从官网找到下载文件,我下载的是redis-6.0.16.tar.gz。 装置1. 解压文件解压文件而后,进入解压文件夹: tar -zxvf redis-6.0.16.tar.gzcd redis-6.0.162. 装置编译 make装置 make install PREFIX=/usr/local/redis从解压文件复制redis.conf 到 usr/local/redis/bin: cp redis.conf usr/local/redis/bin批改 redis.conf1. 开启后盾启动Redis默认前台启动,窗口就不能敞开,否则Redis服务就进行,这很不不便,所以须要开启后盾启动。 daemonize yes2. 开启近程连贯正文bind bind 127.0.0.1敞开平安爱护 protected-mode no增加明码 requirepass 明码配置 systemctl 服务在/usr/lib/systemd/system创立redis.service文件: vim /usr/lib/systemd/system/redis.service增加配置内容:[Unit]Description=redisAfter=network.target[Service]Type=forkingExecStart=/usr/local/redis/bin/redis-server /etc/redis.confExecStop=/usr/local/redis/bin/redis-cli -p 6379 shutdownPrivateTmp=true[Install]systemctl服务命令:# 查问状态systemctl status nginx# 启动systemctl start nginx# 敞开systemctl stop nginx# 设置开机启动systemctl enable nginx启动服务systemctl start nginx执行命令之后,就能胜利近程连贯Redis服务了。

September 11, 2022 · 1 min · jiezi

关于redis:大军闲聊-Redisson和curator的分布式锁

Redisson的分布式锁是基于redis的,curator的分布式锁是基于zookeeper的。curator的分布式锁在zookeeper之分布式锁曾经解说过了,这篇不讲源码的具体实现,讲讲他们实现分布式锁的流程。 Redisson的分布式锁假如当初有某个服务的线程A,向Redis申请锁。他须要提供这个锁的名称(假如lock)、本人的信息(假如Thread-A)以及key生效的工夫(假如30s,如果没有设置生效工夫,当线程A异样退出,这个key会始终保留在Redis中,那其余线程申请锁的时候,会始终阻塞导致申请不到锁)。 向Redis申请锁的时候,线程A会发送Lua脚本给Redis。 因为思考到锁的可重入,所以这个数据结构是哈希,key对应的值是锁的名称,哈希是锁的持有者和拿到锁的数值。 Redis收到申请后,就会判断是否有这个key,因为线程A是第一次申请这个key的,所以很显著没有,于是就创立新的哈希表,设置对应的数值和过期工夫。 如果线程A再申请lock这个锁,Redis此时曾经有了这个key,所以他就会看看这个key的持有者是不是以后申请者,此时以后持有者就是线程A,所以就会把数值加1,从1变成了2,而后再重置过期工夫。 咱们下面设置了30s的过期工夫,如果程序执行了30s,还没执行完,就过期了,那这个锁就生效了,其余线程就会拿到锁,导致互斥性生效,所以Redisson有一个监听狗的机制。 咱们的过期工夫是30s,那监听狗(其实就是开启了一个线程)每30s/3=10s会给Redis发送申请,如果这个key的持有者是以后线程,那就会把生效工夫重置为30s。 在线程A持有锁的时候,线程B也来申请锁。 因为这个key存在,且不是线程B持有的锁,所以他就获取锁失败了。 失败后他就会去订阅这个锁的解锁播送信息,并且创立信号量。 线程A开释锁的时候,会判断是否为以后锁的持有者,因为以后就是线程A,所以把数值从2减1,阐明还是线程A持有锁。 当线程A再开释锁的时候,这个数值就减为0,阐明线程A曾经不须要这个锁了,此时Redis就会公布这个锁的解锁播送。 线程B可是始终订阅这个锁的解锁播送,当A解锁后,线程B就会接管到这个订阅,所以就开释信号量。 过后开释信号量的时候,线程B就开始从新申请,流程同上,如果还是没有获取到锁,那就会持续订阅这个锁的播送信息,当然不是有限期待,获取锁的过期工夫到了,就阐明没有获取锁。 curator的分布式锁同样假如当初有某个服务的线程A,向Zookeeper申请锁。他须要提供这个锁的门路,并要求Zookeeper生成一个长期有序节点。因为这个lock目前还没有创立过长期节点,所以假如这个长期有序节点是/lock/0001。Zookeeper创立后就会把这个门路返回给线程A。 线程A拿到门路后,还会去拿/lock下的所有节点(此时就/lock0001),并进行排序。 排序完看看以后的节点是不是第一个,因为目前就是第一个,所以线程A就相当于拿到了锁,把线程的信息、节点的门路保留在内存中,并默认lockCount的值为1。 如果线程A再申请锁,他就会先看看内存是否有以后线程和这个门路的信息,如果有,就把lockCount加1,此时lockCount就是2。 在线程A持有锁的时候,线程B也来申请锁。 因为这个lock曾经有了0001的长期有序节点,所以Zookeeper就会创立lock0002并返回给线程B。 线程B拿到门路后,同样会去拿/lock下的所有节点(此时就有/lock0001和/lock0002),并进行排序。 很显著他不是第一个节点,所以拿不到锁,于是就监听lock0001,并进入了wait。 线程A开释锁的时候,就会把内存中的lockCount减1,而后判断lockCount是否大于0,如果大于0,阐明之前重入过,还须要持有锁,如果小于0,阐明曾经开释完了,如果等于0,阐明不须要加锁了,那就移除监听,并删除长期有序节点。 Zookeeper删除lock0001时,Zookeeper的watch机制就会告诉到线程B,此时就会唤醒线程B,线程B会从新去Zookeeper拉取子节点列表,并排序,此时lock0002是第一个,所以他就获取到了锁,而后把线程的信息、节点的门路保留在内存中,并默认lockCount的值为1。 Zookeeper分布式锁的流程大略如上。 那为什么是长期节点?当线程A异样退出后,这个长期节点也会删除,这样就能够通过watch机制告诉监听这个节点的服务。 为什么是程序节点?长期节点也能够保障互斥性,然而这样就会很多个服务始终争着去创立这个节点。 所以Zookeeper的长期节点,能够不必像Redis一样,须要过期工夫防死锁,Watch机制不必像Redis一样,须要监听狗来放弃锁。 重入也有不一样的中央,Zookeeper是保留客户端,Redis是保留在Redis中。

September 8, 2022 · 1 min · jiezi

关于redis:每个程序员都应该知道的-Redis-知识-String-底层原理

本文将讲述如下内容: 1.Redis 中 Srting 类型的底层实现原理 通过 String 底层实现原理的学习,咱们能够学习到哪些底层优化办法3.Redis 中对于 String 命令介绍 咱们晓得 Redis 是由 C 语言实现的,在介绍 Sring 类型的实现之前咱们先温习一下 C 语言的字符串类型。C 语言中的字符串是以空字符结尾的字符数组,具体阐明见下图: C 语言中的字符串1、Redis 中 Srting 类型的底层实现原理 Redis 中没有间接应用 C 语言的字符串,而是构建了一套本人的形象类型,名为简略动静字符串,简称 SDS。 如果你看 Redis 源码会发现这样一种构造体: SDS 的定义len 记录 buf 数组中曾经应用字节的数量,也就是 SDS 类型所保留的字符串的长度。 free 记录了 buf 数组中未应用的字节数量 buf 是存储字节的数组,用于保留字符串从下面的构造咱们能够看出: Redis 的 String 类型封装成 SDS 构造,体现了用空间换工夫的算法思维。就义了一些空间,来换取更快的查问效率。比如说构造体中 len 的值 5 示意这个 SDS 保留了一个五个字节长的字符串,O (1) 的工夫复杂度就能够查问出后果。 Redis 的定位就是数据库层之上的一层缓存层,所以处处都要思考高效。其实缓存自身也是用空间换工夫思维的体现。 解决了 C 字符串容易缓冲区溢出的问题。因为 C 字符串不记录字符串长度,所以通过 C 语言的 strcat 函数拼接字符串的时候,容易造成拼接后的字符串超过了自身申请的字符串的长度,造成缓冲区溢出。SDS 就不会呈现这个问题 ...

September 7, 2022 · 1 min · jiezi

关于redis:ECX知识点

有任何问题都能够留言征询。 背景有个解决方案的共事走过去,看了下我电脑界面上的展现。 而后问了几个问题,都是业务相干的,所以这里记录下。 问题1、怎么会有区域集群? 2、怎么还有虚构公有云和子网? ECX知识点ECXecx,中文是边缘云,英文全称是Edge Cloud X。 位于云网络边缘地位,将计算能力、存储能力、网络能力,从云核心云资源池经由运营商网络下沉至城域网,为用户提供属地化云服务。 智能边缘云依靠于遍布全国的运营商IDC机房、MSE机房建设,兼具私有云和CDN个性,算力提供状态丰盛,治理开销小,网络提早低,节点散布广,具备云边协同服务能力,满足您的业务在低时延、大带宽、本地化场景下算力需要。 ECX兼具云和CDN(Content Delivery Network)的个性,能够看成是CDN节点有了云的能力,也能够看成是云的下沉。云资源池将计算资源虚拟化,具备性能组件丰盛的特点;CDN海量节点中转网络边缘,具备反对大连贯、大带宽、计算在边缘卸载的反对能力。ECX联合了两方面的劣势,帮忙客户将计算业务以多种形式下沉至边缘节点。 区域集群

September 7, 2022 · 1 min · jiezi

关于redis:阿里架构师从应用原理集群拓展源码等方面深入解析Redis

全书由:根底篇、利用篇、原理篇、集群篇、拓展篇、源码篇六局部组成总览 因为文档的内容太多,所以只截取了局部内容展现,有想获取残缺PDF文档的敌人【点击此处】即可获取根底篇Redis根底数据结构 利用篇 分布式锁 延时队列 位图 HyperLogLog 布隆过滤器 简略限流 漏斗限流 GeoHash Scan 原理篇 线程IO模型 通信协议 长久化 管道 事务 PubSub 小对象压缩 主从同步 集群篇 Sentinel Codis Cluster 拓展篇 Stream Info指令 再谈分布式锁过期策略LRU懈怠删除优雅地应用Jedis爱护RedisRedis平安通信源码篇 因为文档的内容太多,所以只截取了局部内容展现,有想获取残缺PDF文档的敌人【点击此处】即可获取

September 4, 2022 · 1 min · jiezi

关于redis:Redis-突然变慢了如何排查并解决

Redis 通常是咱们业务零碎中一个重要的组件,比方:缓存、账号登录信息、排行榜等。 一旦 Redis 申请提早减少,可能就会导致业务零碎“雪崩”。 我在独身红娘婚恋类型互联网公司工作,在双十一推出下单就送女朋友的流动。 谁曾想,凌晨 12 点之后,用户量暴增,呈现了一个技术故障,用户无奈下单,过后老大火冒三丈! 通过查找发现Redis。 获取不到连贯资源,并且集群中的单台 Redis 连贯量很高。 大量的流量没了 Redis 的缓存响应,间接打到了 MySQL,最初数据库也宕机了…… 于是各种更改最大连接数、连贯期待数,尽管报错信息频率有所缓解,但还是继续报错。 起初通过线下测试,发现寄存Redis字符数据很大,均匀 1s 返回数据。 能够发现,一旦 Redis 提早过高,会引发各种问题。 Redis 性能出问题了么?最大提早是客户端收回命令到客户端收到命令的响应的工夫,失常状况下 Redis 解决的工夫极短,在微秒级别。 当 Redis 呈现性能稳定的时候,比方达到几秒到十几秒,这个很显著咱们能够认定 Redis 性能变慢了。 有的硬件配置比拟高,当提早 0.6ms,咱们可能就认定变慢了。硬件比拟差的可能 3 ms 咱们才认为呈现问题。 那咱们该如何定义 Redis 真的变慢了呢?所以,咱们须要对以后环境的 Redis 基线性能做测量,也就是在一个零碎在低压力、无烦扰状况下的根本性能。 当你发现 Redis 运行时时的提早是基线性能的 2 倍以上,就能够断定 Redis 性能变慢了。 提早基线测量redis-cli 命令提供了–intrinsic-latency 选项,用来监测和统计测试期间内的最大提早(以毫秒为单位),这个提早能够作为 Redis 的基线性能。 redis-cli --latency -h `host` -p `port`比方执行如下指令: redis-cli --intrinsic-latency 100Max latency so far: 4 microseconds.Max latency so far: 18 microseconds.Max latency so far: 41 microseconds.Max latency so far: 57 microseconds.Max latency so far: 78 microseconds.Max latency so far: 170 microseconds.Max latency so far: 342 microseconds.Max latency so far: 3079 microseconds.45026981 total runs (avg latency: 2.2209 microseconds / 2220.89 nanoseconds per run).Worst run took 1386x longer than the average latency.留神:参数100是测试将执行的秒数。咱们运行测试的工夫越长,咱们就越有可能发现提早峰值。 ...

September 4, 2022 · 4 min · jiezi

关于redis:如何用-Redis-实现分布式锁的

如何用 Redis 实现分布式锁的?分布式锁是用于分布式环境下并发管制的一种机制,用于管制某个资源在同一时刻只能被一个利用所应用。如下图所示: Redis 自身能够被多个客户端共享拜访,正好就是一个共享存储系统,能够用来保留分布式锁,而且 Redis 的读写性能高,能够应答高并发的锁操作场景。 Redis 的 SET 命令有个 NX 参数能够实现「key不存在才插入」,所以能够用它来实现分布式锁: 如果 key 不存在,则显示插入胜利,能够用来示意加锁胜利;如果 key 存在,则会显示插入失败,能够用来示意加锁失败。基于 Redis 节点实现分布式锁时,对于加锁操作,咱们须要满足三个条件。 加锁包含了读取锁变量、查看锁变量值和设置锁变量值三个操作,但须要以原子操作的形式实现,所以,咱们应用 SET 命令带上 NX 选项来实现加锁;锁变量须要设置过期工夫,免得客户端拿到锁后产生异样,导致锁始终无奈开释,所以,咱们在 SET 命令执行时加上 EX/PX 选项,设置其过期工夫;锁变量的值须要能辨别来自不同客户端的加锁操作,免得在开释锁时,呈现误开释操作,所以,咱们应用 SET 命令设置锁变量值时,每个客户端设置的值是一个惟一值,用于标识客户端;满足这三个条件的分布式命令如下: SET lock_key unique_value NX PX 10000 lock_key 就是 key 键;unique_value 是客户端生成的惟一的标识,辨别来自不同客户端的锁操作;NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;PX 10000 示意设置 lock_key 的过期工夫为 10s,这是为了防止客户端产生异样而无奈开释锁。而解锁的过程就是将 lock_key 键删除(del lock_key),但不能乱删,要保障执行操作的客户端就是加锁的客户端。所以,解锁的时候,咱们要先判断锁的 unique_value 是否为加锁客户端,是的话,才将 lock_key 键删除。 能够看到,解锁是有两个操作,这时就须要 Lua 脚本来保障解锁的原子性,因为 Redis 在执行 Lua 脚本时,能够以原子性的形式执行,保障了锁开释操作的原子性。 // 开释锁时,先比拟 unique_value 是否相等,防止锁的误开释if redis.call("get",KEYS[1]) == ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end这样一来,就通过应用 SET 命令和 Lua 脚本在 Redis 单节点上实现了分布式锁的加锁和解锁。 基于 Redis 实现分布式锁有什么优缺点?基于 Redis 实现分布式锁的长处: ...

August 31, 2022 · 1 min · jiezi

关于redis:阿里大佬珍藏的Redis笔记被我搞到手了不得不说从头到尾全是精华

前言Redis 是互联网技术畛域应用最为宽泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「近程字典服务」。Redis 以其超高的性能、完满的文档、简洁易懂的源码和丰盛的客户端库反对在开源中间件畛域广受好评。国内外很多大型互联网公司都在应用 Redis,比方 Twitter、YouPorn、暴雪娱乐、Github、StackOverflow、腾讯、阿里、京东、华为、新浪微博等等,很多中小型公司也都有利用。也能够说,对 Redis 的理解和利用实际已成为当下中高级后端开发者绕不开的必备技能。 所谓「授人以鱼不如授人以渔」,明天小编就给大家分享一份阿里外部《 Redis 笔记》,这2份Redis外围笔记及面试高频解析,共888页,对 Redis 的相干常识做了零碎全面的介绍,还是PDF版本,可自在复制,特地适宜 Redis 初学者疾速入门和进步。 本笔记适宜人群:前半部分适宜 Redis 初学者疾速入门和进步;后半局部适宜对于 Redis 有肯定理解的开发者,深刻开掘其原理。因为篇幅起因,本文只展现了目录和内容截图,有须要学习的小伙伴只需【点击此处】即可获取! 第一份笔记:Redis外围笔记目录 一、缓存概念二、Redis部署与应用三、Redis高可用与集群四、Memcached 1.缓存概念 缓存是为了调节速度不统一的两个或多个不同的物质的速度,在两头对速度较快的一方起到一个减速访问速度较慢的一方的作用,比方 CPU 的一级、二级缓存是保留了 CPU 最近常常拜访的数据,内存是保留 CPU 常常拜访硬盘的数据,而且硬盘也有大小不一的缓存,甚至是物理服务器的 raid 卡有也缓存,都是为了起到减速 CPU 拜访硬盘数据的目标,因为 CPU 的速度太快了,CPU 须要的数据因为硬盘往往不能在短时间内满足 CPU 的需要,因而 PCU 缓存、内存、Raid 卡缓存以及硬盘缓存就在肯定水平上满足了 CPU 的数据需要,即 CPU 从缓存读取数据能够大幅提高 CPU 的工作效率。 零碎缓存用户层缓存CDN 缓存应用层缓存其余层面缓存 2.Redis 部署与利用 Redis和Memcached 是非关系型数据库也称为 NoSQL 数据库,MySQL、Mariadb、SQL Server、PostgreSQL、Oracle 数据库属于关系型数据(RDBMS, Relational Database Management System) Redis 根底Redis 装置及应用Redis 配置文件Redis 数据类型Redis 音讯队列Redis 常用命令 ...

August 30, 2022 · 1 min · jiezi

关于redis:故障分析-Redis-Cluster-分片内存异常使用不均问题诊断

作者:任仲禹 爱可生 DBA 团队成员,善于故障剖析和性能优化,文章相干技术问题,欢送大家一起探讨。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 背景问题产生背景为某生产 Redis 集群(版本 Redis 5.0.10 ,架构为 30 片以上),该集群中某一个分片内存使用率异样高(内存占用达70%以上,其它片内存绝对应用较低),咱们模仿生产环境如下监控图所示: 置信看文章题目大家都已晓得问题论断,我这里想跟大家分享的是排查这种问题的办法。 诊断内存应用散布监控查看内存应用散布发现,该异样分片实例内存 Redis 应用为356M左右,单个 redis 最大可用内存512M其它失常分片 redis 内存应用为100M 以内 异样与失常实例内存应用比照观测到异样实例的数据量(info keyspace)绝对还少一点但异样实例 数据对象占用内存为其它失常实例2倍### 失常实例redis-cli -p 6380 -h 10.186.62.28 info keyspace ##数据量# Keyspacedb0:keys=637147,expires=0,avg_ttl=0redis-cli -p 6380 -h 10.186.62.28 info memory |grep -w used_memory ##内存应用used_memory:104917416### 异样实例redis-cli -p 6382 -h 10.186.62.5 info keyspace ##数据量# Keyspacedb0:keys=191433,expires=0,avg_ttl=0redis-cli -p 6382 -h 10.186.62.56 info memory |grep -w used_memory ## 内存应用used_memory:373672656碎片率应用状况异样实例内存碎片失常,排除碎片过多状况redis-cli -p 6382 -h 10.186.62.56 info memory |grep mem_fragmentation_ratiomem_fragmentation_ratio:0.89 ## 碎片率小于1Bigkeys 扫描剖析后面剖析未果,尝试通过 bigkeys 剖析扫描(为防止影响业务操作,倡议业务低峰进行)扫描后果如下(截取要害局部)# redis-cli -p 6382 -h 10.186.62.56 --bigkeys# Scanning the entire keyspace to find biggest keys as well as# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec# per 100 SCAN commands (not usually needed).[00.00%] Biggest string found so far '"key:{06S}:000061157249"' with 3 bytes[00.03%] Biggest string found so far '"key3691"' with 4 bytes[40.93%] Biggest string found so far '"bigkkkkk:0"' with 102400000 bytes[51.33%] Biggest string found so far '"bigk:0"' with 204800000 bytes-------- summary -------Sampled 191433 keys in the keyspace!Total key length in bytes is 4161149 (avg len 21.74)Biggest string found '"bigk:0"' has 204800000 bytes0 lists with 0 items (00.00% of keys, avg size 0.00)0 hashs with 0 fields (00.00% of keys, avg size 0.00)191433 strings with 307777256 bytes (100.00% of keys, avg size 1607.75)0 streams with 0 entries (00.00% of keys, avg size 0.00)0 sets with 0 members (00.00% of keys, avg size 0.00)0 zsets with 0 members (00.00% of keys, avg size 0.00)其中获悉后果如下 ...

August 29, 2022 · 2 min · jiezi

关于redis:Redis-做接口限流

Redis 除了做缓存,还无能很多很多事件:分布式锁、限流、解决申请接口幂等性。。。太多太多了~ 明天想和小伙伴们聊聊用 Redis 解决接口限流,这也是最近的 TienChin 我的项目波及到这个知识点了,我就拎进去和大家聊聊这个话题。 筹备工作首先咱们创立一个 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好啦,筹备工作就算是到位了。 限流注解接下来咱们创立一个限流注解,咱们将限流分为两种状况:针对以后接口的全局性限流,例如该接口能够在 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 中。 ...

August 25, 2022 · 3 min · jiezi

关于redis:Redis是怎么解决缓存占满内存的

Redis最为罕用的是拿来做缓存,而Redis之所以这么快的起因之一是搭上了内存那纳秒级别的处理速度来存储数据,极大晋升了应用服务的性能。(从用户角度翻译过去就是这玩意反馈快了)然而,凡是技术总有它的局限性,例如在计算机中内存空间远比磁盘空间要小得多,而且内存比磁盘贵。所以咱们要是把数据都放内存,显然是一件老本高,性价很低的事件。所以更多的是采取让Redis寄存热数据,从统计上来说,在大部分业务场景中,按二八定律,是20%的数据奉献了的访问量和拜访频率可能靠近或超过80%(当然总有局部例外)。然而,内存空间大小就这么多,随着业务缓存数据量一直增多,不可避免就会将无限的内存空间不小心给占满。那Redis是怎么解决缓存占满内存的?咱们先来看Java,应用Java都晓得,Java是运行在JVM上的,而JVM的一大亮点就是领有不必让C或C++的同学一样去关怀内存回收状况,也就是垃圾回收机制。Redis也有本人的内存回收机制,然而绝对JVM来说,Redis要"简略"一些,因为Redis内存回收机制次要两个方面的策略。Redis内存回收机制策略Redis 删除过期键策略惰性删除:顾名思义,惰性删除并不被动做任何操作,而是当客户端读取到设置了超时的键时,如果曾经超过过期工夫就会删除。然而很显著的问题就是当过期键始终没有拜访到而及时删除,那么就会导致不能让内存及时开释。定时删除:定时删除实际上就是在Redis外部开启了一个定时工作,通过默认每秒定时的运行多少次和按键过期比例以及快慢的速率模式去回收键。除了删除过期键策略当然还远不够,所以就进一步通过算法来筛选数据淘汰的淘汰策略。Redis淘汰策略然而不论后面的删除过期键策略,还是淘汰策略目标自身都是来避免内存溢出的这一点。Redis淘汰策略提供了8种淘汰策略,Redis4.0实现了6种淘汰策略,4.0之后又减少了2种策略,所以Redis有 8 种淘汰策略。能够分成两类:不淘汰数据的策略,仅有 noeviction 一种。会淘汰数据的有7种策略。咱们这里次要关注淘汰数据的7种策略,这7种细看能够再次归成两类:会在所有数据中淘汰的: allkeys-lru、allkeys-random、allkeys-lfu会在设置过期工夫数据中淘汰的:volatile-lru、volatile-random、volatile-ttl、volatile-lfu小伙伴们,我把熬夜整顿的思维导图放在这了。Redis淘汰策略详解默认状况,当Redis的内存超过maxmemory时,noeviction是作为默认策略的,并不会淘汰任何数据。在Redis缓存一旦被占满之后的写申请都不会再解决,会间接的返回谬误。接下来是 allkeys-lru、allkeys-random、allkeys-lfu 四种淘汰策略。它们会在设置过期工夫的数据中进行淘汰,所以它们筛选的数据范畴都在设置了过期键上。当数据过期时,即便缓存没有写满也会被淘汰删除。volatile-ttl:依据键的ttl(生存工夫值),删除设置过期工夫最近的键,先过期的被先删除。volatile-random:random也就是随机,设置了过期的 key 会随机的删除。volatile-lru:在设置过期工夫的 key,应用LRU算法筛选淘汰键。volatile-lfu:在设置过期工夫的 key,应用LFU算法筛选淘汰键。allkeys-lru、allkeys-random、allkeys-lfu 前缀都带着 all 这三种淘汰策略的淘汰数据范畴包含了所有的键值,范畴是所有键值就是无论是否设置过期工夫都会进行淘汰。allkeys-random:在所有键中随机淘汰数据。allkeys-lru:在所有键中应用LRU算法筛选数据。allkeys-lfu:在所有键中应用lfu算法筛选数据。不论是 ttl 还是 random 算法规定是比较简单,而次要 lru 和 lfu 算法也不简单,让咱们一起看看。LRU(Least Recently Used)LRU (Least Recently Used) 是最近起码应用准则,是将最近最不罕用应用的数据进行筛选,最近不常应用的淘汰,最近常应用的数据留在缓存。具体怎么筛选能够看上面的例子,假如在一块无限的空间里,最近拜访会被移到顶端,最近没拜访到的会移到末端,也就是LRU端。当空间被占满时,此时刚好有新增的数据时,就会把LRU端的开端 key 替换淘汰掉。你看LRU算法是不是很有用户体验 “如果有数据最近被拜访过,那么再被拜访的几率也会很高”那如果要去实现LRU算法,天然就须要有撑持它的数据结构,此时就能够应用链表,用链表来寄存所有缓存数据。不过只应用链表,会有问题,那就是当面临数据量大的状况,链表的挪动也会显得蠢笨而带来耗时,进而影响Redis性能。Redis天然不会放过这个能够优化的机会,所以Redis在LRU算法上动手脚。所以在一开始就记录每个数据最近一次被拜访的工夫戳。之后当Redis筹备淘汰数据时,首先第一次随机的选出N个数据,而后将其作为候选汇合,最初比拟这N个数据携带的lru字段,最小的会从缓存中被淘汰。Redis提供 maxmemory-samples 的配置参数,让Redis选出数据作为候选数据集。当面临淘汰数据,Redis须要筛选数据,那么就会进到首次创立的淘汰候选汇合。筛选规范是:进入候选汇合的数据lru属性值必须小于候选汇合中最小的lru值。当有新数据进入候选数据集后,如果候选数据集中的数据个数达到了maxmemory-samples,Redis就把候选数据集中lru字段值最小的数据淘汰进来。你看这样一来Redis缓存就能够不必为一直增多的数据保护一个也一直增大的大链表,省去每次数据拜访都挪动链表的开销,缓存的性能就能失去晋升。LFU(Least Frequently Used)LFU (Least Frequently Used)是最近起码频率应用,楞一看LFU缓存策略跟 LRU很类似,类似就对了,因为LFU就是在LRU的策略根底上优化进去的缓存策略。LFU不同与LRU的是LFU把LRU原来的24bit的lru字段拆分成Idt值和 counter 值两局部。其中Idt 是lru字段的前16bit 示意拜访工夫戳。counter 值是lru字段的后8bit 也就是示意拜访次数。LFU算法用的是拜访频次递增和拜访频次衰减两种形式。拜访频次递增是通过counter来递增,然而它所能示意的最大值只有255,所以采纳了更优的计数形式。每当数据被拜访时,计数器值乘以lfu_log_factor再加1,取其倒数,失去p值;之后p值和取值范畴 0 和 1 之间的随机数 r 比大小,当 p 值大于r值,计数器才加 1.1/(baseval * lfu_log_factor + 1)Redis官网 提供的一张表,当lfu_log_factor取不同值,不同拜访次数,计数器值的变动状况。从表中能够看到,lfu_log_factor 取值为1,拜访次数100k时, counter值就到顶255,没法辨别拜访次数。当lfu_log_factor取值为100时,拜访次数10M,counter值达到255,此时,拜访次数小于10M的不同数据都能够通过counter值辨别进去。拜访频次衰减Redis实现LFU策略时,除了拜访频次递增,还设计了一个衰减机制。因为从上可知,counter 始终递增会达到顶 255,而且纯正的递增不能反馈一个 key 的热度,所以 key 如果一段时间不被拜访,counter也须要对应缩小。递加的速度由 lfu-decay-time 配置项管制 counter 的递加速度,默认值 1示意如果N分钟没有拜访,那么 counter 减 N。总结咱们围绕 Redis是怎么解决缓存占满内存开展了Redis的内存回收策略,Redis的内存回收策略有两个方面,删除过期键策略和淘汰策略,然而不论是删除过期键策略还是淘汰策略目标都是来管制避免内存溢出。在淘汰策略中,Redis4.0实现了6种淘汰策略,4.0之后又减少了2种策略,所以Redis一共有8 种淘汰策略。其中最为次要的LRU和LFU算法策略。LFU是在LRU的根底上的策略,然而LFU并不是用来替换LRU;它们各自的数据筛选侧重点不同,前者LRU策略偏重数据时效性,而后者LFU偏重拜访频次。敌人们,到这就靠近序幕了。感兴趣的敌人能够在3A服务器上部署环境尝试一下。

August 24, 2022 · 1 min · jiezi

关于redis:RedisSyncer同步引擎的设计与实现

作者: 贾世闻 展恩强 RedisSyncer一款通过replication协定模仿slave来获取源Redis节点数据并写入指标Redis从而实现数据同步的Redis同步中间件。 该我的项目次要包含以下子项目: redis 同步服务引擎 redissyncer-serverredissycner 客户端 redissyncer-cliredis 数据校验工具 redissycner-compare基于docker-compse的一体化部署计划 redissyncer本文次要介绍reidssyncer引擎(既redissyncer-server)的设计与实现,以及引擎运行的机制。 同步流程原生redis master slave 模式次要分为两个阶段,第一个阶段同步rdb镜像,也就是全量同步局部;全量同步实现后进入命令流传模式,每个执行胜利的数据变更操作会同步给slave节点。redissyncer 的模仿了这一机制并将两局部拆解,既能够执行残缺同步工作也能够独自执行全量或增量同步。 建设socket发送auth user password (6.0新增user) OK 胜利 其余 errorsend->ping 返回:ERR invalid password 明码谬误NOAUTH Authentication required.没有发送明码operation not permitted 操作没权限PONG 明码胜利作用:检测主从节点之间的网络是否可用。查看主从节点以后是否承受解决命令。发送从节点端口信息 REPLCONF listening-port <port> -->OK 胜利 -->其余 失败发送从节点IP REPLCONF ip-address <IP> --> OK 胜利 --> 其余 失败发送EOF能力(capability) REPLCONF capa eof--> OK 胜利--> 失败作用:是否反对EOF格调的RDB传输,用于无盘复制,就是可能解析出RDB文件的EOF流格局。用于无盘复制的形式中。redis4.0反对两种能力 EOF 和 PSYNC2redis4.0之前版本仅反对EOF能力发送PSYNC2能力 REPLCONF capa PSYNC2--> OK 胜利 --> 失败作用: 通知master反对PSYNC2命令 , master 会疏忽它不反对的能力. PSYNC2则示意反对Redis4.0最新的PSYN复制操作。发送PSYNC ...

August 23, 2022 · 4 min · jiezi

关于redis:redis-相关问题

底层数据结构如何保障和数据库数据一致性缓存和数据库一致性问题,看这篇就够了如何实现分布式锁长久化形式《Redis长久化形式》一致性hash怎么实现的https://juejin.cn/post/685041...一致性hashredis部署形式 集群 哨兵Redis(二)冰叔带你理解Redis-哨兵模式和高可用集群解析redis哨兵模式选举机制redis过期怎么实现的Redis 的过期策略是如何实现的?如何解决Redis缓存雪崩,缓存穿透,缓存击穿缓存穿透,缓存击穿,缓存雪崩解决方案剖析缓存穿透和缓存击穿解决

August 19, 2022 · 1 min · jiezi

关于redis:深入理解Redis-数据结构字典

字典,又称为符号表、关联数组或映射,是一种用于保留键值对的形象数据结构。在字典中,一个键能够和一个值进行关联,这些关联的键和值称为键值对。键值对中键是惟一的,咱们能够依据键key通过映射查找或者更新对应的值value。很多高级开发语言有对应汇合反对字典这种数据结构,比方Java中的Map汇合。C语言并未内置字典这种数据结构,Redis构建了本人的字典实现。 利用字典在Redis中利用十分宽泛,Redis数据库就是应用字典作为数据底层的实现。对数据的增、删、改、查操作也是建设在字典之上操作。 当执行命令: set msg "hello"在数据库中创立一个键为 msg,值为 hello 的键值对,这个键值对就用字典来实现的。创立其余数据类型的键值对,比方list、hash、set、sort set也是用字典来实现的。 解决用来示意数据中的键值对,字典还是hash数据类型底层实现之一,比方一个hash数据类型website,蕴含100个键值对,这些键值对中的键是网址名称,值是网页地址: redis> HGETALL website1)"Redis"2)"Redis.io"3)"nacos"4)"nacos.io".....website键的底层就是一个字典,包好了100个键值对,例如: 键值对中的键为"Redis",值为"Redis.io"。键值对中的键为"nacos",值为"nacos.io"。字典的实现Redis字典应用哈希表作为底层实现,一个哈希表外面有多个哈希表节点,每个哈希表节点保留字典中的键值对。 哈希表Redis字典应用的哈希表由 dict.h/dictht 构造来示意: /* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */typedef struct dictht { // table 数组 dictEntry **table; // 哈希表的大小 unsigned long size; // 等于size-1,用于计算索引值 unsigned long sizemask; // 已有的键值对数量 unsigned long used;} dictht;正文:这是哈希表构造,每个字典有两个实现增量重散列,从旧的哈希表到新的哈希表。 ...

August 19, 2022 · 2 min · jiezi

关于redis:Redis设计与实现之简单动态字符串

简略动静字符串什么是 SDSSDS,即 Simple Dynamic String,简略动静字符串。 Redis 没有间接应用 C 语言传统的字符串示意(以空字符结尾的字符数组),而是本人构建了一种名为简略动静字符串(simple dynamic string,SDS)的形象类型,并将 SDS 用作 Redis 的默认字符串示意。 在 Redis 外面,C 字符串只会作为字符串字面量(string literal)用在一些毋庸对字符串值进行批改的中央,比方打印日志。当 Redis 须要的不仅仅是一个字符串字面量,而是一个能够被批改的字符串值时,Redis 就会应用 SDS 来示意字符串值,比方在 Redis 的数据库外面,蕴含字符串值的键值对在底层都是由 SDS 实现的。 SDS 的定义在 SDS 构造中蕴含: buf:字节数组,用于保留字符串len:记录 buf 数组中已应用的字节数,等于 SDS 保留的字符串长度free:记录 buf 数组中未应用字节的数量举例: len 属性的值等于 5,示意在 SDS 中保留了一个五字节长度的字符串free 属性的值等于 0,示意在 SDS 中没有调配任何未应用的空间buf 属性是一个 char 数组,前五位保留了五个字符,在最初一个字节保留了一个空字符'\0'须要留神的是:SDS 遵循 C 字符串以空字符结尾的常规,保留空字符的 1 字节空间并不计算在 SDS 的 len 属性中,并且调配这额定的 1 字节空间,以及增加空字符到字符串开端等操作,都是由 SDS 函数主动实现的。 方才展现了 free 为 0 的状况,而存在未应用空间的时候则如下图,咱们还是应用“Redis”字符串。 ...

August 16, 2022 · 2 min · jiezi

关于redis:Redis桌面管理工具Redis-Desktop-Manager-2021中文版

Redis Desktop Manager for Mac是一款十分实用的Redis可视化工具。RDM反对SSL / TLS加密,SSH隧道,基于SSH隧道的TLS,为您提供了一个易于应用的GUI,能够拜访您的Redis数据库并执行一些基本操作:将键视为树,CRUD键,通过shell执行命令。 Redis桌面管理工具——Redis Desktop Manager 2021中文版 随时随地Redis Desktop Manager是实用于Mac OS X,Windows和Linux的GUI应用程序。 实用于BIG数据库你在redis中有超过一百万个键吗?这不是一个问题。 SSH隧道反对间接或通过SSH隧道连贯到Redis 云筹备好了!Redis Desktop Manager可与Amazon ElastiCache,Microsoft Azure Redis Cache和Redis Labs配合应用

August 15, 2022 · 1 min · jiezi

关于redis:Redis为什么这么快

首先Redis是一个应用ANSI C编写的、开源的、反对网络的、基于内存的、可选长久化的键值对存储系统。 1 Redis的发家史2009年由 Salvatore Sanfilippo(Redis之父)公布初始版本2013年5月之前,由VMare资助2013年5月-2015年6月,由Pivotal资助2015年6月起,由Redis Labs资助依据db-engines.com上的排名,到目前为止Redis仍然是最风行的键值对存储系统。 2 Redis次要版本2009年5月公布Redis初始版本2012年公布Redis 2.6.02013年11月公布Redis 2.8.02015年4月公布Redis 3.0.0,在该版本Redis引入集群2017年7月公布Redis 4.0.0,在该版本Redis引入了模块零碎2018年10月公布Redis 5.0.0,在该版本引入了Streams构造2020年5月2日公布 6.0.1(稳定版),在该版本中引入多线程、RESP3协定、无盘复制正本2022年1月31日公布 7.0 RC1,在该版本中次要是对性能和内存进行优化,新的AOF模式 3 Redis 有多快? Redis中带有一个能够进行性能测试的工具 redis-benchmark,通过这个命令,咱们就能够模仿多个客户端同时发动申请的场景,并且能够检测Redis解决这些申请所须要的工夫。 依据官网的文档,Redis 曾经在超过 60000 个连贯上进行了基准测试,并且在这些条件下依然可能维持 50000 q/s。同样的申请量如果打到MySQL上,那必定扛不住,间接就崩掉了。 With high-end configurations, the number of client connections is also an important factor. Being based on epoll/kqueue, the Redis event loop is quite scalable. Redis has already been benchmarked at more than 60000 connections, and was still able to sustain 50000 q/s in these conditions. As a rule of thumb, an instance with 30000 connections can only process half the throughput achievable with 100 connections. Here is an example showing the throughput of a Redis instance per number of connections; ...

August 11, 2022 · 2 min · jiezi

关于redis:Redis-6种淘汰机制看看你知道哪些

redis是个基于内存的缓存数据库,既然是基于内存的,那必定就会有存满的时候 如果真的存满了,再有新的数据过去必定就存不进去了 此时redis会执行既定的一些淘汰策略,本文大略讲一下redis六种淘汰策略 一、六种淘汰策略 1.noeviction(默认策略):对于写申请不再提供服务,间接返回谬误(DEL申请和局部非凡申请除外) 2.allkeys-lru:从所有key中应用LRU算法进行淘汰(LRU算法:即最近起码应用算法) 3.volatile-lru:从设置了过期工夫的key中应用LRU算法进行淘汰 4.allkeys-random:从所有key中随机淘汰数据 5.volatile-random:从设置了过期工夫的key中随机淘汰 6.volatile-ttl:在设置了过期工夫的key中,淘汰过期工夫残余最短的 当应用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key能够被淘汰,则和noeviction一样返回谬误 二、如何获取及设置内存淘汰策略 1、获取以后内存淘汰策略: 127.0.0.1:6379> config get maxmemory-policy能够看到以后应用的默认的noeviction策略 2、获取Redis能应用的最大内存大小 127.0.0.1:6379> config get maxmemory如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限度内存大小,在32位操作系统下最多应用3GB内存。32 位的机器最大只反对 4GB 的内存,而零碎自身就须要肯定的内存资源来反对运行,所以 32 位机器限度最大 3 GB 的可用内存 3、设置淘汰策略 通过配置文件设置淘汰策略(批改redis.conf文件): maxmemory-policy allkeys-lru通过命令批改淘汰策略: 127.0.0.1:6379> config set maxmemory-policy allkeys-lru4、设置Redis最大占用内存大小 #设置Redis最大占用内存大小为100M127.0.0.1:6379> config set maxmemory 100mb起源:blog.csdn.net/ju_362204801/article/details/114441137

August 10, 2022 · 1 min · jiezi

关于redis:被大厂面试官参考的Redis笔记堪称Redis面试天花板

前言在目前的技术选型中,Redis 俨然曾经成为了零碎高性能缓存计划的事实标准,因而当初 Redis 也成为了后端开发的基本技能树之一。 基于上述情况,明天给大家分享一份我亲笔撰写的阿里外部《 Redis 笔记》,这2份Redis外围笔记及面试高频解析,共888页,对 Redis 的相干常识做了零碎全面的介绍,还是PDF版本,可自在复制,特地适宜 Redis 初学者疾速入门和进步。 本笔记适宜人群:前半部分适宜 Redis 初学者疾速入门和进步;后半局部适宜对于 Redis 有肯定理解的开发者,深刻开掘其原理。因为篇幅起因,本文只展现了目录和内容截图,有须要学习的小伙伴只需转发后【点击此处】即可来获取支付形式了! 第一份笔记:Redis外围笔记目录 一、缓存概念二、Redis部署与应用三、Redis高可用与集群四、Memcached 1.缓存概念 缓存是为了调节速度不统一的两个或多个不同的物质的速度,在两头对速度较快的一方起到一个减速访问速度较慢的一方的作用,比方 CPU 的一级、二级缓存是保留了 CPU 最近常常拜访的数据,内存是保留 CPU 常常拜访硬盘的数据,而且硬盘也有大小不一的缓存,甚至是物理服务器的 raid 卡有也缓存,都是为了起到减速 CPU 拜访硬盘数据的目标,因为 CPU 的速度太快了,CPU 须要的数据因为硬盘往往不能在短时间内满足 CPU 的需要,因而 PCU 缓存、内存、Raid 卡缓存以及硬盘缓存就在肯定水平上满足了 CPU 的数据需要,即 CPU 从缓存读取数据能够大幅提高 CPU 的工作效率。 零碎缓存用户层缓存CDN 缓存应用层缓存其余层面缓存 2.Redis 部署与利用 Redis和Memcached 是非关系型数据库也称为 NoSQL 数据库,MySQL、Mariadb、SQL Server、PostgreSQL、Oracle 数据库属于关系型数据(RDBMS, Relational Database Management System) Redis 根底Redis 装置及应用Redis 配置文件Redis 数据类型Redis 音讯队列Redis 常用命令 3.Redis 高可用与集群 尽管 Redis 能够实现单机的数据长久化,但无论是 RDB 也好或者 AOF 也好,都解决不了单点宕机问题,即一旦单台 Redis 服务器自身呈现系统故障、硬件故障等问题后,就会间接造成数据的失落,因而须要应用另外的技术来解决单点问题。 ...

August 10, 2022 · 1 min · jiezi

关于redis:Redis-50-部分源码剖析

从前有句古话说得好,天将降大任于斯人也,必要先看Redis。以前今人还说过,窗前明月光,抬头Redis。今人还说过,所有的答案都在源码里。昨天还有人跟我说,用Redis比Tair申请要不便。不识庐山真面目,只缘身在此山中咱们先给出一副大图,来看看Redis AOF Rewrite的总体流程是怎么样的。先看看大图里的几大组成部分 主过程与子过程,大家都晓得,Redis AOF Rewrite是通过创立一个子过程来实现的。父子过程有一个重要个性,那就是"读时共享,写时复制"。前面咱们具体聊父子过程间通信应用的三个通道。客户端写入Redis主过程时,波及到的两个数据结构,aof_buf,aof_rewrite_buf_blocks;子过程波及到的aof_child_diff数据结构。一份以后应用的AOF文件,这份文件是筹备退休的"现役"文件,另一份是子过程正在重写的"预备役"文件。大抵波及的内容就是如此了,接下来依照一个AOF Rewrite执行的工夫程序来看看到底产生了什么事万般皆由长风起先来看看,Redis AOF Rewrite机制是怎么触发的呢?有两种形式大家应该都很分明了,一种是配置文件中配置的若干工夫内,产生了若干键值对的变动,达到阈值就须要触发一次重写。(这里补充一句,这个查看是在Redis后盾主线程中调度时查看的,这个工夫并不会是很确定的)所谓的serverCron就是这个办法,何时触发,如何触发,咱们回头细说另一种是客户端,要求执行bgrewriteaof。这里写的第三种,是在开启AOF时,才会进行一次。个别是在启动时就实现了AOF的启动。但这里有一种非凡状况,就是在Redis有主从时。从服务在跟主服务同步数据时,主服务会生成一个RDB文件给从,从应用假客户端读取数据复原至内存。这个阶段,从服务是须要进行AOF(如果原来开启的话)的。等到实现了主从同步的数据恢复后,主动开启AOF,这里就会执行一次AOF的重写。 山一程,水一程,fork子过程不论是什么起因,当确定要进行AOF Rewrite之后,首先做的就是进行一系列查看,而后fork一个子过程。有红线的中央就是fork子过程的中央。if语句中的是子过程须要执行的代码,else中是主过程的。先不焦急钻研奴才过程别离做了什么事,看下后面的校验。首先,如果有AOF重写子过程或RDB重写过程存在的话,就不进行本次的重写。其次,如果创立奴才过程的管道失败的话,也不进行重写。创立子过程胜利之后,子过程就会立刻开始AOF文件的重写,而主过程则是持续提供对外服务,只是为了确保重写期间AOF不失落,会多做几步操作。这里创立的管道非常重要,一共有三个: 提醒:因为fork子过程会让奴才过程共享内存,那么子过程肯定是要晓得主过程原有的数据存储在哪里的。这里就波及到将原有主过程的页表复制一份过去的操作。这个操作是阻塞的,会导致fork操作卡住。因而在流量大的时候,要留神AOF Rewrite将Redis卡住,而使RT增大。话分中间子过程咱们看看子过程都做了啥: 创立一个临时文件,名字是temp-rewriteaof-{pid}的文件,而后初始化一下文件句柄之类的援用。判断是否是RDB混合模式,还是纯AOF模式,进行重写。这两者的区别,这里就不赘述了。真正的重写其实很简略,就是挨个的读取db的内容,而后以对应的格局,写入文件中。因为奴才过程之间有"读时共享,写时复制"的限度,也就是如果是读取时两者专用一份内容,当有人要写的数据时,会将原有数据copy进去一份,在新的copy的数据上批改,旧放弃不变。Redis就利用了这一性能,保障了读到的数据是fork之前的最初版本的数据。到这里为止,其实是AOF Rewrite的外围逻辑,其余的逻辑都是围绕在AOF Rewrite期间有数据发生变化来做的。整个重写是十分消耗CPU的,趁着子过程加班加点干活时,咱们来看一眼主过程在做什么。 主过程主过程在fork完子过程,把ORK交代给子过程之后,就对子过程不论不问了。偶然查看一下子过程有没有把工作实现(通信管道有没有新数据/子过程有没有隐没)。假如在AOF重写过程中,有客户端发来了一个set a 1的申请,会将原来的a的值由0改为1。因为有奴才过程"读时共享,写时复制"的存在,不必放心子过程,它会读到老的数据。在实现内存数据变动后,会走到上面这个办法中。咱们认真来看看。这个办法在aof.c文件中,所有写aof的操作都走这个办法。它做了一下几件事: Redis是有多个db的,如果命令操作的db不是以后的db,那么就会插入一条select db的命令。依据ditcid参数来确定将带有过期工夫的操作,转换为PEXPIREAT(EXPIRE/PEXPIRE/EXPIREAT/SETEX/PSETEX/SET EX seconds)将操作的命令,转换为RESP格局写入AOF相干缓存这里与AOF Rewrite相干的,是步骤4,咱们重点来看下这个步骤做了什么:首先,判断是否开启了AOF,那显然咱们是开启了的啊,就须要将这条语句,写入旧的AOF文件中。这个很正当啊,万一重写失败了呢,数据不能够丢啊。其次,如果有aof子过程pid存在,那么还要多做一步aofRewriteBufferAppend(),这个是做什么的呢?它是将刚刚生成的语句,再次保留在一个aof_rewrite_buf_blocks的构造当中。这个aof_rewrite_buf_blocks是一个list构造,它保留的都是10M大小一个的block。block中存储的就是刚刚生成的语句。而后办法返回。本次aof写入操作完结。aof_rewrite_buf_blocks的数据,会期待创立的管道1是否容许写入(写入机会由别的机制保障,这里略过),如果容许写入,就将数据写入这个管道中,而后将内存中写入局部的数据开释。 提醒:这里须要留神,aof_buf与aof_rewrite_buf_blocks是两个数据结构,外面的数据也是两份,不是专用一份。因而重写阶段,数据变更会让主过程将这些数据在内存中存储两份,这对主过程是额定的压力。子过程拿到第一个KR了咱们把眼光再次回到子过程身上。此时它曾经实现对原有数据的重写,拿到第一个KR,咱们祝贺一下它~当初咱们晓得了Rewrite期间变动的数据,是会通过管道告诉的。那么子过程是如何解决的么? 点点滴滴,聚水成河其实在“子过程”章节中重写旧数据时,就开始解决了。子过程在重写旧数据时,会时不时的读取一下管道。rdbSaveRio办法中rewriteAppendOnlyFileRio办法中这些读取进去的数据都会保留在aof_child_diff的数据结构中。这样旧数据会间接写入到aof重写文件中,期间变动的数据会保留在内存aof_child_diff中,数据程序就不会凌乱了。最初把这部分变动的数据对立写入待aof重写文件中即可。 最重要的是向上治理子过程在实现第一个KR,重写了旧数据之后,就快马加鞭的发展了下一项工作。从管道1中读取变动的数据。这里有两个点须要留神,一个是主过程可能始终在承受新的数据,这就导致通道1中永远有数据,子过程就无限度的读取数据,这必定是不能承受到,因而,限度了最多只会读取1s。另一个,如果始终没有数据,也没必要始终等啊,大家工夫都很贵重的,如果20毫秒没有数据,那么子过程也就不会读取了。上面就到了要害一步,要告诉主过程本人的工作曾经实现了80%,进行向上治理了。子过程向通道2写入一个!号,告诉主过程,请进行向管道1当中写入数据 主过程承受会邀主过程在承受到通道2的告诉之后,将aof_stop_sending_diff设置为1,变动的数据仍旧进入aof_rewrite_buf_blocks中,然而不会在写入通道1中了,全副停留在本人的内存中。主过程接着向通道3中写入一个!,表明本人进行写入数据。 临门一脚子过程收到告诉后,最初将通道1中的数据全副读取进去,而后刷入磁盘,并将aof重写文件重写命名为temp-rewriteaof-bg-{pid}.aof,而后本人退出过程。这里的写入,就是指将内存中的aof_child_buf中的数据一股脑的写入文件当中,而后随着过程退出,内存一并开释。 主过程:我来兜底!此时子过程曾经实现了它的工作,退出了过程,主过程在serverCron中发现子过程曾经不存在了之后,就调用backgroundRewriteDoneHandler办法,解决善后的工作。将之前aof_rewrite_buf_blocks还存在的数据,写入aof重写文件之中。(temp-rewriteaof-bg-{pid}.aof)将aof文件设置为重写文件,重写aof正式转正,旧文件退休。 道由白云尽,春与青溪长到此为止,一次残缺的AOF Rewrite重写就完结了。纵观整个过程,咱们能够看到,外围的重点是如何解决AOF Rewrite期间变动的数据。为了保障这部分的数据正确,Redis 5.0 版本总共应用了两个内存构造存储(主过程的aof_rewrite_buf_blocks,子过程的aof_child_diff),两个磁盘IO(主过程写入旧AOF文件,子过程写入新AOF重写文件),三个通信管道,双倍的CPU开销来实现。有趣味的同学能够理解下还未正式公布的Redis 7.0,Multi Part AOF的实现。这个版本的实现完满解决了上述的冗余问题。好的,明天的分享就到此为止啦,感激大家。

August 8, 2022 · 1 min · jiezi

关于redis:Redis-定长队列的探索和实践

vivo 互联网服务器团队 - Wang Zhi一、业务背景从技术的角度来说,技术计划的选型都是受限于理论的业务场景,都以解决理论业务场景为指标。 在咱们的理论业务场景中,须要以游戏的维度收集和上报行为数据,思考数据的量级,执行尽最大致力交付且容许数据的局部抛弃。 数据上报反对游戏的维度的批量上报,反对同一款游戏128个行为进行批量上报。 数据上报须要时效管制,上报的数据必须是上报时刻的前3分钟的数据。 整体数据的业务状态如下图所示: 二、技术选型从业务的角度来说蕴含数据的收集和数据的上报,咱们把数据的收集比作生产者,数据的上报比作消费者,是一个典型的生产生产模型。 生产生产模型在JVM过程外部通过队列+锁或者无锁的Disruptor来实现,在跨过程场景下通过MQ(RocketMQ/kafka)进行处了解耦。 然而细化到具体业务场景来看,音讯的生产有诸多限度,包含:游戏维度的批量行为上报,行为上报的时效限度,细化到各个技术计划选型进行比照。 计划一 应用RocketMQ 或者Kafaka等音讯队列来存储上报的音讯,然而生产侧须要思考在业务过程中依照游戏维度进行聚合,其中技术细节波及依照游戏维度进行拆分,在满足音讯时效性和批量性的前提下触发上报。在这种计划下消息中间件表演的角色实质上音讯的中转站,没有解决任何业务场景中提及的游戏维度拆分、批量性和时效性。计划二 在计划一的根底上,寻求一种技术计划来解决游戏维度的音讯分组、批量生产 、时效性。通过Redis的list构造来实现队列(进一步要求实现定长队列)来解决游戏维度的音讯分组;通过Redis的list反对的Lrange来实现批量生产;通过业务侧的多线程来解决时效问题,针对高频的游戏应用独自的线程池进行解决,上述两个伎俩可能保障生产速度大于生产速度。计划比照 比照两种计划后决定应用Redis的实现了一个伪消息中间件: 通过List对象实现定长队列来保留游戏维度的行为音讯(以游戏作为key的List对象来保留用户行为);通过List来保留所有的存在行为数据的游戏列表;通过Set来进行去重判断来保障2中的List对象的唯一性。整体的技术计划如下图所示: 生产过程 步骤一:游戏维度的某行为数据PUSH到游戏维度的队列当中。 步骤二:判断游戏是否在游戏的汇合Set中,如果在就间接返回,如果不在进行步骤三。 步骤三:往游戏列表中PUSH游戏。 生产过程 步骤一:从游戏对象的列表中循环取出一款游戏。 步骤二:通过步骤一获取的游戏对象去该游戏对象的行为数据队列中批量获取数据处理。 三、技术原理在Redis的反对命令中,在List和Set的根底命令,联合Lua脚本来实现整个技术计划。 音讯数据层面,通过独自的List循环保护待生产的游戏维度的数据,每个游戏维度应用定长的List来保留音讯。 音讯生产过程中,通过联合List的llen+lpop+rpush来实现游戏维度的定长队列,保障队列的长度可控。 音讯生产过程中,通过联合List的lrange+ltrim来实现游戏维度的音讯的批量生产。 在整个执行的复杂度层面,须要保障工夫复杂度在0(N)常量维度,保障工夫可控。 3.1 Lua 脚本EVAL script numkeys key [key ...] arg [arg ...] 工夫复杂度:取决于脚本自身的执行的工夫复杂度。 > eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second1) "key1"2) "key2"3) "first"4) "second" Redis uses the same Lua interpreter to run all the commands.Also Redis guarantees that a script is executed in an atomic way:no other script or Redis command will be executed while a script is being executed.This semantic is similar to the one of MULTI / EXEC.From the point of view of all the other clients the effects of a script are either still not visible or already completed.Redis采纳雷同的Lua解释器去运行所有命令,咱们能够保障,脚本的执行是原子性的。作用就相似于加了MULTI/EXEC。 ...

August 8, 2022 · 2 min · jiezi

关于redis:详细剖析-Redis-三种集群策略

redisredis是单线程的,然而个别的作为缓存应用的话,redis足够了,因为它的读写速度太快了。官网的一个简略测试:测试实现了50个并发执行100000个申请。设置和获取的值是一个256字节字符串。后果:读的速度是110000次/s,写的速度是81000次/s但对于访问量特地大的服务来说,还是稍有有余。那么,如何晋升redis的性能呢?搭建集群。 redis次要提供三种集群策略:主从复制集群哨兵一、主从复制在主从复制中,数据库分为俩类,主数据库(master)和从数据库(slave)。 1.1 主从复制有如下特点:主数据库能够进行读写操作,当读写操作导致数据变动时会主动将数据同步给从数据库从数据库个别都是只读的,并且接管主数据库同步过去的数据一个master能够领有多个slave,然而一个slave只能对应一个master1.2 工作机制slave从节点服务启动并连贯到Master之后,它将被动发送一个SYNC命令。Master服务主节点收到同步命令后将启动后盾存盘过程,同时收集所有接管到的用于批改数据集的命令,在后盾过程执行结束后,Master将传送整个数据库文件到Slave,以实现一次齐全同步。而Slave从节点服务在接管到数据库文件数据之后将其存盘并加载到内存中。尔后,Master主节点持续将所有曾经收集到的批改命令,和新的批改命令顺次传送给Slaves,Slave将在本次执行这些数据批改命令,从而达到最终的数据同步。复制初始化后,master每次接管到的写命令都会同步发送给slave,保障主从数据一致性。如果Master和Slave之间的链接呈现断连景象,Slave能够主动重连Master,然而在连贯胜利之后,一次齐全同步将被主动执行。1.3 主从配置redis默认是主数据,所以master无需配置,咱们只须要批改slave的配置即可。设置须要连贯的master的ip端口:slaveof 192.168.0.107 6379如果master设置了明码。须要配置:masterauth master-password连贯胜利进入命令行后,能够通过以下命令行查看连贯该数据库的其余库信息:info replication 1.3 长处同一个Master能够同步多个Slaves。Slave同样能够承受其它Slaves的连贯和同步申请,这样能够无效的分载Master的同步压力。因而咱们能够将Redis的Replication架构视为图构造。Master Server是以非阻塞的形式为Slaves提供服务。所以在Master-Slave同步期间,客户端依然能够提交查问或批改申请。Slave Server同样是以非阻塞的形式实现数据同步。在同步期间,如果有客户端提交查问申请,Redis则返回同步之前的数据为了分载Master的读操作压力,Slave服务器能够为客户端提供只读操作的服务,写服务依然必须由Master来实现。即便如此,零碎的伸缩性还是失去了很大的进步。Master能够将数据保留操作交给Slaves实现,从而防止了在Master中要有独立的过程来实现此操作。反对主从复制,主机会主动将数据同步到从机,能够进行读写拆散。1.4 毛病Redis不具备主动容错和复原性能,主机从机的宕机都会导致前端局部读写申请失败,须要期待机器重启或者手动切换前端的IP能力复原。主机宕机,宕机前有局部数据未能及时同步到从机,切换IP后还会引入数据不统一的问题,升高了零碎的可用性。Redis的主从复制采纳全量复制,复制过程中主机会fork出一个子过程对内存做一份快照,并将子过程的内存快照保留为文件发送给从机,这一过程须要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新退出集群或者从机和主机网络断开重连时都会进行,也就是网络稳定都会造成主机和从机间的一次全量的数据复制,这对理论的零碎经营造成了不小的麻烦。Redis较难反对在线扩容,在集群容量达到下限时在线扩容会变得很简单。为防止这一问题,运维人员在零碎上线时必须确保有足够的空间,这对资源造成了很大的节约。其实redis的主从模式很简略,在理论的生产环境中是很少应用的,我也不倡议在理论的生产环境中应用主从模式来提供零碎的高可用性,之所以不倡议应用都是由它的毛病造成的,在数据量十分大的状况,或者对系统的高可用性要求很高的状况下,主从模式也是不稳固的。二、哨兵该模式是从Redis的2.6版本开始提供的,然而过后这个版本的模式是不稳固的,直到Redis的2.8版本当前,这个哨兵模式才稳定下来,无论是主从模式,还是哨兵模式,这两个模式都有一个问题,不能程度扩容,并且这两个模式的高可用个性都会受到Master主节点内存的限度。 2.1 哨兵的作用是监控 redis零碎的运行状况,性能如下监控主从数据库是否失常运行。master呈现故障时,主动将它的其中一个slave转化为master。master和slave服务器切换后,master的redis.conf、slave的redis.conf和sentinel.conf的配置文件的内容都会产生相应的扭转,即,saster主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控指标会随之调换。当被监控的某个Redis节点呈现问题时, 哨兵(sentinel) 能够通过 API 向管理员或者其余应用程序发送告诉。多哨兵配置的时候,哨兵之间也会主动监控。多个哨兵能够监控同一个redis。2.2 哨兵工作机制哨兵过程启动时会读取配置文件的内容,通过sentinel monitor master-name ip port quorum查找到master的ip端口。一个哨兵能够监控多个master数据库,只须要提供多个该配置项即可。配置文件还定义了与监控相干的参数,比方master多长时间无响应即即断定位为下线。哨兵启动后,会与要监控的master建设俩条连贯:3.1 一条连贯用来订阅master的sentinel:hello频道,并获取其余监控该master的哨兵节点信息3.2 另一条连贯定期向master发送INFO等命令获取master自身的信息与master建设连贯后,哨兵会执行三个操作,这三个操作的发送频率都能够在配置文件中配置:4.1 定期向master和slave发送INFO命令4.2 定期向master和slave的sentinel:hello频道发送本人的信息4.3 定期向master、slave和其余哨兵发送PING命令这三个操作的意义十分重大,发送INFO命令能够获取以后数据库的相干信息从而实现新节点的主动发现。所以说哨兵只须要配置master数据库信息就能够主动发现其slave信息。获取到slave信息后,哨兵也会与slave建设俩条连贯执行监控。通过INFO命令,哨兵能够获取主从数据库的最新信息,并进行相应的操作,比方角色变更等。 接下来哨兵向主从数据库的sentinel:hello频道发送信息,并与同样监控这些数据库的哨兵共享本人的信息,发送内容为哨兵的ip端口、运行id、配置版本、master名字、master的ip端口还有master的配置版本。这些信息有以下用途:5.1 其余哨兵能够通过该信息判断发送者是否是新发现的哨兵,如果是的话会创立一个到该哨兵的连贯用于发送ping命令。5.2 其余哨兵通过该信息能够判断master的版本,如果该版本高于间接记录的版本,将会更新当实现了主动发现slave和其余哨兵节点后,哨兵就能够通过定期发送ping命令定时监控这些数据库和节点有没有进行服务。发送频率能够配置,然而最长间隔时间为1s,能够通过sentinel down-after-milliseconds mymaster 600设置。如果被ping的数据库或者节点超时未回复,哨兵认为其主观下线。如果下线的是master,哨兵会向其余哨兵点发送命令询问他们是否也认为该master主观下线。如果一个master主服务器被标记为主观下线(SDOWN),则正在监督这个Master主服务器的所有 Sentinel(哨兵)过程要以每秒一次的频率确认Master主服务器确实进入了主观下线状态。如果达到肯定数目(即配置文件中的quorum)投票,哨兵会认为该master曾经主观下线(ODOWN),并选举领头的哨兵节点对主从零碎发动故障复原。如上文所说,哨兵认为master主观下线后,故障复原的操作须要由选举的领头哨兵执行,选举采纳Raft算法:8.1 发现master下线的哨兵节点(咱们称他为A)向每个哨兵发送命令,要求对方选本人为领头哨兵8.2 如果指标哨兵节点没有选过其他人,则会批准选举A为领头哨兵8.3 如果有超过一半的哨兵批准选举A为领头,则A入选8.4 如果有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,此时每个参选的节点期待一个随机工夫后再次发动参选申请,进行下一轮投票精选,直至选举出领头哨兵8.5 选出领头哨兵后,领头者开始对进行故障复原,从呈现故障的master的从数据库slave中筛选一个来入选新的master,抉择规定如下:8.5.1 所有在线的slave中抉择优先级最高的,优先级能够通过slave-priority配置8.5.2 如果有多个最高优先级的slave,则选取复制偏移量最大(即复制越残缺)的入选8.5.3 如果以上条件都一样,选取id最小的slave挑选出须要继任的slaver后,领头哨兵向该数据库发送命令使其升格为master,而后再向其余slave发送命令承受新的master,最初更新数据。将曾经进行的旧的master更新为新的master的从数据库,使其复原服务后以slave的身份持续运行。2.3 哨兵配置哨兵配置的配置文件为sentinel.conf,设置主机名称,地址,端口,以及选举票数即复原时起码须要几个哨兵节点批准。只有配置须要监控的master就能够了,哨兵会监控连贯该master的slave。 sentinel monitor mymaster 192.168.0.107 6379 1启动哨兵节点: redis-server sentinel.conf --sentinel &呈现以下相似信息即启动哨兵胜利 3072:X 12 Apr 22:40:02.554 ### Sentinel runid is e510bd95d4deba3261de72272130322b2ba650e73072:X 12 Apr 22:40:02.554 ### +monitor master mymaster 192.168.0.107 6379 quorum 13072:X 12 Apr 22:40:03.516 * +slave slave 192.168.0.108:6379 192.168.0.108 6379 @ mymaster 192.168.0.107 63793072:X 12 Apr 22:40:03.516 * +slave slave 192.168.0.109:6379 192.168.0.109 6379 @ mymaster 192.168.0.107 6379能够在任何一台服务器上查看指定哨兵节点信息: ...

August 5, 2022 · 2 min · jiezi

关于redis:Mac平台Redis可视化工具Redis-Desktop-Manager-2021

Redis Desktop Manager for Mac是Mac平台上一款十分实用的Redis可视化工具。RDM反对SSL / TLS加密,SSH隧道,基于SSH隧道的TLS,为您提供了一个易于应用的GUI,能够拜访您的Redis数据库并执行一些基本操作:将键视为树,CRUD键,通过shell执行命令。

August 5, 2022 · 1 min · jiezi

关于redis:Redis扩容机制

前言在Redis-对象类型一文中曾经对Redis中的哈希对象进行了学习,已知哈希对象的底层数据结构应用了字典dict数据结构,实际上Redis中的数据就是以key-value的模式存储在dict中,dict数据结构的示意图能够示意如下。 即一个dict数据结构持有两张哈希表dictht,每张dictht中持有一个存储元素的节点数组,每对键值对会被封装成一个dictEntry节点而后增加到节点数组中,当存在哈希抵触时,Redis中应用拉链法解决哈希抵触。然而dictEntry数组的默认容量为4,产生哈希抵触的概率极高,如果不进行扩容,会导致哈希表的工夫复杂度好转为O(logN),所以满足肯定条件时,须要进行dictEntry数组的扩容,即进行Redis的扩容,本篇文章将对Redis的扩容机制进行学习。 Redis源码版本:6.0.16 注释一. Redis的扩容机会Redis会在如下两种状况触发扩容。 如果没有fork子过程在执行RDB或者AOF的长久化,一旦满足ht[0].used >= ht[0].size,此时触发扩容;如果有fork子过程在执行RDB或者AOF的长久化时,则须要满足ht[0].used > 5 * ht[0].size,此时触发扩容。上面将联合源码对Redis的扩容机会进行学习。当向dict增加或者更新数据时,对应的办法是位于dict.c文件中的dictReplace()办法,如下所示。 int dictReplace(dict *d, void *key, void *val) { dictEntry *entry, *existing, auxentry; //如果增加胜利,dictAddRaw()办法会把胜利增加的dictEntry返回 //返回的dictEntry只设置了键,值须要在这里调用dictSetVal()办法进行设置 entry = dictAddRaw(d,key,&existing); if (entry) { dictSetVal(d, entry, val); return 1; } //执行到这里,表明在哈希表中曾经存在一个dictEntry的键与以后待增加的键值对的键相等 //此时应该做更新值的操作,且existing此时是指向这个曾经存在的dictEntry auxentry = *existing; //更新值,即为existing指向的dictEntry设置新值 dictSetVal(d, existing, val); //开释旧值 dictFreeVal(d, &auxentry); return 0;}dictReplace()办法会执行键值对的增加或更新,如果哈希表中不存在dictEntry的键与待增加键值对的键相等,此时会基于待增加键值对新创建一个dictEntry并以头插法插入哈希表中,此时返回1;如果哈希表中存在dictEntry的键与待增加键值对的键相等,此时就为曾经存在的dictEntry设置新值并开释旧值,而后返回0。通常要触发扩容,触发机会个别在增加键值对的时候,所以持续剖析dictAddRaw()办法,其源码实现如下所示。 dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing) { long index; dictEntry *entry; dictht *ht; //判断是否在进行rehash,如果正在进行rehash,则触发渐进式rehash //dictIsRehashing()办法在dict.h文件中,如果dict的rehashidx不等于-1,则表明此时在进行rehash if (dictIsRehashing(d)) _dictRehashStep(d); //获取待增加键值对在哈希表中的索引index //如果哈希表曾经存在dictEntry的键与待增加键值对的键相等,此时_dictKeyIndex()办法返回-1 if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1) return NULL; //如果在进行rehash,待增加的键值对寄存到ht[1],否则寄存到ht[0] ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; //为新dictEntry开拓内存,此时dictEntry的键和值尚未设置 entry = zmalloc(sizeof(*entry)); //头插的形式插入哈希表index地位的哈希桶中 entry->next = ht->table[index]; ht->table[index] = entry; //哈希表的以后大小加1 ht->used++; //为新dictEntry设置键 dictSetKey(d, entry, key); return entry;}dictAddRaw()办法会首先判断以后是否处于rehash阶段(判断以后是否正在扩容),如果正在rehash,则触发一次哈希桶的迁徙操作(这一点前面再详细分析),而后通过_dictKeyIndex()办法获取待增加键值对在哈希表中的索引index,如果获取到的index为-1,表明存在dictEntry的键与待增加键值对的键相等,此时dictAddRaw()办法返回NULL以通知办法调用方须要执行更新操作,如果index不为-1,则基于待增加键值对创立新的dictEntry并以头插的形式插入哈希表index地位的哈希桶中,而后更新哈希表的以后大小以及为新dictEntry设置键。扩容的触发在_dictKeyIndex()办法中,其源码实现如下所示。 ...

August 2, 2022 · 3 min · jiezi

关于redis:Redis-5-种基本数据结构StringListHashSetSorted-Set详解-JavaGuide

首发于:Redis 5 种根本数据结构详解 - JavaGuide 相干文章:Redis常见面试题总结(上) 。 Redis 5 种根本数据结构(String、List、Hash、Set、Sorted Set)在面试中常常会被问到,这篇文章咱们一起来回顾复习一下。 还有几种比拟非凡的数据结构(HyperLogLogs、Bitmap 、Geospatial、Stream)也十分重要,咱们前面下次再聊! 上面是注释。 你能够在 Redis 官网上找到 Redis 数据结构十分具体的介绍: Redis Data StructuresRedis Data types tutorial将来随着 Redis 新版本的公布,可能会有新的数据结构呈现,通过查阅 Redis 官网对应的介绍,你总能获取到最靠谱的信息。 String(字符串)介绍String 是 Redis 中最简略同时也是最罕用的一个数据结构。 String 是一种二进制平安的数据结构,能够用来存储任何类型的数据比方字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的门路)、序列化后的对象。 尽管 Redis 是用 C 语言写的,然而 Redis 并没有应用 C 的字符串示意,而是本人构建了一种 简略动静字符串(Simple Dynamic String,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光能够保留文本数据还能够保留二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是平安的,不会造成缓冲区溢出。 常用命令命令介绍SET key value设置指定 key 的值SETNX key value只有在 key 不存在时设置 key 的值GET key获取指定 key 的值MSET key1 value1 key2 value2 …设置一个或多个指定 key 的值MGET key1 key2 ...获取一个或多个指定 key 的值STRLEN key返回 key 所贮存的字符串值的长度INCR key将 key 中贮存的数字值增一DECR key将 key 中贮存的数字值减一EXISTS key判断指定 key 是否存在DEL key(通用)删除指定的 keyEXPIRE key seconds(通用)给指定 key 设置过期工夫更多 Redis String 命令以及具体使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?gr... 。 ...

July 26, 2022 · 4 min · jiezi

关于redis:LinuxUbuntu-安装Redis

装置Redis更新源 sudo apt update装置redis sudo apt install redis-server查看redis服务状态 sudo systemctl status redis-server容许近程拜访Redis编辑redis配置文件 vi /etc/redis/redis.conf#正文掉bind 127.0.0.1 ::1如果redis.conf文件关上为空 批改文件权限 sudo chmod 755 /etc/redis/redis.conf如果批改文件后:wq无奈保留 先:q!强制退出而后 sudo !! 进入并进行批改 :wq保留重启 Redis 服务 sudo systemctl restart redis-server查看6379端口的状况 ss -an | grep 6379配置防火墙容许指定的IP拜访Redis的端口 sudo ufw allow proto tcp from 192.168.1.3/24 to any port 6379设置登录明码 (也能够不设置,默认无需明码登录)vi /etc/redis/redis.conf搜寻来找到上面这行正文#requirepass foobared勾销正文,把 foobared 改为你想设置的redis的登录明码。比方:requirepass 123456保留后,重启redis服务 sudo systemctl restart redis.service

July 25, 2022 · 1 min · jiezi

关于redis:redis知识点入门

文章不易,请关注公众号 毛毛虫的小小蜡笔,多多反对,谢谢。 有任何问题都能够留言征询。 redis是什么?redis是一款内存数据库。 绝对mysql来说,redis把数据存储在内存,而不是硬盘。 正如浏览器的sessionStorge一样,重启了浏览器,数据就会失落。 也就是如果电脑重启了(其实不必电脑重启,redis服务重启了就能够),redis保留的数据就会失落。 那redis不就存在很大的数据失落危险?详情 请查看:毛毛虫的小小蜡笔

July 22, 2022 · 1 min · jiezi

关于redis:redis学习感悟不定时更新中

redis如何搭建集群?首先要启动多个redis-server通过conf文件标识身份:ip地址:端口号所有的server启动好了过后,通过以下命令制订本人的master,每台服务器启动的时候默认都是本人为master slaveof [ip] [端口]查看主从复制信息 info replication本人不想当slave了,用一个命令复原到master身份 slaveof no oneredis的哨兵模式如何实现?减少配置文件“sentinel.conf”,文件内容一句话搞定! #myredis是为设定的redis master主服务器起的别名#127.0.0.1是master的ip#1 是投票权重sentinel monitor myredis 127.0.0.1 6381 1启动模式 redis-sentinel myredis/sentinel.conf怎么制订谁是哨兵?随开启的哨兵模式谁就是哨兵哨兵之间是如何通信的?通过公布订阅模式,哨兵监听master,订阅master,master公布音讯给哨兵,在master外部存储有订阅者的身份标识(ip:端口),所以每个哨兵都能通过这种形式感知到其余哨兵!楼主目前试验来看,一旦启用哨兵模式,集群就只能有一个master,不能有多个redis 主从复制?slave 只能读master的信息不能写信息,只管他也有本人的slave公布订阅模式也是一样,只有master能够公布频道,slave即便能够公布信息,其余订阅该频道的用户也无奈收到,反正slave只能读的权力,没有写的权力!公布订阅的命令? 下述命令的执行者是redis-clisubscribe channel [channel....]publish channel1 message

July 22, 2022 · 1 min · jiezi

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

咱们接着上一部分来进行分享,咱们能够在如下地址下载 redis 的源码: https://redis.io/download 此处我下载的是 redis-6.2.5 版本的,xdm 能够间接下载上图中的 redis-6.2.6 版本, redis 中 hash 表的数据结构redis hash 表的数据结构定义在: redis-6.2.5\src\dict.h 哈希表的构造,每一个字典都有两个实现从旧表到新表的增量重哈希 typedef struct dictht { dictEntry **table; unsigned long size; unsigned long sizemask; unsigned long used;} dictht;table: table 是一个二级指针,对应这一个数组,数组中的每个元素都是指向了一个 dictEntry 构造体指针的,dictEntry 具体的数据结构是保留一个键值对 具体的 dictEntry 数据结构是这样的: size: size 属性是记录了整个 hash 表的大小,也能够了解为上述 table 数组的大小 sizemask: sizemask 属性,和具体的 hash 值来一起决定键要放在 table 数组的哪个地位 sizemask 的值,总是会比 size 小 1 ,咱们能够来演示一下 应用取余的形式,实际上是很低效的,咱们的计算机是不会做乘除法的,同样都是用加减法来进行解决的,为了高效解决,咱们能够应用二进制的形式 应用二进制的形式,就会用到该字段 sizemask ,次要是用来 和 具体的 hash 值做按位与操作 ...

July 15, 2022 · 2 min · jiezi

关于redis:Redis

Redis能做什么?内存存储,长久化,内存中是断电即失,所以说长久化很重要(RDB,AOF)效率高,可用于高速缓存公布订阅零碎地图信息剖析计时器,计数器(浏览量)。。。。个性多样的数据库类型长久化集群事务Redis装置(Linux环境)下载Redis-7.0.2.tar.gz挪动至/opt解压 tar -zxvf redis-7.0.2.tar.gz 根本环境装置# 装置gcc (gcc version)yum install gcc-c++ # 装置redis环境(server、sentinel等)make# 确认装置make install # 默认装置到了/usr/local/bin门路 # 在/usr/local/bin目录下创立config目录mkdir config# 挪动redis.conf文件到/usr/local/binmv /opt/redis-7.0.2/redis.conf /usr/local/bin/config # 批改redis.conf,使之后台运行vim redis.conf# daemonize改为yesdaemonize yes 启动Redis服务# 通过指定的配置文件启动服务redis-server config/redis.conf # 应用redis客户端连贯redis-cli -p 6379# 测试是否连通pingPONG# 查看redis过程ps -ef | grep redis # 敞开redis服务shutdown# 退出redis-cliexit性能测试redis-benchmark # 100个并发,100000个申请(如果显示NO AUTH则须要加上 -a 明码 )redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000 测试后果

July 10, 2022 · 1 min · jiezi