关于redis:Redis基础客户端

Redis是用单线程来解决多个客户端的拜访,因而作为Redis的开发和运维人员须要理解Redis服务端和客户端的通信协议,以及支流编程语言的Redis客户端应用办法,同时还须要理解客户端治理的相应API以及开发运维中可能遇到的问题。 客户端APIclient listclient list命令能列出与Redis服务端相连的所有客户端连贯信息,输入后果的每一行代表一个客户端的信息,它们是每个客户端的一些执行状态,上面抉择几个重要的属性进行阐明。 标识:id、addr、fd、nameid:客户端连贯的惟一标识,这个id是随着Redis的连贯自增的,重启Redis后会重置为0。addr:客户端连贯的ip和端口。fd:socket的文件描述符,与lsof命令后果中的fd是同一个,如果fd=-1代表以后客户端不是内部客户端,而是Redis外部的伪客户端。name:客户端的名字。输出缓冲区:qbuf、qbuf-freeRedis为每个客户端调配了输出缓冲区,它的作用是将客户端发送的命令长期保留,同时Redis从会输出缓冲区拉取命令并执行。 qbuf和qbuf-free别离代表这个缓冲区的总容量和残余容量,Redis没有提供相应的配置来规定每个缓冲区的大小,输出缓冲区会依据输出内容大小的不同动静调整。 输出缓冲有两点须要留神: 每个客户端缓冲区的大小不能超过1G,超过后客户端将被敞开。输出缓冲区不受maxmemory管制,假如一个Redis实例设置了maxmemory为4G,曾经存储了2G数据,然而如果此时输出缓冲区应用了3G,曾经超过maxmemory限度,可能会产生数据失落、键值淘汰、OOM等状况。输出缓冲区使用不当造成的危害十分大,那么造成输出缓冲区过大的起因有哪些? 输出缓冲区过大次要是因为Redis的处理速度跟不上输出缓冲区的输入速度,并且每次进入输出缓冲区的命令蕴含了大量bigkey,从而造成了输出缓冲区过大的状况。还有一种状况就是Redis产生了阻塞,短期内不能解决命令,造成客户端输出的命令积压在了输出缓冲区,造成了输出缓冲区过大。 那么如何疾速发现和监控呢?监控输出缓冲区异样的办法有两种: 通过定期执行client list命令,收集qbuf和qbuf-free找到异样的连贯记录并剖析,最终找到可能出问题的客户端。通过info命令的info clients模块,找到最大的输出缓冲区,例如能够设置超过10M就进行报警。 输入缓冲区:obl、oll、omemRedis为每个客户端调配了输入缓冲区,它的作用是保留命令执行的后果返回给客户端,为Redis和客户端交互返回后果提供缓冲。 与输出缓冲区不同的是,输入缓冲区的容量能够通过参数client-output-buffer-limit来进行设置,并且输入缓冲区做得更加粗疏,依照客户端的不同分为三种:一般客户端、公布订阅客户端、slave客户端。对应的配置规定是: client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds><class>:客户端类型,分为三种。a)normal:一般客户端;b)slave:slave客户端,用于复制;c)pubsub:公布订阅客户端。<hard limit>:如果客户端应用的输入缓冲区大于<hard limit>,客户端会被立刻敞开。<soft limit>和<soft seconds>:如果客户端应用的输入缓冲区超过了<soft limit>并且继续了<soft limit>秒,客户端会被立刻敞开。输入缓冲区也不受maxmemory的限度,如果使用不当同样会造成maxmemory用满产生的数据失落、键值淘汰、OOM等状况。 实际上输入缓冲区由两局部组成:固定缓冲区(16KB)和动静缓冲区,其中固定缓冲区返回比拟小的执行后果,而动静缓冲区返回比拟大的后果。固定缓冲区应用的是字节数组,动静缓冲区应用的是列表。当固定缓冲区存满后会将Redis新的返回后果寄存在动静缓冲区的队列中,队列中的每个对象就是每个返回后果。 client list中的obl代表固定缓冲区的长度,oll代表动静缓冲区列表的长度,omem代表应用的字节数。监控输入缓冲区的办法和监控输出缓冲区的办法统一。 相比于输出缓冲区,输入缓冲区出现异常的概率绝对会比拟大,那么如何预防呢?办法如下: 进行上述监控,设置阀值,超过阀值及时处理。限度一般客户端输入缓冲区的<hard limit> <soft limit> <soft seconds>,把谬误扼杀在摇篮中。适当增大slave的输入缓冲区的<hard limit> <soft limit> <soft seconds>,如果master节点写入较大,slave客户端的输入缓冲区可能会比拟大,一旦slave客户端连贯因为输入缓冲区溢出被kill,会造成复制重连。限度容易让输入缓冲区增大的命令,例如,高并发下的monitor命令就是一个危险的命令。及时监控内存,一旦发现内存抖动频繁,可能就是输入缓冲区过大。客户端的存活状态client list中的age和idle别离代表以后客户端曾经连贯的工夫和最近一次的闲暇工夫。 客户端的限度maxclients和timeoutRedis提供了maxclients参数来限度最大客户端连接数,一旦连接数超过maxclients,新的连贯将被回绝。maxclients默认值是10000,能够通过info clients来查问以后Redis的连接数。 Redis同时提供了timeout(单位为秒)参数来限度连贯的最大闲暇工夫,一旦客户端连贯的idle工夫超过了timeout,连贯将会被敞开。 客户端类型client list中的flag是用于标识以后客户端的类型,见下表: 其余下表列出之前介绍过以及一些比较简单或者不太重要的属性:

November 3, 2020 · 1 min · jiezi

关于redis:redis源码跳跃表

前言跳跃表(skiplist)是一种有序的数据结构,是在有序链表的根底上进行了扩大,解决了有序链表查找某个值效率问题。跳跃表反对均匀O(logn)、最坏O(n)复杂度的节点查找,大部分状况效率能够和均衡树相媲美,且实现更简略。Redis应用跳跃表作为有序汇合键的底层实现之一。 根本介绍跳跃表作为有序汇合的底层实现之一,咱们先来看有序汇合的简略用法。 > zadd fruit 10 banana(integer) 1> zadd fruit 12 apple(integer) 1> zadd fruit 8 cherry(integer) 1> zrange fruit 0 -1 #按 score 有序取出1) "cherry"2) "banana"3) "apple"原理有序链表的查找如上面的有序链表从该有序表中搜寻元素 < 5, 23, 40 > ,须要比拟的次数别离为 < 2, 4, 6 >,总共比拟的次数为 2 + 4 + 6 = 12 次。那有没有优化的算法呢? 思考链表是有序的,但不能应用二分查找。相似二叉搜寻树,咱们把一些节点提取进去,作为索引。失去如下构造: 这里咱们把 < 4, 8, 34, 45 > 提取进去作为一级索引,这样搜寻的时候就能够缩小比拟次数了。 咱们还能够再从一级索引提取一些元素进去,作为二级索引,变成如下构造: 因为相似二分查找,效率失去进步,查找均匀复杂度能够达到O(logn) 跳跃表实现结构图如下

November 3, 2020 · 1 min · jiezi

关于redis:redis源码字典

