关于redis:redis

数据结构字符串 getset:返回旧值,设置新值 setnx key value 如果key不存在的话设置,没有工夫参数 setex key 60 value,key存在的时候设置,并且有工夫参数// sds数据结构type sds struct { free int len int buffers []int}1. sds 和c比拟 1. 获取长度O(1),c是O(N) 2. 杜绝了缓冲区的溢出,因为c不查看字符串的长度,很容易笼罩到其余字符串 3. 内存重调配次数,C操作的字符串数组,每次减少或者删除,都要进行内存调配操作,遗记开释内存,会导致内存泄露,减少不查看,会导致缓冲区溢出,内存重调配可能会进行零碎调用,影响性能 4. redis 采纳空间预调配,和惰性开释,保障字符串的扩大和缩容高效。 5. 二进制平安2. 编码方式 1. int编码,如果整数能用long示意的 2. embstr编码:redis对象和sds在空间是上间断的,调配一次内存 小于44字节 3. raw:redis对象和sds对象不在一起,内存调配两次哈希 1. 先计算哈希值 2. 计算索引值,而后放到hash表上列表 type listNode struct { next *listNode pre *listNode val }redis 链表是一个双向的汇合 汇合的中的元素无无序的,所以不能依据下标返回,只能返回所有汇合能够计算并集,交加,差集,判断元素是否属于汇合,随机返回一个汇合或者多个汇合实现形式:intsettypedef struct intset {uint32_t encoding;uint32_t length;int8_t contents[];} intset;intset 数据是有序的,从小到大排序,能够进行二分查找有序汇合1.压缩列表2.跳表redis 过期策略和内存淘汰策略过期策略: 定期删除策略:每隔一段时间从过期的key中,随机筛选一部分key进行删除(100ms运行一次)惰性删除策略:当拜访过期key的时候进行删除。定时删除: 在设置过期的键的同时,发明一个定时器,等到过期的到来时,立即删除(对cpu不敌对)redis采纳惰性删除,和定期删除策略 ...

March 8, 2022 · 1 min · jiezi

关于redis:深入理解redisRedis的事务

1.数据库的事务概念2.redis的事务处理3.redis的事务性质总结 1.数据库的事务概念 咱们最后接触事务这个概念的时候,是在大学课堂上,学习数据库的时候。 事务:事务是数据库操作的最小工作单元,这一操作要么齐全地执行,要么齐全不执行。因而事务处理能够确保除非事务中的所有操作都能胜利实现,否则不会执行这些命令。 当咱们提交一个事务的时候,该事务的任何一条sql再执行的时候,都会生成一条撤销日志,而撤销日志中记录的是和以后操作齐全相同的操作,所以咱们常说的数据库回滚就是去执行这些反操作日志。 事务的四大个性: 1 、原子性 事务中的所有操作要么全都执行,要么全都不执行。2 、一致性 事务的后果必须是从一个一致性状态变到另外一个一致性状态。因而当数据库只蕴含胜利事务提交的后果的时候,就证实数据库处于一个统一的状态。如果数据库系统在运行事务的时候产生了故障,有一半的事务执行胜利,个别执行失败,这时的数据库就处于一种不统一的状态。3 、隔离性 事务和事务之间是隔离的,不能互相烦扰, 4 、持续性 一个事务一旦提交,对数据库中的数据的扭转是永久性的。 2.redis的事务处理redis的事务,能够一次性执行多个命令,实质是一组命令的汇合。一个事务中的所有命令都会被序列化,依照程序地串行执行而不被其它命令插入。 咱们先开看一下redis反对事务的命令: DISCARD 勾销事务,放弃事务内所有命令EXEC 执行所有事务块内的命令MULTI 标记一个事务的开始UNWATCH 勾销watch命令对所有key的监督WATCH 监督一个或者多个key,如果事务过程中这个key没变,那么事务将被打断1)失常执行 2)放弃事务 3)事务整体执行失败 当咱们输出一个谬误的命令的时候,所有redis事务的命令是不会执行的。 4)事务局部执行,局部不执行 redis事务,不会回滚,如果一部分命令执行失败,可能会只执行一部分命令。 5)watch命令 watch会监督一个或者多个key的值,如果事务过程中的key没变,那么事务将会被打断。 咱们能够先设置一个余额,而后在余额被篡改和余额不被篡改的状况下,看看执行之后会产生什么。 余额不被篡改,失常执行: 余额被篡改,整个事务勾销执行:监控了key,如果key被批改了,前面一个事务的执行生效 总结:wathc指令,相似于乐观锁,事务提交的时候,如果key的值,已被别的客户端批改,整个事务队列都不会被执行。 通过watch指令在事务执行之前监控多个key,假使watch只有有任何key的更改,exec都将被摈弃,返回Nullmulti-bulk应答以告诉调用者事务执行失败。 由此能够得出,redis事务执行有3个阶段:1)开启:以multi开始一个事务2)入队:将多个命令入队到事务中,接到这些命令不会立即执行,而是放到期待执行的事务队列外面3)执行:由exec命令触发事务 3.redis的事务性质总结 1)独自的隔离操作:以为主线程是单线程执行的,所以事务在执行的过程当中,不会被其它客户端发送的命令打断。2)没有隔离级别的概念,队列中的命令没有提交之前都不会理论地被执行,且主线程是单线程执行,并不会存在事务内的查问要看到事务里的更新,在事务外的查问不能看到这种头疼的问题。3)不保障原子性,redis事务中如果有一条命令执行失败,其它命令依然会被执行,没有回滚。

March 8, 2022 · 1 min · jiezi

关于redis:深入理解redisRedis的持久化机制RDBAOF

1.Redis的长久化机制之RDB2.Redis的长久化机制之AOF3.总结4.应用倡议 1.Redis的长久化机制之RDB 咱们都晓得,Redis的所有数据都保留在内存里,然而redis的数据必定要进行长久化,不然当redis宕机的时候,数据就复原不回来了。 RDB是什么:RDB是在指定的工夫距离内将内存中的数据集快照写入磁盘。 运行原理:Redis会独自创立(fork)一个子线程来进行长久化,会将数据写入到一个临时文件中,期待将数据及快照写入都完结了,再用这个临时文件替换上次长久化好的文件。 整个过程中,主过程是不进行任何IO操作的,这就确保了极高的性能。如果要进行大规模的数据恢复,且对数据恢复的完整性不是很敏感(能够失落一部分数据),那RDB形式比AOF(下文会解释)形式更加高效,RDB的毛病就是最初一次长久化的数据可能会失落。 配置地位: save 秒钟 写操作次数默认值:save 60 10000 是1分钟内改了1万次,save 300 10 是5分钟内改了10次,save 900 1 是15分钟内改了1次。如何触发rdb长久化: 咱们能够期待它到点触发,也能够应用save或者bgsave被动触发。保留的文件地位,默认是和redis一起的。 不过生产上的备份文件,个别都会在另外一台机器上备份,如果备份文件也在redis服务器,就达不到容灾的目标了。 长处毛病适宜大规模的数据恢复会失落最初一次的批改对数据完整性和一致性不高fork的时候,内存中的数据要被克隆一倍,大抵2倍的膨胀性须要思考 2.Redis的长久化机制之AOF Redis的AOF是以日志的模式来记录每个写操作(RDB只记录各个键值对的最终值),将Redis执行过程中的所有写操作指令记下来(读操作不记录)。在文件的尾部减少内容,redis重启后依据日志文件的内容将写指令从头到尾执行一次实现复原工作。 如果aof的文件越写越大怎么办? AOF的重写机制:在备份文件越写越大的时候,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留能够复原数据的最小指令集。 AOF的重写原理:AOF文件持续增长而过大时,会fork出一条新线程来将文件重写,遍历新过程的内存中的数据,每条记录有一条set语句。 重写aof的操作,并不会读取旧的aof文件,而是将整个内存中的数据库内容用命令的形式重写了一个新的aof文件,这点和rdb有点相似。 长处毛病实时性高aof文件大,同步速率慢 3.总结 1)RDB长久化形式可能在指定的工夫距离对你的数据进行长久化。 2)AOF长久化形式记录每次对服务器的写操作,当服务器重启的时候就会从新执行这些命令来复原原始的数据,AOF命令以redis协定追加每次保留的写操作到文件开端,Redis还能够对大的aof文件进行压缩重写。 3)咱们能够同时开启两种长久化形式,在这种状况下,redis会优先载入aof来复原数据,因为aof比rdb的文件数据集要残缺。 4.应用倡议RDB文件只用作后备用处,倡议只在slave上长久化rdb文件,且只有15分钟备份一次就能够,保留save 900 1. 如果咱们开启了aof,益处就是在最顽劣的状况下也只失落不超过两秒的数据,启动脚本较简略。代价就是带来了继续的IO,而是AOF压缩aof文件的时候,造成的阻塞时不可避免的,应该尽量减少aof的频率,AOF重写的根底大小默认值64m太小了,应该设置到5G以上,默认超过原大小的100%大小时就能够重写到适当的数值。 如果不开启aof,仅仅依附正从复制也能够,就节俭了很多IO,代价失落master/slave同时宕机,会失落十几分钟的数据。

March 8, 2022 · 1 min · jiezi

关于redis:京东二面Redis为什么那么快

本期是【大厂面试】系列文章的第6期,模仿Redis基础知识高频面试题目。 面试开始面试官:明天聊聊Redis吧 面试官:都说Redis速度快,那Redis为什么这么快呢? 大彬:次要是因为以下几点起因: 基于内存:Redis是应用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。单线程实现( Redis 6.0以前):Redis应用单个线程解决申请,防止了多个线程之间线程切换和锁资源争用的开销。IO多路复用模型:Redis 采纳 IO 多路复用技术。Redis 应用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上节约过多的工夫。高效的数据结构:Redis 每种数据类型底层都做了优化,目标就是为了谋求更快的速度。面试官:嗯,刚说到Redis是单线程实现的,你认为Redis为何抉择单线程呢? 独白:emm,这得问问Redis作者了... 大彬:1、单线程实现能够防止过多的上下文切换开销。程序始终运行在过程中单个线程内,没有多线程切换的场景。 大彬:2、防止同步机制的开销:如果 Redis抉择多线程模型,须要思考数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,减少程序复杂度的同时还会升高性能。 大彬:3、实现简略,不便保护:如果 Redis应用多线程模式,那么所有的底层数据结构的设计都必须思考线程平安问题,那么 Redis 的实现将会变得更加简单。 面试官:Redis利用场景有哪些? 大彬:缓存热点数据,缓解数据库的压力。 大彬:利用 Redis 原子性的自增操作,能够实现计数器的性能,比方统计用户点赞数、用户拜访数等。 大彬:作为简略的音讯队列,实现异步操作。 大彬:限速器,可用于限度某个用户拜访某个接口的频率,比方秒杀场景用于避免用户疾速点击带来不必要的压力。 大彬:好友关系,利用汇合的一些命令,比方交加、并集、差集等,实现独特好友、共同爱好之类的性能。 面试官:哦,Redis 怎么实现音讯队列? 独白:卧槽,给本人挖坑了... 大彬:1、应用列表,让生产者将工作应用LPUSH命令放进列表,消费者一直用RPOP从列表取出工作。 大彬:2、公布订阅模式。相似于MQ的主题模式。只能生产订阅之后公布的音讯,一个音讯能够被多个订阅者生产。 大彬:3、延时队列。应用sortedset,拿工夫戳作为score,音讯内容作为key,调用zadd来生产音讯,消费者用zrangebyscore指令获取N秒之前的数据轮询进行解决。 面试官:来讲讲Redis主从复制的原理? 大彬:嗯,Redis的复制性能是反对多个数据库之间的数据同步。主数据库能够进行读写操作,当主数据库的数据发生变化时会主动将数据同步到从数据库。从数据库个别是只读的,它会接管主数据库同步过去的数据。 大彬:上面是主从复制的原理: 当启动一个从节点时,它会发送一个 PSYNC 命令给主节点;如果是从节点首次连贯到主节点,那么会触发一次全量复制。此时主节点会启动一个后盾线程,开始生成一份 RDB 快照文件;同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成结束后, 主节点会将RDB文件发送给从节点,从节点会先将RDB文件写入本地磁盘,而后再从本地磁盘加载到内存中;接着主节点会将内存中缓存的写命令发送到从节点,从节点同步这些数据;如果从节点跟主节点之间网络呈现故障,连贯断开了,会主动重连,连贯之后主节点仅会将局部缺失的数据同步给从节点。面试官:不错,理解过期键的删除策略吗? 大彬:1、被动删除。在拜访key时,如果发现key曾经过期,那么会将key删除。 大彬:2、被动删除。定时清理key,每次清理会顺次遍历所有DB,从db随机取出20个key,如果过期就删除,如果其中有5个key过期,那么就持续对这个db进行清理,否则开始清理下一个db。 大彬:3、内存不够时清理。Redis有最大内存的限度,通过maxmemory参数能够设置最大内存,当应用的内存超过了设置的最大内存,就要进行内存开释, 在进行内存开释的时候,会依照配置的淘汰策略清理内存。 面试官:很好,明天就到这吧

March 6, 2022 · 1 min · jiezi

关于redis:Redis-70-新功能新特性总览

阐明:本文依据Redis 7 RC2 的release note 整顿并翻译 近日,Redis 开源社区公布了7.0的两个预览版。在这两个预览版中,有很多Redis 7.0中新减少的个性,新减少的命令或已有命令的新加参数,一些性能上的优化和进步,还有一些API的扭转,并且修复了以前版本中的一些bug,上面让咱们具体来看一下这些方面的内容。 Redis 7.0 包含了以下一些重要的变动: 将AOF文件的存储形式改为在一个文件夹下存储多个文件。将长久化文件RDB的版本升级为10,与之前的RDB文件版本不再兼容。在读取老的RDB文件格式的时候将ziplist转换为listpack,这种转换产生于两种状况之下:从磁盘读取文件或者从一个主节点进行复制文件的时候。在redis.conf配置文件中,protected-mode 默认更改为yes,只有当你心愿你的客户端在没有受权的状况下能够连贯到Redis server的时候能够将protected-mode设置为no。在ACL中,pub/sub channel默认是被阻塞的。在从节点中,TTL的工夫标识的是相对工夫,不再是绝对工夫,从而保障了过期数据被及时删除。不再反对 gopher协定。当在配置文件中设置replica-serve-stale-data=no, 当主节点不再提供服务时,PING命令得不到返回值。Redis 7.0 新个性其中的几个例子: RedisFunctions:一种新的形式用于Redis server端脚本,它不同于以前版本反对的Lua脚本, Redis Functions可反对长久化,可复制,并且在节点重启之后能够间接从server端读取。 咱们会在后续的博客中具体介绍Redis Functions的应用。 如果当初大家就想晓得更多详情,能够参考链接https://redis.io/topics/funct...集群反对显示主机名,而不仅仅显示ip地址。应用多个AOF文件升高了AOF重写期间的内存应用。在Lua脚本中反对了Function的标记。在AOF文件中减少了数据更新工夫点的标识,使得用户能够复原某一时间点的数据。Lua脚本反对RESP3 版本的并且能够通过redis.REDIS_VERSION, redis.REDIS_VERSION_NUM失去Redis的版本。减少了对stream consumer组滞后的追踪和报告 。减少了API以便于能够在functions和Lua脚本中明确地查看ACL。Redis 7.0 新增14个用户端命令和 15个已有命令的相干参数选项,其中包含:ZMPOP, BZMPOP,LMPOP, BLMPOP等新命令,对于EXPIRE和SET命令,新增了更多的命令参数选项。 例如,ZMPOP的格局如下: ZMPOP numkeys key [key ...] MIN|MAX [COUNT count],而BZMPOP是ZMPOP的阻塞版本。 上面是一个应用ZMPOP的例子: redis> ZMPOP 1 notsuchkey MIN(nil)redis> ZADD myzset 1 "one" 2 "two" 3 "three"(integer) 3redis> ZMPOP 1 myzset MIN1) "myzset"2) 1) 1) "one" 2) "1"redis> ZRANGE myzset 0 -1 WITHSCORES1) "two"2) "2"3) "three"4) "3"redis> ZMPOP 1 myzset MAX 1) "myzset"2) 1) 1) "three" 2) "3"redis> ZRANGE myzset 0 -1 WITHSCORES1) "two"2) "2"Redis 7.0 新增10个治理和监控相干的命令及其相干参数选项,其中包含:COMMAND LIST,COMMAND INFO,CLUSTER DELSLOTSRANGE and CLUSTER ADDSLOTSRANGE等。 ...

March 4, 2022 · 1 min · jiezi

关于redis:Redisson分布式锁的实现原理及源码

Redisson分布式锁的实现原理及源码 源码解析简略的业务代码次要负责源码入口, 分布式锁的应用次要有三个办法 RLock lock = redissonClient.getLock("hpc-lock")获取实现可重入分布式锁的类lock.lock() 加锁lock.unlock()解锁 @GetMapping("/redis/lock") public ResResult testDistributedLock() { RLock lock = redissonClient.getLock("hpc-lock"); lock.lock(); try { System.out.println("加锁业务, xxx, xxx, xxxx"); } finally { lock.unlock(); } return new ResResult(true, ""); }源码解析获取实现可重入分布式锁的类redissonClient.getLock("hpc-lock") ,该办法次要是获取实现了分布式可重入锁的类,进入getLock办法 @Override public RLock getLock(String name) { return new RedissonLock(commandExecutor, name); }发现是初始化了RedissonLock类, 追到结构类办法 public RedissonLock(CommandAsyncExecutor commandExecutor, String name) { super(commandExecutor, name); this.commandExecutor = commandExecutor; // 通过下一行代码并进入追踪,发现默认线程的外部锁租用工夫为默认的30s, // 也就是30s后主动释法锁 this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(); this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getLockPubSub(); }加锁逻辑lock.lock() 该办法次要性能是进行加锁,进入lock()办法,并向下追踪找到外围逻辑,找到办法org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean)并查看外围逻辑。该外围逻辑次要有三个点:尝试加锁,未加锁胜利的线程订阅Redis的音讯, 未加锁胜利的线程通过自旋获取锁 ...

March 4, 2022 · 4 min · jiezi

关于redis:个人学习系列-Spring-Boot-配合-Redis-实现简单的发布订阅功能

如果只是想实现简略的公布订阅性能的话,又不想用音讯队列减少零碎的复杂性,咱们能够抉择Redis来做这个事件。公布订阅公布订阅模式就是一种生产者消费者模式,Publisher负责生产音讯,而Subscriber则负责生产它所订阅的音讯。 pom.xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version></dependency>application.ymlserver: # 端口号配置 port: 8080spring: # redis配置 redis: database: 0 host: 127.0.0.1 port: 6379 password:音讯生产者/** * 音讯生产者 * @author zhouzhaodong */@RestControllerpublic class RedisController { private final RedisTemplate<String, String> redisTemplate; public RedisController(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } @GetMapping("/publish") public void publish(@RequestParam String message) { // 发送音讯 // 上面这里须要配置发送的CHANNEL名称 redisTemplate.convertAndSend("helloMessage", message); }}音讯消费者/** * 音讯消费者 * @author zhouzhaodong */@Slf4j@Servicepublic class MessageSubscriber { public MessageSubscriber(RedisTemplate redisTemplate) { RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection(); redisConnection.subscribe((message, bytes) -> { // 收到音讯的解决逻辑 log.info("Receive message : " + message); // 上面这里须要配置接管的CHANNEL名称 }, "helloMessage".getBytes(StandardCharsets.UTF_8)); }}测试调用接口生产音讯控制台打印消费者收到的音讯 ...

March 4, 2022 · 1 min · jiezi

关于redis:别再用-Redis-List-实现消息队列了Stream-专为队列而生

应用 Redis 的 List 实现音讯队列有很多局限性,比方: 没有良好的 ACK 机制;没有 ConsumerGroup 生产组概念;音讯沉积。List 是线性构造,想要查问指定数据须要遍历整个列表;Stream 是 Redis 5.0 引入的一种专门为音讯队列设计的数据类型,Stream 是一个蕴含 0 个或者多个元素的有序队列,这些元素依据 ID 的大小进行有序排列。 它实现了大部分音讯队列的性能: 音讯 ID 系列化生成;音讯遍历;音讯的阻塞和非阻塞读;Consumer Groups 生产组;ACK 确认机制。反对多播。提供了很多音讯队列操作命令,并且借鉴 Kafka 的 Consumer Groups 的概念,提供了生产组性能。 同时提供了音讯的长久化和主从复制机制,客户端能够拜访任何时刻的数据,并且能记住每一个客户端的拜访地位,从而保障音讯不失落。 废话少说,先来看下如何应用,官网文档详见:redis.io/topics/stre… XADD:插入音讯「云岚宗众弟子听命,击杀萧炎!」 当云山最初一字落下,那洋溢的紧绷氛围,登时宣告破碎,悬浮半空的泛滥云岚宗长老背地双翼一振,便是咻咻的划过天际,追杀萧炎。 云山应用以下指令向队列中插入「追杀萧炎」命令,让长老率领子弟去执行。 XADD 云岚宗 * task kill name 萧炎"1645936602161-0"复制代码Stream 中的每个元素由键值对的模式组成,不同元素能够蕴含不同数量的键值对。 该命令的语法如下: XADD streamName id field value [field value ...]复制代码音讯队列名称前面的 「*」 ,示意让 Redis 为插入的音讯主动生成惟一 ID,当然也能够本人定义。 音讯 ID 由两局部组成: 以后毫秒内的工夫戳;程序编号。从 0 为起始值,用于辨别同一时间内产生的多个命令。通过将元素ID与工夫进行关联,并强制要求新元素的ID必须大于旧元素的ID, Redis从逻辑上将流变成了一种只执行追加操作(append only)的数据结构。 这种个性对于应用流实现音讯队列和事件零碎的用户来说是十分重要的: 用户能够确信,新的音讯和事件只会呈现在已有音讯和事件之后,就像事实世界里新事件总是产生在已有事件之后一样,一切都是有序进行的。 ...

March 4, 2022 · 3 min · jiezi

关于redis:深入理解redisRedis快的原因和IO多路复用深度解析

1.Redis是单线程的还是多线程的?2.Redis性能很快的起因3.Redis的瓶颈在哪里4.I/O多路复用模型实践5.I/O多路复用模型JAVA验证6.Redis如何解决并发客户端链接7.Linux内核函数select, poll, epoll解析8.总结 1.Redis是单线程的还是多线程的? 当咱们在学习和应用redis的时候,大家口耳相传的,说的都是redis是单线程的。其实这种说法并不谨严,Redis的版本有十分多,3.x、4.x、6.x,版本不同架构也是不同的,不限定版本问是否单线程不太谨严。1)版本3.x,最早版本,也就是大家口口相传的redis是单线程。2)版本4.x,严格意义上来说也不是单线程,而是负责客户端解决申请的线程是单线程,然而开始加了点多线程的货色(异步删除)3)最新版本的6.0.x后,辞别了刻板印象中的单线程,而采纳了一种全新的多线程来解决问题。 所以咱们能够得出,Redis 6.0版本当前,对于整个redis来说,是多线程的。 Redis是单线程到多线程的演变: 在以前,Redis的网络IO和键值对的读写都是由一个线程来实现的,Redis的申请时包含获取(Socket读),解析,执行,内容返回(socket写)等都是由一个程序串行的主线程解决,这就是所谓的单线程。 但此时,Redis的瓶颈就呈现了:I/O的读写自身是阻塞的,比方当socket中有数据的时候,Redis会通过调用先将数据从内核态空间拷贝到用户态空间,再交给Redis调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所须要的工夫就越多,而这些操作都是基于单线程实现的。 在Redis6.0中新减少了多线程的性能来进步I/O读写性能,他的次要实现思路是将主线程的IO读写工作拆分给一组独立的线程去执行,这样就能够使多个socket的读写并行化了,采纳I/O多路复用技术能够让单个线程高效解决多个连贯申请(尽量减少网络IO的工夫耗费),将最耗时的socket读取,申请解析,写入独自外包进来,剩下的命令执行任然是由主线程串行执行和内存的数据交互。 联合上图可知,网络IO操作就变成多线程化了,其余外围局部依然是线程平安的,是个不错的折中方法。 流程图: 流程简述:1)主线程负责接管建设链接申请,获取socket放入全局期待读取队列2)主线程解决完读事件后,将这些连贯调配给这些IO线程3)主线程阻塞期待IO线程读取socket结束4)IO线程组收集各个socket输出进来的命令,收集结束5)主线解除阻塞,程执行命令,执行结束后输入后果数据6)主线程阻塞期待 IO 线程将数据回写 socket 结束7)解除绑定,清空期待队列 2.Redis性能很快的起因 1)基于内存操作,Redis的所有数据都存在内存中,因而所有的运算都是内存级别的,所以他的性能比拟高。2)数据结构简略:Redis的数据结构都是专门设计的,而这些简略的数据结构的查找和操作的工夫大部分复杂度都是O(1),因而性能比拟强,能够参考我之前写过的这篇文章。深刻了解redis——redis经典五种数据类型及底层实现3)多路复用和非阻塞I/O,Redis应用I/O多路复用来监听多个socket链接客户端,这样就能够应用一个线程链接来解决多个申请,缩小线程切换带来的开销,同时也防止了I/O阻塞操作。4)主线程为单线程,防止上下文切换,因为是单线程模型,因而防止了不必要的上下文切换和多线程竞争(比方锁),这就省去了多线程切换带来的工夫和性能上的耗费,而且单线程不会导致死锁问题的产生。 3.Redis的瓶颈在哪里? 说了这么多redis的长处,redis的性能瓶颈到底在哪里? 从cpu上看:1)redis是基于内存的,因而缩小了cpu将数据从磁盘复制到内存的工夫2)redis是单线程的,因而缩小了多线程切换和复原上下文的工夫3)redis是单线程的,因而多核cpu和单核cpu对于redis来说没有太大影响,单个线程的执行应用一个cpu即可 综上所述,redis并没有太多受到cpu的限度,所以cpu大概率不会成为redis的瓶颈。 而内存大小和网络IO才有可能是redis的瓶颈。 所以Redis应用了I/O多路复用模型,来优化redis。 4.I/O多路复用模型实践在学习IO多路复用之前,咱们先明确上面的几个词语的概念: 1)同步:调用者要始终期待调用后果的告诉后,能力继续执行,相似于串行执行程序,执行结束后返回。 2)异步:指被调用者先返回应答让调用者先回去,而后计算调用后果,计算实现后,再以回调的形式告诉给调用方。 同步,异步的侧重点,在于被调用者,重点在于取得调用后果的告诉形式上。 3)阻塞:调用方始终在等,且其它别的事件都不做,以后线程会被挂起,啥都不干 4)非阻塞:调用在收回去之后,调用方先去干别的事件,不会阻塞以后线程,而会立即返回。 阻塞,非阻塞的侧重点,在于调用者在期待音讯的时候的行为,调用者是否干其它的事。 以上的IO能够组合成4种组合形式:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞 同时也就衍生出了unix网络编程中的五种模型: 接下来咱们用代码进行验证 5.I/O多路复用模型JAVA验证 BIO(阻塞IO): 咱们来看这样一段代码: 假如咱们当初有一个服务器,监听6379端口,让他人来链接,咱们用这样的形式来写代码: public class RedisServer{ public static void main(String[] args) throws IOException { byte[] bytes = new byte[1024]; //监听6379端口 ServerSocket serverSocket = new ServerSocket(6379); while(true) { System.out.println("模仿RedisServer启动-----111 期待连贯"); Socket socket = serverSocket.accept(); System.out.println("-----222 胜利连贯"); System.out.println(); } }}客户端: ...

March 3, 2022 · 2 min · jiezi

关于redis:如何用set实现一个抽奖

提前统计好参加的人数,初始化奖池,放入redis set中,通过spop随机弹出,抽完即扔,如果是中奖区间内的号码则中奖!解密端午抽奖实现逻辑: 首先抽奖的前提必定是: 保障在抽奖奖池抽完后,将奖品刚刚好发完,并且每个人都抽过奖。每个人中奖的概率是一样的。实现:提前统计参加抽奖的人,造成一个数组,比方2600人抽奖,初始化好一个 2600长度的 数组,丢进redis,比方有50集体能够中奖,就将号码牌<50的抽奖号视为中奖。 redis 中spop的实现抽奖 intset 编码的实现办法hashtable 编码的实现办法调用 intsetRandom 函数, 从整数汇合中随机取出一个元素, 在将这个随机元素返回给客户端之后, 调用 intsetRemove 函数, 将随机元素从整数汇合中删除掉。调用 dictGetRandomKey 函数, 从字典中随机取出一个字典键, 在将这个随机字典键的值返回给客户端之后, 调用 dictDelete 函数, 从字典中删除随机字典键所对应的键值对。验证每个人的偏心水平:假如有2600集体抽奖,有50个中奖卡牌,抽奖后将卡牌抛弃。 第一个人抽奖中奖概率: 50/2600 第二个人中奖概率 : (50/2600) (49/2599) + (2550/2600)(50/2599)=50/2600 能够验证失去中奖概率统一是偏心的。

March 3, 2022 · 1 min · jiezi

关于redis:Redis-主从复制的原理及演进

本文作者:百度基础架构部工程师,王钰 Redis 的主从复制经验了屡次演进,本文将从最根本的原理和实现讲起,并层层递进,逐渐出现 Redis 主从复制的演进历史。大家将理解到 Redis 主从复制的原理,以及各个改良版本解决了什么问题,并最终看清 Redis 7.0 主从复制原理的全貌。 什么是主从复制? 在数据库语境下,复制(replication)就是将数据从一个数据库复制到另一个数据库中。主从复制,是将数据库分为主节点和从节点,主节点源源不断地将数据复制给从节点,保障主从节点中存有雷同的数据。有了主从复制,数据能够有多份正本,这带来了多种益处:第一,晋升数据库系统的申请解决能力。单个节点可能撑持的读流量无限,部署多个节点,并形成主从关系,用主从复制放弃主从节点数据统一,如此主从节点能够一起提供服务。第二,晋升整个零碎的可用性。因为从节点中有主节点数据的正本,当主节点宕机后,能够立即晋升其中一个从节点为主节点,持续提供服务。 Redis 主从复制原理 实现主从复制,直观的思路是产生一份主节点数据的快照发送给从节点,并以此做为基准,随后将快照时刻之后的增量数据发送给从节点,如此就能保障主从数据的统一。总体来看,主从复制个别蕴含全量数据同步、增量同步两个阶段。 在 Redis 的主从复制实现中,蕴含两个相似阶段:全量数据同步和命令流传。全量数据同步:主节点产生一份全量数据的快照,即 RDB 文件,并将此快照发送给从节点。且从产生快照时刻起,记录新接管到的写命令。当快照发送实现后,将累积的写命令发送给从节点,从节点执行这些写命令。此时基准曾经建设实现,主从节点间数据曾经大体一致。命令流传:全量数据同步实现后,主节点将执行过的写命令源源不断地发送给从节点,从节点执行这些命令,保障主从节点中数据有雷同的变更,如此保障主从数据继续统一。 下图中给出了 Redis 主从复制的整个过程: 主从关系建设后,从节点向主节点发送一个 SYNC 命令申请进行主从同步。主节点收到 SYNC 命令后,执行 fork 创立一个子过程,子过程中将所有的数据按特定编码存储到 RDB(Redis Database) 文件中,这就产生了数据库的快照。主节点将此快照发送给从节点,从节点接管并载入快照。主节点接着将生成快照、发送快照期间积压的写命令发送给从节点,从节点接管这些命令并执行,命令执行后,从节点中的数据也就有了同样的变更。尔后,主节点源源不断地新执行的写命令同步到从节点,从节点执行流传来的命令。如此,主从数据保持一致。须要阐明的是,命令流传存在时延的,所以任意时刻,不能保障主从节点间数据完全一致。以上就是 Redis 主从复制的基本原理,很简略很容易了解,Redis 最后就采纳这种计划,但这种计划存在一些问题:fork 耗时过长,阻塞主过程执行fork 时,须要拷贝大量的内存页表,这是一个耗时较多的操作,尤其当内存使用量较大的时候。组内同学曾做过测试,内存占用 10GB 时,fork 须要耗费 100 多毫秒。fork 的时候主过程阻塞 100 多毫秒,这对 Redis 而言,切实太长了。另外fork 之后,如果主库中有不少的写入,那么因为写时复制机制,会额定耗费不少的内存,还会增大响应工夫。主从间网络闪断会触发全量同步如果主从之间的网络呈现了故障,连贯意外断开,主节点无奈持续流传命令至该从节点。之后网络复原,从节点从新连贯上主节点后,主节点不能再持续流传新接管到的命令了,因为从节点曾经漏掉了一些命令。此时,从节点须要从头再来,再次执行全副的同步过程,而这要付出很高的代价。网络闪断是常产生的事件,闪断期间主节点中可能只写入了比拟少的数据,但就因为这很少的一部分数据,须要让从节点进行一次代价昂扬的全量同步。这种做法是十分低效的,该如何解决这问题呢?下一节 Redis 局部重同步给你答案。 Redis 局部重同步 网络短暂断开后,从节点须要从新同步,这很浪费资源,很不环保。从节点为什么须要从新同步呢?因为主从断开期间有局部命令没有同步到从节点下来。如果疏忽这些命令持续流传后续的命令,则会导致数据的错乱,因为失落掉的命令是不能疏忽的。为什么不将那些命令保留下来呢?这样当从节点从新连贯后,就能够将断连期间的命令补充给它了,这样就不须要从新全量同步了。Redis 2.8 版本后,引入了局部同步。它在主节点中保护了一个复制积压缓冲区,命令一方面会流传到从节点,另外还会记录在这个缓冲区中。保留所有的命令是不必要的,Redis 中应用了一个环形的缓冲区,这样就能够只保留最近的一些命令了。 命令是保留下来了,但从节点从新连贯后,主节点该从什么中央开始给从节点发送命令呢?如果能给所有命令编一个号,则从节点只须要通知主节点本人最初收到的命令的编号,主节点就晓得该从什么地位发送命令了。Redis 的实现中是对字节进行编号,这个编号在 Redis 的语境中叫做复制偏移量。 有了局部同步后,主从复制的流程变成了上面这样: 主从复制的时候不再应用SYNC命令,而是应用PSYNC,意思的Partial SYNC,局部同步。PSYNC的语法如下:PSYNC <master id> <replication offset> ...

March 2, 2022 · 1 min · jiezi

关于redis:CentOS-7环境安装redis及简单使用

1、装置wget yum -y install wget2、/opt门路下下载redis wget http://download.redis.io/releases/redis-6.2.1.tar.gz3、装置gcc yum install gccgcc --version4、解压redis包 tar -zxvf redis-6.2.1.tar.gz5、cd redis-6.2.1/下,执行make编译成C 6、执行make install,装置实现 7、默认装置地位 /usr/local/bin8、根本应用(配置后盾启动) /opt/redis-6.2.1/redis.conf复制配置文件cp redis.conf /etc/redis.conf编辑/etc/redis.conf daemonize no批改为yes启动cd /usr/local/bin./redis-server /etc/redis.conf查看redis状态ps -ef|grep redis通过客户端连贯redis./redis-cli127.0.0.1:6379> ping 打印pong示意联通状态失常 敞开127.0.0.1:6379> shutdown 或kill -9 <过程号>

March 1, 2022 · 1 min · jiezi

关于redis:深入理解redis缓存双写一致性之更新策略探讨

1.Redis缓存双写一致性2.数据库和缓存一致性的几种更新策略2.1先更新数据库,再更新缓存2.2先删除缓存,再更新数据库2.3先更新数据库,再删除缓存2.4先更新缓存,再更新数据库3.Redis与MySQL数据双写一致性工程落地案例之canal4.总结 1.Redis缓存双写一致性咱们都晓得,只有咱们应用redis,就会遇到缓存与数据库的双存储双写,那么只有是双写,就肯定会有数据一致性问题,为了保障双写一致性,咱们要先动redis还是mysql? 通常地来说,有为了保证数据的一致性,会有以下两种状况: 1)如果redis中有数据,咱们须要和数据库中的值雷同 2)如果redis中无数据,数据库里的值是最新值 2.数据库和缓存一致性的几种更新策略 咱们要保障数据库和缓存的一致性,然而这毕竟是两个工具,必然会造成肯定的提早,所以咱们要保障的是最终一致性! 咱们能够给缓存的key设置过期工夫,这是保障最终一致性的解决方案。 咱们对存入缓存的数据设置过期工夫,所有写操作以数据库为准,对缓存操作只用尽最大致力即可。如果数据库写胜利,缓存更新失败,只有达到过期工夫,前面的读申请天然会从数据库中取新值,而后回写缓存,达到一致性。 上述的案例只是目前支流+成熟的做法,思考到每个公司的业务性质不同,请抉择适宜咱们本人公司的办法。 2.1先更新数据库,再更新缓存当咱们进行这种操作的时候,如果线程并发量足够大,个别会呈现两个问题,咱们用列表格的形式来进行形容: 异常情况1: 工夫线程 A线程 Bt1更新数据库的值 t2 查问申请,缓存命中旧数据,导致查问脏数据t3更新缓存的数据 异常情况2: 工夫线程 A线程 Bt1更新数据库的值 t2 查问申请,缓存命中旧数据,导致查问脏数据t3缓存更新失败,导致肯定工夫内查问的都为脏数据 2.2先删除缓存,再更新数据库异常情况1: 工夫线程 A线程 Bt1删除缓存 t2 大量查问申请,间接导致缓存击穿t3服务器宕机 异常情况1解决方案:缓存击穿的解决方案,咱们在后面这篇博客曾经解释过了。深刻了解redis——缓存雪崩/缓存击穿/缓存穿透 异常情况2: 工夫线程 A线程 Bt1删除缓存 t2 查问申请,缓存无数据,去数据库查问旧数据t3更新mysql的值,导致和缓存中数据不统一 异常情况2解决方案:采纳延时双删策略 public void delayDoubleDeleteUser(TUser user) throws InterruptedException { //线程胜利删除redis缓存 redisTemplate.delete(CACHE_KEY_USER + user.getId()); //线程再更新mysql userMapper.updateById(user); //休眠两秒钟,期待其它查问业务逻辑先执行结束,缓存中曾经齐全是旧值的时候 Thread.sleep(2000); //再删除一遍缓存 redisTemplate.delete(CACHE_KEY_USER + user.getId()); }延时双删造成的问题1:那么,这个延时双删,线程要休眠多久呢? 个别在业务我的项目运行的时候,先统计下线程的读和写操作的工夫,由此为根底,再依据写数据的休眠工夫在读数据业务逻辑的耗时根底上减少百毫秒即可。 延时双删造成的问题2:这种同步策略造成吞吐量升高怎么办? 再开一个线程就能够了。 public void delayDoubleDeleteUser(TUser user) throws InterruptedException, ExecutionException { //线程胜利删除redis缓存 redisTemplate.delete(CACHE_KEY_USER + user.getId()); //线程再更新mysql userMapper.updateById(user); //休眠两秒钟,期待其它业务逻辑先执行结束 //开一个线程,再删除一遍缓存 CompletableFuture.supplyAsync(()->{ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return redisTemplate.delete(CACHE_KEY_USER + user.getId()); }).get(); }2.3先更新数据库,再删除缓存这种是业内提倡的解决方案: ...

March 1, 2022 · 4 min · jiezi

关于redis:一个命令让redis服务端所有信息无所遁形收藏吃灰系列

1、info命令作用在redis客户端执行INFO 命令以便于计算机解析和人工浏览的简略格局返回无关redis服务端的所有信息和统计数据。可选参数可用于抉择特定的信息局部: Server 服务器根本信息Clients 客户端连贯信息Memory内存信息Persistence长久化相干Stats 试试监控信息Replication主从复制相干信息CPU信息Cluster集群信息Keyspace键存储空间信息请留神,依据 Redis 的版本,有些字段已被增加或删除。因而,弱小的客户端应该跳过未知属性来解析此命令的后果,并优雅地解决短少的字段 #在客户端输出会返回以下信息info 2、Server 服务器根本信息# redis版本号redis_version:5.0.3# redis源码包git信息redis_git_sha1:00000000redis_git_dirty:0redis_build_id:da69b07a37c06dc8# 运行模式(“独立”,“哨兵”或“集群”)redis_mode:standalone# 操作系统信息os:Linux 3.10.0-514.el7.x86_64 x86_64# 64位架构arch_bits:64# 编译运行的底层依赖信息multiplexing_api:epollatomicvar_api:atomic-builtingcc_version:4.8.5# 过程IDprocess_id:10040# 实例运行的随机值标识符(sentinel和集群中有用)run_id:df903681f11f712523e0615cd4c7e45afbf500b6# 端口tcp_port:6379# 运行时长uptime_in_seconds:42593# 运行时长(天)uptime_in_days:0# 服务器的频率设置hz:10configured_hz:10# LRU运作的时钟(分钟为单位)lru_clock:7473903# 可执行文件executable:/usr/local/redis/./bin/redis-server# 以后读取的配置config_file:/usr/local/redis/conf/redis_6379.conf3、Clients 客户端连贯信息# 连接数connected_clients:1# 客户端输出缓冲区client_recent_max_input_buffer:2# 客户端输入缓冲区client_recent_max_output_buffer:0# 阻塞的客户端数量(卡住了就看看这个)blocked_clients:04、Memory内存信息# 内存总量(字节数)used_memory:854280# 内存总量(更不便查看的格局)used_memory_human:834.26K# 已调配的内存总量used_memory_rss:8388608used_memory_rss_human:8.00M# 内存耗费峰值used_memory_peak:854280used_memory_peak_human:834.26K# 峰值内存占用的内存百分比used_memory_peak_perc:100.15%# 外部机制所需的内存used_memory_overhead:840838# 启动时耗费的内存used_memory_startup:791032# 数据占用的内存大小used_memory_dataset:13442# 数据占用的内存大小百分比used_memory_dataset_perc:21.25%# 未说明(从名字能够看出是内存申请的信息)allocator_allocated:844856allocator_active:1011712allocator_resident:3665920# 整个零碎内存total_system_memory:1041199104total_system_memory_human:992.96M# Lua脚本存储占用的内存used_memory_lua:37888used_memory_lua_human:37.00K# 未说明used_memory_scripts:0used_memory_scripts_human:0Bnumber_of_cached_scripts:0# 最大内存配置maxmemory:0maxmemory_human:0B# 内存管理策略maxmemory_policy:noeviction# 官网未说明allocator_frag_ratio:1.20allocator_frag_bytes:166856allocator_rss_ratio:3.62allocator_rss_bytes:2654208rss_overhead_ratio:2.29rss_overhead_bytes:4722688mem_fragmentation_ratio:10.33mem_fragmentation_bytes:7576576mem_not_counted_for_evict:0mem_replication_backlog:0mem_clients_slaves:0mem_clients_normal:49694mem_aof_buffer:0# 内存分配器,在编译时抉择mem_allocator:jemalloc-5.1.0# 碎片整顿是否存于活动状态active_defrag_running:0# 期待被开释的对象数量lazyfree_pending_objects:05、Persistence长久化相干# 示意Redis是否正在加载备份文件的标记loading:0# 从最近一次转储至今,RDB的批改次数rdb_changes_since_last_save:2# 示意Redis正在保留RDB的标记rdb_bgsave_in_progress:0# 上次RDB胜利保留的工夫戳rdb_last_save_time:1550935182# 最初一次RDB保留操作的状态rdb_last_bgsave_status:ok# 最初一次RDB保留操作的持续时间(以秒为单位)rdb_last_bgsave_time_sec:-1# 正在进行的RDB保留操作的持续时间(如果有)rdb_current_bgsave_time_sec:-1# 上次RBD保留操作期间写时复制调配的字节大小rdb_last_cow_size:0# 示意AOF记录的标记已激活aof_enabled:1# 示意AOF重写操作的标记正在进行中aof_rewrite_in_progress:0# 一旦正在进行的RDB保留实现,将指定示意AOF重写操作的标记。aof_rewrite_scheduled:0# 最初一次AOF重写操作的持续时间,以秒为单位aof_last_rewrite_time_sec:-1# 正在进行的AOF重写操作的持续时间(如果有)aof_current_rewrite_time_sec:-1# 最初一次AOF重写操作的状态aof_last_bgrewrite_status:ok# 最初一次写入操作到AOF的状态aof_last_write_status:ok# 上次AOF重写操作期间写时复制调配的大小(以字节为单位)aof_last_cow_size:0# AOF以后文件大小aof_current_size:77# 最新启动或重写时的AOF文件大小aof_base_size:77# 一旦正在进行的RDB保留实现,将指定示意AOF重写操作的标记。aof_pending_rewrite:0# AOF缓冲区的大小aof_buffer_length:0# AOF重写缓冲区的大小aof_rewrite_buffer_length:0# fsync挂起作业数aof_pending_bio_fsync:0# 提早fsync计数器aof_delayed_fsync:0# 如果数据恢复中可能会有这些值# loading_start_time:加载操作开始的工夫戳# loading_total_bytes:文件总大小# loading_loaded_bytes:已加载的字节数# loading_loaded_perc:雷同的值示意为百分比# loading_eta_seconds:ETA在几秒钟内实现负载6、Stats 试试监控信息# Redis服务器承受的连贯总数total_connections_received:1# Redis服务器解决的命令总数total_commands_processed:1# 每秒钟解决的命令数量instantaneous_ops_per_sec:0# 通过网络接管的数据总量,以字节为单位total_net_input_bytes:34# 通过网络发送的数据总量,以字节为单位total_net_output_bytes:7# 每秒钟接收数据的速率,以kbps为单位instantaneous_input_kbps:0.00# 每秒钟发送数据的速率,以kbps为单位instantaneous_output_kbps:0.00# Redis服务器因为maxclients限度而回绝的连贯数量rejected_connections:0# Redis主机和从机进行齐全同步的次数sync_full:0# Redis服务器承受PSYNC申请的次数sync_partial_ok:0# Redis服务器回绝PSYNC申请的次数sync_partial_err:0# 键过期事件的总数expired_keys:0expired_stale_perc:0.00expired_time_cap_reached_count:0# 因为maxmemory限度,而被回收内存的键的总数evicted_keys:0# 在主字典中胜利查找到键的次数keyspace_hits:1# 在主字典中未能胜利查找到键的次数keyspace_misses:0# 公布/订阅频道的总数量pubsub_channels:0# 客户端订阅的公布/订阅模式的总数量pubsub_patterns:0# 最近一次fork操作耗费的工夫,以微秒为单位latest_fork_usec:0# 迁徙已缓存的套接字的数量migrate_cached_sockets:0# 为实现key过期而跟踪的key数数量(仅实用于可写正本)slave_expires_tracked_keys:0# 碎片整顿过程执行的值重新分配的数量active_defrag_hits:0# 碎片整顿过程启动的停止值重新分配数active_defrag_misses:0# 碎片整顿的key数量active_defrag_key_hits:0# 碎片整顿过程跳过的key数量active_defrag_key_misses:07、Replication主从复制相干信息# 角色 master或者 slaverole:master# 已连贯的Redis从机的数量connected_slaves:0# 主从复制过程中master的标识idmaster_replid:6ea01bd968c7f14cb6de138462ddaf11930a4269master_replid2:0000000000000000000000000000000000000000# 全局的复制偏移量master_repl_offset:0second_repl_offset:-1# 示意Redis服务器是否为局部同步开启复制备份日志repl_backlog_active:0# 备份日志的循环缓冲区的大小repl_backlog_size:1048576# 备份日志缓冲区中的首个字节的复制偏移量repl_backlog_first_byte_offset:0# 备份日志的理论数据长度repl_backlog_histlen:0# 主从复制状况下可能会有的一些额定信息# master_host:Redis主机的主机名或IP地址# master_port:Redis主机监听的TCP端口# master_link_status:链路状态(连贯/断开# master_last_io_seconds_ago:最近一次和Redis主机交互至今的耗费工夫,以秒为单位# master_sync_in_progress:示意Redis主机正在将数据同步至从机# master_sync_left_bytes:在同步实现之前,还残余的数据总量,以字节为单位# master_sync_last_io_seconds_ago:在一次SYNC操作期间,最近一次传输数据的I/O操作至今的耗费工夫,以秒为单位# master_link_down_since_seconds:从链路断开至今的工夫,以秒为单位8、CPU信息# 服务器消耗的零碎 CPUused_cpu_sys:26.932586# 服务器消耗的用户 CPUused_cpu_user:36.964424# 后盾过程消耗的零碎 CPUused_cpu_sys_children:0.000000# 后盾过程消耗的用户 CPUused_cpu_user_children:0.0000009、Cluster集群信息# 一个标记值,记录集群性能是否曾经开启cluster_enabled:010、Keyspace键存储空间信息# 数据库的key数量、处于无效工夫内的key数量,过期key数量db0:keys=2,expires=0,avg_ttl=0

February 28, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习六redis-事务处理和监控事务

【Redis 系列】redis 学习六,redis 事务处理和监控事务写在后面咱们学过的事务都是保障原子性的,然而 redis 的事务中执行多个指令,是不保障原子性的 redis 事务的实质 就是一组命令的汇合,一个事务中所有的命令都会被序列化,在事务执行的过程,是依照程序执行命令的,他们领有 一次性程序性排他性redis 的事务没有隔离级别的概念 redis 事务中,命令是这样执行的 命令放在事务中,并没有马上执行,而是发动执行命令的时候才会执行,通过 exec 触发 redis 是单条指令保障原子性,然而事务不保障原子性 执行一个事务的流程是这个样子的: multi 开启事务各种命令入队exec 执行事务开启事务MULTI 开启一个事务 EXEC 执行事务外面的指令 执行事务结束后,须要再应用事务,那么须要再次开启事务 127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set k1 v1QUEUED127.0.0.1:6379(TX)> set k2 v2QUEUED127.0.0.1:6379(TX)> set name xiaozhuQUEUED127.0.0.1:6379(TX)> set age 19QUEUED127.0.0.1:6379(TX)> set hobby palyQUEUED127.0.0.1:6379(TX)> exec1) OK2) OK3) OK4) OK5) OK127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set city changshaQUEUED127.0.0.1:6379(TX)> get cityQUEUED127.0.0.1:6379(TX)> exec1) OK2) "changsha"放弃事务DISCARD 放弃最近开启的事务 127.0.0.1:6379> flushdbOK127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set name xiaozhuQUEUED127.0.0.1:6379(TX)> set age 10QUEUED127.0.0.1:6379(TX)> set city beijingQUEUED127.0.0.1:6379(TX)> DISCARDOK127.0.0.1:6379> get city(nil)127.0.0.1:6379> get name(nil)事务出错事务出错分为两种: ...

