聊聊 Redis 使用场景
缓存 会话缓存 时效性 访问频率 计数器 社交列表 记录用户判定信息 交集、并集和差集 热门列表与排行榜 最新动态 消息队列 十一项场景清单覆盖了 Redis 的常见用法,但清单式罗列缺两块:每个场景对应哪种数据结构与命令组合(落地能力),以及哪些场景今天已有更优替代(边界认知)。本文把清单升级为”场景 → 结构 → 命令 → 注意事项”的四元组手册。 场景-结构-命令映射表 场景 结构 核心命令 注意 缓存(对象/页面) String SET/GET/SETEX 过期策略+穿透/击穿/雪崩三防 会话缓存 String/Hash SET session:uid / HSET 多节点共享会话;敏感信息脱敏存储 时效性(过期业务) String TTL / Zset EXPIRE/TTL、ZRANGEBYSCORE 延迟队列用 Zset score=时间戳 访问频率限流 String+TTL / Zset INCR+EXPIRE、滑动窗口 ZADD+...
Redis 内存淘汰机制
Redis 内存淘汰指的是用户存储的一些键被可以被 Redis 主动地从实例中删除,从而产生读 miss 的情况,那么 Redis 为什么要有这种功能?这就是我们需要探究的设计初衷。Redis 最常见的两种应用场景为缓存和持久存储,首先要明确的一个问题是内存淘汰策略更适合于那种场景?是持久存储还是缓存? 假设我们有一个 Redis 服务器,服务器物理内存大小为 1G 的,我们需要存在 Redis 中的数据量很小,这看起来似乎足够用很长时间了,随着业务量的增长,我们放在 Redis 里面的数据越来越多了,数据量大小似乎超过了 1G,但是应用还可以正常运行,这是因为操作系统的可见内存并不受物理内存限制,而是虚拟内存,物理内存不够用没关系,操作系统会从硬盘上划出一片空间用于构建虚拟内存,比如32位的操作系统的可见内存大小为 2^32,而用户空间的可见内存要小于 2^32 很多,大概是 3G 左右。好了,我们庆幸操作系统为我们做了这些,但是我们需要知道这背后的代价是不菲的,不合理的使用内存有可能发生频繁的 swap,频繁 swap 的代价是惨痛的。所以回过头来看,作为有追求的程序员,我们...
Redis 内部结构
Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。 type :代表一个 value 对象具体是何种数据类型。 encoding :是不同数据类型在 redis 内部的存储方式,比如:type=string 代表 value 存储的是一个普通字符串,那么对应的 encoding 可以是 raw 或者是 int,如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:”123” “456”这样的字符串。 vm 字段:只有打开了 Redis 的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。 Redis 使用 redisObject 来表示所有的 key/value 数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给 Redis 不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用。 redisObject 的 type/encoding 二层结构是理解 Redis 内存行为的钥匙...
Redis 有哪些类型
在 Redis 中有五种数据类型 String:字符串 Hash:字典 List:列表 Set:集合 Sorted Set:有序集合 背出这五种只算及格。真正决定使用效果的是两个更深的问题:每种类型底层用什么编码实现(决定内存与性能),以及什么业务场景该选哪种类型(决定设计质量)。本文按”类型 → 底层编码 → 典型场景”三层展开。 String:不只是字符串String 是最基础的类型,值可以是字符串、整数或浮点数,底层编码有三种: int:值是能 long 表示的整数,直接存数值,可做原子自增 embstr:短字符串(≤44 字节),对象头与数据一次分配,读快但修改会转 raw raw:长字符串,SDS 实现,O(1) 取长度、二进制安全 典型场景:缓存对象(序列化后整存)、计数器(INCR 原子性天然适合点赞数/限流)、分布式锁(SET key val NX PX)。注意整存整取意味着局部字段更新要全量重写,字段多时优先考虑 Hash。 Hash:对象的结构化存储Hash 是 field-value 字典,底层编码: ziplist(listpack):...
聊聊 ElasticSearch 使用场景
全文搜索,这个是用的最多的。加上分词插件、拼音插件什么的可以做成强大的全文搜索引擎。 数据库,挺奇葩的用法,因为 ES 存数相同数据,更费空间,不过确实不错,因为他的强大统计分析汇总能力,再加上分布式 P2P 扩展能力,现在硬件又那么便宜,所以就有人拿来当数据库了。 在线统计分析引擎,日志系统,LogStash,不用解释了吧; 可以实时动态分析数据,很是爽。 三大场景(搜索、另类数据库、日志分析)是 ES 的经典画像,但生产落地还需要:ES 为什么快/为什么费空间(机制层)、与 MySQL 的分工边界、以及”当数据库用”的代价清单。本文补全机制与边界两层。 机制层:ES 的能力来源 倒排索引(Lucene):全文检索与关键词过滤的 O(词项) 基础(见倒排索引篇) 列式存储 doc_values:聚合/排序走列存(按字段连续存储),而非倒排——这是”统计分析强”的物理基础 分布式模型:索引分 shard(Lucene 实例),shard 分 primary/replica;查询 scatter-gather(各 shard 并行查再归并);水平扩...
倒排索引
倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。有两种不同的反向索引形式: 一条记录的水平反向索引(或者反向档案索引)包含每个引用单词的文档的列表。 一个单词的水平反向索引(或者完全反向索引)又包含每个单词在一个文档中的位置。 定义与两种形式讲清了”是什么”,但倒排索引的完整图景还包括:构建流程(分词→词典→posting)、与正排索引的对照、以及工程上的压缩与更新问题。本文补全,让倒排索引从名词变成可解释的机制。 正排 vs 倒排:一次对照 正排(forward index):文档 → 词列表(doc1: [ai, chip, export])。”查文档含哪些词” O(1),但”查含某词的文档”需扫全部文档 倒排(inverted index):词 → 文档列表(chip: [doc1, doc7, doc99])。把”查词”的扫描成本预计算成索引,与数据库索引”用空间换查询路径”同一思想 倒排索引的两部分: ...
JDK8 新特性
Java 基础 - JDK8 新特性概述以下列出两点重要特性: Lambda表达式(匿名函数) Stream多线程并行数据处理(重要) 新特性 接口的默认方法只需要使用default关键字即可,这个特性又叫扩展方法 Lambda表达式 Functional接口函数式接口是指仅仅只包含一个抽象方法的接口,每一个该类型的Lambda表达式都会被匹配到这个抽象方法。你只需要给你的接口添加@FunctionalInterface注解。 使用::双冒号关键字来传递方法(静态方法和非静态方法) Predicate接口和Lambda表达式 Function接口 Function有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法。 compose方法表示在某个方法之前执行 andThen方法表示在某个方法之后执行 注意:compose和andThen方法调用之后都会把对象自己本身返回,这可以 方便链式编程 Supplier接口,返回一个任意泛型的值,和Function接口不同的是该接口 没有任何参数 Consumer接口,接收一个任意泛型的值,和Function接口不同...
双亲委派模型
类加载器加载类的开放性类加载器(ClassLoader) 是Java语言的一项创新,也是Java语言流行的一个重要原因,在类加载的第一阶段“加载”过程中需要通过一个类的全限定名来回去定义此类的二进制字节流,完成这个动作的代码块就是 类加载器,这一动作是放在Java虚拟机外部去实现的,以便让应用程序自己去决定如何获取所需的类。 虚拟机规范并没有指明二进制字节流要从一个Class文件获取,或者说根本没有指明从哪里获取、怎样获取。这种开放使得Java在很多领域得到充分运用,例如: 从ZIP包中读取,这很常见,成为JAR,EAR,WAR格式的基础 从网络中获取,最典型的运用就是Applet 运用时计算生成,最典型的是动态代理技术,在java.lang.reflect.Proxy中,就是用了ProxyGrenerator.generateProxyClass 来为特定接口生成形式为“*$Proxy”的代理类的二进制字节流 有其它文件生成,最典型的JSP应用,由JSP文件生成对应的Class类 类加载器与类的唯一性类加载器虽然只用于实现类的加载动作,但是对于任意一个类,都需要由它的类加载...
死信、延迟、重试队列
死信队列DLQ(Dead Letter Queue),死信队列。当一个消息在队列中变成死信之后,它能被重新发送到 DLQ 中,与 DLQ 绑定的队列就是死信队列。 什么情况下消息会变成死信 消息被拒绝:消费者 basic.nack/basic.reject 且 requeue=false 消息过期:TTL 到期仍未被消费 队列达到最大长度:x-max-length 超限后队头消息被挤出 以 RabbitMQ 为例:生产者把消息投递到普通队列,队列设置 TTL 为 10 秒;10 秒内无人消费,消息过期,若配置了 x-dead-letter-exchange,过期消息会被重新投递到死信交换机,最终落入死信队列等待人工或补偿程序处理。 死信队列的工程价值死信队列不是”垃圾堆”,而是失败消息的隔离区与取证现场: 隔离:失败消息不再阻塞正常队列的消费进度 取证:消息体、失败次数、原始队列等头信息保留,便于排查 补偿:运维或定时任务可对死信消息修复后重放(replay) 生产建议:死信队列必须配监控告警(堆积即报警),否则它只是把问题藏起来而不是解决问题。 延迟队列延迟队列...
Zookeeper-假死闹裂
该问题就是服务集群因为网络震荡导致的多主多从问题,解决方案就是设置服务切换的超时时间,但也同时会导致无法达到高可用的要求。 一句话版本到此为止。但”假死”与”脑裂”其实是两个相关却不同的故障形态,面试和排障中都值得拆开讲清楚:它们各自的成因、表现、以及 ZooKeeper 如何用会话与配额机制把它们约束在可控范围内。 假死:活着但”被认为死了”假死(zombie/半死状态)指节点进程还活着、甚至还在对外服务,但由于 GC 长停顿、磁盘 IO 阻塞、网络分区等原因,无法在会话超时时间内与集群通信,于是被集群判定死亡:Leader 被重新选举、临时节点被清理、锁被释放。 危险在于:假死节点恢复后仍可能认为自己持有锁或仍是 Leader,继续写数据——这就是假死引发的”双主”写冲突。ZooKeeper 的约束手段: 会话(session)+ 临时节点(ephemeral node):会话过期,临时节点立即删除,锁/注册信息随之失效 Fencing Token(隔离令牌):ZooKeeper 的 zxid/epoch 单调递增,假死 Leader 恢复后携...