1.简介字典应用哈希表作为底层实现,一个字典里蕴含2个哈希表,一个ht[0]是失常状况下应用,ht[1]会在rehash的时候应用,一个哈希表蕴含多个哈希表节点,每个哈希表节点保留了字典中的一个键值对。Redis中的数据库就是应用字典来作为底层实现。 2.结构图(一般状态下没有进行rehash的状况) /* * 字典 */typedef struct dict { // 类型特定函数 dictType *type; // 公有数据 void *privdata; // 哈希表 dictht ht[2]; // rehash 索引 // 当 rehash 不在进行时,值为 -1 int rehashidx; /* rehashing not in progress if rehashidx == -1 */ // 目前正在运行的平安迭代器的数量 int iterators; /* number of iterators currently running */} dict;/* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. *//* * 哈希表 * * 每个字典都应用两个哈希表,从而实现渐进式 rehash 。 */typedef struct dictht { // 哈希表数组 dictEntry **table; // 哈希表大小 unsigned long size; // 哈希表大小掩码,用于计算索引值 // 总是等于 size - 1 unsigned long sizemask; // 该哈希表已有节点的数量 unsigned long used;} dictht;/* * 哈希表节点 */typedef struct dictEntry { // 键 void *key; // 值 union { void *val; uint64_t u64; int64_t s64; } v; // 指向下个哈希表节点,造成链表 struct dictEntry *next;} dictEntry;/* * 字典类型特定函数 */typedef struct dictType { // 计算哈希值的函数 unsigned int (*hashFunction)(const void *key); // 复制键的函数 void *(*keyDup)(void *privdata, const void *key); // 复制值的函数 void *(*valDup)(void *privdata, const void *obj); // 比照键的函数 int (*keyCompare)(void *privdata, const void *key1, const void *key2); // 销毁键的函数 void (*keyDestructor)(void *privdata, void *key); // 销毁值的函数 void (*valDestructor)(void *privdata, void *obj);} dictType;

November 3, 2020 · 1 min · jiezi

关于redis:SpringBoot-DB-系列Redis-高级特性之-HyperLoglog

【SpringBoot DB 系列】Redis 高级个性之 HyperLogloghyperloglog 算法,利用非常少的空间,实现比拟大的数据量级统计;比方咱们后面在介绍 bitmap 的过程中,说到了日活的统计,当数据量达到百万时,最佳的存储形式是 hyperloglog,本文将介绍一下 hyperloglog 的基本原理,以及 redis 中的应用姿态 <!-- more --> I. 根本应用1. 配置咱们应用 SpringBoot 2.2.1.RELEASE来搭建我的项目环境,间接在pom.xml中增加 redis 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>如果咱们的 redis 是默认配置,则能够不额定增加任何配置;也能够间接在application.yml配置中,如下 spring: redis: host: 127.0.0.1 port: 6379 password:2. 应用姿态咱们下来看应用姿态,原理放在前面阐明redis 中,hyperlolog应用非常简单,个别就两个操作命令,增加pfadd + 计数pfcount;另外还有一个不罕用的merge a. add增加一条记录 public boolean add(String key, String obj) { // pfadd key obj return stringRedisTemplate.opsForHyperLogLog().add(key, obj) > 0;}b. pfcount非精准的计数统计 public long count(String key) { // pfcount 非精准统计 key的计数 return stringRedisTemplate.opsForHyperLogLog().size(key);}a. merge将多个 hyperloglog 合并成一个新的 hyperloglog;感觉用的场景并不会特地多 ...

November 2, 2020 · 1 min · jiezi

关于redis:redis源码SDS

SDS概念:简略动静字符串(simple dynamic string, SDS)结构图 /* * 保留字符串对象的构造 */struct sdshdr { // buf 中已占用空间的长度 int len; // buf 中残余可用空间的长度 int free; // 数据空间 char buf[];};3.疑难代码解析 /* * 返回 sds 理论保留的字符串的长度 * * T = O(1) */static inline size_t sdslen(const sds s) { struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); return sh->len;}struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))) 是什么意思呢?解析 /* * 类型别名,用于指向 sdshdr 的 buf 属性 */typedef char *sds;sds s是指到字符串buf的地位的指针char buf[] 是柔性数组,不占据内存大小,所以sizeof(struct sdshdr)为8所以struct sdshdr sh = (void) (s-(sizeof(struct sdshdr))) 就是指向了sdshdr构造体的头部,如图所示: ...

November 1, 2020 · 1 min · jiezi

关于redis:虚拟机搭建3主3从redis集群

搭建3主3从的redis cluster:基于redis分布式锁。 1.redis包上传将redis-4.0.1.tar.gz包上传到一台机器下来,/usr/local目录下,解压缩,重命名为redis scp ./redis-4.0.1.tar.gz ./ruby-2.3.4.tar.gz root@192.168.1.8:/usr/local/ tar -zxvf redis-4.0.1.tar.gz //解压redis mv redis-4.0.1 redis //将解压后的redis文件命名为redis 2.装置redis依赖yum install -y tcl gcc zlib-devel openssl-devel以上装置了tcl、gcc、zlib-devel 、openssl-devel 3.编译redis进入redis录内,执行make MALLOC=libc命令 make MALLOC=libc 4.把redis的一些命令脚本拷贝到PATH变量所在的目录cp -a src/redis-server src/redis-cli src/redis-sentinel src/redis-trib.rb src/redis-check-aof src/redis-check-rdb src/redis-benchmark /usr/local/bin/ 那个/usr/local/bin,就是PATH变量的目录,这样就能够间接执行redis的一些命令了 5、创立redis集群的目录cd /optmkdir redis-clustermkdir redis-cluster/nodes-{7001,7002,7003} //在redis-cluster上面创立:nodes-7001、nodes-7002、nodes-7003的3个目录 6、批改配置文件编辑redis.conf配置文件(以7001来举个例子,将7002和7003都编辑一下配置文件) cd redis-cluster/nodes-7001vi redis.conf批改配置文件内容如下: bind 127.0.0.1 192.168.9.216 //(留神:bind的意思不是绑定内部服务器的IP,而是绑定本机能够承受拜访的IP)port 7001 pidfile redis_7001.pid loglevel noticelogfile "/opt/redis-cluster/nodes-7001/redis_7001.log" //日志文件寄存目录 dir /opt/redis-cluster/nodes-7001/ //节点主目录寄存地位 cluster-config-file nodes-7001.conf //集群配置文件 daemonize yes //后盾过程supervised no //appendonly yescluster-enabled yes //集群模式开启cluster-node-timeout 15000 //集群节点之间超时工夫是15秒save 900 1 //save 300 10save 60 10000dbfilename dump.rdbappendfilename "appendonly.aof"appendfsync everysec ...

November 1, 2020 · 2 min · jiezi

关于redis:Redis延时队列这次彻底给你整明白了

所谓延时队列就是延时的音讯队列,上面说一下一些业务场景 实际场景 订单领取失败,每隔一段时间揭示用户 用户并发量的状况,能够延时2分钟给用户发短信 先来看看Redis实现一般的音讯队列 咱们晓得,对于业余的音讯队列中间件,如Kafka和RabbitMQ,消费者在生产音讯之前要进行一系列的繁琐过程。 如RabbitMQ发消息之前要创立 Exchange,再创立 Queue,还要将 Queue 和 Exchange 通过某种规定绑定起来,发消息的时候要指定 routingkey,还要管制头部信息 然而绝大 少数状况下,尽管咱们的音讯队列只有一组消费者,但还是须要经验下面一些过程。 有了 Redis,对于那些只有一组消费者的音讯队列,应用 Redis 就能够十分轻松的搞定。Redis 的音讯队列不是业余的音讯队列,它没有十分多的高级个性, 没有 ack 保障,如果对音讯的可靠性有着极致的谋求,那么它就不适宜应用 异步音讯队列根本实现Redis 的 list(列表) 数据结构罕用来作为异步音讯队列应用,应用 rpush/lpush 操作入队列, 应用 lpop 和 rpop 来出队列 > rpush queue 月伴飞鱼1 月伴飞鱼2 月伴飞鱼3(integer) 3> lpop queue"月伴飞鱼1"> llen queue(integer) 2问题1:如果队列空了客户端是通过队列的 pop 操作来获取音讯,而后进行解决。解决完了再接着获取音讯, 再进行解决。如此周而复始,这便是作为队列消费者的客户端的生命周期。 可是如果队列空了,客户端就会陷入 pop 的死循环,不停地 pop,没有数据,接着再 pop, 又没有数据。这就是节约生命的空轮询。空轮询岂但拉高了客户端的 CPU,redis 的 QPS 也 会被拉高,如果这样空轮询的客户端有几十来个,Redis 的慢查问可能会显著增多。 通常咱们应用 sleep 来解决这个问题,让线程睡一会,睡个 1s 钟就能够了。岂但客户端 的 CPU 能降下来,Redis 的 QPS 也降下来了 ...

October 31, 2020 · 3 min · jiezi

关于redis:redisredis为什么这么快

作为一名服务端工程师,工作中你必定和 Redis 打过交道。Redis 为什么快,这点想必你也晓得,至多为了面试也做过筹备。很多人晓得 Redis 快仅仅因为它是基于内存实现的,对于其它起因倒是不置可否。 那么明天就和小莱一起看看: 思维导图 - 基于内存实现 这点在一开始就提到过了,这里再简略说说。 Redis 是基于内存的数据库,那不可避免的就要与磁盘数据库做比照。对于磁盘数据库来说,是须要将数据读取到内存里的,这个过程会受到磁盘 I/O 的限度。 而对于内存数据库来说,自身数据就存在于内存里,也就没有了这方面的开销。 高效的数据结构 Redis 中有多种数据类型,每种数据类型的底层都由一种或多种数据结构来反对。正是因为有了这些数据结构,Redis 在存储与读取上的速度才不受妨碍。这些数据结构有什么特地的中央,各位看官接着往下看: 1、简略动静字符串 这个名词可能你不相熟,换成 SDS 必定就晓得了。这是用来解决字符串的。理解 C 语言的都晓得,它是有解决字符串办法的。而 Redis 就是 C 语言实现的,那为什么还要反复造轮子?咱们从以下几点来看: (1)字符串长度解决 这个图是字符串在 C 语言中的存储形式,想要获取 「Redis」的长度,须要从头开始遍历,直到遇到 '\0' 为止。 Redis 中怎么操作呢?用一个 len 字段记录以后字符串的长度。想要获取长度只须要获取 len 字段即可。你看,差距不言自明。前者遍历的工夫复杂度为 O(n),Redis 中 O(1) 就能拿到,速度显著晋升。 (2)内存重新分配 C 语言中波及到批改字符串的时候会从新分配内存。批改地越频繁,内存调配也就越频繁。而内存调配是会耗费性能的,那么性能降落在劫难逃。 而 Redis 中会波及到字符串频繁的批改操作,这种内存调配形式显然就不适宜了。于是 SDS 实现了两种优化策略: 空间预调配对 SDS 批改及空间裁减时,除了调配所必须的空间外,还会额定调配未应用的空间。 具体调配规定是这样的:SDS 批改后,len 长度小于 1M,那么将会额定调配与 len 雷同长度的未应用空间。如果批改后长度大于 1M,那么将调配1M的应用空间。 ...

October 30, 2020 · 1 min · jiezi

关于redis:Redis可以做哪些事

Redis是一种基于键值对的NoSQL数据库,它的值次要由string(字符串),hash(哈希),list(列表),set(汇合),zset(有序汇合)五种根本数据结构形成,除此之外还反对一些其余的数据结构和算法。key都是由字符串形成的,那么这五种数据结构的应用场景有哪些?一起来看看! 一 字符串字符串类型是Redis最根底的数据结构,字符串类型能够是JSON、XML甚至是二进制的图片等数据,然而最大值不能超过512MB。 1.1 外部编码Redis会依据以后值的类型和长度决定应用哪种外部编码来实现。 字符串类型的外部编码有3种: int:8个字节的长整型。embstr:小于等于39个字节的字符串。raw:大于39个字节的字符串。1.2 应用场景1.2.1 缓存在web服务中,应用MySQL作为数据库,Redis作为缓存。因为Redis具备撑持高并发的个性,通常能起到减速读写和升高后端压力的作用。web端的大多数申请都是从Redis中获取的数据,如果Redis中没有须要的数据,则会从MySQL中去获取,并将获取到的数据写入redis。 1.2.2 计数Redis中有一个字符串相干的命令incr key,incr命令对值做自增操作,返回后果分为以下三种状况: 值不是整数,返回谬误值是整数,返回自增后的后果key不存在,默认键为0,返回1比方文章的浏览量,视频的播放量等等都会应用redis来计数,每播放一次,对应的播放量就会加1,同时将这些数据异步存储到数据库中达到长久化的目标。 1.2.3 共享Session在分布式系统中,用户的每次申请会拜访到不同的服务器,这就会导致session不同步的问题,如果一个用来获取用户信息的申请落在A服务器上,获取到用户信息后存入session。下一个申请落在B服务器上,想要从session中获取用户信息就不能失常获取了,因为用户信息的session在服务器A上,为了解决这个问题,应用redis集中管理这些session,将session存入redis,应用的时候间接从redis中获取就能够了。 1.2.4 限速为了平安思考,有些网站会对IP进行限度,限度同一IP在肯定工夫内拜访次数不能超过n次。 二 哈希Redis中,哈希类型是指一个键值对的存储构造。 2.1 外部编码哈希类型的外部编码有两种: ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)同时所有值都小于hash-max-ziplist-value配置(默认64字节)时应用。ziplist应用更加紧凑的构造实现多个元素的间断存储,所以比hashtable更加节俭内存。hashtable(哈希表):当ziplist不能满足要求时,会应用hashtable。2.2 应用场景因为hash类型存储的是一个键值对,比方数据库有以下一个用户表构造 idnameage1Java旅途18将以上信息存入redis,用表明:id作为key,用户属性作为值: hset user:1 name Java旅途 age 18应用哈希存储会比字符串更加不便直观 三 列表列表类型用来存储多个有序的字符串,一个列表最多能够存储2^32-1个元素,列表的两端都能够插入和弹出元素。 3.1 外部编码列表的外部编码有两种: ziplist(压缩列表):当哈希类型元素个数小于list-max-ziplist-entries配置(默认512个)同时所有值都小于list-max-ziplist-value配置(默认64字节)时应用。ziplist应用更加紧凑的构造实现多个元素的间断存储,所以比hashtable更加节俭内存。linkedlist(链表):当ziplist不能满足要求时,会应用linkedlist。3.2 应用场景3.2.1 音讯队列列表用来存储多个有序的字符串,既然是有序的,那么就满足音讯队列的特点。应用lpush+rpop或者rpush+lpop实现音讯队列。除此之外,redis反对阻塞操作,在弹出元素的时候应用阻塞命令来实现阻塞队列。 3.2.2 栈因为列表存储的是有序字符串,满足队列的特点,也就能满足栈先进后出的特点,应用lpush+lpop或者rpush+rpop实现栈。 3.2.3 文章列表因为列表的元素不然而有序的,而且还反对依照索引范畴获取元素。因而咱们能够应用命令lrange key 0 9分页获取文章列表 四 汇合汇合类型也能够保留多个字符串元素,与列表不同的是,汇合中不容许有反复元素并且汇合中的元素是无序的。一个汇合最多能够存储2^32-1个元素。 4.1 外部编码汇合类型的外部编码有两种: intset(整数汇合):当汇合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,redis会选用intset来作为汇合的外部实现,从而缩小内存的应用。hashtable(哈希表):当intset不能满足要求时,会应用hashtable。4.2 应用场景4.2.1 用户标签例如一个用户对篮球、足球感兴趣,另一个用户对橄榄球、乒乓球感兴趣,这些趣味点就是一个标签。有了这些数据就能够失去喜爱同一个标签的人,以及用户的独特感兴趣的标签。给用户打标签的时候须要①给用户打标签,②给标签加用户,须要给这两个操作减少事务。 给用户打标签sadd user:1:tags tag1 tag2给标签增加用户sadd tag1:users user:1sadd tag2:users user:1应用交加(sinter)求两个user的独特标签 sinter user:1:tags user:2:tags4.2.2 抽奖性能汇合有两个命令反对获取随机数,别离是: 随机获取count个元素,汇合元素个数不变srandmember key [count] ...

October 30, 2020 · 1 min · jiezi

关于redis:Redis-migrate-数据迁移工具

在工作中可能会遇到单点Redis向Redis集群迁徙数据的问题,但又不能老麻烦运维来做。为了不便研发本人迁徙数据,我这里写了一个简略的Redis迁徙工具,心愿对有须要的人有用。 本工具反对:单点Redis到单点Redis迁徙单点Redis到Redis集群迁徙Redis集群到Redis集群迁徙Redis集群到单点Redis迁徙该工具曾经编译成了多平台命令,间接从Github下载二进制文件执行就好了。我的项目地址: https://github.com/icowan/redis-tool把代码拉下来之后间接执行命令 make 就能够编译多个平台可执行文件,须要依赖golang编译器。 Windows amd64: redis-tool-windows-amd64.exeMacOS amd64: redis-tool-darwin-amd64Linux amd64: redis-tool-linux-amd64Linux arm64: redis-tool-linux-arm64查看应用办法: $ chmod a+x redis-tool-linux-amd64 $ ./redis-tool-linux-amd64 -h 反对的数据类型string 字符串hash 散列列表list 列表sorted-set 有序汇合如何应用下载好命令并受权之后执行 ./redis-tool-linux-amd64 -h 能够查看该工具所反对的所有性能: $ ./redis-tool-darwin-amd64 migrate -h数据迁徙命令Usage:redis-tool migrate [command]Examples:反对命令:[hash, set, sorted-set, list]Available Commands:all 迁徙所有hash 哈希列表迁徙list 列表迁徙 set redis set 迁徙sorted-set 有序汇合迁徙Flags: -h, --help help for migrate --source-auth string 源明码 --source-database int 源database --source-hosts string 源redis地址, 多个ip用','隔开 (default "127.0.0.1:6379") --source-prefix string 源redis前缀 --source-redis-cluster 源redis是否是集群 --target-auth string 指标明码 --target-database int 指标database --target-hosts string 指标redis地址, 多个ip用','隔开 (default "127.0.0.1:6379") --target-prefix string 指标redis前缀 --target-redis-cluster 指标redis是否是集群Use "redis-tool migrate [command] --help" for more information about a command.参数阐明: ...

October 29, 2020 · 1 min · jiezi

关于redis:redis-内存满了怎么办

淘汰机制在redis.conf中,能够配置maxmemory 100mb来设置redis的内存,如果设置为0,在64位零碎中,有多少内存就能够应用多少,在32位零碎中,能够应用3GB的内存。有些key设置过期工夫的,所以redis会清理掉使之不占内存空间,有些key是没有设置过期工夫的,这些key会始终占用内存空间,当内存达到指定的内存大小maxmemory时,redis有以下几种淘汰机制提供抉择: noeviction:当内存满的时候,回绝写入。allkeys-lru:当内存满的时候,移除最近应用较少的(LRU)key。volatile-lru:当内存满的时候,在设置过期的key中移除最近应用较少的(LRU)key。allkeys-random:当内存满的时候,随机移除key。volatile-random:当内存满的时候,在设置过期的key中随机移除key。volatile-ttl:当内存满的时候,在设置过期的key中移除快要过期的key。allkeys-lru是比拟罕用的机制,依据应用工夫,大概率的保留应用频繁的内存,进步零碎的拜访性能。比方A数据,应用很频繁,能够在内存中始终获取数据,而不必从数据库读取数据。如果保留的B数据,几天才拜访一次,就占用了内存空间。allkeys-random在循环拜访的时候,能够用的上,因为所有的key拜访的频率是一样的,感觉应用场景不多。noeviction应用的场景不多,volatile-lru、volatile-random、volatile-ttl对于设置过期的key的场景,也没有很多。淘汰机制是如何工作的呢? 客户端发送增加数据的命令。redis查看内存空间是否超过maxmemory的配置值,如果超过了,应用相应的淘汰机制清理数据。执行客户端的命令。这个流程有可能使理论内存大于maxmemory的配置值。比方以后的内存大于等于maxmemory的配置值100mb,redis清理后还有99MB,而后执行客户端命令,插入了1.1MB,这样redis中就有了100.1MB。

October 26, 2020 · 1 min · jiezi

关于redis:redis-过期数据怎么处理

简略概念在redis中,通常状况下,当咱们设置一个key的时候,会始终存在的,直到咱们用del命令删除。如果咱们须要为一个key设置过期的工夫,咱们会用到EXPIRE命令,执行后会返回0或者1。0示意所设置的key不存在,1示意设置胜利。 local:0>expire name 100"0"local:0>set name zhangsan"OK"local:0>expire name 100"1"当咱们执行del或者set操作时,会时过期工夫生效。 local:0>get namenulllocal:0>set name zhangsan"OK"local:0>expire name 100"1"local:0>ttl name"98"local:0>set name lisi"OK"local:0>ttl name"-1"过期形式redis的过期有两种形式:被动过期和被动过期。 被动过期被动过期是指,当客户端拜访一个key时,会看他是否设置了过期的工夫,如果设置了,再看是否曾经过期了,如果过期了,把key删除掉,并返回null。 被动过期如果仅仅靠被动过期,导致曾经过期的key不再拜访,会使这些key始终占用内存,节约内存空间,所以redis也有被动过期的形式,每隔10秒,redis会做一下操作: 随机抽取20个过期属性的key。删除曾经过期的key。如果超过25%的key曾经过期了,就会从新执行第一个操作。从这个算法来说,整个redis内存中,过期的数据会管制在25%以内。 AOF文件如何解决在redis - 长久化提到,AOF会将redis执行过的命令追加到文件的开端。当一个键过期时,将在AOF文件中合成一个DEL操作,这样key在AOF中就显示曾经没有了。然而因为下面的被动过期机制,可能还会有25%的过期key在AOF文件中。

October 26, 2020 · 1 min · jiezi

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

redis缓存穿透穿透解决方案-布隆过滤器 咱们先来看一段代码 cache_key = "id:1" cache_value = GetValueFromRedis(cache_key); //判断缓存是否有数据 if cache_value != nil{ //如果有 间接返回数据 return cache_value } db_value = GetValueFromDb(cache_key) // 从数据库中查问数据 if db_value == nil{ return db_value } expire_time = 300 SetRedisValue(cache_key, db_value, expire_time) //将数据库的后果更新到缓存中,并间接返回后果 return db_value置信绝大多数同学都是这么解决申请的,这样用redis可能给mysql抵御住大部分的申请。其实这样是存在肯定的问题的 问题1 我在申请的时候,用id=-1来申请 id=-1这条记录在数据库中是不存在的,当然对应的redis中也是没有的。那么就须要去申请数据库而后把数据写入到redis中,这样就会造成没有必要的数据库申请,一两个申请无所谓,然而如果从-∞到-1 有限的高频率的申请,就会给线上造成很大的压力。 针对问题1的解决方案 咱们能够通过程序来限度id的合法性,判断id<1的状况都间接在接口层面拦挡,这个形式确实能够解决下面说的那种状况,然而咱们接下来往下看 问题2 比方当初数据库id的最大值为1000,咱们用比1000大的数字去申请 这种状况原理和问题1是一样的,这次咱们就没法通过参数判断来拦挡住申请了,所以咱们就得用接下来一种经典的形式,布隆过滤器 布隆过滤器其实就是一种比拟奇妙的概率型数据结构,它能够通知你某种货色肯定不存在或者可能存在。从而达到对脏数据过滤的成果。他存在的地位如图 其实对布隆过滤器比拟生疏的同学能够先想想,作为一个过滤器须要满足什么条件? 速度得快,得从内存查,如果从硬盘查的话还不如间接查数据库因为过滤器外面得存入数据库所有的数据,所以内存势必是比拟缓和的,所以内存要做到相对的节俭,说到节俭内存,大家应该很容易能想到 redis外面的setbit操作布隆过滤器的实现 写入过程 通过bit数组来标识数据比方id=10的数据,通过hash算法算进去后果为1把bit数组下表为1的地位的值标记为1查问过程 将id=10做hash运算,失去后果1看bit数组下表为1的数据标识为1,则阐明数据存在其实咱们看下面的算法是存在肯定的问题的 1:只有是hash运算,就会存在hash碰撞问题,比方id=10 和id=100可能通过hash运算之后后果都为1,那么id=10写入之后查问id=100是否存在会误判为id=100也存在 2:当bit数组满了之后,查问的错误率必定是百分之百,因为每个数据都存在 这些其实都是导致错误率的起因,错误率是不可能防止的,然而咱们能够缩小错误率,缩小错误率的办法有两个 1:加大bit数组的长度,对于bit数组的长度的减少是不必放心的,因为是bit操作,所以能够加到很大的值 2:减少hash函数的个数,hash函数的个数减少了,阐明标识一个数组须要的地位就会变多。这样会升高产生hash碰撞的概率。然而hash的函数也不是越多越好,须要参照数组的长度来定 hash错误率: 布隆算法说数据存在,那么理论有可能不存在 ...

October 26, 2020 · 1 min · jiezi

关于redis:学习总结之Redis持久化和内存优化策略

概述Redis属于NoSql,即非关系性数据库,采纳key-value的模式存值。采纳Redis作为缓存,能够缓解数据库的压力。因为Redis的运行环境在内存中,同时应用c语言编写,所以取数据的速度比拟快。同时因为在内存环境中,所以要思考两个问题。(1)内存环境个别断电即擦除,所以须要长久化策略。(2)Redis的存储数据的能力是无限的,内存数据满了,应该如何解决。 Redis的长久化策略1.RDB模式RDB是Redis默认的长久化策略。应用Redis,会主动生成一个RDB文件。RDB文件记录的是数据快照,占用空间小,长久化效率高。然而Redis是单线程的,记录快照会阻塞Redis的应用,所以它的默认设置是,900秒内,1次扭转,进行快照。300秒内,10次扭转,进行快照。60秒内,10000次扭转,进行快照。这就有失落数据的可能性。 2.AOF模式为了应答数据失落的可能性,Redis推出AOF模式,它默认是敞开的。它记录的是用户层操作行为,能够确保数据不失落。然而因为记录的是操作行为,文件占用内存大,并且复原数据的速度慢。 3.模式抉择1.如果对数据完整性要求较高,应用AOF模式2.如果谋求效率,容许大量失落数据,应用RDB模式3.如果想两者兼顾能够采纳集群的形式,主机和从机采纳不同的模式。 Redis内存优化策略1.默认策略noevictionRedis内存满了之后,默认是不进行任何解决。失常状况下都会批改redis.conf,批改默认策略。 2.LRU(Least Recently Used)LRU是最近起码应用,开启之后,当内存满了,会将最近起码应用的数据删除。Redis外面有两种可选策略,一种是针对快到期的数据,进行LRU算法(volatile-lru),一种是针对所有数据进行LRU算法(allkeys-lru) 3.LFU(Least Frequently Used)LFU是最近起码应用。它思考到有已经特地热门的数据,然而前期很少有人拜访的状况,所以是计数寄存器定时右移一位,呈指数递加。Redis外面有两种可选策略,一种是针对快到期的数据,进行LFU算法(volatile-lfu),一种是针对所有数据进行LFU算法(allkeys-lfu) 4.RandomRandom 是随机删除数据,这就有可能带来缓存击穿或者穿透问题,所以失常状况下是不会应用这种算法,和下面两种一样,Redis也提供了两种可选策略。一种快到期,一种针对所有数据 5.TTLTTL是将行将超时的数据删除,所以是只针对设定了超时工夫的数据。Redis也只提供了一种策略。

October 25, 2020 · 1 min · jiezi

关于redis:Redis

阐明:应用缓存能够无效的升高用户拜访物理设施的频次.疾速从内存中获取数据,之后返回给用户,同时须要保障内存中的数据就是数据库数据.思考:1.缓存的运行环境应该在内存中.(快)2.应用C语言开发缓存3.缓存应该应用什么样的数据结构呢--------K-V构造 个别采纳String类型居多 key必须惟一 . v:JSON格局4.内存环境断电即擦除,所以应该将内存数据长久化(执行写盘操作)5.如果没有保护内存的大小,则容易导致 内存数据溢出. 采纳LRU算法优化!!! redis介绍redis是一个key-value存储系统。和Memcached相似,它反对存储的value类型绝对更多,包含string(字符串)、list(链表)、set(汇合)、zset(sorted set --有序汇合)和hash(哈希类型)。这些数据类型都反对push/pop、add/remove及取交加并集和差集及更丰盛的操作,而且这些操作都是原子性的。在此基础上,redis反对各种不同形式的排序。与memcached一样,为了保障效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把批改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。 原子性阐明: Redis的操作是单过程单线程操作,所以没有线程并发性的平安问题. 采纳队列的形式一个一个操作. 用法缓存 数据库 消息中间件(因为反对链表构造,所以能够 装置redis只须要在redis根目录下顺次执行make和make install命令即可 配置正文ip绑定 #bind 127.0.0.1敞开保护模式 protected-mode no开启后盾启动 daemonize yes 命令1.启动命令: redis-server redis.conf2.检索命令: ps -ef | grep redis3.进入客户端: redis-cli -p 63794.敞开redis: kill -9 PID号 | redis-cli -p 6379 shutdown 根本办法jedis.setex("key",time,"val"):向redis插入规定超时工夫的数据jedis.setnx("key","val"):jedis里存在key则不变,不存在则批改jedis.set("key","val",new setParam().ex(time).nxjedis.multi():开启事务jedis.exec():提交事务jedis.discard():回滚事务 分布式锁机制同步锁只能解决tomcat外部问题,不能解决多态tomcat并发问题.思维:1.锁应该应用第三方操作 ,锁应该专用.2.准则:如果锁被人正在应用时,其余的用户不能操作.3.策略: 用户向redis中保留一个key,如果redis中有key示意有人正在应用这把锁 其余用户不容许操作.如果redis中没有key ,则示意我能够应用这把锁.4.危险: 如何解决死锁问题. 设定超时工夫. aop可通过aop来管制哪些办法应用缓存 Redis长久化策略RDB特点:1.RDB模式是redis的默认的长久化策略.2.RDB模式记录的是Redis 内存数据的快照. 最新的快照会笼罩之前的内容 所有RDB长久化文件占用空间更小 长久化的效率更高.3.RDB模式因为是定期长久化 所以可能导致数据的失落. 命令: save 要求立刻马上长久化 同步的操作 其余的redis操作会陷入阻塞的状态.bgsave 开启后盾运行 异步的操作 因为是异步操作,所以无奈保障rdb文件肯定是最新的须要期待.AOF特点:1.AOF模式默认条件下是敞开的,须要用户手动的开启 ...

October 21, 2020 · 1 min · jiezi

关于redis:redis-哨兵进阶

redis - 哨兵(高可用)提了哨兵的作用以及简略配置,这边就讲一讲他的原理。 哨兵是怎么发现其余哨兵的哨兵与哨兵之间,是须要晓得其余哨兵的衰弱状况以及信息的分享,咱们在后面的配置中,并没有看到其余哨兵地址的配置,只配置了master的地址。哨兵通过redis的Pub/Sub性能,发现也监督的master/slave的其余哨兵。哨兵会往名字为__sentinel__:hello的Channel里发送hello,此时其余的哨兵就能够接管到音讯,并晓得其余哨兵的存在。 每隔两秒,会向__sentinel__:hello的Channel发送音讯,内容包含ip, port, runid。Hello音讯还包含主服务器的残缺以后配置。如果接管哨兵为给定的主服务器配置了比接管到的更老的配置,它会立刻更新到新的配置。每个哨兵会监听_sentinel__:hello的Channel的音讯,当他发现其余哨兵的信息时,就会被退出哨兵群里。退出哨兵群里之前,他会判断runid是否一样,或者ip+port是否一样,如果一样,旧的信息降被移除,用新的信息代替。哨兵是怎么晓得master挂了每个哨兵,都会给每个节点发送ping申请(上面只画了master,slave也会发送ping申请),如果节点在down-after-milliseconds(sentinel的配置,比方30秒)内没有响应sentinel-1的ping申请,则咱们认为他是被动下线了(SDOWN),如果sentinel-2也发现他没有响应ping申请了,此时两个sentinel大于等于quorum(此时是2),则咱们认为他是主观下线了(ODOWN)。也能够这么了解,比方路人甲认为路人乙很坏,可能是因为他跟路人乙有矛盾,主观的认为他坏,其他人可能并不认可他的想法,所以是主观下线。然而路人丙路人丁等少数人感觉路人很坏,那很可能路人乙真的坏,大家的评估是主观的,所以是主观下线。那咱们以什么判断是达到了ODOWN的条件呢,就是咱们之前配置的Quorum,然而是否进行故障转移,也要思考到majority,也就是数量要达到majority才运行受权做故障转移。比方有5个哨兵,Quorum设置为2,majority设置为3,此时master没响应哨兵的数量为2,曾经达到ODOWN的条件了,还是不能做故障转移,只有达到3的时候,才能够做故障转移。如果Quorum设置为3,majority设置为2,此时master没响应哨兵的数量为2,尽管能够受权做故障转移,然而并没有达到ODOWN的条件,还是不能做故障转移。 哨兵是怎么选举master的当master被认为是ODOWN的时候,哨兵会通过以下几个选举一个slave为master: 从master断开的工夫slave的优先级offset 的值Run ID如果slave和master的断开工夫超过down-after-milliseconds的10倍+master的SDOWN的时长,则不思考切换为master,公示如下: (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_stateslave被选举为master的程序如下: redis.conf配置的优先级,replica-priority越低优先级越高。优先级一样,看offset,offset越大,阐明对master的同步数据越多。看offset也一样,则抉择runid最小的,抉择最小的是为了在抉择的时候有更多的确定性,而不是随机选一个。确定了哪个slave降级为master,那哨兵也是要从哨兵中选举一个来做这些操作的。 对应的slave执行slaveof no one,把slave的role移除,变为新的master。告诉其余的slav指向新的master。告诉应用程序指向新的master。出故障的原master重启后,成为新master的slave节点。

October 21, 2020 · 1 min · jiezi

关于redis:redis高可用模型

本文次要针对Redis常见的几种应用形式及其优缺点开展剖析。 一、常见应用形式 Redis的几种常见应用形式包含: • Redis单正本;• Redis多正本(主从);• Redis Sentinel(哨兵);• Redis Cluster; • Redis自研。 二、各种应用形式的优缺点 1、Redis单正本 Redis单正本,采纳单个Redis节点部署架构,没有备用节点实时同步数据,不提供数据长久化和备份策略,实用于数据可靠性要求不高的纯缓存业务场景。 长处: • 架构简略,部署不便;• 高性价比:缓存应用时无需备用节点(单实例可用性能够用supervisor或crontab保障),当然为了满足业务的高可用性,也能够就义一个备用节点,但同时刻只有一个实例对外提供服务;• 高性能。 毛病: • 不保证数据的可靠性;• 在缓存应用,过程重启后,数据失落,即便有备用的节点解决高可用性,然而依然不能解决缓存预热问题,因而不适用于数据可靠性要求高的业务; • 高性能受限于单核CPU的解决能力(Redis是单线程机制),CPU为次要瓶颈,所以适宜操作命令简略,排序、计算较少的场景。也能够思考用Memcached代替。 2、Redis多正本(主从) Redis多正本,采纳主从(replication)部署构造,相较于单正本而言最大的特点就是主从实例间数据实时同步,并且提供数据长久化和备份策略。主从实例部署在不同的物理服务器上,依据公司的根底环境配置,能够实现同时对外提供服务和读写拆散策略。 长处: • 高可靠性:一方面,采纳双机主备架构,可能在主库呈现故障时主动进行主备切换,从库晋升为主库提供服务,保障服务安稳运行;另一方面,开启数据长久化性能和配置正当的备份策略,能无效的解决数据误操作和数据异样失落的问题;• 读写拆散策略:从节点能够扩大主库节点的读能力,有效应对大并发量的读操作。 毛病: • 故障复原简单,如果没有RedisHA零碎(须要开发),当主库节点呈现故障时,须要手动将一个从节点降职为主节点,同时须要告诉业务方变更配置,并且须要让其它从库节点去复制新主库节点,整个过程须要人为干涉,比拟繁琐;• 主库的写能力受到单机的限度,能够思考分片;• 主库的存储能力受到单机的限度,能够思考Pika; • 原生复制的弊病在晚期的版本中也会比较突出,如:Redis复制中断后,Slave会发动psync,此时如果同步不胜利,则会进行全量同步,主库执行全量备份的同时可能会造成毫秒或秒级的卡顿;又因为COW机制,导致极其状况下的主库内存溢出,程序异样退出或宕机;主库节点生成备份文件导致服务器磁盘IO和CPU(压缩)资源耗费;发送数GB大小的备份文件导致服务器进口带宽暴增,阻塞申请,倡议降级到最新版本。 3、Redis Sentinel(哨兵) Redis Sentinel是社区版本推出的原生高可用解决方案,其部署架构次要包含两局部:Redis Sentinel集群和Redis数据集群。 其中Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,能够实现故障发现、故障主动转移、配置核心和客户端告诉。Redis Sentinel的节点数量要满足2n+1(n>=1)的奇数个。 长处: • Redis Sentinel集群部署简略;• 可能解决Redis主从模式下的高可用切换问题;• 很不便实现Redis数据节点的线形扩大,轻松冲破Redis本身单线程瓶颈,可极大满足Redis大容量或高性能的业务需要;• 能够实现一套Sentinel监控一组Redis数据节点或多组数据节点。 毛病: • 部署绝对Redis主从模式要简单一些,原理了解更繁琐;• 资源节约,Redis数据节点中slave节点作为备份节点不提供服务;• Redis Sentinel次要是针对Redis数据节点中的主节点的高可用切换,对Redis的数据节点做失败断定分为主观下线和主观下线两种,对于Redis的从节点有对节点做主观下线操作,并不执行故障转移。• 不能解决读写拆散问题,实现起来绝对简单。 倡议: • 如果监控同一业务,能够抉择一套Sentinel集群监控多组Redis数据节点的计划,反之抉择一套Sentinel监控一组Redis数据节点的计划。• sentinel monitor <master-name> <ip> <port> <quorum> 配置中的<quorum>倡议设置成Sentinel节点的一半加1,当Sentinel部署在多个IDC的时候,单个IDC部署的Sentinel数量不倡议超过(Sentinel数量 – quorum)。 ...

October 20, 2020 · 1 min · jiezi

关于redis:redis-哨兵数据丢失的处理

redis - 哨兵(高可用)中提过,Sentinel并不保证数据的零失落,然而他也提供了能让数据最小失落的办法。第一个数据失落的场景,如下图,数据失落的时候,是因为redis的复制是异步的,也就是说他是ap模型的,ap模仿绝对于cp模型来说,性能是更好的,然而数据的一致性就没方法保障了。所以客户端在master写入数据后就正确返回,master再异步把数据同步给slave。第二个数据失落的场景,如下图,呈现了脑裂,尽管master是失常的,然而Sentinel集群却无奈和他通信,此时就会认为master宕机了而做故障转移,然而告诉客户端之前,客户端还是会始终写入数据到旧master,而旧master在网络解决后,重启连贯到新master,会删除旧的数据,导致这部分的数据失落。redis提供了以下两个配置来最大水平的缩小数据失落: min-replicas-to-write 1min-replicas-max-lag 10这个意思是至多有1个slave曾经有10秒没有同步,则master暂停接管申请。所以不会说master始终写入数据,而slave没有同步,如果产生以上两个场景,最多失落10秒的数据。从另外一方面说,10秒后升高了可用性,进步了数据的一致性,从ap模型临时的变成了cp模型。

October 19, 2020 · 1 min · jiezi

关于redis:redis-哨兵高可用

参考官网redis - 主从(高性能)中,提供了高性能,然而没方法提供高可用。比方master挂了,尽管slave能够提供查问,然而不能提供写入服务,绝对于不可用了。尽管能够把slave通过slaveof no one命令变成master,然而手动还是不太不便。redis能够应用sentinel主动实现故障发现和转移,并提供了以下性能: 监控:监控master和slave是否失常工作。告诉:当发现某个redis异样时,能够告诉应用程序或者系统管理员。故障转移:当master出现异常时,sentinel会选举一个新的master,并让其余slave指向新的master地址,同时告诉应用程序应用新的master地址。配置核心:产生故障转移时,告诉新master地址。为了sentinel的健壮性,sentinel也是分布式的,多个sentinel协同工作的劣势如下: master是否失常工作,由大部分的sentinel决定的,缩小误判的概率。局部sentinel的异样,并不障碍整体的性能。部署案例在部署之前,咱们须要理解一下根本信息: 为了保障Sentinel服务的健壮性,至多须要三个Sentinel实例。三个示例应该放在不同的虚拟机或物理机上。因为redis的异步复制,所以Sentinel不保证数据的零失落。两个实例+----+ +----+| M1 |---------| R1 || S1 | | S2 |+----+ +----+配置: quorum = 1这个示例中,一个服务器部署了Mater和Sentinel,一个服务器部署了slave和Sentinel。此时,如果M1挂了,S1和S2只有有一个认为M1挂了,就会选举一个Sentinel让slave降级为master,然而如果M1所在的服务器异样了,则只剩下S2,此时1小于majority(2)是无奈做故障转移的。 三个实例 +----+ | M1 | | S1 | +----+ |+----+ | +----+| R2 |----+----| R3 || S2 | | S3 |+----+ +----+配置: quorum = 2这个示例中,如果M1所在服务器挂了,此时S2和S3只有有一个认为M1挂了,就就会选举一个Sentinel做故障转移,此时2个Sentinel大于等于majority(2)是能够做故障转移的。以上两个是比拟典型的案例,当然官网还有跟客户端一个服务器的。至于为什么要一起部署,是因为咱们服务器的资源是贵重(要钱)的,一起部署既达到了服务的健壮性,也节约了资源。 简略示例首先是主从,在redis - 主从(高性能)中也说了如何配置,sentinel的配置也比较简单,如下: port 26379sentinel monitor mymaster ip port 2有三个配置就启动三个sentinel,整体流程图如下:如果监听多组集群,能够设置多个monitor,比方 port 26379sentinel monitor mymaster ip port 2sentinel monitor resque ip port 2原理局部前面章节解说 ...

October 19, 2020 · 1 min · jiezi

关于redis:redis分布式锁自动延长过期时间

背景项目组曾经有个分布式锁注解(参考前文《记一次分布式锁注解化》),然而在设置锁过期工夫时,须要去预估业务耗时工夫,如果锁的过期工夫能依据业务运行工夫主动调整,那应用的就更不便了。思路思路参考了redisson: 保留原先的可自定义设置过期工夫,只有在没有设置过期工夫(过期工夫为默认值0)的状况下,才会启动主动缩短。申请锁时,设置一个缩短过期工夫,定时每隔缩短过期工夫的三分之一工夫就从新设置过期工夫(期间工夫值为缩短过期工夫)。为了避免某次业务因为异样而呈现工作继续很久,从而长时间占有了锁,增加最大延期次数参数。加锁用一个Map来存储须要续期的工作信息。在加锁胜利之后将工作信息放入Map,并启动提早工作,提早工作在执行延期动作前先查看下Map里锁数据是不是还是被当前任务持有。每次续期工作实现并且胜利之后,就再次启动提早工作。申请锁复用之前的加锁办法,把缩短过期工夫作为加锁过期工夫。 public Lock acquireAndRenew(String lockKey, String lockValue, int lockWatchdogTimeout) { return acquireAndRenew(lockKey, lockValue, lockWatchdogTimeout, 0);}public Lock acquireAndRenew(String lockKey, String lockValue, int lockWatchdogTimeout, int maxRenewTimes) { if (lockKey == null || lockValue == null || lockWatchdogTimeout <= 0) { return new Lock(this).setSuccess(false).setMessage("illegal argument!"); } Lock lock = acquire(lockKey, lockValue, lockWatchdogTimeout); if (!lock.isSuccess()) { return lock; } expirationRenewalMap.put(lockKey, new RenewLockInfo(lock)); scheduleExpirationRenewal(lockKey, lockValue, lockWatchdogTimeout, maxRenewTimes, new AtomicInteger()); return lock;}定时续期以后锁还未被开释(Map里还有数据),并且以后延期工作执行胜利,则持续下一次工作。 ...

October 19, 2020 · 2 min · jiezi

关于redis:第七阶段-第二模块

October 18, 2020 · 0 min · jiezi

关于redis:SpringBoot-DB-系列Redis-高级特性之-Bitmap-使用姿势及应用场景介绍

【SpringBoot DB 系列】Redis 高级个性之 Bitmap 应用姿态及利用场景介绍后面介绍过 redis 的五种根本数据结构,如 String,List, Set, ZSet, Hash,这些属于绝对常见了;在这些根本后果之上,redis 还提供了一些更高级的性能,如 geo, bitmap, hyperloglog,pub/sub,本文将次要介绍 Bitmap 的应用姿态以及其实用场景,次要知识点包含 bitmap 根本应用日活统计利用场景中 bitmap 应用姿态点赞去重利用场景中 bitmap 应用姿态布隆过滤器 bloomfilter 基本原理及体验 case<!-- more --> I. 根本应用1. 配置咱们应用 SpringBoot 2.2.1.RELEASE来搭建我的项目环境,间接在pom.xml中增加 redis 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>如果咱们的 redis 是默认配置,则能够不额定增加任何配置;也能够间接在application.yml配置中,如下 spring: redis: host: 127.0.0.1 port: 6379 password:2. 应用姿态bitmap 次要就三个操作命令,setbit,getbit以及 bitcount a. 设置标记即setbit,次要是指将某个索引,设置为 1(设置 0 示意抹去标记),根本语法如下 # 请留神这个index必须是数字,前面的value必须是0/1setbit key index 0/1对应的 SpringBoot 中,借助 RestTemplate 能够比拟容易的实现,通常有两种写法,都能够 ...

October 17, 2020 · 3 min · jiezi

关于redis:如何用redis打造一个AB测试系统

如何用redis打造一个AB测试零碎https://github.com/lloydzhou/redisab 总体设计数据长久化到redis外面大量应用redis-lua脚本(根本每一个接口都是应用redis-lua脚本实现,晋升性能的同时,能利用redis-lua脚本的原子性达到相似事务的成果)设计一个测试环境和正式环境,通过"X-Env"辨认,数据通过redis db离开存储通过"X-User-Id"传递user_id设计一个控制台治理流量层和试验(登录应用http auth_basic)murmurhash2 local ffi = require "ffi" ffi.cdef[[ typedef unsigned char u_char; uint32_t ngx_murmur_hash2(u_char *data, size_t len); ]] murmurhash2 = function(value) return tonumber(ffi.C.ngx_murmur_hash2(ffi.cast('uint8_t *', value), #value)) end启动我的项目Makefile中曾经将各个工作综合起来,独自运行一下make命令就能够实现整个流程make首先build docker镜像而后编译控制台页面应用docker-compose启动我的项目运行Makefile中的init-redis将redis-lua脚本load进去接口获取变量接口curl "localhost:8011/ab/var?name=var1" -H 'X-Env: dev' -H 'X-User-Id: 0001'--> 返回以后user_id(0001)在这个试验中调配的变量: 类型是数字,值为2{"value":"2","msg":"success","layer":"layer1","code":0,"hash":838060847,"test":"test1","type":"number"}回传指标接口上传名字为target1的指标,代表user_id=0001这个用户在以后试验版本下产生转化的指标curl "localhost:8011/ab/track?name=var1" -H 'X-Env: dev' -H 'X-User-Id: 0001' -d '{"target1": 1}'--> {"msg":"success","code":0}demo创立一个名字叫layer1的流量层 在这个流量曾创立一个名字叫test1的试验给这个试验创立两个版本(value=0/1)别离调配50%的流量给这个试验创立一个叫做target1的指标

October 16, 2020 · 1 min · jiezi

关于redis:Redis集群

因为内存大小的限度,应用一台 Redis 实例显然无奈满足需要,这时就须要应用多台 Redis作为缓存数据库。然而如何保证数据存储的一致性呢,这时就须要搭建redis集群.采纳redis集群,能够保证数据扩散存储,同时保证数据存储的一致性.并且在外部实现高可用的机制.实现了服务故障的主动迁徙.集群搭建1.在Redis里筹备集群文件夹Mkdir cluster 2.在cluster文件夹中别离创立7000-7005文件夹 复制配置文件阐明: 将redis根目录中的redis.conf文件复制到cluster/7000/ 并以原名保留 cp redis.conf cluster/7000/ 编辑配置文件 复制批改后的配置文件 批改端口阐明:别离将7001-7005文件中的7000改为对应的端口号的名称(须要改3处) 通过脚本编辑启动/敞开指令创立启动脚本vim start.sh 创立敞开的脚本vim shutdown.sh 启动脚本sh start.sh 查看脚本是否启动失常 创立Redis集群5.0版本执行redis-cli --cluster create --cluster-replicas 1 192.168.126.129:7000 192.168.126.129:7001 192.168.126.129:7002 192.168.126.129:7003 192.168.126.129:7004 192.168.126.129:7005

October 15, 2020 · 1 min · jiezi

关于redis:Redis分片机制-SpringBoot整合分片机制-主从挂载-哨兵机制

Redis分片机制三台Redis当做一台Redis来用,就叫做分片机制如果须要存储海量的内存数据,如果只应用一台Redis,无奈保障Redis工作的效率,大量的工夫都节约到了寻址当中,能够采纳分片机制 分片的搭建创立新的文件夹在文件夹中创立3个配置文件更改端口号(三个端口号改成不一样)启动3个Redis服务器 留神阐明目前当启动多台服务器 多台之间是互不影响的,各自都是独立的实体如果将分片通过程序的形式进行操作,要把3台Redis当做一台来用 分片入门案例/** * 测试Redis分片机制 */@Testpublic void testShards(){ List<JedisShardInfo>shards=new ArrayList<>(); shards.add(new JedisShardInfo("192.168.126.129",6379)); shards.add(new JedisShardInfo("192.168.126.129",6380)); shards.add(new JedisShardInfo("192.168.126.129",6381)); ShardedJedis shardedJedis = new ShardedJedis(shards); shardedJedis.set("shards","分片机制"); System.out.println(shardedJedis.get("shards"));}只存储到了其中一台 一致性hash算法是一种非凡的算法,目标是在移除或增加一个服务器时,可能更小地扭转已存在的服务申请与解决申请服务器之间的映射关系 个别的hash是多少位的多少进制数?8位16进制数 如果对雷同的数据进行hash运算 问后果是否雷同?后果雷同 一个数据1M 与数据 1G的hash运算的速度是否雷同?雷同(任何数据用hash计算,不以它的量为规范,计算的速度简直都是雷同的) 个性1-平衡性概念:平衡性是指hash的后果应该平均分配到各个节点,这样从算法上解决了负载平衡问题 [4] 。(大抵均匀)问题形容: 因为节点都是通过hash形式进行合计.所以可能呈现如图中的景象.,导致负载重大不均衡解决办法: 引入虚构节点 个性2-枯燥性特点: 枯燥性是指在新增或者删减节点时,不影响零碎失常运行 个性3-分散性谚语: 鸡蛋不要放到一个篮子里.③分散性是指数据应该扩散地寄存在分布式集群中的各个节点(节点本人能够有备份),不用每个节点都存储所有的数据 SpringBoot整合Redis分片编写配置文件 @Configuration@PropertySource("classpath:/properties/redis.properties")public class JedisConfig {@Value("${redis.nodes}")private String nodes; @Bean public ShardedJedis shardedJedis(){ nodes=nodes.trim();//去除多余空格 List<JedisShardInfo> shards=new ArrayList<>(); String[] strings = nodes.split(","); for (String str:strings ) { String host = str.split(":")[0]; Integer port= Integer.valueOf(str.split(":")[1]); JedisShardInfo info=new JedisShardInfo(host,port); shards.add(info); } return new ShardedJedis(shards); }}自定义注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface CacheFind { public String preKey();//用户标识key的前缀 public int seconds() default 0;//如果用户不写示意不须要超时,如果写了以用户为准}应用注解 ...

October 15, 2020 · 2 min · jiezi

关于redis:Redis主从复制与优化

简介: Redis主从复制与优化 Redis主从复制与优化 主从复制咱们关注主从复制之前,首先要思考单机有什么问题? 机器故障容量瓶颈QPS瓶颈这些都是单节点所遇到的问题,所以这个时候呈现了主从复制(一主一从,一主多从) 应用主从复制能够: 数据正本扩大读性能留神: 一个master能够有多个slave一个slave只有一个master数据流向是单向的,master到slave*主从复制的配置两种实现形式 slaveof命令两台机器:主节点:47.11.11.11 从节点 47.22.22.22 在从节点执行 slaveof 命令 47.22.22.22-6379 > slacefof 47.11.11.11 6379OK勾销复制: 47.22.22.22-6379 > slacefof no oneOK批改配置slaveof ip port //从节点ip + 端口slave-read-only yes //开启只做读的操作两种形式比拟 查看主从127.0.0.1:6379> info replication# Replicationrole:master //主节点 connected_slaves:0master_replid:1d43401335a5343b27b1638fc9843e3a593fc1a7master_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0 知识点 : 主节点 runID:每个redis节点启动后都会动态分配一个40位的十六进制字符串为运行ID。运行ID的次要作用是来惟一辨认redis节点,比方从节点保留主节点的运行ID辨认自已正在复制是哪个主节点。如果只应用ip+port的形式辨认主节点,那么主节点重启变更了整体数据集(如替换RDB/AOF文件),从节点再基于偏移量复制数据将是不平安的,因而当运行ID变动后从节点将做全量复制。能够在info server命令查看以后节点的运行ID。 须要留神的是redis敞开再启动,运行的id会随之变动。 全量复制和局部复制等全量复制用于首次复制或其它无奈进行局部复制的状况,将主节点中的所有数据都发送给从节点。当数据量过大的时候,会造成很大的网络开销。 redis2.8+ 全量复制流程 开销: bgsave工夫RDB文件网络传输从节点清空数据工夫从节点加载RDB工夫可能的AOF重写工夫局部复制用于解决在主从复制中因网络闪退等起因造成数据失落场景,当从节点再次连上主节点,如果条件容许,主节点会补发失落数据给从节点,因为补发的数据远远小于全量数据,能够无效防止全量复制的过高开销。但须要留神,如果网络中断工夫过长,造成主节点没有可能残缺地保留中断期间执行的写命令,则无奈进行局部复制,仍应用全量复制 。 流程: 复制偏移量:参加复制的主从节点都会保护本身复制偏移量,主节点在解决完写入命令操作后,会把命令的字节长度做累加记录,统计信息在info replication中的master_repl_offset指标中。从节点每秒钟上报本身的复制偏移量给主节点,因而主节点也会保留从节点的复制偏移量slave0:ip=192.168.1.3,port=6379,state=online,offset=116424,lag=0从节点在接管到主节点发送的命令后,也会累加记录本身的偏移量。统计信息在info replication中的slave_repl_offset中。复制积压缓冲区:复制积压缓冲区是保留在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连贯的从节点时被创立,这时主节点响应写命令时,岂但会把命令发给从节点,还会写入复制积压缓冲区。在命令流传阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中 的每个字节对应的复制偏移量(offset) 。因为复制积压缓冲区定长且先进先出,所以它保留的是主节点最近执行的写命令;工夫较早的写命令会被挤出缓冲区。*生产中常见问题读写拆散分流到从节点。主节点写数据,从节点读数据,可能遇到读问题 复制数据提早读到过期数据从节点故障主从配置不统一例如maxmemory 不统一 会导致 失落数据例如数据结构优化参数(例如hash-max-ziplist-entries):内存不统一躲避全量复制第一次全量复制的时候  - 第一次不可避免,尽量小节点 ,低峰解决节点 运行ID不匹配  - 故障转移,例如哨兵或者集群复制积压缓存区有余  - 增大复制缓存区配置rel_backlog_size ,网络加强躲避复制风暴单机器复制风暴(redis<4.0当master宕机重启,会导致该机器下所有slave同时产生复制。防止单机部署一套redis主从)====》主节点扩散多台机*最初的注意事项:在上述的过程的实现是从库不开启AOF长久化状况下,如果从库开启的AOF长久化,重启时候仍然应用全量复制。之前从master复制过去的数据并不会失落,只是不再同步之前master(如上图的6379节点)后续写入的数据slaveof 能够用来扭转其所属的master节点,即从新成为另一台master的slave,然而新的master首先就会把从节点的数据全副革除掉对于读写拆散延时: 读写拆散 ,master会一步将数据复制到slave,如果slave产生阻塞,则会提早master数据的写命令,造成数据不统一的问题。-------个别不思考这个问题读到过期数据:redis在删除key时有两种策略,一种是懈怠型策略,即只有当redis操作这个key时才会将key删除,第二种是定期采样key删除--------当key数据十分多时,采样速度比不上key生成速度会造成很多过期数据没有删除,因为redis个别都是在master节点(减少删除数据),slave查问到过期数据也不能删除。会导致slave读到过期数据(在redis3.2中曾经解决)举荐 redis 主从文章https://www.cnblogs.com/wdliu/p/9407179.html举荐 redis 全量复制与局部复制文章https://blog.csdn.net/gaobinzhan/article/details/106536326集体博客:[http://blog.yanxiaolong.cn/集体博客:http://blog.yanxiaolong.cn/) ...

October 15, 2020 · 1 min · jiezi

关于redis:Redis分布式锁

背景在多线程的环境下,为了保障一个代码在同一时间只能由一个线程拜访,Java中咱们个别应用synchronized关键字和ReetrantLock去保障,这是JVM外部锁,即本地锁。当初风行分布式架构,在分布式环境下,如何保障一个代码在不同节点、同一时间只能有一个线程拜访呢? 分布式锁介绍对于分布式场景,咱们能够应用分布式锁,它是管制分布式系统之间互斥访问共享资源的一种形式。若一个分布式系统没有分布式锁,当客户端发动一个申请时,那么多个服务有可能会进行并发操作,如果操作是插入数据,就会导致数据反复插入,对于某些不容许有多余数据的业务来说,这就会造成问题。而分布式锁就是为了解决这些问题,保障多个服务之间 互斥 的访问共享资源,抢到分布式锁的服务持续进行操作,其余服务不进行操作。如图所示:[image:7AD640BA-CBE5-4224-90CB-CB911A69B7C3-2984-000015C7B7AB0682/16a53749547937bb.png] 特点互斥性:同一时刻只能有一个线程持有锁可重入性:同一节点上的同一个线程如果活去了锁,之后可能再次获取锁锁超时:反对设置超时工夫,避免死锁高性能和高可用性:加锁和解锁须要高效,同时也须要高可用,避免分布式锁生效阻塞性和非阻塞性:可能及时从状态中被唤醒实现形式基于数据库基于redis基于zookeeper本文次要介绍基于redis如何实现分布式锁 redis的分布式锁实现加锁1.利用setnx+expire命令(谬误的做法)SETNX(SET IF NOT Exists):Setnx key value,将key设置为value,当键不存在时,能力胜利。胜利返回1,失败返回0。expire:用来设置超时工夫 public boolean tryLock(String key,String requset,int timeout) { Long result = jedis.setnx(key, requset); // result = 1时,设置胜利,否则设置失败 if (result == 1L) { return jedis.expire(key, timeout) == 1L; } else { return false; }}这么设置谬误的起因是,setnx和expire是离开的两步操作,不具备原子性,如果执行完第一条指令利用异样或者重启了。锁将无奈过期。一种改善计划是应用Lua脚本来保障原子性(蕴含setnx和expire两条指令) 2.应用Lua脚本(蕴含setnx和expire两条指令)代码如下: public boolean tryLock_with_lua(String key, String UniqueId, int seconds) { String lua_scripts = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" + "redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end"; List<String> keys = new ArrayList<>(); List<String> values = new ArrayList<>(); keys.add(key); values.add(UniqueId); values.add(String.valueOf(seconds)); Object result = jedis.eval(lua_scripts, keys, values); //判断是否胜利 return result.equals(1L);}3.应用set key value 【EX seconds]【PX milliseconds]【NX][XX] 命令Redis在2.6.12版本开始,为SET命令减少一系列选项: ...

October 15, 2020 · 4 min · jiezi

关于redis:redis源码之dict

大家都晓得redis默认是16个db,然而这些db底层的设计构造是什么样的呢?咱们来简略的看一下源码,重要的字段都有所正文 typedef struct redisDb { dict *dict; /* The keyspace for this DB 字典数据结构,十分重要*/ dict *expires; /* Timeout of keys with a timeout set 过期工夫*/ dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) list一些数据结构中用到的阻塞api*/ dict *ready_keys; /* Blocked keys that received a PUSH */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS 事务相干解决 */ int id; /* Database ID */ long long avg_ttl; /* Average TTL, just for stats */ unsigned long expires_cursor; /* Cursor of the active expire cycle. */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */} redisDb;redis中的所有kv都是寄存在dict中的,dict类型在redis中十分重要。 ...

October 15, 2020 · 2 min · jiezi

关于redis:redis的两种持久化的机制你真的了解么

redis提供了两种长久化的机制 RDB和AOF机制 RDB(redis Database):RDB保留某一个工夫点之前的快照数据。 AOF(Append-Only File):指所有的命令行记录以redis命令申请协定的格局齐全长久化存储保留为AOF文件 混合长久化(4.0版本当前):指进行AOF重写时子过程将以后工夫点的数据快照保留为RDB文件格式,而后将父过程累计命令保留为AOF格局。 RDB快照有两种触发形式 1:为通过配置参数,如下: 通过肯定的工夫周日内看,命令执行的个数,超过阈值立刻执行快照生成 save 900 1 //900秒内有1次更新save 300 10 //30秒内有10次更新save 60 10000 //60秒内有10000次更新2:手动执行bgsave/save,手动触发生成快照间接执行save会阻塞主过程,bgsave的话会fork一个子过程实现快照 然而redis在产生RDB长久化的过程中有几个问题须要思考 1.RDB快照过程中Redis是否会进行对外服务 2.如果不回进行服务,那如何解决新的申请 接下来咱们看redis的 RDB长久化的具体过程 1:主过程会fork一个子过程 2:子过程会共享一部分主过程的数据空间,并且把共享的数据置为read-only的状态,在这个过程中,子过程以rdb的协定来履行长久化 3:在长久化的过程中是防止不了有新的数据写入的,因为咱们有一部分的数据是共享的,两个过程同时领有一块数据,必定会导致数据不统一的问题,然而依赖于操作系统的fork机制,在批改的时候肯定是批改局部内存页的数据,这个时候会触发对应内存页的copyonwrite的操作,不会影响子过程完成长久化,长久化完结后,主过程会对子过程进行回收 RDB的文件格式 redis的rdb文件是一个十分紧凑的格局 结尾的REDIS是固定的一个格局,redis在读取长久化文件的时候发现不是以REDIS结尾的会报错 0006是RDB_VERSION以后RDB协定的版本 AUX_FIELD_KEY_VALUE_PAIRS是一些辅助的字段 data则为保留的数据,数据首先会抉择redis_db,db抉择之后就是键值对的数据,对应的键值对又会分为设置过过期工夫和未设置过期工夫的数据 RDB长久化的长处 1:二进制的数据十分紧凑,数据的复原速度十分快 2:在长久化的过程中,性能最大化,fork子过程来实现写操作,让主过程持续解决命令,应用独自子过程来进行长久化,保障了redis的高性能 RDB长久化的毛病 1:数据安全性低,RDB是距离一段时间进行长久化,如果长久化之间redis产生了故障,会产生数据失落 2:linux fork之后,kernel把父过程中所有的内存页权限都设置readonly,而后子过程的地址空间指向父过程。当父子过程都只读内存时,相安无事。当其中某个过程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异样终端(page-fault),陷入kernal的一个中断例程。中断例程中,kernel的copyonwrite机制就会把触发的异样页复制一份,于是父子过程各自持有独立的一份。如果这个时候有大量的写入操作,会产生大量的分页谬误(页异常中断page-fault),这样就得消耗不少性能在复制上。 AOF长久化执行流程 通过appendonly yes开启 Redis应用单线程响应命令,如果每次AOF文件命令都追加到磁盘,会极大的影响解决性能,所以Redis先写入aof缓冲区,依据用户配置的同步磁盘策略写入aof文件中,能够通过appendfsync参数配置同步策略:含意如下 appendfsync always #示意每次更新操作后手动调用fsync()将数据写入到磁盘appendfsync everysec #示意每秒同步一次(折中计划,默认值)appendfsync no #表述等操作系统进行数据缓存同步到磁盘(疾速响应客户端,不对AOF做数据同步,同步文件由操作系统负责,通常同步周期最长30S)AOF重写机制随着命令得一直写入AOF,文件会越来越大,为了解决这个问题Redis引入了AOF重写机制压缩文件体积。AOF文件重写是把Redis过程内的数据转化为写命令同步到新AOF文件的过程,AOF重写机制能够通过手动触发了主动触发 手动触发:bgreweuteaof命令 主动触发: auto-aof-rewrite-percentage 100 #示意以后AOF文件空间和上一次重写后AOF文件空间的比值(100%)auto-aof-rewrite-min-size 64mb #代表AOF重写时文件最小体积AOF的长处:数据安全,AOF长久化能够配置appendfsync属性,有always,每进行一次命令操作就记录到aof文件中一次。 AOF的毛病:数据集比拟大的时候,比RDB启动效率低 混合长久化能够通过aof-use-rdb-preamble yes开启 加载时,首先会辨认AOF文件是否以REDIS字符串结尾,如果是,就依照RDB格局加载,加载完RDB后持续按AOF格局加载残余局部。混合式长久化计划兼顾了RDB的速度,和AOF的安全性 关注我的技术公众号,每周都有优质技术文章推送。微信扫一扫下方二维码即可关注:

October 14, 2020 · 1 min · jiezi

关于redis:Redis-对过期数据的处理

Redis 对过期数据的解决 在 redis 中,对于曾经过期的数据,Redis 采纳两种策略来解决这些数据,别离是惰性删除和定期删除 惰性删除 惰性删除不会去被动删除数据,而是在拜访数据的时候,再查看以后键值是否过期,如果过期则执行删除并返回 null 给客户端,如果没有过期则返回失常信息给客户端。 它的长处是简略,不须要对过期的数据做额定的解决,只有在每次拜访的时候才会查看键值是否过期,毛病是删除过期键不及时,造成了肯定的空间节约。 源码 robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { robj *val; if (expireIfNeeded(db,key) == 1) { /* Key expired. If we are in the context of a master, expireIfNeeded() * returns 0 only when the key does not exist at all, so it's safe * to return NULL ASAP. */ if (server.masterhost == NULL) { server.stat_keyspace_misses++; notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id); return NULL; } /* However if we are in the context of a slave, expireIfNeeded() will * not really try to expire the key, it only returns information * about the "logical" status of the key: key expiring is up to the * master in order to have a consistent view of master's data set. * * However, if the command caller is not the master, and as additional * safety measure, the command invoked is a read-only command, we can * safely return NULL here, and provide a more consistent behavior * to clients accessign expired values in a read-only fashion, that * will say the key as non existing. * * Notably this covers GETs when slaves are used to scale reads. */ if (server.current_client && server.current_client != server.master && server.current_client->cmd && server.current_client->cmd->flags & CMD_READONLY) { server.stat_keyspace_misses++; notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id); return NULL; } } val = lookupKey(db,key,flags); if (val == NULL) { server.stat_keyspace_misses++; notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id); } else server.stat_keyspace_hits++; return val;}定期删除 ...

October 14, 2020 · 2 min · jiezi

关于redis:Redis-配置说明

reids默认条件下反对数据的长久化操作。当Redis中有数据时会定期将数据保留到磁盘中。当Redis服务器重启时,会依据配置文件读取指定的长久化文件 实现内存数据的复原 长久化形式RDB模式 特点: 1.RDB模式是Redis的默认的长久化策略 2.RDB模式记录的是Redis内存数据的快照,最新的快照会笼罩之前的内容 所有RDB长久化文件占用空间更小 长久化的效率更高 3.RDB模式因为是定期长久化 所以可能导致数据的失落 是周期性的命令: save 要求立刻马上长久化 同步的操作 其余的Redis操作会陷入阻塞的状态bgsave 开启后盾运行 异步的操作 因为是异步操作 所以无奈保障rdb文件是最新的,须要期待配置:1.长久化文件名称: 2.长久化文件地位dir ./ 相对路径的写法dir /usr/local/src/redis 绝对路径写法3.RDB模式长久化策略 AOF模式默认是敞开的,须要手动开启特点: 1.是异步的操作 记录的是用户的操作的过程 能够避免用户的数据失落 2.因为记录的是程序的运行状态,所以长久化文件绝对较大,复原数据的工夫长 须要人为的优化长久化文件 长久化操作的总结1.如果不容许数据失落 应用AOF形式2.如果谋求效率 运行大量数据失落 应用RDB模式3.如果既要保障效率 又要保证数据 则应该配置Redis的集群 主机应用RDB 从机应用AOF 对于Redis内存策略对于内存策略的阐明阐明:Redis数据的存储都在内存中.如果始终想内存中存储数据 必然会导致内存数据的溢出.解决形式: 尽可能为保留在redis中的数据增加超时工夫.利用算法优化旧的数据.LRU算法特点: 最好用的内存优化算法.LRU是Least Recently Used的缩写,即最近起码应用,是一种罕用的页面置换算法,抉择最近最久未应用的页面予以淘汰。该算法赋予每个页面一个拜访字段,用来记录一个页面自上次被拜访以来所经验的工夫 t,当须淘汰一个页面时,抉择现有页面中其 t 值最大的,即最近起码应用的页面予以淘汰。维度: 工夫 T LFU算法LFU(least frequently used (LFU) page-replacement algorithm)。即最不常常应用页置换算法,要求在页置换时置换援用计数最小的页,因为常常应用的页应该有一个较大的援用次数。然而有些页在开始时应用次数很多,但当前就不再应用,这类页将会长工夫留在内存中,因而能够将引用计数寄存器定时右移一位,造成指数衰减的均匀应用次数。维度: 应用次数 RANDOM算法随机删除数据 TTL算法把设定了超时工夫的数据将要移除的提前删除的算法. Redis内存数据优化1. volatile-lru 设定了超时工夫的数据采纳lru算法2. allkeys-lru 所有的数据采纳LRU算法3. volatile-lfu 设定了超时工夫的数据采纳lfu算法删除4. allkeys-lfu 所有数据采纳lfu算法删除5. volatile-random 设定超时工夫的数据采纳随机算法6. allkeys-random 所有数据的随机算法7. volatile-ttl 设定超时工夫的数据的TTL算法8. noeviction 如果内存溢出了 则报错返回. 不做任何操作. 默认值 ...

October 14, 2020 · 1 min · jiezi

关于redis:Redis拼多多面试官问我zset底层是如何实现的我反手就把跳表的数据结构画了出来

引言Redis因为其齐全基于内存、性能杰出,加上具备丰盛的数据类型,在电商环境中深受后端开发的青睐。其中有序汇合zset就是根本数据类型之一,并且每个member都带有score(可用于排序),因而很适宜在打赏日榜、近一周收益这类场景中使用。 数据结构初探有序汇合对象的编码能够是ziplist或者skiplist。同时满足以下条件时应用ziplist编码: 元素数量小于128个所有member的长度都小于64字节以上两个条件的上限值可通过zset-max-ziplist-entries和zset-max-ziplist-value来批改。 ziplist编码的有序汇合应用紧挨在一起的压缩列表节点来保留,第一个节点保留member,第二个保留score。ziplist内的汇合元素按score从小到大排序,score较小的排在表头地位。 skiplist编码的有序汇合底层是一个命名为zset的构造体,而一个zset构造同时蕴含一个字典和一个跳跃表。跳跃表按score从小到大保留所有汇合元素。而字典则保留着从member到score的映射,这样就能够用O(1)的复杂度来查找member对应的score值。尽管同时应用两种构造,但它们会通过指针来共享雷同元素的member和score,因而不会节约额定的内存。 深刻跳表skiplist在介绍跳表前,咱们先来回顾一下根本链表,并思考为何一步一步演化成跳表的数据结构。 在这样一个链表中,如果咱们要查找某个数据,那么须要从头开始一一进行比拟,直到找到蕴含数据的那个节点,或者找到第一个比给定数据大的节点为止(没找到)。也就是说,工夫复杂度为O(n)。同样,当咱们要插入新数据的时候,也要经验同样的查找过程,从而确定插入地位。 如果咱们每隔一个节点减少一个指针,让指针指向下下个节点,如下图: 当初当咱们想查找数据的时候,能够先沿着这个新链表进行查找。当碰到比待查数据大的节点时,再回到原来的链表中进行查找。比方,咱们想查找23,查找的门路是沿着下图中标红的指针所指向的方向进行的: 23首先和7比拟,再和19比拟,比它们都大,持续向后比拟。但23和26比拟的时候,比26要小,因而回到上面的链表(原链表),与22比拟。23比22要大,沿上面的指针持续向后和26比拟。23比26小,阐明待查数据23在原链表中不存在,而且它的插入地位应该在22和26之间。在这个查找过程中,因为新减少的指针,工夫复杂度不再是O(N)了,也即咱们不再须要与链表中每个节点一一进行比拟了。须要比拟的节点数大略只有原来的一半。 利用同样的形式,咱们能够在下层新产生的链表上,持续扩大指针,从而产生第三层链表。如下图: 在这个新的三层链表构造上,如果咱们还是查找23,那么沿着最上层链表首先要比拟的是19,发现23比19大,接下来咱们就晓得只须要到19的前面去持续查找,从而一下子跳过了19后面的所有节点。能够设想,当链表足够长的时候,这种多层链表的查找形式能让咱们跳过很多上层节点,大大放慢查找的速度。 skiplist正是受这种多层链表的想法的启发而设计进去的。实际上,依照下面生成链表的形式,下面每一层链表的节点个数,是上面一层的节点个数的一半,这样查找过程就十分相似于一个二分查找,使得查找的工夫复杂度能够升高到O(log n)。然而,这种办法在插入数据的时候有很大的问题。新插入一个节点之后,就会打乱高低相邻两层链表上节点个数严格的2:1的对应关系。如果要维持这种对应关系,就必须把新插入的节点前面的所有节点(也包含新插入的节点)从新进行调整,这会让工夫复杂度从新堕落成O(n)。删除数据也有同样的问题。 skiplist为了防止这一问题,它不要求高低相邻两层链表之间的节点个数有严格的对应关系,而是为每个节点随机出一个层数(level)。附源码: #define ZSKIPLIST_MAXLEVEL 32 #define ZSKIPLIST_P 0.25 int zslRandomLevel(void) { int level = 1; while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;}执行插入操作时计算随机数的过程,是一个很要害的过程,它对skiplist的统计个性有着很重要的影响。这并不是一个一般的遵从均匀分布的随机数,而是遵从肯定规定的: 首先,每个节点必定都有第1层指针(每个节点都在第1层链表里)。如果一个节点有第i层(i>=1)指针(即节点曾经在第1层到第i层链表中),那么它有第(i+1)层指针的概率为p。节点最大的层数不容许超过一个最大值,记为MaxLevel(Redis里是32)。比方,一个节点随机出的层数是3,那么就把它链入到第1层到第3层这三层链表中。为了表白分明,下图展现了如何通过一步步的插入操作从而造成一个skiplist的过程: 从下面skiplist的创立和插入过程能够看出,每一个节点的层数(level)是随机进去的,而且新插入一个节点不会影响其它节点的层数。因而,插入操作只须要批改插入节点前后的指针,而不须要对很多节点都进行调整。这就升高了插入操作的复杂度。实际上,这是skiplist的一个很重要的个性,这让它在插入性能上显著优于均衡树的计划。这在前面咱们还会提到。 skiplist,指的就是除了最上面第1层链表之外,它会产生若干层稠密的链表,这些链表外面的指针成心跳过了一些节点(而且越高层的链表跳过的节点越多)。这就使得咱们在查找数据的时候可能先在高层的链表中进行查找,而后逐层升高,最终降到第1层链表来准确地确定数据地位。在这个过程中,咱们跳过了一些节点,从而也就放慢了查找速度。 刚刚创立的这个skiplist总共蕴含4层链表,当初假如咱们在它外面仍然查找23,下图给出了查找门路: 须要留神的是,后面演示的各个节点的插入过程,实际上在插入之前也要先经验一个相似的查找过程,在确定插入地位后,再实现插入操作。 理论利用中的skiplist每个节点应该蕴含member和score两局部。后面的形容中咱们没有具体辨别member和score,但实际上列表中是依照score进行排序的,查找过程也是依据score在比拟。 为什么采纳跳表,而不应用哈希表或均衡树实现呢skiplist和各种均衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因而,在哈希表上只能做单个key的查找,不合适做范畴查找。所谓范畴查找,指的是查找那些大小在指定的两个值之间的所有节点。在做范畴查找的时候,均衡树比skiplist操作要简单。在均衡树上,咱们找到指定范畴的小值之后,还须要以中序遍历的程序持续寻找其它不超过大值的节点。如果不对均衡树进行肯定的革新,这里的中序遍历并不容易实现。而在skiplist上进行范畴查找就非常简单,只须要在找到小值之后,对第1层链表进行若干步的遍历就能够实现。均衡树的插入和删除操作可能引发子树的调整,逻辑简单,而skiplist的插入和删除只须要批改相邻节点的指针,操作简略又疾速。从内存占用上来说,skiplist比均衡树更灵便一些。一般来说,均衡树每个节点蕴含2个指针(别离指向左右子树),而skiplist每个节点蕴含的指针数目均匀为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么均匀每个节点蕴含1.33个指针,比均衡树更有劣势。

October 14, 2020 · 1 min · jiezi

关于redis:redis-主从高性能

当并发量达到肯定水平时,web利用无奈解决这么多申请,咱们能够用nginx做负载平衡,连贯多个web利用。当数据库无奈撑持的时候,咱们做主从架构,读写拆散,分担数据库压力。在redis中,也能够用主素来分担压力。 简略配置复制redis.windows.conf文件,批改端口,以及增加slaveof这行配置。slaveof用于指向主redis服务的ip和端口。 port 6380slaveof 127.0.0.1 6379先启动master,再启动slave,能够看到slave曾经同步了master的数据,在master的操作,也会同步到slave中。master的信息,能够看到他role是master,有个从服务器slave0,以及slave0的相干信息。 local:0>info replication"# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6380,state=online,offset=208,lag=1master_repl_offset:208repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:207"salve的信息,能够看到他的role是slave,以及master的相干信息。 local6380:0>info replication"# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:2master_sync_in_progress:0slave_repl_offset:194slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0"如果想让slave断开同步,则能够执行slaveof no one命令,再查看info replication时,发现他slave曾经变成了master。如果持续同步,则执行slaveof 127.0.0.1 6379,此时查看info replication,role还是slave。 原理如果master的rdb还没生成实现,此时如果有其余slave连进来,是能够间接应用这个rdb以及缓冲区的命令的,如果曾经生成完,则会从新生成rdb文件。尽管从库的数据能够作为备份,然而还是倡议开启主库的长久化。如果没有开启,当主库宕机重启后,主库的数据是空的,而从库就会把空的数据复制过去并清空本身的数据,则从库的数据也变成空的了。以上流程是第一次同步的时候,redis也反对断点续传。master中记录了backlog,master和redis都记录了对应的offset,master依据这个offer进行同步数据,如果没有对应的offset,只能全量复制了。 一主多从生成rdb时,是fork了一个子过程来解决,此时并不影响master接管客户端的命令,然而redis解决命令的效率会升高。如果咱们须要多个slave,会导致master的性能降落的重大,所以咱们能够采纳一主多从的形式来做。当从服务器1从master同步的时候,他会断开从服务器a、b、c的连贯,同步实现后,再把数据以master的模式同步给服务器a、b、c。

October 13, 2020 · 1 min · jiezi

关于redis:Redis简介-商品缓存案例

Redis介绍redis是一个key-value存储系统。它反对存储的value类型绝对更多,包含string(字符串)、list(链表)、set(汇合)、zset(sorted set --有序汇合)和hash(哈希类型)。这些数据类型都反对push/pop、add/remove及取交加并集和差集及更丰盛的操作,而且这些操作都是原子性的。原子性阐明: Redis的操作是单过程复线程操作,所以没有线程并发性的平安问题. 采纳队列的形式一个一个操作.Redis常见用法:1.Redis能够当做缓存应用2.Redis能够当做数据库应用 验证码3.Redis能够消息中间件应用 银行转账等 Redis装置1).解压 Redis安装包先下载https://redis.io/下载好压缩包移至LINUX服务器 cd /usr/local/src/解压 [root@localhost src]# tar -zxvf redis-5.0.4.tar.gz2). 装置Redis阐明:在Redis的根目录中执行命令命令: 1.make 2.make install ![image.png](/img/bVcHiTE)3). 批改Redis的配置文件命令1: 展示行号 :set nu批改地位1: 正文IP绑定批改地位2: 敞开保护模式批改地位3: 开启后盾启动 redis 服务器命令1.启动命令: redis-server redis.conf2.检索命令: ps -ef | grep redis3.进入客户端: redis-cli -p 63794.敞开redis: kill -9 PID号 | redis-cli -p 6379 shutdown Redis入门案例引入jar包文件 编辑测试APIpackage com.jt.test;import org.junit.jupiter.api.Test;import redis.clients.jedis.Jedis;public class TestRedis { /** * 1.测试redis程序链接是否失常 * 步骤: * 1.实例化jedis工具API对象(host:port) * 2.依据实例 操作redis 办法就是命令 * * 对于链接不通的阐明: * 1.查看Linux防火墙 * 2.查看Redis配置文件批改项 * 2.1 IP绑定 * 2.2 保护模式 * 2.3 后盾启动 * 3.查看redis启动形式 redis-server redis.conf * 4.查看IP 端口 及redis是否启动... * * */ @Test public void test01(){ String host = "192.168.126.129"; int port = 6379; Jedis jedis = new Jedis(host,port); jedis.set("cgb2006","好好学习 天天向上"); System.out.println(jedis.get("cgb2006")); //2.练习是否存在key if(jedis.exists("cgb2006")){ jedis.del("cgb2006"); }else{ jedis.set("cgb2006", "xxxx"); jedis.expire("cgb2006", 100); } } /* 2.需要: * 1.向Redis中插入数据 * 2.为key设定超时工夫 * 3.线程sleep 3秒 * 4.获取key的剩余时间 */ @Testpublic void test02() throws InterruptedException { Jedis jedis = new Jedis("192.168.126.129",6379); /* * 如果应用Redis 并且须要增加超时工夫时 个别须要满足原子性要求 * 要么同时胜利 要么同时失败 */ jedis.setex("宝可梦",60,"小火龙"); Thread.sleep(3000); System.out.println(jedis.ttl("宝可梦")); /* jedis.set("宝可梦","小火龙"); int a=1/0;//出现异常 程序不会往下执行 jedis.expire("宝可梦",60); Thread.sleep(3000); System.out.println(jedis.ttl("宝可梦"));*/ }@Testpublic void test03() throws InterruptedException { Jedis jedis = new Jedis("192.168.126.129", 6379); jedis.setnx("a","ab");//a存在不做批改 不存在则新建 /*if (jedis.exists("a")) { System.out.println("key曾经存在"); } else { jedis.set("a", "ac"); }*/ System.out.println(jedis.get("a")); }@Testpublic void test04() throws InterruptedException { Jedis jedis = new Jedis("192.168.126.129", 6379); SetParams setParams=new SetParams(); setParams.nx().ex(10);//加锁操作 并设置超时工夫 秒 String result=jedis.set("b","办法四",setParams); System.out.println(result);//加锁胜利会返回OK 反 //之null System.out.println(jedis.get("b")); Thread.sleep(3000); System.out.println(jedis.ttl("b")); jedis.del("b");//解锁操作 }@Testpublic void test05() throws InterruptedException { Jedis jedis = new Jedis("192.168.126.129", 6379); //开启事务 Transaction transaction=jedis.multi(); try{ transaction.set("aa","aa"); //提交事务 transaction.exec(); }catch (Exception e){ e.printStackTrace(); //回滚事务 transaction.discard(); }}秒杀业务逻辑 ...

October 13, 2020 · 3 min · jiezi

关于redis:Github-星标-8K-这款国人开源的-Redis-可视化管理工具真香

做程序员就少不了与一些工具打交道,比方:监控工具、管理工具等,有些工具是命令行界面,有些工具是可视化界面,反正都是能够可能满足日常应用的性能需要。 对于redis管理工具来说,也有不少可能的产品,比方: 1、Redis Desktop Manager2、RedisStudio3、phpRedisAdmin4、Go-Redis5、RedisClient有开源、收费的,也有商业免费的版本,基本上性能都还能够,不过开源、收费还是最大的需要点之一哈,老司机都懂得的。 所以,明天,民工哥就给大家安利一款由国人开源的Redis可视化管理工具,名字很有意思,叫:Another Redis Desktop Manager。 Github:https://github.com/qishibo/An... Another Redis Desktop Manager简述Another Redis Desktop Manager是一个更快,更好,更稳固的Redis桌面管理器,与Linux,Windows和Mac兼容。而且,加载大量密钥时,它不会解体。 Another Redis Desktop Manager装置这类的软件、工具装置都十分的简略,易操作。 1、Mac或Linux装置 # clone codegit clone https://github.com/qishibo/AnotherRedisDesktopManager.gitcd AnotherRedisDesktopManager# install dependenciesnpm install# if download electron failed during installing, use this command# ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" npm install# serve with hot reload at localhost:9988npm start# after the previous step is completed, open another tab, build up a desktop clientnpm run electron如果呈现上面的报错信息,官网也比拟贴心给出了解决方案。 # if error like this../src/FontManagerLinux.cc:1:35: fatal error: fontconfig/fontconfig.h: No such file or directory# then try thisyum install libfontconfig1-dev -y2、Windows装置 # install build tools for the first time, just execute oncenpm install -g windows-build-tools# clone codegit clone https://github.com/qishibo/AnotherRedisDesktopManager.gitcd AnotherRedisDesktopManager# install dependencies, 32-bit or 64-bit all use win32npm install --platform=win32# if download electron failed during installing, use this command# npm config set ELECTRON_MIRROR http://npm.taobao.org/mirrors/electron/# npm install --platform=win32# serve with hot reload at localhost:9988npm start# after the previous step is completed to 100%, open another tab, build up a desktop clientnpm run electron当然,WIN零碎你也能够间接下载.exe的软件包,双击运行装置更不便。 https://github.com/qishibo/An... Another Redis Desktop Manager性能展现1、主界面 反对:SSH、SSL、Cluster等几种模式。集群能够输出任意集群中的节点都能够自动识别进去。 2、操作界面 更多实用的性能,大家感兴趣的能够下载下来,亲自应用体验一下。

October 13, 2020 · 1 min · jiezi

关于redis:Redis-缓存性能实践及总结

一、前言在互联网利用中,缓存成为高并发架构的要害组件。这篇博客次要介绍缓存应用的典型场景、实操案例剖析、Redis应用标准及惯例 Redis 监控。 二、常见缓存比照常见的缓存计划,有本地缓存,包含HashMap/ConcurrentHashMap、Ehcache、Memcache、Guava Cache等,缓存中间件包含Redis、Tair等。 三、Redis应用场景1. 计数 Redis实现疾速计数及缓存性能。 例如:视频或直播在线观看人数,用户每播放一次,就会自增1。 2. Session集中管理 Session能够存储在应用服务是JVM中,但这一种计划会有一致性的问题,还有高并发下,会引发JVM内存溢出。Redis将用户的Session集中管理,这种状况下只有保障Redis的高可用和扩展性,每次用户更新或查问登录都间接从Redis中信息获取。 3.限速 例如:业务要求用户一分钟内,只能获取5次验证码。 4.排行榜 关系型数据库在排行榜方面查问速度广泛偏慢,所以能够借助redis的SortedSet进行热点数据的排序。 比方在我的项目中,如果须要统计主播的吸金排行榜,能够以主播的id作为member, 当天打赏的流动礼物对应的热度值作为 score, 通过zrangebyscore就能够获取主播流动日榜。 5.分布式锁 在理论的多过程并发场景下,应用分布式锁来限度程序的并发执行。多用于避免高并发场景下,缓存被击穿的可能。 分布式锁的理论就是"占坑",当另一个过程来执行setnx时,发现标识位曾经为1,只好放弃或者期待。 四、案例解析1、【案例】过期设置- set命令会去掉过期工夫Redis所有的数据结构,都能够设置过期工夫。如果一个字符串曾经设置了过期工夫,而后从新设置它,会导致过期工夫隐没。所以在我的项目中须要正当评估Redis容量,防止因为频繁set导致没有过期策略,间接导致内存被占满。 如下是 Redis 源码截图: 2、【案例】对于 Jedis 2.9.0 及以下版本过期设置 BUG发现Jedis在进行expiredAt命令调用时有bug,最终调用的是pexpire命令,这个bug会导致key过期工夫很长,导致Redis内存溢出等问题。倡议降级到Jedis 2.9.1及以上版本。 BinaryJedisCluster.java源码如下: @Override public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) { return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) { @Override public Long execute(Jedis connection) { return connection.pexpire(key, millisecondsTimestamp); //此处pexpire应该是pexpireAt } }.runBinary(key); }比照pexpire和pexpireAt: 比方咱们以后应用的工夫是2018-06-14 17:00:00,它的unix工夫戳为1528966800000毫秒,当咱们应用PEXPIREAT命令时,因为是过来的工夫,相应的key会立刻过期。 ...

October 12, 2020 · 2 min · jiezi

关于redis:Redis的线程模型和事务

1. 前言我本来只是想学习Redis的事务,但起初发现,Redis和传统关系型数据库的事务在ACID的体现上差别很大。而要想具体理解其中的原因,就离不开Redis独特的单线程模型,因而本文将二者分割在一起解说。 上面先会补充一些常识储备,包含解答几个常犯错的问题,剖析Redis的线程模型,为前面的章节打好根底。随后再解说Redis的事务实现,和关系型数据库的事务做比照,以及会附上springboot中实现事务的代码。 2. 常见问题2.1. 高并发不等于高并行咱们最多听到的就是并发,但实际上很多时候并不谨严,有些状况应该被定义为并行: 并发,是指在一个时间段内有多个过程在执行。只不过在人的角度看,因为这个计算机角度的工夫切实是太短暂了,人基本就感触不到是多个过程,看起来像是同时进行,这种是并发。并行,指的是在同一时刻有多个过程在同时执行。一个是时间段内产生的,一个是某一时刻产生的,如果是在只有单核CPU的状况下,是无奈实现并行的,因为同一时刻只能有一个过程被调度执行,如果此时同时要执行其余过程则必须上下文切换,这种只能称之为并发,而如果是多个CPU的状况下,就能够同时调度多个过程,这种就能够称之为并行。 2.2. 什么时候该用多线程咱们首先要明确,多线程不肯定比单线程快,因为多线程还波及到CPU上下文切换的耗费,和频繁创立、销毁线程的耗费 。那么多线程是为了优化什么而应用的呢?我所理解的有两点: 1.充分利用多核CPU的资源,实现并行因为多核cpu每一个外围都能够独立执行一个线程,所以多核cpu能够真正实现多线程的并行。但这点优化算不上什么,一台服务器上个别部署了很多的利用,哪有那么多闲暇的CPU外围闲暇着。 2.应答CPU的“阻塞”我认为这才是次要起因。“阻塞”包含网络io、磁盘io等这类io的阻塞,还包含一些执行很慢的逻辑操作等。例如:某个接口的办法中,依照执行程序分成A、B、C三个独立的局部。 如果每个局部执行的都很慢(如:查询数据库视图,将数据导出excel文件),都要10秒。那么办法执行实现,单线程要用30秒,多线程别离执行只须要10秒。优化了20秒,线程创立和CPU上下文切换的影响,和20秒比起来不算什么。 如果每个局部执行的都很快,都只须要10毫秒。依照下面的计算形式,实践上优化了20毫秒,可线程创立和CPU上下文切换的影响,可是要大于20毫秒的。 因而总体来说,多线程开发对于程序的优化,次要体现在应答导致CPU“阻塞”的点。 3. 线程模型Redis服务端通过单过程单线程,解决所有客户端的申请。Redis官网数据是说反对100000+ 的QPS(峰值工夫的每秒申请),很难置信这是靠单线程来撑持的。因而咱们要探索一下,Redis的线程模型为啥能反对它执行这么快? 3.1. 性能瓶颈官网示意,Redis是基于内存操作,CPU不是Redis的性能瓶颈,Redis的性能瓶颈是机器的内存和网络带宽。看到这句话,我有个纳闷,为啥 “Redis是基于内存操作,CPU不是Redis的性能瓶颈” ? 这就分割到第二章中“2.多线程不肯定快”的知识点了-- 在多线程开发对于程序的优化,次要体现在应答导致CPU“阻塞”的点。一般数据库的瓶颈在于磁盘io,可Redis是基于内存操作,没有磁盘io的瓶颈,而且基于Reactor模型,也没有网络io的阻塞。没有多线程的必要,CPU也就不是Redis的性能瓶颈。 另外Redis是将所有的数据全副放在内存中的,所有说应用单线程去操作执行效率就是最高的,多线程在执行过程中须要进行 CPU 的上下文切换,这个是耗时操作。对于内存零碎来说,如果没有上下文切换效率就是最高的,屡次读写都是在一个 CPU 上的,在内存状况下,这个就是最佳计划。 咱们能够了解成,因为Redis作为内存数据库,又有个很好的线程模型,并不存在io阻塞和CPU等性能瓶颈。再往后能够晋升Redis空间的,就在于机器的内存和网络带宽了。 3.2. 线程模型我之前的很多篇文章都提到了Reactor线程模型,像Tomcat、Netty等,都应用了Reactor线程模型来实现IO多路复用,这次再加上Redis。还记得之前有介绍Reactor模型有三种:单线程Reactor模型,多线程Reactor模型,主从Reactor模型。 通常来说,主从Reactor模型是最强壮的,Tomcat和Netty都是应用这种,然而 Redis是应用单线程Reactor模型。 上图形容了Redis工作的线程模型,模仿了服务端解决客户端命令的过程: 文件事件处理器应用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,行将套接字的fd注册到epoll上,当被监听的套接字筹备好执行连贯应答(accept)、读取(read)、写入(write)、敞开(close)等操作时,与操作绝对应的文件事件就会产生。只管多个文件事件可能会并发地呈现,但I/O多路复用程序总是会将所有产生事件的套接字都推到一个队列外面,而后通过这个队列,以有序(sequentially)、同步(synchronously)、每次一个套接字的形式向文件事件分派器传送套接字。此时文件事件处理器就会调用套接字之前关联好的事件处理器来解决这些事件。文件事件处理器以单线程形式运行,这就是之前始终提到的Redis线程模型中,效率很高的那个单线程。值得注意的是,在执行命令阶段,因为Redis是单线程来解决命令的,所有每一条达到服务端的命令不会立即执行,所有的命令都会进入一个队列中,而后一一被执行。并且多个客户端发送的命令的执行程序是不确定的。然而能够确定的是,不会有两条命令被同时执行,不会产生并行问题,这也是前面咱们探讨Redis事务的根底。 3.3. 剖析为什么不怕Reactor单线程模型的弊病?咱们回顾之前的文章,Reactor单线程模型的最大毛病在于:Acceptor和Handlers都共用一个线程,只有某个环节产生阻塞,就会阻塞所有。整个尤其是Handlers是执行业务办法的,最容易产生阻塞,像Tomcat就默认应用200容量大线程池来执行。那Redis为什么就不怕呢? 起因就在于Redis作为内存数据库,它的Handlers是可预知的,不会呈现像Tomcat那样的自定义业务办法。不过也倡议不要在Reids中执行要占用大量工夫的命令。 总结:Redis单线程效率高的起因纯内存拜访:数据寄存在内存中,内存的响应工夫大概是100纳秒,这是Redis每秒万亿级别拜访的重要根底。非阻塞I/O:Redis采纳epoll做为I/O多路复用技术的实现,再加上Redis本身的事件处理模型将epoll中的连贯,读写,敞开都转换为了工夫,不在I/O上节约过多的工夫。单线程防止了线程切换和竞态产生的耗费。4. 事务后面说过,因为Redis单线程的个性,所有的命令都是进入一个队列中,顺次执行。因而不会有两条命令被同时执行,不会产生并行问题。这点和传统关系型数据库不一样,没有并行问题,也就没有像表锁、行锁这类锁竞争的问题了。 4.1. 概念那么Redis的事务是为了解决什么状况?假如,客户端A提交的命令有A1、A2和A3 这三条,客户端B提交的命令有B1、B2和B3,在进入服务端队列后的程序实际上很大部分是随机。假如是:A1、B1、B3、A3、B2、A2,可客户端A冀望本人提交的是依照程序一起执行的,它就能够应用事务实现:B2、A1、A2、A3、B1、B3,客户端B的命令执行程序还是随机的,然而客户端A的命令执行程序就保障了。 Redis 事务的实质是一组命令的汇合。事务反对一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会依照程序串行化执行队列中的命令,其余客户端提交的命令申请不会插入到事务执行命令序列中。 总结说:redis事务就是一次性、程序性、排他性的执行一个队列中的一系列命令。 Redis事务相干命令:watch key1 key2 ... : 监督一或多个key,如果在事务执行之前,被监督的key被其余命令改变,则事务被打断 ( 相似乐观锁 )multi : 标记一个事务块的开始( queued )exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被勾销掉 ) discard : 勾销事务,放弃事务块中的所有命令unwatch : 勾销watch对所有key的监控事务执行过程multi命令能够将执行该命令的客户端从非事务状态切换至事务状态,执行后,后续的一般命令(非multi、watch、exec、discard的命令)都会被放在一个事务队列中,而后向客户端返回QUEUED回复。 ...

October 11, 2020 · 2 min · jiezi

关于redis:从零手写缓存框架14redis渐进式rehash详

redis 的 rehash 设计本文思维导图如下: HashMap 的 rehash 回顾读过 HashMap 源码的同学,应该都晓得 map 在扩容的时候,有一个 rehash 的过程。 没有读过也没有关系,能够花工夫浏览下 从零开始手写 redis(13) HashMap源码详解 简略理解下整个过程即可。 HashMap 的扩容简介这里简略介绍下: 扩容(resize)就是从新计算容量,向HashMap对象里不停的增加元素,而HashMap对象外部的数组无奈装载更多的元素时,对象就须要扩充数组的长度,以便能装入更多的元素。 当然Java里的数组是无奈主动扩容的,办法是应用一个新的数组代替已有的容量小的数组,就像咱们用一个小桶装水,如果想装更多的水,就得换大水桶。 redis 中的扩容设计HashMap 的扩容须要对汇合中大部分的元素进行从新计算,然而对于 redis 这种企业级利用,特地是单线程的利用,如果像传统的 rehash 一样把所有元素来一遍的话,预计要十几秒的工夫。 十几秒对于常见的金融、电商等绝对高并发的业务场景,是无法忍受的。 那么 redis 的 rehash 是如何实现的呢? 实际上 redis 的 rehash 动作并不是一次性、集中式地实现的, 而是分屡次、渐进式地实现的。 这里补充一点,不单单是扩容,缩容也是一样的情理,二者都须要进行 rehash。 只增不降就是对内存的节约,节约就是立功,特地是内存还这么贵。 ps: 这种思维和 key 淘汰有殊途同归之妙,一口吃不了一个大瘦子,一次搞不定,那就 1024 次,慢慢来总能解决问题。 Redis 的渐进式 rehash这部分间接选自经典入门书籍《Redis 设计与实现》 为什么要渐进式解决?实际上 redis 外部有两个 hashtable,咱们称之为 ht[0] 和 ht[1]。传统的 HashMap 中只有一个。 为了防止 rehash 对服务器性能造成影响, 服务器不是一次性将 ht[0] 外面的所有键值对全副 rehash 到 ht[1] , 而是分屡次、渐进式地将 ht[0] 外面的键值对缓缓地 rehash 到 ht[1] 。 ...

October 11, 2020 · 3 min · jiezi

关于redis:缓存雪崩缓存击穿缓存穿透你真的都懂了么

前言Hello,everybody,我是asong,明天与大家一起来聊一聊面试中几个常见的缓存问题。为什么会忽然想做一篇这个文章呢,明天翻了一下我当初筹备面试时整顿的一些材料,发现缓存在面试中占比还是很高的,当初为了面试也是背了良久的,不过因为都是背的,当初也有点忘了,明天就想着好好整顿一下这一部分,好好记录一下。因为本人能力无限,这一篇主讲通俗易懂,不波及太难的缓存应用场景。好啦,咱们开始吧。缓存利用缓存在咱们平时的我的项目中多多少少都会应用到,缓存应用的应用场景还是比拟多的,缓存是分布式系统中的重要组件,次要解决高并发、大数据场景下,热点数据拜访的性能问题。进步性能的数据快速访问。一提到缓存,这些是咱们都能想到的一些缓存利用场景,然而咱们是不太分明缓存的实质思维是什么的。缓存的根本思维就是咱们十分相熟的空间换工夫。缓存也并不是那么的高大上,尽管他能够为零碎的性能进行晋升。缓存的思维理论在操作系统或者其余中央都被大量用到。 比方 CPU Cache 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题。 再比方操作系统在 页表计划 根底之上引入了 快表 来减速虚拟地址到物理地址的转换。咱们能够把快表了解为一种非凡的高速缓冲存储器(Cache)。 下面简略介绍了缓存的根本思维,当初回到业务零碎来说:咱们为了防止用户在申请数据的时候获取速度过于迟缓,所以咱们在数据库之上减少了缓存这一层来补救。画个图能更加不便大家的了解: 简略点说当咱们查问一条数据时,先去查问缓存,如果缓存有就间接返回,如果没有就去查询数据库,而后返回。这种状况下就可能会呈现一些景象。 1. 缓存雪崩1.1 什么是缓存雪崩这里咱们通过一个例子进行剖析。比方马老师的某宝,咱们关上某宝的首页时,看到一些图片呀、举荐店铺信息呀等等,这些都属于热点数据,为什么他们会加载的那么快呢?因为应用到了缓存呗。这些热点数据都做了缓存,假如当初把这些热点数据的缓存生效工夫为一样,当初咱们马老师要做一个秒杀流动,假如在秒杀流动时每秒有8000个申请,原本有缓存咱们是能够扛住每秒 6000 个申请,然而缓存过后所有的Key都生效了。此时 1 秒 8000 个申请全副落数据库,数据库必然扛不住,它会报一下警,真实情况可能DBA都没反馈过去就间接挂了。此时,如果没用什么特地的计划来解决这个故障,DBA 很着急,重启数据库,然而数据库立马又被新的流量给打死了。下面造成缓存雪崩的起因是因为生效工夫造成,还有一种可能是因为缓存服务宕机。 1.2 解决办法这里分三个时间段进行进行剖析 1.2.1 事先如果缓存雪崩造成的起因是因为缓存服务宕机造成的,能够将redis采纳集群部署,能够应用 主从+哨兵 ,Redis Cluster 来防止 Redis 全盘解体的状况。若缓存雪崩是因为大量缓存因为生效工夫而造成的,咱们在批量往redis存数据的时候,把每个Key的生效工夫都加个随机值就好了,这样能够保证数据不会在同一时间大面积生效,或者设置热点数据永远不过期,有更新操作就更新缓存就能够了。 1.2.2 事中如果咱们之前没有思考缓存雪崩的问题,那么在理论应用中真的产生缓存雪崩了,咱们该怎么办呢?这时咱们就要思考应用其余办法避免出现这种状况了。咱们能够应用ehcache 本地缓存 + Hystrix 限流&降级 ,防止 MySQL 被打死的状况产生。 这里应用echache本地缓存的目标就是思考在 Redis Cluster 齐全不可用的时候,ehcache 本地缓存还可能撑持一阵。 应用 Hystrix 进行 限流 & 降级 ,比方一秒来了5000个申请,咱们能够设置假如只能有一秒 2000 个申请能通过这个组件,那么其余残余的 3000 申请就会走限流逻辑,而后去调用咱们本人开发的降级组件(降级)。比方设置的一些默认值呀之类的。以此来爱护最初的 MySQL 不会被大量的申请给打死。 1.2.3 预先如果缓存服务宕机了,这里咱们能够开启Redis 长久化 RDB+AOF,一旦重启,主动从磁盘上加载数据,疾速复原缓存数据。 ...

October 11, 2020 · 1 min · jiezi

关于redis:Redis-最佳实践业务和运维层面优化

作者:Kaito出处:kaito-kidd.com/2020/07/04/redis-best-practices/这篇文章咱们就来总结一下,在应用Redis时的最佳实际形式,次要蕴含两个层面:业务层面、运维层面。 因为我之前写过很多UGC后端服务,在大量场景下用到了Redis,这个过程中也踩过很多坑,所以在应用过程中也总结了一套正当的应用办法。 起初做基础架构,开发Codis、Redis相干的中间件,在这个阶段关注畛域从应用层面下沉到Redis的开发和运维,更多聚焦在Redis的外部实现和运维过程中产生的各种问题,在这块也积攒了一些教训。 上面就针对这两块,分享一下我认为比拟正当的Redis应用和运维办法,不肯定最全面,也可能与你应用Redis的办法不同,但以下这些办法都是我在踩坑之后总结的理论教训,供你参考。关注公众号Java技术栈回复redis获取系列Redis教程。 业务层面次要是开发人员须要关注,也就是开发人员在写业务代码时,如何正当地应用Redis。开发人员须要对Redis有根本的理解,能力在适合的业务场景应用Redis,从而防止业务层面导致的提早问题。 在开发过程中,业务层面的优化倡议如下: key的长度尽量要短,在数据量十分大时,过长的key名会占用更多的内存肯定防止存储过大的数据(大value),过大的数据在分配内存和开释内存时耗时重大,会阻塞主线程Redis 4.0以上倡议开启lazy-free机制,开释大value时异步操作,不阻塞主线程倡议设置过期工夫,把Redis当做缓存应用,尤其在数量很大的时,不设置过期工夫会导致内存的有限增长不应用简单度过高的命令,例如SORT、SINTER、SINTERSTORE、ZUNIONSTORE、ZINTERSTORE,应用这些命令耗时较久,会阻塞主线程查问数据时,一次尽量获取较少的数据,在不确定容器元素个数的状况下,防止应用LRANGE key 0 -1,ZRANGE key 0 -1这类操作,应该设置具体查问的元素个数,举荐一次查问100个以下元素写入数据时,一次尽量写入较少的数据,例如HSET key value1 value2 value3...,-管制一次写入元素的数量,举荐在100以下,大数据量分多个批次写入批量操作数据时,用MGET/MSET替换GET/SET、HMGET/MHSET替换HGET/HSET,缩小申请来回的网络IO次数,升高提早,对于没有批量操作的命令,举荐应用pipeline,一次性发送多个命令到服务端禁止应用KEYS命令,须要扫描实例时,倡议应用SCAN,线上操作肯定要管制扫描的频率,防止对Redis产生性能抖动防止某个工夫点集中过期大量的key,集中过期时举荐减少一个随机工夫,把过期工夫打散,升高集中过期key时Redis的压力,防止阻塞主线程依据业务场景,抉择适合的淘汰策略,通常随机过期要比LRU过期淘汰数据更快应用连接池拜访Redis,并配置正当的连接池参数,防止短连贯,TCP三次握手和四次挥手的耗时也很高只应用db0,不举荐应用多个db,应用多个db会减少Redis的累赘,每次拜访不同的db都须要执行SELECT命令,如果业务线不同,倡议拆分多个实例,还能进步单个实例的性能读的申请量很大时,举荐应用读写拆散,前提是能够容忍从节数据更新不及时的问题写申请量很大时,举荐应用集群,部署多个实例摊派写压力运维层面 目标是正当布局Redis的部署和保障Redis的稳固运行,次要优化如下: 不同业务线部署不同的实例,各自独立,防止混用,举荐不同业务线应用不同的机器,依据业务重要水平划分不同的分组来部署,防止某一个业务线呈现问题影响其余业务线保障机器有足够的CPU、内存、带宽、磁盘资源,避免负载过高影响Redis性能以master-slave集群形式部署实例,并散布在不同机器上,防止单点,slave必须设置为readonlymaster和slave节点所在机器,各自独立,不要穿插部署实例,通常备份工作会在slave上做,做备份时会耗费机器资源,穿插部署会影响到master的性能举荐部署哨兵节点减少可用性,节点数量至多3个,并散布在不同机器上,实现故障主动故障转移提前做好容量布局,一台机器部署实例的内存下限,最好是机器内存的一半,主从全量同步时会占用最多额定一倍的内存空间,防止网络大面积故障引发所有master-slave的全量同步导致机器内存被吃光做好机器的CPU、内存、带宽、磁盘监控,在资源有余时及时报警解决,Redis应用Swap后性能急剧下降,网络带宽负载过高拜访提早显著增大,磁盘IO过高时开启AOF会拖慢Redis的性能设置最大连接数下限,避免过多的客户端连贯导致服务负载过高单个实例的应用内存倡议管制在20G以下,过大的实例会导致备份工夫久、资源耗费多,主从全量同步数据工夫阻塞工夫更长设置正当的slowlog阈值,举荐10毫秒,并对其进行监控,产生过多的慢日志须要及时报警 设置正当的复制缓冲区repl-backlog大小,适当调大repl-backlog能够升高主从全量复制的概率设置正当的slave节点client-output-buffer-limit大小,对于写入量很大的实例,适当调大能够防止主从复制中断问题备份时举荐在slave节点上做,不影响master性能不开启AOF或开启AOF配置为每秒刷盘,防止磁盘IO耗费升高Redis性能当实例设置了内存下限,须要调大内存下限时,先调整slave再调整master,否则会导致主从节点数据不统一对Redis减少监控,监控采集info信息时,应用长连贯,频繁的短连贯也会影响Redis性能,redis性能监控指标,参考这个文章线上扫描整个实例数时,记得设置休眠工夫,防止扫描时QPS突增对Redis产生性能抖动做好Redis的运行时监控,尤其是expired_keys、evicted_keys、latest_fork_usec指标,短时间内这些指标值突增可能会阻塞整个实例,引发性能问题以上就是我在应用Redis和开发Redis相干中间件时,总结进去Redis举荐的实际办法,以上提出的这些方面,都或多或少在理论应用中遇到过。 可见,要想稳固施展Redis的高性能,须要在各个方面做好工作,凡是某一个方面呈现问题,必然会影响到Redis的性能,这对咱们应用和运维提出了更高的要求。 如果你在应用Redis过程中,遇到更多的问题或者有更好的应用教训,能够留言一起探讨!

October 11, 2020 · 1 min · jiezi

关于redis:从零开始手写缓存框架-redis13HashMap-源码原理详解

为什么学习 HashMap 源码?作为一名 java 开发,基本上最罕用的数据结构就是 HashMap 和 List,jdk 的 HashMap 设计还是十分值得深刻学习的。 无论是在面试还是工作中,晓得原理都对会咱们有很大的帮忙。 本篇的内容较长,倡议先珍藏,再细细品味。 不同于网上简略的源码剖析,更多的是实现背地的设计思维。 波及的内容比拟宽泛,从统计学中的泊松散布,到计算机根底的位运算,经典的红黑树、链表、数组等数据结构,也谈到了 Hash 函数的相干介绍,文末也引入了美团对于 HashMap 的源码剖析,所以整体深度和广度都比拟大。 思维导图如下: 本文是两年前整顿的,文中未免有疏漏过期的中央,欢送大家提出贵重的意见。 之所以这里拿进去,有以下几个目标: (1)让读者了解 HashMap 的设计思维,晓得 rehash 的过程。下一节咱们将本人实现一个 HashMap (2)为什么要本人实现 HashMap? 最近在手写 redis 框架,都说 redis 是一个个性更加弱小的 Map,天然 HashMap 就是入门根底。Redis 高性能中一个过人之处的设计就是渐进式 rehash,和大家一起实现一个渐进式 rehash 的 map,更能领会和了解作者设计的奇妙。 想把常见的数据结构独立为一个开源工具,便于前期应用。比方这次手写 redis,循环链表,LRU map 等都是从零开始写的,不利于复用,还容易有 BUG。 好了,上面就让咱们一起开始 HashMap 的源码之旅吧~ HashMap 源码HashMap 是平时应用到十分多的一个汇合类,感觉有必要深刻学习一下。 首先尝试本人浏览一遍源码。 java 版本$ java -versionjava version "1.8.0_91"Java(TM) SE Runtime Environment (build 1.8.0_91-b14)Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)数据结构从构造实现来讲,HashMap是数组+链表+红黑树(JDK1.8减少了红黑树局部)实现的。 ...

October 10, 2020 · 10 min · jiezi

关于redis:从零开始手写缓存框架12redis-expire-过期的随机特性详解及实现

前言java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(二)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不失落? java从零手写实现redis(四)增加监听器 java从零手写实现redis(五)过期策略的另一种实现思路 java从零手写实现redis(六)AOF 长久化原理详解及实现 java从零开始手写redis(七)LRU 缓存淘汰策略详解 java从零开始手写redis(八)奢侈 LRU 淘汰算法性能优化 第二节中咱们曾经初步实现了相似 redis 中的 expire 过期性能,不过存在一个问题没有解决,那就是遍历的时候不是随机返回的,会导致每次遍历从头开始,可能导致很多 Keys 处于“饥饿”状态。 能够回顾: java从零手写实现redis(二)redis expire 过期原理 java从零手写实现redis(五)过期策略的另一种实现思路 本节咱们一起来实现一个过期的随机性版本,更近一步体会一下 redis 的奇妙之处。 以前的实现回顾开始新的旅程之前,咱们先回顾一下原来的实现。 expire 实现原理其实过期的实思路也比较简单:咱们能够开启一个定时工作,比方 1 秒钟做一次轮训,将过期的信息清空。 过期信息的存储/** * 过期 map * * 空间换工夫 * @since 0.0.3 */private final Map<K, Long> expireMap = new HashMap<>();@Overridepublic void expire(K key, long expireAt) { expireMap.put(key, expireAt);}咱们定义一个 map,key 是对应的要过期的信息,value 存储的是过期工夫。 轮询清理咱们固定 100ms 清理一次,每次最多清理 100 个。 ...

October 8, 2020 · 4 min · jiezi

关于redis:java-从零开始手写-redis11clock时钟淘汰算法详解及实现

前言java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不失落? java从零手写实现redis(四)增加监听器 java从零手写实现redis(五)过期策略的另一种实现思路 java从零手写实现redis(六)AOF 长久化原理详解及实现 java从零开始手写 redis(七)LRU 缓存淘汰策略详解 后面咱们实现了 FIFO/LRU/LFU 等常见的淘汰策略,不过在操作系统中,实际上应用的是时钟页面置换算法。 LRU 的性能的确很好,不过比拟耗费内存,而且实现比拟麻烦。 时钟页面置换算法就是一种近似 LRU 的算法实现,能够看作是对 FIFO 算法的改良。 Clock 页面置换算法为什么须要 clock 算法?LRU算法的性能靠近于OPT,然而实现起来比拟艰难,且开销大;FIFO算法实现简略,但性能差。 所以操作系统的设计者尝试了很多算法,试图用比拟小的开销靠近LRU的性能,这类算法都是CLOCK算法的变体。 因为该算法循环地查看各页面的状况,故称为CLOCK算法,又称为最近未用(Not Recently Used, NRU)算法。 基本思路须要用到页表项的拜访位(access bit),当一个页面被装入内存时,把该位初始化为0,而后如果这个页被拜访(读/写)时,硬件把它置为1. 把各个页面组织成环形链表(相似钟外表),把指针指向最老的页面(最先进来); 当产生一个缺页中断,考查指针所指向的最老的页面,若它的拜访为为0,则立刻淘汰。若拜访为1,则把该地位为0,而后指针往下挪动一格。如此上来,直到找到被淘汰的页面,而后把指针挪动到它的下一格。 集体纳闷(1)如果找了一圈发现元素都是 1 怎么办? 是不是间接默认取第一个元素,这样认为就是回到了奢侈的 FIFO 机制。 (2)拜访的性能问题 这里的遍历能够认为是一个循环链表: 每一个节点内容: K key;boolean accessFlag;奢侈的 FIFO 非常简单,间接往队列中扔元素就行,而后淘汰最老的一个元素。 这个如果真的应用链表作为数据结构,那么查找,更新工夫复杂度就是 O(N),显然性能个别。 能想到的计划就是 HashMap 中存储 key+双向链表节点。 和性能改良版本的 LRU 比照,就是每次更新不做节点的移除调整,而只是更新对应的标记位。 简略的CLOCK算法是通过给每一个拜访的页面关联一个附加位(reference bit),有些中央也叫做应用位(use bit)。 他的次要思维是:当某一页装入主存时,将use bit初始化为0;如果该页之后又被拜访到,应用位也还是标记成1。 对于页面置换算法,候选的帧汇合能够看成是一个循环缓冲区,并且有一个指针和缓冲区相关联。遇到页面替换时,指针指向缓冲区的下一帧。 如果这页进入主存后发现没有空余的帧(frame),即所有页面的应用位均为1,那么这时候从指针开始循环一个缓冲区,将之前的应用位都清0,并且留在最后的地位上,换出该桢对应的页。 ps: 这里发现没有空余的帧,会将所有应用位都清零。 ...

October 7, 2020 · 4 min · jiezi

关于redis:java-从零开始手写-redis十缓存淘汰算法-LFU-最少使用频次

前言java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不失落? java从零手写实现redis(四)增加监听器 java从零手写实现redis(五)过期策略的另一种实现思路 java从零手写实现redis(六)AOF 长久化原理详解及实现 java从零手写实现redis(七)LRU 缓存淘汰策略详解 从零开始手写 redis(八)奢侈 LRU 淘汰算法性能优化 本节一起来学习下另一个罕用的缓存淘汰算法,LFU 起码应用频次算法。 LFU 基础知识概念LFU(Least Frequently Used)即最近最不罕用.看名字就晓得是个基于拜访频次的一种算法。 LRU是基于工夫的,会将工夫上最不常拜访的数据给淘汰,在算法体现上是放到列表的顶部;LFU为将频率上最不常拜访的数据淘汰. 既然是基于频率的,就须要有存储每个数据拜访的次数. 从存储空间上,较LRU会多出一些持有计数的空间. 核心思想如果一个数据在最近一段时间内应用次数很少,那么在未来一段时间内被应用的可能性也很小。 实现思路O(N) 的删除为了可能淘汰起码应用的数据,集体第一直觉就是间接一个 HashMap<String, Interger>, String 对应 key 信息,Integer 对应次数。 每次拜访到就去+1,设置和读取的工夫复杂度都是 O(1);不过删除就比拟麻烦了,须要全副遍历比照,工夫复杂度为 O(n); O(logn) 的删除另外还有一种实现思路就是利用小顶堆+hashmap,小顶堆插入、删除操作都能达到O(logn)工夫复杂度,因而效率相比第一种实现办法更加高效。比方 TreeMap。 O(1) 的删除是否可能更进一步优化呢? 其实 O(1) 的算法是有的,参见这篇 paper: An O(1) algorithm for implementing the LFU cache eviction scheme简略说下集体的想法: 咱们要想实现 O(1) 的操作,必定离不开 Hash 的操作,咱们 O(N) 的删除中就实现了 O(1) 的 put/get。 ...

October 6, 2020 · 4 min · jiezi

关于redis:java-从零开始手写-redis九LRU-缓存淘汰算法如何避免缓存污染

前言java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不失落? java从零手写实现redis(四)增加监听器 java从零手写实现redis(五)过期策略的另一种实现思路 java从零手写实现redis(六)AOF 长久化原理详解及实现 java从零手写实现redis(七)LRU 缓存淘汰策略详解 从零开始手写 redis(八)奢侈 LRU 淘汰算法性能优化 前两节咱们别离实现了 LRU 算法,并且进行了性能优化。 本节作为 LRU 算法的最初一节,次要解决一下缓存净化的问题。 LRU 基础知识是什么LRU算法全称是最近起码应用算法(Least Recently Use),宽泛的利用于缓存机制中。 当缓存应用的空间达到下限后,就须要从已有的数据中淘汰一部分以维持缓存的可用性,而淘汰数据的抉择就是通过LRU算法实现的。 LRU算法的根本思维是基于局部性原理的工夫局部性: 如果一个信息项正在被拜访,那么在近期它很可能还会被再次拜访。 拓展浏览Apache Commons LRUMAP 源码详解 Redis 当做 LRU MAP 应用 java 从零开始手写 redis(七)redis LRU 驱除策略详解及实现 奢侈 LRU 算法的有余当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存净化状况比较严重。 扩大算法1. LRU-KLRU-K中的K代表最近应用的次数,因而LRU能够认为是LRU-1。 LRU-K的次要目标是为了解决LRU算法“缓存净化”的问题,其核心思想是将“最近应用过1次”的判断规范扩大为“最近应用过K次”。 相比LRU,LRU-K须要多保护一个队列,用于记录所有缓存数据被拜访的历史。只有当数据的拜访次数达到K次的时候,才将数据放入缓存。 当须要淘汰数据时,LRU-K会淘汰第K次访问工夫距以后工夫最大的数据。 数据第一次被拜访时,退出到历史拜访列表,如果数据在拜访历史列表中没有达到K次访问,则依照肯定的规定(FIFO,LRU)淘汰; 当拜访历史队列中的数据拜访次数达到K次后,将数据索引从历史队列中删除,将数据移到缓存队列中,并缓存数据,缓存队列从新依照工夫排序; 缓存数据队列中被再次拜访后,从新排序,须要淘汰数据时,淘汰缓存队列中排在开端的数据,即“淘汰倒数K次访问离当初最久的数据”。 LRU-K具备LRU的长处,同时还能防止LRU的毛病,理论利用中LRU-2是综合最优的抉择。 因为LRU-K还须要记录那些被拜访过、但还没有放入缓存的对象,因而内存耗费会比LRU要多。 2. two queueTwo queues(以下应用2Q代替)算法相似于LRU-2,不同点在于2Q将LRU-2算法中的拜访历史队列(留神这不是缓存数据的)改为一个FIFO缓存队列,即:2Q算法有两个缓存队列,一个是FIFO队列,一个是LRU队列。 当数据第一次拜访时,2Q算法将数据缓存在FIFO队列外面,当数据第二次被拜访时,则将数据从FIFO队列移到LRU队列外面,两个队列各自依照本人的办法淘汰数据。 新拜访的数据插入到FIFO队列中,如果数据在FIFO队列中始终没有被再次拜访,则最终依照FIFO规定淘汰; 如果数据在FIFO队列中再次被拜访到,则将数据移到LRU队列头部,如果数据在LRU队列中再次被拜访,则将数据挪动LRU队列头部,LRU队列淘汰开端的数据。 3. Multi Queue(MQ)MQ算法依据拜访频率将数据划分为多个队列,不同的队列具备不同的拜访优先级,其核心思想是:优先缓存拜访次数多的数据。 具体的算法结构图如下,Q0,Q1....Qk代表不同的优先级队列,Q-history代表从缓存中淘汰数据,但记录了数据的索引和援用次数的队列: ...

October 5, 2020 · 7 min · jiezi

关于redis:从零开始手写-redis八朴素-LRU-淘汰算法性能优化

前言java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不失落? java从零手写实现redis(四)增加监听器 java从零手写实现redis(五)过期策略的另一种实现思路 java从零手写实现redis(六)AOF 长久化原理详解及实现 咱们后面简略实现了 redis 的几个个性,java从零手写实现redis(一)如何实现固定大小的缓存? 中实现了先进先出的驱除策略。 然而理论工作实际中,个别举荐应用 LRU/LFU 的驱除策略。 LRU 基础知识是什么LRU算法全称是最近起码应用算法(Least Recently Use),宽泛的利用于缓存机制中。 当缓存应用的空间达到下限后,就须要从已有的数据中淘汰一部分以维持缓存的可用性,而淘汰数据的抉择就是通过LRU算法实现的。 LRU算法的根本思维是基于局部性原理的工夫局部性: 如果一个信息项正在被拜访,那么在近期它很可能还会被再次拜访。 拓展浏览Apache Commons LRUMAP 源码详解 Redis 当做 LRU MAP 应用 java 从零开始手写 redis(七)redis LRU 驱除策略详解及实现 简略的实现思路基于数组计划:为每一个数据附加一个额定的属性——工夫戳,当每一次拜访数据时,更新该数据的工夫戳至以后工夫。 当数据空间已满后,则扫描整个数组,淘汰工夫戳最小的数据。 有余:保护工夫戳须要消耗额定的空间,淘汰数据时须要扫描整个数组。 这个工夫复杂度太差,空间复杂度也不好。 基于长度无限的双向链表计划:拜访一个数据时,当数据不在链表中,则将数据插入至链表头部,如果在链表中,则将该数据移至链表头部。当数据空间已满后,则淘汰链表最开端的数据。 有余:插入数据或取数据时,须要扫描整个链表。 这个就是咱们上一节实现的形式,毛病还是很显著,每次确认元素是否存在,都要耗费 O(n) 的工夫复杂度去查问。 基于双向链表和哈希表计划:为了改良下面须要扫描链表的缺点,配合哈希表,将数据和链表中的节点造成映射,将插入操作和读取操作的工夫复杂度从O(N)降至O(1) 毛病:这个使咱们上一节提到的优化思路,不过还是有毛病的,那就是空间复杂度翻倍。 数据结构的抉择(1)基于数组的实现 这里不倡议抉择 array 或者 ArrayList,因为读取的工夫复杂度为 O(1),然而更新绝对是比较慢的,尽管 jdk 应用的是 System.arrayCopy。 (2)基于链表的实现 如果咱们抉择链表,HashMap 中还是不能简略的存储 key, 和对应的下标。 因为链表的遍历,实际上还是 O(n) 的,双向链表实践上能够优化一半,然而这并不是咱们想要的 O(1) 成果。 ...

October 4, 2020 · 3 min · jiezi

关于redis:redis学习

根本类型字符串Hash临时依照对象了解 命令有 HMSETHGETHGETALLList 列表Redis 列表是简略的字符串列表,依照插入程序排序。你能够增加一个元素到列表的头部(右边)或者尾部(左边)。 Setstring类型的无序组合。 成员不容许反复 saddsmemberszsetzset是有序汇合。 每个元素关联一个double类型的分数。 成员为1,分数能够是雷同的 命令 zadd zadd key score member key操作DEL 删除exists 是否存在expire 设置过期工夫,这个过期工夫单位是秒expireat 过期,参数是unix工夫戳pexpire 以毫秒工夫过期keysPERSIST 移除残余的工夫,该key变为长久放弃pttl 以毫秒为单位,返回key的剩余时间ttl 以秒为单位,返回key的剩余时间randomkey 随即返回一个keyrename 重命名 rename key newkeytype 返回key的类型renamenxconst result = await redis.exists('foo');// 返回1 有,0 没有await redis.keys('f*o'); // => ['foo']await redis.exipre(1)await redis.pttl('foo');// 1000await redis.rename('foo','foo0')https://www.runoob.com/redis/redis-keys.html https://cloud.tencent.com/developer/article/1112627

October 4, 2020 · 1 min · jiezi

关于redis:java-从零开始手写-redis七LRU-缓存淘汰策略详解

前言java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不失落? java从零手写实现redis(四)增加监听器 java从零手写实现redis(五)过期策略的另一种实现思路 java从零手写实现redis(六)AOF 长久化原理详解及实现 咱们后面简略实现了 redis 的几个个性,java从零手写实现redis(一)如何实现固定大小的缓存? 中实现了先进先出的驱除策略。 然而理论工作实际中,个别举荐应用 LRU/LFU 的驱除策略。 LRU 基础知识拓展学习Apache Commons LRUMAP 源码详解 Redis 当做 LRU MAP 应用 LRU 是什么LRU 是由 Least Recently Used 的首字母组成,示意最近起码应用的含意,个别应用在对象淘汰算法上。 也是比拟常见的一种淘汰算法。 其核心思想是如果数据最近被拜访过,那么未来被拜访的几率也更高。 连续性在计算机科学中,有一个领导准则:连续性准则。 工夫连续性:对于信息的拜访,最近被拜访过,被再次拜访的可能性会很高。缓存就是基于这个理念进行数据淘汰的。 空间连续性:对于磁盘信息的拜访,将很有可能拜访间断的空间信息。所以会有 page 预取来晋升性能。 实现步骤新数据插入到链表头部;每当缓存命中(即缓存数据被拜访),则将数据移到链表头部;当链表满的时候,将链表尾部的数据抛弃。其实比较简单,比起 FIFO 的队列,咱们引入一个链表实现即可。 一点思考咱们针对下面的 3 句话,逐句考虑一下,看看有没有值得优化点或者一些坑。 如何判断是新数据?(1) 新数据插入到链表头部; 咱们应用的是链表。 判断新数据最简略的办法就是遍历是否存在,对于链表,这是一个 O(n) 的工夫复杂度。 其实性能还是比拟差的。 当然也能够思考空间换工夫,比方引入一个 set 之类的,不过这样对空间的压力会加倍。 什么是缓存命中(2)每当缓存命中(即缓存数据被拜访),则将数据移到链表头部; put(key,value) 的状况,就是新元素。如果已有这个元素,能够先删除,再退出,参考下面的解决。 get(key) 的状况,对于元素拜访,删除已有的元素,将新元素放在头部。 remove(key) 移除一个元素,间接删除已有元素。 keySet() valueSet() entrySet() 这些属于无差别拜访,咱们不对队列做调整。 ...

October 3, 2020 · 3 min · jiezi

关于redis:SpringBoot234整合Redis实现缓存

文章次要内容一、SpringCache 介绍 二、SpringCache 注解 三、注解形式实现Redis缓存(Windows版Redis) 四、RedisUtils工具类原生形式实现Redis缓存(Windows版Redis) 一、SpringCache介绍官网文档地址:https://docs.spring.io/spring... 在Spring Boot中,数据的缓存治理存储依赖于Spring框架中cache相干的org.springframework.cache.Cache和org.springframework.cache.CacheManager缓存管理器接口。 如果程序中没有定义类型为CacheManager的Bean组件或者是名为cacheResolver的CacheResolver缓存解析器,Spring Boot将尝试抉择并启用以下缓存组件(依照指定的程序): (1)Generic (2)JCache (JSR-107)(EhCache 3、Hazelcast、Infinispan等) (3)EhCache 2.x (4)Hazelcast (5)Infinispan (6)Couchbase (7)Redis (8)Caffeine (9)Simple 下面依照Spring Boot缓存组件的加载程序,列举了反对的9种缓存组件,在我的项目中增加某个缓存治理组件(例如Redis)后,Spring Boot我的项目会抉择并启用对应的缓存管理器。如果我的项目中同时增加了多个缓存组件,且没有指定缓存管理器或者缓存解析器(CacheManager或者cacheResolver),那么Spring Boot会依照上述程序在增加的多个缓存中优先启用指定的缓存组件进行缓存治理。 Spring Boot默认缓存治理中,没有增加任何缓存治理组件能实现缓存治理。这是因为开启缓存治理后,Spring Boot会依照上述列表程序查找无效的缓存组件进行缓存治理,如果没有任何缓存组件,会默认应用最初一个Simple缓存组件进行治理。Simple缓存组件是Spring Boot默认的缓存治理组件,它默认应用内存中的ConcurrentMap进行缓存存储,所以在没有增加任何第三方缓存组件的状况下,能够实现内存中的缓存治理,然而咱们不举荐应用这种缓存治理形式 当在Spring Boot默认缓存治理的根底上引入Redis缓存组件,即在pom.xml文件中增加Spring Data Redis依赖启动器后,SpringBoot会应用RedisCacheConfigratioin当做失效的主动配置类进行缓存相干的主动拆卸,容器中应用的缓存管理器是RedisCacheManager, 这个缓存管理器创立的Cache为 RedisCache, 进而操控redis进行数据的缓存 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>核心思想:当咱们调用一个办法时会把该办法的参数和返回后果最为一个键值对寄存在缓存中,等下次利用同样的参数来调用该办法时将不会再执行,而是间接从缓存中获取后果进行返回。 了解:springboot 的缓存机制是通过切面编程 aop来实现的 二、SpringCache 注解Spring Cache 提供了 @Cacheable 、@CachePut 、@CacheEvict 、@Caching 等注解,在办法上应用。 基于注解形式SpringCache引入Redis做缓存,须要先理解@EnableCaching、@CacheConfig、@Cacheable、@CachePut、@CacheEvict、@Caching相干注解的应用 1、@EnableCaching开启缓存性能,个别放在启动类上或者自定义的RedisConfig配置类上。 2、@CacheConfig当咱们须要缓存的中央越来越多,能够应用@CacheConfig(cacheNames = "cacheName")注解在 class 之上来对立指定value的值,对立治理keys,这时可省略value,如果你在你的办法仍旧写上了value,那么仍然以办法的value值为准。 示例: @Service@CacheConfig(cacheNames = "categories")public class CategoryServiceImpl implements CategoryService{ ......} ...

October 3, 2020 · 11 min · jiezi

关于redis:redis高可用之主从复制

redis高可用redis主从CAP原理C:一致性A:可用性P:分区容忍性分布式系统的节点往往都是散布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的危险,这个网络断开的场景的业余词汇叫网络分区。当网络分区产生时,两个分布式节点之间无奈进行通信,咱们对一个节点的批改操作无奈同步到另一个节点,所以数据的一致性将无奈满足。除非咱们就义可用性,也就是暂停分布式服务,在网络分区产生时不再提供批改数据的性能,直到网络情况齐全恢复正常再持续对外提供服务。CAP:当网络分区产生时,一致性和可用性两难全。 最终统一redis的主从数据都是异步同步的,所以分布式redis不满足一致性的要求。当客户端在redis的主节点批改了数据后立刻返回,即便在主从网络断开的状况下,主节点仍旧能够失常对外提供批改服务,所以redis满足可用性。redis保障最终一致性,从节点会致力追赶主节点,最终从节点的状态会和主节点的状态保持一致。如果网络断开了,主从节点的数据会呈现大量不统一,但一旦网络复原,从节点会采纳多种策略致力追赶,持续尽力放弃和主节点统一。 主从同步如何进行主从同步须要留神,主从复制的开启,齐全是在从节点发动的;不须要咱们在主节点做任何事件。从节点开启主从复制,有3种形式: 配置文件在从服务器的配置文件中退出:slaveof <masterip> <masterport> 启动命令redis-server启动命令后退出 —slaveof <masterip> <masterport> 客户端命令Redis服务器启动后,间接通过客户端执行命令:slaveof <masterip> <masterport>,则该Redis实例成为从节点。 断开复制:通过slaveof <masterip> <masterport>命令建设主从复制关系当前,能够通过slaveof no one断开。须要留神的是,从节点断开复制后,不会删除已有的数据,只是不再承受主节点新的数据变动。从节点执行slaveof no one后,打印日志如下所示;能够看出断开复制后,从节点又变回为主节点:[image:78C4F792-4EEB-4F44-9DAF-96837A296B27-1128-000018F29E3D7E5F/493BD180-F46A-48EF-A661-95A2643B9EF2.png] 主节点打印日志如下:[image:AD5036CA-AE6F-45C4-8E8D-F82BCD908CA5-1128-000018F724596E6C/C280DF44-8820-41AE-A4C9-1D4D0261101E.png] 主从同步的步骤须要留神的是,在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端;而到了这一阶段及当前,主从节点互为客户端。起因在于:在此之前,主节点只须要响应从节点的申请即可,不须要被动发申请,而在数据同步阶段和前面的命令流传阶段,主节点须要被动向从节点发送申请(如推送缓冲区中的写命令),能力实现复制。 主从同步分为 2 个步骤:同步和命令流传 同步:将从服务器的数据库状态更新成主服务器以后的数据库状态。命令流传:当主服务器数据库状态被批改后,导致主从服务器数据库状态不统一,此时须要让主从数据同步到统一的过程。这里须要提前阐明一下:在 Redis 2.8 版本之前,进行主从复制时肯定会程序执行上述两个步骤,而从 2.8 开始则可能只须要执行命令流传即可 同步阶段全量复制从服务器向主服务器发送 sync 命令收到 sync 命令后,主服务器执行 bgsave 命令,用来生成 rdb 文件,并在一个缓冲区中记录从当初开始执行的写命令。bgsave 执行实现后,将生成的 rdb 文件发送给从服务器,用来给从服务器更新数据主服务器再将缓冲区记录的写命令发送给从服务器,从服务器执行完这些写命令后,此时的数据库状态便和主服务器统一了。 全量复制是十分重型的操作: 主服务器须要执行bgsave命令来生成RDB文件,这个生成操作会消耗主服务器大量的cpu,内存和磁盘io资源。主服务器须要将本人生成的RDB文件发送给从服务器,这个发送操作会消耗主服务器大量的网络资源(带宽和流量),并对主服务器响应命令申请的工夫产生影响。接管到RDB文件的从服务器须要清空老数据,载入主服务器发来的RDB文件,并且在载入期间从服务器会因为阻塞而没方法解决命令申请。局部复制局部复制的实现,依赖于三个重要的概念: 主从服务器的复制偏移量主服务器的复制积压缓冲区服务器的运行 id(run id)复制偏移量执行复制的主从服务器都会别离保护各自的复制偏移量:主服务器每次向从服务器流传 n 个字节数据时,都会将本人的复制偏移量加 n。从服务器承受主服务器传来的数据时,也会将本人的复制偏移量加 n举个例子:若以后主服务器的复制偏移量为 10000,此时向从服务器流传 30 个字节数据,完结后复制偏移量为 10030。这时,从服务器还没接管这 30 个字节数据就断线了,而后从新连贯上之后,该从服务器的复制偏移量仍旧为 10000,阐明主从数据不统一,此时会向主服务器发送 psync 命令。那么主服务器应该对从服务器执行残缺重同步还是局部重同步呢?如果执行局部重同步的话,主服务器又如何晓得同步哪些数据给从服务器呢?以下答案都和复制积压缓冲区无关 复制积压缓冲区复制积压缓冲区是由主节点保护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时创立,其作用是备份主节点最近发送给从节点的数据。留神,无论主节点有一个还是多个从节点,都只须要一个复制积压缓冲区。在命令流传阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。因为复制积压缓冲区定长且是先进先出,所以它保留的是主节点最近执行的写命令;工夫较早的写命令会被挤出缓冲区。因为该缓冲区长度固定且无限,因而能够备份的写命令也无限,当主从节点offset的差距过大超过缓冲区长度时,将无奈执行局部复制,只能执行全量复制。反过来说,为了进步网络中断时局部复制执行的概率,能够依据须要增大复制积压缓冲区的大小(通过配置repl-backlog-size);例如如果网络中断的均匀工夫是60s,而主节点均匀每秒产生的写命令(特定协定格局)所占的字节数为100KB,则复制积压缓冲区的均匀需要为6MB,保险起见,能够设置为12MB,来保障绝大多数断线状况都能够应用局部复制。从节点将offset发送给主节点后,主节点依据offset和缓冲区大小决定是否执行局部复制: * 如果offset偏移量之后的数据,依然都在复制积压缓冲区里,则执行局部复制;* 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。run id运行 id 是在进行首次复制时,主服务器将会将本人的运行 id 发送给从服务器,让其保存起来。当从服务器断线重连后,从服务器会将这个运行 id 发送给刚连贯上的主服务器。若以后服务器的运行 id 与之雷同,阐明从服务器断线前复制的服务器就是以后服务器,主服务器能够尝试执行局部同步;若不同则阐明从服务器断线前复制的服务器不是以后服务器,主服务器间接执行残缺重同步。 ...

October 3, 2020 · 1 min · jiezi

关于redis:redis持久化详解

redis长久化RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个工夫点上的数据备份。非常适合备份,全量复制等场景。比方每6小时执行 bgsave 备份,并把 RDB 文件拷贝到近程机器或者文件系统中,用于劫难复原。Redis 加载 RDB 复原数据远远快于 AOF 的形式RDB 形式数据没方法做到实时长久化,而 AOF 形式能够做到。AOFAOF日志存储的是redis服务器的程序指令序列,AOF日志只记录对内存进行批改的指令记录。redis是先执行指令再将日志存盘。 AOF追加当 AOF 长久化性能处于关上状态时,Redis 在执行完一个写命令之后,会以协定格局(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )将被执行的写命令追加到 Redis 服务端保护的 AOF 缓冲区开端。 对于AOF的同步策略是波及到操作系统的write函数和fsync函数的,在《Redis设计与实现》中是这样阐明的 为了进步文件写入效率,在古代操作系统中,当用户调用write函数,将一些数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区的空间被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。这样的操作尽管进步了效率,但也为数据写入带来了平安问题:如果计算机停机,内存缓冲区中的数据会失落。为此,零碎提供了fsync、fdatasync同步函数,能够强制操作系统立即将缓冲区中的数据写入到硬盘里,从而确保写入数据的安全性。AOF重写AOF 重写是一个有歧义的名字,理论的重写工作是针对数据库的以后值来进行的,程序既不读写、也不应用原有的 AOF 文件。 如果AOF日志太长,须要对AOF日志进行重写(bgrewriteaof指令)。原理是开拓一个子过程对内存进行遍历,转换成一系列的redis操作指令,序列化到一个新的AOF日志文件中。序列化结束后再将操作期间产生的增量AOF日志追加到这个新的AOF日志文件中,追加结束后就立刻代替旧的AOF日志文件了,瘦身工作就实现了。 AOF 重写能够由用户通过调用 BGREWRITEAOF 手动触发。另外, 服务器在 AOF 性能开启的状况下, 会维持以下三个变量: 记录以后 AOF 文件大小的变量 aof_current_size 。记录最初一次 AOF 重写之后, AOF 文件大小的变量 aof_rewrite_base_size 。增长百分比变量 aof_rewrite_perc 。每次当 serverCron 函数执行时, 它都会查看以下条件是否全副满足, 如果是的话, 就会触发主动的 AOF 重写: 没有 BGSAVE 命令在进行。没有 BGREWRITEAOF 在进行。以后 AOF 文件大小大于 server.aof_rewrite_min_size (默认值为 1 MB)。以后 AOF 文件大小和最初一次 AOF 重写后的大小之间的比率大于等于指定的增长百分比。默认状况下, 增长百分比为 100% , 也即是说, 如果后面三个条件都曾经满足, 并且以后 AOF 文件大小比最初一次 AOF 重写时的大小要大一倍的话, 那么触发主动 AOF 重写。 ...

October 2, 2020 · 1 min · jiezi

关于redis:通过-Homebrew-安装配置-Redis

背景通过 Homebrew , 在本地机器上装置&配置 Redis 装置 Redis$ brew install redisRedis 相干配置电脑开机时, 启动 Redis$ ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents通过 "launchctl" 启动 Redis$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist以指定配置文件启动 Redis$ redis-server /usr/local/etc/redis.conf进行以后加载的 Agent$ launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.redis.plist默认配置文件的地位cat /usr/local/etc/redis.conf卸载 Redis 和相干配置$ brew uninstall redis$ rm ~/Library/LaunchAgents/homebrew.mxcl.redis.plist获取 Redis 相干信息$ brew info redis校验 Redis 是否运行$ redis-cli ping如果失常运行, 响应 PONG

October 1, 2020 · 1 min · jiezi

关于redis:记一次向redis中插入上千万key

背景从海量key里查问出某一固定前缀的key 要解决这个问题首先须要有海量的key 向redis插入上千万数据Linux下执行Bash批量生成redis测试数据1.Linux Bash上面执行 for((i=1;i<=20000000;i++)); do echo "set k$i v$i" >> /tmp/redisTest.txt ;done; 生成2千万条redis批量设置kv的语句(key=kn,value=vn)写入到/tmp目录下的redisTest.txt文件中2.用vim去掉行尾的^M符号,应用形式如下:: vim /tmp/redisTest.txt :set fileformat=dos #设置文件的格局,通过这句话去掉每行结尾的^M符号 ::wq #保留退出3.通过redis提供的管道--pipe模式,去跑redis,传入文件的指令批量灌数据,须要花10分钟左右 cat /tmp/redisTest.txt | 门路/redis-5.0.0/src/redis-cli -h 主机ip -p 端口号 --pipe第三步这我的是cat /tmp/redisTest.txt | /usr/local/redis/bin/redis-cli --pipe

September 30, 2020 · 1 min · jiezi

关于redis:虚拟机中centos7安装使用redis

Redis DeskTop Manage是redis的一款可视化管理工具,那么如何连贯到虚拟机(centos7为例)的redis服务器呢? 装置虚拟机redis环境关上redis官网,点击download,能够看到如下下载教程,能够抉择下载安装包,也能够抉择命令下载,我这里抉择后者wget http://download.redis.io/releases/redis-6.0.8.tar.gz 解压tar xzf redis-6.0.8.tar.gz 进入解压后的目录开始装置cd redis-6.0.8 在装置前须要确认gcc是否装置gcc -v,因为是用c++写的,所以make前须要装置gcc或者降级gccyum install gcc-c++ 前提是曾经进入到理解压后的redis-6.0.8目录,进行装置,默认装置完的目录是/usr/local/binmake装置后进入/usr/local/bin目录查看,这里的config文件夹是我前面手动创立的mkdir config,用来搁置我的redis配置文件 把解压后的redis-6.0.8文件夹内的redis.config文件复制到咱们刚创立的装置后的目录下的config文件夹内 cd /usr/local/bin/cp /home/parallels/redis-6.0.8/redis.conf congigredis配置文件## 在redis中,非法的"尺寸单位",无大小写辨别.# 1k => 1000 bytes# 1kb => 1024 bytes# 1m => 1000000 bytes# 1mb => 1024*1024 bytes# 1g => 1000000000 bytes# 1gb => 1024*1024*1024 bytes##将redis是否当前台过程的形式运行,默认为"no"**daemonize** no##如果"daemonize yes",那么将会把过程id信息写入文件中.请留神:启动redis过程的用户须要具备写入此目录的权限.##pidfile ~/redis.pid##指令操作:./redis.server --daemonize yes --pidfile ~/redis.pid##**pidfile** /var/run/redis.pid##指定server须要侦听的客户端连贯端口,client与server在此端口进行TCP通信.**port** 6379##如果你的物理服务器有多个网络接口,请你为将server socket绑定在指定IP上.# **bind** 127.0.0.1# 指定socket连贯闲暇工夫(秒).如果connection闲暇超时,将会敞开连贯(TCP socket选项)##如果为0,示意永不超时.**timeout** 0##指定TCP连贯是否为长连贯,"侦探"信号有server端保护,长连贯将会额定的减少server端的开销(TCP socket选项)##默认为0.示意禁用,非0值示意开启"长连贯";"侦探"信号的发送距离将有linux零碎决定##在屡次"侦探"后,如果对等端(客户端socket)仍不回复,将会敞开连贯,否则连贯将会被放弃开启.##client端socket也能够通过配置keepalive选项,开启"长连贯".(单位:秒)**tcp-keepaliv**e 0##server日志级别,非法值:debug,verbose,notice,warning 默认为notice##debug适宜开发环境,客户端操作信息都会输入日志##verbose输入一些绝对有用的信息,目前成果不明##notice适宜生产环境##warning异样信息**loglevel** notice##指定redis日志文件目录,默认为stdout##logfile ~/redislog.log**logfile** stdout##设定redis所容许的最大"db簇"的个数,默认为16个簇.##客户端能够通过"select"指令指定须要应用的"db簇"索引号,默认为0.##redis的顶层数据结构中,所有K-V都潜在的包含了"db簇"索引号,任何一个key都将隶属于一个"db".##任何对数据的检索,只会笼罩指定的"db";例如数据被插入到"db 10"中,那么在"db 1"中去get,将会返回null.##对数据归类到不同的db簇中,能够帮忙咱们实现一些特定的需要,比方依据不同客户端连贯,来指定不同的db索引号.**databases** 16##snapshot配置,save <seconds> <changes>,用来形容"在多少秒期间至多多少个变更操作"触发snapshot##snapshot最终将生成新的dump.rdb文件##save ""用来禁用snapshot性能##如下示意12小时内至多一个key变更,触发snapshot**save** 43200 1##如果snapshot过程中呈现谬误,即数据长久化失败,是否终止所有的客户端write申请.##这个选项很让人尴尬,"yes"示意终止,一旦snapshot故障,那么此server为只读服务;##如果为"no",那么此次snapshot将失败,但下一次snapshot不会受到影响,不过如果呈现故障,数据只能复原到"最近一个胜利点".**stop-writes-on-bgsave-error** yes##是否启用rdb文件压缩伎俩,默认为yes.##压缩可能须要额定的cpu开销,不过这可能无效的减小rdb文件的大小,有利于存储/备份/传输/数据恢复.**rdbcompression** yes##是否对rdb文件应用CRC64校验和,默认为"yes",那么每个rdb文件内容的开端都会追加CRC校验和.##对于其余第三方校验工具,能够很不便的检测文件的完整性**rdbchecksum** yes##指定rdb文件的名称**dbfilename** dump.rdb##指定rdb/AOF文件的目录地位**dir** ./# 将以后server做为slave,并为其指定master信息.# **slaveof** <masterip> <masterport>##以后server的受权明码##任何客户端或者slave与此server交互前,须要提交明码,其余server的masterauth配置和此参数值保持一致##明码应该足够简单(64字节)# **requirepass** <foobared>## 以认证的形式连贯到master.如果master中应用了"密码保护",slave必须交付正确的受权明码,能力连贯胜利## "requirepas"配置项指定了以后server的明码.## 此配置项中<master-password>值须要和master机器的"requirepas"保持一致。此参数配置在slave端。# **masterauth** <master-password>##如果以后server是slave,那么当slave与master失去通信时,是否持续为客户端提供服务,"yes"示意持续,"no"示意终止.##在"yes"状况下,slave持续向客户端提供只读服务,有可能此时的数据曾经过期.##在"no"状况下,任何向此server发送的数据申请服务(包含客户端和此server的slave)都将被告知"error"**slave-serve-stale-data** yes##slave是否为"只读",强烈建议为"yes"**slave-read-only** yes##slave向指定的master发送ping音讯的工夫距离(秒),默认为10# **repl-ping-slave-period** 10##slave与master通信中,最大闲暇工夫,默认60秒.超时将导致连贯敞开.# **repl-timeout** 60##slave与master的连贯,是否禁用TCP nodelay选项.##"yes"示意禁用,那么socket通信中数据将会以packet形式发送(packet大小受到socket buffer限度),##       能够进步socket通信的效率(tcp交互次数),然而小数据将会被buffer,不会被立刻发送,对于接受者可能存在提早.##"no"示意开启tcp nodelay选项,任何数据都会被立刻发送,及时性较好,然而效率较低##倡议为"no"**repl-disable-tcp-nodelay** no##实用Sentinel模块(unstable,M-S集群治理和监控),须要额定的配置文件反对##slave的权重值,默认100.当master生效后,Sentinel将会从slave列表中找到权重值最低(>0)的slave,并晋升为master##如果权重值为0,示意此slave为"观察者",不参加master选举**slave-priority** 100##重命名指令,对于一些与"server"管制无关的指令,可能不心愿近程客户端(非管理员用户)链接随便应用,##那么就能够把这些指令重命名为"难以浏览"的其余字符串.##例如"slaveof"   "CONFIG"   "BGREWRITEAOF"   "BGREWRITE"   "FLUSHALL"等指令须要被限度拜访##配置项格局: rename-command <command> <newCommand># **rename-command** CONFIG 3ed984507a5dcd722aeade310065ce5d    (形式:MD5('CONFIG^!'))##所容许的客户端连接数,默认为10000.##此值不可能被设置成过大,因为每个socket连贯都会以"文件描述符"的形式被零碎关上,它受到零碎"文件关上个数"的限度##如果超过此值,server将会回绝连贯.# **maxclients** 10000##redis-cache所能应用的最大内存(bytes),默认为0,示意"无限度",最终由OS物理内存大小决定(如果物理内存不足,有可能会应用swap)##如果此值设置过小(比方32字节),将间接导致server无奈应用.##此值尽量不要超过机器的物理内存尺寸,从性能和施行的角度思考,能够为物理内存3/4.##此配置须要和"maxmemory-policy"配合应用,当redis中内存数据达到maxmemory时,触发"革除策略".##如果应用"革除策略"后,仍无奈失去足够的内存来存储新的数据,那么write操作的客户端将会收到"error OOM.."信息,此时server只读.##在"内存不足"时,任何write操作(比方set,lpush等)都会触发"革除策略"的执行.##在理论环境中,倡议redis的所有物理机器的硬件配置保持一致(内存统一),同时确保master/slave中"maxmemory""policy"配置统一# **maxmemory** <bytes>##"内存不足"时,数据革除策略,默认为"volatile-lru"## _volatile-lru_    ->对"过期汇合"中的数据采取LRU(近期起码应用)算法.如果对key应用"expire"指令指定了过期工夫,那么此key将会被增加到"过期汇合"中.##每个Redis对象,都保留一个“最初拜访工夫”的属性,能够用来判断此对象闲暇的工夫,那么LRU算法就能够依据此属性来进行判断。## 将曾经过期/LRU的数据优先移除.如果"过期汇合"中全副移除仍不能满足内存需要,将OOM.## _allkeys-lru_ ->对所有的数据,采纳LRU算法## _volatile-random_ ->对"过期汇合"中的数据采取"随即选取"算法,并移除选中的K-V,直到"内存足够"为止.## 如果如果"过期汇合"中全副移除全副移除仍不能满足,将OOM## _allkeys-random_ ->对所有的数据,采取"随即选取"算法,并移除选中的K-V,直到"内存足够"为止.## _volatile-ttl_ ->对"过期汇合"中的数据采取TTL算法(最小存活工夫),移除行将过期的数据.## _noeviction_ ->不做任何烦扰操作,间接返回OOM异样.#####如果数据的过期不会对"利用零碎"带来异样,且零碎中write操作比拟密集,倡议采取"_**allkeys-lru**_"# **maxmemory-policy** volatile-lru##是否开启aof性能,"yes"示意开启,在开启状况下,aof文件同步性能才失效,默认为"no"##对master机器,倡议应用AOF,对于slave,倡议敞开(采纳snapshot),**appendonly** no##aof中文件同步机制## _always_ ->任何一个aof记录都立刻进行文件同步(磁盘写入),安全性最高;如果write申请比拟密集,将会造成较高的磁盘IO开销和响应提早## _everysec_ ->每秒同步一次,性能和安全性都较高的策略,也是默认值## _no_ ->不间接同步,让文件同步交给OS管制,OS将会依据文件流通道中buffer状况/闲暇状况进行择机写入磁盘.安全性和效率与OS设定无关.**appendfsync** everysec##在aof rewrite期间,是否对aof新记录的append暂缓应用文件同步策略,次要思考磁盘IO开销和申请阻塞工夫.##默认为no,示意"不暂缓",新的aof记录依然会被立刻同步##**no-appendfsync-on-rewrite** no##aof rewrite触发机会,最小文件尺寸**auto-aof-rewrite-min-size** 64mb##aof每次rewrite之后,都会记住以后aof文件的大小,当文件增长到肯定比例后,持续进行aof rewrite**auto-aof-rewrite-percentage** 100##aof rewrite过程中,是否采取增量"文件同步"策略,默认为"yes",而且必须为yes.##rewrite过程中,每32M数据进行一次文件同步,这样能够缩小"aof大文件"写入对磁盘的操作次数.**aof-rewrite-incremental-fsync** yes##lua脚本运行的最大工夫**lua-time-limit** 5000##"慢操作日志"记录,单位:微秒(百万分之一秒,1000 * 1000),如果操作工夫超过此值,将会把command信息"记录"起来.(内存,非文件)##其中"操作工夫"不包含网络IO开销,只包含申请达到server后进行"内存施行"的工夫."0"示意记录全副操作.**slowlog-log-slower-than** 10000##"慢操作日志"保留的最大条数,"记录"将会被队列化,如果超过了此长度,旧记录将会被移除.##能够通过"SLOWLOG <subcommand> args"查看慢记录的信息(SLOWLOG get 10,SLOWLOG reset)##通过"SLOWLOG get num"指令能够查看最近num条慢速记录,其中包含"记录"操作的工夫/指令/K-V等信息**slowlog-max-len** 128##通过"TYPE key"指令查看key的数据类型##通过"OBJECT encoding key"查看key的编码类型##hash类型的数据结构在编码上能够应用ziplist和hashtable##ziplist的特点就是文件存储(以及内存存储)所需的空间较小,在内容较小时,性能和hashtable简直一样.因而redis对hash类型默认采取ziplist.##如果hash中条目标条目个数或者value长度达到阀值,将会被重构为hashtable.##ziplist中容许存储的最大条目个数,倡议为128**hash-max-ziplist-entries** 512##ziplist中容许条目value值最大字节数,倡议为1024**hash-max-ziplist-value** 64##对于list类型,将会采取ziplist,linkedlist两种编码类型.##同hash.**list-max-ziplist-entries** 512**list-max-ziplist-value** 64##zset为有序汇合,有2中编码类型:ziplist,skiplist##因为"排序"将会耗费额定的性能,当zset中数据较多时,将会被重构为skiplist.##同hash.**zset-max-ziplist-entries** 128**zset-max-ziplist-value** 64##intset中容许保留的最大条目个数,如果达到阀值,intset将会被重构为hashtable**set-max-intset-entries** 512##是否开启顶层数据结构的rehash性能,如果内存容许,请开启.##rehash可能很大水平上进步K-V存取的效率.**activerehashing** yes##客户端buffer管制##在客户端与server进行的交互中,每个连贯都会与一个buffer关联,此buffer用来队列化亟待被client承受的响应信息.##如果client不能及时的生产响应信息,那么buffer将会被一直积压而给server带来内存压力.如果buffer中积压的数据达到阀值,将会##导致连贯被敞开,buffer被移除."##buffer管制类型包含:## _normal_ -> 一般连贯## _slave_ ->与slave之间的连贯## _pubsub_ ->pub/sub类型连贯,此类型的连贯,往往会产生此种问题;因为pub端会密集的公布音讯,然而sub端可能生产有余.##指令格局:client-output-buffer-limit <class> <hard> <soft> <seconds>",其中hard示意buffer最大值,一旦达到阀值将立刻敞开连贯;##soft示意"容忍值",它和seconds配合,如果buffer值超过soft且持续时间达到了seconds,也将立刻敞开连贯,如果超过了soft然而在seconds之后##buffer数据小于了soft,连贯将会被保留.# 其中hard和soft都设置为0,则示意禁用buffer管制.通常hard值大于soft.**client-output-buffer-limit** normal 0 0 0**client-output-buffer-limit** slave 256mb 64mb 60**client-output-buffer-limit** pubsub 32mb 8mb 60##Redis server执行后台任务的频率,默认为10,此值越大示意redis对"间歇性task"的执行次数越频繁(次数/秒)##"间歇性task"包含"过期汇合"检测、敞开"闲暇超时"的连贯等,此值必须大于0且小于500.(参见redis.h源码)##此值过小就意味着更多的cpu周期耗费,后盾task被轮询的次数更频繁##此值过大意味着"内存敏感"性较差.##倡议放弃默认值**hz** 10##include指令用来载入额定的配置文件模板,也能够在redis server启动时,手动指定须要include的配置文件.# **include** /path/to/local.conf# include /path/to/other.conf启动redis进入装置后的redis目录,应用脚本运行cd /usr/local/bin ...

September 30, 2020 · 2 min · jiezi

关于redis:从零开始手写-redis三内存数据重启后如何不丢失

前言咱们在 从零手写 cache 框架(一)实现固定大小的缓存 中曾经初步实现了咱们的 cache。 咱们在 从零手写 cache 框架(一)实现过期个性 中实现了 key 的过期个性。 本节,让咱们来一起学习一下如何实现相似 redis 中的 rdb 的长久化模式。 长久化的目标咱们存储的信息都是间接放在内存中的,如果断电或者利用重启,那么内容就全副失落了。 有时候咱们心愿这些信息重启之后还在,就像 redis 重启一样。 load 加载阐明在实现长久化之前,咱们来看一下一个简略的需要: 如何在缓存启动的时候,指定初始化加载的信息。 实现思路这个也不难,咱们在 cache 初始化的时候,间接设置对应的信息即可。 api为了便于前期拓展,定义 ICacheLoad 接口。 public interface ICacheLoad<K, V> { /** * 加载缓存信息 * @param cache 缓存 * @since 0.0.7 */ void load(final ICache<K,V> cache);}自定义初始化策略咱们在初始化的时候,放入 2 个固定的信息。 public class MyCacheLoad implements ICacheLoad<String,String> { @Override public void load(ICache<String, String> cache) { cache.put("1", "1"); cache.put("2", "2"); }}测试只须要在缓存初始化的时候,指定对应的加载实现类即可。 ...

September 30, 2020 · 3 min · jiezi

关于redis:redis-持久化

咱们晓得redis很快是因为都是纯内存操作的,那如果数据仅仅在内存中,redis宕机了数据是不是就没了,此时大量的申请在redis中找不到缓存的数据,就会间接申请数据库,导致数据库挂掉。所以redis提供了RDB和AOF两种不同的长久化办法把数据存储到硬盘中。 RDB是以快照的模式,将某一时刻的数据都写入到硬盘。AOF(append-only file)是将每条执行的命令保留在硬盘,复原的时候,能够从文件里读取命令在redis里从新执行,达到复原的成果。这两种形式能够独自应用,也能够一起应用,取决于咱们的理论利用场景需要。 RDB在RDB中长久化的触发有两种:主动触发和手动触发。 手动触发手动触发有save和bgsave命令。当redis接管到save命令时,就会创立快照,并且在快照创立结束之前,不会响应其余的命令。当redis接管到bgsave命令时,redos会fork一个子过程来创立快照,而后父过程会持续解决其余的命令。尽管不会阻塞其余命令,然而创立子过程会导致redis进展,并且因为子过程会争抢资源,导致创立快照的速度会比save慢。 主动触发save配置如下:代表在N秒内,如果有了M次的变动,则触发bgsave命令,如果配置了多个,则任意一个失效都触发bgsave命令。 save <seconds> <changes>redis默认的配置如下: #900秒内有1次变动则创立快照save 900 1#300秒内有10次变动则创立快照save 300 10#60秒内有10000次变动则创立快照save 60 10000除了下面的规定被触发会执行bgsave外,redis接管到shutdown命令或者服务器敞开申请,又或者收到规范TERM信号时,会执行save命令,此时客户端的所有的申请将被阻塞,不在执行,并且在save命令执行完后敞开服务器。redis的其余对于RDB配置 # bgsava失败后是否进行接收数据stop-writes-on-bgsave-error yes# 是否对快照进行压缩rdbcompression yes# 快照文件名dbfilename dump.rdb优缺点长处: 因为是快照模式,所以实用于全量备份。复原速度快与AOF。毛病: 无奈实时创立快照,有可能造成局部数据失落。通过子过程创立快照时,如果数据文件太大,有可能导致redis短暂进行服务。AOFAOF会将redis执行过的命令追加到文件的开端,也就是说如果从文件的结尾开始执行到最初,就会失去之前一样的数据。redis默认是没有开启AOF的,须要appendonly设置为yes才开启。除了appendonly配置,还有其余配置: # 默认AOF文件名appendfilename "appendonly.aof"# 每个命令都要追加到文件,性能绝对差# appendfsync always# 每秒同步,最多会失落一秒的数据appendfsync everysec# 让操作系统决定何时同步,不可控,不举荐# appendfsync no因为AOF须要始终的写文件,会导致文件会始终增长,redis提供了AOF文件压缩的命令,BGREWRITEAOF。相似于bgsave,BGREWRITEAOF也会fork一个子过程,对AOF文件进行重写,所以一样会有因为创立子进行导致的资源争抢和内存占用的问题。对于重写,有以下配置: # 比上一次重写后的体积大于100%并且大于64m的时候重写auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb优缺点长处: 最多失落一秒的数据。把命令追加到文件开端,没有任何磁盘寻址的开销,写入的速度十分快。毛病: 文件大,复原速度慢。绝对于RDB,因为每秒须要追加日志,反对的写QPS较低。复原需复原的时候,只有把RDB或者AOF文件放在配置的门路上面,重启redis服务器就能够复原。如果同时存在两种长久化形式,并且有AOF文件,就会从AOF文件复原数据,否则从RDB文件复原数据。

September 29, 2020 · 1 min · jiezi

关于redis:Redis持久化机制

Redis数据长久化Redis作为一个内存数据库,数据是以内存为载体存储的,即断电即失(一旦Redis服务器过程退出,服务器中的数据也会隐没)。为了解决这个问题,Redis提供了长久化机制,也就是把内存中的数据保留到磁盘当中,防止数据意外失落 Redis提供了两种长久化计划:RDB长久化和AOF长久化,一个是快照的形式,一个是相似日志追加、历史记录的形式 一、RDB(Redis DataBase)RDB是快照长久化,即通过快照SnapShot的形式,在指定的工夫距离内将内存中的数据集快照写入磁盘。在创立快照之后,用户能够备份该快照,能够将快照复制到其余服务器以创立雷同数据的服务器正本,或者在重启服务器后复原数据。RDB是Redis默认的长久化形式 RDB长久化会生成rdb文件,该文件是一个压缩过的二进制文件,能够通过该文件还原快照时的数据库状态,即生成该rdb文件时的服务器数据。rdb文件默认为当前工作目录下的dump.rdb,能够依据配置文件redis.conf中SNAPSHOTTING局部的dbfilename和dir设置rdb的文件名和文件地位。默认其实能够不须要改变。只有dump.rdb文件在redis的启动目录下,redis启动的时候就会主动查看dump.rdb复原数据。 # 设置 dump 的文件名dbfilename dump.rdb# 工作目录# 例如下面的 dbfilename 只指定了文件名,# 然而它会写入到这个目录下。这个配置项肯定是个目录,而不能是文件名。dir ./获取redis装置目录 127.0.0.1:6379> config get dir1) "dir"2) "/usr/local/bin" #如果在这个目录下存在dump.rdb文件,启动redis就会主动复原数据1、快照触发机会:执行save和bgsave命令配置文件中save <seconds> <changes>规定,主动间隔性执行bgsave命令,如下图 示意在seconds秒内,至多有changes次变动,就会主动触发gbsave命令 主从复制时,从库全量复制同步主库数据,主库会执行bgsave执行flushall命令清空服务器数据执行shutdown命令敞开Redis时,默认会执行save命令(能够手动shutdown nosave就不执行save命令)2、save和bgsave命令:执行save和bgsave命令,能够手动触发快照,生成rdb文件,两者的区别如下 应用save命令会阻塞Redis服务器过程,服务器过程在rdb文件创建实现之前是不能解决任何的命令申请 127.0.0.1:6379> saveOK复制代码而应用bgsave命令不同的是,bgsave命令会fork一个子过程,而后该子过程会负责创立rdb文件,而服务器过程会持续解决命令申请。 fork()是由操作系统提供的函数,作用是创立以后过程的一个正本作为子过程 fork一个子过程,子过程会把数据集先写入临时文件,写入胜利之后,再替换之前的rdb文件,用二进制压缩存储,这样能够保障rdb文件始终存储的是残缺的长久化内容 【补充】 在生产环境中,通常咱们会将dump.rdb文件进行备份。 3、优缺点长处: 适宜大规模的数据恢复对数据完整性和一致性要求不高毛病: 在肯定间隔时间做一次备份,如果redis意外宕机,会丢掉最初一次快照后的所有批改RDB应用fork()产生子过程进行数据的长久化,如果数据比拟大的话可能就会破费点工夫,造成Redis进行服务几毫秒。如果数据量很大且CPU性能不是很好的时候,进行服务的工夫甚至会到1秒。二、AOF(Append Only File)AOF长久化会把被执行的写命令写到AOF文件的开端,记录数据的变动。默认状况下,Redis是没有开启AOF长久化的,开启后,每执行一条更改Redis数据的命令,都会把该命令追加到AOF文件中,这就相当于是把redis执行过的所有指令记录下来(读操作不记录),有点相似历史记录,复原数据时将这个文件全副执行一遍。这会升高Redis的性能,但大部分状况下这个影响是可能承受的,另外应用较快的硬盘能够进步AOF的性能。AOF保留的是appendonly.aof文件 在配置文件中redis.conf开启AOF和对于AOF的配置操作: # appendonly参数开启AOF长久化,默认是noappendonly no# AOF长久化的文件名,默认是appendonly.aofappendfilename "appendonly.aof"# AOF文件的保留地位和RDB文件的地位雷同,都是通过dir参数设置的dir ./# 同步策略# appendfsync always 示意每次写入都执行fsync,以保证数据同步到磁盘。appendfsync everysec #示意每秒执行一次fsync,可能会导致失落这1s数据。# appendfsync no 示意不执行fsync,由操作系统保证数据同步到磁盘,速度最快。# aof重写期间是否同步,默认no即可,保障数据安全no-appendfsync-on-rewrite no# 重写触发配置,设置重写的基准值auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb# 加载aof出错如何解决aof-load-truncated yes# RDB和AOF的混合长久化aof-use-rdb-preamble yes# 文件重写策略aof-rewrite-incremental-fsync yes1、AOF的实现AOF须要记录Redis的每个写命令,步骤为:命令追加(append)、文件写入(write)和文件同步(sync) ...

September 29, 2020 · 1 min · jiezi

关于redis:从零开始手写缓存之如何实现固定缓存大小

程序员的三高前段时间有一位共事体检,体检医生说他三高。 我打趣道,程序员三高不是高性能、高并发、高可用吗?你是哪三高? 每一个谋求性能的开发者,都对高性能手不释卷地谋求着,而缓存是咱们踏上这条高性能小道的必经之路。 小到 cpu 设计,大到服务分布式缓存,咱们每时每刻都在接触缓存,明天咱们就一起学习下缓存的倒退之路,以及如何如何手写一个能够指定大小的 cache。 cache 倒退之路现代社会 - HashMap当咱们利用有肯定流量之后或者查询数据库特地频繁,这个时候就能够祭出咱们的java中自带的HashMap或者ConcurrentHashMap。 咱们能够在代码中这么写: public class CustomerService { private HashMap<String,String> hashMap = new HashMap<>(); private CustomerMapper customerMapper; public String getCustomer(String name){ String customer = hashMap.get(name); if ( customer == null){ customer = customerMapper.get(name); hashMap.put(name,customer); } return customer; }}然而这样做就有个问题HashMap无奈进行数据淘汰,内存会无限度的增长,所以hashMap很快也被淘汰了。 比方以前查问,查问 redis,然而心愿能够本地缓存放一些热点数据,应用 HashMap 显然无奈满足这种需要。 当然,此处能够应用弱援用解决内存始终增长的问题。 当然并不是说他齐全就没用,就像咱们现代社会也不是所有的货色都是过期的,比方咱们中华名族的传统美德是永不过期的, 就像这个hashMap一样的能够在某些场景下作为缓存,当不须要淘汰机制的时候,比方咱们利用反射,如果咱们每次都通过反射去搜寻 Method, Field,性能必然低效,这时咱们用HashMap将其缓存起来,性能能晋升很多。 近代社会 - LRUHashMap在现代社会中难住咱们的问题无奈进行数据淘汰,这样会导致咱们内存有限收缩,显然咱们是不能够承受的。 有人就说我把一些数据给淘汰掉呗,这样不就对了,然而怎么淘汰呢?随机淘汰吗? 当然不行,试想一下你刚把A装载进缓存,下一次要拜访的时候就被淘汰了,那又会拜访咱们的数据库了,那咱们要缓存干嘛呢? 所以聪慧的人们就创造了几种淘汰算法,上面列举下常见的三种FIFO,LRU,LFU(还有一些ARC,MRU感兴趣的能够自行搜寻): FIFO先进先出,在这种淘汰算法中,先进入缓存的会先被淘汰。这种堪称是最简略的了,然而会导致咱们命中率很低。 试想一下咱们如果有个拜访频率很高的数据是所有数据第一个拜访的,而那些不是很高的是前面再拜访的,那这样就会把咱们的首个数据然而他的拜访频率很高给挤出。 LRU最近起码应用算法。 在这种算法中防止了下面的问题,每次拜访数据都会将其放在咱们的队尾,如果须要淘汰数据,就只须要淘汰队首即可。 然而这个仍然有个问题,如果有个数据在1个小时的前59分钟拜访了1万次(可见这是个热点数据),再后一分钟没有拜访这个数据,然而有其余的数据拜访,就导致了咱们这个热点数据被淘汰。 ...

September 27, 2020 · 3 min · jiezi

关于redis:Redis-越来越慢常见延迟问题定位与分析

起源:http://kaito-kidd.com/2020/07... Redis作为内存数据库,领有十分高的性能,单个实例的QPS可能达到10W左右。但咱们在应用Redis时,常常时不时会呈现拜访提早很大的状况,如果你不晓得Redis的外部实现原理,在排查问题时就会一头雾水。 很多时候,Redis呈现拜访提早变大,都与咱们的使用不当或运维不合理导致的。 这篇文章咱们就来剖析一下Redis在应用过程中,常常会遇到的提早问题以及如何定位和剖析。 应用复杂度高的命令如果在应用Redis时,发现拜访提早忽然增大,如何进行排查? 首先,第一步,倡议你去查看一下Redis的慢日志。Redis提供了慢日志命令的统计性能,咱们通过以下设置,就能够查看有哪些命令在执行时提早比拟大。 首先设置Redis的慢日志阈值,只有超过阈值的命令才会被记录,这里的单位是微秒,例如设置慢日志的阈值为5毫秒,同时设置只保留最近1000条慢日志记录: # 命令执行超过5毫秒记录慢日志CONFIG SET slowlog-log-slower-than 5000# 只保留最近1000条慢日志CONFIG SET slowlog-max-len 1000设置实现之后,所有执行的命令如果提早大于5毫秒,都会被Redis记录下来,咱们执行SLOWLOG get 5查问最近5条慢日志 127.0.0.1:6379> SLOWLOG get51)1)(integer)32693# 慢日志ID2)(integer)1593763337# 执行工夫3)(integer)5299# 执行耗时(微秒)4)1)"LRANGE"# 具体执行的命令和参数2)"user_list_2000"3)"0"4)"-1"2)1)(integer)326922)(integer)15937633373)(integer)50444)1)"GET"2)"book_price_1000"...通过查看慢日志记录,咱们就能够晓得在什么工夫执行哪些命令比拟耗时,如果你的业务常常应用O(n)以上复杂度的命令,例如sort、sunion、zunionstore,或者在执行O(n)命令时操作的数据量比拟大,这些状况下Redis解决数据时就会很耗时。 如果你的服务申请量并不大,但Redis实例的CPU使用率很高,很有可能是应用了复杂度高的命令导致的。 解决方案就是,不应用这些复杂度较高的命令,并且一次不要获取太多的数据,每次尽量操作大量的数据,让Redis能够及时处理返回。 存储大key如果查问慢日志发现,并不是复杂度较高的命令导致的,例如都是SET、DELETE操作呈现在慢日志记录中,那么你就要狐疑是否存在Redis写入了大key的状况。 Redis在写入数据时,须要为新的数据分配内存,当从Redis中删除数据时,它会开释对应的内存空间。 如果一个key写入的数据十分大,Redis在分配内存时也会比拟耗时。同样的,当删除这个key的数据时,开释内存也会耗时比拟久。 你须要查看你的业务代码,是否存在写入大key的状况,须要评估写入数据量的大小,业务层应该防止一个key存入过大的数据量。 那么有没有什么方法能够扫描当初Redis中是否存在大key的数据吗? Redis也提供了扫描大key的办法: redis-cli -h $host -p $port --bigkeys -i 0.01应用下面的命令就能够扫描出整个实例key大小的散布状况,它是以类型维度来展现的。 须要留神的是当咱们在线上实例进行大key扫描时,Redis的QPS会突增,为了升高扫描过程中对Redis的影响,咱们须要管制扫描的频率,应用-i参数管制即可,它示意扫描过程中每次扫描的工夫距离,单位是秒。 应用这个命令的原理,其实就是Redis在外部执行scan命令,遍历所有key,而后针对不同类型的key执行strlen、llen、hlen、scard、zcard来获取字符串的长度以及容器类型(list/dict/set/zset)的元素个数。 而对于容器类型的key,只能扫描出元素最多的key,但元素最多的key不肯定占用内存最多,这一点须要咱们留神下。不过应用这个命令个别咱们是能够对整个实例中key的散布状况有比拟清晰的理解。 针对大key的问题,Redis官网在4.0版本推出了lazy-free的机制,用于异步开释大key的内存,升高对Redis性能的影响。即便这样,咱们也不倡议应用大key,大key在集群的迁徙过程中,也会影响到迁徙的性能,这个前面在介绍集群相干的文章时,会再具体介绍到。 集中过期有时你会发现,平时在应用Redis时没有延时比拟大的状况,但在某个工夫点忽然呈现一波延时,而且报慢的工夫点很有法则,例如某个整点,或者距离多久就会产生一次。 如果呈现这种状况,就须要思考是否存在大量key集中过期的状况。 如果有大量的key在某个固定工夫点集中过期,在这个工夫点拜访Redis时,就有可能导致提早减少。 Redis的过期策略采纳被动过期+懈怠过期两种策略: •被动过期:Redis外部保护一个定时工作,默认每隔100毫秒会从过期字典中随机取出20个key,删除过期的key,如果过期key的比例超过了25%,则持续获取20个key,删除过期的key,周而复始,直到过期key的比例降落到25%或者这次工作的执行耗时超过了25毫秒,才会退出循环 •懈怠过期:只有当拜访某个key时,才判断这个key是否已过期,如果曾经过期,则从实例中删除 留神,Redis的被动过期的定时工作,也是在Redis**主线程**中执行的,也就是说如果在执行被动过期的过程中,呈现了须要大量删除过期key的状况,那么在业务拜访时,必须等这个过期工作执行完结,才能够解决业务申请。此时就会呈现,业务拜访延时增大的问题,最大提早为25毫秒。 而且这个拜访提早的状况,不会记录在慢日志里。慢日志中只记录真正执行某个命令的耗时,Redis被动过期策略执行在操作命令之前,如果操作命令耗时达不到慢日志阈值,它是不会计算在慢日志统计中的,但咱们的业务却感到了提早增大。 此时你须要查看你的业务,是否真的存在集中过期的代码,个别集中过期应用的命令是expireat或pexpireat命令,在代码中搜寻这个关键字就能够了。 如果你的业务的确须要集中过期掉某些key,又不想导致Redis产生抖动,有什么优化计划? 解决方案是,在集中过期时减少一个`随机工夫`,把这些须要过期的key的工夫打散即可。 伪代码能够这么写: # 在过期工夫点之后的5分钟内随机过期掉redis.expireat(key, expire_time + random(300))这样Redis在解决过期时,不会因为集中删除key导致压力过大,阻塞主线程。 另外,除了业务应用须要留神此问题之外,还能够通过运维伎俩来及时发现这种状况。 做法是咱们须要把Redis的各项运行数据监控起来,执行info能够拿到所有的运行数据,在这里咱们须要重点关注expired_keys这一项,它代表整个实例到目前为止,累计删除过期key的数量。 咱们须要对这个指标监控,当在很短时间内这个指标呈现突增时,须要及时报警进去,而后与业务报慢的工夫点比照剖析,确认工夫是否统一,如果统一,则能够认为的确是因为这个起因导致的提早增大。 实例内存达到下限有时咱们把Redis当做纯缓存应用,就会给实例设置一个内存下限maxmemory,而后开启LRU淘汰策略。 当实例的内存达到了maxmemory后,你会发现之后的每次写入新的数据,有可能变慢了。 导致变慢的起因是,当Redis内存达到maxmemory后,每次写入新的数据之前,必须先踢出一部分数据,让内存维持在maxmemory之下。 ...

September 27, 2020 · 1 min · jiezi

关于redis:Redis学习笔记一-初遇篇

原本这篇文章叫三分钟入门Redis的,发现篇幅拉的太长,就不好意思叫三分钟入门Redis了,就当学习笔记了,我的学习笔记的话,个别就是三篇: 初遇、相识、甚欢。初遇讲根本思维和根本应用,相识讲高级个性和利用,甚欢篇讲思维与实现。如果哪一篇须要一点额定的常识,则会独立进去独自成一篇文章,像写NIO学习笔记的时候,须要用到操作系统和组成原理,我就写了《操作系统与通用计算机组成原理简论》。是什么?Redis 是 Remote Dictionary Server , 直译为近程字典服务。 Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message brokerRedis是一个开源的,存储在内存中的数据结构存储器,常被用来做数据库、缓存、音讯队列。 留神Redis本人认为本人是一个存储于内存中的数据结构构造存储器,再强调一遍,Redis是一个内存中的数据结构存储器。各位明确我在强调什么了吗? 我在强调数据结构,为什么强调这个呢? 以java为例,各种各样的框架和中间件层出不穷,学着学着,就会有一种学不动的感觉了,这是失常的,然而咱们肯定要重视领会它的设计思维,重视根底,这里的根底就包含数据结构,这个在大学时代重复被强调的课程。 回顾一下,工作中的数据结构呈现率也是很高的,MySQL索引的B+树,图数据库中的图,所以我不倡议计算机专业的学生太过谋求框架和潮流的技术,感觉学校不交框架就是和社会脱节了,你根底学的好,会发现学这些框架非常的简略。 有什么长处?快内存速度十分快,而Redis将数据存储在内存中,天然也快。文档全当初我学习一项新技术,个别都先去官网先看看文档,Redis的文档真是丰盛,我想晓得的,文档上都有。构造丰盛截止以后,Redis提供的数据结构就有九种。性能齐备 多机性能: 复制Sentinel(哨兵)集群数据库治理主动过期流水线事务Lua脚本模块 长久化公布与订阅多级性能、Lua脚本、 模块、公布与订阅属于高级个性,本篇不讲。 举荐的学习材料: Redis 官网中文网站 先装置在说Redis举荐在Linux装置部署,其余操作系统上也能跑,只是性能不佳而已。目前Redis最新版本是6.0.7,个别风行都落后于最新版本,咱们本次抉择的是5.0.9。怎么装置,官网曾经讲得很分明了,不信你能够去看。 而后咱们依照官网讲的步骤,来装置一下: wget http://download.redis.io/rele...tar redis-5.0.9.tar.gz // 解压 mv redis-5.0.9 redis // 文件重命名cd redis make // 编译 make test // 测试编译是否胜利 src/redis-server // 启动redis 服务端 这个是前台启动,不改配置文件的话,启动后就始终卡这里, Redis 的官网文档真的是非常丰盛,你能够轻松的在Documentation中找到如何配置Redis ...

September 26, 2020 · 4 min · jiezi

关于redis:redisredis面试讲解

>> 面试常问问题一 redis 集群模式的工作原理能说一下么?在集群模式下, redis 的 key 是如何寻址的?分布式寻址都有哪些算法?理解一致性 hash 算法吗? 1、面试官心理剖析 在前几年,redis 如果要搞几个节点,每个节点存储一部分的数据,得借助一些中间件来实现,比如说有codis,或者 twemproxy,都有。有一些 redis 中间件,你读写 redis 中间件,redis 中间件负责将你的数据分布式存储在多台机器上的 redis 实例中。 这两年,redis 一直在倒退,redis 也一直有新的版本,当初的 redis 集群模式,能够做到在多台机器上,部署多个 redis 实例,每个实例存储一部分的数据,同时每个 redis 主实例能够挂 redis 从实例,主动确保说,如果 redis 主实例挂了,会主动切换到 redis 从实例上来。 当初 redis 的新版本,大家都是用 redis cluster 的,也就是 redis 原生反对的 redis 集群模式,那么面试官必定会就 redis cluster 对你来个几连炮。要是你没用过 redis cluster,失常,以前很多人用 codis 之类的客户端来反对集群,然而起码你得钻研一下 redis cluster 吧。 如果你的数据量很少,次要是承载高并发高性能的场景,比方你的缓存个别就几个 G,单机就足够了,能够应用 replication,一个 master 多个 slaves,要几个 slave 跟你要求的读吞吐量无关,而后本人搭建一个 sentinel 集群去保障 redis 主从架构的高可用性。 ...

September 25, 2020 · 4 min · jiezi

关于redis:redis-延迟队列

之前有介绍几种提早队列的对方,如java并发编程学习之DelayQueue、ActiveMQ - 提早队列、RabbitMQ - 提早队列,对于提早队列,我还是举荐用以上几种,这边只对redis如何实现提早队列做了一个例子。为了实现提早队列,咱们须要定义两个类型的数据: 队列,须要执行的工作,间接放入队列,线程通过队列的内容进行执行工作。有序汇合,通过成员的分数用来判断是否能够放入队列执行。咱们往有序汇合插入数据的时候,分数就是以后工夫+提早的工夫,判断的时候,就能够通过以后工夫和分数进行比拟,如果以后工夫大于分数,阐明还没到执行的时候。如果小于等于分数,则放入队列执行。示例把工作退出到有序汇合,如果分数为0,阐明没有提早,间接退出到队列中。如果分数不为0,阐明有提早,把以后工夫加上延迟时间,作为分数存入有序汇合中。 private static void add(double score) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = formatter.format(new Date()); // 马上执行的工作 if (score == 0) { JedisUtils.rpush(queue, format); } else { double date = System.currentTimeMillis() + score; JedisUtils.zadd(delay, date, format + "-" + score); }}队列解决工作。如果取到工作,间接执行,如果没有工作,则休眠10毫秒。 static String queue = "queue:";static String delay = "delay:";static class QueueThread implements Runnable { @Override public void run() { while (true) { String res = JedisUtils.lpop(queue); if (null != res) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(formatter.format(new Date()) + "---" + "解决音讯:" + res); } else { //临时没有音讯,就休眠10毫秒 try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }}提早工作解决。在while循环中 ,先依据小于以后工夫的分数取有序汇合的数据,如果有数据,阐明存在马上执行的工作,把工作从有序汇合移除,并退出到队列中。 ...

September 24, 2020 · 1 min · jiezi

关于redis:Redis做分布式锁可能不那么简单

在计算机世界里,对于锁大家并不生疏,在古代所有的语言中简直都提供了语言级别锁的实现,为什么咱们的程序有时候会这么依赖锁呢?这个问题还是要从计算机的倒退说起,随着计算机硬件的一直降级,多核cpu,多线程,多通道等技术把计算机的计算速度大幅度晋升,原来同一时间只能执行一条cpu指令的时代曾经过来。随着多条cpu指令能够并行执行的起因,原来未曾呈现的资源竞争随着呈现,在程序中的体现就是随处可见的多线程环境。比方要更新数据库的一个信息,如果没有并发管制,多个线程同时操作的话,就会呈现相互笼罩的景象产生。 锁要解决的就是资源竞争的问题,也就是要把执行的指令程序化为什么须要分布式锁随着互联网的衰亡,古代软件产生了天翻地覆的变动,以前单机的程序,曾经支撑不了古代的业务。无论是在抗压,还是在高可用等方面都须要多台计算机协同工作来解决问题。古代的互联网零碎都是分布式部署的,分布式部署的确能带来性能和效率上的晋升,但为此,咱们就须要多解决一个分布式环境下,数据一致性的问题。 当某个资源在多零碎之间共享的时候,为了保障大家拜访这个资源数据是统一的,那么就必须要求在同一时刻只能被一个客户端解决,不能并发的执行,否者就会呈现同一时刻有人写有人读,大家拜访到的数据就不统一了。 在分布式系统的时代,传统线程之间的锁机制,就没作用了,零碎会有多份并且部署在不同的机器上,这些资源曾经不是在线程之间共享了,而是属于过程(服务器)之间共享的资源。 因而,为了解决这个问题,咱们就必须引入「分布式锁」。分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行拜访。分布式锁的特点如下: 互斥性:和咱们本地锁一样互斥性是最根本,然而分布式锁须要保障在不同节点的不同线程的互斥。可重入性:同一个节点上的同一个线程如果获取了锁之后那么也能够再次获取这个锁。锁超时:和本地锁一样反对锁超时,避免死锁。高效,高可用:加锁和解锁须要高效,同时也须要保障高可用避免分布式锁生效,能够减少降级。反对阻塞和非阻塞:和 ReentrantLock 一样反对 lock 和 trylock 以及 tryLock(long timeOut)。基于redis分布式锁如果你通过网络搜寻分布式锁,最多的就是基于redis的了。基于redis的分布式锁得益于redis的单线程执行机制,单线程在执行上就保障了指令的程序化,所以很大水平上升高了开发人员的思考设计老本。然而,基于redis做分布式锁难道真的这么容易吗? 原子操作基于redis的分布式锁常用命令是 SETNX key value只在键 key 不存在的状况下,将键 key的值设置为value 。若键key 曾经存在, 则SETNX 命令不做任何动作。SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。代码示例: redis> SETNX redislock "redislock" # redislock 设置胜利(integer) 1redis> SETNX redislock "redislock2" # 尝试笼罩 redislock ,失败(integer) 0redis> GET redislock # 没有被笼罩"redislock"胜利获取到锁之后,而后设置一个过期工夫(这里防止了客户端down掉,锁得不到开释的问题) redis> expire redislock 5胜利拿到锁的客户端顺利进行本人的业务,业务代码执行完,而后再删除该key redis> DEL redislock如果所有都想设想的那么顺利,程序员TMD就不必996了。如果客户端拿到锁之后,执行设置超时指令之前down掉了(事实总是那么喜剧),那这个锁就永远都开释不了.兴许你会想到用 Redis 事务来解决。然而这里不行,因为 expire 是依赖于 setnx 的执行后果的,如果 setnx 没抢到锁,expire 是不应该执行的。事务里没有 if-else 分支逻辑,事务的特点是一口气执行,要么全副执行要么一个都不执行。公司几个亿的业务又被你耽搁了... ...

September 24, 2020 · 1 min · jiezi

关于redis:高并发下为什么更喜欢进程内缓存

过程内缓存是指缓存和应用程序在雷同地址空间。即同一个过程内。分布式缓存是指缓存和应用程序位于不同过程的缓存,通常部署在不同服务器上。从前有个机构,机构的客人叫做 CPU,这个机构专门派佣人取一些货色而后做相应的解决。上面是这个机构日常的场景。 以上故事纯属预估数据,实在数据会依据不同的硬件配置和网络环境有误差。通过以上不正经的小故事,咱们能够理解到cpu取各个设施数据的大体差距。至于YY妹子的问题,大家也应该理解了。 首先把数据从磁盘加载到内存做缓存,这个是对的。毕竟磁盘的IO速度比内存要慢的多。就拿咱们当初应用的大多数PC机以及服务器来说,磁盘往往是性能的瓶颈。如果有条件或者框架反对能够实现过程内缓存,我还是举荐应用过程内缓存,毕竟相似Redis这样的kv存储和应用程序少数状况不在一台服务器上,尽管局域网的速度肉眼看起来十分快,然而对于cpu来讲,还是让cpu休了一个大假。至于什么状况下适宜利用过程内缓存,我感觉有几点须要留神: 雷同的申请或者设置的雷同缓存key的申请每次都是同一个服务器上的同一个程序去解决,这样这个申请的缓存失常状况下只会产生一份。 如果每次申请都会路由到不同的服务器,便会产生多个缓存的正本,保护这些缓存数据的一致性是须要代价的。当有新的服务器节点退出或者服务器节点退出的时候,不能产生雪崩景象,所有缓存申请都穿透达到数据库,那是比拟要命的。比方能够看一下菜菜以前的文章:分布式缓存的一条明路(附代码)如果缓存的解决服务器发生变化,比方:因为某种原因,开始申请是由服务器A来解决,起初A服务器down了,当初由服务器B来解决,在缓存转移的过程中,必须能保证数据的正确性和一致性。程序的过程内缓存必须有过期策略,在无限内存大小的状况下,正当的应用。举荐应用LRU淘汰算法来保障内存不会撑爆。零碎的并发量及其大,对性能的要求及其高,能够思考应用过程内缓存。如果是小局部只读数据,并且访问量比拟大,例如常常应用的字典数据等,能够思考应用过程内缓存。绝对于分布式缓存,比方Redis,过程内缓存有哪些劣势呢? 过程内缓存性能比拟高,提早会更小,更节俭带宽,毕竟分布式缓存网络调用的性能和本地调用比起来慢太多,因为和应用程序位于同一过程,共享雷同的虚拟内存,所以在状态保护上更容易一些,其次过程内的缓存不设计到网络传输,所以没有序列化的过程,在性能上更胜一筹。过程内缓存的数据类型简直能够是语言级别反对的任意类型,数据类型设计上比大多数分布式缓存设施反对要灵便许多。在应答高并发的状况下,如果有适当的环境菜菜还是感觉过程内缓存为首选,另外一点程序要尽量避免线程切换,尽量异步化。如果能够最好能预估出缓存数据的大小,防止内存透露等景象产生。 当然分布式缓存有本人的劣势,在监控,容灾,扩展性,易用性等方面更胜一筹。至于用过程内还是分布式缓存,没有定论,能解决业务痛点就是最好的后果  写在最初程序如果要想最大水平的晋升并发量,缩短响应工夫, 就把用户须要的数据放在离用户最近的中央 更多精彩文章- 分布式大并发系列- 架构设计系列- 趣学算法和数据结构系列- 设计模式系列

September 22, 2020 · 1 min · jiezi

关于redis:一个-randomkey-命令导致的-Redis-事故

最近在公司对redis做一些二次开发时,发现一个randomkey命令可能导致整个redis实例长时间阻塞的问题,redis版本为3.2.9,以此记录。 问题因为咱们公司应用的是redis集群版Codis,Codis内置的redis版本比拟低,为3.2.9版本。 咱们近期在做Codis双机房时,须要对redis减少一些性能以此反对双机房,在开发和测试中发现,执行randomkey命令有可能导致整个redis长时间阻塞的问题。 randomkey次要性能是在redis中随机返回一个key进去,它随机选取key的代码如下。 从下面代码能够看进去,如果以后是个slave,并且整个实例中存在大量曾经过期的key(key已过期,但redis还未来得及删除key),执行randomkey命令时,因为找不到不过期的key,那么这个逻辑就会陷入死循环,阻塞住整个实例,整个实例不可用。 如果以后是master,执行randomkey命令时,redis会始终随机抉择key,直到找到一个不过期的key,同时会把曾经过期的key从整个实例中删除。 也就是说,在这种场景下,尽管不会长时间阻塞整个实例,但也会比执行一个一般的命令耗时要久。如果你在一个大量已过期的实例上执行randomkey命令,那可能会导致业务拜访redis变慢。 解决咱们比照了官网最新版的redis,曾经针对此问题进行了修复。 解决方案就是减少一个最大重试次数,如果整个实例都是过期key,那么最多寻找maxtries次就返回,防止阻塞整个实例。 留神点但要留神的是,如果达到了maxtries,那么返回的key是曾经过期的key,你尽管在randomkey中看到了这个key,但对这个key执行其余命令时,还是拿不到这个key的。 这个计划只针对slave上执行这个命令进行了修复,也就是不会再让redis陷入死循环。 但在master上执行这个命令还是会产生上述的变慢问题,如果你在应用redis时,常常应用这个命令,同时实例中存在大量曾经过期的key,那么redis变慢很有可能是这个问题导致的。

September 21, 2020 · 1 min · jiezi

关于redis:redis-分布式锁

在java中,咱们能够用synchronized进行加锁,也能够用lock的lock办法加锁和unlock办法开释锁。然而在多个过程或者夸服务器的状况下,这种加锁的形式就没方法用了,因为他没方法让其余客户端的人晓得是否被加锁了。所以咱们能够用redis、zookeeper、etcd等来实现。redis也有相似于lock的乐观锁,在redis - 商品交易中也展现了WATCH的应用,然而当key里的内容足够多时,监控频繁的变动反而导致性能的降落。java的map,有putIfAbsent办法,意思是如果对应的key曾经赋值了,则不能持续赋值。redis中,也有相似的办法,setnx,SET if Not eXists,也是如果对应的key有值了,则不能持续赋值,所以咱们能够用他这个办法作为分布式锁。 // 锁的keystatic String lockName = "lock:";static int cnt = 1000;static CountDownLatch countDownLatch = new CountDownLatch(cnt);@Testpublic void testLock() throws InterruptedException { JedisUtils.del(lockName); // 锁的工夫,达到这个工夫就开释 int lockTime = 1; // 锁的超时工夫,达到这个工夫就放弃获取 long timeOut = 2500; for (int i = 0; i < cnt; i++) { new Thread(new LockThread(i, lockTime, timeOut)).start(); countDownLatch.countDown(); } TimeUnit.SECONDS.sleep(3);}static class LockThread implements Runnable { int lockTime; long timeOut; int idx; public LockThread(int idx, int lockTime, long timeOut) { this.idx = idx; this.lockTime = lockTime; this.timeOut = timeOut; } @Override public void run() { try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } String lock = lock(lockName, lockTime, timeOut); if (StringUtils.isNotEmpty(lock)) { System.out.println(idx + ":获取到了锁"); //JedisUtils.del(lockName); } }}private static String lock(String lockName, int lockTime, long timeOut) { long end = System.currentTimeMillis() + timeOut; String value = UUID.randomUUID().toString(); while (System.currentTimeMillis() < end) { long setnx = JedisUtils.setnx(lockName, value); // 1阐明获取锁,返回 if (setnx == 1) { JedisUtils.expire(lockName, lockTime); return value; } try { // 没获取则休眠100毫秒持续抢锁 TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } return null;}失常状况下,下面的分布式锁能够运行的,然而有以下几个问题: ...

September 21, 2020 · 1 min · jiezi

关于redis:除了手动清理Redi还有哪些回收策略

Redis内存回收机制次要体现在以下两个方面: 删除达到工夫的键对象。内存应用达到maxmemory下限时触发内存溢出控制策略。删除过期键对象Redis所有的键都能够设置过期属性,外部保留在过期字典中。因为过程内保留了大量的键,保护每个键精准的过期删除机制会导致耗费大量的CPU,对于单线程的Redis来说老本过高,因而Redis采纳惰性删除和定时工作删除机制实现过期键的内存回收。 惰性删除:惰性删除用于当客户端读取带有超时属性的键时,如果曾经超过键设置的过期工夫,会执行删除操作并返回空,这种策略是出于节俭CPU老本思考,不须要独自保护TTL链表来解决过期键的删除。然而独自用这种形式存在内存泄露的问题,当过期键始终没有拜访将无奈失去及时删除,从而导致内存不能及时开释。正因为如此,Redis还提供另一种定时工作删除机制作为惰性删除的补充。定时工作删除:Redis外部保护一个定时工作,默认每秒运行10次(通过配置hz管制)。定时工作中删除过期键逻辑采纳了自适应算法,依据键的过期比例,应用快慢两种速率模式回收键。比方: 定时工作在每个数据库空间随机查看20个键,当发现过期时删除对应的键。如果超过查看数25%的键过期,循环执行回收逻辑直到有余25%或运行超时为止,慢模式下超时工夫为25ms。如果之前回收键逻辑超时,则在Redis触发外部事件之前再次以快模式运行回收过期键工作,快模式下超时工夫为1ms且2s内只能运行1次。快慢两种模式外部删除逻辑雷同,只是执行的超时工夫不同。内存溢出控制策略当Redis所用内存达到maxmemory下限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数管制,Redis反对6种策略,如下所示: noeviction:默认策略,当内存不足以包容新写入数据时,新写入操作会报错。应该没人用吧。allkeys-lru:当内存不足以包容新写入数据时,在键空间中,移除最近起码应用的 Key。举荐应用,目前我的项目在用这种。allkeys-random:当内存不足以包容新写入数据时,在键空间中,随机移除某个 Key。应该也没人用吧,你不删起码应用 Key,去随机删。volatile-lru:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,移除最近起码应用的 Key。这种状况个别是把 Redis 既当缓存,又做长久化存储的时候才用。不举荐。volatile-random:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,随机移除某个 Key。仍然不举荐。volatile-ttl:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,有更早过期工夫的 Key 优先移除。不举荐。如果没有对应的键,则回退到noeviction策略

September 19, 2020 · 1 min · jiezi

关于redis:7000字-Redis-超详细总结笔记建议收藏

Redis 简介Redis 是齐全开源收费的,恪守 BSD 协定,是一个高性能的 key - value 数据库 Redis 与 其余 key - value 缓存产品有以下三个特点: Redis 反对数据长久化,能够将内存中的数据保留在磁盘中,重启的时候能够再次加载进行应用。Redis 不仅仅反对简略的 key - value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储Redis 反对数据的备份,即 master - slave 模式的数据备份Redis 劣势性能极高 – Redis 读的速度是 110000 次 /s, 写的速度是 81000 次 /s 。丰盛的数据类型 - Redis 反对二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。原子性 - Redis 的所有操作都是原子性的,意思就是要么胜利执行要么失败齐全不执行。单个操作是原子性的。多个操作也反对事务,即原子性,通过 MULTI 和 EXEC 指令包起来。其余个性 - Redis 还反对 publish/subscribe 告诉,key 过期等个性。Redis 数据类型Redis 反对 5 中数据类型:string(字符串),hash(哈希),list(列表),set(汇合),zset(sorted set:有序汇合) ...

September 19, 2020 · 2 min · jiezi

关于redis:redis中间件性能问题总结

对redis性能问题的总结前言编写背景: xxx零碎因redis内存溢出导致现网故障,联合之前局部我的项目也用到redis组件且存在或多或少的问题,但都没有造成针对性的无效的剖析。思考到应用redis组件的我的项目越来越多,须要对redis进行针对性的剖析。 编写目标: 1、 不便相干人员理解redis的作用、应用办法,能够对redis的我的项目中的作用有个清晰的意识 2、 剖析redis的运行机制,对redis的缓存过程有个大抵认知 3、 列举redis的长处与有余,对redis常遇到的问题进行粗疏剖析 4、 提供性能测试过程中对redis的监控办法,及如何评估和设置redis相干参数 Redis概述1. 什么是redisRedis(REmote DIctionary Server,近程数据字典服务器)是开源的内存数据库,罕用作缓存或者音讯队列,因为该数据库是存在于零碎内存中,因而具体内存占用高,执行速度快等特点 2. Redis的特点a) Redis存在于内存,应用硬盘作为长久化;每秒十万读写,执行速度快且稳固。 b) 具备丰盛的数据结构,字符串、哈希表、列表、汇合、有序汇合;提供交加、并集、差集等操作。 c) 设置TTL存活工夫,到期主动删除。 d) Redis的所有操作都是原子性的,同时Redis还反对对几个操作全并后的原子性执行。 e) Redis反对主从及集群。 3. Redis在以后Xxx我的项目中的作用从“Redis的概述”中能够理解到redis的特点,进而对redis的作用有肯定理解。这里在在对redis的作用进行总结,以便提供一个更加清晰的意识。 Redis是一种内存型的数据库,比mysql快的多,通常被用作缓存框架和音讯解决队列,可能更疾速的解决音讯。 在XXX零碎中,redis是这样被应用的:零碎的业务数据依然存在mysql数据库中,每当一个新用户登录零碎进行查分时,会将数据从mysql数据库中取出并存入redis中,当这个用户第二次在进行查分时,会间接从redis中取数据,而不会在去查mysql数据库。因为数据曾经被缓存至redis库中,从redis中读取间接走内存,比mysql更快,能够防止大量查问会导致mysql数据库出现异常。 Redis次要机制介绍及会带来的性能问题这里介绍的次要机制为容易引起性能问题的机制 1. Redis长久化Redis提供了两种长久化形式:1 RDB快照形式 2 AOF形式 1.1 RDB长久化介绍及性能1.1.1 RDB长久化介绍满足肯定条件时,会创立一个子过程,复制以后的数据,把数据写入到硬盘中某个文件,写入实现后替换原来的存储文件,用二进制压缩。数据个别存储在dump.rdb中。UNIX零碎中反对写时复制,即刚开始会执行长久化写入磁盘的操作,如果此时有其余的数据产生扭转,就复制一份数据执行。 除了这种主动的快照形式,还反对命令形式长久化: SAVE:通过阻塞的形式,用父过程来长久化,此时无奈执行其余的申请。 BGSAVE:通过fork子过程的形式,长久化。 RDB形式的具体过程: redis调用fork,当初有了子过程和父过程。2. 父过程持续解决client申请,子过程负责将内存内容写入到临时文件。因为os的写时复制机制(copy on write)父子过程会共享雷同的物理页面,当父过程解决写申请时os会为父过程要批改的页面创立正本,而不是写共享的页面。所以子过程的地址空间内的数据是fork时刻整个数据库的一个快照。3. 当子过程将快照写入临时文件结束后,用临时文件替换原来的快照文件,而后子过程退出1.1.2 RDB长久化带来的性能问题问题1: 在保留快照时,Redis调用fork产生父过程和子过程,父过程根底解决client的申请,而子过程则负责将内存写入临时文件。内存耗费会变成原来的两倍(具体起因与子过程的执行内容无关,不在深究)。 问题2: 每次快照长久化都是将内存数据残缺写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比拟多,必然会引起大量的磁盘io操作,可能会重大影响性能。 问题3: 因为快照形式是在肯定间隔时间做一次的,所以如果redis意外down掉的话,就会失落最初一次快照后的所有批改。如果利用要求不能失落任何批改的话,这种形式实现的长久化有会有问题 1.1.3 RDB长久化性能问题解决思路针对上述可能呈现的性能问题,通过材料收集,总结可对应的解决办法如下: 问题一,两倍内存问题: 必须保留足够的内存。对内存的估算办法:对缓存数据进行评估,预估出最大的缓存数据容量,则给redis预留的缓存空间至多为最大缓存数据容量的两倍 问题二,大量磁盘IO操作: 对于数据量十分大的状况下,倡议减少redis服务做主从或者集群,防止大量io导致服务器呈现高负载的状况 问题三,最新数据失落 该问题不属于性能问题。能够通过AOF长久化形式解决 1.2 AOF长久化介绍及性能1.2.1 AOF长久化介绍aof 长久化是redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof)。当redis重启时会通过从新执行文件中保留的写命令来在内存中重建整个数据库的内容。AOF长久化以日志的模式记录服务器所解决的每一个写、删除操作,查问操作不会记录. ...