February 26, 2022 · 2 min · jiezi

关于redis:Redis-内存满了会出现什么情况

机器的内存大小毕竟无限,随着要缓存的数据量越来越大,无限的缓存空间不可避免地会被写满。解决这个问题就波及缓存零碎的一个重要机制,即缓存数据的淘汰机制。简略来说,数据淘汰机制包含两步: 依据肯定策略,筛选出对利用零碎来说“不重要”的数据。将这些数据从缓存中删除,为新来的数据腾出空间。缓存容量设置操作系统的缓存容量是无限的,而且也不可能齐全都调配给 Redis 应用,这就须要思考设置多大的缓存容量适合呢?缓存容量设置得是否正当,会间接影响到应用缓存得性价比。咱们通常都心愿以最小的代价去获取最大的收益,内存资源是低廉的,正当调配就显得很重要了。缓存容量的划分是须要联合利用数据理论拜访特色和老本开销总和思考的。零碎的设计抉择是一个衡量的过程:大容量缓存是不能带来减速的收益的,且老本也会更高,而小容量缓存不肯定就起不到减速拜访的成果。一般来说,倡议把缓存容量设置为总数据量的 15% ~ 30%,兼顾拜访性能和内存空间开销。即便设置了正当的缓存容量,缓存被写满也是不可避免的,一旦被写满了就须要淘汰局部数据腾出空间,不过淘汰数据前会面临两个问题: 淘汰哪些数据。如果解决这些被淘汰的数据。Redis 有哪些淘汰策略截止到目前版本 Redis 一共提供了 8 种内存淘汰策略: 策略阐明noenviction禁止淘汰策略,这也是默认策略。这种策略下 Redis 不再提供服务,而是间接返回谬误。volatile-lru从已设置过期工夫的数据集中选出最近起码应用的数据淘汰volatile-ttl从已设置过期工夫的数据集中选出将要过期的数据淘汰,依据过期工夫为根据,越早过期的越先被删除volatile-random从已设置过期工夫的数据集中随机抉择数据淘汰volatile-lfu从已设置过期工夫的数据集中选出应用频率最低的数据淘汰allkeys-lru从数据集中选出最近起码应用的数据淘汰allkeys-lfu从数据集中选出应用频率最低的数据淘汰allkeys-random从数据集中随机选出数据淘汰分类咱们能够对这些淘汰策略进行下分类,依照是否会进行数据淘汰分为两类: 不进行数据淘汰的策略,只有 noeviction。剩下 7 种都是会进行数据淘汰的。会进行数据淘汰的策略,依据淘汰候选数据集的范畴分为两类: 设置了过期工夫的数据中进行淘汰,包含:volatile-lru、volatile-ttl、volatile-random、volatile-lfu,即便缓存没有写满,如果数据过期了也会被删除。在所有数据范畴内进行淘汰,包含:allkeys-lru、allkeys-random、allkeys-lfu,如果一个键值对被删除策略选中了,即便它的过期工夫还没到,也须要被删除。如果过期工夫到了没有被策略选中,也会被删除。 LRU 算法volatile-lru 和 allkeys-lru 策略都用到了 LRU 算法,那就一块理解下 LRU(Least Recently Used) 算法吧。LRU 算法是依照最近起码应用的准则来筛选数据,最不罕用的数据会被筛选进去,而最近频繁应用的数据会留在缓存中。具体怎么筛选呢?LRU 会把所有的数据组织成一个链表,链表的头和尾别离示意 MRU 端和 LRU 端,别离代表最近最常应用的数据和最近最不常应用的数据。依据 LRU 算法,刚被拜访过的数据会被移到 MRU 端,就尽可能地留在缓存中了。如果是有一个新数据要被写入缓存,当此时曾经没有了缓存空间,也就是链表没有空余地位了,那么 LRU 算法会做两件事: 如果有刚被拜访的数据会放到 MRU 端。算法会把 LRU 端的一个数据从缓存中删除,这个数据在链表中就不存在了,就腾出了空间。Redis 近似 LRU 算法LRU 算法在实现时,须要用链表来治理所有的缓存数据,这会带来额定的空间开销,而且还会进行数据挪动,会很耗时,会对性能有影响。Redis 就对 LRU 算法做了简化。Redis 默认会记录每个数据的最近一次拜访的工夫戳。而后 Redis 在决定淘汰数据时,第一次会随机选出 N 个数据,把它们作为一个候选汇合。接下来,Redis 会比拟这 N 个数据的 lru 字段,把 lru 字段值最小的数据从缓存中淘汰进来。这个参数 N 是能够设置的: ...

February 26, 2022 · 1 min · jiezi

关于redis:深入理解redisredis经典五种数据类型及底层实现

1.Redis的根底类型dictEntry和redisObject2.程序员应用redis时的底层思维3.String底层数据结构4.Hash数据结构介绍5.List数据结构介绍6.Set数据结构介绍7.ZSet数据结构介绍8.总结 1.Redis的根底类型dictEntry和redisObject 咱们能够先去redis的github上下载源码:https://github.com/redis/redis 就像咱们的JAVA对象,顶层全是Object一样,咱们的redis的顶层都是dictEntry,让咱们来看这样一段源码(dict.h中): typedef struct dictEntry { void *key; //示意字符串 就是redis KV构造中的KEY union { void *val; //val指向的是redisObject中 uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; /* Next entry in the same hash bucket. */ void *metadata[]; /* An arbitrary number of bytes (starting at a * pointer-aligned address) of size as returned * by dictType's dictEntryMetadataBytes(). */} dictEntry;咱们以最简略的set k1 v1 为例,因为Redis是以KV为构造的数据库,每个键值对都会有一个dictEntry, 这外面指向了key和value的指针,next指向下一个dictEntry。key是字符串,然而Redis没有间接应用C的char数组,而是存在了redis的自定义字符串中(等等上面会解释),value因为会有不同的类型,redis将这几种根本的类型形象成了redisObject中,实际上五种罕用的数据类型,都是通过redisObject来存储的。咱们看看redisObject的源码(server.h): typedef struct redisObject { unsigned type:4; //以后对象的类型 unsigned encoding:4; //以后对象的底层编码类型 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or LRU或者LFU的拜访工夫数据 * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; //对象援用计数的次数 void *ptr; //真正指向底层数据结构的指针} robj;接下来咱们便能够取得这张图片,咱们对redis的存储构造就高深莫测了: ...

February 25, 2022 · 3 min · jiezi

关于redis:Redis-实现限流的三种方式

面对越来越多的高并发场景,限流显示的尤为重要。 当然,限流有许多种实现的形式,Redis具备很弱小的性能,我用Redis实际了三种的实现形式,能够较为简单的实现其形式。Redis不仅仅是能够做限流,还能够做数据统计,左近的人等性能,这些可能会后续写到。 第一种:基于Redis的setnx的操作咱们在应用Redis的分布式锁的时候,大家都晓得是依附了setnx的指令,在CAS(Compare and swap)的操作的时候,同时给指定的key设置了过期实际(expire),咱们在限流的次要目标就是为了在单位工夫内,有且仅有N数量的申请可能拜访我的代码程序。所以依附setnx能够很轻松的做到这方面的性能。 比方咱们须要在10秒内限定20个申请,那么咱们在setnx的时候能够设置过期工夫10,当申请的setnx数量达到20时候即达到了限流成果。代码比较简单就不做展现了。 具体的setnx用法能够参照我另一篇博客 RedisTemplate下Redis分布式锁引发的系列问题 当然这种做法的弊病是很多的,比方当统计1-10秒的时候,无奈统计2-11秒之内,如果须要统计N秒内的M个申请,那么咱们的Redis中须要放弃N个key等等问题 第二种:基于Redis的数据结构zset其实限流波及的最次要的就是滑动窗口,下面也提到1-10怎么变成2-11。其实也就是起始值和末端值都各+1即可。 而咱们如果用Redis的list数据结构能够轻而易举的实现该性能 咱们能够将申请打造成一个zset数组,当每一次申请进来的时候,value放弃惟一,能够用UUID生成,而score能够用以后工夫戳示意,因为score咱们能够用来计算以后工夫戳之内有多少的申请数量。而zset数据结构也提供了range办法让咱们能够很轻易的获取到2个工夫戳内有多少申请 代码如下 public Response limitFlow(){ Long currentTime = new Date().getTime(); System.out.println(currentTime); if(redisTemplate.hasKey("limit")) { Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime - intervalTime, currentTime).size(); // intervalTime是限流的工夫 System.out.println(count); if (count != null && count > 5) { return Response.ok("每分钟最多只能拜访5次"); } } redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime); return Response.ok("拜访胜利"); }通过上述代码能够做到滑动窗口的成果,并且能保障每N秒内至少M个申请,毛病就是zset的数据结构会越来越大。实现形式绝对也是比较简单的。 第三种:基于Redis的令牌桶算法提到限流就不得不提到令牌桶算法了。 令牌桶算法提及到输出速率和输入速率,当输入速率大于输出速率,那么就是超出流量限度了。 也就是说咱们每拜访一次申请的时候,能够从Redis中获取一个令牌,如果拿到令牌了,那就阐明没超出限度,而如果拿不到,则后果相同。 依附上述的思维,咱们能够联合Redis的List数据结构很轻易的做到这样的代码,只是简略实现 依附List的leftPop来获取令牌 // 输入令牌public Response limitFlow2(Long id){ Object result = redisTemplate.opsForList().leftPop("limit_list"); if(result == null){ return Response.ok("以后令牌桶中无令牌"); } return Response.ok(articleDescription2); }再依附Java的定时工作,定时往List中rightPush令牌,当然令牌也须要唯一性,所以我这里还是用UUID进行了生成 ...

February 25, 2022 · 1 min · jiezi

关于redis:什么你不知道-Redis-缓冲区会溢出

说到缓冲区,在主从数据同步和增量复制的环节提到过,在这两个环节中缓冲区的次要作用是保留 Redis 的写操作,用来保障免得呈现因为数据和命令的处理速度慢于发送速度而导致数据失落和性能问题。 然而了缓冲区的内存空间也是有限度的,如果读速度慢于写速度,就会导致缓冲区一直的扩充内存来暂存数据。但缓冲区占用的内存超过了设定的上线阈值,就会呈现缓冲区溢出导致数据失落。一旦耗尽了 Redis 实例所在机器的可用内存,就会导致 Redis 实例解体,引发生产事变。 缓冲区利用场景缓冲区的利用次要有两个利用场景: 客户端和服务端之间通信。用来暂存客户端发送的命令数据,或是服务端返回给客户端的数据后果。主从节点数据同步。用来暂存主节点接管的写命令和数据。一般客户端缓冲区客户端输出和输入缓冲区先来看一张图,这张图是客户端和服务端连贯中,缓冲区的阐明。 从图中能够看到,输出缓冲区会先把客户端发送的命令暂存起来,Redis 主线程再从输出缓冲区中读取命令,进行解决。当 Redis 主线程解决完数据后,会把后果写入到输入缓冲区,再通过输入缓冲区返回给客户端。 可能导致溢出的状况输出缓冲区可能导致溢出的状况: 写入了 bigkey,比方一下写入了百万级别的汇合类型数据;服务端解决申请的速度过慢,例如,Redis 主线程呈现间歇性阻塞,无奈及时处理失常发送的申请,导致客户端发送的申请在输出缓冲区越积越多;就先来看看如何查看输出缓冲区的内存应用状况吧,理解哪个客户端引起的: # Redis 提供的查看客户端连贯信息的命令CLIENT LISTid=5 addr=127.0.0.1:50487 fd=9 name= age=4 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=clientCLIENT 命令返回的信息能够划分为两类: 一类是与服务器端连贯的客户端信息。如 addr。一类是与输出缓冲区相干的三个参数: cmd:示意客户端最新执行的命令。示例是 CLIENT 命令。qbuf:示意输出缓冲区曾经应用的大小。示例是 CLIENT 命令曾经应用了 26 字节大小的缓冲区。qbuf-free:示意输出缓冲器尚未应用的大小。示例 CLIENT 命令还能够应用 32742 字节的缓冲区。qbuf 和 qbuf-free 的总和就是,Redis 服务器端以后为已连贯的客户端调配的缓冲区总大小。示例总共调配了 26 + 32742 = 32768 字节(32KB)。 溢出产生的影响输出缓冲区溢出产生的影响: 如果 qubf-free 耗尽,就会引起客户端输出缓冲区溢出,Redis 的解决办法就是把客户端连贯敞开,导致的后果就是业务程序无奈进行数据存取。通常状况下,会有很多的客户端连贯,当客户端连贯占用的内存总量,超过了 Redis 的 maxmemory 配置时,就会触发 Redis 进行数据淘汰,影响业务程序的拜访性能。甚至多个客户端会导致 Redis 内存占用过大,也会导致内存溢出问题,进而引起 Redis 解体。怎么防止?能够从两个角度着手思考: ...

February 25, 2022 · 2 min · jiezi

关于redis:Window下Redis的安装和部署详细教程

文中介绍了Windows中Redis的安装包mis和压缩包zip的装置教程,还有几个Redis罕用的可视化插件,如treeNMS、RedisStudio、Redis Desktop Manager等请选择性观看。Redis下载地址:windows版本readis下载(GitHub): https://github.com/tporadowsk...(举荐应用) https://github.com/MicrosoftA... 官网下载(无Windows版本):https://redis.io/download Redis中文网站:http://www.redis.cn 所有版本这里都有:https://download.redis.io/rel...(下载后是个Linux的压缩文件,须要下载、解压和编译) 发行阐明: https://raw.githubusercontent... Redis 反对 32 位和 64 位。依据你所应用的零碎和理论状况进行抉择,这里我下载 Redis-x64-xxx.zip压缩包到磁盘,解压后,将文件夹重新命名为 redis。Windows下的.msi装置和.zip格局区别: .msi是Windows installer开发进去的程序安装文件,它能够让你装置,批改,卸载你所装置的程序。说白了.msi就是Windows installer的数据包,把所有和安装文件相干的内容封装在一个包里。此外:它还蕴含无关装置过程本人的信息。例如:装置序列、指标文件夹门路、装置选项和管制装置过程的属性。.zip是一个压缩包,解压之后即可,不须要装置 一、zip压缩包形式下载安装1、下载Redis压缩包这里我在GitHub中下载window用的 5.0 版本Redis-x64-5.0.14.1.zip。 https://github.com/tporadowsk... 2、解压到文件夹将下载的压缩包解压到指定的文件夹中,如:D:\Redis,内容如下: 3、启动Redis服务在Redis的装置目录下关上cmd窗口,而后执行命令来启动服务: redis-server.exe redis.windows.conf切换到redis目录: 能够关上cmd应用 cd 命令切换到redis所在的目录:cd /d d:\redis 间接在Redis目录门路处输出cmd回车也能够进入命令窗口 cd 切换目录命令示例: 例:cd // 显示当前目录 例:cd .. // 进入父目录 例:cd /d d: // 进入上次d盘所在的目录(或在间接输出:d:) 例:cd /d d:\ // 进入d盘根目录 例:cd d: // 显示上次d盘所在的目录 例:cd /d d:\src // 进入d:\src目录 随后应用redis-server.exe redis.windows.conf命令来启动redis服务: ...

February 23, 2022 · 1 min · jiezi

关于redis:深入理解redisRedis的缓存过期淘汰策略

1.Redis内存满了怎么办2.往redis里写的数据是怎么没了的?它如何删除的?3.redis缓存淘汰策略4.总结 1.Redis内存满了怎么办redis是咱们每天都在开发和应用的一个工具,然而咱们在应用它的时候,有认真关注过它的默认占用内存是多少吗?以及如何批改呢? 1.1Redis默认内存多少?怎么查看?如何批改? 咱们关上redis的配置文件,搜寻一下maxmemory,会发现这么一行代码: 能够看出,把这个设置关上,并且在<bytes>局部设置值之后,就能够设置redis占用内存的大小了,maxmemory是bytes字节类型,留神转换。 设置为 0 ,或者不设置这个字段,示意不限度 Redis 内存应用。 1.2生产上如何配置redis占用内存的大小?个别举荐Redis设置内存为最大物理内存的四分之三。 1.3 如果打满了redis的应用内存会怎么样? 咱们设置一下redis的缓存应用下限,咱们设置为10个byte,再设置一个值,看看。 因为这个key没有加上过期工夫,就会导致maxmemory异样,为了防止这个状况,咱们要有缓存淘汰策略。 2.往redis里写的数据是怎么没了的?它如何删除的? 咱们先来思考一个问题,在什么都不配置的状况下,redis的一个键如果是设置了过期工夫的,到期之后,是不是马上就会从redis中被删除呢? 答案:如果不配置缓存淘汰策略,redis是不会把过期的key从内存中删除进来的,尽管看起来这个key曾经查不到了: 让咱们来思考一下,如果要删除过期的key,能有什么策略?惯例能想到的有: 1)立刻删除 redis始终遍历着所有被设置了过期工夫的key,来检测是否曾经到了过期工夫,而后对它进行删除。 立刻删除能保障内存中数据的最大新鲜度,因为它能保障键值对在过期后马上被删除,所占用的内存也会被开释,但这样对cpu是不敌对的,因为遍历/删除都会占用cpu的工夫,如果刚好碰倒cpu很忙的时候,就会给cpu造成额定的压力,产生极大的性能耗费,同时也会影响读取操作。 总结:对内存敌对,对cpu不敌对。 2)惰性删除 数据达到过期工夫,不做解决,等下次访问该数据的时候如果未过期,返回该数据如果过期了,就删除这个key,返回不存在 惰性删除的毛病是,它对内存极其不敌对。如果这个key曾经过期,而且是一个大key,如果这个key不被拜访,就会始终存在于数据库中,那么它永远也不会被删除。咱们甚至能够将这种状况视为内存透露-无用的垃圾数据占用了大量的内存,而服务器却不去开释他们,对于十分依赖内存的redis而言,必定不是一个好消息。 3)定期删除策略定期删除策略是下面两种形式的折中解决:每隔一段时间执行一次删除过期key操作,并通过限度删除操作执行时长和频率来缩小删除操作对cpu工夫都的影响(有点像jvm虚拟机中能管制吞吐量的Parallel垃圾收集器)。定期轮询redis库中的时效性数据,采纳随机抽取的策略,抽到过期的key就进行删除。 特点1:cpu性能占用设置有峰值,检测频率和自行管制特点2:内存压力不是很大,长期占用内存的冷数据会被继续清理 毛病:1)定期删除执行的时长和频率很难界定,如果执行地太频繁,或者执行工夫太长,这种策略就会变为立刻删除策略,耗费cpu,如果执行地太不频繁,又会像惰性删除策略一样,呈现节约内存的状况。2)会有始终都没被抽查到的key(漏网之鱼)的存在。 这时,为了解决这些问题,咱们的redis缓存淘汰策略就退场了! 3.redis缓存淘汰策略 咱们关上redis的配置文件,搜寻 'maxmemory-policy' 就会发现,默认的缓存淘汰策略就是什么都不淘汰。 redis的缓存淘汰策略有哪些: 1)noeviction: 不会驱赶任何key2)allkeys-lru: 对所有key应用LRU算法进行删除3)volatile-lru: 对所有设置了过期工夫的key应用LRU算法进行删除4)allkeys-random: 对所有key随机删除5)volatile-random: 对所有设置了过期工夫的key随机删除6)volatile-ttl: 删除马上要过期的key7)allkeys-lfu: 对所有key应用LFU算法进行删除8)volatile-lfu: 对所有设置了过期工夫的key应用LFU算法进行删除 下面呈现了两个名词,LRU和LFU,咱们先来解释一下这两个词语的意思: LRU:LRU是Least Recently Used的缩写,即最近起码应用,是一种罕用的页面置换算法。 LFU:即最不常常应用页置换算法,要求在页置换时置换援用计数最小的页。 一个强调的是应用频率,一个强调的是最近未应用。 咱们能够先把下面的redis缓存淘汰策略进行归类:1)是对所有key,还是对设置了过期工夫的key2)是随机删除,全副删除,还是用LRU或者LFU删除。 两个维度和四个方面,取得了八种后果。 那咱们平时应用哪个呢? 如果分为个别的热数据和冷数据,那么咱们都举荐应用 allkeys-lru 策略,其中一部分key常常被读写,如果在不确定业务的状况下,allkeys-lru是一个比拟好的抉择(被淘汰的冷数据如果又要再次被拜访,则能够通过业务逻辑代码从新读入缓存中)。 如果各个key拜访的频率差不多,则能够应用 allkeys-random 策略, 即读写所有元素的概率差不多。 如果要让redis依据ttl来筛选要删除的key,请应用volatile-ttl 策略。volatile-lru 和 volatile-random 策略利用场景是: 既有要过期的key,又有长久key的实例中。 对于这类场景,咱们个别应用两个独自的redis。 ...

February 23, 2022 · 1 min · jiezi

关于redis:redis学习笔记

一、9种数据类型概述咱们先通过一张图理解下 Redis 外部内存治理中是如何形容这些不同数据类型的: 首先Redis外部应用一个redisObject对象来示意所有的key和value,redisObject最次要的信息如上图所示:type代表一个value对象具体是何种数据类型,encoding是不同数据类型在redis外部的存储形式。 redis反对丰盛的数据类型,不同的场景应用适合的数据类型能够无效的优化内存数据的寄存空间: string:最根本的数据类型,二进制平安的字符串,最大512M。list:依照增加程序放弃程序的字符串列表。set:无序的字符串汇合,不存在反复的元素。sorted set:已排序的字符串汇合。hash:key-value对的一种汇合。bitmap:更细化的一种操作,以bit为单位。hyperloglog:基于概率的数据结构。 # 2.8.9新增Geo:地理位置信息储存起来, 并对这些信息进行操作 # 3.2新增流(Stream)# 5.0新增String 字符串常用命令:setnx,set,get,decr,incr,mget 等。 利用场景:字符串是最罕用的数据类型,他可能存储任何类型的字符串,当然也包含二进制、JSON化的对象、甚至是Base64编码之后的图片。在Redis中一个字符串最大的容量为512MB,能够说是无所不能了。redis的key和string类型value限度均为512MB。 缓存,热点数据分布式session分布式锁INCR计数器文章的浏览量,微博点赞数,容许肯定的提早,先写入 Redis 再定时同步到数据库全局ID INT 类型,INCRBY,利用原子性 INCR 限流 以访问者的 IP 和其余信息作为 key,拜访一次减少一次计数,超过次数则返回 false。setbit 位操作 外部编码:int:8 个字节的长整型(long,2^63-1)embstr:小于等于44个字节的字符串,embstr格局的SDS(Simple Dynamic String)raw:SDS大于 44 个字节的字符串redis 为什么要本人写一个SDS的数据类型,次要是为了解决C语言 char[] 的四个问题 字符数组必须先给指标变量调配足够的空间,否则可能会溢出查问字符数组长度 工夫复杂度O(n)长度变动,须要从新分配内存通过从字符串开始到结尾碰到的第一个\0来标记字符串的完结,因而不能保留图片、音频、视频、压缩文件等二进制(bytes)保留的内容,二进制不平安redis SDS 不必放心内存溢出问题,如果须要会对 SDS 进行扩容因为定义了 len 属性,查问数组长度工夫复杂度O(1) 固定长度空间预调配,惰性空间开释依据长度 len来判断是完结,而不是 \0Hash 哈希表常用命令:hget,hsetnx,hset,hvals,hgetall,hmset,hmget 等。 利用场景:咱们简略举个实例来形容下 Hash 的利用场景,比方咱们要存储一个用户信息对象数据,蕴含以下信息:用户 ID 为查找的 key,存储的 value 用户对象蕴含姓名,年龄,生日等 外部编码:ziplist(压缩列表):当哈希类型中元素个数小于 hash-max-ziplist-entries 配置(默认 512 个),同时所有值都小于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会应用 ziplist 作为哈希的外部实现。hashtable(哈希表):当上述条件不满足时,Redis 则会采纳 hashtable 作为哈希的外部实现。List 列表常用命令:lpush,rpush,lpop,rpop,lrange等。 ...

February 17, 2022 · 2 min · jiezi

关于redis:深入理解redisRedis分布式锁

1.锁的品种2.一个健壮性高的分布式锁应该具备的特质3.单个redis分布式锁的演变4.多redis分布式锁5.总结 1.锁的品种咱们在日常的开发流动中,个别把锁分为两类:1)同一个JVM里的锁,比方synchronized和Lock,ReentrantLock等等2)跨JVM的分布式锁,因为服务是集群部署的,单机版的锁不再起作用,资源在不同的服务器之间共享。 2.一个健壮性高的分布式锁应该具备的特质1)独占性 任何时刻只能有一个线程持有锁2)高可用 在redis集群环境下,不能因为某个节点挂了而呈现锁生效的状况3)防死锁 不能有死锁状况,要有超时管制的性能4)不乱抢 不能unlock他人的锁,本人的锁只能本人开释5)重入性 同一个节点的同一个线程取得锁之后,能够再次取得这个锁 3.单个redis分布式锁的演变 版本1:单机版的锁 咱们先来看这样无锁的代码: @RestControllerpublic class GoodController { @Autowired private StringRedisTemplate stringRedisTemplate; @Value("${server.port}") private String serverPort; @GetMapping("/buy_goods") public String buy_Goods() { String result = stringRedisTemplate.opsForValue().get("goods:001"); int goodsNumber = result == null ? 0 : Integer.parseInt(result); if(goodsNumber > 0) { int realNumber = goodsNumber - 1; stringRedisTemplate.opsForValue().set("goods:001",realNumber + ""); System.out.println("你曾经胜利秒杀商品,此时还残余:" + realNumber + "件"+"\t 服务器端口:"+serverPort); return "你曾经胜利秒杀商品,此时还残余:" + realNumber + "件"+"\t 服务器端口:"+serverPort; }else{ System.out.println("商品曾经售罄/流动完结/调用超时,欢送下次光顾"+"\t 服务器端口:"+serverPort); } return "商品曾经售罄/流动完结/调用超时,欢送下次光顾"+"\t 服务器端口:"+serverPort; }}以上的程序在进行商品销售的时候,并没有加锁,在并发下会造成超卖的景象。 ...

February 17, 2022 · 5 min · jiezi

关于redis:深入理解redis缓存雪崩缓存击穿缓存穿透