September 17, 2020 · 3 min · jiezi

关于redis:论程序的健壮性就看Redis

“众里寻他千百度,蓦然回首,那人却在,灯火阑珊处”。多年的IT生涯,始终心愿本人写的程序可能有很强的健壮性,也始终心愿能找到一个高可用的标杆程序去借鉴学习,不畏惧内存溢出、磁盘满了、断网、断电、机器重启等等状况。但意想不到的是,这个标杆程序居然就是从一开始就在应用的分布式缓存——Redis。 Redis(Remote Dictionary Server ),即近程字典服务,是 C 语言开发的一个开源的高性能键值对(key-value)的内存数据库。因为它是基于内存的所以它要比基于磁盘读写的数据库效率更快。因而Redis也就成了大家解决数据库高并发拜访、分布式读写和分布式锁等首选解决方案。 那么既然它是基于内存的,如果内存满了怎么办?程序会不会解体?既然它是基于内存的,如果服务器宕机了怎么办?数据是不是就失落了?既然它是分布式的,这台Redis服务器断网了怎么办? 明天咱们就一起来看看Redis的设计者,一名来自意大利的小伙,是如何打造出一个超强健壮性和高可用性的程序,从而不害怕这些状况。 一、 Redis的内存管理策略——内存永不溢出Redis次要有两种策略机制来保障存储的key-value数据不会把内存塞满,它们是:过期策略和淘汰策略。 1、 过期策略用过Redis的人都晓得,咱们往Redis里增加key-value的数据时,会有个选填参数——过期工夫。如果设置了这个参数的值,Redis到过期工夫后会自行把过期的数据给革除掉。“过期策略”指的就是Redis外部是如何实现将过期的key对应的缓存数据革除的。 在Redis源码中有三个外围的对象构造:redisObject、redisDb和serverCron。 redisObject:Redis 外部应用redisObject 对象来形象示意所有的 key-value。简略地说,redisObject就是string、hash、list、set、zset的父类。为了便于操作,Redis采纳redisObject构造来对立这五种不同的数据类型。 redisDb:Redis是一个键值对数据库服务器,这个数据库就是用redisDb形象示意的。redisDb构造中有很多dict字典保留了数据库中的所有键值对,这些字典就叫做键空间。如下图所示其中有个“expires”的字典就保留了设置过期工夫的键值对。而Redis的过期策略也是围绕它来进行的。 serverCron:Redis 将serverCron作为工夫事件来运行,从而确保它每隔一段时间就会主动运行一次。因而redis中所有定时执行的事件工作都在serverCron中执行。理解完Redis的三大外围构造后,咱们回到“过期策略”的具体实现上,其实Redis次要是靠两种机制来解决过期的数据被革除:定期过期(被动革除)和惰性过期(被动革除)。 惰性过期(被动革除):就是每次拜访的时候都去判断一下该key是否过期,如果过期了就删除掉。该策略就能够最大化地节俭CPU资源,然而却对内存十分不敌对。因为不实时过期了,本来该过期删除的就可能始终沉积在内存外面!极其状况可能呈现大量的过期key没有再次被拜访,从而不会被革除,占用大量内存。定期过期(被动革除):每隔肯定的工夫,会扫描Redis数据库的expires字典中肯定数量的key,并革除其中已过期的 key。Redis默认配置会每100毫秒进行1次(redis.conf 中通过 hz 配置)过期扫描,扫描并不是遍历过期字典中的所有键,而是采纳了如下办法:(1)从过期字典中随机取出20个键;(server.h文件下ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP配置20) (2)删除这20个键中过期的键; (3)如果过期键的比例超过 25% ,反复步骤 1 和 2; 具体逻辑如下图: 因为Redis中同时应用了惰性过期和定期过期两种过期策略,所以在不同状况下使得 CPU 和内存资源达到最优的均衡成果的同时,保障过期的数据会被及时革除掉。 2、淘汰策略在Redis可能没有须要过期的数据的状况下,还是会把咱们的内存都占满。比方每个key设置的过期工夫都很长或不过期,始终增加就有可能把内存给塞满。那么Redis又是怎么解决这个问题的呢?——那就是“淘汰策略”。 官网地址:https://redis.io/topics/lru-c...Reids官网下面列出的淘汰策略一共有8种,但从本质算法来看只有两种实现算法,别离是LRU和LFU。 LRU(Least Recently Used):翻译过去是最久未应用,依据时间轴来走,淘汰那些间隔上一次应用工夫最长远的数据。LRU的简略原理如下图: 从上图咱们能够看出,在容器满了的状况下,间隔上次读写工夫最长远的E被淘汰掉了。那么数据每次读取或者插入都须要获取一下以后零碎工夫,以及每次淘汰的时候都须要拿以后零碎工夫和各个数据的最初操作工夫做比照,这么干势必会减少CPU的负荷从而影响Redis的性能。Redis的设计者为了解决这一问题,做了肯定的改善,整体的LRU思路如下: (1)、Redis里设置了一个全局变量 server.lruclock 用来寄存零碎以后的工夫戳。这个全局变量通过serverCron 每100毫秒调用一次updateCachedTime()更新一次值。 (2)、每当redisObject数据被读或写的时候,将以后的 server.lruclock值赋值给 redisObject 的lru属性,记录这个数据最初的lru值。 (3)、触发淘汰策略时,随机从数据库中抉择采样值配置个数key, 淘汰其中热度最低的key对应的缓存数据。 注:热度就是拿以后的全局server.lruclock 值与各个数据的lru属性做比照,相差最长远的就是热度最低的。 Redis中所有对象构造都有一个lru字段, 且应用了unsigned的低24位,这个字段就是用来记录对象的热度。 LFU(Least Frequently Used):翻译成中文就是最不罕用。是按着应用频次来算的,淘汰那些应用频次最低的数据。说白了就是“开端淘汰制”!方才讲过的LRU依照最久未应用尽管能达到淘汰数据开释空间的目标,然而它有一个比拟大的弊病,如下图: 如图所示A在10秒内被拜访了5次,而B在10秒内被拜访了3 次。因为 B 最初一次被拜访的工夫比A要晚,在等同的状况下,A反而先被回收。那么它就是不合理的。LFU就完满解决了LRU的这个弊病,具体原理如下: ...

September 16, 2020 · 1 min · jiezi

关于redis:redis-根据ip查询地址

需要通常咱们的做法是把ip起始地址和完结地址转为long型寄存数据库,而后把须要查问的ip转long型,判断是否在某个ip区间内,如果在,则取出对应的地址信息,如果不在,则不存在ip地址信息。比方北京的ip范畴是0-100,上海的ip地址是200-300,那所要查问的ip是50,就属于北京,如果是250,就是上海,如果是150,那就是没有对应的地址。这边为了不便,数字是瞎编的。如果用sql的话,是这样写的: select * from table where 50>start and 50<end下面sql,start是ip初始值,end是完结值。在redis中,能够通过有序汇合对分值进行排序以及依据分数进行排序。咱们能够这样设计,在有序汇合中,member的值为id+:+s|e,s代表起始地位,e代表完结。数据如下: valuescore1:s01:e1002:s2003:e300查问的时候,能够获取小于查问值的第一个成员,比方50,取到1:s,因为是s结尾,所以阐明在某个区间内,再通过1获取详细信息。如果传入的是150,则获取到1:e,因为是e结尾,所以不在任何区间内。如果是250,取到的是2:s,因为是s结尾,所以阐明在某个区间内,再通过2获取详细信息。下面失去的1或者2获取的详细信息,是通过hash存的,1作为主键,内容寄存相应对象。 实际@Testpublic void init() { // 初始化有序汇合 JedisUtils.zadd("ip:segment", 0, "1:s"); JedisUtils.zadd("ip:segment", 100, "1:e"); JedisUtils.zadd("ip:segment", 200, "2:s"); JedisUtils.zadd("ip:segment", 300, "2:e"); // 初始化hash Map<String, String> map = new HashMap<>(); map.put("name", "北京"); JedisUtils.hmset("ip:info:1", map); Map<String, String> map2 = new HashMap<>(); map2.put("name", "上海"); JedisUtils.hmset("ip:info:2", map2);}@Testpublic void test() { getInfo(-1); getInfo(50); getInfo(150); getInfo(250);}public void getInfo(double num) { Set<String> tuples = JedisUtils.zrevrangeByScore("ip:segment", num, Integer.MIN_VALUE, 0, 1); if (tuples.size() == 0) { System.out.println("暂无对应地址"); return; } String member = tuples.iterator().next(); if (member.endsWith(":e")) { System.out.println("暂无对应地址"); return; } String id = member.replace(":s", ""); Map<String, String> map = JedisUtils.hgetAll("ip:info:" + id); System.out.println(map);}

September 16, 2020 · 1 min · jiezi

关于redis:对比-Redis-中-RDB-和-AOF-持久化

概念Redis 是内存数据库,数据存储在内存中,一旦服务器过程退出,数据就失落了,所以 Redis 须要想方法将存储在内存中的数据长久化到磁盘。 Redis 提供了两种长久化性能: RDB (Redis Database):生成 RDB 文件,保留的是 key-value 的模式。AOF (Append Only File):保留 Redis 执行过程中的写命令。生成RDB 的生成SAVE 命令会阻塞 Redis 服务过程,直到 RDB 文件创建结束为止,在服务器过程阻塞期间,服务器不能解决任何命令申请。BGSAVE 命令会派生出一个子过程,而后由子过程负责创立 RDB 文件,服务器过程(父过程)持续解决命令申请。如果两个 key 值的批改具备事务性,须要手动加事务,不然备份时可能会导致两个值不统一。 除了被动执行命令,咱们还能够通过 save 选项设置多个保留条件,只有任意一个条件满足,服务器就会执行 BGSAVE 命令: save 900 1save 300 10save 60 10000那么只有满足以下三个条件中的任意一个,BGSAVE 命令就会被执行: 服务器在900秒之内,对数据库进行了至多1次批改。服务器在300秒之内,对数据库进行了至多10次批改。服务器在60秒之内,对数据库进行了至多10000次批改。AOF 的生成只有关上 AOF 长久化性能,服务器在执行完一个写命令后,会以协定格局将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的开端。 古代操作系统中,用户在写文件时,操作系统通常会将写入数据临时保留在一个内存缓冲区外面,等到缓冲区被填满,或者超过了指定时限之后,才真正将缓冲区中的数据写入磁盘。这就有可能导致缓冲区内的数据还未写入磁盘,计算机产生停机,导致数据失落。 appendfsync 选项的值能够决定 AOF 长久化性能的效率和安全性: always 每次都同步到磁盘,效率低,安全性高;everysec 每隔一秒同步到磁盘,效率足够快,安全性高,只失落一秒的数据;no 由操作系统管制同步到磁盘的机会,速度最快,安全性最低。AOF 的重写因为 AOF 保留的是写命令,随着服务器的运行,同一个键值被操作的次数越多,单个键值就会产生多条写命令,AOF 文件就会越大,还原的工夫就会越久。 为了解决 AOF 体积收缩的问题,Redis 提供了 AOF 文件重写(rewrite)性能。 ...

September 16, 2020 · 1 min · jiezi

关于redis:Redis相关知识点整理

Redis相干知识点整顿1、Redis是什么?在web利用倒退的初期,关系型数据库倍受关注。因为过后的web站点访问量和并发量不高,交互少。在起初随着访问量的晋升,应用关系型数据库的web站点都开始在性能上呈现瓶颈。而这个瓶颈的源头都是在磁盘的读写上。磁盘的读写速度速度比起高并发业务下的访问量来说要慢的多,而对着互联网的倒退,导致对web利用的性能有了更高的需要,次要体现在: 低提早的读写速度:利用能够快速反应用户的申请反对海量的数据和流量:对于搜寻这样的大型利用而言,须要利用PB级别的数据和应答百万级别的流量大规模的集群治理:系统管理员心愿分布式应用能更简略的部署和治理宏大经营老本的考量:系统管理员心愿分布式应用能简略的部署和治理为了克服这一问题,NoSQL应运而生,同时以高性能,可扩展性强,高可用等长处宽泛受到开发人员和仓库管理人员的青眼。Redis是什么?Redis是当初最受欢迎的NoSQL数据库之一,Redis是一个应用ANSIC编码的开源数据库,蕴含多种数据结构,反对网络,基于内存,可选持久性的键值对存储数据库,个性: 基于内存运行,性能高效反对分布式,实践上能够有限扩大key-value存储系统...绝对于其余数据库类型,Redis具备的特点是: 操作具备原子性长久化高并发读写2、Redis的利用场景Redis的利用场景包含:缓存零碎(热点数据,高频读,低频写),计数器。音讯队列零碎,排行榜,社交网络和实时零碎3、Redis的数据类型和次要个性Redis提供的数据类型包含五种自在类型和一种自定义类型,这5种自有类型包含:Stirng类型,哈希类型,列表类型,汇合类型,和程序汇合类型。String类型:它是一个二进制平安的字符串,意味着它不仅能存储字符串,还能存储图片视频等多种类型,最大长度反对521MHash类型:该类型是有field和关联的value组成的map。其中,field和value都是字符串类型的。列表类型:该类型是一个插入程序排序的字符串元素汇合,基于双链表实现Set汇合类型:是一种无程序的汇合,List是有序的且惟一程序汇合类型:zset是一种有序汇合类型,每个元素都会关联一个double类型的分数权值,通过这个权值能够为汇合中的成员进行从小到大的排序,与set类型一样,其底层也是通过这个哈希表实现的4、Redis简略动静字符串基于c语言中传统字符串的缺点,Redis本人构建了一种名为简略动静字符串的形象类型,SDS简直贯通了Redis的所有数据结构。 5、事务Redis自身的每条语句是原子性的,而且对多个语句提供了事务的反对。 命令序列化原子性三阶段:开始事务-命令入队-执行事务6、Redis常见的问题-击穿什么叫缓存的击穿:当Redis获取某一个key时,因为key不存在,而必须向DB发动一次申请的行为,叫做Redis击穿引发击穿的起因: 第一次拜访歹意拜访不存在的keykey过期正当的躲避计划 服务器启动时提前进行写入标准key的命名,通过中间件拦挡对于高频拜访的key,设置正当的TTL或永不过期7、Redis常见的问题-雪崩什么叫缓存的雪崩:Redis缓存层因为某种原因宕机后,所有的申请会涌向存储层,短时间内的高并发会导致存储层挂机,称之为Redis雪崩正当的躲避计划: 应用Redis集群限流8、除Redis之外还有什么NoSQL型数据库MemCache 是一个与Redis十分类似的数据库,然而他的数据结构没有Redis丰盛。是一个分布式的通知缓存零碎,被许多网站用于晋升网站的访问速度,对于一些大型的须要被频繁拜访数据库的网站访问速度晋升非常显著Mongo9、Redis集群Redis集群有三种集群模式,别离是主从模式,Sentinel模式,Cluster模式10、主从模式主从模式是三种模式中最简略的一种,在主从赋值中,数据库分为两类,主数据库和从数据库(master和slave),特点有: 主数据库能够进行读写操作,当读写操作导致数据变动后会主动将数据同步给从数据库从数据库个别都是只读的,并且只承受从主数据库同步过去的数据一个master能够领有多个slave,一个slave只能有一个masterslave挂了不影响其余slave的读和master的读和写,重新启动后将数据从master中同步过去master挂掉之后,不影响算啦测的读,然而redis不再提供写服务,master重启后会从新对外提供master挂了之后,不会在slave当选一个master工作机制: 当slave启动后,被动向master发送SYNC命令。master接管到SYNC命令后在后盾保留快照(RDB长久化),和缓存保留快照这段时间的命令,而后将保留的快照文件和缓存的命令发送给slave,slave承受到快照文件和命令后会加载快照文件和缓存的执行命令复制初始化之后,master每次承受到的写命令都会同步发送给slave,保障主从数据统一。平安设置: 当master设置明码后,客户端拜访master须要明码,启动slave须要明码,在配置文件中配置即可,客户端拜访slave不须要明码、毛病:从下面能够看出,master节点在主从模式中惟一,若master挂掉,则redis无奈对外提供写服务主从模式的搭建11、Sentinel模式(哨兵模式)主从模式的弊病就是不具备高可用性,当主机挂掉之后,Redis不再对外提供写入操作,因而哨兵机制由此而生哨兵就是用来监控redis集群的情况,特点如下 哨兵模式是建设在主从模式的根底上,如果只有一个redis节点,哨兵机制就没有任何意义当master挂掉之后,哨兵会在slave中抉择额一个阶段作为master,并主动批改他们的配置文件,其余slave节点的属性会被批改,至多slaveof属性会更新当master启动之后,它将不再是master而是作为slave接管新的master同步数据哨兵机制因为也是一个过程,也有挂掉的时候,所以sentinel也会启动多个造成一个sentinel集群当多个sentinel配置的时候,他们之间也会相互监控当主从模式配置明码时,sentinel也会同步将配置信息批改到配置文件中,不须要放心一个sentinel或sentinel集群能够治理多个主从redis,多个sentinel也能够监控同一个redissentinel最好不要和redis部署在同一台机器上,不然redis服务器挂掉之后,sentinel也挂了工作机制 每个sentinel以每秒钟一次的频率向他所知的master及其他sentinel实例发送一个PING命令如果一个实例间隔最初一次无效回复PING的命令工夫超过down-after-milliseconds 选项所指定的值,这个实例会被sentinel标记为主观下线(就是,我每秒钟会叫你一次,设定一个工夫,若这个工夫内你还没有回复我,则我认为你曾经死了)如果一个master被标记为主观下线,则正在监督这个master的所有sentinel会每秒一次的频率确定master确实进入了主观下线状态当有足够数量的sentinel(大于等于配置文件指定的值)则在指定的工夫范畴内认为master确实进入的主观下线的状态,会被标记为主观下线(我叫你不答复,其他人叫你也不答复,则由可能宕机状态变为主观上的宕机状态)在个别状况下,没个sentinel会以每10s一次的评率向它始终的所有master,slave发送INFO命令当master被sentinel标记为主观下线时,sentinel向下线的master的所有slave发送INFO的评率会从10s一次转换成1s一次若没有足够数量的sentinel批准master曾经下线,master的下线状态就会移除,这个时候若master向sentinel的PING命令就有返回值,master的主观下线状态就会被移除当应用sentinel模式的时候,客户端就不必间接连贯redis,而是连贯sentinel中的ip和port,由sentinel来提供具体的可提供服务的redis实现,这样当master节点挂掉之后,sentinel就会感知,并将新的节点告知使用者.之后即便原来的节点重启,也只能作为slave节点.12、Cluster模式sentinel模式能够满足个别的生产需要,具备高可用性,然而当数据量过大到一台服务器放不下的状况时,主从模式或者哨兵模式就不能满足需要了,这个时候须要对数据的存储进行分片,将数据存储到多个redis实例中,cluster模式的呈现就是为了解决单机redis容量无限的问题,所以redis的数据依据肯定的队则调配到多个机器上cluster能够说是sentinel模式和主从模式的结合体,通过cluster能够实现主从和master重选性能,如果配置两个正本和三个分片的话,就须要六个redis实例,因为redis的数据是依据肯定的规定调配到cluster的不同机器的,当数据量过大时,能够新增机器进行扩容应用集群,只须要将redis配置文件中的cluster-enable配置关上即可,每个集群中至多须要三个主数据库能力失常运行,性增节点也十分不便cluster集群的特点 多个redis节点网络互联,数据共享所有的节点都是一主一从(也能够是一主多从)其中从库不提供服务,仅作为备用不反对同时解决多个key,因为redis须要将key平均的散布在每个节点上,并发量很高的状况下同时创立key-value会升高性能并导致不可预测的行为反对在线减少和删除节点客户端能够连贯任何一个主节点进行读写redis-cluster是去中心化的,连贯哪个节点都能够获取数据和设置数据(主节点),从节点只是作为备份存在,个别不提供服务注:redis-cluster是redis的分布式解决方案,无效的解决了redis分布式方面的需要,redis-cluster个别由多个节点组成,节点数量至多为6个能力保障组成残缺高可用集群.其中,三个主节点,三个从节点,单个主节点会调配槽,解决客户端的命令申请,而从节点会在主节点故障之后,顶替主节点13、数据分片策略分布式存储计划中最重要的一点就是数据分片,shading为了使得集群可能程度扩大,首要解决的问题就是如何将整个数据集依照肯定的规定调配到多个节点上,长用的数据分片办法有:范畴分片,哈希分片,一致性哈希算法和虚构哈希槽范畴分片:范畴分片假如数据是有序,将程序相邻近的数据放在一起,能够很好的反对变俩操作,这种分片模式的毛病是依照程序写的时候会存在热点(即缓存在同一个片上),当日志在写入的时候,个别的日志都是依照工夫排序的,这个时候工夫是枯燥递增的,这个时候写入的热点永远在最初一个分片上。redis-cluster采纳哈希槽分区,所有的键依据哈希函数映射到0-16383个整数槽内,计算公式slot=CRC16(KEY) & 16383.每一个节点负责保护一部分槽及所映射的键值数据虚构槽分区的特点 解耦数据和槽之间的关系,简化了节点的扩容和膨胀难度节点本身保护槽的映射关系,不须要客户端或代理服务器保护槽分区的元数据反对节点,槽和键之间的映射查问14、集群扩容 当一个 Redis 新节点运行并退出现有集群后,咱们须要为其迁徙槽和数据。首先要为新节点指定槽的迁徙打算,确保迁徙后每个节点负责类似数量的槽,从而保障这些节点的数据平均。首先启动一个 Redis 节点,记为 M4。应用 cluster meet 命令,让新 Redis 节点退出到集群中。新节点刚开始都是主节点状态,因为没有负责的>槽,所以不能承受任何读写操作,后续咱们就给他迁徙槽和填充数据。对 M4 节点发送 cluster setslot { slot } importing { sourceNodeId } 命令,让指标节点筹备导入槽的数据。 >4) 对源节点,也就是 M1,M2,M3 节点发送 cluster setslot { slot } migrating { targetNodeId } 命令,让源节>点筹备迁出槽的数据。源节点执行 cluster getkeysinslot { slot } { count } 命令,获取 count 个属于槽 { slot } 的键,而后执行步骤>六的操作进行迁徙键值数据。在源节点上执行 migrate { targetNodeIp} " " 0 { timeout } keys { key... } 命令,把获取的键通过 pipeline 机制>批量迁徙到指标节点,批量迁徙版本的 migrate 命令在 Redis 3.0.6 以上版本提供。反复执行步骤 5 和步骤 6 直到槽下所有的键值数据迁徙到指标节点。向集群内所有主节点发送 cluster setslot { slot } node { targetNodeId } 命令,告诉槽调配给指标节点。为了>保障槽节点映射变更及时流传,须要遍历发送给所有主节点更新被迁徙的槽执行新节点。15、膨胀集群膨胀节点就是将 Redis 节点下线,整个流程须要如下操作流程。 ...