1.缓存雪崩2.缓存击穿3.缓存穿透4.总结 1.缓存雪崩缓存雪崩是如何产生的?1)redis服务间接挂掉,redis全盘解体2)redis中有大量缓存同时过期,导致大量查问直击mysql 解决1.1)redis缓存集群实现高可用,主从+哨兵1.2)ehcache本地缓存 + Hystrix或者阿里sentinel限流&降级1.3)开启Redis长久化机制aof/rdb,尽快恢复缓存集群 2.1)设置缓存更新的工夫都为固定工夫+随机肯定的工夫,造成不会同时过期。 2.缓存击穿缓存雪崩是如何产生的?大量的申请正在拜访同一个key, 此时这个key正好生效了,就会导致大量的申请到数据库下面。 危害:会造成某一时刻的mysql压力过大,有宕机危险。 解决方案:1)对于高热点key,设置永不过期2)设置一个互斥锁,来避免缓存击穿: public TUser findById(Integer id) { TUser user = (TUser)redisTemplate.opsForValue().get(CACHE_KEY_USER + id); if(user==null){ //小厂用 /* user = userMapper.selectById(id); if(user!=null) { redisTemplate.opsForValue().set(CACHE_KEY_USER + id, user); }*/ //大厂用,对于高qps的优化,进来就先加锁,保障一个申请操作,让里面的redis期待一下,防止击穿mysql synchronized (TUserServiceImpl.class){ //双端检索,再次查问redis user = (TUser)redisTemplate.opsForValue().get(CACHE_KEY_USER + id); if(user==null){ //还是为空,就去查mysql user = userMapper.selectById(id); if(user!=null){ redisTemplate.opsForValue().setIfAbsent(CACHE_KEY_USER + id, user,7L, TimeUnit.DAYS); } } } } return user; }3)双缓存保留,定时轮询,互斥更新,差别生效工夫 需要:假如咱们要实现网页上的一个聚划算性能,每隔两个小时换一批商品。比方:8:000~10:00一批商品,10:00~12:00一批商品。如果依照定时工作的做法,8:00开始更新商品,假如数据量很大,mysql在很难查出来的状况下,到点了可能缓存曾经过期了,然而mysql还没查问出商品,又会有大量查问攻打mysql,导致服务宕机。 思路:咱们设置两个缓存,都保留雷同的内容,两个缓存的过期工夫不同。缓存B比缓存A过期工夫更长一些,这样就能够保障缓存里永远有数据。 查问:先查问缓存A,如果A没有,查问缓存B。 //采纳redis list数据结构的lrange命令实现分页查问list = this.redisTemplate.opsForList().range(Constants.JHS_KEY_A, start, end);if (CollectionUtils.isEmpty(list)) {log.info("=========A缓存曾经生效了,记得人工修补,B缓存主动连续5天");//用户先查问缓存A(下面的代码),如果缓存A查问不到(例如,更新缓存的时候删除了),再查问缓存Bthis.redisTemplate.opsForList().range(Constants.JHS_KEY_B, start, end);更新:先更新缓存B,再更新缓存A。 ...

February 15, 2022 · 1 min · jiezi

关于redis:深入理解redis布隆过滤器BloomFilter

1.布隆过滤器是什么2.布隆过滤器的特点3.布隆过滤器应用场景4.布隆过滤器原理5.布隆过滤器优缺点6.布隆过滤器利用7.总结 1.布隆过滤器是什么 redis的布隆过滤器其实有点像咱们之前学习过的hyperloglog 深刻了解redis——新类型bitmap/hyperloglgo/GEO ,它也是不保留元素的一个汇合,它也不保留元素的具体内容,然而能断定这个元素是否在这个汇合中存在(hyperloglog是断定汇合中存在的不反复元素的个数)。 1)它是由一个初值都为零的bit数组和多个哈希函数形成,用来疾速判断某个数据是否存在。 2)实质就是判断具体数据存不存在一个大的汇合中。 3)布隆过滤器是一种相似set的数据结构,只是统计后果不太精确 2.布隆过滤器的特点 1)一个元素如果在布隆过滤器里断定后果为不存在,则肯定不存在2)一个元素在布隆过滤器里断定结果存在,则不肯定存在(原理会在上面解释)3)布隆过滤器能够增加元素,然而不能删除元素,删除元素会导致误判率减少。4)误判只会产生在过滤器没有增加过的元素,对于已经增加过的元素不会产生误判。 3.布隆过滤器应用场景 1)解决缓存穿透的问题: 缓存穿透是什么:个别状况下,咱们在查问数据的时候,如果用到redis,那么先去查redis,如果缓存中没有,再去查数据库,如果数据库中也不存在,那么就产生了缓存穿透。 当产生缓存穿透的时候,可能会有大量的查问直击mysql,肯定水平上会拖垮数据库。 解决方案:1.1)给空key设置一个空value.然而当大量空key进去的时候,也相当于查了很屡次mysql,也可能会拖垮数据库。 1.2)应用布隆过滤器把已存在的key保留在布隆过滤器中,相当于redis后面有一层布隆过滤器的爱护。当呈现申请的时候:1.2.1)先去查问布隆过滤器是否存在(如果布隆过滤器返回为不存在,那是肯定不存在。)1.2.2)如果存在,才去redis,甚至mysql查问,如果不存在,间接返回。 2)黑白名单的问题:解决原理同上,把黑名单全副放入布隆过滤器,再进行过滤。 3)海量数据查找是否存在的问题都能够用布隆过滤器。(比方现有50亿个电话号码,和10万个电话号码,疾速精确地断定号码是否存在。) 4.布隆过滤器原理 布隆过滤器应用了多个Hash函数和一个初始值都为0的bit大型数组形成。 add:比方咱们当初有一个对象obj1,它先用多个hash函数失去多个不同的值,再拿数组长度进行对这多个值取模失去多个地位,将这几个地位置为1,就实现了add操作。 query:查问的时候,只有多个哈希函数算进去的下标其中有一位是0就代表这个key不存在,如果都是1,可能是存在,则可能遇上了哈希抵触(这就是为什么,布隆过滤器,无是肯定无,有可能有)。 为什么布隆过滤器不能删除:如果布隆过滤器删除了一个元素,就是将某个对象的多个下标置为了0,就大概率会影响到别的元素,因为很可能多个元素共享了某一个下标,所以删除元素会导致误判率减少。 5.布隆过滤器优缺点 长处:高效地插入和查问,占用空间少 毛病:不能删除元素,存在误判。 6.布隆过滤器利用 public class RedissonBloomFilterDemo { public static final int _1W = 10000; //布隆过滤器里预计要插入多少数据 public static int size = 100 * _1W; //误判率,它越小误判的个数也就越少 public static double fpp = 0.03; static RedissonClient redissonClient = null; static RBloomFilter rBloomFilter = null; static { Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0); //结构redisson redissonClient = Redisson.create(config); //通过redisson结构rBloomFilter rBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter", new StringCodec()); //初始化布隆过滤器 rBloomFilter.tryInit(size, fpp); // 1测试 布隆过滤器有+redis有 rBloomFilter.add("10086"); redissonClient.getBucket("10086", new StringCodec()).set("chinamobile10086"); // 2测试 布隆过滤器有+redis无 //rBloomFilter.add("10087"); //3 测试 ,都没有 } public static void main(String[] args) { String phoneListById = getPhoneListById("10087"); System.out.println("------查问进去的后果: " + phoneListById); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } redissonClient.shutdown(); } private static String getPhoneListById(String IDNumber) { String result = null; if (IDNumber == null) { return null; } //1 先去布隆过滤器外面查问 if (rBloomFilter.contains(IDNumber)) { //2 布隆过滤器里有,再去redis外面查问 RBucket rBucket = redissonClient.getBucket(IDNumber, new StringCodec()); result = rBucket.get(); if (result != null) { return "i come from redis: " + result; } else { result = getPhoneListByMySQL(IDNumber); if (result == null) { return null; } // 从新将数据更新回redis redissonClient.getBucket(IDNumber, new StringCodec()).set(result); } return "i come from mysql: " + result; } return result; } private static String getPhoneListByMySQL(String IDNumber) { return "chinamobile" + IDNumber; }} 7.总结 ...

February 14, 2022 · 2 min · jiezi

关于redis:Redis-70-Multi-Part-AOF的设计和实现

简介:本文将详解Redis中现有AOF机制的一些有余以及Redis 7.0中引入的Multi Part AOF的设计和实现细节。 Redis 作为一种十分风行的内存数据库,通过将数据保留在内存中,Redis 得以领有极高的读写性能。然而一旦过程退出,Redis 的数据就会全副失落。 为了解决这个问题,Redis 提供了 RDB 和 AOF 两种长久化计划,将内存中的数据保留到磁盘中,防止数据失落。本文将重点探讨AOF长久化计划,以及其存在的一些问题,并探讨在Redis 7.0 (已公布RC1) 中Multi Part AOF(下文简称为MP-AOF,本个性由阿里云数据库Tair团队奉献)设计和实现细节。 AOFAOF( append only file )长久化以独立日志文件的形式记录每条写命令,并在 Redis 启动时回放 AOF 文件中的命令以达到复原数据的目标。 因为AOF会以追加的形式记录每一条redis的写命令,因而随着Redis解决的写命令增多,AOF文件也会变得越来越大,命令回放的工夫也会增多,为了解决这个问题,Redis引入了AOF rewrite机制(下文称之为AOFRW)。AOFRW会移除AOF中冗余的写命令,以等效的形式重写、生成一个新的AOF文件,来达到缩小AOF文件大小的目标。 AOFRW图1展现的是AOFRW的实现原理。当AOFRW被触发执行时,Redis首先会fork一个子过程进行后盾重写操作,该操作会将执行fork那一刻Redis的数据快照全副重写到一个名为temp-rewriteaof-bg-pid.aof的长期AOF文件中。 因为重写操作为子过程后盾执行,主过程在AOF重写期间仍然能够失常响应用户命令。因而,为了让子过程最终也能获取重写期间主过程产生的增量变动,主过程除了会将执行的写命令写入aof_buf,还会写一份到aof_rewrite_buf中进行缓存。在子过程重写的前期阶段,主过程会将aof_rewrite_buf中累积的数据应用pipe发送给子过程,子过程会将这些数据追加到长期AOF文件中(具体原理可参考这里)。 当主过程承接了较大的写入流量时,aof_rewrite_buf中可能会沉积十分多的数据,导致在重写期间子过程无奈将aof_rewrite_buf中的数据全副生产完。此时,aof_rewrite_buf残余的数据将在重写完结时由主过程进行解决。 当子过程实现重写操作并退出后,主过程会在backgroundRewriteDoneHandler 中解决后续的事件。首先,将重写期间aof_rewrite_buf中未生产完的数据追加到长期AOF文件中。其次,当所有准备就绪时,Redis会应用rename 操作将长期AOF文件原子的重命名为server.aof_filename,此时原来的AOF文件会被笼罩。至此,整个AOFRW流程完结。 图1 AOFRW实现原理 AOFRW存在的问题内存开销由图1能够看到,在AOFRW期间,主过程会将fork之后的数据变动写进aof_rewrite_buf中,aof_rewrite_buf和aof_buf中的内容绝大部分都是反复的,因而这将带来额定的内存冗余开销。 在Redis INFO中的aof_rewrite_buffer_length字段能够看到以后时刻aof_rewrite_buf占用的内存大小。如上面显示的,在高写入流量下aof_rewrite_buffer_length简直和aof_buffer_length占用了同样大的内存空间,简直节约了一倍的内存。 aof_pending_rewrite:0aof_buffer_length:35500aof_rewrite_buffer_length:34000aof_pending_bio_fsync:0当aof_rewrite_buf占用的内存大小超过肯定阈值时,咱们将在Redis日志中看到如下信息。能够看到,aof_rewrite_buf占用了100MB的内存空间且主过程和子过程之间传输了2135MB的数据(子过程在通过pipe读取这些数据时也会有外部读buffer的内存开销)。对于内存型数据库Redis而言,这是一笔不小的开销。 3351:M 25 Jan 2022 09:55:39.655 * Background append only file rewriting started by pid 68173351:M 25 Jan 2022 09:57:51.864 * AOF rewrite child asks to stop sending diffs.6817:C 25 Jan 2022 09:57:51.864 * Parent agreed to stop sending diffs. Finalizing AOF...6817:C 25 Jan 2022 09:57:51.864 * Concatenating 2135.60 MB of AOF diff received from parent.3351:M 25 Jan 2022 09:57:56.545 * Background AOF buffer size: 100 MBAOFRW带来的内存开销有可能导致Redis内存忽然达到maxmemory限度,从而影响失常命令的写入,甚至会触发操作系统限度被OOM Killer杀死,导致Redis不可服务。 ...

February 14, 2022 · 6 min · jiezi

关于redis:深入理解redis新类型bitmaphyperloglgoGEO

1.为什么会有这三种类型?2.统计的类型有哪些?3.bitmap类型及其利用4.hyperloglog类型及其利用5.GEO类型及其利用6.总结 1.为什么会有这三种类型?假如咱们当初有上面这三个需要:1.有一个签到打卡的性能,须要统计一个月内间断打卡的用户。2.统计一个网站的用户浏览量(PV)。3.打车软件中,查出左近闲暇的车辆 如果以上的数据都是亿级别,请问你该如何统计? 需要痛点:1.存储问题:数据量太大,如何省空间存储2.取数问题:数据量大的时候,数据可能存的进,取不出 2.统计的类型有哪些? 在咱们的日常报表开发中,常见的有以下四种统计 1)聚合统计:统计多个元素聚合的后果,比方总和,交并差汇合。 2)排序统计:依照某个值进行排序,并返回列表的统计,比方排行榜。 3)二值统计:汇合元素取值只有0或者1两种,比方签没签到。 4)基数统计:统计汇合中不反复的元素个数,比方统计网站UV值。 3.bitmap类型及其利用咱们先看一下bitMap的数据结构:阐明:用String类型作为底层数据结构实现的一种统计二值状态(0或者1)的数据类型。bitMap把一个字节拆分成了8位,每一个位别离能存0或者1,这是一种把一个字节拆成八个位的存储方法,空间利用率比拟高。Bitmap反对的最大位数是2^32位,它能够极大的节约存储空间,应用512M内存就能够存储多大42.9亿的字节信息(2^32 = 4294967296) 利用:签到性能,日沉闷数统计,该用户一年中登录的天数等等。 假如咱们当初要制作一个签到性能 惯例的思路:mysql作为解决方案: CREATE TABLE user_sign(keyid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,user_key VARCHAR(200),#京东用户IDsign_date DATETIME,#签到日期(20210618)sign_count INT #间断签到天数)创立一张这样的表,而后依据业务逻辑crud就能够了,然而数据量一旦达到亿级,就有可能呈现存的进,取不出的难堪状况,而后存储的空间也会变得十分大。 改良办法:基于Redis的BitMaps实现签到日历。 咱们后面提到过,bitMap将一个字节拆分成了8bit,咱们只须要用到一个bit,就能够示意一天的签到状况(值为0或者1)。一个月最多31天,那么只须要用到31个bit位,一年的签到状况也只须要用到365个bit位,咱们再拿 "年-月" 等值作为key,就能够极大节俭空间。 根本命令:redis: setbit key offset value //setbit 键 偏移位 只能零或者1 getbit key offset strlen //统计字节数占用多少bitcount //全副键外面含有1的有多少个?bitop //将两个值做与运算,比方统计两天同时都签到的人 java: stringRedisTemplate.opsForValue().setBit(key, offset, value); 4.hyperloglog类型及其利用hyperloglog是什么:它是一种去重统计性能的基数预计算法,它并不保留数据自身,而是依据输出的元素,而判断总共有多少个元素是不反复的。 基数是什么:是一种数据集,去反复后的数据的个数。 如果有这样一个需要:统计一个有亿万级并发的网站上面,每天拜访的用户数(UV数),要依据用户去重,咱们会有以下几种统计办法:1)mysql 2)HashSet 以上两种办法,随着元素内容的一直减少,数据量越来越宏大,难以管控,而且存的艰巨,取地艰巨。 3)hyperloglog 后面也说过,它不保留元素的具体数据,只是进行不反复的基数统计,而且应用固定的空间。 留神:hyperloglog 有误差,就义准确率来换取空间,误差仅仅只是0.81%左右。 根本命令:redis: pfadd key element //增加元素pfcount key //基数统计pfmerge new_key key1 key2 //合并两个hyperloglog ...

February 14, 2022 · 1 min · jiezi

关于redis:手把手教你部署redis

先手材料请移步 下载yum通过yum下载wget:yum install wget通过wget下载redis的压缩文件wget http://download.redis.io/rele... 解压redis的包:tar -zxvf redis-4.0.6.tar.gz进入redis目录:make MALLOC=libc cd src&&make install 启动redis服务:./redis-sever 启动redis三种形式第一种启动形式:./redis-sever,过程级别,敞开过程后,redis敞开第二种形式:指定配置文件启动:查看redis的配置文件:vi redis.conf 批改daemonize为yes 输出命令:./redis-server /usr/local/redis-4.0.6/redis.conf 启动redis 进入redis-cli:./redis-cli -p 6379 第三种形式:应用redis启动脚本设置开机自启动在etc文件下创立redis文件:mkdir /etc/redis拷贝redis.conf到etc/redis目录下:cp /usr/local/redis-4.0.6/redis.conf /etc/redis/6379.conf拷贝redis_init_script文件到etc/init.d/目录,命名为redisd:cp redis_init_script /etc/init.d/redisd设置开机自启:chkconfig redisd on设置为开机自启动,间接配置开启自启动 chkconfig redisd on 发现错误: service redisd does not support chkconfig解决形式,编辑/etc/init.d/redisd文件,在结尾退出以下命令:#!/bin/sh# chkconfig: 2345 90 10启动redis:service /etc/init.d/redisd start可能会报错:/var/run/redis_6379.pid exists, process is already running or crashed解决办法:rm -rf /var/run/redis_6379.pid

February 11, 2022 · 1 min · jiezi

关于redis:Redis源码数据结构reidsServerredisDBrobjClientredisCommand

一、redisServer、redisDB、robj关系 Redis没有表的概念。然而能够用key作标识进行辨别,比方:user:1000作为key值,示意在user表下id为1000的元素,相似于user表的id=1000的行。 Redis逻辑关系: redisServer–>redisDB–>key-redisObject。所以这里次要介绍redisServer、redisDB、redisObject构造。 二、redisServer构造体(在server.h中)服务端构造体redisServer存储Redis服务器的所有信息,包含但不限于数据库、配置参数、命令表、监听端口与地址、客户端列表、若干统计信息、RDB与AOF长久化相干信息、主从复制相干信息、集群相干信息等。 redis.h/redisServer 构造记录了和服务器相干的所有数据, 这个构造体次要蕴含以下信息: 服务器中的所有数据库是数组指针。已连贯客户端的信息 list指针。服务器配置选项:redis.conf中配置的存储。命令hash表:在执行命令时,依据字符来查找相应命令的实现函数。服务端的网络信息:套接字地址、端口,以及套接字描述符。日志(log)和慢查问日志(slowlog)的选项和相干信息。统计信息:比方键有多少次命令、不命中,服务器的运行工夫,等等。数据长久化(AOF 和 RDB)的配置和状态。实现订阅与公布(pub/sub)性能所需的数据结构。Lua 脚本的运行环境及相干选项。struct redisServer { /* General */ //配置文件门路 char *configfile; /* Absolute config file path, or NULL */ //serverCron()调用频率 int hz; /* serverCron() calls frequency in hertz */ //数据库对象数组指针 redisDb *db; //反对的命令列表 dict *commands; /* Command table */ //没有转化的命令 dict *orig_commands; /* Command table before command renaming. */ //事件 aeEventLoop *el; //每分钟减少一次 unsigned lruclock:22; /* Clock incrementing every minute, for LRU */ unsigned lruclock_padding:10; int shutdown_asap; /* SHUTDOWN needed ASAP */ int activerehashing; /* Incremental rehash in serverCron() */ //验证明码 char *requirepass; /* Pass for AUTH command, or NULL */ char *pidfile; /* PID file path */ int arch_bits; /* 32 or 64 depending on sizeof(long) */ int cronloops; /* Number of times the cron function run */ char runid[REDIS_RUN_ID_SIZE+1]; /* ID always different at every exec. */ int sentinel_mode; /* True if this instance is a Sentinel. */ /* Networking */ int port; /* TCP listening port */ int tcp_backlog; /* TCP listen() backlog */ char *bindaddr[REDIS_BINDADDR_MAX]; /* Addresses we should bind to */ int bindaddr_count; /* Number of addresses in server.bindaddr[] */ char *unixsocket; /* UNIX socket path */ mode_t unixsocketperm; /* UNIX socket permission */ int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */ int ipfd_count; /* Used slots in ipfd[] */ int sofd; /* Unix socket file descriptor */ int cfd[REDIS_BINDADDR_MAX];/* Cluster bus listening socket */ int cfd_count; /* Used slots in cfd[] */ // 连贯的客户端 list *clients; /* List of active clients */ list *clients_to_close; /* Clients to close asynchronously */ list *slaves, *monitors; /* List of slaves and MONITORs */ redisClient *current_client; /* Current client, only used on crash report */ int clients_paused; /* True if clients are currently paused */ mstime_t clients_pause_end_time; /* Time when we undo clients_paused */ char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */ dict *migrate_cached_sockets;/* MIGRATE cached sockets */ /* RDB / AOF loading information */ int loading; /* We are loading data from disk if true */ off_t loading_total_bytes; off_t loading_loaded_bytes; time_t loading_start_time; off_t loading_process_events_interval_bytes; /* Fast pointers to often looked up command */ struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand, *rpopCommand; /* Fields used only for stats */ time_t stat_starttime; /* Server start time */ long long stat_numcommands; /* Number of processed commands */ long long stat_numconnections; /* Number of connections received */ long long stat_expiredkeys; /* Number of expired keys */ long long stat_evictedkeys; /* Number of evicted keys (maxmemory) */ long long stat_keyspace_hits; /* Number of successful lookups of keys */ long long stat_keyspace_misses; /* Number of failed lookups of keys */ size_t stat_peak_memory; /* Max used memory record */ long long stat_fork_time; /* Time needed to perform latest fork() */ long long stat_rejected_conn; /* Clients rejected because of maxclients */ long long stat_sync_full; /* Number of full resyncs with slaves. */ long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */ long long stat_sync_partial_err;/* Number of unaccepted PSYNC requests. */ //保留慢日志命令 list *slowlog; /* SLOWLOG list of commands */ long long slowlog_entry_id; /* SLOWLOG current entry ID */ long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */ unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */ /* The following two are used to track instantaneous "load" in terms * of operations per second. */ long long ops_sec_last_sample_time; /* Timestamp of last sample (in ms) */ long long ops_sec_last_sample_ops; /* numcommands in last sample */ long long ops_sec_samples[REDIS_OPS_SEC_SAMPLES]; int ops_sec_idx; /* Configuration */ int verbosity; /* Loglevel in redis.conf */ int maxidletime; /* Client timeout in seconds */ int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */ int active_expire_enabled; /* Can be disabled for testing purposes. */ size_t client_max_querybuf_len; /* Limit for client query buffer length */ int dbnum; /* Total number of configured DBs */ int daemonize; /* True if running as a daemon */ clientBufferLimitsConfig client_obuf_limits[REDIS_CLIENT_LIMIT_NUM_CLASSES]; /* AOF persistence */ int aof_state; /* REDIS_AOF_(ON|OFF|WAIT_REWRITE) */ int aof_fsync; /* Kind of fsync() policy */ char *aof_filename; /* Name of the AOF file */ int aof_no_fsync_on_rewrite; /* Don't fsync if a rewrite is in prog. */ int aof_rewrite_perc; /* Rewrite AOF if % growth is > M and... */ off_t aof_rewrite_min_size; /* the AOF file is at least N bytes. */ off_t aof_rewrite_base_size; /* AOF size on latest startup or rewrite. */ off_t aof_current_size; /* AOF current size. */ int aof_rewrite_scheduled; /* Rewrite once BGSAVE terminates. */ pid_t aof_child_pid; /* PID if rewriting process */ list *aof_rewrite_buf_blocks; /* Hold changes during an AOF rewrite. */ sds aof_buf; /* AOF buffer, written before entering the event loop */ int aof_fd; /* File descriptor of currently selected AOF file */ int aof_selected_db; /* Currently selected DB in AOF */ time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */ time_t aof_last_fsync; /* UNIX time of last fsync() */ time_t aof_rewrite_time_last; /* Time used by last AOF rewrite run. */ time_t aof_rewrite_time_start; /* Current AOF rewrite start time. */ int aof_lastbgrewrite_status; /* REDIS_OK or REDIS_ERR */ unsigned long aof_delayed_fsync; /* delayed AOF fsync() counter */ int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */ int aof_last_write_status; /* REDIS_OK or REDIS_ERR */ int aof_last_write_errno; /* Valid if aof_last_write_status is ERR */ /* RDB persistence */ long long dirty; /* Changes to DB from the last save */ long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */ pid_t rdb_child_pid; /* PID of RDB saving child */ struct saveparam *saveparams; /* Save points array for RDB */ int saveparamslen; /* Number of saving points */ char *rdb_filename; /* Name of RDB file */ int rdb_compression; /* Use compression in RDB? */ int rdb_checksum; /* Use RDB checksum? */ time_t lastsave; /* Unix time of last successful save */ time_t lastbgsave_try; /* Unix time of last attempted bgsave */ time_t rdb_save_time_last; /* Time used by last RDB save run. */ time_t rdb_save_time_start; /* Current RDB save start time. */ int lastbgsave_status; /* REDIS_OK or REDIS_ERR */ int stop_writes_on_bgsave_err; /* Don't allow writes if can't BGSAVE */ /* Propagation of commands in AOF / replication */ redisOpArray also_propagate; /* Additional command to propagate. */ /* Logging */ char *logfile; /* Path of log file */ int syslog_enabled; /* Is syslog enabled? */ char *syslog_ident; /* Syslog ident */ int syslog_facility; /* Syslog facility */ /* Replication (master) */ int slaveseldb; /* Last SELECTed DB in replication output */ long long master_repl_offset; /* Global replication offset */ int repl_ping_slave_period; /* Master pings the slave every N seconds */ char *repl_backlog; /* Replication backlog for partial syncs */ long long repl_backlog_size; /* Backlog circular buffer size */ long long repl_backlog_histlen; /* Backlog actual data length */ long long repl_backlog_idx; /* Backlog circular buffer current offset */ long long repl_backlog_off; /* Replication offset of first byte in the backlog buffer. */ time_t repl_backlog_time_limit; /* Time without slaves after the backlog gets released. */ time_t repl_no_slaves_since; /* We have no slaves since that time. Only valid if server.slaves len is 0. */ int repl_min_slaves_to_write; /* Min number of slaves to write. */ int repl_min_slaves_max_lag; /* Max lag of <count> slaves to write. */ int repl_good_slaves_count; /* Number of slaves with lag <= max_lag. */ /* Replication (slave) */ char *masterauth; /* AUTH with this password with master */ char *masterhost; /* Hostname of master */ int masterport; /* Port of master */ int repl_timeout; /* Timeout after N seconds of master idle */ redisClient *master; /* Client that is master for this slave */ redisClient *cached_master; /* Cached master to be reused for PSYNC. */ int repl_syncio_timeout; /* Timeout for synchronous I/O calls */ int repl_state; /* Replication status if the instance is a slave */ off_t repl_transfer_size; /* Size of RDB to read from master during sync. */ off_t repl_transfer_read; /* Amount of RDB read from master during sync. */ off_t repl_transfer_last_fsync_off; /* Offset when we fsync-ed last time. */ int repl_transfer_s; /* Slave -> Master SYNC socket */ int repl_transfer_fd; /* Slave -> Master SYNC temp file descriptor */ char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */ time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */ int repl_serve_stale_data; /* Serve stale data when link is down? */ int repl_slave_ro; /* Slave is read only? */ time_t repl_down_since; /* Unix time at which link with master went down */ int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */ int slave_priority; /* Reported in INFO and used by Sentinel. */ char repl_master_runid[REDIS_RUN_ID_SIZE+1]; /* Master run id for PSYNC. */ long long repl_master_initial_offset; /* Master PSYNC offset. */ /* Replication script cache. */ dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */ list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */ int repl_scriptcache_size; /* Max number of elements. */ /* Synchronous replication. */ list *clients_waiting_acks; /* Clients waiting in WAIT command. */ int get_ack_from_slaves; /* If true we send REPLCONF GETACK. */ /* Limits */ unsigned int maxclients; /* Max number of simultaneous clients */ unsigned long long maxmemory; /* Max number of memory bytes to use */ int maxmemory_policy; /* Policy for key eviction */ int maxmemory_samples; /* Pricision of random sampling */ /* Blocked clients */ unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */ list *unblocked_clients; /* list of clients to unblock before next loop */ list *ready_keys; /* List of readyList structures for BLPOP & co */ /* Sort parameters - qsort_r() is only available under BSD so we * have to take this state global, in order to pass it to sortCompare() */ int sort_desc; int sort_alpha; int sort_bypattern; int sort_store; /* Zip structure config, see redis.conf for more information */ size_t hash_max_ziplist_entries; size_t hash_max_ziplist_value; size_t list_max_ziplist_entries; size_t list_max_ziplist_value; size_t set_max_intset_entries; size_t zset_max_ziplist_entries; size_t zset_max_ziplist_value; time_t unixtime; /* Unix time sampled every cron cycle. */ long long mstime; /* Like 'unixtime' but with milliseconds resolution. */ /* Pubsub */ dict *pubsub_channels; /* Map channels to list of subscribed clients */ list *pubsub_patterns; /* A list of pubsub_patterns */ int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an xor of REDIS_NOTIFY... flags. */ /* Cluster */ int cluster_enabled; /* Is cluster enabled? */ mstime_t cluster_node_timeout; /* Cluster node timeout. */ char *cluster_configfile; /* Cluster auto-generated config file name. */ struct clusterState *cluster; /* State of the cluster */ int cluster_migration_barrier; /* Cluster replicas migration barrier. */ /* Scripting */ lua_State *lua; /* The Lua interpreter. We use just one for all clients */ redisClient *lua_client; /* The "fake client" to query Redis from Lua */ redisClient *lua_caller; /* The client running EVAL right now, or NULL */ dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */ mstime_t lua_time_limit; /* Script timeout in milliseconds */ mstime_t lua_time_start; /* Start time of script, milliseconds time */ int lua_write_dirty; /* True if a write command was called during the execution of the current script. */ int lua_random_dirty; /* True if a random command was called during the execution of the current script. */ int lua_timedout; /* True if we reached the time limit for script execution. */ int lua_kill; /* Kill the script if true. */ /* Assert & bug reporting */ char *assert_failed; char *assert_file; int assert_line; int bug_report_start; /* True if bug report header was already logged. */ int watchdog_period; /* Software watchdog period in ms. 0 = off */};三、redisDB构造体(在server.h中)typedef struct redisDb { int id; /* id是本数据库的序号,为0-15(默认Redis有16个数据库) */ dict *dict; /* 存储数据库所有的key-value */ dict *expires; /* 键的过期工夫,字典的键为键,字典的值为过期 UNIX 工夫戳 */ dict *blocking_keys; /* blpop 存储阻塞key和客户端对象 */ dict *ready_keys; /* 阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象 */ dict *watched_keys; /* 存储watch监控的的key和客户端对象 */ long long avg_ttl; /* 存储的数据库对象的均匀ttl(time to live),用于统计 */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */} redisDb;long long 类型在32位的零碎中: short与int占两个字节, long 占四个字节, long long 占八个字节; ...

February 10, 2022 · 12 min · jiezi

关于redis:Redis-使用-List-实现消息队列能保证消息可靠么

分布式系统中必备的一个中间件就是音讯队列,通过音讯队列咱们能对服务间进行异步解耦、流量消峰、实现最终一致性。 目前市面上曾经有 RabbitMQ、RochetMQ、ActiveMQ、Kafka等,有人会问:“Redis 适宜做音讯队列么?” 在答复这个问题之前,咱们先从实质思考: 音讯队列提供了什么个性?Redis 如何实现音讯队列?是否满足存取需要?明天,码哥联合音讯队列的特点一步步带大家剖析应用 Redis 的 List 作为音讯队列的实现原理,并分享如何把 SpringBoot 与 Redission 整合使用到我的项目中。 什么是音讯队列音讯队列是一种异步的服务间通信形式,实用于分布式和微服务架构。音讯在被解决和删除之前始终存储在队列上。 每条音讯仅可被一位用户解决一次。音讯队列可被用于拆散重量级解决、缓冲或批处理工作以及缓解高峰期工作负载。 Producer:音讯生产者,负责产生和发送音讯到 Broker;Broker:音讯解决核心。负责音讯存储、确认、重试等,个别其中会蕴含多个 queue;Consumer:音讯消费者,负责从 Broker 中获取音讯,并进行相应解决;音讯队列的应用场景有哪些呢?音讯队列在理论利用中包含如下四个场景: 利用耦合:发送方、接管方零碎之间不须要理解单方,只须要意识音讯。多利用间通过音讯队列对同一音讯进行解决,防止调用接口失败导致整个过程失败;异步解决:多利用对音讯队列中同一音讯进行解决,利用间并发解决音讯,相比串行解决,缩小解决工夫;限流削峰:广泛应用于秒杀或抢购流动中,防止流量过大导致利用零碎挂掉的状况;音讯驱动的零碎:零碎分为音讯队列、音讯生产者、音讯消费者,生产者负责产生音讯,消费者(可能有多个)负责对音讯进行解决;音讯队列满足哪些个性音讯有序性 音讯是异步解决的,然而消费者须要依照生产者发送音讯的程序来生产,避免出现后发送的音讯被先解决的状况。 反复音讯解决 生产者可能因为网络问题呈现音讯重传导致消费者可能会收到多条反复音讯。 同样的音讯反复屡次的话可能会造成一业务逻辑屡次执行,须要确保如何防止反复生产问题。 可靠性 一次保障音讯的传递。如果发送音讯时接收者不可用,音讯队列会保留音讯,直到胜利地传递它。 当消费者重启后,能够持续读取音讯进行解决,避免音讯脱漏。 List 实现音讯队列Redis 的列表(List)是一种线性的有序构造,能够依照元素被推入列表中的程序来存储元素,能满足「先进先出」的需要,这些元素既能够是文字数据,又能够是二进制数据。 LPUSH 生产者应用 LPUSH key element[element...] 将音讯插入到队列的头部,如果 key 不存在则会创立一个空的队列再插入音讯。 如下,生产者向队列 queue 先后插入了 「Java」「码哥字节」「Go」,返回值示意音讯插入队列后的个数。 > LPUSH queue Java 码哥字节 Go(integer) 3RPOP 消费者应用 RPOP key 顺次读取队列的音讯,先进先出,所以 「Java」会先读取生产: > RPOP queue"Java"> RPOP queue"码哥字节"> RPOP queue"Go" 实时生产问题65 哥:这么简略就实现了么?别快乐的太早,LPUSH、RPOP 存在一个性能危险,生产者向队列插入数据的时候,List 并不会被动告诉消费者及时生产。 ...

February 10, 2022 · 2 min · jiezi

关于redis:redis安装与基本使用

redis装置与根本应用一、redis在Ubuntu中的装置1、sudo apt install redis或sudo apt install redis-server 2、将/etc/redis/redis.conf文件挪动到一个自主目录中(目标是为了后续集群的部署,也不便对配置文件批改出错时可能重新配置) 例如:我搁置在了/root/yjnconfig目录中 3、批改一些配置信息: 将挪动后的配置文件关上,批改如下配置信息 3、执行命令 redis-server /root/yjnconfig/redis.conf 以开启咱们复制后的redis 4、执行redis-cli 以关上客户端 在客户端中应用ping命令测试连通性,返回PONG即为失常 5、还能够应用ps -ef|grep redis命令查看是否启动胜利 6、退出时在redis-cli中输出shutdown,接着输出exit退出 此时,能够看到过程中曾经没有了redis 7、应用redis-benchmark进行压力测试 二、redis常用命令1、select [num] 抉择号码为num的数据库(共16个数据库,从0开始) 例 select 1 2、set [name] [value] 设置一个字段名为[name],值为[value] 例 set name alpaca 设置一个字段名为name,值为alpaca 3、get [name] 获取字段名为[name]的值 例 get name 4、exists [name] 判断字段[name]是否存在 例 exists name 5、keys * 列出以后数据库所有字段 6、move [name] 1 将以后数据库中的字段name挪动到数据库1中 ...

February 9, 2022 · 2 min · jiezi

关于redis:实用WGCLOUD监测Redis运行状态教程

介绍通过WGCLOUD来监测Redis运行的状态,端口,日志 https://www.bilibili.com/vide...

February 5, 2022 · 1 min · jiezi

关于redis:免费领取红包封面Redis-系列redis-学习四set-集合hash-哈希zset-有序集合初步认知

文末收费支付红包封面哦,总共 2000 个 ,先到先得 set 汇合 set 汇合外面的数据是不能重读的 SADD key member [member ...]向 set 汇合中增加元素 SMEMBERS key查看汇合中的所有元素 SISMEMBER key member查看某一个数据是否在汇合中 SCARD key查看汇合数据的个数,也就是汇合的长度 SREM key member [member ...]移除汇合中指定的元素 127.0.0.1:6379> sadd myset "hello"(integer) 1127.0.0.1:6379> sadd myset "wolrd" "xiaomotong"(integer) 2127.0.0.1:6379> SMEMBERS myset1) "wolrd"2) "xiaomotong"3) "hello"127.0.0.1:6379> SISMEMBER myset hello(integer) 1127.0.0.1:6379> SCARD myset(integer) 3127.0.0.1:6379> SREM myset wolrd(integer) 1127.0.0.1:6379> SMEMBERS myset1) "xiaomotong"2) "hello"SRANDMEMBER key [count]随机获取汇合外面的数据,能够指定个数 127.0.0.1:6379> sadd myset "v1" "v2" "v3" "v4"(integer) 4127.0.0.1:6379> SMEMBERS myset1) "v4"2) "xiaomotong"3) "v1"4) "v2"5) "hello"6) "v3"127.0.0.1:6379> SRANDMEMBER myset"v2"127.0.0.1:6379> SRANDMEMBER myset"xiaomotong"127.0.0.1:6379> SRANDMEMBER myset 21) "v4"2) "v1"127.0.0.1:6379>127.0.0.1:6379> SRANDMEMBER myset 21) "xiaomotong"2) "v1"SPOP key [count]随机删除汇合中的任意元素 ...

January 29, 2022 · 3 min · jiezi

关于redis:redis优化bigkeyhotkey

bigkey优化什么是bigkeybigkey是指某个key的value太大,分成两种状况。 字符串类型:它的big体现在单个value值很大,个别认为超过10KB就是bigkey。比如说把文章正文甚至小说全文都缓存进redis了。 非字符串类型:哈希、列表、汇合、有序汇合,它们的big体现在元素个数太多。比方通过hash的形式来存储每一天用户订单次数。其中key = 日期,field = 用户id,value = 用户订单数。那么如果一天有上千万个用户下订单,那么field就会十分多,存储空间也很大,造成所谓的大key。 redis自身曾经提供了查找bigkey的命令。 bigkey的危害 读写bigkey可能会导致超时,而redis是单线程操作数据,重大的会导致阻塞整个redis服务。而且一个key只会被分片到一个节点,无奈摊派读写压力。 bigkey优化办法对于string类型,应该防止存入过大的字符串,比方不要把文章正文放入redis之类的。对于其余类型,应该尽量减少成员数,能够对元素汇合进行宰割。比方在hash的field过多的状况下,能够对field进行rehash,并且对1000取模,而后作为原key的后缀,生成1000个新的key,这些key还能够被分片到其余节点,摊派读写压力。 hotkey优化hotkey的危害 hotkey就是访问量大的key,因为一个key只能被分片到一个节点,因而短时间集中拜访hotkey会压垮该redis节点,而接下来的申请会打到数据库上,压垮整个服务。 发现hotkey的办法 业务教训,比方明星、大V相干的key根本都是hotkey。在redis的proxy层做统计,毛病是有的redis集群可能没有proxy。redis自带命令,比方redis-cli –hotkeys命令,会扫描所有key来找出hotkey,毛病是当key比拟多时执行比较慢。hotkey优化办法 优化的关键点在于加重hotkey所在节点的压力。 服务器本地缓存:服务器把hotkey缓存在内存中,当用户申请hotkey时不须要拜访redis,间接从内存查找、返回即可。备份hotkey:在所有节点上都备份这个hotkey,各自加上一个序号后缀作为key,用户申请hotkey时随机生一个后缀数字,而后拜访对应的节点。而且某个节点的hotkey过期后能够不读数据库,而是读其余节点以重建缓存。过期工夫:设置hotkey永不过期,或者设置每个hotkey的备份的过期工夫不同,这样能够避免集中过期导致大量申请打到数据库上。