September 15, 2020 · 1 min · jiezi

关于redis:redis-日志记录

参考《redis实战》 需要1、依据插入的程序记录日志,保留10条信息2、记录每种类型的日志次数,并能够按类型的次数排序剖析第一个需要按程序插入,能够应用队列,以下是队列lpush、ltrim、lrange的用法。 //从右边插入一条数据local:0>lpush queue 1"1"local:0>lpush queue 2"2"local:0>lpush queue 3"3"local:0>lpush queue 4"4"local:0>lpush queue 5"5"//保留下表为0到3的元素local:0>ltrim queue 0 3"OK"//取指定范畴队列的值local:0>lrange queue 0 -11) "5"2) "4"3) "3"4) "2"第二个需要按类型的次数进行排序,能够用有序汇合。以下是zincrby、zrevrange的用法。 // 对指定成员递增local:0>zincrby score: 1 zhangsan"1"local:0>zincrby score: 1 zhangsan"1"local:0>zincrby score: 1 lisil"1"// 返回指定范畴的值及分数local:0>zrevrange score: 0 -1 withscores1) "zhangsan"2) "2"3) "lisil"4) "1"实际在这里咱们应用Pipeline,Pipeline办法会主动的创立事务,会主动应用multi和exec包裹起用户的多个命令,可能无效的缩小客户端和redis之间的通信次数,进步性能。 @Testpublic void testLog() { String[] level = {"DEBUG", "INFO", "WARN", "ERROR"}; for (int i = 0; i < 100; i++) { Pipeline pipeline = JedisUtils.pipeline(); pipeline.lpush("log:", "音讯:" + i); pipeline.ltrim("log:", 0, 10); pipeline.zincrby("msg:", 1, level[new Random().nextInt(4)]); pipeline.sync(); } List<String> queue = JedisUtils.lrange("log:", 0, -1); System.out.println("以后队列信息:" + queue); Set<Tuple> tuples = JedisUtils.zrangeWithScores("msg:", 0, -1); System.out.println("以后音讯状况:" + tuples);}

September 15, 2020 · 1 min · jiezi

关于redis:BugRedis-608紧急发布请尽快升级

前不久,Redis 创始人发表 Redis 6.0.0 稳定版正式 GA!,前面又公布了6.0.6版本(Redis 6.0.6 公布),至于Redis 6.0 版本除了多线程以外,还有哪些牛逼的性能呢,详情请查看:Redis 6.0 除了多线程,别忘了这个牛逼个性! 不过,就在前两天,Redis 忽然公布了紧急版本 6.0.8 ,之前音讯称 6.0.7 被称作最初一个 6.x 版本,但 Redis 团队示意 6.0.8 版本升级迫切性等级为高:任何将 Redis 6.0.7 与 Sentinel 或 CONFIG REWRITE 命令配合应用的人都会受到影响,应尽快降级。 官网布告:https://groups.google.com/g/r... 次要的问题能够参考官网给出的具体解答: https://github.com/redis/redi... 从官网给出的信息来看,预计是呈现了Bug,具体更新的内容如下: Bug修复CONFIG REWRITE在通过CONFIG设置oom-score-adj-values后,能够通过CONFIG设置或从配置文件中加载,会生成一个损坏的配置文件。将会导致Redis无奈启动修改MacOS上redis-cli --pipe的问题。在不存在的密钥上,修复HKEYS/HVALS的RESP3响应。各种小的谬误修复新性能当设置为madvise时,移除THP正告。容许在群集中只读正本上应用读命令进行EXEC。在redis-cli-cluster调用命令中减少master/replicas选项。模块化API增加RedisModule_ThreadSafeContextTryLock。依据官网给出的修复计划中,能够看出是因为设置oom-score-adj-values这个参数导致的问题。官网链接:https://github.com/redis/redi...。 如果,各位读者有在应用 Redis 的,也请尽快降级吧,下载链接如下: http://download.redis.io/rele... 如果须要理解更多对于Redis的Bug、或者谬误修复的问题,请参考上面的链接: https://github.com/redis/redi...