January 28, 2022 · 1 min · jiezi

关于redis:Redis大Key删除真的会造成主线程阻塞吗做实验来证明

背景网上很多对于redis的话题都谈到了要防止造成大key,因为删除会造成主线程阻塞。看到过一个评论说测试删除2G的一个大key,零碎阻塞了大略80秒的工夫。已经面试时被问到如何删除一个大key。最近在浏览redis5.0.8源码,看到其中对于大key的删除,实际上只是在主线程删除了key相干的数据,而理论的value及其内存开释是放在异步删除的线程进行的。这种操作应该不会像网上所说,造成80秒主线程阻塞那么恐怖。测试筹备redis5.0.x服务端程序一份(不便起见,此处应用docker构建)。php7.3(无他,零碎自带7.3版本),也可应用其它脚本语言。c编译器,增加数据的程序应用的c开发(因为自带的php没装置pcntl扩大=,=,所以应用c的多线程来并发的结构和增加数据),这里次要用于结构数据,能够应用其它语言。结构数据编写数据结构代码,此处基于macos平台编写,可依据理论操作系统稍作调整。写的比拟毛糙,增加不同数量的数据以及批改服务端信息都须要批改宏定义,而后从新编译。#include <stdio.h>#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/wait.h>#include <sys/types.h>#include <string.h>#include <errno.h>#include <arpa/inet.h>#include <time.h>#include <sys/time.h>#include <signal.h>#define RESV_BUFF_SIZE 512#define SEND_BUFF_SIZE 1024/** * redis服务端的IP地址 */#define DEST_ADDR "172.20.23.83"/** * redis服务端的端口号 */#define DEST_PORT 6379/** * 每个线程总共执行插入动作的次数 */#define TRANS_PER_THREAD 10000/** * 每隔多少次插入进行一次日志打印 */#define LOG_STEP 255/** * 总共开启线程数量 */#define THREADS_NUM 100int numLen(int num) { int i = 0; do { num /= 10; i++; } while (num != 0); return i;}/** * 线程执行代码 * @param p 从0-99的线程编号,用于确定以后线程执行插入的数据段 * 比方编号为1,则插入TRANS_PER_THREAD*1 至 TRANS_PER_THREAD*1 + TRANS_PER_THREAD 区间的数据 */void threadMain(void *p){ int sockfd,*index=p,rlen; *index *= TRANS_PER_THREAD; int end = *index + TRANS_PER_THREAD; struct sockaddr_in dest_addr; bzero(&(dest_addr), sizeof(dest_addr)); char resvbuf[RESV_BUFF_SIZE]; char sendbuf[SEND_BUFF_SIZE]; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("socket create failed:%d!\n",sockfd); } dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(DEST_PORT); inet_pton(AF_INET,DEST_ADDR,&dest_addr.sin_addr); if (connect(sockfd,(struct sockaddr*)&dest_addr, sizeof(struct sockaddr)) == -1) { printf("connect failed:%d!\n",errno); perror("error: "); } else { printf("connect success!\n"); struct timeval tv,ltv; for (int i = *index; i < end; ++i) { rlen = numLen(i); memset(sendbuf,0,SEND_BUFF_SIZE); /** * 结构当次申请发送的数据 * 需满足RESP协定标准 */ sprintf(sendbuf,"*4\r\n$4\r\nHSET\r\n$6\r\nmigkey\r\n$%d\r\nmest%d\r\n$%d\r\nmest67890%d\r\n",rlen+4,i,rlen+9,i); write(sockfd,sendbuf,strlen(sendbuf)); /** * 尽管咱们并不关怀服务端的返回 * 此处仍然进行了读取操作,防止缓冲堆满 */ read(sockfd,resvbuf,RESV_BUFF_SIZE); /** * 每执行LOG_STEP+1次就打印一条日志信息 * LOG_STEP需满足(2^n - 1) */ if ((i & LOG_STEP) == 0) { gettimeofday(&tv, NULL); if (i > *index) { printf("used %ld.%d seconds.\n",tv.tv_sec - ltv.tv_sec,tv.tv_usec - ltv.tv_usec); fflush(stdout); } ltv = tv; } } } printf("finished index:%d.\n",*index); close(sockfd);}void handle_pipe(int sig) {// printf("sig %d ignore.\n",sig);}int main() { /** * 因为tcp的client敞开后,服务端依然有可能向咱们发送数据 * 这样会造成咱们的过程收到SIGPIPE信号 * 所以此处注册信号处理函数 */ struct sigaction sa; sa.sa_handler = handle_pipe; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE,&sa,NULL); pthread_t pts[THREADS_NUM]; int indexs[THREADS_NUM]; struct timeval tv,ltv; gettimeofday(&ltv, NULL); /** * 创立线程 */ for (int i = 0; i < THREADS_NUM; ++i) { indexs[i] = i; if (pthread_create(&pts[i], NULL, threadMain, &indexs[i]) != 0) { printf("create thread error!\n"); } else { printf("thread %d created!\n",i); } } /** * 期待线程 */ for (int i = 0; i < THREADS_NUM; ++i) { pthread_join(pts[i], NULL); } gettimeofday(&tv, NULL); printf("\n------All finished!------\n""used %ld.%d seconds.\n",tv.tv_sec - ltv.tv_sec,tv.tv_usec - ltv.tv_usec); return 0;}编译并执行,此处通过批改代码以及屡次执行,总共向服务端增加了3个大key。别离为:bigkey,数据长度100万,占用内存约58M。migkey,数据长度100万,占用内存约58M。sigkey,数据长度10万,占用内存5.5M。筹备测试程序测试代码,一个简略的php脚本,每100ms向服务端发送一个get命令。察看在执行了删除大key的命令后,这个get命令执行的延时距离判断对主线程的阻塞水平。脚本代码如下(此处是查问一个key为m的string类型数据。):<?php $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_connect($socket, '172.20.23.83',6379); $st = explode(' ',microtime()); $st = $st[0] + $st[1]; while (true) { $msg = "*2\r\n$3\r\nGET\r\n$1\r\nm\r\n"; socket_write($socket,$msg,strlen($msg)); $s = socket_read($socket,100); $et = explode(' ',microtime()); $et = $et[0] + $et[1]; if (($et - $st) > 0.13) { echo "-------------------xxx---------------------\n"; } echo microtime().'--'.$s; usleep(100000); $st = $et; } socket_close($socket);执行测试应用redis-cli连贯redis服务端。设置一个key为m的string类型数据。执行用于观测的php脚本。redis-cli执行删除命令。敞开php脚本。查找-------------------xxx---------------------标记。观测标记前后两次输入的工夫距离长度。测试后果migkey:0.27018300 1643271634--$4mack-------------------xxx---------------------0.66599900 1643271634--$4mackbigkey(前面的两个标记工夫距离尽管超过了0.13秒,然而相差不远,能够确定为网络稳定造成。这里应该察看第一个标记的前后时间差。):0.23842800 1643271538--$4mack-------------------xxx---------------------0.76545200 1643271538--$4mack-------------------xxx---------------------0.90037200 1643271538--$4mack0.00909800 1643271539--$4mack-------------------xxx---------------------0.48198300 1643271539--$4macksigkey:未观测到稳定。初步后果总结5.5M的key未造成显著阻塞。58M左右的key造成约50ms左右的阻塞。持续结构更大的数据与网上所说的2G数据差距太大,须要构建更大的数据。因为通过网络客户端构建数据,即便是多线程,构建效率仍旧不高,所以此次大数据采纳特地的形式(利用redis-cli带pipe参数)进行构建。应用php脚本构建一个蕴含3000万个redis命令文件0.txt,脚本如下:<?phpecho "start...\n";$st = explode(' ', microtime());$st = $st[0] + $st[1];for ($i=0; $i < 30000000; $i++) { $rstr = "{$i}"; $slen = strlen($rstr); $klen = $slen + 4; $vlen = $slen + 9; $msg = "*4\r\n$4\r\nHSET\r\n$6\r\nligkey\r\n\${$klen}\r\ntest{$rstr}\r\n\${$vlen}\r\ntest67890{$rstr}\r\n"; file_put_contents('0.txt',$msg,FILE_APPEND);}$et = explode(' ', microtime());$et = $et[0] + $et[1];$used = $et - $st;echo "finished...\n","used $used seconds.\n";执行脚本生成0.txt文件。应用命令 cat 0.txt | redis-cli --pipe 导入数据。再次察看通过redis命令查看到此次数据约为1.8G。执行观测脚本。删除数据ligkey。大数据观测后果0.31861400 1643279405--$4mack-------------------xxx---------------------0.39795600 1643279424--$4mack删除1.8G数据耗时约19.08s。论断删除大key的确会造成主线程长时间阻塞,且随着数据的增大阻塞工夫也变长。下次笔记中须要从源码的角度了解为何会阻塞主线程以及是否有解决方案。

January 27, 2022 · 3 min · jiezi

关于redis:嫌-OSS-查询太慢看我们如何将速度提升-10-倍

背景HDFS 是 Hadoop 生态的默认存储系统,很多数据分析和管理工具都是基于它的 API 设计和实现的。但 HDFS 是为传统机房设计的,在云上保护 HDFS 一点也不轻松,须要投入不少人力进行监控、调优、扩容、故障复原等一系列事件,而且还费用昂扬,老本可能是对象存储是十倍以上。 在存储与计算拆散大趋势下,很多人尝试用对象存储来构建数据湖计划,对象存储也提供了用于 Hadoop 生态的 connector,但因为对象存储本身的局限性,性能和性能都十分无限,在数据增长到肯定规模后这些问题更加突出。 JuiceFS 正是为了解决这些问题而设计的,在保留对象存储的云原生特点的同时,更好地兼容 HDFS 的语义和性能,显著晋升整体性能。本文以阿里云 OSS 为例,给大家介绍一下 JuiceFS 是如何全面晋升对象存储在云上大数据场景中的体现的。 元数据性能为了残缺兼容 HDFS 并提供极致的元数据性能,JuiceFS 应用全内存的形式来治理元数据,将 OSS 作为数据存储应用,所有的元数据操作都不须要拜访 OSS 以保障极致的性能和一致性。绝大部分元数据操作的响应工夫都在 1ms 以内,而 OSS 通常要几十到一百毫秒以上。上面是应用 NNBench 进行元数据压测的后果: 上图中的 rename 操作还只是针对单个文件的,因为它要拷贝数据所以很慢。在大数据理论的工作中通常是对目录做重命名,OSS 是 O(N) 复杂度,会随着目录里文件数量的增多显著变慢,而 JuiceFS 的 rename 的复杂度是 O(1) 的, 只是服务器端的一个原子操作,不论目录多大都能够始终这么快。 相似的还有 du 操作,它是要看一个目录里所有文件的总大小,在治理容量或者理解数据规模时十分有用。 下图是对一个 100GB 数据(蕴含3949个子目录和文件)的目录做 du 的工夫比照,JuiceFS 比 OSS 快 76倍!这是因为 JuiceFS 的 du 是基于服务器端内存中实时统计好的大小即时返回的,而 OSS 须要通过客户端遍历目录下的所有文件再累加求和,如果目录下的文件更多的话,性能差距会更大。 ...

January 26, 2022 · 2 min · jiezi

关于redis:redis高可用主从哨兵集群

https://xie.infoq.cn/article/...高可用指通过设计缩小零碎不能提供服务的工夫,是分布式架构设计必须要思考的因素。单点故障:只部署一个节点,机器故障,服务就会进行。redis 三种高可用模式 主从模式部署多台机器就要思考多台机器的数据同步问题。redis提供复制性能,一台redis数据库中的数据发生变化会主动同步到其余机器上。主节点读写、从节点读。默认配置的机器都是主节点,在redis.conf中配置后能够变成从节点。 # 配置主节点的ip和端口slaveof 192.168.1.10 6379# 从redis2.6开始,从节点默认是只读的slave-read-only yes# 假如主节点有登录明码,是123456masterauth 123456#在启动从节点时,指定主节点./redis-server --slaveof 192.168.1.10 6379主节点挂了,在从节点1上执行slaveof no one,将从节点变成主节点。在其余从节点上执行slaveof 从节点1。 主从复制的机制 从库连贯主库,发送sync主库收到sync,执行bgsava生成rdb文件,应用缓冲区记录增量写主库bgsave实现,向从库发送快照,持续记录增量写从库收到快照,从库载入快照。主库发送快照实现,开始发送缓冲区里的增量写从库载入快照实现,开始执行缓冲区的增量写。(从库初始化实现)主库每次执行写都向从库发送,从库会执行。呈现断开重连后,2.8后的版本会把断线期间的命令传给从库,增量复制。主从刚刚连贯时全量同步,全量完结后增量同步。如有须要随时能够发动全量同步。 长处反对主从复制,能够读写拆散slave能够承受slave的连贯和同步申请,分担master的同步压力。master以非阻塞的形式为slave提供服务,同步期间不影响读写。slave以阻塞的形式为slave提供服务,同步期间读会返回历史版本。? 毛病不能主动容错和复原,宕机会导致读写失败,须要手动切换ip主机宕机,没来得及同步到从机的数据会有不统一问题slave重启时会和主机同步,多个slave同时重启会导致masterIO剧增宕机集群容量达到下限时没法在线扩容?数据冗余,耗费内存。哨兵模式哨兵是个独立过程,非数据节点,能够和redis机器部署在一起或离开。哨兵向所有节点发送命令,期待响应,从而监控,像zookeeper的性能。为了便于选举应用奇数个哨兵,哨兵间也会互相通信。哨兵检测到master宕机,会主动把slave切换到master并通过公布订阅模式告诉其余从服务器换master。 工作形式每个哨兵每秒一次向所有节点发送ping命令(蕴含其余哨兵)如果一个实例响应工夫超过down-after-milliseconds,则被标记为主观下线。其余哨兵每秒一次确认此master确实进入主观下线。(sdown)足够数量哨兵认定主观下线后,哨兵会进行投票,执行failover,公布订阅告诉其余节点,此master被标记为主观下线。(odown)失常状况下哨兵每10s向所有主从服务器发送info命令。master主观下线时改成1s一次info。集群模式没有应用一致性hash算法而是应用slots插槽。对数据进行分片,每个节点上贮存不同内容。节点间通过集群总线通信。客户端能够连贯任何一个node操作,当key不在该node上时会指向正确的node。去中心化。集群部署须要至多3个master,最好3主3从。每个key处在哪个节点上是依据key的hash对16383取余调配的。半数以上主节点与master1失去通信,认定master1宕机,启用slave。slave也宕机,集群fail,因为失落了一部分数据。如果半数以上master宕机,不论是否有slave,集群都fail。扩容:先加节点再重调配插槽、数据迁徙。缩容:先重调配slot再移除节点。 长处可扩大,高可用。 毛病节点依附goss协定实现协同,节点太多通信老本高,占用网络资源。扩缩容只能手动。数据迁徙时会阻塞。

January 23, 2022 · 1 min · jiezi

关于redis:Redis-系列redis-学习四set-集合hash-哈希zset-有序集合初步认知

set 汇合 set 汇合外面的数据是不能重读的 SADD key member [member ...]向 set 汇合中增加元素 SMEMBERS key查看汇合中的所有元素 SISMEMBER key member查看某一个数据是否在汇合中 SCARD key查看汇合数据的个数,也就是汇合的长度 SREM key member [member ...]移除汇合中指定的元素 127.0.0.1:6379> sadd myset "hello"(integer) 1127.0.0.1:6379> sadd myset "wolrd" "xiaomotong"(integer) 2127.0.0.1:6379> SMEMBERS myset1) "wolrd"2) "xiaomotong"3) "hello"127.0.0.1:6379> SISMEMBER myset hello(integer) 1127.0.0.1:6379> SCARD myset(integer) 3127.0.0.1:6379> SREM myset wolrd(integer) 1127.0.0.1:6379> SMEMBERS myset1) "xiaomotong"2) "hello"SRANDMEMBER key [count]随机获取汇合外面的数据,能够指定个数 127.0.0.1:6379> sadd myset "v1" "v2" "v3" "v4"(integer) 4127.0.0.1:6379> SMEMBERS myset1) "v4"2) "xiaomotong"3) "v1"4) "v2"5) "hello"6) "v3"127.0.0.1:6379> SRANDMEMBER myset"v2"127.0.0.1:6379> SRANDMEMBER myset"xiaomotong"127.0.0.1:6379> SRANDMEMBER myset 21) "v4"2) "v1"127.0.0.1:6379>127.0.0.1:6379> SRANDMEMBER myset 21) "xiaomotong"2) "v1"SPOP key [count]随机删除汇合中的任意元素 ...

January 21, 2022 · 3 min · jiezi

关于redis:深入理解redis五种经典数据类型介绍及运用

1.String类型及其利用2.hash类型及其利用3.list类型及其利用4.set类型及其利用5.Zset类型及其利用6.总结 1.String类型及其利用String类型是咱们最罕用的类型,有些人甚至只会用这一种类型。 //最罕用的api//单个值操作set key valueget key//同时设置/获取多个键值MSET key value [key value ....]MGET key [key ....]//数值增减//递增数字INCR key//减少指定的数字INCRBY key increment//递加数字DECR key//缩小指定的整数DECRBY key decrement//获取字符串长度STRLEN key//分布式锁set key value [EX seconds] [PX milliseconds] [NX|XX]应用场景:1)保留个别的json对象,能够用这个类型(比方从表里查出来的User信息)。2)比方保留浏览量,点赞数等等,能够应用自增的性能(不必思考线程安全性)。3)分布式锁,就是一个有工夫限度的key和value值,能够保障程序执行的串行化,是一种跨JVM的锁,后续会有文章进行零碎介绍。4)token等信息。 2.hash类型及其利用hash类型转化成咱们的JAVA数据结构,能够用Map<String,Map<Object,Object>>来形容。就是一个key,它的value是由多个key和value组成 //最罕用的api //一次设置一个字段值HSET key field value//一次获取一个字段值HGET key field//一次设置多个字段值HMSET key field value [field value ...]//一次获取多个字段值HMGET key field [field ....]//获取字段所有值hgetall key//获取某个key内的全副数量hlen//删除hdel假如咱们当初要保留一个用户信息,最后咱们能够用json的模式来保留:然而这样做之后咱们发现,可能咱们只须要用到用户的一个或者两个字段的数据,然而这样却每次都要对用户的所有信息进行序列/反序列化,减少了服务器的开销,有没有一个方法咱们可能能够准确取出某一个咱们须要的属性呢?这个时候就能够用hash: 这样咱们就能够准确取得用户信息的字段值了。 3.list类型及其利用list在java中咱们肯定罕用,然而在redis中,list是一个双端链表的构造,容量是2的32次方减1个元素,大略40多亿,次要性能有push/pop等。罕用api: //向列表右边增加元素LPUSH key value [value ...]//向列表左边增加元素RPUSH key value [value ....]//查看列表LRANGE key start stop//获取列表中元素的个数LLEN key利用场景:1)微信公众号订阅的音讯只有我关注的作者公布了新文章,就会播送到我的list外面,查看的文章的时候,一次显示10条,相似于分页:lrange likearticle:UserId 0 9 不过数据个别不会保留太远古的数据,可能就保留两千条左右,再长远的数据就要去数据库里查问。 2)商品评论列表针对一些商品公布的评论,能够放在一个list外面,而后工夫按降序排列。 lpush items:comment:1001 {"id":1001,"name":"huawei","date":1600484283054,"content":"lasjfdljsa;fdlkajsd;lfjsa;ljf;lasjf;lasjfdlsad"}3)保留分页信息因为lrange命令就像limit一样,所以用这个命令,能够反对分页查问,先把数据筹备好放入缓存中,再进行分页。 ...

January 21, 2022 · 1 min · jiezi

关于redis:Redis架构原理及应用实践

一.Redis简介Redis 是齐全开源收费的,是一个高性能的key-value类型的内存数据库。整个数据库通通加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保留。因为是纯内存操作,Redis的性能十分杰出,每秒能够解决超过 10万次读写操作,是已知性能最快的Key-Value DB。 Redis的杰出之处不仅仅是性能,Redis最大的魅力是反对保留多种数据结构,此外单个value的最大限度是1GB,因而Redis能够用来实现很多有用的性能,比方说用List来做FIFO双向链表,实现一个轻量级的高性 能音讯队列服务,用他的Set能够做高性能的tag零碎等等。另外Redis也能够对存入的Key-Value设置expire工夫。总结来说,应用Redis的益处如下: 1.速度快,因为数据存在内存中,读的速度是 110000 次 /s, 写的速度是 81000 次 /s; 2.反对丰盛数据类型,反对string,list,set,sorted set,hash; 3.反对事务,操作都是原子性,对数据的更改要么全副执行,要么全副不执行,事务中任意命令执行失败,其余命令仍然被执行。也就是说 Redis 事务不保障原子性,也不反对回滚;事务中的多条命令被一次性发送给服务器,服务器在执行命令期间,不会去执行其余客户端的命令申请。 4.丰盛的个性:可用于缓存,音讯(反对 publish/subscribe 告诉),按key设置过期工夫,过期后将会主动删除,具体淘汰策略有: 4.1.volatile-lru:从曾经设置过期工夫的数据集中,筛选最近起码应用的数据淘汰 4.2.volatile-ttl:从曾经设置过期工夫的数据集中,筛选行将要过期的数据淘汰 4.3.volatile-random:从曾经设置过期工夫的数据集中,随机筛选数据淘汰 4.4.allkeys-lru:从所有的数据集中,筛选最近起码应用的数据淘汰 4.5.allkeys-random:从所有的数据集中,随机筛选数据淘汰 4.6.no-enviction:禁止淘汰数据 具体过期键的策略有:定时删除(缓存过期工夫到就删除,创立timer耗CPU),惰性删除(获取的时候查看,不获取始终留在内存,对内存不敌对),定期删除(CPU和内存的折中计划) 5.反对数据长久化,能够将内存中的数据保留在磁盘中,重启的时候能够再次加载进行应用; 6.反对数据的备份,即 master - slave 模式的数据备份。 Redis的次要毛病是数据库容量受到物理内存的限度,不能用作海量数据的高性能读写,因而Redis适宜的场景次要局限在较小数据量的高性能操作和运算上。 二.Redis的数据类型Redis 反对 5 中数据类型:string(字符串),hash(哈希),list(列表),set(汇合),zset(sorted set:有序汇合)。每种数据类型的具体命令请参考Redis 命令参考 stringstring 是 redis 最根本的数据类型。一个 key 对应一个 value。string 是二进制平安的。也就是说 redis 的 string 能够蕴含任何数据。比方 jpg 图片或者序列化的对象。string 类型是 redis 最根本的数据类型,string 类型的值最大能存储 512 MB。 hashRedis hash 是一个键值对(key - value)汇合。Redis hash 是一个 string 类型的 key 和 value 的映射表,hash 特地适宜用于存储对象。并且能够像数据库中一样只对某一项属性值进行存储、读取、批改等操作。 ...

January 20, 2022 · 2 min · jiezi

关于redis:Redis真的又小又快又持久吗

不苟言笑面试官:小伙子,谈谈对Redis的认识。我:啊,认识呀,坐着看还是躺着看。Redis很小?很快?但很长久? 面试官:不苟言笑的说,我狐疑你在开车,不仅开开车还搞色彩。我:。。。 面试官:去去去,我工夫无限,别瞎扯淡。回到正题,你对Redis理解有多少。我:轻量体积小、基于内存十分快、RDB配合AOF长久化让其一样坚挺长久。 面试官:说点具体的。我:请看注释。 注释简介Redis是一个开源的、高性能的、基于键值对的缓存与存储系统,通过提供多种键值数据类型来适应不同场景下的缓存与存储需要。与此同时,Redis的诸多高层级性能让其能够胜任音讯队列、工作队列等不同的角色。除此之外,Redis还反对内部模块扩大,在某些特定的场景下能够作为主数据库应用。 因为内存的读写速度远快于硬盘,就算当初的固态盘思维预计也是朝着内存那个思维模式倒退的,大略兴许我是个在行,然而短暂存储还是应用机械盘。所以Redis数据库中的所有数据都存储在内存中那是相当快的。也有肯定的危险,会导致失落数据,但配合RDB以及AOF长久化会缩小危险。 一、初识Redis1、linux下装置(Redhat7系列)1.1、装置此处筹备的是源码包,版本不在于最新,在于稳固实用。 其余版本在官网获取,或者在其托管的平台github上获取,如下为Redis的官网下载地址。 https://redis.io/download redis-6.0.8.tar.gz#装置tar -zxvf redis-6.0.8.tar.gz#编译make && make install1.2、排查谬误make[1]: *** [server.o] 谬误 1 1.3、解决方案1.3.1、装置依赖环境 yum -y install centos-release-sclyum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils1.3.2、加环境变量并失效 scl enable devtoolset-9 bashecho "/opt/rh/devtoolset-9/enable" >> /etc/profile 从新读取环境变量配置文件 source /etc/profile从新编译解决问题 #切换到Redis的装置目录,个别源码包装置会放在/usr/local/上面,看集体应用习惯cd /opt/redis-6.0.8/#编译make && make install罕用根本命令练习能够参考菜鸟教程 https://www.runoob.com/redis/redis-commands.html 1.4、启动与登录启动redis-server服务端 #启动redis服务nohup /opt/redis-6.0.8/src/redis-server & 登录redis-cli客户端 #登录redis-cli/opt/redis-6.0.8/src/redis-cli测试验证,此时linux下的redis正式启动胜利,上面会带来根本用法介绍。 pingpong 1.5、设置明码默认是没有凋谢明码设置的,须要手动开启正文掉的参数配置。 #编辑配置文件vim /opt/redis-6.0.8/redis.conf#本来的被正文掉,复制一行改成你设置的明码即可#requirepass foobaredrequirepass 1234562、Windows下装置2.1、装置Redis-x64-3.2.100.zip2.1.1、Windows下解压或者msi间接装置即可。 2.1.2、设置服务命令(注册为服务模式,自启)装置服务 redis-server --service-install redis.windows-service.conf --loglevel verbose卸载服务 redis-server --service-uninstall2.2、启动与敞开redis-server redis.windows.conf2.2.1、开启服务 redis-server --service-start2.2.2、进行服务 ...

January 19, 2022 · 2 min · jiezi

关于redis:Redis-系列redis-学习三redis-数据结构之-string-和-list-基本使用及熟悉

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 key 的根本命令ping查看客户端是否连贯胜利 set key value设置 key 和 value get key获取 key 的值 keys *获取所有 key move key 1删除 key expire key number对 key 设置过期工夫 ttl key查看 key 的残余无效工夫 type key查看 key 的类型 EXISTS key查看是否存在 key root@iZuf66y3tuzn4wp3h02t7pZ:/# redis-cli -p 6379127.0.0.1:6379> pingPONG127.0.0.1:6379> keys *(empty array)127.0.0.1:6379> set name xiaomotongOK127.0.0.1:6379> get name"xiaomotong"127.0.0.1:6379> type namestring127.0.0.1:6379> EXISTS name(integer) 1127.0.0.1:6379> EXISTS xiaozhu(integer) 0127.0.0.1:6379> set age 18OK127.0.0.1:6379> keys *1) "name"2) "age"127.0.0.1:6379> set hobby sportsOK127.0.0.1:6379> EXPIRE hobby 20(integer) 1127.0.0.1:6379> ttl hobby(integer) 14127.0.0.1:6379> ttl hobby(integer) 13127.0.0.1:6379> get hobby(nil)127.0.0.1:6379> move name 1(integer) 1127.0.0.1:6379> keys *1) "age"127.0.0.1:6379>string 字符串设置单个值 ...

January 16, 2022 · 4 min · jiezi

关于redis:树莓派-redis-压力测试-QPS-和-TPS

应用一个树莓派 4B 4GB 版本应用 redis-benchmark 作为压力测试工具 用法:间接在中终端输出该命令 redis-benchmark局部后果: ╭─ubuntu@RaspberryPi ~╰─➤ redis-benchmark====== PING_INLINE ====== 100000 requests completed in 6.72 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds93.56% <= 2 milliseconds99.98% <= 3 milliseconds100.00% <= 3 milliseconds14872.10 requests per second====== PING_BULK ====== 100000 requests completed in 6.82 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds93.04% <= 2 milliseconds99.99% <= 3 milliseconds100.00% <= 4 milliseconds14652.01 requests per second====== SET ====== 100000 requests completed in 6.99 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds88.83% <= 2 milliseconds99.75% <= 3 milliseconds99.85% <= 4 milliseconds99.95% <= 5 milliseconds99.98% <= 6 milliseconds99.99% <= 7 milliseconds100.00% <= 7 milliseconds14300.01 requests per second====== GET ====== 100000 requests completed in 6.75 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds93.66% <= 2 milliseconds100.00% <= 2 milliseconds14806.04 requests per second====== INCR ====== 100000 requests completed in 6.73 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds94.82% <= 2 milliseconds99.98% <= 3 milliseconds100.00% <= 3 milliseconds14858.84 requests per second====== LPUSH ====== 100000 requests completed in 6.70 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds94.51% <= 2 milliseconds100.00% <= 3 milliseconds100.00% <= 3 milliseconds14923.15 requests per second====== RPUSH ====== 100000 requests completed in 6.71 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds94.91% <= 2 milliseconds99.97% <= 3 milliseconds100.00% <= 3 milliseconds14909.80 requests per second====== LPOP ====== 100000 requests completed in 6.69 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds95.04% <= 2 milliseconds100.00% <= 2 milliseconds14938.75 requests per second====== RPOP ====== 100000 requests completed in 6.71 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds94.07% <= 2 milliseconds99.94% <= 3 milliseconds99.97% <= 4 milliseconds99.98% <= 5 milliseconds99.99% <= 6 milliseconds100.00% <= 7 milliseconds14903.13 requests per second====== SADD ====== 100000 requests completed in 6.70 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds95.02% <= 2 milliseconds100.00% <= 3 milliseconds100.00% <= 3 milliseconds14914.24 requests per second====== HSET ====== 100000 requests completed in 6.73 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds92.32% <= 2 milliseconds99.97% <= 5 milliseconds100.00% <= 6 milliseconds14865.47 requests per second====== SPOP ====== 100000 requests completed in 6.73 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds95.41% <= 2 milliseconds99.98% <= 3 milliseconds100.00% <= 3 milliseconds14861.05 requests per second====== LPUSH (needed to benchmark LRANGE) ====== 100000 requests completed in 6.66 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds96.09% <= 2 milliseconds99.98% <= 3 milliseconds100.00% <= 3 milliseconds15008.25 requests per second====== LRANGE_100 (first 100 elements) ====== 100000 requests completed in 8.77 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds0.01% <= 2 milliseconds99.96% <= 3 milliseconds99.99% <= 4 milliseconds100.00% <= 4 milliseconds11401.21 requests per second====== LRANGE_300 (first 300 elements) ====== 100000 requests completed in 15.59 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds0.00% <= 2 milliseconds0.01% <= 3 milliseconds76.04% <= 4 milliseconds99.65% <= 5 milliseconds99.92% <= 6 milliseconds99.96% <= 7 milliseconds99.99% <= 8 milliseconds100.00% <= 9 milliseconds6413.55 requests per second====== LRANGE_500 (first 450 elements) ====== 100000 requests completed in 19.95 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 2 milliseconds0.01% <= 3 milliseconds0.01% <= 4 milliseconds62.01% <= 5 milliseconds99.31% <= 6 milliseconds99.80% <= 7 milliseconds99.98% <= 8 milliseconds99.99% <= 9 milliseconds100.00% <= 10 milliseconds100.00% <= 12 milliseconds100.00% <= 12 milliseconds5011.53 requests per second====== LRANGE_600 (first 600 elements) ====== 100000 requests completed in 24.36 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 2 milliseconds0.00% <= 3 milliseconds0.01% <= 4 milliseconds0.02% <= 5 milliseconds38.54% <= 6 milliseconds99.88% <= 7 milliseconds99.96% <= 8 milliseconds99.97% <= 9 milliseconds99.98% <= 10 milliseconds99.99% <= 11 milliseconds100.00% <= 12 milliseconds100.00% <= 13 milliseconds4104.92 requests per second====== MSET (10 keys) ====== 100000 requests completed in 6.67 seconds 50 parallel clients 3 bytes payload keep alive: 10.00% <= 1 milliseconds95.90% <= 2 milliseconds99.97% <= 3 milliseconds100.00% <= 3 milliseconds14994.75 requests per second╭─ubuntu@RaspberryPi ~╰─➤能够看到 QPS 和 TPS 在 4000-16000之间 ...

January 15, 2022 · 21 min · jiezi

关于redis:Redis-系列redis-学习二

Redis 是什么?Redis(Remote Dictionary Service),近程字典服务 是一个开源的应用ANSI C语言编写、反对网络、可基于内存亦可长久化的日志型、Key-Value数据库,并提供多种语言的API Redis 是收费且开源的,是当下最热门的 Nosql 技术之一,他也被成为结构化数据库 Redis 反对的语言有这么多 Redis 会周期性的把更新的数据写入磁盘或者把批改操作写入追加的记录文件,并在此基础上实现 master - slave (主从同步) Redis 能干嘛?Redis 无能的事件十分的多,咱们列举一些: 内存存储,长久化。 数据存储在内存中,若服务解体或者服务器宕机,内存中的数据就会失落, 长久化相当重要,Redis 外面的长久化有 RDB,AOF能够用于高速缓存,Redis 效率很高做公布订阅零碎做地图信息剖析做计数器,计时器 等等Redis 有啥个性多样的数据类型长久化集群事务处理学习 Redis 须要用到的材料官网:https://redis.io/中文网站:http://www.redis.cn/下载地址: 如何装置 Rediswindow 下装置 Redis 1、官网上下载 windows Redis 的安装包:https://github.com/tporadowsk... Redis-x64-5.0.10.zip 2、解压安装包 解压 zip 安装包 Redis-x64-5.0.10.zip,这个压缩包当初有 14 M了 redis-serverredis 服务端 redis-clientredis 客户端 redis-check-aof 和 redis-check-rdbredis 的长久化工具 redis-benchmarkredis 的检测工具 3、关上 redis-server 进入服务端,能够看到如下界面 4、关上客户端 redis-cli ,能够尝试输出 ping命令,失去后果为 PONG 阐明连贯胜利 ...

January 12, 2022 · 2 min · jiezi

关于redis:大数据分析中Redis怎么做到220万ops

大数据时代,海量数据分析就像吃饭一样,成为了咱们每天的工作。为了更好的为公司提供经营决策,各种抖伶俐甚至胡思乱想的想法都会紧跟着接踵而来!业务多变,决定了必须每天批改零碎,从新跑数据,这就要求极高的海量数据读取和存储速度! 公司每天减少几亿行的业务日志数据,咱们须要从中剖析出各种维度的业务画像。通过很长时间的摸索,抉择了Redis作为读写数据的缓存。 1,开发平台,C#Net,写Windows服务抓取原始日志数据,合并精简压缩后,写入Redis集群。 2,各业务零碎从工夫维度上遍历Redis缓存数据,逐行剖析解决,两头后果和最终后果写入Redis。 3,另一套Windows服务抓取Redis里的后果数据,保留回数据库。这里有点像MQ的工作形式。 实际上,第一步只有一套零碎,这是数据根底。第二第三个别每个子系统都有一对。甚至A零碎的后果间接拜访B零碎放在Redis中的后果数据。 整体上看起来耦合度有点高,然而这一套架构失去了极高的速度,单个子系统实例每秒钟可解决1万到10万个订单!并且是很多套子零碎同时工作,繁多子系统因业务起因不会吃齐全部Redis性能。独自对某一台Redis服务器做压力测试,最高失去了222万ops的速度,测试的是比较简单的业务,统计满足某种业务规定的订单总数。 为何须要这么高速度?? 业务规定一旦扭转,批改程序后,往往须要从新跑最近一周什么一个月的历史数据。如果每天改几次呢?如果赶上双十一淡季,太慢的速度恐怕连实时数据都赶不上。 Redis怎么做到220万ops 1,Redis是单线程模型,因而32外围服务器装置32个实例 2,数据分片,key散列后均分到几十个实例上 3,敞开长久化,运维和Linux保障可靠性 4,管制好数据包大小,高性能网络通信最忌收发大量小包,管制在1400字节左近最佳,最差也要pipeline 5,其它在网上能轻易找到的细小技巧 为什么不必数据库?? 通过大量验证,同样32外围服务器,数据库3巨头个别失去20000qps的查问速度和靠近10000tps的写入速度。这是依照单表几百万数据有两个索引的状况测试。如果数据达到几千万上亿,再多两个索引,读写同时进行,那么速度只剩下四分之一不到。真真一个惨字! 大数据分析,有很多是长期数据,须要合并、叠加、去重等等,它们的生命周期不长,个别24小时或48小时,也有不少是两三个小时,要害是数据量还特地大,每天几千万很常见。这类数据,写数据库是很不适合的。 而应用Redis,一台32U512G机器,能够装下一个月几十亿通过压缩解决的历史数据,资源占用在50%高低。 我是大石头,打1999年起,18年轻码农。目前在物流行业从事数据分析架构工作。欢送大家一起C#大数据

January 10, 2022 · 1 min · jiezi

关于redis:这-4-种-Redis-常用运维工具都不会你算啥运维人

咱们在利用 Redis 时,常常会面临的运维工作,包含 Redis 的运行状态监控 ,数据迁徙 ,主从集群 、切片集群的部署和运维。接下来,我就从这三个方面,给你介绍一些工具。 咱们先来学习下监控 Redis 实时运行状态的工具,这些工具都用到了 Redis 提供的一个监控命令:INFO 。 最根本的监控命令:INFO 命令Redis 自身提供的 INFO 命令会返回丰盛的实例运行监控信息,这个命令是 Redis 监控工具的根底。 INFO 命令在应用时,能够带一个参数 section,这个参数的取值有好几种,相应的,INFO 命令也会返回不同类型的监控信息。我把 INFO 命令的返回信息分成 5 大类,其中,有的类别当中又蕴含了不同的监控内容,如下表所示: 在监控 Redis 运行状态时,INFO 命令返回的后果十分有用。如果你想理解 INFO 命令的所有参数返回后果的具体含意,能够查看 Redis官网的介绍。 这里,我给你提几个运维时须要重点关注的参数以及它们的重要返回后果。 首先,无论你是运行单实例或是集群,我倡议你重点关注一下stat 、commandstat 、cpu 和 memory 这四个参数的返回后果,这外面蕴含了命令的执行状况(比方命令的执行次数和执行工夫、命令应用的 CPU 资源),内存资源的应用状况(比方内存已使用量、内存碎片率),CPU 资源应用状况等,这能够帮忙咱们判断实例的运行状态和资源耗费状况。 另外,当你启用 RDB 或 AOF 性能时,你就须要重点关注下 persistence 参数的返回后果,你能够通过它查看到 RDB 或者 AOF 的执行状况。 如果你在应用主从集群,就要重点关注下 replication 参数的返回后果,这外面蕴含了主从同步的实时状态。 不过,INFO 命令只是提供了文本模式的监控后果,并没有可视化,所以,在理论利用中,咱们还能够应用一些第三方开源工具,将 INFO 命令的返回后果可视化。接下来,我要讲的 Prometheus ,就能够通过插件将 Redis 的统计后果可视化。Redis性能指标监控!你知几何? ...

January 10, 2022 · 2 min · jiezi

关于redis:Redis-系列redis-学习一数据库的演进及-Nosql-的初步认知

Nosql 为什么要用 Nosql ?咱们一起来看看数据库的倒退过程 1、单机的 MYSQL 时代 利用拜访数据库是 利用 – DAL 数据库拜访层 – DB 数据库 在单机的 MYSQL 时代,数据都不会太大,而且网页也是动态网页,个别网站的访问量也小,因而单数据库就齐全够用了 下面这种网站,瓶颈就会很显著: 数据量变得大了,一个机器放不下如何解决数据的索引 (B+ Tree),一个机器的内存放不下了如何解决读写的访问量,一个服务器接受不了了如何解决随着历史进程的演进,下面这种网站必然会面临如上问题,就必须要解决 2、Memecache + MYSQL + 垂直拆分 为了解决下面的问题,一个机器解决不过去,咱们就放多个机器,然而如何保证数据的一致性呢? 因而就想到了读写拆散,专门指定一个数据库用于写数据,其余数据库用于读取数据,并且其余的数据库会同步用于写数据的 MYSQL 中的数据 慢慢的发现每次申请,都须要去操作数据库,这样耗时耗力,须要扭转 因而在拜访数据库之前,退出了缓存服务器 Memcache,第一次读取数据库,第二次读取的时候在数据库不扭转的状况下,读取 Memcache 中的数据,减小数据库的压力 这一块倒退历程是这样的: 优化数据库的构造和索引 – 文件缓存(IO) – 缓存服务器 Memcache 3、分库分表 + 程度拆分 + MYSQL 集群 随着技术倒退,业务也跟着飞越倒退,也就带来了更多的问题 最开始咱们应用的 MYSQL 应用的引擎的是 MyISAM,他是表锁,非常影响效率,当在高并发的状况下,问题尤为显著 前面就有了 MYSQL 的 InnoDB 引擎,他是行锁,随着业务的不停增长,就有了 MYSQL 集群 应用集群的形式,每一个集群存储一部分数据,若数据量依然回升,那么持续减少集群部署 4、当初 因为古代的数据量真的十分大了,大数据时代了,MYSQL 关系型数据库就不够用了,当初数据量多,变动快,以前应用 MYSQL ,设计的时候,就得把所有字段,可能用到的字段,全副想分明,设计明确,对于当初瞬息万变的时代,这就很难了 ...

January 8, 2022 · 1 min · jiezi

关于redis:Redis5源码阅读笔记事件驱动框架及运行流程

源码版本5.0.8 大抵流程图深蓝示意main函数执行流程浅蓝示意IO多路复用调用地位绿色示意Reactor模型中的acceptor和handler地位紫色示意事件调用解决地位灰色示意外部执行流程

December 31, 2021 · 1 min · jiezi

关于redis:网易有道-REDIS-云原生实战

REDIS 云原生实战 摘要本次以Redis为范例,论述了有道基础架构团队在基础设施容器化路线上的实际,次要将从申明式治理,Operator工作原理,容器编排,主从模式,集群模式,高可用策略,集群扩缩容等方面开展。 目录 背景面临的挑战申明式治理Operator工作原理 容器编排主从模式• 主从拓扑图 • 和谐原理 集群模式• 集群拓扑图 • 和谐原理高可用策略 • Kubernetes保障的高可用 • Redis集群的高可用监控观测集群扩缩容总结与瞻望背景Redis 是业务零碎中较为罕用的缓存服务,罕用于流量顶峰、数据分析、积分排序等场景,并且通过中间件能够实现零碎之间的解耦,晋升零碎的可扩展性。 传统物理机部署中间件,须要运维人员手动搭建,启动工夫较长,也不利于前期保护,无奈满足业务疾速倒退的需要。 云原生相较于传统IT,能够助力业务平滑迁徙、疾速开发、稳固运维,大幅升高技术老本,节约硬件资源。 云原生中间件是指依靠容器化、服务网格、微服务、Serverless等技术,构建可扩大的基础设施,继续交付用于生产零碎的根底软件,在性能不变的前提下,进步了利用的可用性与稳定性。 在这种大趋势下,有道基础架构团队开始了云原生中间件的实际,除了本文介绍的 Redis,还包含 Elasticsearch、ZooKeeper 等。 面临的挑战利用云原生技术能够解决以后Redis部署迟缓,资源利用率低等问题,同时容器化 Redis 集群也面临着一些挑战: • Kubernetes 如何部署 Redis 有状态服务• 容器 Crash 后如何不影响服务可用性; • 容器重启后如何保障Redis 内存中的数据不丢; • 节点程度扩容时如何做到 slots 迁徙时不影响业务; • pod ip变动后集群的状态如何解决。申明式治理对于一个 Redis 集群,咱们的冀望是可能 7x24 小时无间断提供服务,遇故障可自行修复。这与Kubernetes API的申明式特点一模一样。 所谓“申明式”, 指的就是咱们只须要提交一个定义好的 API 对象来“申明”我所冀望的状态是什么样子,Kubernetes中的资源对象可在无外界烦扰的状况下,实现以后状态到冀望状态的转换,这个过程就是Reconcile过程。例如,咱们通过yaml创立了一个Deployment ,Kubernetes将“主动的”依据yaml中的配置,为其创立好Pod,并拉取指定存储卷进行挂载,以及其余一系列简单要求。 因而,咱们的Redis集群是否能够应用一个相似的服务去实现这个过程呢?即咱们须要定义这样的对象,定义服务Reconcile的过程。Kubernetes的Operator刚好能够满足这个需要,能够简略的了解Operator由资源定义和资源控制器形成,在充沛解读集群和Operator的关系后,咱们将整体架构图设计如下Operator集群自身采纳Deployment部署,由ETCD 实现选主,下层与Kubernetes的Api Server 、Controller Manager等组件进行通信,上层继续和谐Redis集群状态。 哨兵模式中Redis服务用一套哨兵集群,应用StatefulSet部署,长久化配置文件。Redis server也采纳 StatefulSet部署, 哨兵模式的实例为一主多从。 集群模式中的每个分片应用StatefulSet部署,代理采纳Deployment部署。原生Pod、StatefulSet、Service、调度策略等由Kubernetes自身负责。 Redis的资源定义在ETCD中存储一份即可,咱们只须要事后提交自定义资源的 yaml配置。如下所示为创立三个正本的Redis主从集群: ...

December 27, 2021 · 2 min · jiezi

关于redis:Redis数据结构底层原理