September 15, 2020 · 1 min · jiezi

关于redis:缓存高并发问题

在高并发的条件下因为引入缓存然而可能会引发如下的问题,导致数据库服务器宕机,影响用户的体验. 缓存-穿透特点: 用户高并发的环境下,拜访一个数据库中不存在的数据就叫缓存穿透!!!(库中不存在,缓存中也就没有数据,缓存有效==穿透)解决方案: 限度用户单位工夫内的拜访次数 , 封禁IP地址. 网关过滤. 缓存-击穿特点: 某些高频的拜访数据因为操作不当导致缓存生效.从而使得大量的用户间接拜访数据库. (误操作删除了一个正在高并发拜访的数据)解决方案: 配置多级缓存 缓存-雪崩特点: 因为redis中的高频key在同一时间内大量的生效.导致用户的申请间接拜访数据库,导致宕机的危险. (flushAll,删除多个数据)解决方案: 配置多级缓存,设定不同的超时工夫.

September 14, 2020 · 1 min · jiezi

关于redis:Redis源码之压缩列表

压缩列表是列表键和哈希键的底层实现,用于寄存小的字符串及数字。 次要数据结构zlentryzlbyte:压缩列表的字节数(蕴含zlend)zltail:指向列表的最初一个元素zllen:元素个数zlend:标记是否列表的开端 `/* * 保留 ziplist 节点信息的构造 */typedef struct zlentry { // prevrawlen :前置节点的长度 // prevrawlensize :编码 prevrawlen 所需的字节大小 unsigned int prevrawlensize, prevrawlen; // len :以后节点值的长度 // lensize :编码 len 所需的字节大小 unsigned int lensize, len; // 以后节点 header 的大小 // 等于 prevrawlensize + lensize unsigned int headersize; // 以后节点值所应用的编码类型 unsigned char encoding; // 指向以后节点的指针 unsigned char *p;} zlentry;`有一点得留神一下:unsigned char ziplistInsert(unsigned char zl, unsigned char p, unsigned char s, unsigned int slen);该函数传入的参数p、s,该元素的类型并非为zlentry,而是压缩列表节点 ...

September 13, 2020 · 1 min · jiezi

关于redis:缓存服务器Redis-06-Redis哨兵机制

哨兵机制次要是为了实现redis的高可用,即高度可用性,不会轻易宕机. 高可用实现高可用实现前提如果须要实现Redis服务器的高可用,前提条件应该实现主从的配置. 复制哨兵目录1.敞开redis服务器(敞开之前的分片)2.cp -r shards sentinel复制shards目录为sentinel目录3.rm -f dump.rdb删除长久化文件4.redis-server 6379.conf & redis -server 6380.conf & redis-server 6381.conf &运行3台服务器 实现主从挂载1.搭建规定: 1)6379 当作主机 2)6380/6381 当作从机查看主从的状态: info replication2.实现主从的挂载 slaveof 主机IP 主机端口3.对于主从挂载的特点 查看从机中的状态:查看主机的状态:4.挂载谬误怎么做?阐明:因为误操作可能导致主从的构造挂载异样.如何从新挂载呢? 操作阐明:能够将redis服务器全副敞开,之后重启 默认条件下的主从的挂载则会生效. 从新挂载即可.补充阐明:因为slaveof指令在内存中失效.如果内存资源开释,则主从的关系将生效.为了实现永恒无效,应该将主从的关系写在配置文件中即可. 问题从下面晓得主从关系是写在配置文件中的,可是如果主机意外宕机了,那么由谁来批改配置文件呢? 哨兵哨兵的工作原理1.当哨兵启动时,会监控以后的主机信息.同时获取链接以后主机的从机信息. 2.当哨兵利用心跳检测机制(PING-PONG),测验主机是否失常.如果间断3次发现主机没有响应信息.则开始进行选举. 3.当哨兵选举实现之后.其余的节点都会当做新主机的从机. 哨兵机制实现1.复制哨兵配置文件:cp sentinel.conf sentinel/ 2.批改配置文件:1).批改保护模式:protected-mode no2).开启后盾运行:daemonize yes3).批改哨兵的监控,其中的1示意投票失效的数量(如:3人投票2票失效):sentinel monitor mymaster 192.168.126.129 6379 14).哨兵宕机之后的选举工夫:sentinel down-after-milliseconds mymaster 100005).批改哨兵选举的超时工夫:sentinel failover-timeout mymaster 20000 3.哨兵高可用测试:1).启动哨兵:redis-sentinel sentinel.conf2).先敞开主机,之后期待10秒之后,查看从机是否入选主机.之后再次启动主机(宕机的),查看是否为新主机的从 哨兵选举平票阐明如果有多个哨兵进行选举,如果间断3次投票失败,可能引发脑裂景象的产生. 脑裂景象产生的概率是1/8 = 12.5% 解决策略:只有减少选举的节点的数量,能够无效的升高脑裂景象的产生. SpringBoot链接哨兵入门案例 /** * 哨兵的测试 * 参数阐明: masterName: 主机的变量名称 * sentinels: 链接哨兵的汇合. * 了解: 哨兵尽管链接3台redis. 然而3台redis中存储的数据都是一样的.对于用户而言 * 就是一台. */ @Test public void testSentinel(){ Set<String> sets = new HashSet<>(); sets.add("192.168.126.129:26379");//26379为哨兵端口 JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster",sets); Jedis jedis = sentinelPool.getResource(); jedis.set("aaa", "wt"); System.out.println(jedis.get("aaa")); jedis.close(); }哨兵与分片总结分片: 1.次要的作用实现内存数据的扩容. 2.因为运算产生在业务服务器中,所以执行的效率更高. 3.Redis的分片没有高可用的成果. 如果其中一个节点呈现了问题则导致程序运行出错. ...

September 12, 2020 · 1 min · jiezi

关于redis:缓存服务器Redis-05-Redis分片机制

应用场景当咱们须要redis存储大量的数据时,只有一台redis服务器不可能满足需要;但如果扩充内容,因为须要长时间去寻址,也不能达到要求.就须要另一种形式 Redis分片阐明个别是采纳多台Redis,别离去存储用户的数据,从而实现内存数据的扩容.对于用户而言:会将Redis分片的多个Redis当作一个整体,不在乎数据存在那个中,只在乎能不能存储分片的次要作用:实现内存的扩容 筹备1.创立目录在redis根目录中创立一个shards目录2.分片搭建因为Redis启动是依据配置文件运行的,所以如果须要筹备3台redis,则须要复制3份配置文件redis.conf. 端口号顺次为6379/6380/6381.命令:cp redis.conf shards/6379.conf/cp redis.conf shards/6380.conf/cp redis.conf shards/6381.conf3.批改端口号依据配置文件名称,动静批改对应的端口即可--文件中port批改4.启动Redis启动Redis服务: redis-server 6379.conf redis-server 6380.conf redis-server 6381.conf通过ps -ef | grep redis查看服务是否启动. 入门案例 /** * 测试Redis分片机制 * 业务思路: * 用户须要通过API来操作3台redis.用户无需关怀数据如何存储, * 只须要理解数据是否存储即可. */ @Test public void testShards(){ List<JedisShardInfo> list = new ArrayList<>(); list.add(new JedisShardInfo("192.168.126.129", 6379)); list.add(new JedisShardInfo("192.168.126.129", 6380)); list.add(new JedisShardInfo("192.168.126.129", 6381)); ShardedJedis shardedJedis = new ShardedJedis(list); shardedJedis.set("2020", "redis分片"); System.out.println(shardedJedis.get("2020")); }一致性hash算法通过上边的入门案例,咱们能够看到有输入的kv后果,证实咱们的分片是胜利进行了存储的,然而咱们共启动了三个redis服务,咱们通过服务端keys *命令的查找,也发现,其数据是存储在6381端口的redis中.那么它是怎么判断存储在哪个redis中呢?--这就用到了hash一致性算法 介绍百度介绍如下:一致性哈希算法在1997年由麻省理工学院提出,是一种非凡的哈希算法,目标是解决分布式缓存的问题。在移除或者增加一个服务器时,可能尽可能小地扭转已存在的服务申请与解决申请服务器之间的映射关系。一致性哈希解决了简略哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动静伸缩等问题。 hash常见hash是8位16进制数16进制数取值:0-9 A-F 共16个数hash的取值范畴:00000000-FFFFFFFF(00000000与FFFFFFFFF是同一个数)00000000-FFFFFFFF共有2^32个数对雷同的数据取hash值雷同一致性hash阐明步骤:1.首先计算node节点hash值2.将用户的key进行hash计算,之后依照顺时针的方向找到最近的node节点之后链接,执行set操作.如图所示: ...

September 11, 2020 · 1 min · jiezi

关于redis:redis-笔记

Redis 应用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。为什么Redis应用单线程莫O型会达到每秒万级别的解决能力? 纯内存拜访非阻塞I/O,Reis应用epoll作为I/O多路复用技术的实现,再加上Redis本身的工夫解决模型将epoll中的连贯、读写、敞开都转换为工夫,不再网络I/O上节约过多的工夫。单线程防止了线程切换和竞态产生的耗费。常用命令:set key value [ex second] px[millsenconds] [nx|xx]set 命令有几个选项: ex seconds : 为键设置秒级过期工夫px millseconds :为键设置毫秒级过期工夫nx :键必须不存在,才能够设置胜利,用于增加xx :键必须存在,才能够设置胜利,用于更新 获取值get key批量设置值mset key1 value1 key2 value2批量获取kkeumget key1 key2计数 incr keyincr 命令用于对值做自增操作,返回后果分三种状况 值不少整数,返回谬误值是整数,返回自增后的后果键不存在,依照值位0自增,返回后果是1不常用命令: append key value 向字符串尾部追加值strlen key 字符串长度getset key value 设置并返回原值setrange key offset value 设置指定地位的字符串getrange key start end 获取指定局部的字符串外部编码 字符串类型外部的编码有3种 int : 8 个字节的长整型embstr :小于等于39个子节点字符串raw : 大于39个字节的字符串

September 11, 2020 · 1 min · jiezi

关于redis:redisbitmap在签到和统计状态项目中的妙用

一、应用redis-bitmap背景和劣势1.我的项目背景最近做的需要中,有个每日签到性能,须要用户每日签到,并且统计累计签到了多少天等这样的信息。在思考我的项目计划的时候,最简略的就是mysql,记录下用户每天签到的数据,每个用户每天签到的时候都insert一条数据,利用mysql统计起来也超级不便,然而;思考一个问题,一个用户一年的签到数据将是365条,如果用户数大的话,将是365*用户数这么多条记录,这些记录仅仅是一个状态数据,一个签到状态实际上用“1”或者“0”这样的数字就能齐全示意进去,那么有没有更好更节俭存储空间的形式呢?实际上,遇到这种只须要存储 “是”或者“否” 状态的数据的时候,能够尝试是否能够应用bitmap这种数据结构来存储数据。2.什么是bitmapbitmap能够设想成一个围棋盘这种点位阵,每个格子代表一个bit位,只能存储1或者0两种数据。因而,bitmap应用场景实际上就是只能存储“状态”类型数据,比方咱们将在我的项目中用到的签到数据,因为签到状态只有两种,一种已签到能够用“1”示意,一种未签到能够用“0”示意。3.bitmap劣势bitmap最大的劣势就是能够存储海量数据并且疾速检索到具体的数据。咱们再回到之前说的签到性能的mysql计划,一个用户一天的签到数据蕴含uid、sign_status、created_at,最起码三个int类型字段,一个int类型是4个字节,三个int就是12个字节,也就是一个用户一天的数据最低占12个字节,千万用户一年的数据是1000w x 365 x 12=438000w字节=42773M,请记住这个42773M,是mysql存储占用的空间,大概41G。那么咱们来计算一下用bitmap存储千万用户一年的数据占据多少空间。如果咱们用户uid大部分是间断的,那么每天所有的用户签到数据都存在一个key,比方uid=1000的用户签到,就将这个key的第1000位设置为1,这样实际上所有用户一年的数据只有365条,一条数据保留1000w个bit,因为一个字节8个bit,就是125w字节,那么千万用户一年的数据是125w x 365=45625w字节=445M。以上,咱们晓得,应用mysql保留一年千万用户最低42773M空间,而应用bitmap保留一年千万用户最低445M,bitmap空间只占了mysql的1%,这个差异真的是天差地别。另外bitmap查问数据的时候,redis提供了相干命令能够间接统计一个key中有多少个1,也能够间接获取到具体那个位bit的值。其中,在一台2010MacBook Pro上,offset为2^32-1(调配512MB)须要~300ms,offset为2^30-1(调配128MB)须要~80ms,offset为2^28-1(调配32MB)须要~30ms,offset为2^26-1(调配8MB)须要8ms。所以查问某个位的值的时候速度也是相当快,redis中一个string类型的key限度不能超过512M。二、redis-bitmap相干命令介绍Setbit 语法:setbit key offset value 形容: 对key所贮存的字符串值,设置或革除指定偏移量上的位(bit)。 位的设置或革除取决于 `value` 参数,能够是 `0` 也能够是 `1` 。 当 `key` 不存在时,主动生成一个新的字符串值。 字符串会进行舒展(grown)以确保它能够将 `value` 保留在指定的偏移量上。当字符串值进行舒展时,空白地位以 `0` 填充。 留神: `offset` 参数必须大于或等于 `0` ,小于 2^32 (bit 映射被限度在 512 MB 之内)。 因为 Redis 字符串的大小被限度在 512 兆(megabytes)以内, 所以用户可能应用的最大偏移量为 2^29-1(536870911) , 如果你须要应用比这更大的空间, 请应用多个 `key。` 当生成一个很长的字符串时, Redis 须要分配内存空间, 该操作有时候可能会造成服务器阻塞(block)。 在2010年出产的Macbook Pro上, 设置偏移量为 536870911(512MB 内存调配)将消耗约 300 毫秒, 设置偏移量为 134217728(128MB 内存调配)将消耗约 80 毫秒, 设置偏移量 33554432(32MB 内存调配)将消耗约 30 毫秒, 设置偏移量为 8388608(8MB 内存调配)将消耗约 8 毫秒。getbit ...

September 10, 2020 · 2 min · jiezi

关于redis:缓存服务器Redis-02-redis-API

本文次要说一下redis的操作命令,即API String类型1.set增加key-valueset username admin 2.get依据key获取数据get username 3.strlen依据key获取值的长度strlen key 4.exists判断key是否存在exists name返回1存在 0不存在 5.del删除redis中的keydel key 6.Keys用于查问符合条件的keykeys * 查问redis中全副的keykeys n?me 应用占位符获取数据keys nam* 获取nam结尾的数据 7.mset赋值多个key-valuemset key1 value1 key2 value2 key3 value3 8.mget获取多个key的值mget key1 key2 9.append对某个key的值进行追加append key value 10.type查看某个key的类型type key 11.select切换redis数据库select 0-15 redis中共有16个数据库 12.flushdb清空单个数据库flushdb 13.flushall清空全副数据库flushall 14.incr主动加1incr key 15.decr主动减1decr key 16.incrby指定数值增加incrby 10 17.decrby指定数值减decrby 10 18.expire指定key的失效工夫 单位秒expire key 20key20秒后生效 19.pexpire指定key的生效工夫 单位毫秒pexpire key 2000key 2000毫秒后生效 20.ttl查看key的残余存活工夫ttl key -2数据不存在 -1该数据永不超时 21.persist撤销key的生效工夫persist key Hash类型能够用散列类型保留对象和属性值--例:User对象{id:2,name:小明,age:19} 1.hset为对象增加数据hset key field value ...

September 10, 2020 · 1 min · jiezi

关于redis:基于Docker搭建Redis集群

先决条件装置docker、docker-compose.docker-compose.yml文件version: '3'services: redis-node01: container_name: redis-node01 image: docker.io/redis:6.0.7-alpine ports: - "6378:6379" volumes: - /data/redis/data/master:/data - /data/redis/config:/usr/local/etc/redis networks: - redis-network restart: always command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes --appendfsync everysec labels: registory: 'office-registry' editor: 'huyindu' redis-node02: container_name: redis-node02 image: docker.io/redis:6.0.7-alpine ports: - "6377:6379" volumes: - /data/redis/data/slave01:/data - /data/redis/config:/usr/local/etc/redis networks: - redis-network restart: always command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes --appendfsync everysec labels: registory: 'office-registry' editor: 'huyindu' redis-node03: container_name: redis-node03 image: docker.io/redis:6.0.7-alpine ports: - "6376:6379" volumes: - /data/redis/data/slave02:/data - /data/redis/config:/usr/local/etc/redis networks: - redis-network restart: always command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes --appendfsync everysec labels: registory: 'office-registry' editor: 'huyindu'networks: redis-network: driver: bridge批改Redis配置文件咱们先下载Redis的官网配置文件,下载地址:http://download.redis.io/redis-stable/redis.conf ...

September 10, 2020 · 1 min · jiezi

关于redis:redis-商品交易

参考《redis实战》 需要1、商品交易的主体是集体,集体领有姓名、资产属性。2、每个人都有N个商品,每个商品都有惟一的商品编号。3、容许商品投放到市场进行交易,一经投放,则商品从集体转移到市场,市场的商品依照价格排序。4、交易须要判断资产是否足够,如果足够,商品从市场转移到买家5、商品从集体到市场,或者市场到集体,都须要事务判断剖析第一个需要能够用键值对保留姓名和资产信息,主键为用户的id,hincrBy办法对资产进行减少或缩小。以下是hmset、hgetall、hincrBy的用法。 // 赋值local:0>hmset person:1 name zhangsan funds 100"OK"// 显示hash内容local:0>hgetall person:11) "name"2) "zhangsan"3) "funds"4) "100"// 对hash的某个key的值进行相加local:0>hincrBy person:1 funds 20"120"// 显示hash内容local:0>hgetall person:11) "name"2) "zhangsan"3) "funds"4) "120"第二个需要能够用汇合来存储商品,主键是商品的id,汇合里放商品的编号。以下是sadd、smembers、sismember、srem的用法。 // 退出汇合local:0>sadd package:1 itemX"1"local:0>sadd package:1 itemY"1"local:0>sadd package:1 itemZ"1"// 显示汇合元素local:0>smembers package:11) "itemZ"2) "itemX"3) "itemY"// 汇合是否存在某个元素,1为是local:0>sismember package:1 itemX"1"// 移除汇合的元素local:0>srem package:1 itemX"1"// 汇合是否存在某个元素,0为否local:0>sismember package:1 itemX"0"// 显示汇合元素local:0>smembers package:11) "itemZ"2) "itemY"第三个需要市场的商品排序,须要按价格排序,所以能够用有序汇合。以下是zadd、zrevrange、zrem的用法。 // 新增元素local:0>zadd market: 10 itemX.1"1"local:0>zadd market: 11 itemY.1"1"local:0>zadd market: 12 itemY.2"1"// 从高到低排序local:0>zrevrange market: 0 -1 withscores1) "itemY.2"2) "12"3) "itemY.1"4) "11"5) "itemX.1"6) "10"// 移除local:0>zrem market: itemY.1"1"local:0>zrevrange market: 0 -1 withscores1) "itemY.2"2) "12"3) "itemX.1"4) "10"第四个需要从hash取出资产,跟商品金额进行判断 ...

September 9, 2020 · 2 min · jiezi

关于redis:搞定面试官系列当面试官问你如何保证缓存和数据库双写一致性时他到底想问什么

面试开始小伙子你好,看你简历上写到了MySQL和Redis。明天咱们就围绕他们两个开展吧。Redis和MySQL是后端开发中举重若轻的重要角色。理论开发中二者也基本上如影随行,为了进步性能和响应,Redis经常寄存热点数据,MySQL寄存所有数据,保证数据长久化。所以Redis能够说是MySQL的一部分数据。 那么问题来了,当MySQL中的长久化数据产生扭转,如何告诉Redis呢?也即如何保障缓存和数据库数据的双写一致性呢?面试官您好,咱们在开发中采取的计划是:先更新数据库,而后删除相应缓存,直到下次申请缓存发现没有数据,再从MySQL中读取,同时将数据更新到Redis。 那为什么采纳删除缓存而不是更新缓存呢?如下图所示,如果采取更新缓存的形式,可能呈现申请A先于申请B产生,更新缓存应该比申请B更新缓存早才对,然而因为网络等起因,B却比A更早更新了缓存,这就导致了脏数据。其次,如果你是一个写数据库场景比拟多,而读数据场景比拟少的业务需要,采纳更新缓存的计划就会导致数据压根还没被读取过,但缓存已被频繁的更新,节约性能。 那先删除缓存,再更新数据库会不会有什么问题?如下图所示,申请A进行写操作,删除缓存,申请B查问发现缓存不存在,就去数据库查问失去旧值,之后将旧值写入缓存,此时申请A再将新值写入数据库。这种状况就会导致数据不统一的情景呈现。而且,如果不采纳给缓存设置过期工夫策略,该缓存数据永远都是脏数据。 好的,刚刚说的计划的确在并发环境中都会有问题。那你们采纳先更新数据库,再删除缓存,这种计划肯定不会呈现并发问题吗?答案是不肯定,也可能会呈现并发问题。如下图所示,当步骤Step3的更新数据库操作比步骤Step2的读取数据库操作耗时更短,就有可能使得步骤Step4先于步骤Step5,此时缓存中的就是脏数据。但个别状况下数据库的读操作的速度是远快于写操作的(此点从MySQL的并发读写量就可看出,雷同硬件配置下并发读的效率是并发写的数倍)。 因而,如果你想实现根底的缓存和数据库双写统一的逻辑,那么在大多数状况下,在不想做过多设计、减少太大工作量的状况下,请先更新数据库,再删除缓存!先更新数据库,再删除缓存,除了你刚刚说得问题,还会有别的问题吗?如果MySQL采纳了读写拆散架构,当申请A更新数据在master库上并删除了缓存,但此时数据库主从同步还未实现。申请B查问缓存产生Cache miss之后从slave库上读取到的还是旧值,此时也会造成数据不统一。 你刚刚说到先更新数据库,再删除缓存也有可能造成数据不统一,怎么解决呢?采纳延时双删。如下图所示,申请A更新数据库之后,为避免删除缓存后行产生于申请B的将缓存写入旧值,能够通过将申请A更新完数据库之后休眠一会(例如100ms,200ms,依据理论业务场景拟定),再删除缓存,这样根本能保障缓存中寄存的不会是脏数据。主从架构也是这个原理,就是申请A在更新master之后不必立刻删除缓存,通过延时双删保障主从同步曾经实现,最初删除缓存数据。 但你这种计划,申请A休眠一段时间的话,可能会影响到接口的RT,升高零碎的吞吐量,如何解决呢?这里比拟优雅的计划是通过异步实现。即开启一个线程池,在申请A的时候开启一个独自的线程,异步的休眠一段时间而后执行缓存删除。当然也能够通过将缓存中相应的key扔到音讯队列,通过MQ异步删除,但仅为了异步删除缓存就多加了一层音讯队列,可能会造成零碎设计更加简单,并且会带来别的问题。 后面始终有提到删除缓存,如果删除缓存失败了怎么办呢?再加一个重试机制,保障删除缓存胜利。 如果我肯定要数据库和缓存数据一致性怎么办?没有方法做到相对的一致性,这是由CAP实践决定的,缓存零碎实用的场景就是非强一致性的场景,所以它属于CAP中的AP。 CAP实践是分布式系统中的经典实践,即一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。依据BASE(Basically Available,Soft State和Eventually Consistent)实践,缓存和数据库只能做到数据的最终一致性。 面试完结工夫也不早了,明天就到这里吧,看进去小伙子对这块把握的比拟深刻。咱们公司就短少你这种人才,要不当初就签把Offer签了吧。这个时候你必定欲绝还迎,一手接Offer一边摆摆手:不行不行,深圳马那边也急着等我给回复呢,催了我好几天了。面试官一听,payroll组何在,加价! 小结应用缓存并不是一个很简略的事件,尤其在须要缓存与数据库放弃强统一的场景,才晓得让数据库数据和缓存数据放弃一致性是一门很浅近的学识。从远古的硬件缓存,操作系统缓存开始,缓存就是一门独特的学识。这个问题也被业界探讨了十分久,争执至今也无果,因为其实这是一个衡量的问题。我是少侠露飞,爱技术,爱分享。

September 9, 2020 · 1 min · jiezi

关于redis:缓存服务器Redis-01

缓存机制阐明缓存机制次要的目标:就是升高用户拜访物理设施的频次.提供用户的查问的效率. 衍生: 能够利用缓存服务器无效的升高用户拜访数据库的压力.例如:最简略的,咱们能够手写一个Map,通过AOP来在内存中发进行查问数据的存储,不便下一次的查问 缓存的因素如果实现缓存须要应用什么样的数据存储构造--- K-V缓存服务的开发应该应用什么语言--- C语言缓存服务运行的环境在内存中.缓存服务运行环境在内存中 如果断电之后数据将全副删除--- 长久化(磁盘)缓存的数据都在内存中,如果始终存储数据则必然导致内存溢出--- 内存优化LRU算法/LFU算法Redis介绍Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它能够用作数据库、缓存和消息中间件。 它反对多种类型的数据结构,如 字符串(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反对5种数据类型 速度快: 读 11.2万次/秒 写 8.6万次/秒 均匀大概10万次/秒 Redis装置1.上传安装包将tar.gz压缩包拖至MobaXterm中/usr/local/src目录下 2.解压tar -xvf解压-->mv命令将tar.gz放在soft目录下-->mv命令将其改名为redis 3.装置Redis要求:在redis的根目录中执行 /usr/local/src/redis/命令1: make命令2: make install 4.批改Redis配置文件vim redis.conf-->1).将IP绑定正文2).敞开保护模式3).开启后盾运行 5.Redis应用命令我将redis根底的启动敞开等称为应用命令;客户端中的kv值的操作称为操作命令,具体的操作命令下一篇文章写 1.启动命令: redis-server redis.conf2.校验服务是否运行失常: ps -ef | grep redis3.进入客户端命令: redis-cli -p 63794.敞开Redis命令:1).形式1. kill -9/15 PID号2).形式2. redis-cli -p 6379 shutdown ...