简略动静字符串(SDS)redis是C语言的程序,然而字符串并没有采纳C语言提供能的,本人写了一个字符串的构造。 redis中的字符串构造: struct sdshdr { // 记录buf数组中已应用字节的数量 // 等于SDS所保留字符串的长度 int len; // 记录buf数组中未应用字节的数量 int free; // 字节数组,用于保留字符串 char buf[];};并且redis中字符串同样采纳了C语言中的格局,就是最初一个应用空字符''/0''完结,这个过程对程序员是通明的,sds主动追加的。 为什么有现成的构造,还要自定义构造呢?起因有以下几点: SDS构造的长处取得字符串长度时,复杂度升高为O(1)C语言中的字符串获取长度的时候,须要遍历所有字符,晓得碰到'/0',过程中的个数是长度。复杂度为O(n),redis为了谋求疾速。优化了这一点,构造体中保留数据的长度,属性诶len。使得复杂度升高为O(1). 防止缓冲区溢出的危险c语言中的字符串赋值前,须要先分配内存空间。如果扭转字符串的长度,还须要思考内存空间是否够用,如果没有提前调配空间,就会呈现缓冲区溢出。为了防止这种状况,sds会先查看内存是否够用,不够用的状况下,会先分配内存空间,之后扭转字符串。这样就不会呈现缓冲区溢出。 缩小了内存重调配的次数c语言中的字符串扭转,追加字符换时,须要减少空间,截取字符串时,又要开释空间。内存中的重调配是一个耗时的操作。为了有话这一点。sds会在追加的时候,事后调配额定的空间,而在截取的时候,不会立刻开释内存。 空间预调配如果对SDS进行批改之后,SDS的长度(也即是len属性的值)将小于1MB,那么程序调配和len属性,同样大小的未应用空间,这时SDS len属性的值将和free属性的值雷同。举个例子,如果进行批改之后,SDS的len将变成13字节,那么程序也会调配13字节的未应用空间,SDS的buf数组的理论长度将变成13+13+1=27字节(额定的一字节用于保留空字符)。如果对SDS进行批改之后,SDS的长度将大于等于1MB,那么程序会调配1MB的未应用空间。举个例子,如果进行批改之后,SDS的len将变成30MB,那么程序会调配1MB的未应用空间,SDS的buf数组的理论长度将为30MB+1MB+1byte用过这种策略,不必每次追加时都要调配空间。 惰性空间开释截取字符串时,不会立刻开释空间,而是扭转属性free的值。 二进制数据安全c语言中的字符串是以‘/0’完结的。所以只有看到‘/0’,就认为该字符串曾经完结了,这样就无奈保留二进制数据。sds优化了这一点。 总结比起C字符串,SDS具备以下长处:1)常数复杂度获取字符串长度。2)杜绝缓冲区溢出。3)缩小批改字符串长度时所需的内存重调配次数。4)二进制平安。5)兼容局部C字符串函数(因为sds会主动追加一个‘/0’)。 链表链表的作用通常是装一组数据。C语言没有提供这种数据结构,redis自定义了该构造。 链表的构造应用ListNode节点承装数据 typedef struct listNode { // 前置节点 struct listNode * prev; // 后置节点 struct listNode * next; // 节点的值 void * value;}listNode;list作为工具类应用相似于JDK中的LinkedList类。redis中的链表是一个双向链表。 typedef struct list { // 表头节点 listNode * head; // 表尾节点 listNode * tail; // 链表所蕴含的节点数量 unsigned long len; // 节点值复制函数 void *(*dup)(void *ptr); // 节点值开释函数 void (*free)(void *ptr); // 节点值比照函数 int (*match)(void *ptr,void *key);} list;Redis的链表实现的个性能够总结如下: ...

December 24, 2021 · 3 min · jiezi

关于redis:Redis-分布式锁的正确实现原理演化历程与-Redission-实战总结

Redis 分布式锁应用 SET 指令就能够实现了么?在分布式畛域 CAP 实践始终存在。 分布式锁的门道可没那么简略,咱们在网上看到的分布式锁计划可能是有问题的。 「码哥」一步步带你深刻分布式锁是如何一步步欠缺,在高并发生产环境中如何正确应用分布式锁。 在进入注释之前,咱们先带着问题去思考: 什么时候须要分布式锁?加、解锁的代码地位有考究么?如何避免出现锁再也无奈删除?「」超时工夫设置多少适合呢?如何防止锁被其余线程开释如何实现重入锁?主从架构会带来什么平安问题?什么是 RedlockRedisson 分布式锁最佳实战看门狗实现原理……什么时候用分布式锁?码哥,说个艰深的例子解说下什么时候须要分布式锁呢?诊所只有一个医生,很多患者前来就诊。 医生在同一时刻只能给一个患者提供就诊服务。如果不是这样的话,就会呈现医生在就诊肾亏的「肖菜鸡」筹备开药时候患者切换成了脚臭的「谢霸哥」,这时候药就被谢霸哥取走了。 治肾亏的药被有脚臭的拿去了。 当并发去读写一个【共享资源】的时候,咱们为了保证数据的正确,须要管制同一时刻只有一个线程拜访。 分布式锁就是用来管制同一时刻,只有一个 JVM 过程中的一个线程能够拜访被爱护的资源。 分布式锁入门65 哥:分布式锁应该满足哪些个性?互斥:在任何给定时刻,只有一个客户端能够持有锁;无死锁:任何时刻都有可能取得锁,即便获取锁的客户端解体;容错:只有大多数 Redis的节点都曾经启动,客户端就能够获取和开释锁。码哥,我能够应用 SETNX key value 命令是实现「互斥」个性。这个命令来自于SET if Not eXists的缩写,意思是:如果 key 不存在,则设置 value 给这个key,否则啥都不做。Redis 官网地址说的: 命令的返回值: 1:设置胜利;0:key 没有设置胜利。如下场景: 敲代码一天累了,想去放松按摩下肩颈。 168 号技师最热门,大家喜爱点,所以并发量大,须要分布式锁管制。 同一时刻只容许一个「客户」预约 168 技师。 肖菜鸡申请 168 技师胜利: > SETNX lock:168 1(integer) 1 # 获取 168 技师胜利谢霸哥前面到,申请失败: > SETNX lock 2(integer) 0 # 客户谢霸哥 2 获取失败此刻,申请胜利的客户就能够享受 168 技师的肩颈放松服务「共享资源」。 享受完结后,要及时开释锁,给后来者享受 168 技师的服务机会。 肖菜鸡,码哥考考你如何开释锁呢?很简略,应用 DEL 删除这个 key 就行。 ...

December 24, 2021 · 5 min · jiezi

关于redis:公司更喜欢年轻的程序员更有经验的程序员不比他们香

随着互联网的高速倒退,越来越多的互联网公司出现在公众背后。而在公司招聘员工方面,却存在一个普遍现象,那就是很多公司都比拟喜爱年老的程序员,而不是更有教训的程序员,这到底是为什么呢? 个别互联网公司抉择候选人都会鉴于有教训的程序员和刚从大学毕业的人之间的抉择,大多数公司每次都更喜爱有教训的程序员。然而鉴于以下几点,个别大公司就会对他们望而生畏。 首先有教训的程序员提出的薪资要求比拟低廉,一般来说,他们都曾经处于而立之年,大多数人都曾经成家,甚至曾经有了小孩。家庭的累赘不得不让他们在外要寻求更高的薪水。还有就是这些有教训的程序员个别在找工作时会比拟挑,再加上有敌人内推,个别手里都会拿好几个offer,在这种状况下,个别的互联网企业很难招到这些人。而且当初互联网企业大都有加班文化,而这些程序员都须要抽出一些工夫去陪家人,所以导致他们在抉择公司的时候会思考更多。另外有一些经验丰富的程序员曾经在互联网工作多年,但实际上并不“经验丰富”,因为每个人此前可能是做本人比拟善于的畛域,换了一个环境或者一个公司,之前积攒到的一些常识可能就不会用到。例如,公司在招聘时可能会遇到一个十分有教训的人,比方在他工作了15年的公司应用的一个十分非凡的COBOL堆栈,但如果他所做的就是在这15年中保护一个比拟老的零碎,那他可能不肯定能解决新公司须要解决的问题。 在互联网畛域,多年从事十分专业化问题的人经常陷入适度专业化的陷阱。他们习惯应用这一形式,然而却容易演变成公司的螺丝钉。这最终成为许多人招聘过程中比拟辣手的一个问题,再加上年龄的限度,导致在找工作的时候对他们没有过多的帮忙。 而当今的大学生思路也比拟凋谢,又因为企业中一直会用到新的技术。所以企业会更加看重一个候选人的学习能力和基础知识。 当代的大学生在校园也会参加一些我的项目的研发以及技能的造就,所以他们也绝对具备肯定的能力,在创意方面也会显得比工作多年的人不逊一些。而他们更心愿学习,心愿能够在企业中尝试新的事物并失去锤炼。 所以在当今竞争较为强烈的互联网行业中,招聘公司会依据他们的需要来正当的需要人才,而不单单看重应聘者的工作年限。 大学生作为当代倒退的生力军,进入企业既能够做基层工作,又能够当做企业的人才储备。而一些多年教训的程序员,执行力较大学生会绝对较差,学习激情也不是很强,所以大部分的互联网公司天然会比拟青眼一些大学生。 所以最初逼得程序员这个职业就只能疯狂跳槽,鉴于年后筹备跳槽的小伙伴很多,我最近也是在网上收集了许多的Java进阶材料,都是大厂面试的时候问的比拟多的知识点,比方上面这套阿里大牛自产的“Redis深度笔记”,起码是我目前看到过的最欠缺,最有深度的一份笔记了。 上面展现一部分截图,点击此处可收费支付! 对于一个开发者而言,则设计开发计划时,肯定要将计划思考周全。怎样才能将计划思考周全?唯有继续一直地学习! 笔记大略分为以下几个局部: 开篇根底局部九大利用局部八大原理局部三大集群局部九大拓展局部七大源码局部一、开篇根底局部开篇:授人以鱼不若授人以鱼-Redis能够用来做什么根底:万丈高楼平地起-Redis根底数据结构 二、九大利用局部千帆竞发-分布式锁金蝉脱壳-延时队列省吃俭用-位图四两拨千斤-HyperLogLog层峦叠嶂-布隆过滤器断尾求生-简略限流爱财如命-漏斗限流近水楼台-GeoHash海底捞针-Scan 三、八大原理局部鞭辟入里-线程IO模型窃窃私语-通信协议防患未然-长久化雷厉风行-管道风雨同舟-事务小道消息-PubSub开源节流-小对象压缩有恃无恐-主从同步 四、三大集群局部李代桃僵-Sentinel分而治之-Codis万众一心-Cluster 五、九大拓展局部耳听八方-Stream无所不知-Info指令拾脱漏补-再谈分布式锁朝生暮死-过期策略优胜劣汰-LRU平波缓进-懈怠删除妙手仁心-优雅地应用Jedis居安思危-爱护Redis隔墙有耳- Redis平安通信 六、七大源码局部极度深寒-摸索(字串符)内部结构极度深寒-摸索(字典)外部极度深寒-摸索(压缩列表)外部极度深寒-摸索(疾速列表)外部极度深寒-摸索(跳跃列表)内部结构极度深寒-摸索(紧凑列表)构造极度深寒-摸索(基数树)构造 Redis 波及的知识点是十分繁多的,这本笔记的作者曾经整顿的很全面了,但仍然无奈顾及到 Redis 的方方面面,不过曾经是能找到最棒的,点击此处可收费支付!

December 22, 2021 · 1 min · jiezi

关于redis:缓存淘汰之路上

Redis(Remote Dictionary Server ),即近程字典服务,是一个开源的应用ANSI C语言编写、反对网络、可基于内存亦可长久化的日志型、Key-Value数据库。是咱们研发人员的查问存储长期数据的利器,尽管它速度快,反对高并发,多种业务都会交融到缓存,但要用好它,可不是那么简略的事件。上面咱们就追随卢卡的视角来去寻找一下Redis内存淘汰背地的故事。背景第一次提到一个内存淘汰,是因为业务中很多过期数据,曾经将缓存的速度拖的很慢,而且始终在库中的数据留存上来,额,尽管说曾经没有实际意义了,然而至多证实它之前存在过,数据的心声,作为Redis的管理员conf大佬, 就给他们上课了,将数据整顿好,确定好标签,给他们找寻数据最初的归属。 数据听了登时留下了眼泪,踌躇满志的想要从新来过,等啊等啊。就是到了过期工夫, 它还是没有被0号库的皇帝召见,看来期待它的就只能默默的来到了。逐步地,数据感觉本人被冷清了,皇帝也不喜爱我了,说着就开始给淘汰机制的警察诉苦,过后是他给我标注的,说好会始终待我好,当初呢,就不爱了,渣男,呸。 作为淘汰警察的内存掌控者,每天这样的事件也曾经习惯了, 谁让是缓存呢,对吧,好的货色就是比拟罕见,人家就是有特权抉择你们这些单纯的数据,淘汰警察说的话一次次的牵动着数据的心, 看来只有哭诉,才能够让当前的生存成长起来。经验过缓存的抛弃,渣男的摈弃,数据在一次在淘汰警察的激励下,站了起来,我当前要跟你混。 淘汰警察急躁的告诫数据,当前的路要本人好好走,我这里可是很简单的,你有信念去走向远方吗? 数据,间接动摇的说了一句, YES ,SIR,(话说给淘汰警察整的不会了) 那你要是有趣味,就先留下来吧,说着,数据眼角留下了热泪,温顺的笑了起来。 迎接你新的生存吧,淘汰警察慢悠悠的说到! 备注:其实conf大佬是Redis的配置文件 数据缓存第一集(被缓存警察上课一天) 数据跟淘汰警察历险记 第二天,一大早数据就被,淘汰警察喊醒了,通知你明天第一个工作,获取最新的内存状况 也就是将你一样,被0号库皇帝治理的韭菜数据,他们要获取一下他们当初的总体状况,你先去交接一下 memory大哥,把这个info的令牌带着, 先去找你command妹妹,通知他要查一下 内存状况; 实例状况: 缓存Redis的内存状况,用命令 info memory,能够获取到; 看到最新的内存状况的,数据很是快乐, 刚要回家,就看到淘汰警察给的工作卡上还有工作, 既然有挑战,那我就要实现,数据曾经决定了,开始向着下个工作驶入; 工作一:缓存Redis内存如何配置,生产环境中个别配置多少? 数据一看,这个配置,只能去寻找conf大哥了, 屁颠屁颠的去找了conf大哥让他查查内存配置,正好看见数据是老手,就给他指明一条路; 如果默认没有配置Redis的内存,64位操作环境下,个别不限度内存大小; 然而conf大哥,仔细的通知数据, 想要配置内存,也是有技巧的; 数据认真的听了起来 生产环境缓存内存配置: 生产环境,公司个别会配置 以后主内存的四分之三,(Redis底层用hash算法,负载因子是0.75) 如何设置Redis的内存: 办法1:能够通过conf来设置 maxmemory <bytes>    单位是字节: 1024字节-1kb    1048576是1MB=1024*1024    比方:     maxmemory 1048576000   //代表1000MB 办法2:应用命令来配置 对于命令个别存储在config中   启动redis  , redis -cli  config get maxmemory config set maxmemory  104857600 工作二:当Redis的内存满了,会产生什么状况? 数据一看这个工作,感觉要起义的感觉,内存总有一天要被咱们数据占据,到时候看看鹿死谁手, 实例状况: 我将最大内存设置为1字节, 127.0.0.1:6379> config set maxmemory 1OK127.0.0.1:6379> set key1 hello(error) OOM command not allowed when used memory > 'maxmemory'. Redis内存已满 工作三:Redis数据满了之后,如何淘汰数据? 数据一看,这不就是我之后的生存吗,我得好好体现, 这个还得从conf大哥那边找找灵感,说着就跑去找conf大哥了, 默认的Redis conf文件中,对于淘汰数据; maxmemory-policy noeviction(不会驱赶数据的一种数据策略) 如果内存够,根本都能够保留下来; 面试题: 在内存中,以后一个键值对(key-value)是过期的,数据是否会立即被删除呢? set k1 hello ex 10   10秒生效 答案:当一个key过期,标识不可用之后,不会立即删除 因为会有过期键值对的策略在起作用 卢卡寄语 数据在淘汰警察的工作下,逐步理解了内存的细节状况, 对于内存中最大数据,配置,以及过期保留都有了肯定的理解,接下来会是对Redis,key值中过期策略的制订, 果然: 淘汰警察说,数据你还有很远的路要走。 ...

December 20, 2021 · 1 min · jiezi

关于redis:redis入门知识第4篇redis中的string数据类型与基本的数据存取操作

redis 最常利用于各种构造类型和非构造类型高热度数据的拜访减速。在本文,咱们将从 redis 中 string 数据类型开始理解 redis 对数据的存取操作。本文是该系列的第四篇原创笔记,如果你还没浏览之前的局部,能够通过以下链接进行浏览01-redis入门常识第1篇-redis简介02-redis入门常识第2篇-redis的装置与测试03-redis入门常识第3篇-redis的基本操作与数据类型1. string 类型的特色存储的数据:单个数据,最简略的数据类型,也是最罕用的存储类型存储数据的格局:一个存储空间保留一个数据存储空间:通常应用字符串,如果存储的字符是数值的模式,能够应用数值操作(比方减少指定值、缩小指定值)的性能2. string 类型的基本操作增加/批改数据set key value获取数据get key删除数据del key增加/批改多个数据(M 即 multiple)mset key1 value1 key2 value2 ...获取多个数据mget key1 key2 ...获取数据字符个数strlen key追加信息到原始数据开端(如原始存在则追加,否则新建)如果 key 曾经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的开端。 append key value3. 单数据操作 与 多数据操作的比照3.1. 指令设置单条数据:set key value设置多条数据:mset key1 value1 key2 value23.2. 操作时间差假如每执行一个动作须要一个工夫单位,那么执行一次命令,须要的工夫单位为:发送执行命令(往)的工夫 + 执行的工夫 + 返回后果(返)的工夫,一共须要 3 个工夫单位。 单数据操作,执行 3 条指令的执行过程:往返 6 个单位 + 执行 3 个多数据操作,执行 3 条指令的执行过程:往返 2 个单位 + 执行 3 个4. string数值类型数据的操作设置数值数据减少指定的值 ...

December 17, 2021 · 1 min · jiezi

关于redis:redis笔记第1篇redis简介

1. 概述服务器软件我的项目的瓶颈的个别因为海量用户和高并发引起,其中罪魁祸首是关系型数据库。起因是关系型数据库存在以下的毛病: 性能瓶颈:磁盘 IO 性能低下扩大瓶颈:数据关系简单,扩展性差,不便与大规模集群想要解决这个磁盘IO的瓶颈,于是产生一个解决思路:升高磁盘 IO,越低越好,即用内存存储。去除数据关系,越简略越好,不存储数据之间的关系,仅存数据。这就是咱们说的 Nosql2. 什么是NoSQL?2.1. 概念NoSQL: Not Only SQL(泛指非关系型数据库),作为关系型数据库的补充。NoSQL 的作用是:应答基于海量用户和海量数据的前提下的数据处理问题。其具备以下特点: 可扩容,可裁减大量数据下高性能灵便的数据模型高可用2.2. 常见的 NoSQLRedismemcacheHBaseMongoDB2.3. 电商解决办法(1)商品根本信息:名称、价格、厂商,这类结构化信息个别存在关系型数据库中,如MySQL (2)商品附加信息:形容、详情、评论,这类文档信息个别用 MongoDB (3)图片信息:动态文件用分布式文件系统 (4)搜寻关键字:ElasticSearch、Lucence、solr (5)热点信息(高频、波段性的信息):用高性能的内存数据库 redis、memcache、tair 3. Redis的概念以及特色3.1. 概念:Redis(Remote Dictionary Server)是用 C 语言开发的一个开源的高性能键值对(key-value)数据库。是目前最热门的用于数据缓存,以实现高并发的工具。 3.2. 特色:(1). 数据之间没有必然的关联关系 (2). 外部采纳单线程机制进行 (3). 高性能。官网测试数据,50 个并发执行 100000 个申请,读的速度是 110000 次/s,写的速度是 81000 次/s。 (4). 数据类型的反对 字符串类型 string列表类型 list散列类型 hash汇合类型 set有序汇合类型 sorted_set(5). 数据长久化。能够进行数据劫难复原 3.3. redis 的利用数据热点的减速查问(次要场景),如热点商品、热点新闻、热点征询、热点推广等高 访问量信息工作队列,如秒杀、抢购、购票排队及时信息查问,如各类排行、各类网站的拜访统计、公交到站信息、在线人数信息(聊天室、网站)、设施信号等实效性信息的管制,如验证码管制、投票管制分布式数据共享,如分布式集群架构中的 session 拆散音讯队列分布式锁4. 总计在这篇文章中,咱们理解了NoSQL的概念,同时介绍了Redis相干概念以及利用场景,在后续的文章中,咱们持续探讨Redis的常识。

December 12, 2021 · 1 min · jiezi

关于redis:redis-请求卡住排查思路

redis 卡住,导致的问题线上应用了redis 做为分布式锁,常常碰到续锁失败排查思路slowlog get 排查慢查问info间接查看日志: 文件句柄不够,导致命令aof 梗塞,从而命令卡住ulimit -a 查看 发现句柄不够ulimit 应用

December 10, 2021 · 1 min · jiezi

关于redis:Redis-超出内存大小报错

明天在测试环境冒烟,发现redis报错,JedisDataException: OOM command not allowed when used memory > 'maxmemory'!狐疑是写入redis key过大导致,于是排查redis内存应用状况,应用INFO命令,后果如下,能够看到最大内存为256M,理论应用曾经262M,故报错,因为测试服务器没有redis客户端,只能通过DEBUG OBJECT KEY进行排查,发现是在进行每日下单用户数量统计时,因为用户数量过多,存储的userId导致key过大,通过理论业务思考redis大小配置以及调整代码解决 {"redis_version": "5.0.5","redis_git_sha1": 0,"redis_git_dirty": 0,"redis_build_id": "4a12254f65de94f7","redis_mode": "standalone","os": "Linux 2.6.32-696.el6.x86_64 x86_64","arch_bits": 64,"multiplexing_api": "epoll","atomicvar_api": "atomic-builtin","gcc_version": "4.8.2","process_id": 29941,"run_id": "9ea1e2e825576ce1510b81ea726bcd3bd6b9806d","tcp_port": 6451,"uptime_in_seconds": 1644062,"uptime_in_days": 19,"hz": 10,"configured_hz": 10,"lru_clock": 11564232,"executable": "/usr/local/redis-5.0.5/bin/redis-server","config_file": "/data/redis6451/redis-6451.conf","connected_clients": 4,"client_recent_max_input_buffer": 2,"client_recent_max_output_buffer": 0,"blocked_clients": 0,"used_memory": 275653448,"used_memory_human": "262.88M","used_memory_rss": 249323520,"used_memory_rss_human": "237.77M","used_memory_peak": 275775880,"used_memory_peak_human": "263.00M","used_memory_peak_perc": "99.96%","used_memory_overhead": 896364,"used_memory_startup": 791392,"used_memory_dataset": 274757084,"used_memory_dataset_perc": "99.96%","allocator_allocated": 275688736,"allocator_active": 276066304,"allocator_resident": 281153536,"total_system_memory": 33669332992,"total_system_memory_human": "31.36G","used_memory_lua": 36864,"used_memory_lua_human": "36.00K","used_memory_scripts": 216,"used_memory_scripts_human": "216B","number_of_cached_scripts": 1,"maxmemory": 268435456,"maxmemory_human": "256.00M","maxmemory_policy": "volatile-lfu","allocator_frag_ratio": 1.0,"allocator_frag_bytes": 377568,"allocator_rss_ratio": 1.02,"allocator_rss_bytes": 5087232,"rss_overhead_ratio": 0.89,"rss_overhead_bytes": -31830016,"mem_fragmentation_ratio": 0.9,"mem_fragmentation_bytes": -26266784,"mem_not_counted_for_evict": 0,"mem_replication_backlog": 0,"mem_clients_slaves": 0,"mem_clients_normal": 100460,"mem_aof_buffer": 0,"mem_allocator": "jemalloc-5.1.0","active_defrag_running": 0,"lazyfree_pending_objects": 0,"loading": 0,"rdb_changes_since_last_save": 73047,"rdb_bgsave_in_progress": 0,"rdb_last_save_time": 1637310122,"rdb_last_bgsave_status": "ok","rdb_last_bgsave_time_sec": -1,"rdb_current_bgsave_time_sec": -1,"rdb_last_cow_size": 0,"aof_enabled": 0,"aof_rewrite_in_progress": 0,"aof_rewrite_scheduled": 0,"aof_last_rewrite_time_sec": -1,"aof_current_rewrite_time_sec": -1,"aof_last_bgrewrite_status": "ok","aof_last_write_status": "ok","aof_last_cow_size": 0,"total_connections_received": 77449,"total_commands_processed": 230844,"instantaneous_ops_per_sec": 0,"total_net_input_bytes": 9262857,"total_net_output_bytes": 13814796,"instantaneous_input_kbps": 0.0,"instantaneous_output_kbps": 0.0,"rejected_connections": 0,"sync_full": 0,"sync_partial_ok": 0,"sync_partial_err": 0,"expired_keys": 0,"expired_stale_perc": 0.0,"expired_time_cap_reached_count": 0,"evicted_keys": 0,"keyspace_hits": 275,"keyspace_misses": 12,"pubsub_channels": 0,"pubsub_patterns": 0,"latest_fork_usec": 0,"migrate_cached_sockets": 0,"slave_expires_tracked_keys": 0,"active_defrag_hits": 0,"active_defrag_misses": 0,"active_defrag_key_hits": 0,"active_defrag_key_misses": 0,"role": "master","connected_slaves": 0,"master_replid": "af9620a1329123d2a5ec942f9e78115dcc7c8f72","master_replid2": 0,"master_repl_offset": 0,"second_repl_offset": -1,"repl_backlog_active": 0,"repl_backlog_size": 33554432,"repl_backlog_first_byte_offset": 0,"repl_backlog_histlen": 0,"used_cpu_sys": 460.765952,"used_cpu_user": 769.999942,"used_cpu_sys_children": 0.0,"used_cpu_user_children": 0.0,"cluster_enabled": 0,"db0": { "keys": 81, "expires": 0, "avg_ttl": 0}} ...

December 8, 2021 · 1 min · jiezi

关于redis:Redis-高并发场景-获取连接失败-amp-击穿-amp-锁-等问题复盘

获取连贯失败 Could not get a resource from the pool 场景形容在C端我的项目上线后遇到高并发场景,后盾继续报警,获取不到redis连贯,此时单机redis最大连接数为10,经排查,发现该业务场景的动态资源均寄存于redis中,每个用户申请都会去redis获取数据,导致高并发场景下,获取连贯的速度大于开释连贯的速度,导致了此异样 解决方案转移局部动态资源至本地缓存中,每个实例保留一份,缩小redis压力 后续复盘在通过第一轮优化后,发现扔会呈现此状况,于是将单实例最大连接数调整至50,零碎不再报错,在高并发场景下须要慎重考虑分布式缓存与本地缓存的利用场景,不可一味应用分布式缓存,以及须要思考redis适宜的连接数,保障性能失常运行 高并发场景下审慎应用分布式锁 场景形容在上述场景下,线程池打满,服务器报错 Thread pool is EXHAUSTED...Pool Size:200,经排查发现在队列工作中,每一个工作自选获取分布式锁,锁开释速度小于获取锁的速度,导致大量申请累积在线程池中,造成异样 解决方案讲队列中锁勾销掉,依据业务需要决定应用偏心锁或非偏心锁模式,不须要额定分布式锁 缓存击穿场景形容上述场景中,某一个业务须要依据userId查问数据,并放入缓存中,下次进入时将命中缓存,高并发时,因为代码疏漏没有将第一次的后果放入缓存中,导致大量申请同时进入数据库,导致数据库连贯超时 解决方案调整代码,将数据写入缓存,并设置正当的过期工夫,避免缓存雪崩&击穿

December 3, 2021 · 1 min · jiezi

关于redis:Redis线程模型的前世今生

一、概述家喻户晓,Redis是一个高性能的数据存储框架,在高并发的零碎设计中,Redis也是一个比拟要害的组件,是咱们晋升零碎性能的一大利器。深刻去了解Redis高性能的原理显得越发重要,当然Redis的高性能设计是一个系统性的工程,波及到很多内容,本文重点关注Redis的IO模型,以及基于IO模型的线程模型。 咱们从IO的起源开始,讲述了阻塞IO、非阻塞IO、多路复用IO。基于多路复用IO,咱们也梳理了几种不同的Reactor模型,并剖析了几种Reactor模型的优缺点。基于Reactor模型咱们开始了Redis的IO模型和线程模型的剖析,并总结出Redis线程模型的长处、毛病,以及后续的Redis多线程模型计划。本文的重点是对Redis线程模型设计思维的梳理,捋顺了设计思维,就是一通百通的事了。 注:本文的代码都是伪代码,次要是为了示意,不可用于生产环境。二、网络IO模型发展史咱们常说的网络IO模型,次要蕴含阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO,本文重点关注跟Redis相干的内容,所以咱们重点剖析阻塞IO、非阻塞IO、多路复用IO,帮忙大家后续更好的了解Redis网络模型。 咱们先看上面这张图; 2.1 阻塞IO咱们常常说的阻塞IO其实分为两种,一种是单线程阻塞,一种是多线程阻塞。这外面其实有两个概念,阻塞和线程。 阻塞:指调用后果返回之前,以后线程会被挂起,调用线程只有在失去后果之后才会返回; 线程:零碎调用的线程个数。 像建设连贯、读、写都波及到零碎调用,自身是一个阻塞的操作。 2.1.1 单线程阻塞服务端单线程来解决,当客户端申请来长期,服务端用主线程来解决连贯、读取、写入等操作。 以下用代码模仿了单线程的阻塞模式; import java.net.Socket; public class BioTest { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(8081); while(true) { Socket socket=server.accept(); System.out.println("accept port:"+socket.getPort()); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); String inData=null; try { while ((inData = in.readLine()) != null) { System.out.println("client port:"+socket.getPort()); System.out.println("input data:"+inData); if("close".equals(inData)) { socket.close(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}咱们筹备用两个客户端同时发动连贯申请、来模仿单线程阻塞模式的景象。同时发动连贯,通过服务端日志,咱们发现此时服务端只承受了其中一个连贯,主线程被阻塞在上一个连贯的read办法上。 ...

November 30, 2021 · 3 min · jiezi

关于redis:Redis在Windows操作系统的各版本安装包压缩包下载地址

注:本文首发于码友网--《Redis在Windows操作系统的各版本安装包(压缩包)下载地址》概述现在的程序开发中,无论是.NET程序开发,.NET Core程序开发,.NET 5程序开发还是Java,Go或者其余开发语言,中间件技术已日趋成熟,在各种利用开发中还常常用到中间件,如:消息中间件,缓存中间件还有利用中间件等等。 常见的支流消息中间件有:RabbitMQ, Kafka, RocketMQ。 常见的支流缓存中间件有:Redis, MemCached(其中Redis应用更为宽泛和广泛)。 鉴于很多开发者的还应用的Windows操作系统,且Redis官网并未提供Windows操作系统版本的Redis服务端下载。所以,为不便大家下载(特地是拜访Github速度慢的敌人),本文为大家分享可运行在Windows操作系统中装置Redis的服务端/客户端安装包以压缩包。 注:这也是国外开发者应用Redis源码打包生成的基于Windows操作系统的Redis安装包(压缩包)。Redis for Windows蕴含的版本共享文件夹中蕴含了如下的Redis for Windows版本: Redis 5.0.14 for WindowsRedis 5.0.10 for WindowsRedis 5.0.9 for WindowsRedis 4.0.14.2 for WindowsRedis 4.0.14.1 for WindowsRedis 4.0.14 for WindowsRedis下载地址1.「阿里云盘」链接:https://www.aliyundrive.com/s... 举荐应用阿里云盘,下载不限速(飞个别的感觉)。 2.百度网盘下载链接: 请至原文提取每个版本的Redis都蕴含种版本的文件,别离是用于Windows操作系统的.msi格局的Redis安装包和.zip格局的Redis绿色版压缩包,你能够依据须要任意抉择一种形式在Windows操作系统中装置或运行Redis。 其中:.msi格局需双击进行装置Redis服务,而.zip格局的无需装置,只需使在命令行工具运行redis-server.exe命令即可。 以下是Redis for Windows 5.0.14在Windows操作系统中运行的截图:

November 29, 2021 · 1 min · jiezi

关于redis:Redis单机性能为何这么强

Redis的高性能怎么做到的?Redis这个NOSQL数据库在计算机界堪称是无人不知,无人不晓。只有波及到数据那么就须要数据库,数据库类型很多,然而NOSQL的kv内存数据库也很多,redis作为其中一个是怎么做到行业天花板的呢?是怎么做到高性能的呢?怎么做到高可用的呢?明天这篇八股文我就整顿一些redis的设计写写,本篇还是偏对于高性能这一块。 高效数据结构Redis的数据库相比传统的关系数据库,在数据结构上也是比拟非凡的,它的所有数据类型都能够看做是一个map的构造,key作为查问条件。 Redis基于KV内存数据库,它外部构建了一个哈希表,依据指定的KEY拜访时,只须要O(1)的工夫复杂度就能够找到对应的数据,而value的值又是一些领有各种个性的数据结构,这就给redis在数据操作的时候提供很好的性能了。 基于内存存储相比传统的关系数据库,数据文件可能以lsm tree 或者 b+ tree模式存在硬盘上,这个时候读取文件要有io操作了,而redis在内存中进行,并不会大量耗费CPU资源,所以速度极快。 内存从上图能够看到它介于硬盘和cpu缓存两头的,相比硬盘查找数据必定是快的,当然这里笔者个人见解上,如果关系型数据库把一些平庸操作的数据库也搁置在内存中缓存,也会失去一些性能的晋升,像操作系统外面缺页异样一样解决,把数据片段通过一些非凡算法缓存在内存外面,缩小文件io的开销。 io多路复用传统对于并发状况,如果一个过程不行,那搞多个过程不就能够同时解决多个客户端连贯了么?多过程是能够解决一些并发问题,然而还是有一些问题,上下文切换开销,线程循环创立,从PCB来回复原效率较低。随着客户端申请增多,那么线程也随着申请数量直线回升,如果是并发的时候波及到数据共享拜访,有时候波及到应用锁来管制范畴程序,影响其余线程执行效率。(过程在Linux也能够了解为线程,每个过程只是有一个线程,当然这里我下面写的过程,别纠结这些。。。) 线程是运行在过程上下文的逻辑流,一个过程能够蕴含多个线程,多个线程运行在同一过程上下文中,因而可共享这个过程地址空间的所有内容,解决了过程与过程之间通信难的问题,同时,因为一个线程的上下文要比一个过程的上下文小得多,所以线程的上下文切换,要比过程的上下文切换效率高得多。 像redis和Nginx这种利用就是单线程的程序,为什么他们能做到这么强的性能?首先看一个例子: Blocking IO中午吃饭,我给餐厅老板说要一碗‘热干面’,而后我就在那边始终等着老板做,老板没有做好,我就始终在哪里等着什么也不做,直到‘热干面’做好。这个流程就是咱们常说的Blocking I/O如图: 同步阻塞 IO 模型中,应用程序发动 read 调用后,会始终阻塞,直到内核把数据拷贝到用户空间。 Non Blocking IO切换一下常见: 同样你中午吃饭,给餐厅老板说要一碗‘热干面’,而后老板开始做了,你每隔几分钟向老板问一下‘好了吗?’,直到老板说好了,你取到‘热干面’完结。 同步非阻塞 IO 模型中,应用程序会始终发动 read 调用,期待数据从内核空间拷贝到用户空间的这段时间里,线程仍然是阻塞的,直到在内核把数据拷贝到用户空间,通过轮询操作,防止了始终阻塞,取回热干面的过程就是内核把筹备好的数据交换到用户空间过程。 综上两种模型,毛病都是差不多,都是在期待内核筹备数据,而后阻塞期待,同样逃不开阻塞这个问题,应用程序一直进行I/O零碎调用轮询数据是否曾经筹备好的过程是非常耗费CPU资源的。 I/O Multiplexing还是之前那个例子: 中午吃饭,给餐厅老板说要一碗‘热干面’,而后老板安顿给上面的厨子做,具体哪个厨子做不晓得,有好几个厨子,而后老板每隔一段时间询问上面的厨子有木有做好,如果做好了,就告诉我来去取餐。 IO 多路复用模型中,线程首先发动 select 调用,询问内核数据是否准备就绪,等内核把数据筹备好了,用户线程再发动 read 调用,read 调用的过程(数据从内核空间->用户空间)还是阻塞的。 Reactor 通过 I/O复用程序监控客户端申请事件,收到事件后通过工作分派器进行散发。针对建设连贯申请事件,通过 Acceptor 解决,并建设对应的 handler 负责后续业务解决,针对非连贯事件,Reactor 会调用对应的 handler 实现 read->业务解决->write 解决流程,并将后果返回给客户端,整个过程都在一个线程里实现。 Redis 是基于 Reactor 单线程模式来实现的,IO多路复用程序接管到用户的申请后,全副推送到一个队列里,交给文件分派器。对于后续的操作,和在 reactor 单线程实现计划里看到的一样,整个过程都在一个线程里实现,因而 Redis 被称为是单线程的操作。 咱们平时说的Redis单线程快是指它的申请处理过程十分地快!在单线程中监听多个Socket的申请,在任意一个Socket可读/可写时,Redis去读取客户端申请,在内存中操作对应的数据,而后再写回到Socket中。 单线程的益处: 没有了访问共享资源加锁的性能损耗开发和调试十分敌对,可维护性高没有多个线程上下文切换带来的额定开销,不是没有,是缩小了单线程不是没有毛病,其实毛病也是很显著的,如果前一个申请产生耗时比拟久的操作,那么整个Redis就会阻塞住,其余申请也无奈进来,直到这个耗时久的操作解决实现并返回,其余申请能力被解决到,然而redis应用的Reactor 单线程模式来实现的能够缓解这种状况。 ...

November 28, 2021 · 1 min · jiezi

关于redis:环境搭建-Linux-安装redis

装置:1.获取redis资源 cd /optwget http://download.redis.io/releases/redis-5.0.7.tar.gz2.解压 tar xzvf redis-5.0.7.tar.gz3.装置 cd redis-5.0.7makecd srcmake install PREFIX=/usr/local/redis4.挪动配置文件到装置目录下 cd ../mkdir /usr/local/redis/etcmv redis.conf /usr/local/redis/etc5.配置redisredis的配置文件为 redis.conf,在刚刚配置的门路中,为/usr/local/redis/etc/redis.conf 配置项名称配置项值范畴阐明daemonizeyes、noyes示意启用守护过程,默认是no即不以守护过程形式运行。其中Windows零碎下不反对启用守护过程形式运行port 指定 Redis 监听端口,默认端口为 6379bind 绑定的主机地址,如果须要设置近程拜访则间接将这个属性备注下或者改为bind * 即可,这个属性和上面的protected-mode管制了是否能够近程拜访 。protected-modeyes 、no保护模式,该模式管制内部网是否能够连贯redis服务,默认是yes,所以默认咱们外网是无法访问的,如需外网连贯rendis服务则须要将此属性改为no。timeout300当客户端闲置多长时间后敞开连贯,如果指定为 0,示意敞开该性能logleveldebug、verbose、notice、warning日志级别,默认为 noticedatabases16设置数据库的数量,默认的数据库是0。整个通过客户端工具能够看失去rdbcompressionyes、no指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采纳 LZF 压缩,如果为了节俭 CPU 工夫,能够敞开该选项,但会导致数据库文件变的微小。dbfilenamedump.rdb指定本地数据库文件名,默认值为 dump.rdbdir 指定本地数据库寄存目录requirepass 设置 Redis 连贯明码,如果配置了连贯明码,客户端在连贯 Redis 时须要通过 AUTH <password> 命令提供明码,默认敞开maxclients0设置同一时间最大客户端连接数,默认无限度,Redis 能够同时关上的客户端连接数为 Redis 过程能够关上的最大文件描述符数,如果设置 maxclients 0,示意不作限度。当客户端连接数达到限度时,Redis 会敞开新的连贯并向客户端返回 max number of clients reached 错误信息。maxmemory 指定 Redis 最大内存限度,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试革除已到期或行将到期的 Key,当此办法解决 后,依然达到最大内存设置,将无奈再进行写入操作,但依然能够进行读取操作。Redis 新的 vm 机制,会把 Key 寄存内存,Value 会寄存在 swap 区。配置项值范畴列里XXX为数值。vi /usr/local/redis/etc/redis.conf1 - 配置redis为后盾启动 ...

November 27, 2021 · 1 min · jiezi

关于redis:RedisJson发布官方性能报告性能碾压ES和Mongo

一、概述近期官网给出了RedisJson(RedisSearch)的性能测试报告,堪称碾压其余NoSQL,上面是外围的报告内容,先上论断: 对于隔离写入(isolated writes),RedisJSON 比 MongoDB 快 5.4 倍,比 ElasticSearch 快 200 倍以上。对于隔离读取(isolated reads),RedisJSON 比 MongoDB 快 12.7 倍,比 ElasticSearch 快 500 倍以上。在混合工作负载场景中,实时更新不会影响 RedisJSON 的搜寻和读取性能,而 ElasticSearch 会受到影响。以下是具体的数据: RedisJSON* 反对的操作数/秒比 MongoDB 高约 50 倍,比 ElasticSearch 高 7 倍/秒。RedisJSON* 的提早比 MongoDB 低约 90 倍,比 ElasticSearch 低 23.7 倍。此外,RedisJSON 的读取、写入和负载搜寻提早在更高的百分位数中远比 ElasticSearch 和 MongoDB 稳固。当减少写入比率时,RedisJSON 还能解决越来越高的整体吞吐量,而当写入比率减少时,ElasticSearch 会升高它能够解决的整体吞吐量。 二、查问引擎如前所述,reresearch和RedisJSON的开发十分强调性能。对于每一个版本,咱们都想确保开发者能够体验到稳固和产品。为此,咱们咱们给出了一些剖析工具、探测器来进行性能剖析。 并且,咱们每次发行新版本时时,也在一直的晋升性能。特地是对于reresearch来说,2.2版本在加载和查问性能上都比2.0快了1.7倍,同时还改良了吞吐量和数据加载的提早。 2.1 加载优化接下来的两个图显示了运行纽约市出租车基准测试的运行后果(具体数据能够查看这里,该基准测试测量了吞吐量和加载耗时等根底数据。 从这些图表中能够看出,每一个reresearch的新版本都有一个实质性的性能改良。 2.2 全文搜寻优化为了评估搜寻性能,咱们索引了590万篇维基百科摘要。而后咱们运行一个全文搜寻查问面板,失去的后果如下图所示(详细信息在这里)。从下面的图能够看出,通过从v2.0迁徙到v2.2,同样的数据,在写、读、搜寻(提早图)方面都有了大幅度的改良,从而进步了运行Search和JSON的可实现吞吐量。 三、和其余框架的比照为了评估RedisJSON的性能,咱们决定将它与MongoDB和ElasticSearch进行比拟。为了不便比照,咱们会从文档存储、本地可用、云中可用、业余反对和提供可伸缩性、性能等方面进行全方位的比照。 咱们应用了欠缺的YCSB规范来进行测试比照,它可能基于常见的工作负载来评估不同的产品,测量提早、吞吐量曲线直到饱和。除了CRUD YCSB操作之外,咱们还增加了一个两个字的搜寻操作,专门帮忙开发人员、零碎架构师和DevOps从业者找到适宜他们用例的最佳搜索引擎。 3.1 基准测试此次测试,咱们应用了如下的一些软件环境: MongoDB v5.0.3ElasticSearch 7.15RedisJSON (RediSearch 2.2+RedisJSON 2.0)此次是在Amazon Web Services 实例上运行基准测试,这三种解决方案都是分布式数据库,并且最罕用于生产中的分布式形式。这就是为什么所有产品都应用雷同的通用 m5d.8xlarge VM 和本地 SSD,并且每个设置由四个 VM 组成:一个客户端 + 三个数据库服务器。基准测试客户端和数据库服务器都在处于最佳网络条件下的独自 m5d.8xlarge 实例上运行,将实例严密地打包在一个可用区内,实现稳态剖析所需的低提早和稳固的网络性能。 ...

November 25, 2021 · 3 min · jiezi

关于redis:Redis-压缩列表

压缩列表压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只蕴含大量列表项,并且每个列表项要么就是小整数值,要么就是长度比拟短的字符串,那么 Redis 就会应用压缩列表来做列表键的底层实现。 压缩列表的形成压缩列表是 Redis 为了节俭内存而开发的,是由一系列非凡编码的间断内存块组成的程序型数据结构。一个压缩列表能够蕴含任意多个节点(entry),每个节点能够保留一个字节数组或者一个整数值。 zlbytes :4字节,记录整个压缩列表占用的内存字节数。在对压缩列表进行内存重调配或者计算 zlend 的地位时应用zltail :4字节,记录压缩列表表尾节点间隔压缩列表的起始地址有多少个字节。通过这个偏移量,毋庸便当整个压缩列表就能够确定表尾节点的地址zllen :记录了压缩列表蕴含的节点数量。当这个属性的值小于 65535 时,这个属性的值就是压缩列表蕴含节点的数量,当这个值等于 65535 时,节点的实在数量须要便当能力计算得出entryX :压缩列表蕴含的各个节点,节点的长度由节点保留的内存决定zlend :非凡值 0xFF(十进制 255),用于标记压缩列表的末端压缩列表节点的形成每个压缩列表能够保留一个字节数组或者一个整数值。 每个压缩列表节点都由 previous_entry_length 、encoding 、content 三个局部组成。 previous_entry_lengthprevious_entry_length 属性以字节为单位,记录了压缩列表中前一个节点的长度。previous_entry_length 属性的长度能够是 1 字节或者 5 字节。 如果前一节点的长度小于 254 字节,那么 previous_entry_length 属性的长度为 1 字节;否则长度为 5 字节,其中属性的第一个字节设置为 0xFE ,而之后的四个字节则用于保留前一节点的长度。压缩列表从表尾向表头遍历操作就是应用这一原理实现的,只有领有了一个指向某个节点起始地址的指针,那么通过这个指针以及这个节点的 previous_entry_length 属性,就能够始终向前一个节点回溯,最终达到压缩列表的表头节点。 encodingencoding 属性记录了节点的 content 属性所保留数据的类型以及长度。 content节点的 content 属性负责保留节点的值,节点值能够是一个字节数组或者整数,值的类型和长度由节点的 encoding 属性决定。 连锁更新因为每个节点的 previous_entry_length 属性都记录了前一个节点的长度,如果存在一个压缩列表中,又多个间断的、长度介于 250 字节到 253 字节之间的节点,这时将一个长度大于等于 254 字节的新节点设置为压缩列表的表头节点,之前的第一个节点中的 previous_entry_length 属性长度仅为 1,没方法保留新节点的长度,所以会对压缩列表执行空间重调配操作,将以后节点的 previous_entry_length 属性从原来的 1 字节长拓展为 5 字节长。 ...

November 22, 2021 · 1 min · jiezi

关于redis:redis管理工具-Redis-Web-Ui

GoRedisManagerredis 客户端治理平台(redis manager)【部署简略便捷,SSH连贯,用户校验,操作日志等】 性能清单治理连贯、切换DB反对 string 类型反对 list 类型反对 set 类型反对 zset 类型反对 hash 类型反对 stream 类型SSH连贯redis用户登录操作日志我的项目地址https://github.com/gphper/go-... 应用文档开始应用配置文件开始应用-c string 配置文件门路 (default "./config.yaml")默认拜访地址:http://127.0.0.1:8088/index配置文件connections:# 不启动ssh的配置办法 - servicename: localhost host: 127.0.0.1 port: "6379" password: "" usessh: 0 sshconfig: sshhost: "" sshport: "" sshusername: "" sshpassword: ""# 启动ssh的配置办法 - servicename: ceshi host: 127.0.0.1 port: "6379" password: redispass usessh: 1 sshconfig: sshhost: 127.0.0.1 sshport: "2203" sshusername: root sshpassword: "123456"hostname: 127.0.0.1port: "8088"#启用用户登录验证 不须要此性能则将 accounts 数据块正文掉即可accounts:- account: "admin" password: "123456"

November 20, 2021 · 1 min · jiezi

关于redis:CPU被挖矿Redis竟是内鬼

大家好 我是周杰伦 却说这一日,Redis正如平常个别工作,不久便收到了一条SAVE命令。 虽说这Redis常被用来当做缓存,数据只存在于内存中,却也能通过SAVE命令将内存中的数据保留到磁盘文件中以便长久化存储。只见Redis刚关上文件,筹备写入,不知何处忽然冲出几个大汉将其擒住。 到底是怎么回事?Redis一脸懵。 这事还得要从一个月之前说起。 挖矿病毒一个月前,从天而降的警报声突破了Linux帝国夜晚的平静,CPU占用率忽然飙升,却不知何人所为。在unhide的帮忙下,总算揪出了暗藏的过程。本认为危机曾经解除,岂料···夜已深了,平安警报忽然再一次响了起来。 “部长,rm那小子是混充的,明天他骗了咱们,挖矿病毒基本没删掉,又卷土重来了!” 安全部长望向远处的天空,CPU工厂门口的风扇又开始疯狂地转了起来··· 无奈之下,部长只好再次招集大家。 unhide再一次拿出看家本领,把潜藏的几个过程给捉了进去。kill老哥拿着他们的pid,手起刀落,动作干脆利落。 这一次,没等找到真正的rm,部长亲自动手,清理了这几个程序文件。 “部长,总这么上来不是个方法,删了又来,得想个长久之计啊!”,一旁的top说到。 “肯定要把背地的真凶给揪出来!”,ps说到。 “它们是怎么混进来的,也要考察分明!”,netstat说到。 “对,对,就是”,众人皆附和。 部长起身说道,“大家说得没错,在诸位到来之前,我曾经安顿助理去核查了,置信很快会有线索。” 此时,防火墙上前说道:“为了避免走漏消息,倡议先停掉所有的网络连接” “也罢,这三更半夜的,对业务影响也不大,停了吧!”,安全部长说到。 不多时,助理行色匆匆地赶了回来,在部长耳边窃窃私语一番,听得安全部长刹时脸色大变。 “sshd留一下,其他人能够先撤了”,部长说到。 大伙先后散去,只留下sshd,心里不觉忐忑了起来。 “等一下,kill也留一下”,部长补充道。 一听这话,sshd心跳的更放慢了。助理关上了大门,安全部长轻声说到:“据刚刚失去的音讯,有人非法近程登录了进来,这挖矿病毒极有可能就是被人近程上传了进来” sshd一听这话大惊失色,慌忙问道:“难道登录明码泄露了?” “应该不是,是应用的公私钥免密登录”,一旁的助理答复到。 “你看,在/root/.ssh/authorized_keys文件中,咱们发现了一个新的登录公钥,这在之前是没有的”,随后,助理输入了这文件的内容: [root@xuanyuan ~]# cat .ssh/authorized_keysssh-rsa AAAAB3NzaC1yc2EAAAADAQABA······“绝不是我干的”,sshd急忙撇清。 “近程登录,这不是你负责的业务吗?”,助理问到。 “的确是我负责,但我也只是按程序办事,他能用公私钥登录的前提是得先把公钥写入进来啊,所以到底是谁写进来的,这才是要害!”,sshd说到。 “说的没错,别缓和,想想看,有没有看到过谁动过这个文件?”,部长拍了下sshd的肩膀说到。 “这倒是没注意” 部长紧锁眉头,来回走了几步,说道:“那好,这公钥咱们先清理了。回去当前盯紧这个文件,有人来拜访立即报给我” “好的”,sshd随后来到,发现自己曾经吓出了一身冷汗。 凶手浮现工夫一晃,一个月就过来了。 自从把authorized_keys文件中的公钥清理后,Linux帝国总算是太平了一阵子,挖矿病毒入侵事件也慢慢被人淡忘。 这天早晨夜已深,sshd打起了瞌睡。忽然,“咣当”一声,sshd醒了过去,睁眼一看,竟发现有程序闯入了/root/.ssh目录! 这一下sshd睡意全无,等了一个多月,难道这家伙要现身了? sshd不觉缓和了起来,到底会是谁呢? 此刻,sshd紧紧盯着authorized_keys文件,眼睛都不敢眨一下,惟恐错过些什么。 果然,一个身影走了过去,径直走向这个文件,随后关上了它! sshd不敢犹豫,连忙给安全部长助理发去了音讯。 那背影转过身来,这一下sshd看清了他的模样,居然是Redis! 收到音讯的部长带人火速赶了过去,不等Redis写入数据,就上前按住了他。 “好家伙,没想到内鬼竟然是你!”,sshd得意的说到。 Redis看着众人,一脸冤屈,“你们这是干什么?我也没做什么好事啊” “人赃并获,你还抵赖?说吧,你为什么要来写authorized_keys文件?” “那是因为我要来执行数据长久化存储,把内存中的数据写到文件中保留”,Redis答道。 “你长久化存储,为什么会写到authorized_keys文件外面来?”,sshd持续质问。 “刚刚收到几条命令,设置了长久化存储的文件名就是这个,不信你看”,说罢,Redis拿出了刚刚收到的几条命令: CONFIG SET dir /root/.sshCONFIG SET dbfilename authorized_keysSAVE“第一条指定保留门路,第二条指定保留的文件名,第三条就是保留数据到文件了”,Redis持续解释到。 安全部长认真看着几条命令,说道:“把你要写入的数据给我看看” “这可有点多,你等一下”,说罢,Redis拿出了所有的键值数据,散落一地。众人在一大片数据中看花了眼。 “部长快看!”,sshd忽然大叫。 顺着他手指的方向,一个醒目的公钥呈现在了大家背后。 ssh-rsa AAAAB3NzaC1yc2EAA···“果然是你!” Redis还是一脸懵,还不知产生了什么。 “你这家伙,被人当枪使了!你写的这个文件可不是一般文件,你这要是写进去了,他人就能近程登录进来了,之前的挖矿病毒就是这么进来的!”,sshd说到。 ...

November 17, 2021 · 1 min · jiezi

关于redis:一站式学习Redis-从入门到高可用分布式实践MK

download:一站式学习Redis 从入门到高可用分布式实际MKimport org.json.JSONArray;import android.app.Activity;import android.app.AlertDialog;import android.content.ActivityNotFoundException;import android.content.DialogInterface;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import com.phonegap.api.PhonegapActivity;import com.phonegap.api.Plugin;import com.phonegap.api.PluginResult;public class PluginTest extends Plugin { public static String ACTION = "hello"; public PluginTest() {}/** * Executes the request and returns PluginResult. * * @param action The action to execute. * @param args JSONArray of arguments for the plugin. * @param callbackId The callback id used when calling back into JavaScript. * @return A PluginResult object with a status and message. */@Overridepublic PluginResult execute(String action, JSONArray args, String callbackId) { try { JSONObject jsonObj = new JSONObject();//可能返回给JS的JSON数据 if (action.equals("hello")) { String str1= args.getString(0); //获取第一个参数 String str2= args.getString(1); //获取第二个参数 jsonObj.put("str1", str1+"1"); //把参数放到JSONObject对象中 jsonObj.put("str2", str2+"2"); //把参数放到JSONObject对象中 } PluginResult r = new PluginResult(PluginResult.Status.OK,jsonObj); return r; } catch (Exception e) { e.printStackTrace(); }}} 复制代码二、在plugins.xml中配置插件 ...

November 13, 2021 · 1 min · jiezi

关于redis:阿里P6面试官Redis如何实现分布式锁锁过期了怎么办

Redis实现分布式锁的原理后面讲了Redis在理论业务场景中的利用,那么上面再来理解一下Redisson功能性场景的利用,也就是大家常常应用的分布式锁的实现场景。 对于分布式锁的概念,本文就不做形容。•引入redisson依赖 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.16.0</version> </dependency>•编写简略的测试代码 public class RedissonTest { private static RedissonClient redissonClient; static { Config config=new Config(); config.useSingleServer().setAddress("redis://192.168.221.128:6379"); redissonClient=Redisson.create(config); } public static void main(String[] args) throws InterruptedException { RLock rLock=redissonClient.getLock("updateOrder"); //最多期待100秒、上锁10s当前主动解锁 if(rLock.tryLock(100,10,TimeUnit.SECONDS)){ System.out.println("获取锁胜利"); } Thread.sleep(2000); rLock.unlock(); redissonClient.shutdown(); } }Redisson分布式锁的实现原理你们会发现,通过redisson,非常简单就能够实现咱们所须要的性能,当然这只是redisson的冰山一角,redisson最弱小的中央就是提供了分布式个性的常用工具类。使得本来作为协调单机多线程并发程序的并发程序的工具包取得了协调分布式多级多线程并发零碎的能力,升高了程序员在分布式环境下解决分布式问题的难度,上面剖析一下RedissonLock的实现原理 RedissonLock.tryLock@Overridepublic boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { long time = unit.toMillis(waitTime); long current = System.currentTimeMillis(); long threadId = Thread.currentThread().getId(); //通过tryAcquire办法尝试获取锁 Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId); // lock acquired if (ttl == null) { //示意胜利获取到锁,间接返回 return true; } //省略局部代码....}tryAcquireprivate <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) { RFuture<Long> ttlRemainingFuture; //leaseTime就是租约工夫,就是redis key的过期工夫。 if (leaseTime != -1) { //如果设置过期工夫 ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG); } else {//如果没设置了过期工夫,则从配置中获取key超时工夫,默认是30s过期 ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG); } //当tryLockInnerAsync执行完结后,触发上面回调 ttlRemainingFuture.onComplete((ttlRemaining, e) -> { if (e != null) { //阐明出现异常,间接返回 return; } // lock acquired if (ttlRemaining == null) { //示意第一次设置锁键 if (leaseTime != -1) { //示意设置过超时工夫,更新internalLockLeaseTime,并返回 internalLockLeaseTime = unit.toMillis(leaseTime); } else { //leaseTime=-1,启动Watch Dog scheduleExpirationRenewal(threadId); } } }); return ttlRemainingFuture;}tryLockInnerAsync通过lua脚本来实现加锁的操作 ...

November 9, 2021 · 6 min · jiezi

关于redis:Redis-Stream类型的使用

一、背景最近在看redis这方面的常识,发现在redis5中产生了一种新的数据类型Stream,它和kafka的设计有些相似,能够当作一个简略的音讯队列来应用。 二、redis中Stream类型的特点是可长久化的,能够保证数据不失落。反对音讯的多播、分组生产。反对音讯的有序性。三、Stream的构造 解释: 消费者组: Consumer Group,即应用 XGROUP CREATE 命令创立的,一个消费者组中能够存在多个消费者,这些消费者之间是竞争关系。 同一条音讯,只能被这个消费者组中的某个消费者获取。多个消费者之间是互相独立的,互不烦扰。消费者: Consumer 生产音讯。last_delivered_id: 这个id保障了在同一个消费者组中,一个音讯只能被一个消费者获取。每当消费者组的某个消费者读取到了这个音讯后,这个last_delivered_id的值会往后挪动一位,保障消费者不会读取到反复的音讯。pending_ids:记录了消费者读取到的音讯id列表,然而这些音讯可能还没有解决,如果认为某个音讯解决,须要调用ack命令。这样就确保了某个音讯肯定会被执行一次。音讯内容:是一个键值对的格局。Stream 中 音讯的 ID: 默认状况下,ID应用 * ,redis能够主动生成一个,格局为 工夫戳-序列号,也能够本人指定,个别应用默认生成的即可,且后生成的id号要比之前生成的大。四、Stream的命令1、XADD 往Stream开端增加音讯1、命令格局xadd key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value ...] 2、举例xadd 命令 返回的是数据的id, xx-yy (xx指的是毫秒数,yy指的是在这个毫秒内的第几条音讯) 1、向流中减少一条数据,127.0.0.1:6379> xadd stream-key * username zhangsan # 向stream-key这个流中减少一个 username 是zhangsan的数据 *示意主动生成id"1635999858912-0" # 返回的是ID127.0.0.1:6379> keys *1) "stream-key" # 能够看到stream主动创立了127.0.0.1:6379>2、向流中减少数据,不主动创立流127.0.0.1:6379> xadd not-exists-stream nomkstream * username lisi # 因为指定了nomkstream参数,而not-exists-stream之前不存在,所以退出失败(nil)127.0.0.1:6379> keys *(empty array)127.0.0.1:6379>3、手动指定ID的值127.0.0.1:6379> xadd stream-key 1-1 username lisi # 此处id的值是本人传递的1-1,而不是应用*主动生成"1-1" # 返回的是id的值127.0.0.1:6379>4、设置一个固定大小的Stream1、准确指定Stream的大小指定指定Stream的大小比含糊指定Stream的大小会略微多少耗费一些性能。 ...

November 9, 2021 · 4 min · jiezi

关于redis:Redis源码5-异步事件

指标在Redis源码-3 网络编程, 学习了redis封装的网络库。其中用了循环不断去执行anetTcpAccept, 这是机制效率差,为了进步程序的并发数,操作系统引入了epoll等相似的机制,防止了死等,能够做到当事件产生时告诉用户。Redis没有才用现有的异步库,自研了一个适宜redis的库。比拟玲珑,适宜学习。本文引入Redis中的ae模块,革新之前的流程。 可运行代码源码 筹备工作:从redis源码中拷贝代码 cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/ae* . ...批改server.c#include "anet.h"#include "zmalloc.h"#include <sys/socket.h>#include <unistd.h>#include "sys/socket.h"#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */#include "ae.h"void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);#define MAX_ACCEPTS_PER_CALL 1000#define UNUSED(V) ((void) V)void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { int cport, cfd, max = MAX_ACCEPTS_PER_CALL; char cip[NET_IP_STR_LEN]; UNUSED(el); UNUSED(mask); UNUSED(privdata); while(max--) { char* neterr; neterr = zmalloc(100); cfd = anetTcpAccept(neterr, fd, cip, sizeof(cip), &cport); if (cfd == ANET_ERR) { continue; } anetCloexec(cfd); printf("accept...%d\n",cfd); char buf[1024]; recv(cfd, buf, sizeof(buf), MSG_WAITALL); printf("recv from %s:%d %s\n",cip, cport, buf); close(cfd); }}int main() { // 错误信息 char *neterr = zmalloc(10); printf("staring...\n"); aeEventLoop *el; el = aeCreateEventLoop(100); // 端口6380 int serverSocket = anetTcpServer(neterr, 6380,"*" , 2); if ( ! neterr ) { printf("start err %s \n", neterr); return 1; } printf("listening...%d \n",serverSocket ); if (aeCreateFileEvent(el, serverSocket, AE_READABLE, acceptTcpHandler,NULL) == AE_ERR) { aeDeleteFileEvent(el, serverSocket, AE_READABLE); return 1; } aeMain(el); aeDeleteEventLoop(el); return 0; }去掉了while(1)循环,调用anetTcpServer创立事件循环,调用aeCreateFileEvent注册文件事件。之后进入事件循环aeMain。当有文件事件产生时,会调用acceptTcpHandler, 在这里执行anetTcpAccept, 对文件描述符进行读操作。 ...

November 8, 2021 · 2 min · jiezi

关于redis:Redis-60-新特性篇深度剖析客户端缓存Client-side-caching原理与性能

码老湿,上次你解说了 Redis 多线程模型,这次我想晓得客户端缓存(Client side caching)技术,他的英文名叫: Redis server-assisted client side caching ,能够说说么?我不是嫖客,看完我会点赞、再看、分享的。别装逼了,还整英文,咋不入地,做人要说话算数哟,不然中午尿裤子。在说这个之前,码哥先给读者送一段寄语作为开篇。 开篇寄语不要悭吝你的赞美,如果他人做的很好,就给他正反馈,这也是一种利他。 另外,少关注用「赞美」投票的事物,而多关注用「交易」投票的事物。 判断一个人是否牛逼,不是看网上有多少人赞美他,而是看有多少人违心跟他产生交易、赞叹、领取、下单。 因为赞美太便宜,而违心与他产生交易,才是真正的信赖。 为啥须要客户端缓存Redis 的Tracking Feature 的实现代码在: https://github.com/antirez/redis/blob/unstable/src/tracking.c。 很多公司应用 Redis 做缓存零碎,并且很好的进步了数据拜访的性能,为了进一步应答热点数据,还是会在 Redis 的 Client 端缓存一部分热点数据,用来应答「吃瓜事件」。 比方,「这该死的 996 福报」、「吴亦凡之慷慨牢房」、「工夫治理巨匠」、「思聪舔我不得就锤我」、「吴秀波之谈恋爱么,能坐牢的那种」…… 除了应用 Redis 缓存防止间接拜访数据库以外,还会加更多的 cache 层,比方采纳 Memcachced 作为热点数据的本地缓存: 先去 Memcachced 中查问数据,命中间接返回。Memcachced 未命中,则再从 Redis 查问,命中则返回数据,并在 Memcachced 保留这个数据。Redis 未命中,则去 MySQL中查问,并顺次设置到 Redis 和 Memcachced中。拜访本地内存的的性能必然比通过网络拜访 Redis 快,所以这种模式能够极大地缩小获取数据的提早,并且能够缩小 Redis 的负载,进步性能。 拜访 Redis 获取数据,服务器响应。 应用客户端缓存,应用程序将获取的热门的数据存储在用用程序中,无需再次通过网络拜访 Redis。 应该缓存什么 咱们不应该缓存一直变动的键。咱们不该缓存很少申请的键。咱们心愿缓存常常申请并以正当速率更改的键。对于没有稳固变动速度的例子,比方一直被INCR批改的全局计数器,就不应该缓存。客户端缓存实现原理码老湿, Redis 中的数据批改或者生效了,如何及时同步告知客户端生效了呢?本人实现也太简单了。Redis 实现的是一个服务端帮助的客户端缓存,叫做tracking。客户端缓存的命令是: CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]Redis 6.0 实现 Tracking 性能提供了两种模式解决这个问题,别离是应用RESP3 协定版本的一般模式和播送模式,以及应用 RESP2 协定版本的转发模式。 ...

November 8, 2021 · 2 min · jiezi

关于redis:Redis源码4-日志和时间

指标在平时的开发过程中,调试代码是很重要的。对于调试,打印日志又是最常见,最疾速的形式,本文钻研redis中的日志模块。钻研其中的应用形式。 整体剖析redis中的日志模块写在了server.c中,没有独立成一个文件。本文以最小可运行的形式,抽离出日志模块,以及日志依赖的工夫模块。对于日志,redis封装了serverLog, 该办法第一个参数是日志等级,残余参数会拼接成字符串。放弃了和高级语言一样的形式。能够把日志输入到stdout, 也能够输入到指定文件。日志中会蕴含工夫,redis又封装了不同精度的工夫。 次要关注unix工夫戳。它反对原子操作。 源代码源码 筹备工作:从redis源码中拷贝代码cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/localtime.c .cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/atomicvar.h .新建server.c#include "stdio.h"#include "server.h"#include <sys/time.h>#include <syslog.h>#include <stdarg.h>#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */struct redisServer server;/* * Gets the proper timezone in a more portable fashion * i.e timezone variables are linux specific. */long getTimeZone(void) {#if defined(__linux__) || defined(__sun) return timezone;#else struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); return tz.tz_minuteswest * 60L;#endif}/* We take a cached value of the unix time in the global state because with * virtual memory and aging there is to store the current time in objects at * every object access, and accuracy is not needed. To access a global var is * a lot faster than calling time(NULL). * * This function should be fast because it is called at every command execution * in call(), so it is possible to decide if to update the daylight saving * info or not using the 'update_daylight_info' argument. Normally we update * such info only when calling this function from serverCron() but not when * calling it from call(). */void updateCachedTime(int update_daylight_info) { server.ustime = ustime(); server.mstime = server.ustime / 1000; time_t unixtime = server.mstime / 1000; atomicSet(server.unixtime, unixtime); /* To get information about daylight saving time, we need to call * localtime_r and cache the result. However calling localtime_r in this * context is safe since we will never fork() while here, in the main * thread. The logging function will call a thread safe version of * localtime that has no locks. */ if (update_daylight_info) { struct tm tm; time_t ut = server.unixtime; localtime_r(&ut,&tm); server.daylight_active = tm.tm_isdst; }}/* Return the UNIX time in microseconds */long long ustime(void) { struct timeval tv; long long ust; gettimeofday(&tv, NULL); ust = ((long long)tv.tv_sec)*1000000; ust += tv.tv_usec; return ust;}void initServerConfig(void) { updateCachedTime(1); server.timezone = getTimeZone();}int main() { tzset(); initServerConfig(); server.sentinel_mode = 0; server.masterhost = NULL; //server.logfile = "/tmp/server.log"; server.logfile = ""; server.verbosity = 0; server.syslog_enabled = 1; serverLog(LL_WARNING, " Redis is starting unixtime is: %ld", server.unixtime);}/* Like serverLogRaw() but with printf-alike support. This is the function that * is used across the code. The raw version is only used in order to dump * the INFO output on crash. */void _serverLog(int level, const char *fmt, ...) { va_list ap; char msg[LOG_MAX_LEN]; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); serverLogRaw(level,msg);}/*============================ Utility functions ============================ *//* We use a private localtime implementation which is fork-safe. The logging * function of Redis may be called from other threads. */void nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst);/* Low level logging. To use only for very big messages, otherwise * serverLog() is to prefer. */void serverLogRaw(int level, const char *msg) { const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; const char *c = ".-*#"; FILE *fp; char buf[64]; int rawmode = (level & LL_RAW); int log_to_stdout = server.logfile[0] == '\0'; level &= 0xff; /* clear flags */ if (level < server.verbosity) return; fp = log_to_stdout ? stdout : fopen(server.logfile,"a"); if (!fp) return; if (rawmode) { fprintf(fp,"%s",msg); } else { int off; struct timeval tv; int role_char; pid_t pid = getpid(); gettimeofday(&tv,NULL); struct tm tm; nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active); off = strftime(buf,sizeof(buf),"%d %b %Y %H:%M:%S.",&tm); snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); if (server.sentinel_mode) { role_char = 'X'; /* Sentinel. */ } else if (pid != server.pid) { role_char = 'C'; /* RDB / AOF writing child. */ } else { role_char = (server.masterhost ? 'S':'M'); /* Slave or Master. */ } fprintf(fp,"%d:%c %s %c %s\n", (int)getpid(),role_char, buf,c[level],msg); } fflush(fp); if (!log_to_stdout) fclose(fp); if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);}新建server.h#include <fcntl.h>#include <time.h>#include "atomicvar.h"#include <unistd.h>#define LOG_MAX_LEN 1024 /* Default maximum length of syslog messages.*/typedef long long ustime_t; /* microsecond time type. */typedef long long mstime_t; /* millisecond time type. */long long ustime(void);/* Log levels */#define LL_DEBUG 0#define LL_VERBOSE 1#define LL_NOTICE 2#define LL_WARNING 3#define LL_RAW (1<<10) /* Modifier to log without timestamp *//* Use macro for checking log level to avoid evaluating arguments in cases log * should be ignored due to low level. */#define serverLog(level, ...) do {\ if (((level)&0xff) < server.verbosity) break;\ _serverLog(level, __VA_ARGS__);\ } while(0)#ifdef __GNUC__void _serverLog(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));#elsevoid _serverLog(int level, const char *fmt, ...);#endifvoid serverLogRaw(int level, const char *msg);struct redisServer { pid_t pid; /* Main process pid. */ char *masterhost; /* Hostname of master */ int sentinel_mode; /* True if this instance is a Sentinel. */ char *logfile; /* Path of log file */ int syslog_enabled; /* Is syslog enabled? */ int verbosity; /* Loglevel in redis.conf */ time_t timezone; /* Cached timezone. As set by tzset(). */ int daylight_active; /* Currently in daylight saving time. */ mstime_t mstime; /* 'unixtime' in milliseconds. */ ustime_t ustime; /* 'unixtime' in microseconds. */ redisAtomic time_t unixtime; /* Unix time sampled every cron cycle. */};在redisServer中,因为在log中应用,所有须要加了一些看似没有用属性。这一点redis做的不是很好,业务和库耦合了。redisServer能够了解为有状态的容器,须要全局无效,共享的,都能够加到这下面来。在函数中间接拜访属性就能够了。 ...

November 8, 2021 · 4 min · jiezi

关于redis:Redis源码3-网络编程

Redis 网络编程开发一个server和client, client向server发送音讯, server输入client内容 源代码源码 筹备工作:从redis源码中拷贝代码cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/zmalloc.* .cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/anet.* .cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/atomicvar.h .cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/fmacros.h .新建server.c#include "stdio.h"#include "anet.h"#include "zmalloc.h"#include <sys/socket.h>#include <unistd.h>#include "sys/socket.h"#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */int main() { // 错误信息 char *neterr = zmalloc(10); printf("staring...\n"); // 端口6380 int serverSocket = anetTcpServer(neterr, 6380,"*" , 2); if ( ! neterr ) { printf("start err %s \n", neterr); return 1; } printf("listening...%d \n",serverSocket ); while(1){ int cfd; // 错误信息 char* err = zmalloc(20); char cip[NET_IP_STR_LEN]; int cport; cfd = anetTcpAccept(err, serverSocket, cip, sizeof(cip), &cport); if ( cfd == ANET_ERR ) continue; printf("accept...%d\n",cfd); char buf[1024]; recv(cfd, buf, sizeof(buf), MSG_WAITALL); printf("recv from %s:%d %s\n",cip, cport, buf); close(cfd); } close(serverSocket);}新建client.c#include "stdio.h"#include "anet.h"#include "zmalloc.h"#include <sys/socket.h>#include <unistd.h>#include "sys/socket.h"#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */int main() { // 错误信息 char *neterr = zmalloc(10); printf("staring...\n"); int port; port = 6380; char* addr; addr = "127.0.0.1"; int fd = anetTcpNonBlockConnect(NULL,addr,port); char * hello = "hello"; printf("send %s\n", hello); send(fd, hello, sizeof(hello),MSG_DONTWAIT ); close(fd);}新建Makefile文件all: server client @echo "anet demo"server : anet.o zmalloc.o server.o $(CC) -o $@ $^client : anet.o zmalloc.o client.o $(CC) -o $@ $^%.o: %.c $(CC) -O0 -DREDIS_TEST=1 -MMD -o $@ -c $<.PHONY: cleanclean: rm -rf *.o *.d server client解释anet把封装的了socket编程的接口, 原先须要socket, bind, listen, accept , connect等接口封装为很少的接口,屏蔽了一些简单的参数,开发网络编程更加靠近Go、Python等高级语言。 ...

November 6, 2021 · 2 min · jiezi

关于redis:Redis中的内存分配和原子操作

内容redis中的内存调配apiredis中的原子操作api源代码Redis中的内存调配和原子操作 代码构建cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/zmalloc.* .cp /home/vagrant/github/server_installer/servers/redis/redis-6.2/src/atomicvar.h .zmalloc.c中援用了config.h, 删除掉 新建server.c, 内容如下 #include "stdio.h"#include "atomicvar.h"#include "zmalloc.h"int main(int argc, char **argv){ redisAtomic long long i = 10; atomicIncr(i, 3); printf("hello %lld\n",i); char *p; p = zmalloc(10); p = "hello"; printf("hello %s\n",p); int accurate; accurate = 10;#ifdef REDIS_TEST zmalloc_test( argc,argv,accurate );#endif return 0;}新建Makefile server : zmalloc.o server.o $(CC) -o $@ $^%.o: %.c $(CC) -DREDIS_TEST=1 -MMD -o $@ -c $<.PHONY: cleanclean: rm -rf *.o *.d server再看一下zmalloc_test的内容 ...

November 5, 2021 · 1 min · jiezi

关于redis:M1Mac中docker的redis集群配置

M1-Mac中docker的redis集群配置前言 题目起名有些绕不过为了避免读者误会这也是一个必要的,本文是集体的一次mac上搭建redis集群的实战笔记,笔者为mac零碎,尽管很多操作相似Linux然而有差别,也踩了不少的坑,本教程也能够作为linux的docker搭建redis集群参考应用,最初有任何疑难欢送探讨。 提醒:本教程实用于linux和mac零碎,然而须要留神的是mac零碎中/usr/local目录上面其实是被mac关闭的,不能作为配置和应用,尽管能够通过sudo强制构建配置等文件,然而会呈现莫名其妙的状况,本文也会列出集体的踩坑点,心愿能帮忙同样应用mac零碎的同学避坑。一、筹备docker 巧妇难为无米之炊,所以先得在mac上装一个docker。 1. 装置docker 集体目前应用mac作为主力机,所以所有的演示都是在mac上实现,当然下载也是只提供mac的下载地址,首先须要跑到这个网址进行下载,https://docs.docker.com/deskt...。 mac的装置间接拖过来就行,这里装置实现之后不晓得为什么docker容器老是无奈启动,然而点击了unintall啥的之后忽然就好了,目前通过版本迭代docker曾经能够失常在m1中应用了。装置实现之后启动软件,能够先运行一下docker的ddocker info的命令,也能够先抉择软件举荐的镜像来运行一下,命令如下 :docker run -d -p 80:80 docker/getting-started运行实现之后,间接拜访localhost即可,此时会进入一个docker的疾速入门页面。2. 更新镜像 因为国外的docker切实是慢,所以这里须要先切换为国内的镜像仓库,最终应用的是网易的镜像地址:http://hub-mirror.c.163.com。在任务栏点击 Docker for mac 利用图标 -> Perferences... -> Daemon -> Registry mirrors。在列表中填写加速器地址即可,批改实现之后,点击 Apply & Restart 按钮,Docker 就会重启并利用配置的镜像地址了。 新版的docker 只有更改 docker engine的相干json配置即可,比方上面的就是替换之后的后果,也算是一个踩坑点,网上的少数教程都是老版本的docker,找了半天没找到在哪=-=: 最初为了验证配置是否失效咱们能够应用docker info查看,在打印信息的最下方看到对应的配置地址阐明配置失效了: Insecure Registries: 127.0.0.0/8 Registry Mirrors: https://docker.mirrors.ustc.edu.cn/ https://hub-mirror.c.163.com/ Live Restore Enabled: false二、docker的redis单机部署 单机部署就非常简略了,只须要上面几个命令即可: #默认拉取一个最新的redis镜像docker pull redis#在默认的6379端口上启动一个redis服务docker run --name test-redis -p 6379:6379 -d redis#进入容器外部docker exec -it test-redis /bin/bash# 连贯redisredis-cli#进入之后装置常规 ping一下即可ping 单机运行还是配置还是挺快的,不过须要留神单机的运行应用的配置都是默认的配置,并且docker启动redis镜像里是没有配置文件的,如果想要像装置redis一样应用自定义的配置文件启动须要做如下的更改。 ...

November 4, 2021 · 2 min · jiezi

关于redis:深入剖析Redis客户端Jedis的特性和原理

一、开篇Redis作为目前通用的缓存选型,因其高性能而倍受欢迎。Redis的2.x版本仅反对单机模式,从3.0版本开始引入集群模式。 Redis的Java生态的客户端当中蕴含Jedis、Redisson、Lettuce,不同的客户端具备不同的能力是应用形式,本文次要剖析Jedis客户端。 Jedis客户端同时反对单机模式、分片模式、集群模式的拜访模式,通过构建Jedis类对象实现单机模式下的数据拜访,通过构建ShardedJedis类对象实现分片模式的数据拜访,通过构建JedisCluster类对象实现集群模式下的数据拜访。 Jedis客户端反对单命令和Pipeline形式拜访Redis集群,通过Pipeline的形式可能进步集群拜访的效率。 本文的整体剖析基于Jedis的3.5.0版本进行剖析,相干源码均参考此版本。 二、Jedis拜访模式比照Jedis客户端操作Redis次要分为三种模式,分表是单机模式、分片模式、集群模式。 单机模式次要是创立Jedis对象来操作单节点的Redis,只实用于拜访单个Redis节点。分片模式(ShardedJedis)次要是通过创立ShardedJedisPool对象来拜访分片模式的多个Redis节点,是Redis没有集群性能之前客户端实现的一个数据分布式计划,实质上是客户端通过一致性哈希来实现数据分布式存储。集群模式(JedisCluster)次要是通过创立JedisCluster对象来拜访集群模式下的多个Redis节点,是Redis3.0引入集群模式后客户端实现的集群拜访拜访,实质上是通过引入槽(slot)概念以及通过CRC16哈希槽算法来实现数据分布式存储。单机模式不波及任何分片的思维,所以咱们着重剖析分片模式和集群模式的理念。 2.1 分片模式分片模式实质属于基于客户端的分片,在客户端实现如何依据一个key找到Redis集群中对应的节点的计划。Jedis的客户端分片模式采纳一致性Hash来实现,一致性Hash算法的益处是当Redis节点进行增减时只会影响新增或删除节点前后的小局部数据,绝对于取模等算法来说对数据的影响范畴较小。Redis在大部分场景下作为缓存进行应用,所以不必思考数据失落以致缓存穿透造成的影响,在Redis节点增减时能够不必思考局部数据无奈命中的问题。分片模式的整体利用如下图所示,外围在于客户端的一致性Hash策略。 (援用自:www.cnblogs.com) 2.2 集群模式集群模式实质属于服务器分片技术,由Redis集群自身提供分片性能,从Redis 3.0版本开始正式提供。 集群的原理是:一个 Redis 集群蕴含16384 个哈希槽(Hash slot), Redis保留的每个键都属于这16384个哈希槽的其中一个, 集群应用公式CRC16(key)%16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键key的CRC16校验和 。 集群中的每个节点负责解决一部分哈希槽。举个例子, 一个集群能够有三个哈希槽, 其中: 节点 A 负责解决 0 号至 5500 号哈希槽。节点 B 负责解决 5501 号至 11000 号哈希槽。节点 C 负责解决 11001 号至 16383 号哈希槽。Redis在集群模式下对于key的读写过程首先将对应的key值进行CRC16计算失去对应的哈希值,将哈希值对槽位总数取模映射到对应的槽位,最终映射到对应的节点进行读写。以命令set("key", "value")为例子,它会应用CRC16算法对key进行计算失去哈希值28989,而后对16384进行取模失去12605,最初找到12605对应的Redis节点,最终跳转到该节点执行set命令。 集群模式的整体利用如下图所示,外围在于集群哈希槽的设计以及重定向命令。 (援用自:www.jianshu.com) 三、Jedis的根底用法// Jedis单机模式的拜访public void main(String[] args) { // 创立Jedis对象 jedis = new Jedis("localhost", 6379); // 执行hmget操作 jedis.hmget("foobar", "foo"); // 敞开Jedis对象 jedis.close();} // Jedis分片模式的拜访public void main(String[] args) { HostAndPort redis1 = HostAndPortUtil.getRedisServers().get(0); HostAndPort redis2 = HostAndPortUtil.getRedisServers().get(1); List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(2); JedisShardInfo shard1 = new JedisShardInfo(redis1); JedisShardInfo shard2 = new JedisShardInfo(redis2); // 创立ShardedJedis对象 ShardedJedis shardedJedis = new ShardedJedis(shards); // 通过ShardedJedis对象执行set操作 shardedJedis.set("a", "bar");} // Jedis集群模式的拜访public void main(String[] args) { // 构建redis的集群池 Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("127.0.0.1", 7001)); nodes.add(new HostAndPort("127.0.0.1", 7002)); nodes.add(new HostAndPort("127.0.0.1", 7003)); // 创立JedisCluster JedisCluster cluster = new JedisCluster(nodes); // 执行JedisCluster对象中的办法 cluster.set("cluster-test", "my jedis cluster test"); String result = cluster.get("cluster-test");}Jedis通过创立Jedis的类对象来实现单机模式下的数据拜访,通过构建JedisCluster类对象来实现集群模式下的数据拜访。 ...

November 2, 2021 · 9 min · jiezi

关于redis:Redis核心原理与实践Redis启动过程源码分析

Redis服务器负责接管解决用户申请,为用户提供服务。Redis服务器的启动命令格局如下: redis-server [ configfile ] [ options ]configfile参数指定配置文件。options参数指定启动配置项,它能够笼罩配置文件中的配置项,如 redis-server /path/to/redis.conf --port 7777 --protected-mode no该命令启动Redis服务,并指定了配置文件/path/to/redis.conf,给出了两个启动配置项:port、protected-mode。 本文通过浏览Redis源码,剖析Redis启动过程,内容摘自新书《Redis外围原理与实际》。本文波及Redis的很多概念,如事件循环器、ACL、Module、LUA、慢日志等,这些性能在作者新书《Redis外围原理与实际》做了详尽剖析,感兴趣的读者能够参考本书。 服务器定义提醒:本章代码如无非凡阐明,均在server.h、server.c中。 Redis中定义了server.h/redisServer构造体,存储Redis服务器信息,包含服务器配置项和运行时数据(如网络连接信息、数据库redisDb、命令表、客户端信息、从服务器信息、统计信息等数据)。 struct redisServer { pid_t pid; pthread_t main_thread_id; char *configfile; char *executable; char **exec_argv; ...}redisServer中的属性很多,这里不一一列举,等到剖析具体性能时再阐明相干的server属性。server.h中定义了一个redisServer全局变量: extern struct redisServer server;本书说到的server变量,如无非凡阐明,都是指该redisServer全局变量。例如,第1局部说过server.list_max_ziplist_size等属性,正是指该变量的属性。能够应用INFO命令获取服务器的信息,该命令次要返回以下信息: server:无关Redis服务器的惯例信息。clients:客户端连贯信息。memory:内存耗费相干信息。persistence:RDB和AOF长久化信息。stats:惯例统计信息。replication:主/正本复制信息。cpu:CPU耗费信息。commandstats:Redis 命令统计信息。cluster:Redis Cluster集群信息。modules:Modules模块信息。keyspace:数据库相干的统计信息。errorstats:Redis谬误统计信息。INFO命令响应内容中除了memory和cpu等统计数据,其余数据大部分都保留在redisServer中。 main函数server.c/main函数负责启动Redis服务: int main(int argc, char **argv) { ... // [1] server.sentinel_mode = checkForSentinelMode(argc,argv); // [2] initServerConfig(); ACLInit(); moduleInitModulesSystem(); tlsInit(); // [3] server.executable = getAbsolutePath(argv[0]); server.exec_argv = zmalloc(sizeof(char*)*(argc+1)); server.exec_argv[argc] = NULL; for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]); // [4] if (server.sentinel_mode) { initSentinelConfig(); initSentinel(); } // [5] if (strstr(argv[0],"redis-check-rdb") != NULL) redis_check_rdb_main(argc,argv,NULL); else if (strstr(argv[0],"redis-check-aof") != NULL) redis_check_aof_main(argc,argv); // more}【1】查看该Redis服务器是否以sentinel模式启动。【2】initServerConfig函数将redisServer中记录配置项的属性初始化为默认值。ACLInit函数初始化ACL机制,moduleInitModulesSystem函数初始化Module机制。【3】记录Redis程序可执行门路及启动参数,以便后续重启服务器。【4】如果以Sentinel模式启动,则初始化Sentinel机制。【5】如果启动程序是redis-check-rdb或redis-check-aof,则执行redis_check_rdb_main或redis_check_aof_main函数,它们尝试测验并修复RDB、AOF文件后便退出程序。Redis编译实现后,会生成5个可执行程序: ...

October 28, 2021 · 3 min · jiezi

关于redis:加锁了还有并发问题Redis分布式锁你真的了解

新接手的我的项目,偶然会呈现账不平的问题。之前的技术老大临走时给的解释是:排查了,没找到起因,之后太忙就没再解决,可能是框架的起因…… 既然我的项目交付到手中,这样的问题是必须要解决的。梳理了所有账务解决逻辑,最终找到了起因:数据库并发操作热点账户导致。就这这个问题,来聊一聊分布式系统下基于Redis的分布式锁。顺便也合成一下问题造成起因及解决方案。 起因剖析零碎并发量并不高,存在热点账户,但也不至于那么重大。问题的本源在于零碎架构设计,人为的制作了并发。场景是这样的:商户批量导入一批数据,零碎会进行前置解决,并对账户余额进行增减。 此时,另外一个定时工作,也会对账户进行扫描更新。而且对同一账户的操作散布到各个系统当中,热点账户也就呈现了。 针对此问题的解决方案,从架构层面能够思考将账务零碎进行抽离,集中在一个零碎中进行解决,所有的数据库事务及执行程序由账务零碎来兼顾解决。从技术方面来讲,则能够通过锁机制来对热点账户进行加锁。 本篇文章就针对热点账户基于分布式锁的实现形式进行具体的解说。 锁的剖析在Java的多线程环境下,通常有几类锁能够应用: JVM内存模型级别的锁,罕用的有:synchronized、Lock等;数据库锁,比方乐观锁,乐观锁等;分布式锁;JVM内存级别的锁,能够保障单体服务下线程的安全性,比方多个线程拜访/批改一个全局变量。但当零碎进行集群部署时,JVM级别的本地锁就无能为力了。 乐观锁与乐观锁像上述案例中,热点账户就属于分布式系统中的共享资源,咱们通常会采纳数据库锁或分布式锁来进行解决。 数据库锁,又分为乐观锁和乐观锁。 乐观锁是基于数据库(Mysql的InnoDB)提供的排他锁来实现的。在进行事务操作时,通过select ... for update语句,MySQL会对查问后果集中每行数据都增加排他锁,其余线程对该记录的更新与删除操作都会阻塞。从而达到共享资源的程序执行(批改); 乐观锁是绝对乐观锁而言的,乐观锁假如数据个别状况不会造成抵触,所以在数据进行提交更新的时候,才会正式对数据的抵触与否进行检测。如果抵触则返回给用户异样信息,让用户决定如何去做。乐观锁实用于读多写少的场景,这样能够进步程序的吞吐量。在乐观锁实现时通常会基于记录状态或增加version版本来进行实现。 乐观锁生效场景我的项目中应用了乐观锁,但乐观锁却生效了。这也是应用乐观锁时,常见的误区,上面来剖析一下。 失常应用乐观锁的流程: 通过select ... for update锁定记录;计算新余额,批改金额并存储;执行实现开释锁;常常犯错的解决流程: 查问账户余额,计算新余额;通过select ... for update锁定记录;批改金额并存储;执行实现开释锁;谬误的流程中,比方A和B服务查问到的余额都是100,A扣减50,B扣减40,而后A锁定记录,更新数据库为50;A开释锁之后,B锁定记录,更新数据库为60。显然,后者把前者的更新给笼罩掉了。解决的计划就是扩充锁的范畴,将锁提前到计算新余额之前。 通常乐观锁对数据库的压力是十分大的,在实践中通常会依据场景应用乐观锁或分布式锁等形式来实现。 上面进入正题,讲讲基于Redis的分布式锁实现。 Redis分布式锁实战演习这里以Spring Boot、Redis、Lua脚本为例来演示分布式锁的实现。为了简化解决,示例中Redis既承当了分布式锁的性能,也承当了数据库的性能。 场景构建集群环境下,对同一个账户的金额进行操作,根本步骤: 从数据库读取用户金额;程序修改金额;再将最新金额存储到数据库;上面从最后不加锁,不同步解决,逐渐推演出最终的分布式锁。 根底集成及类构建筹备一个不加锁解决的根底业务环境。 首先在Spring Boot我的项目中引入相干依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>账户对应实体类UserAccount: public class UserAccount { //用户ID private String userId; //账户内金额 private int amount; //增加账户金额 public void addAmount(int amount) { this.amount = this.amount + amount; } // 省略构造方法和getter/setter }创立一个线程实现类AccountOperationThread: public class AccountOperationThread implements Runnable { private final static Logger logger = LoggerFactory.getLogger(AccountOperationThread.class); private static final Long RELEASE_SUCCESS = 1L; private String userId; private RedisTemplate<Object, Object> redisTemplate; public AccountOperationThread(String userId, RedisTemplate<Object, Object> redisTemplate) { this.userId = userId; this.redisTemplate = redisTemplate; } @Override public void run() { noLock(); } /** * 不加锁 */ private void noLock() { try { Random random = new Random(); // 模仿线程进行业务解决 TimeUnit.MILLISECONDS.sleep(random.nextInt(100) + 1); } catch (InterruptedException e) { e.printStackTrace(); } //模仿数据库中获取用户账号 UserAccount userAccount = (UserAccount) redisTemplate.opsForValue().get(userId); // 金额+1 userAccount.addAmount(1); logger.info(Thread.currentThread().getName() + " : user id : " + userId + " amount : " + userAccount.getAmount()); //模仿存回数据库 redisTemplate.opsForValue().set(userId, userAccount); }}其中RedisTemplate的实例化交给了Spring Boot: ...

October 28, 2021 · 6 min · jiezi

关于redis:redis中lua脚本的简单使用

一、背景在应用redis的过程中,发现有些时候须要原子性去操作redis命令,而redis的lua脚本正好能够实现这一性能。比方: 扣减库存操作、限流操作等等。redis的pipelining尽管也能够一次执行一组命令,然而如果在这一组命令的执行过程中,须要依据上一步执行的后果做一些判断,则无奈实现。 二、应用lua脚本Redis中应用的是 Lua 5.1 的脚本标准,同时咱们编写的脚本的时候,不须要定义 Lua 函数。同时也不能应用全局变量等等。 1、lua脚本的格局和注意事项1、格局EVAL script numkeys key [key ...] arg [arg ...]127.0.0.1:6379> eval "return {KEYS[1],ARGV[1],ARGV[2]}" 1 key1 arg1 arg21) "key1"2) "arg1"3) "arg2"127.0.0.1:6379> 2、注意事项Lua脚本中的redis操作的key最好都是通过 KEYS来传递,而不要写死。否则在Redis Cluster的状况下可能有问题. 1、好的写法127.0.0.1:6379> eval "return redis.call('set',KEYS[1],'zhangsan')" 1 usernameOK127.0.0.1:6379> get username"zhangsan"redis命令操作的key是通过KEYS获取的。 2、差的写法127.0.0.1:6379> eval "return redis.call('set','username','zhangsan')" 0OK127.0.0.1:6379> get username"zhangsan"redis命令操作的key是间接写死的。 2、将脚本加载到redis中需要: 此处定义一个lua脚本,将输出的参数的值+1返回。 留神: 当咱们把 lua脚本加载到redis中,这个脚本并不会马上执行,而是会缓存起来,并且返回sha1校验和,前期咱们能够通过 EVALSHA 来执行这个脚本。 此处咱们记住这个脚本加载后返回的hash值,在下一步执行的时候须要用到。127.0.0.1:6379> script load "return tonumber(KEYS[1])+1""ef424d378d47e7a8b725259cb717d90a4b12a0de"127.0.0.1:6379>3、执行lua脚本1、通过eval执行127.0.0.1:6379> eval "return tonumber(KEYS[1]) + 1" 1 100(integer) 101127.0.0.1:6379>2、通过evalsha执行ef424d378d47e7a8b725259cb717d90a4b12a0de的值为上一步通过 script load加载脚本后获取的。 127.0.0.1:6379> evalsha ef424d378d47e7a8b725259cb717d90a4b12a0de 1 100(integer) 101127.0.0.1:6379>通过 evalsha 执行的益处是能够节俭带宽。如果咱们的lua脚本比拟长,程序在执行的时候将lua脚本发送到redis服务器则可能消耗的带宽多,如果发送的是hash值的话,则消耗的带宽少。 ...

October 27, 2021 · 3 min · jiezi

关于redis:如何使用-redis-实现限流

如何应用 redis 实现限流首发于 Dale’s blog 背景在工作中时常会遇到须要对接口或者某个调用进行限流的状况。也会遇到在限流的同时对 redis 数据进行一些解决,在波及到分布式的情景下,就须要操作的原子性。 限流算法支流的限流算法为以下四种: 计数器(固定窗口)滑动窗口(宰割计数器)漏桶算法令牌桶算法对于算法的解释,网上有很多好文章,在这里贴上罕用四种限流算法。 在本文中,探讨前两种也就是 计数器 以及 滑动窗口。 业务解释限流,是在业务中常常遇到的场景。例如:对接口的限流、对调用的限流,etc。 以对接口限流为例,流程如下: 申请打到服务器之后,须要先判断以后接口是否达到阈值,若: 达到阈值,则完结本次申请。未达到阈值,则 计数++,持续下一步调用。限流能够有很多中方法,如果只是小型的单机部署利用,则能够思考在内存中进行计数与操作。若是简单的我的项目且分布式部署的我的项目,能够思考应用 redis 进行计数。且限流的逻辑不肯定要限于 Java 代码中,也能够应用 lua 在 nginx 进行操作,例如赫赫有名的 openresty,同理其余网关服务也可实现。分布式业务中的限流首先剖析业务场景,在分布式部署的api场景中须要留神以下几点: 应用网关对api进行负载平衡,部署在不同服务器上的进行之间内存很难做到共享。基于限流的业务,是对整个零碎的某一个或者某一些接口进行限流,所以计数必须做到不同的过程都能够读取。对于计数的触发,是申请达到服务器上之后产生的,所以须要思考原子性。即:同一时刻,只有一个申请能够触发计数。这就对计数服务的要求提出了很高的并发要求。剖析 nginx + lua 的可行性nginx 罕用于申请的入口,在应用它的负载平衡之后,能够实现将申请散发到不同的服务上。应用 lua 对内存进行操作,仿佛能够实现上述要求(可行性待验证)。 然而,在理论状况中,一个零碎并肯定只会部署一个 nginx 作为入口。一方面是单机危险,另一方面是地理位置的不同,网络的不同对同一台机器的访问速度可能会有天差地别。所以,大家更喜爱应用 DNS 或者其余将申请达到多态 nginx 先做一层负载平衡。所以,单是 nginx + lua 并不能达到咱们的需要。 剖析 redis 的可行性redis 是基于内存的一种非关系型数据库,它的并发是经得住考验的,同时它也能够满足不同过程对雷同数据读取、批改的需要。 对于原子性,redis 操作天生反对原子性,而且 string 类型的 INCR(原子累加) 操作与 限流 业务又非常的符合。 redis 实现限流让咱们再回到一开始的流程,计数限流的操作有: 查问以后计数累加以后计数在分布式系统中,必须要时刻留神 原子性。在繁多过程中,咱们保持数据线程平安的方法是加锁,无论是可重入锁还是synchronized,其语义都是通知其余线程,这个数据(代码块)我当初征用了,你们等会再来。那在分布式系统中,咱们自然而然的能够想到分布式锁。 伪代码如下: Lock lock = getDistributedLock();try{ lock.lock(); // 从 redis 中获取计数 Integer count = getCountFromRedis(); if(count >= limit){ // 超过阈值,不予调用 return false; } // 未超过阈值,容许调用 incrRedisCount(); return true;}catch{ ...}finally{ lock.unlock();}乍一看,这种逻辑没有问题,但其实问题很大: ...

October 26, 2021 · 2 min · jiezi

关于redis:Redis的持久化

开发环境原本一片祥和(外表上看起来),就在为上线做筹备,验证零碎某一个关键性问题的时候,Redis集群忽然不能用了,并给出了上面这段十分“敌对”的提醒。 Caused by: redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.尽管第一次见到这个问题,然而这里呈现的RDB,只能联想到Redis的长久化。 于是,从长久化开始找问题。首先是确保生成的rdb文件没有问题,在服务器上执行命令查看rdb的合法性redis-check-rdb dump.rdb 顺带将aof也检测一下redis-check-aof appendonly.aof 如果aof文件存在问题,还能够通过命令修复(这里的修复是将有问题的局部删除)redis-check-aof --fix appendonly.aof 我啰嗦一下redis的长久化机制,Redis是折腾内存的,但内存有个问题,一旦断电,所有的数据都会失落。所以就须要有一个机制来解决这种可能呈现的状况。于是Redis呈现了两种长久化策略,别离是RDB和AOF。 RDBRDB就是指Redis DataBase,是Redis中默认开启的长久化策略,在配置文件中处于SNAPSHOTTING模块。 规定当满足某一种规定时,Redis就会fork一个子过程(一说Redis单线程),将数据写入到一个临时文件,当这个临时文件写完之后,再将原来的dump.rdb文件替换为这个临时文件,这就实现了一次长久化操作。Redis配置文件中默认提供了三种规定: ...

October 25, 2021 · 1 min · jiezi

关于redis:redis-十一redis之Bitmaps

[toc] 一、Bitmaps(位图)Bitmaps 并不是理论的数据类型,而是定义在String类型上的一个面向字节操作的汇合。因为字符串是二进制平安的块,他们的最大长度是512M,最适宜设置成2^32个不同字节。 Bitmaps 的最大劣势之一在存储信息时极其节约空间。例如,在一个以增量用户ID来标识不同用户的零碎中,记录用户的四十亿的一个独自bit信息(例如,要晓得用户是否想要接管最新的来信)仅仅应用512M内存。 1. getbit key offset获取位图指定索引的值 127.0.0.1:6379> set bitmap bigOK127.0.0.1:6379> getbit bitmap 0(integer) 0127.0.0.1:6379> getbit bitmap 1(integer) 1127.0.0.1:6379> getbit bitmap 2(integer) 1127.0.0.1:6379> 2. setbit key offset value给位图指定索引设置值,返回该索引地位的原始值 127.0.0.1:6379> setbit bitmap 7 1(integer) 0127.0.0.1:6379> get bitmap"cig"127.0.0.1:6379>3. bitcount key [start end]获取位图指定范畴(start到end,单位为字节,如果不指定就是获取全副)位值为1的个数。 127.0.0.1:6379> bitcount bitmap(integer) 13127.0.0.1:6379> setbit bitmap 8 1(integer) 0127.0.0.1:6379> bitcount bitmap(integer) 14127.0.0.1:6379>4. bitop and|or|not|xor destkey key [key...]做多个bitmap的and(交加)、or(并集)、not(非)、xor(异或)操作并将后果保留到destkey中。 127.0.0.1:6379> set hello goodOK127.0.0.1:6379> set world goodOK127.0.0.1:6379> bitop and hello_world hello world(integer) 4127.0.0.1:6379> get hello_world"good"127.0.0.1:6379> bitop or hello_world hello world(integer) 4127.0.0.1:6379> get hello_world"good"127.0.0.1:6379> bitop not hello_world hello(integer) 4127.0.0.1:6379> get hello_world"\x98\x90\x90\x9b"127.0.0.1:6379> bitop xor hello_world hello world(integer) 4127.0.0.1:6379> get hello_world"\x00\x00\x00\x00"127.0.0.1:6379> 5. bitpos key targetBit [start] [end] (起始版本:2.8.7)计算位图指定范畴(start到end,单位为字节,如果不指定就是获取全副)第一个偏移量对应的值等于targetBit的地位。 ...

October 22, 2021 · 1 min · jiezi

关于redis:redis-十redis之HyperLogLog

redis系列文章:https://liudongdong.top/categ...本篇起源:https://liudongdong.top/archi...公众号:雨中散步撒哈拉备注:欢送关注公众号,一起学习,共同进步!一、HyperLogLog基数统计HyperLogLog,上面简称为HLL,它是 LogLog 算法的升级版,作用是可能提供不准确的去重计数。存在以下的特点: 代码实现较难。可能应用极少的内存来统计巨量的数据,在 Redis 中实现的 HyperLogLog,只须要12K内存就能统计2^64个数据。计数存在肯定的误差,误差率整体较低。标准误差为 0.81% 。误差能够被设置辅助计算因子进行升高。略微对编程中的根底数据类型内存占用有理解的同学,应该会对其只须要12K内存就能统计2^64个数据而感到诧异。为什么这样说呢,上面咱们举下例子: 取 Java 语言来说,个别long占用8字节,而一字节有8位,即:1 byte = 8 bit,即long数据类型最大能够示意的数是:2^63-1。对应下面的2^64个数,假如此时有2^63-1这么多个数,从 0 ~ 2^63-1,依照long以及1k = 1024字节的规定来计算内存总数,就是:((2^63-1) * 8/1024)K,这是很宏大的一个数,存储空间远远超过12K。而 HyperLogLog 却能够用 12K 就能统计完。 1. pfadd元素增加将除了第一个参数以外的参数存储到以第一个参数为变量名的HyperLogLog构造中. 这个命令的一个副作用是它可能会更改这个HyperLogLog的外部来反映在每增加一个惟一的对象时预计的基数(汇合的基数). 如果一个HyperLogLog的预计的近似基数在执行命令过程中发了变动, PFADD 返回1,否则返回0,如果指定的key不存在,这个命令会主动创立一个空的HyperLogLog构造(指定长度和编码的字符串). 如果在调用该命令时仅提供变量名而不指定元素也是能够的,如果这个变量名存在,则不会有任何操作,如果不存在,则会创立一个数据结构(返回1). 理解更多HyperLogLog数据结构,请查阅PFCOUNT命令页面. 返回值integer-reply 如果 HyperLogLog 的外部被批改了,那么返回 1,否则返回 0 . 实例 127.0.0.1:6379> pfadd hll one two three four(integer) 1127.0.0.1:6379> pfadd hll five sex(integer) 1127.0.0.1:6379>2. pfcount元素统计(不反复统计)Hyperloglog 是用来做基数统计的数据类型 假如有如下一个数据集 { 1, 3, 5, 7, 9, 5 },那么这个数据集的基数就是 { 1, 3, 7, 9},基数即是值一个数据集中的不反复元素 ...

October 22, 2021 · 1 min · jiezi

关于redis:redis-九redis之Geospatial

redis系列文章:https://liudongdong.top/categ...本篇起源:https://liudongdong.top/archi...公众号:雨中散步撒哈拉备注:欢送关注公众号,一起学习,共同进步! 一、基本概念Geospatial类型,底层实现原理实现为zset类型!1. 应用什么样的地球模型(Earth model)?这只是假如地球是一个球体,因为应用的间隔公式是Haversine公式。这个公式仅实用于地球,而不是一个完满的球体。当在社交网站和其余大多数须要查问半径的利用中应用时,这些偏差都不算问题。然而,在最坏的状况下的偏差可能是0.5%,所以一些地理位置很要害的利用还是须要审慎思考。 2. 它是如何工作的?sorted set应用一种称为Geohash的技术进行填充。经度和纬度的位是交织的,以造成一个独特的52位整数. 咱们晓得,一个sorted set 的double score能够代表一个52位的整数,而不会失去精度。 这种格局容许半径查问查看的1 + 8个畛域须要笼罩整个半径,并抛弃元素以外的半径。通过计算该区域的范畴,通过计算所涵盖的范畴,从不太重要的局部的排序集的得分,并计算得分范畴为每个区域的sorted set中的查问。 GeoHash是一种地址编码方法。他可能把二维的空间经纬度数据编码成一个字符串 3. 能够做什么?查问某个坐标左近的坐标(左近的人性能)查问两点间的间隔......二、命令实际因为geo命令过少,不进行了分类,残缺命令请看官网,本篇命令请看附录! 中国诚恳坐标经纬度查问,进行查问中国城市具体坐标! 1. geoadd 增加经纬元素将指定的天文空间地位(纬度、经度、名称)增加到指定的key中。这些数据将会存储到sorted set这样的目标是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查问等操作。 该命令以采纳规范格局的参数x,y,所以经度必须在纬度之前。这些坐标的限度是能够被编入索引的,区域面积能够很靠近极点然而不能索引。具体的限度,由EPSG:900913 / EPSG:3785 / OSGEO:41001 规定如下: 无效的经度从-180度到180度。无效的纬度从-85.05112878度到85.05112878度。当坐标地位超出上述指定范畴时,该命令将会返回一个谬误。 返回值增加到sorted set元素的数目,但不包含已更新score的元素。 127.0.0.1:6379> geoadd china 116.408 39.904 beijing(integer) 1127.0.0.1:6379> geoadd china 121.445 31.213 shanghai 117.246 39.117 tianjing(integer) 2127.0.0.1:6379> zrange china 0 -11) "shanghai"2) "tianjing"3) "beijing"127.0.0.1:6379>2. geodist获取俩个元素之间直线间隔如果两个地位之间的其中一个不存在, 那么命令返回空值。 指定单位的参数 unit 必须是以下单位的其中一个: m 示意单位为米。km 示意单位为千米。mi 示意单位为英里。ft 示意单位为英尺。如果用户没有显式地指定单位参数, 那么 GEODIST 默认应用米作为单位。GEODIST 命令在计算间隔时会假如地球为完满的球形, 在极限状况下, 这一假如最大会造成 0.5% 的误差。 ...

October 21, 2021 · 2 min · jiezi

关于redis:redis-八redis之Zset

redis系列文章:https://liudongdong.top/categ...本篇起源:https://liudongdong.top/archi...公众号:雨中散步撒哈拉备注:欢送关注公众号,一起学习,共同进步! 一、基本概念Redis 有序汇合和汇合一样也是 string 类型元素的汇合,且不容许反复的成员。 不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为汇合中的成员进行从小到大的排序。 有序汇合的成员是惟一的,但分数(score)却能够反复。 汇合是通过哈希表实现的,所以增加,删除,查找的复杂度都是 O(1)。 汇合中最大的成员数为 232 - 1 (4294967295, 每个汇合可存储40多亿个成员)。 实例: 127.0.0.1:6379> zadd zset 1 one 2 two 3 three 4 four 5 five(integer) 5127.0.0.1:6379> zrange zset 0 -11) "one"2) "two"3) "three"4) "four"5) "five"127.0.0.1:6379>二、命令分类依据集体了解和便于学习,进行了简略分类!分为以下几类: 新增成员删除成员查问成员1. 新增成员zadd:新增成员2. 删除成员zrem:依据指定key进行删除zremrangebylex:依据指定汇合区间进行删除zremrangebyrank:依据指定排名区间进行删除zremrangebyscore:依据分数区间进行删除3. 查问成员zcard:查问汇合成员数量zcount:分数区间成员数量zlexcount:成员区间成员数量zscore:指定key和值,获取分数zrange:获取成员信息zrank:指定key和值,获取下标zrangebylex:指定汇合区间,获取列表zrangebyscore:指定分数区间,获取列表zrevrange:倒序展现列表zrevrangebyscore:依据分数区间,倒序展现列表zreverank:倒序获取成员下标三、命令实际1. 新增成员127.0.0.1:6379> keys *(empty list or set)127.0.0.1:6379> zadd zset 1 one 2 two 3 three 4 four 5 five(integer) 5127.0.0.1:6379> zrange zset 0 -11) "one"2) "two"3) "three"4) "four"5) "five"127.0.0.1:6379>2. 删除成员# 移除zset下的one127.0.0.1:6379> zrem zset one(integer) 1127.0.0.1:6379> zrange zset 0 -11) "two"2) "three"3) "four"4) "five"# 移除分数排名为1和2成员127.0.0.1:6379> zremrangebyrank zset 1 2(integer) 2127.0.0.1:6379> zrange zset 0 -11) "two"2) "five"# 移除分数区间为[1,2]127.0.0.1:6379> zrange zset 0 -11) "two"2) "five"127.0.0.1:6379> zremrangebyscore zset 1 2(integer) 1127.0.0.1:6379> zrange zset 0 -11) "five"127.0.0.1:6379> 3. 查问成员127.0.0.1:6379> zrange zset 0 -11) "one"2) "two"3) "three"4) "four"5) "five"# 查问汇合数量127.0.0.1:6379> zcard zset(integer) 5# 查问问题在[0,5]区间内的成员127.0.0.1:6379> zcount zset 0 5(integer) 5# 依据汇合区间进行统计127.0.0.1:6379> zlexcount zset - +(integer) 5# 依据key和value值,获取分数127.0.0.1:6379> zscore zset four"4"# 获取所有汇合信息127.0.0.1:6379> zrange zset 0 -11) "one"2) "two"3) "three"4) "four"5) "five"127.0.0.1:6379> zrange zset 0 31) "one"2) "two"3) "three"4) "four"# 依据key和值获取下标127.0.0.1:6379> zrank zset three(integer) 2# 依据汇合区间进行获取汇合成员127.0.0.1:6379> zrangebylex zset - +1) "one"2) "two"3) "three"4) "four"5) "five"# 依据分数区间进行获取汇合列表127.0.0.1:6379> zrangebyscore zset 0 41) "one"2) "two"3) "three"4) "four"# 倒序排列汇合127.0.0.1:6379> zrevrange zset 0 -11) "five"2) "four"3) "three"4) "two"5) "one"# 倒序排列依据分数区间127.0.0.1:6379> zrevrangebyscore zset 5 21) "five"2) "four"3) "three"4) "two"# 倒序排列,依据key和value,获取下标127.0.0.1:6379> zrevrank zset three(integer) 2127.0.0.1:6379>附录下表列出了 redis 有序汇合的根本命令: ...

October 21, 2021 · 2 min · jiezi

关于redis:解决Redis序列化Java8的LocalDateTime问题

在从Redis获取带有LocalDateTime类型属性的对象时,产生序列化和反序列化问题解决办法形式一:实体类上指定LocalDateTime的序列化器和反序列化器 @JsonDeserialize(using = LocalDateTimeDeserializer.class) //反序列化 @JsonSerialize(using = LocalDateTimeSerializer.class) //序列化 private LocalDateTime birthday;形式二:在Redis的配置类中指明序列化的形式 @Configuration public class RedisConfig { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); /** * JSON序列化 */ Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper = new ObjectMapper(); //勾销工夫转换格局,默认是工夫戳,同时须要设置要体现的工夫格局 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); mapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false); mapper.setDateFormat(new SimpleDateFormat("yyy-MM-dd HH:mm:ss")); //默认序列化没有实现,反序列化有实现 JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));// javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_FORMATTER));// javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(TIME_FORMATTER)); mapper.registerModule(javaTimeModule); //设置时区 mapper.setTimeZone(TimeZone.getDefault()); //设置格式化输入// MAPPER.enable(SerializationFeature.INDENT_OUTPUT); //设置格式化输入 mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); serializer.setObjectMapper(mapper);// StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(serializer); template.setHashKeySerializer(serializer); template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); return template; }}Good Mai http://www.goodmai.com/ ...

October 20, 2021 · 1 min · jiezi

关于redis:Redis数据结构-整数集合

整数汇合整数汇合是汇合键的底层实现之一,当一个汇合只蕴含整数值元素,并且这个汇合的元素数量不多时,Redis 就会应用整数汇合作为汇合键的底层实现。 整数汇合实现typedef struct intset { uint32_t encoding; uint32_t length; int8_t contents[];} intset;contents : 整数汇合的底层实现,整数汇合的每个元素都是 contents 数组的一个数组项,各个项在数据中按值的大小从小到大有序地排序,并且数组中不蕴含任何反复项。length : 记录了数组汇合蕴含的元素数量,即 contents 数组的长度。encoding : 数组的编码格局,决定了数组中保留的数据的真正格局。 encoding 可选的值有 INTSET_ENC_INT16 、INTSET_ENC_INT32、INTSET_ENC_INT64,别离对应 contents 中保留值的真正类型:int16_t、int32_t、int64_t 降级当将一个新元素增加到整数汇合时,新元素超出现有整数汇合中数据类型的最大值或者最小值时,整数汇合须要先进行降级,而后能力将新元素增加到整数汇合中。 整个降级过程分为三步: 依据新元素的类型,扩大整数汇合底层数组的空间大小,并为新元素调配空间。将底层数组现有的所有元素都转换成与新元素雷同的类型,并将类型转换后的元素搁置到正确的地位上,而且在搁置元素的过程中, 须要持续维持底层数组的有序性质不变。将新元素增加到底层数组外面。因为每次向数据汇合增加新元素时都可能会引起降级,而每次降级都须要对底层数组中已有的所有元素进行类型转换,所以向整数汇合增加新元素的工夫复杂度为 O(N) 降级的益处整数汇合的降级策略有两个益处,一个是晋升整数汇合的灵活性,另一个是尽可能地节约内存。 晋升灵活性为了防止类型谬误,通常不会将两种不同类型的值放在同一个数据结构外面。但因为整数汇合能够通过主动降级底层数组来适应新元素,所以在搁置值的时候,不须要思考值的类型,而不用放心呈现类型谬误。 节约内存通过主动降级,整数汇合在保留数字时,确保只会在有须要的时候才应用占内存较大的类型,尽可能节俭内存。 降级整数汇合不反对降级操作,一旦对数组进行了降级,编码就会始终放弃降级后的状态。

October 19, 2021 · 1 min · jiezi

关于redis:Redis数据结构-跳跃表

跳跃表跳跃表(skiplist)是一种有序数据结构,它通过在每个字节中维持多个指向其它节点的指针,从而达到快速访问节点的目标。 跳跃表是在有序链表的根底上实现的。 在有序链表中,查找元素时,只能逐个查找,工夫复杂度为 O(N),操作非常迟缓。为了优化,能够思考在链表其中的元素上建索引的形式,就失去了链表。 跳跃表在节点查问的工夫复杂度均匀为 O(logN),最坏状况下 O(N),还能够通过程序性操作来批量解决节点。 在大部分状况下,跳跃表的效率能够和均衡树相媲美,并且因为跳跃表的实现比均衡树要来得更为简略,所以有不少程序都应用跳跃表来代替均衡树。 Redis 应用跳跃表作为有序汇合键的底层实现之一,如果一个有序列表蕴含的元素数量比拟多,又或者有序汇合中元素的成员是比拟长的字符串时,Redis 就会应用跳跃表来作为有序汇合键的底层实现。 Redis 只在两个中央用到了跳跃表,一个是实现有序汇合键,另一个是在集群节点中用作外部数据结构。 跳跃表的实现跳跃表节点typedef struct zskiplistNode { struct zskiplistLevel { struct zskiplistNode *forward; unsigned int span; } level[]; struct zskiplistNode *backward; double score; robj *obj;} zskiplistNode;level : 蕴含多个元素,每个元素都蕴含一个指向其它节点的指针,程序能够通过这些层来放慢拜访其它节点的速度,一般来说,层的数量越多,拜访其它节点的速度就越快; 每次创立一个新跳跃表节点的时候,程序都依据幂次定律随机生成一个介于 1 和 32 之间的值作为 level 数组的大小,这个大小就是层的“高度” forward : 每个层都有一个指向表尾方向的后退指针,用于从表头向表尾方向拜访节点。span : 代表层的跨度,用于记录两个节点之间的间隔。两个节点之间的跨度越大, 它们相距就越远;指向 null 的所有后退指针的跨度都为 0,因为它们没有连向任何节点。backward : 节点的后退指针用于从表尾想表头方向拜访节点。跟能够一次跳过多个节点的后退指针不同,因为每个结点只有一个后退指针,所以每次只能后退至前一个节点。score : 跳跃表的所有节点都按分值从小到大来排序。obj : 是一个指针,指向一个字符串对象,而字符串对象则保留着一个 SDS 值。在同一个跳跃表中,各个节点保留的成员对象必须是惟一的,然而多个节点保留的分值却是能够雷同的。分值雷同的节点将依照成员对象在字典序中的大小来进行排序,成员对象较小的节点会排在后面(凑近表头的方向),而成员对象较大的节点则会排在前面(凑近表尾的方向)。 跳跃表typedef struct zskiplist { struct zskiplistNode *header, *tail; unsigned long length; int level;} zskiplist;header : 指向跳跃表的表头指针tail : 指向跳跃表的表尾指针length : 记录节点的数量level : 记录跳跃表中层数最大的节点的层数

October 18, 2021 · 1 min · jiezi

关于redis:面试必备100道Redis面试题

我把所有Java相干的面试题和答案都整顿成了PDF,并且带书签目录,浏览起来十分不便 面试题及答案PDF下载:https://www.hicxy.com/?p=2645 面试题及答案PDF下载:https://www.hicxy.com/?p=2645 面试题及答案PDF下载:https://www.hicxy.com/?p=2645 1. 为什么redis须要把所有数据放到内存中?Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的形式将数据写入磁盘。所以redis具备疾速和数据长久化的特色。如果不将数据放在内存中,磁盘I/O速度为重大影响redis的性能。在内存越来越便宜的明天,redis将会越来越受欢迎。 如果设置了最大应用的内存,则数据已有记录数达到内存限值后不能持续插入新值。 2. Redis中数据库默认是多少个db即作用?Redis默认反对16个数据库,能够通过配置databases来批改这一数字。客户端与Redis建设连贯后会主动抉择0号数据库,不过能够随时应用select命令更换数据库。 Redis反对多个数据库,并且每个数据库是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。 3. Redis事务其余实现基于Lua脚本,Redis能够保障脚本内的命令一次性、按程序地执行,其同时也不提供事务运行谬误的回滚,执行过程中如果局部命令运行谬误,剩下的命令还是会持续运行完基于两头标记变量,通过另外的标记变量来标识事务是否执行实现,读取数据时先读取该标记变量判断是否事务执行实现。但这样会须要额定写代码实现,比拟繁琐4. Redis 集群如何抉择数据库?Redis 集群目前无奈做数据库抉择,默认在 0 数据库。 5. 应用形式GEOADD key longitude latitude member [longitude latitude member ...]将给定的地位对象(纬度、经度、名字)增加到指定的key。其中,key为汇合名称,member为该经纬度所对应的对象。在理论使用中,当所需存储的对象数量过多时,可通过设置多key(如一个省一个key)的形式对对象汇合变相做sharding,防止单汇合数量过多。 胜利插入后的返回值: (integer) N其中N为胜利插入的个数。 前面的问题,大家能够先本人独立思考一下。 另外我把所有Java相干的面试题和答案都整理出来了,给大家参考一下 面试题及答案PDF下载:https://www.hicxy.com/?p=2645 面试题及答案PDF下载:https://www.hicxy.com/?p=2645 面试题及答案PDF下载:https://www.hicxy.com/?p=2645 6. Redis 回收过程如何工作的?7. 都有哪些方法能够升高Redis的内存应用状况呢?8. 定期删除策略9. Redis 内部结构10. Redis的内存占用状况怎么样?11. 为什么要做Redis分区?12. 锁互斥机制13. RDB和AOF的优缺点14. 事务管理(ACID)概述15. Redis 集群最大节点个数是多少?16. Redis长久化数据和缓存怎么做扩容?17. Redis与其余key-value存储有什么不同?18. Redis 有哪几种数据淘汰策略?19. Redis提供了哪几种长久化形式?20. Redis key 的过期工夫和永恒无效别离怎么设置?21. Redis 常见的性能问题和解决方案22. Redis 到底是怎么实现“左近的人”23. Redis 为什么是单线程的24. Redis 集群计划什么状况下会导致整个集群不可用?25. 在抉择缓存时,什么时候抉择 redis,什么时候抉择 memcached?26. Redis 集群计划应该怎么做?都有哪些计划?27. 生产环境中的 redis 是怎么部署的?28. Redis是单线程的,如何进步多核CPU的利用率?29. Redis key的过期工夫和永恒无效别离怎么设置?30. Redis 集群之间是如何复制的?31. 为什么要用 redis/为什么要用缓存32. 一个Redis实例最多能寄存多少的keys?List、Set、Sorted Set他们最多能寄存多少元素?33. Redis如何做内存优化?34. 如何抉择适合的长久化形式?35. 加锁机制36. 怎么了解 Redis 事务?37. Redis事务的三个阶段38. redis 过期策略都有哪些?LRU 算法晓得吗?39. Redis分区有什么毛病?40. Redis的过期策略以及内存淘汰机制41. Redis 次要耗费什么物理资源?42. Redis 有哪些适宜的场景?43. 定时删除策略44. redis 和 memcached 什么区别?为什么高并发下有时单线程的 redis 比多线程的memcached 效率要高?45. Redis罕用治理命令46. redis 常见数据结构以及应用场景剖析47. 缓存雪崩和缓存穿透问题解决方案48. Redis 事务相干的命令有哪几个?49. 晓得 redis 的长久化吗?底层如何实现的?有什么长处毛病?50. 为什么Redis的操作是原子性的,怎么保障原子性的?51. Redis为什么这么快52. 什么是Redis长久化?53. Reids的特点54. Redis 官网为什么不提供 Windows 版本?55. 解说下Redis线程模型56. Redis 集群会有写操作失落吗?为什么?57. 惰性删除策略58. MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保障 redis 中的数据都是热点数据?59. 你晓得有哪些Redis分区实现计划?60. Redis 反对哪几种数据类型?61. 什么是 Redis?简述它的优缺点?62. 一个字符串类型的值能存储最大容量是多少?63. Redis 的数据淘汰策略有哪些64. 为什么 Redis 须要把所有数据放到内存中?65. 缓存穿透、缓存击穿、缓存雪崩解决方案?66. Redis 集群的主从复制模型是怎么的?67. 批改配置不重启Redis会实时失效吗?68. 说说 Redis 哈希槽的概念?69. Redis有哪些优缺点70. Redis 的长久化机制是什么?各自的优缺点?71. 应用 Redis 做过异步队列吗,是如何实现的72. 对于大量的申请怎么样解决73. 如何保障缓存与数据库双写时的数据一致性?74. Redis 长久化机制(怎么保障 redis 挂掉之后再重启数据能够进行复原)75. Redis的内存用完了会产生什么?76. Redis事务反对隔离性吗77. Redis有哪些适宜的场景?78. Redis事务79. Redis 如何实现延时队列80. 如何抉择适合的长久化形式81. Redis的过期键的删除策略82. Reids三种不同删除策略83. Redis 中的管道有什么用?84. 缓存与数据库不统一怎么办85. Redis的数据类型,以及每种数据类型的应用场景86. 请用Redis和任意语言实现一段歹意登录爱护的代码,限度1小时内每用户Id最多只能登录5次。具体登录函数或性能用空函数即可,不必具体写出。87. Redis事务的概念88. Redis 与 memcached 相比有哪些劣势?89. Redis 集群计划应该怎么做?都有哪些计划?90. 分布式Redis是后期做还是前期规模上来了再做好?为什么?91. watch dog 主动延期机制92. 应用redis有哪些益处?93. 如何解决 Redis 的并发竞争 Key 问题94. Redis 常见性能问题和解决方案?95. 单线程的redis为什么这么快96. 应用过 Redis 做异步队列么,你是怎么用的?有什么毛病?97. 咱们晓得通过expire来设置key 的过期工夫,那么对过期的数据怎么解决呢?98. 应用 redis 如何设计分布式锁?说一下实现思路?应用 zk 能够吗?如何实现?这两种有什么区别?99. 什么是缓存穿透?如何防止?什么是缓存雪崩?何如防止?100. Redis事务保障原子性吗,反对回滚吗101. Redis 如何做内存优化?

October 18, 2021 · 2 min · jiezi

关于redis:Redis数据结构-字典

字典字典,即键值对的形象数据结构。 因为 Redis 应用的 C 语言没有内置这种数据结构,因而 Redis 构建了本人的字典实现,和其它高级编程语言个性一样,字典中的每个键都是举世无双的。 字典在 Redis 中的利用相当宽泛,比方 Redis 的数据库就是应用字典来作为底层实现的。 除此之外,字典还是哈希键的底层实现之一,当一个哈希键蕴含的键值对比拟多,又或者键值对中的元素都比拟长的字符串时,Redis 就会应用字典作为哈希键的底层实现。 字典的实现Redis 的字典应用哈希表作为底层实现,一个哈希表外面能够有多个哈希表节点,而每个哈希表节点就保留了字典中的一个键值对。 哈希表typedef struct dictht { dictEntity **table; unsigned long size; unsigned long sizemask; unsigned long used;}table : 是一个数组,数组中的每个元素都是一个指向 dictEntity 构造的指针,每个 dictEntity 构造保留着一个键值对。size : 记录了哈希表的大小,即 table 数组的大小sizemask : 哈希表大小掩码,用于计算索引值,总是等于 size - 1,这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引下面used : 记录了哈希表目前已有节点(键值对)的数量哈希表节点typedef struct dictEntity { void *key; union { void *val; uint64_t u64; int64_t s64; } v; struct dictEntity *next;} dictEntity;key : 保留着键值对中的键v : 保留着键值对中的值,其中键值对的值能够是一个指针,或者是一个 uint64_t 整数,又或者是一个 int64_t 属性next : 指向另一个哈希表节点的指针,这个指针能够将多个哈希值雷同的键值对连贯在一起,以此来解决键抵触的问题字典typedef struct dict { dictType *type; void *privdata; dictht ht[2]; int trehashidx;} dict;type : 属性是一个指向 dictType 构造的指针,每个 dictType 构造保留了一簇用于操作特定类型键值对的函数,Redis 会为用处不同的字典设置不同的类型特定函数 ...

October 17, 2021 · 2 min · jiezi

关于redis:Redis数据类型以及应用场景

Redis反对五种数据类型string(字符串)hash(哈希)list(列表)set(汇合)zset(sorted set:有序汇合)。 一、String阐明:string 类型是二进制平安的。redis 的 string 能够蕴含任何数据。比方jpg图片或者序列化的对象。string 类型是 Redis 最根本的数据类型,string 类型的值最大能存储 512MB。 场景:很少应用 二、Hash阐明:redis hash 是一个键值(key=>value)对汇合。redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特地适宜用于存储对象。 场景:存储、读取、批改用户属性 三、List(列表)阐明:Redis 列表是简略的字符串列表,依照插入程序排序。你能够增加一个元素到列表的头部(右边)或者尾部(左边)。 场景:音讯队列邮件存储(依照顺序存储) 四、set阐明:Redis 的 Set 是 string 类型的无序汇合。汇合是通过哈希表实现的,所以增加,删除,查找的复杂度都是 O(1)。增加一个 string 元素到 key 对应的 set 汇合中,胜利返回 1,如果元素曾经在汇合中返回 0。 场景:独特好友利用唯一性,统计拜访网站的所有独立ip好友举荐时,依据tag求交加,大于某个阈值就能够举荐 五、sorted set 有序汇合阐明:Redis 有序汇合和汇合一样也是string类型元素的汇合,且不容许反复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为汇合中的成员进行从小到大的排序。有序汇合的成员是惟一的,但分数(score)却能够反复。汇合是通过哈希表实现的,所以增加,删除,查找的复杂度都是O(1)。汇合中最大的成员数为 232 - 1 (4294967295, 每个汇合可存储40多亿个成员)。 场景:排行榜带权重的音讯队列

October 15, 2021 · 1 min · jiezi

关于redis:Redis-Redisson-分布式锁的应用和源码

1. 前言之前写过一篇《Redis分布式锁的实现》的文章,次要介绍的Redis分布式锁的原始性实现,外围是基于setnx来加锁,以及应用lua保障事务的原子性等。但毕竟比拟原始,须要依据不同的利用场景做不同的代码实现,也容易考虑不周。过后文章中就有提到 Redisson框架,刚好最近工作中又用的比拟多,这次就着重介绍。 Redisson 是架设在 Redis根底上的一个Java开发框架,底层基于 Netty框架,为使用者提供了一系列具备分布式个性的常用工具类。Redisson的性能十分丰盛,具体可参考 github中文wiki,但本文只介绍 Redisson分布式锁的性能。 2. 一般可重入锁2.1. 应用示例在SpringBoot我的项目通过Redisson来加锁非常容易,不须要像之前文章中一样写一大堆代码,框架屏蔽掉了很多细节。如下例: Config config = new Config();config.useSingleServer().setAddress("redis://ip:port").setPassword("password").setDatabase(0);RedissonClient redissonClient = Redisson.create(config);RLock lock = redissonClient.getLock("LOCK_KEY");long waitTime=500L;long leaseTime=15000L;boolean isLock;try { isLock = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); if (isLock) { // do something ... }} catch (InterruptedException e) { Thread.currentThread().interrupt();} finally { lock.unlock();}留神代码中 Config 并无限度,示例中是Redis单节点连贯,但实际上能够是哨兵模式、集群模式、主从模式等。 2.2. 源码解说后面例子中加锁用到了RLock接口,这里贴一下源码:org.redisson.api.RLock.java public interface RLock extends Lock, RLockAsync { String getName(); void lockInterruptibly(long var1, TimeUnit var3) throws InterruptedException; boolean tryLock(long var1, long var3, TimeUnit var5) throws InterruptedException; void lock(long var1, TimeUnit var3); boolean forceUnlock(); boolean isLocked(); boolean isHeldByThread(long var1); boolean isHeldByCurrentThread(); int getHoldCount(); long remainTimeToLive();}对于可重入锁,接口对应的实现办法在org.redisson.RedissonLock类外面,源码就不贴了,能够看到落到Redis时,理论的“加锁”和“解锁”过程也是一段lua脚本。 ...

October 14, 2021 · 4 min · jiezi

关于redis:Redis数据结构-链表

链表提供了高效的节点重排能力,以及程序性的节点拜访形式,并且能够通过增删节点来灵便调整链表的长度。 因为 Redis 应用的 C 语言并没有内置这种数据结构,所以 Redis 构建了本人的链表实现。 链表在 Redis 中的利用十分宽泛,比方列表键(list)的底层实现之一就是链表。当一个列表键蕴含了数据比拟多的元素,又或者列表中蕴含的元素都是比拟长的字符串时,Redis 就会应用链表作为列表键的底层实现。 当一个列表键只蕴含大量列表项,并且每个列表项是小整数值,或者长度比拟短的字符串,Redis 应用压缩列表来作为列表键的底层实现。除了列表键之外,公布订阅、慢查问、监视器等性能也用到了链表,Redis 服务器自身还应用链表来保留多个客户端的状态信息,曾经应用链表来构建客户端输入缓冲区。 链表节点(listNode)的实现typedef struct listNode { struct listNode *prev; struct listNode *next; void *value;}其中: prev:代表以后节点的前置节点next:代表以后节点多个的后置节点value:代表以后节点的值链表(list)的实现typedef struct list { listNode *head; listNode *tail; unsigned long len; void *(*dup) (void *ptr); void (*free) (void *ptr); int (*match) (void *ptr, void *key);}其中: head : 表头节点的指针tail : 表尾节点的指针len : 链表长度计数器dup : 用于复制链表节点所保留的值free : 用于开释链表节点所保留的值match : 用于比拟链表节点所保留的值和另一个值是否相等个性Redis 的链表实现中,节点都带有 prev 和 next 指针,且链表中也存储了 head(表头节点指针)和 tail(表尾节点指针),为双向链表,在获取某个节点的前置节点和后置节点的工夫复杂度都是 O(1),获取链表的表头节点和表尾节点工夫复杂度同样也是 O(1); ...

October 13, 2021 · 1 min · jiezi

关于redis:Redis数据结构-字符串

Redis 在存储字符串时,没有应用 C 语言传统的字符串示意,而是本人构建了一种名为简略动静字符串(simple dynamic string, SDS)的形象类型。 除了用来保留数据库中的字符串值之外,SDS 还被用作缓冲区:AOF 模块的 AOF 缓冲区,以及客户端状态中的输出缓冲区。 SDS定义在 Redis 中 SDS 的构造如下所示: struct sdshdr { int len; int free; char buf[];}其中: len : 记录 buf 数组中已应用字节的数量,即 SDS 中所保留字符串的长度;free : 记录 buf 数组中未应用字节的数量buf : 字节数组,用于保留字符串。SDS 遵循 C 语言字符串中以空字符结尾的常规,保留空字符串的 1 字节不计算在 SDS 的 len 中,但会占用一个字节空间,同样也不会统计在 free 属性中。 与 C 语言的区别1. 获取字符串长度C 语言中的字符串并不会记录本身的长度信息,所以,为了获取长度时,须要遍历整个字符串,间接遇到完结字符为止,整个操作的工夫复杂度为 O(N); SDS 中保留了字符串长度,在获取长度时,能够间接返回,工夫复杂度为 O(1)。 2. 字符串批改在 C 语言中,字符串所分配内存等于字符串长度 + 1,在执行批改操作时,都须要对内存重新分配。 C 语言中字符串拼接时,须要手动调配足够多的内存,而后再执行拼接操作,如果内存不够,则会产生缓冲区溢出;而如果执行缩短操作时,须要执行内存重调配来开释闲暇内存,否则就会生产内存透露; Redis 作为数据库,且罕用于对速度要求严苛、数据频繁批改的场合,如果每次批改,都须要执行一次内存调配的话,会对性能产生影响。 ...

October 12, 2021 · 1 min · jiezi

关于redis:实践篇-Redis客户端缓存在SpringBoot应用的探究

本文探索Redis最新个性--客户端缓存在SpringBoot上的利用。 Redis TrackingRedis客户端缓存机制基于Redis Tracking机制实现的。咱们先理解一下Redis Tracking机制。 为什么须要Redis TrackingRedis因为速度快、性能高,经常作为MySQL等传统数据库的缓存数据库。但因为Redis是近程服务,查问Redis须要通过网络申请,在高并发查问情景中不免造成性能损耗。所以,高并发利用通常引入本地缓存,在查问Redis前先查看本地缓存是否存在数据。如果应用MySQL存储数据,那么数据查问流程下图所示。 引入多端缓存后,批改数据时,各数据缓存端如何保证数据统一是一个难题。通常的做法是批改MySQL数据,并删除Redis缓存、本地缓存。当用户发现缓存不存在时,会从新查问MySQL数据,并设置Redis缓存、本地缓存。 在分布式系统中,某个节点批改数据后不仅要删除以后节点的本地缓存,还须要发送申请给集群中的其余节点,要求它们删除该数据的本地缓存,如下图所示。如果分布式系统中节点很多,那么该操作会造成不少性能损耗。 为此,Redis 6提供了Redis Tracking机制,对该缓存计划进行了优化。开启Redis Tracking后,Redis服务器会记录客户端查问的所有键,并在这些键产生变更后,发送生效音讯告诉客户端这些键已变更,这时客户端须要将这些键的本地缓存删除。基于Redis Tracking机制,某个节点批改数据后,不须要再在集群播送“删除本地缓存”的申请,从而升高了零碎复杂度,并进步了性能。 Redis Tracking的利用下表展现了Redis Tracking的根本应用(1)为了反对Redis服务器推送音讯,Redis在RESP2协定上进行了扩大,实现了RESP3协定。HELLO 3命令示意客户端与Redis服务器之间应用RESP3协定通信。留神:Redis 6.0提供了Redis Tracking机制,但该版本的redis-cli并不反对RESP3协定,所以这里须要应用Redis 6.2版本的redis-cli进行演示。(2)CLIENT TRACKING on命令的作用是开启Redis Tracking机制,尔后Redis服务器会记录客户端查问的键,并在这些键变更后推送生效音讯告诉客户端。生效音讯以invalidate结尾,前面是生效键数组。上表中的客户端 client1 查问了键 score 后,客户端 client2 批改了该键,这时 Redis 服务器会马上推送生效音讯给客户端 client1,但 redis-cli 不会间接展现它收到的推送音讯,而是在下一个申请返回后再展现该音讯,所以 client1 从新发送了一个 PING申请。 下面应用的非播送模式,另外,Redis Tracking还反对播送模式。在播送模式下,当变更的键以客户端关注的前缀结尾时,Redis服务器会给所有关注了该前缀的客户端发送生效音讯,不论客户端之前是否查问过这些键。 下表展现了如何应用Redis Tracking的播送模式。阐明一下CLIENT TRACKING命令中的两个参数:BCAST参数:启用播送模式。PREFIX参数:申明客户端关注的前缀,即客户端只关注cache结尾的键。 强调一下非播送模式与播送模式的区别:非播送模式:Redis服务器记录客户查问过的键,当这些键发生变化时,Redis发送生效音讯给客户端。播送模式:Redis服务器不记录客户查问过的键,当变更的键以客户端关注的前缀结尾时,Redis就会发送生效音讯给客户端。 对于Redis Tracking的更多内容,我曾经在新书《Redis外围原理与实际》中详细分析,这里不再赘述。 Redis客户端缓存既然Redis提供了Tracking机制,那么客户端就能够基于该机制实现客户端缓存了。 Lettuce实现Lettuce(6.1.5版本)曾经反对Redis客户端缓存(单机模式下),应用CacheFrontend类能够实现客户端缓存。 public static void main(String[] args) throws InterruptedException { // [1] RedisURI redisUri = RedisURI.builder() .withHost("127.0.0.1") .withPort(6379) .build(); RedisClient redisClient = RedisClient.create(redisUri); // [2] StatefulRedisConnection<String, String> connect = redisClient.connect(); Map<String, String> clientCache = new ConcurrentHashMap<>(); CacheFrontend<String, String> frontend = ClientSideCaching.enable(CacheAccessor.forMap(clientCache), connect, TrackingArgs.Builder.enabled()); // [3] while (true) { String cachedValue = frontend.get("k1"); System.out.println("k1 ---> " + cachedValue); Thread.sleep(3000); }}构建RedisClient。构建CacheFrontend。ClientSideCaching.enable开启客户端缓存,即发送“CLIENT TRACKING”命令给Redis服务器,要求Redis开启Tracking机制。最初一个参数指定了Redis Tracking的模式,这里用的是最简略的非播送模式。这里能够看到,通过Map保留客户端缓存的内容。反复查问同一个值,查看缓存是否失效。咱们能够通过Redis的Monitor命令监控Redis服务收到的命令,应用该命令就能够看到,开启客户端缓存后,Lettuce不会反复查问同一个键。而且咱们批改这个键后,Lettuce会从新查问这个键的最新值。 ...

October 12, 2021 · 2 min · jiezi

关于redis:Windows下安装Redis

RedisRedis的官网地址为:https://redis.io因为官网拜访较为迟缓,因而你能够拜访Redis中文网:http://www.redis.net.cn/Redis装置教程能够参考:Redis装置 | Redis教程 装置步骤一、下载Redis下载Redis压缩包:https://github.com/dmajkic/re...下载结束后,解压缩即可间接应用。依据本人的理论状况抉择32位/64位。局部文件如下: redis.windows.conf:配置文件redis-cli.exe:redis的客户端redis-server.exe:redis服务器端二、运行服务器端在Redis文件夹中关上命令行窗口,依据官网教程,运行以下命令: redis-server.exe redis.conf若提醒Failed to open the .conf file: redis.conf CWD=你的Redis门路,那是因为配置文件名为redis.windows.conf。 因而你须要在命令行输出的命令为: redis-server.exe redis.windows.conf若呈现谬误提醒:# QForkMasterInit: system error caught. error code=0x00000070, message=CreateFileMapping failed.: no space on device,那是因为你的配置文件中没有设置Redis的最大内存。 关上配置文件(redis.windows.conf),在文件开端增加以下两行: maxmemory 268435456maxheap 314572800 从新在命令行输出命令: redis-server.exe redis.windows.conf若呈现下图则示意服务器运行胜利: 能够看到服务器端的端口号和过程号。 三、运行客户端在Redis目录下开启另一个命令行窗口,输出以下命令: redis-cli.exe -h 127.0.0.1 -p 6379若呈现127.0.0.1:6379>则客户端开启胜利。 四、应用Redis应用set和get即可应用Redis存储键值对。如下: 应用set username shadowckk存储了一个键名为"username"、键值为"shadowckk"的键值对; 再应用get username取出"username"的值"shadowckk"。参考资料Redis装置 | Redis教程QForkMasterInit: system error caught. error code=0x000005af, message=VirtualAllocEx failed.: unknown

October 9, 2021 · 1 min · jiezi

关于redis:redis-批量删除记录

前段时间写的业务代码,在缓存的时候没有设置过期工夫,导致数据没有从新拉取,造成了一些数据问题。遂批改了代码。然而redis上曾经有近2000个key须要删除了,查问半天找到了批量删除的办法: 第一步:先查问初须要删除的key值: 筹备1.txt 外面是查问命令 ''' select 2keys finan* ''' 执行命令:./redis-cli -h 10.102.. -p 6379 -a ** <1.txt >2.txt 第二步将: 将上一步输入2.txt通过vim编辑,在每一个key的结尾退出del ,vim替换命令如下:%s/^/del cat 2.txt|./redis-cli -h 10.102.*.* -p 6379 -a **** >4.txt搞定!

October 8, 2021 · 1 min · jiezi

关于redis:redis分布式锁

加锁 生成一个非凡值(比方随机值+以后线程ID),记录在在ThreadLocal里。通过setnx向特定的key写入第一步生成的随机值,同时设置生效工夫,操作胜利则代表加锁胜利。SET resource_name my_random_value NX PX 30000阐明 设置一个生效工夫是为了防止死锁。写入一个随机值是为了防止加锁与解锁是同一线程写入随机值与设置生效工夫是同时的是为了保障加锁是原子操作。解锁 依据key获得随机值,从ThreadLocal里取出随机数,进行二者判断,删除redis上的key数据,要保障获取数据、判断随机值是否匹配及删除数据这三个操作是原子的,可通过lua脚本实现。 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0end潜在的问题:过期工夫设多长? 设置过小,有可能上一个线程还没执行完锁的逻辑锁就开释了,另一个线程获取锁而后呈现并发问题。设置过大,如果客户端断线了,这个锁要期待很长时间。这个问题Redisson提供了主动延期机制也就是watch dog。 Redisson中客户端一旦加锁胜利,如果客户端没有被动设置生效工夫就会启动一个watch dog看门狗。watch dog是一个后盾线程,会每隔10s(可配置)检查一下,如果客户端还持有锁key,那么就会一直的缩短锁key的生存工夫。如果客户端呈现了宕机,那客户端加的锁会怎么样呢?首先因为续锁线程(一个定时工作)和加锁线程同在一个JVM实例里,机器宕机后续锁线程也会挂掉所以不会呈现始终续期的情景。另外在在客户端没有设置生效工夫,Redisson会有一个默认的过期工夫30s(也能够通过Config.lockWatchdogTimeout配置进行调整),因为续期线程已不存在,所以到了工夫后天然会删除锁,这样就不会存在死锁的情景。另外须要阐明下,线程被中断或在续期过程中设置过期工夫失败都会开释锁。 这个计划是目前最优的分布式锁计划,然而如果在Redis集群环境下仍然存在问题: 因为Redis集群数据同步为异步,假如在Master节点获取到锁后未实现数据同步状况下Master节点crash,此时在新的Master节点仍然能够获取锁,所以多个Client同时获取到了锁。针对这个问题,之前提出了Redlock解决方案,不过当初已被官网弃用了,起因是Redisson RedLock 是基于联锁 MultiLock 实现的,然而应用过程中须要本人判断 key 落在哪个节点上,对使用者不是很敌对。这个是被弃用的阐明:Redlock弃用起因,上面是英文文档:https://github.com/redisson/r... 8.4. RedLockThis object is deprecated. RLock operations now propagated to all Redis slaves.参考的文档:RedissonBaseLock源码Redisson分布式锁源码剖析Redlock的阐明Redlock源码Redlock源码剖析Redisson锁续期定制化调整Redisson重连后WatchDog生效问题解决Redisson阐明文档

October 5, 2021 · 1 min · jiezi

关于redis:springdataredis-上百万的-QPS-压力太大连接失败我-TM-人傻了

大家好,咱们最近业务量暴涨,导致我最近始终 TM 人傻了。前几天早晨,发现因为业务压力激增,某个外围微服务新扩容起来的几个实例,在不同水平上,呈现了 Redis 连贯失败的异样: org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to redis.production.com at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1553) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1461) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.connection.lettuce.LettuceConnection.doGetAsyncDedicatedConnection(LettuceConnection.java:1027) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.connection.lettuce.LettuceConnection.getOrCreateDedicatedConnection(LettuceConnection.java:1013) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.connection.lettuce.LettuceConnection.openPipeline(LettuceConnection.java:527) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.connection.DefaultStringRedisConnection.openPipeline(DefaultStringRedisConnection.java:3245) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at jdk.internal.reflect.GeneratedMethodAccessor319.invoke(Unknown Source) ~[?:?] at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?] at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?] at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at com.sun.proxy.$Proxy355.openPipeline(Unknown Source) ~[?:?] at org.springframework.data.redis.core.RedisTemplate.lambda$executePipelined$1(RedisTemplate.java:318) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:222) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:189) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:176) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.core.RedisTemplate.executePipelined(RedisTemplate.java:317) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.core.RedisTemplate.executePipelined(RedisTemplate.java:307) ~[spring-data-redis-2.4.9.jar!/:2.4.9] at org.springframework.data.redis.core.RedisTemplate$$FastClassBySpringCGLIB$$81812bd6.invoke(<generated>) ~[spring-data-redis-2.4.9.jar!/:2.4.9] //省略一些堆栈Caused by: org.springframework.dao.QueryTimeoutException: Redis command timed out at org.springframework.data.redis.connection.lettuce.LettuceConnection.closePipeline(LettuceConnection.java:592) ~[spring-data-redis-2.4.9.jar!/:2.4.9] ... 142 more同时,也有业务调用 Redis 命令超时的异样: ...

October 4, 2021 · 4 min · jiezi

关于redis:redis学习之事务

事务的实现 事务是通过MULTI命令开始的,在非事务状态下客户端发送的命令会被立刻执行,而在事务状态下,除了EXEC/WATCH/DISCARD这几个命令外,redis会将命令保留在事务队列里。 typedef strcut redisClient{ //事务状态 multiState mstate; ...}redisClient;typedef struct multiState{ //事务队列 FIFO multiCmd *commands; //已入队命令计数 int counts;}multiState;typedef struct multiCmd{ //参数 robj *args; //参数数量 int argc; //命令指针 struct redisCommand *cmd;}multiCmd;当一个处于事务状态的客户端向服务器发送EXEC命令时,服务器会遍历这个客户端的事务队列并执行队列里的所有命令,最初将执行命令后的所有后果返回给客户端。 redis事务的ACID redis是具备原子性、一致性、隔离性的,不过不具备性情的持久性。 对于redis的事务性来说,事务队列中的命令要么全副执行要么一个都不执行,不过redis并不反对事务的回滚,这是它与关系性数据库很大的一个区别。redis是应用单线程的形式来执行事务,且事务在执行的过程中不会被中断,也就是说redis的事务总是以串行的形式运行的,这样其事务也就具备了隔离性。事务的持久性是指一个事务执行结束时,执行这个事务所得的后果曾经被保留到了永恒存储介质里,即便服务器在事务执行后停机,执行所得的后果也不会失落。redis将数据写入内存后,会依据长久化配置将数据同步到磁盘里,这其实是有一个滞后过程的,显然redis并不具备这一个性本文参考的有:黄健宏的《Redis设计与实现》一书

October 1, 2021 · 1 min · jiezi