September 9, 2020 · 1 min · jiezi

关于redis:记一次Redis存储设计优化的线上问题

优化前:数据结构 string优化后:数据结构 sorted set + string 性能redis 键名列表页专辑id + sorted setnovel:hot_ids列表页专辑id对应的详情 + stringnovel:hot_12345问题场景:设置了24小时过期工夫,上线后没问题,第2天缓存过期后,列表页没有数据,页面空白。 起因:数据结构批改后,被动更新未革除sorted set缓存,导致只有列表页更新,id永不过期, 但string 24h过期了,前台sorted set获取分页列表的ids, 再依据id获取string中的详情时拿不到数据。Redis::zAdd("novel:hot_ids", 12345);Redis::setex("novel:hot_12345", 24*3600, json_encode("a"=>1));Redis::expire("novel:hot_ids", 24*3600); 解决方案:被动更新缓存前删除redis keyRedis::del("novel:hot_ids");Redis::setex("novel:hot_12345", 24*3600, json_encode("a"=>1));Redis::expire("novel:hot_ids", 24*3600);

September 5, 2020 · 1 min · jiezi

关于redis:Redis面试系列-第一话小黑板引君入瓮面试官初露峥嵘

前言:目前Redis相干的常识内容曾经成为后端面试的考核常客了,把握并了解Redis能够为你的面试大大加分哦,另外想要在面试中怀才不遇,靠死记硬背标准答案是不可取的。在本系列中,小黑板将会以面试为导向,串联起Redis的相干常识,帮忙大家在面试中牢牢抓住面试官的小心心~ 话不多说,咱们间接进入正题!咱们循序渐进地模仿一下实在的面试场景,读者敌人们也能够尝试作答,看看面对面试官的“拷问”,你会给出怎么的答复。 Q1:我看你的简历上写的有用过redis,那你能大略讲一下你理解哪些货色吗?或者讲一下你对redis的意识也行剖析:这个问题就是最常见的热身问题了,然而也不可小觑,因为你答复的内容会作为接下来面试官的次要切入点,所以肯定要答复本人把握的内容,你能够不全理解,然而须要保障你提到的点是你可能讲清楚的,这一点十分重要!并且倡议分点作答,会显得更加有条理性,是很大的加分项! A:好的,Redis的话其实有着十分丰盛的应用场景,比方能够缓存、排行榜啊、独特关注列表啊之类的场景下都能够应用Redis 那其实提到redis首先会有几个特点让我印象比拟粗浅: 第一点,redis是单线程的第二点,redis是基于内存的,速度十分快第三点,redis提供了丰盛的数据结构剖析:一般来说答复出以上几点就能够很好的为面试官提供切入点了,面试是一个互动的过程,如何将面试官引入你事后设计好的“陷阱”中是一门学识,只有筹备好了,不怕你问,就怕你不问,嘿嘿~ Q2:你提到了Redis是单线程的,那它为什么这么快?剖析:这个问题算是redis面试中的高频问题了,那么如何答复上来的同时体现本人的思考就十分重要了。因为面试官这个问题可能曾经问了几十个人了,如果你也依照所谓的标准答案来作答,那样的话是无奈给面试官留有深刻印象的哦。答复这个问题首先要讲清楚Redis的单线程到底指的是什么?其实是Redis的外围模块是单线程的,并不是全部都是单线程的,此外,Redis6.0是反对多线程的,这一点能讲进去其实就是加分项了,阐明你有自主学习的驱动力。其次,须要阐明外围模块是单线程的,那么redis是怎么保障速度的,依照这个思路分点答复,并进行拓展即可。 A:Redis的单线程指的其实是指执行Redis命令的外围模块是单线程的,也就是文件事件处理器,次要由四个局部组成:套接字、IO多路复用、文件事件分派器和事件处理器 并且Redis6.0其实曾经反对多线程来进步性能了。而Redis之所以快次要起因有三点 第一,Redis是基于内存的,数据都寄存在内存中,速度十分快第二,Redis的IO模型是非阻塞IO第三,Redis外围模块的单线程使得能够防止线程切换带来的开销Q3:既然聊到了IO,那你说一下你对IO模型的理解吧,理解哪些IO模型?具体说一下?剖析:IO模型的问题同样是面试常客,大部分同学在面试的时候可能就会答复IO模型有阻塞IO、非阻塞IO、IO多路复用之类的,而后把概念一摆,两手一摊,给面试官的感觉就是你在着跟我背课文呢。这样的面试天然获得不到好的成果,还是那句话,面试官心愿听到的答复中须要有你本人的思考和总结,这样你的答复才具备肯定的区分度,很多同学对于同步阻塞、同步非阻塞IO这些概念性的货色十分的迷糊,其实没有那么难,看上来就晓得了 A:实际上IO的过程次要蕴含了两个阶段 第一阶段,期待数据筹备好第二阶段,将数据从内核空间拷贝到应用程序地址空间依据这两个阶段的不同体现来将IO进行分类: 在第一个阶段阻塞即为阻塞IO,不阻塞间接返回则为非阻塞IO在第二个阶段阻塞则为同步IO,不阻塞则为异步IO多路复用IO其实是通过轮询机制来负责多个socket补充:对于同步阻塞IO、同步非阻塞IO、异步IO、IO多路复用等很多初学者了解起来比拟艰难,而网络上的文章品质又参差不齐,很容易误导读者,前面贴心的小黑板会专门写一篇对于IO的文章,帮忙大家在面试中轻松应答这道考题 Q4:既然Redis是单线程的,那么它如何利用多核呢?A:能够通过开启多个Redis实例来利用多核 依据你抛出的前两点,其实常问的问题差不多就是这样,重头戏其实在第三点,Redis的数据结构局部。既有根底数据结构如hash、list、set等,同样有高级数据结构Hyperlogger、布隆过滤器等等。 这些数据结构的用法以及底层原理和实现常常会在面试过程中被问到,答复好的话是十分亮眼的加分项,在下一回合的交锋中,面试官又会如何针对这些知识点进行考查呢?在答复这些问题的时候又有哪些套路呢? 欲知后事如何,欢送大家点点手指关注小黑板哦~

September 4, 2020 · 1 min · jiezi

关于redis:redis-windows本地服务安装

首先下载redis下载解压到集体磁盘中例如:D:ProgramFilesredis运行cmd命令应用管理员关上D:ProgramFilesredis>redis-server.exe --service-install redis.windows.conf --loglevel verbose [12820] 06 Sep 11:00:26.431 # HandleServiceCommands: system error caught. error code=1073, message = CreateService failed: unknown error呈现这个谬误意味着redis曾经存在解决办法: 1)先卸载服务: redis-server --service-uninstall 2)而后再装置: redis-server--service-install redis.windows.conf 启停: 启动服务:redis-server --service-start 进行服务:redis-server --service-stop

September 4, 2020 · 1 min · jiezi

关于redis:Redis二进制安全的原理

全是干货的技术号:本文已收录在github,欢送 star/fork:https://github.com/Wasabi1234...二进制平安二进制平安是一种次要用于字符串操作函数相干的计算机编程术语。一个二进制平安函数,其本质是将操作输出作为原始的、无任何特殊字符意义的数据流。其在操作上应蕴含一个字符所能有的256种可能的值(假如为8比特字符) 那什么是特殊字符?标记字符,如本义码,0结尾的字符串(如C语言中的字符串),不是二进制平安的。 场景在解决未知格局的数据,例如随便的文件、加密数据及相似状况时,二进制平安性能是必须的。函数必须晓得数据长度,以便函数操作整体数据。 Redis二进制平安原理struct sdshdr{ int len;//buf数组中曾经应用的字节的数量,也就是SDS字符串长度 int free;//buf数组中未应用的字节的数量 char buf[];//字节数组,字符串就保留在这外面};redis通过定义上述构造体的形式,扩大了C语言底层字符串的毛病,字符串长度的获取工夫复杂度从原来的O(N)变成了O(1),另一方面也能够通过free的动静扭转来缩小内存的调配。须要强调一点的是buf数组不是存储的字符,而是二进制数组,因为C语言字符串两头是不能呈现空字符的,而二进制数据两头很有可能会有空字符,所以C语言是二进制不平安的,而redis又是二进制平安。为了存储多种类型的数据,redis就间接把所有数据当作二进制来存储,这样就能够存储媒体文件和字符串,所以SDS尽管叫简略动静字符串,然而它可不只是用来保留字符串。SDS在Redis中是实现字符串对象的工具。当你对该字符串取值时是通过len属性判断理论内容的长度,而后取的值。拼接字符串时是追加到free空间中的。 简略总结: 二进制平安的意思就是,只关怀二进制化的字符串,不关怀具体格局,只会严格的依照二进制的数据存取,不会妄图以某种非凡格局解析数据。 Redis的简略动静字符串SDS比照C语言的字符串char*,有以下个性: 能够在O(1)的工夫复杂度失去字符串的长度能够高效的执行append追加字符串操作SDS通过判断以后字符串空余的长度与须要追加的字符串长度,如果空余长度大于等于须要追加的字符串长度,那么间接追加即可,这样就缩小了从新分配内存操作;否则,先用sdsMakeRoomFor函数对SDS进行扩大,依照肯定的机制来决定扩大的内存大小,而后再执行追加操作,扩大后多余的空间不开释,不便下次再次追加字符串,这样做的代价就是节约了一些内存,然而在Redis字符串追加操作很频繁的状况下,这种机制能很高效的实现追加字符串的操作。

September 4, 2020 · 1 min · jiezi

关于redis:聊聊claudb的NotificationManager

序本文次要钻研一下claudb的NotificationManager NotificationManagerclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/event/NotificationManager.java public class NotificationManager implements PatternSubscriptionSupport { private final DBServerContext server; private final ExecutorService executor = Executors.newSingleThreadExecutor(); public NotificationManager(DBServerContext server) { this.server = server; } public void start() { // nothing to do } public void stop() { executor.shutdown(); } public void enqueue(Event event) { executor.execute(() -> patternPublish(server, event.getChannel(), event.getValue())); }}NotificationManager实现了PatternSubscriptionSupport接口,其结构器要求输出DBServerContext,它提供了start、stop、enqueue办法;其stop办法执行executor.shutdown();其enqueue办法应用executor异步执行patternPublish(server, event.getChannel(), event.getValue())patternPublishclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/pubsub/PatternSubscriptionSupport.java public interface PatternSubscriptionSupport extends BaseSubscriptionSupport { String PSUBSCRIPTION_PREFIX = "psubscription:"; String PMESSAGE = "pmessage"; //...... default int patternPublish(DBServerContext server, String channel, SafeString message) { int count = 0; for (Tuple2<String, ImmutableSet<SafeString>> entry : getPatternSubscriptions(server.getAdminDatabase(), channel)) { count += publish(server, entry.get2(), toPatternMessage(entry.get1(), channel, message)); } return count; } default ImmutableSet<Tuple2<String, ImmutableSet<SafeString>>> getPatternSubscriptions(Database admin, String channel) { return getPatternSubscriptions(admin).entries().filter(subscriptionApplyTo(channel)); } //......}patternPublish办法遍历getPatternSubscriptions(server.getAdminDatabase(), channel),挨个执行publish(server, entry.get2(), toPatternMessage(entry.get1(), channel, message)BaseSubscriptionSupportclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/pubsub/BaseSubscriptionSupport.java ...

September 3, 2020 · 2 min · jiezi

关于redis:Redis做消息队列全攻略

Redis音讯队列在程序员这个圈子打拼了太多年,见过太多的程序员应用redis,其中一部分喜爱把redis做缓存(cache)应用,其中最典型的当属存储用户session,除此之外,把redis作为音讯队列应用也不在少数,可见redis在互联网中利用是如许的宽泛。 redis作为音讯队列应用,redis反对的数据结构是能够撑持这类业务,次要是利用了list这种数据结构的个性。Redis的列表相当于编程语言外面的 LinkedList,是一个双向的列表构造,这意味着列表新增和删除元素是十分快的,工夫复杂度为O(1),然而查找一个元素的时候须要遍历列表,工夫复杂度为O(n)。因为列表的元素操作和音讯队列操作相似,所以redis能够实用于音讯队列的场景,当然,在实用于的栈的场景下也能够胜任。 须要揭示一下,生产环境中如果对音讯的可靠性有非常高的要求(比方订单领取的生产音讯),请应用业余的音讯队列(例如:rmq,amq等),对音讯的失落有肯定容忍度的程序齐全能够应用redis,例如咱们的日志收集程序 列表这种数据结构的命令为 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到期待超时或发现可弹出元素为止。BLPOP key1 [key2 ] timeout移出并获取列表的最初一个元素, 如果列表没有元素会阻塞列表直到期待超时或发现可弹出元素为止。BRPOP key1 [key2 ] timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到期待超时或发现可弹出元素为止。BRPOPLPUSH source destination timeout 通过索引获取列表中的元素LINDEX key index 在列表的元素前或者后插入元素LINSERT key BEFORE|AFTER pivot value 获取列表长度LLEN key 移出并获取列表的第一个元素LPOP key 将一个或多个值插入到列表头部LPUSH key value1 [value2] 将一个值插入到已存在的列表头部LPUSHX key value 获取列表指定范畴内的元素LRANGE key start stop 移除列表元素LREM key count value 通过索引设置列表元素的值LSET key index value 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。LTRIM key start stop 移除列表的最初一个元素,返回值为移除的元素。RPOP key 移除列表的最初一个元素,并将该元素增加到另一个列表并返回RPOPLPUSH source destination 在列表中增加一个或多个值RPUSH key value1 [value2] 为已存在的列表增加值RPUSHX key value 缺点音讯队列的实质还是消费者和生产者的问题,只有是这样的场景,就会波及到两端不均衡的状况,具体可体现为: ...

September 3, 2020 · 1 min · jiezi

关于redis:微信开发系列之七-使用Redis存储微信聊天记录

In the second blog Wechat development series 2 – development Q&A service using nodejs of this series, we have developed a kind of Q&A service in Wechat which leverages a free Tuning Restful service so that you could chat with this service: In this blog, I will show the steps how to store all of those conversation logs via Redis, so that you can review the conversations within Wechat app or delete them if necessary. ...

September 2, 2020 · 4 min · jiezi

关于redis:聊聊claudb的string-command

序本文次要钻研一下claudb的string command GetCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/GetCommand.java @ReadOnly@Command("get")@ParamLength(1)@ParamType(DataType.STRING)public class GetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { return convert(db.get(safeKey(request.getParam(0)))); }}GetCommand实现了DBCommand接口,其execute办法执行db.get(safeKey(request.getParam(0)))MultiGetCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/MultiGetCommand.java @ReadOnly@Command("mget")@ParamLength(1)public class MultiGetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { ImmutableArray<DatabaseValue> result = request.getParams() .map(DatabaseKey::safeKey) .filter(key -> db.isType(key, DataType.STRING)) .map(db::get); return convert(result); }}MultiGetCommand实现了DBCommand接口,其execute办法遍历request.getParams()取出type为DataType.STRING,而后挨个执行db::getSetCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/SetCommand.java @Command("set")@ParamLength(2)public class SetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { return com.github.tonivade.purefun.type.Try.of(() -> parse(request)) .map(params -> onSuccess(db, request, params)) .recover(this::onFailure) .get(); } private RedisToken onSuccess(Database db, Request request, Parameters parameters) { DatabaseKey key = safeKey(request.getParam(0)); DatabaseValue value = parseValue(request, parameters); return value.equals(saveValue(db, parameters, key, value)) ? responseOk() : nullString(); } private DatabaseValue parseValue(Request request, Parameters parameters) { DatabaseValue value = string(request.getParam(1)); if (parameters.ttl != null) { value = value.expiredAt(Instant.now().plus(parameters.ttl)); } return value; } private RedisToken onFailure(Throwable e) { return Pattern1.<Throwable, RedisToken>build() .when(instanceOf(SyntaxException.class)) .returns(error("syntax error")) .when(instanceOf(NumberFormatException.class)) .returns(error("value is not an integer or out of range")) .otherwise() .returns(error("error: " + e.getMessage())) .apply(e); } private DatabaseValue saveValue(Database db, Parameters params, DatabaseKey key, DatabaseValue value) { DatabaseValue savedValue = null; if (params.ifExists) { savedValue = putValueIfExists(db, key, value); } else if (params.ifNotExists) { savedValue = putValueIfNotExists(db, key, value); } else { savedValue = putValue(db, key, value); } return savedValue; } private DatabaseValue putValue(Database db, DatabaseKey key, DatabaseValue value) { db.put(key, value); return value; } private DatabaseValue putValueIfExists(Database db, DatabaseKey key, DatabaseValue value) { DatabaseValue oldValue = db.get(key); if (oldValue != null) { return putValue(db, key, value); } return oldValue; } private DatabaseValue putValueIfNotExists(Database db, DatabaseKey key, DatabaseValue value) { return db.merge(key, value, (oldValue, newValue) -> oldValue); } private Parameters parse(Request request) { Parameters parameters = new Parameters(); if (request.getLength() > 2) { for (int i = 2; i < request.getLength(); i++) { SafeString option = request.getParam(i); if (match("EX", option)) { if (parameters.ttl != null) { throw new SyntaxException(); } parameters.ttl = parseTtl(request, ++i) .map(Duration::ofSeconds) .getOrElseThrow(SyntaxException::new); } else if (match("PX", option)) { if (parameters.ttl != null) { throw new SyntaxException(); } parameters.ttl = parseTtl(request, ++i) .map(Duration::ofMillis) .getOrElseThrow(SyntaxException::new); } else if (match("NX", option)) { if (parameters.ifExists) { throw new SyntaxException(); } parameters.ifNotExists = true; } else if (match("XX", option)) { if (parameters.ifNotExists) { throw new SyntaxException(); } parameters.ifExists = true; } else { throw new SyntaxException(); } } } return parameters; } private boolean match(String string, SafeString option) { return string.equalsIgnoreCase(option.toString()); } private Option<Integer> parseTtl(Request request, int i) { Option<SafeString> ttlOption = request.getOptionalParam(i); return ttlOption.map(SafeString::toString).map(Integer::parseInt); } private static class Parameters { private boolean ifExists; private boolean ifNotExists; private TemporalAmount ttl; } private static class SyntaxException extends RuntimeException { private static final long serialVersionUID = 6960370945568192189L; }}SetCommand实现了DBCommand接口,其execute办法先通过parse办法解析成parameters,这里反对EX、PX、NX、XX参数,若语法错误抛出SyntaxException,而后onFailure会针对不同异样进行解决;onSuccess办法则通过parseValue(request, parameters)结构DatabaseValue,而后执行saveValue办法,该办法会针对ifExists执行putValueIfExists,针对ifNotExists执行putValueIfNotExists,其余的执行putValueMultiSetCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/MultiSetCommand.java ...

September 2, 2020 · 6 min · jiezi

关于redis:windows下安装Redis-5x版本

Windows用户装置Redis 5.x及以上版本目前颇费周折,写下此文让同道中人少走弯路。1. 下载windows安装包并解压如果是Linux版本能够间接到官网下载,自3.x起官网和微软网站就没有redis安装包更新了,好在github有开发者在编译公布更新(目前最新有5.0.9版本可下),地址:redis windows 5+版本下载 下载zip版本解压即可: 2. 启动服务并验证解压后间接运行目录下的 redis-server.execmd新起一个命令行窗口,切换到redis目录,输出 redis-cli 进入redis命令行,验证装置是否胜利。 diboot 简略高效的轻代码开发框架

September 2, 2020 · 1 min · jiezi

关于redis:redis-购物车

参考《redis实战》 需要1、记录用户最初一次拜访的工夫,并统计用户的最新拜访记录2、记录用户最近浏览过的商品,商品数量为53、记录商品被拜访的次数,仅显5个热门数据4、购物车增加移除商品剖析第一个需要统计用户的最新拜访记录这种类型的,都能够用有序汇合来依据工夫进行排序。并且如果新增的member一样,则会更新score,刚好能够更新最初一次的拜访工夫。比方上面的例子,通过zadd减少成员,通过zrange失去范畴内的值。 local:1>zadd score 1 zhangsan"1"local:1>zadd score 2 zhangsan"0"local:1>zadd score 2 lisi"1"local:1>zrange score 0 -1 withscores1) "lisi"2) "2"3) "zhangsan"4) "2"第二个需要因为仅保留最近浏览过的5个商品,所以能够用有序汇合来进行过滤。比方上面的例子,通过zremrangebyrank移除,仅保留一个分值高的。 local:1>zadd score 1 zhangsan"1"local:1>zadd score 2 lisi"1"local:1>zremrangebyrank score 0 -2"1"local:1>zrange score 0 -1 withscores1) "lisi"2) "2"local:1>第三个需要跟第三个需要雷同 第四个需要购物车的商品及数量,能够当做键值对,拜访的人能够当做key来解决。用hmset进行保留或更新成员,通过hdel来移除成员。 local:1>hmset cart:A itemA 1"OK"local:1>hmset cart:A itemB 2"OK"local:1>hgetall cart:A1) "itemA"2) "1"3) "itemB"4) "2"local:1>hdel cart:A itemA"1"local:1>hgetall cart:A1) "itemB"2) "2"local:1>hmset cart:A itemB 3"OK"local:1>hgetall cart:A1) "itemB"2) "3"实际拜访页面及商品页@Testpublic void testLoginAndView() { String zhangsan = "zhangsan"; System.out.println("张三登录网站"); view(zhangsan, null); System.out.println("张三拜访商品"); view(zhangsan, "itemA"); view(zhangsan, "itemB"); view(zhangsan, "itemC"); view(zhangsan, "itemA"); view(zhangsan, "itemB"); view(zhangsan, "itemA"); view(zhangsan, "itemD"); view(zhangsan, "itemE"); view(zhangsan, "itemF"); view(zhangsan, "itemG"); System.out.println("张三最初登录工夫:" + JedisUtils.zscore("lastTime:", zhangsan)); System.out.println("张三最近浏览商品:" + JedisUtils.zrevrange("view:" + zhangsan, 0, -1)); System.out.println("商品被浏览信息:" + JedisUtils.zrangeWithScores("item:", 0, -1)); String lisi = "lisi"; System.out.println("李四登录网站"); view(lisi, null); System.out.println("李四拜访商品"); view(lisi, "itemA"); view(lisi, "itemB"); view(lisi, "itemC"); System.out.println("李四最初登录工夫:" + JedisUtils.zscore("lastTime:", lisi)); System.out.println("李四最近浏览商品:" + JedisUtils.zrange("view:" + lisi, 0, -1)); System.out.println("商品被浏览信息:" + JedisUtils.zrangeWithScores("item:", 0, -1)); String wangwu = "wangwu"; System.out.println("王五登录网站"); view(wangwu, null); System.out.println("王五拜访商品"); view(wangwu, "itemA"); view(wangwu, "itemB"); view(wangwu, "itemC"); System.out.println("李四最初登录工夫:" + JedisUtils.zscore("lastTime:", wangwu)); System.out.println("李四最近浏览商品:" + JedisUtils.zrange("view:" + wangwu, 0, -1)); System.out.println("商品被浏览信息:" + JedisUtils.zrangeWithScores("item:", 0, -1)); System.out.println("用户登录信息:" + JedisUtils.zrange("lastTime:", 0, -1));}private void view(String token, String item) { long timestamp = System.currentTimeMillis(); // 更新最初拜访的工夫 JedisUtils.zadd("lastTime:", timestamp, token); if (StringUtils.isNotEmpty(item)) { JedisUtils.zadd("view:" + token, timestamp, item); JedisUtils.zremrangeByRank("view:" + token, 0, -6); JedisUtils.zincrby("item:", 1, item); JedisUtils.zremrangeByRank("item:", 0, -6); }}退出购物车@Testpublic void testCart() { String zhangsan = "zhangsan"; System.out.println("itemA退出购物车2件"); addCart(zhangsan, "itemA", 2); System.out.println("itemB退出购物车1件"); addCart(zhangsan, "itemB", 1); System.out.println("itemC退出购物车1件"); addCart(zhangsan, "itemC", 1); System.out.println("购物车信息:" + JedisUtils.hgetAll("cart:" + zhangsan)); System.out.println("itemB缩小1件"); addCart(zhangsan, "itemB", -1); System.out.println("itemA缩小1件"); addCart(zhangsan, "itemA", -1); System.out.println("购物车信息:" + JedisUtils.hgetAll("cart:" + zhangsan));}private void addCart(String token, String item, int num) { List<String> list = JedisUtils.hmget("cart:" + token, item); if (JedisUtils.hexists("cart:" + token, item)) { num += Integer.valueOf(list.get(0)); } if (num <= 0) { JedisUtils.hdel("cart:" + token, item); } else { JedisUtils.hset("cart:" + token, item, num + ""); }}

September 2, 2020 · 2 min · jiezi

关于redis:历史数据-股票工具

历史数据 - 股票工具,分享一个m费的股票历史数据下载工具,是个在线的工具,测试能够下载所有A股、港股、美股的个股历史数据,Excel,比拟不便 只有两步就能下载: 填股市代码、邮箱5分钟后收到股市历史数据Excel放在公主号下载也实属无奈,心愿大家体谅【不然很容易被歹意下载】,股票历史数据下载是很不便的,在公主号里进行1、2两个步骤就能够了,个别5分钟就能收到股票历史数据。 在线下载很容易被有限歹意下载,十分影响失常用户的下载,【失常用户基本挤不过歹意下载的】。所以起初为了屏蔽掉这些歹意下载股票历史数据的,就设置了通过公主号提交、邮件发送的形式。 就是因为在线下载容易被攻打、歹意下载,所以最初抉择了放在公主号,这样的话要攻打也是先攻打公主号,应该还没有谁有这么大的本事,所以这种办法【一是M费(0¥),二是能短暂】,还是心愿大家能了解,搜公主号【数据即服务】即可两步下载,0¥,不必转发分享,帖子就在这里,不能下载回来骂我 当初做在线下载的,基本上都是套路要钱的,不信你找一圈还得回来... 股票历史数据 import reimport pandasimport requestsurl = '指标网站的url' # 这里不写具体网站的链接了,大体做法都是一样的。response = requests.get(url).text # 失去网页的源代码###上面是解析收盘数据的代码,大部分都能够这么参考,简略的正则表达式###times = re.findall('class="first left bold noWrap">(.*?)</td>').group(1) # 获取到所有历史的交易工夫open_price = re.findall('class="first left bold noWrap">.*?</td>\s+<td data-real-value="(.*?)"').group(1) # 失去所有历史开盘价数据close_price = re.findall('class="first left bold noWrap">.*?</td>\s+<td.*?</td>\s+<td data-real-value="(.*?)">').group(1) # 失去所有历史收盘价数据high_price = re.findall('class="first left bold noWrap">.*?</td>\s+<td.*?</td>\s+<td.*?</td>\s+<td data-real-value="(.*?)">').group(1) # 失去所有历史收盘价数据###上面是数据下载老本地数据局部,以下载为本地Excel为例###df = pandas.DataFrame(a, columns=['open_price', 'close_price', 'vol']) # 把数据转成DataFrame格局wt = ExcelWriter(path) # path 是文件的保留门路,要准确到文件名。数据下载老本地数据之后,就要到这个门路去找。例如:C:\\Users\\Administrator\\Desktop\\股票所有历史收盘数据下载老本地数据.xls 就依照这个门路找到保留好的Excel就行了df.to_excel(wt, sheet_name='如何下载股票的历史收盘价' , index=False)历史数据 - 股票工具 ...

September 1, 2020 · 1 min · jiezi

关于redis:聊聊claudb的scripting-command

序本文次要钻研一下claudb的scripting command AbstractEvalCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/AbstractEvalCommand.java abstract class AbstractEvalCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { return script(request).map(script -> execute(request, script)) .getOrElse(error("NOSCRIPT No matching script. Please use EVAL")); } private RedisToken execute(Request request, SafeString script) { int numParams = parseInt(request.getParam(1).toString()); if (numParams + 2 > request.getLength()) { return error("invalid number of arguments"); } List<SafeString> params = request.getParams().stream().skip(2).collect(toList()); List<SafeString> keys = readParams(numParams, params); List<SafeString> argv = readArguments(numParams, params); return LuaInterpreter.buildFor(request).execute(script, keys, argv); } protected abstract Option<SafeString> script(Request request); private List<SafeString> readParams(int numParams, List<SafeString> params) { List<SafeString> keys = new LinkedList<>(); for (int i = 0; i < numParams; i++) { keys.add(params.get(i)); } return keys; } private List<SafeString> readArguments(int numParams, List<SafeString> params) { List<SafeString> argv = new LinkedList<>(); for (int i = numParams; i < params.size(); i++) { argv.add(params.get(i)); } return argv; }}AbstractEvalCommand实现了DBCommand接口,其execute办法先通过子类实现的script办法获取SafeString,而后再外部的execute办法执行脚本;execute办法先解析keys、argv,而后通过LuaInterpreter.buildFor(request).execute(script, keys, argv)执行lua脚本EvalCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/EvalCommand.java ...

August 31, 2020 · 2 min · jiezi

关于redis:来吧展示Redis的分布式锁及其实现Redisson的全过程

前言分布式锁是管制分布式系统之间同步访问共享资源的一种形式。在分布式系统中,经常须要协调他们的动作。如果不同的零碎或是同一个零碎的不同主机之间共享了一个或一组资源,那么拜访这些资源的时候,往往须要互斥来避免彼此烦扰来保障一致性,这个时候,便须要应用到分布式锁。 什么是分布式锁1.在分布式环境中应用到的锁就是分布式锁2.在分布式环境中对不同应用程序操作的共享资源进行加锁就是分布式锁 分布式环境1.同一个利用下的子利用采纳独自部署的形式运行就是分布式2.只有利用存在跨JVM就是分布式环境 为什么要有分布式锁JDK中原生锁(Synchronized、Lock)只针对是同一个JVM实例上操作资源而言,对于不同JVM的操作是没方法进行加锁的。 分布式锁的利用场景只有存在跨JVM操作,并且存在共享资源竞争问题,就是必须应用到分布式锁的场景。 分布式锁有哪些常见的分布式锁有以下几种:1.基于数据库(乐观锁)实现分布式锁2.基于Redis的分布式锁Redisson3.基于Zookeeper实现分布式锁 基于redis分布式锁原理获取锁通过Redis创立一个惟一的key,如果以后线程能创立这个惟一的key,则示意以后线程获取到锁。 开释锁当删除Redis中的代表锁的惟一key,则示意开释锁。 什么是死锁在开释锁时出现异常,Redis中代表锁的惟一key未被删除,而其余线程始终在自旋期待并心愿可能获取锁,但事实上所有线程都没能获取到锁的状况称为死锁。 如何解决死锁咱们能够通过对Redis中代表锁的惟一Key设置过期工夫来防止死锁的产生。 如何防止锁被其余线程开释创立锁时记录线程ID,本人的锁只能本人开释。 如何保障锁的可重入性当线程获取到锁后在Redis中把以后线程的ID做为key的值进行存储,加锁时判断以后线程与Redis锁的值是否统一。 基于redis分布式锁Redisson配置pom.xml<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>site.yangpan</groupId> <artifactId>yangpan-spring-boot</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>site.yangpan.redission</groupId> <artifactId>yangpan-spring-boot-redission</artifactId> <version>0.0.1-SNAPSHOT</version> <name>yangpan-spring-boot-redission</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--上面这两个依赖就是集成redission的依赖--> <!--第一个依赖与spring boot版本有关系,参考官网文档--> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-21</artifactId> <version>3.13.3</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.13.3</version> </dependency> </dependencies></project>留神:网上很多都是间接引入redission依赖,然而我这里是通过Spring Boot Starter的形式引入 配置application.properties# 公共spring boot配置spring.redis.host=127.0.0.1spring.redis.port=6379spring.redis.password=#spring.redis.cluster.nodes=#spring.redis.sentinel.master=#spring.redis.sentinel.nodes=## Redisson 配置spring.redis.redisson.config=classpath:redisson.yaml配置redisson.yamlsingleServerConfig: idleConnectionTimeout: 10000 pingTimeout: 1000# connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500# reconnectionTimeout: 3000# failedAttempts: 3 password: subscriptionsPerConnection: 5 clientName: address: "redis://127.0.0.1:6379" subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 connectionMinimumIdleSize: 32 connectionPoolSize: 64 database: 0# dnsMonitoring: false dnsMonitoringInterval: 5000threads: 0nettyThreads: 0codec: !<org.redisson.codec.JsonJacksonCodec> {}#"transportMode":"NIO"编写RedissonSpringDataConfig接下来咱们注册 RedissonConnectionFactory 到 Spring context ...

August 30, 2020 · 2 min · jiezi

关于redis:聊聊claudb的transaction-command

序本文次要钻研一下claudb的transaction command TransactionStateclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/TransactionState.java public class TransactionState implements Iterable<Request> { private List<Request> requests = new LinkedList<>(); public void enqueue(Request request) { requests.add(request); } public int size() { return requests.size(); } @Override public Iterator<Request> iterator() { return requests.iterator(); }}TransactionState实现了Iterable<Request>接口,它定义了requests属性,提供了enqueue办法将request增加到requests中MultiCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/transaction/MultiCommand.java @Command("multi")@TxIgnorepublic class MultiCommand implements DBCommand { private static final String TRASACTION_KEY = "tx"; @Override public RedisToken execute(Database db, Request request) { if (!isTxActive(request.getSession())) { createTransaction(request.getSession()); return responseOk(); } else { return error("ERR MULTI calls can not be nested"); } } private void createTransaction(Session session) { session.putValue(TRASACTION_KEY, new TransactionState()); } private boolean isTxActive(Session session) { return session.getValue(TRASACTION_KEY).isPresent(); }}MultiCommand实现了DBCommand接口,其execute办法先判断isTxActive,非active的话才createTransactionExecCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/transaction/ExecCommand.java ...

August 30, 2020 · 1 min · jiezi