微信 公司网站 怎么做模板下载失败

张小明 2026/1/12 0:50:07
微信 公司网站 怎么做,模板下载失败,网站备案资料查询,cms管理是什么一、Redis 概述1.1、什么是Redis?Redis#xff08;Remote Dictionary Server#xff09;是一个开源的内存键值存储数据库#xff0c;支持多种数据结构#xff0c;常被用作数据库、缓存和消息中间件。1.2、主要特性#xff1a;内存存储#xff1a;数据主要存储在内存中Remote Dictionary Server是一个开源的内存键值存储数据库支持多种数据结构常被用作数据库、缓存和消息中间件。1.2、主要特性内存存储数据主要存储在内存中提供极高的读写性能持久化支持支持 RDB 和 AOF 两种持久化机制数据结构丰富支持字符串、列表、集合、有序集合、哈希、位图等单线程模型采用单线程处理命令避免上下文切换开销高可用性支持主从复制、哨兵模式、集群模式1.3、适用场景缓存系统会话存储实时排行榜消息队列社交网络功能地理位置存储二、Redis 核心数据结构2.1 简单动态字符串SDSRedis 自定义的字符串结构替代 C 语言原生的 char[]。/** * Redis SDS (Simple Dynamic String) 的 Java 实现 */ public class SDS { private int len; // 已使用的字节数 private int free; // 未使用的字节数 private byte[] buf; // 字节数组 public SDS(String initial) { byte[] bytes initial.getBytes(); this.len bytes.length; this.free 0; this.buf bytes; } public SDS(String initial, int capacity) { byte[] bytes initial.getBytes(); this.len bytes.length; this.free capacity - len; this.buf new byte[capacity]; System.arraycopy(bytes, 0, buf, 0, len); } // O(1) 获取长度 public int length() { return len; } // 空间预分配 private void ensureCapacity(int needed) { if (free needed) { return; } int newLength len needed; // 空间预分配策略小于1MB时翻倍大于1MB时每次增加1MB if (newLength 1024 * 1024) { newLength * 2; } else { newLength 1024 * 1024; } byte[] newBuf new byte[newLength]; System.arraycopy(buf, 0, newBuf, 0, len); buf newBuf; free newLength - len; } // 追加字符串 public void append(String str) { byte[] bytes str.getBytes(); ensureCapacity(bytes.length); System.arraycopy(bytes, 0, buf, len, bytes.length); len bytes.length; free - bytes.length; } // 惰性空间释放 public void trim() { if (free len) { byte[] newBuf new byte[len]; System.arraycopy(buf, 0, newBuf, 0, len); buf newBuf; free 0; } } Override public String toString() { return new String(buf, 0, len); } }特点O(1) 时间复杂度获取字符串长度杜绝缓冲区溢出减少内存重分配次数空间预分配和惰性空间释放二进制安全2.2 字典Dict/Hash TableRedis 的键空间和哈希类型底层实现。数据结构import java.util.Arrays; import java.util.LinkedList; ​ /** * Redis 字典的 Java 简化实现 */ public class RedisDictK, V { // 哈希表节点 static class DictEntryK, V { K key; V value; DictEntryK, V next; // 链地址法解决冲突 DictEntry(K key, V value) { this.key key; this.value value; } } // 哈希表 static class DictHashTableK, V { DictEntryK, V[] table; int size; // 哈希表大小 int used; // 已有节点数量 SuppressWarnings(unchecked) DictHashTable(int size) { this.size size; this.used 0; this.table new DictEntry[size]; } } private DictHashTableK, V ht0; // 主哈希表 private DictHashTableK, V ht1; // rehash 时使用的哈希表 private int rehashIdx -1; // rehash 索引-1 表示不在 rehash public RedisDict() { this.ht0 new DictHashTable(4); // 初始大小 } // 简化版哈希函数实际 Redis 使用 MurmurHash2 private int hash(K key) { return Math.abs(key.hashCode()); } // 负载因子 private float loadFactor() { return (float) ht0.used / ht0.size; } // 扩容检查 private void checkResize() { // 触发扩容条件 if (rehashIdx -1 loadFactor() 1.0) { resize(ht0.size * 2); } } // 启动扩容/缩容 private void resize(int newSize) { ht1 new DictHashTable(newSize); rehashIdx 0; } // 渐进式 rehash private void rehashStep() { if (rehashIdx -1) return; // 每次 rehash 一个桶 while (rehashIdx ht0.size ht0.table[rehashIdx] null) { rehashIdx; } if (rehashIdx ht0.size) { DictEntryK, V entry ht0.table[rehashIdx]; while (entry ! null) { DictEntryK, V next entry.next; int idx hash(entry.key) % ht1.size; entry.next ht1.table[idx]; ht1.table[idx] entry; entry next; } ht0.table[rehashIdx] null; rehashIdx; } // rehash 完成 if (rehashIdx ht0.size) { ht0 ht1; ht1 null; rehashIdx -1; } } // 查找键值对考虑 rehash private DictEntryK, V findEntry(K key) { // 如果在 rehash先执行一步 rehash if (rehashIdx ! -1) { rehashStep(); } // 在 ht0 中查找 int idx hash(key) % ht0.size; DictEntryK, V entry ht0.table[idx]; while (entry ! null !entry.key.equals(key)) { entry entry.next; } if (entry ! null || rehashIdx -1) { return entry; } // 如果在 rehash也在 ht1 中查找 idx hash(key) % ht1.size; entry ht1.table[idx]; while (entry ! null !entry.key.equals(key)) { entry entry.next; } return entry; } public void put(K key, V value) { if (rehashIdx ! -1) { rehashStep(); } DictEntryK, V entry findEntry(key); if (entry ! null) { entry.value value; return; } // 新键添加到 ht0 或 ht1如果在 rehash DictHashTableK, V ht (rehashIdx ! -1) ? ht1 : ht0; int idx hash(key) % ht.size; DictEntryK, V newEntry new DictEntry(key, value); newEntry.next ht.table[idx]; ht.table[idx] newEntry; ht.used; checkResize(); } public V get(K key) { DictEntryK, V entry findEntry(key); return entry ! null ? entry.value : null; } }哈希算法MurmurHash2 算法冲突解决链地址法separate chaining扩容/缩容负载因子 used / size扩容条件服务器没有执行 BGSAVE/BGREWRITEAOF 且负载因子 ≥ 1或正在执行且负载因子 ≥ 5缩容条件负载因子 0.1渐进式 rehash为 ht[1] 分配空间维护 rehashidx 计数器初始为 0每次对字典执行操作时顺带将 ht[0] 中 rehashidx 索引上的所有键值对 rehash 到 ht[1]rehashidx完成后设为 -12.3 跳跃表Skip List有序集合ZSET的底层实现之一。import java.util.Random; /** * Redis 跳跃表的 Java 实现 */ public class SkipListT extends ComparableT { private static final double P 0.25; // 层数增长概率 private static final int MAX_LEVEL 32; // 最大层数 // 跳跃表节点 class SkipListNode { T value; double score; // 用于排序的分数Redis ZSET 使用 SkipListNode[] forwards; // 前进指针数组 SkipListNode backward; // 后退指针 SuppressWarnings(unchecked) SkipListNode(T value, double score, int level) { this.value value; this.score score; this.forwards new SkipListNode[level]; } } private SkipListNode header; // 头节点 private SkipListNode tail; // 尾节点 private int level; // 当前最大层数 private int length; // 节点数量 private Random random; public SkipList() { this.header new SkipListNode(null, 0, MAX_LEVEL); this.level 1; this.length 0; this.random new Random(); } // 随机生成层数幂次定律 private int randomLevel() { int level 1; while (random.nextDouble() P level MAX_LEVEL) { level; } return level; } // 插入节点 public void insert(T value, double score) { // 更新数组记录每层需要更新的节点 SkipListNode[] update new SkipListNode[MAX_LEVEL]; SkipListNode current header; // 从最高层开始查找插入位置 for (int i level - 1; i 0; i--) { while (current.forwards[i] ! null (current.forwards[i].score score || (current.forwards[i].score score current.forwards[i].value.compareTo(value) 0))) { current current.forwards[i]; } update[i] current; } // 生成随机层数 int newLevel randomLevel(); if (newLevel level) { for (int i level; i newLevel; i) { update[i] header; } level newLevel; } // 创建新节点 SkipListNode newNode new SkipListNode(value, score, newLevel); // 更新各层指针 for (int i 0; i newLevel; i) { newNode.forwards[i] update[i].forwards[i]; update[i].forwards[i] newNode; } // 更新后退指针 newNode.backward (update[0] header) ? null : update[0]; if (newNode.forwards[0] ! null) { newNode.forwards[0].backward newNode; } else { tail newNode; } length; } // 查找节点 public boolean contains(T value, double score) { SkipListNode current header; for (int i level - 1; i 0; i--) { while (current.forwards[i] ! null current.forwards[i].score score) { current current.forwards[i]; } } current current.forwards[0]; return current ! null current.score score current.value.equals(value); } // 范围查询 public void rangeQuery(double minScore, double maxScore) { SkipListNode current header; // 找到起始位置 for (int i level - 1; i 0; i--) { while (current.forwards[i] ! null current.forwards[i].score minScore) { current current.forwards[i]; } } current current.forwards[0]; System.out.print(范围查询结果: ); while (current ! null current.score maxScore) { System.out.print(current.value ( current.score ) ); current current.forwards[0]; } System.out.println(); } }特性平均 O(logN) 查找复杂度支持范围查询层高随机生成幂次定律越高的层出现概率越低2.4 压缩列表ZipList列表和哈希的底层实现之一小数据量时。内存布局zlbytes zltail zllen entry entry ... entry zlend特性连续内存块节省内存适用于小规模数据插入/删除需要内存重分配2.5 快速列表QuickListRedis 3.2 后列表的默认实现是双向链表 压缩列表的组合。import java.util.Arrays; ​ /** * Redis 快速列表QuickList的 Java 实现 * 快速列表是双向链表 压缩列表的组合结构 */ public class QuickListT { // 压缩列表的简化实现 static class ZipListT { private static final int ZIPLIST_MAX_ENTRIES 512; // 最大元素数量 private static final int ZIPLIST_MAX_VALUE 64; // 最大元素长度字节 private Object[] entries; // 存储元素 private int size; // 元素个数 private int capacity; // 容量 public ZipList() { this.capacity 8; // 初始容量 this.entries new Object[capacity]; this.size 0; } // 添加元素到末尾 public boolean push(T value) { // 检查是否超出限制 if (size ZIPLIST_MAX_ENTRIES || (value instanceof String ((String) value).length() ZIPLIST_MAX_VALUE)) { return false; // 需要转换为普通列表 } ensureCapacity(); entries[size] value; return true; } // 在指定位置插入 public boolean insert(int index, T value) { if (size ZIPLIST_MAX_ENTRIES || (value instanceof String ((String) value).length() ZIPLIST_MAX_VALUE)) { return false; } if (index 0 || index size) { throw new IndexOutOfBoundsException(); } ensureCapacity(); // 移动元素 System.arraycopy(entries, index, entries, index 1, size - index); entries[index] value; size; return true; } // 删除指定位置的元素 public T delete(int index) { if (index 0 || index size) { throw new IndexOutOfBoundsException(); } SuppressWarnings(unchecked) T removed (T) entries[index]; // 移动元素 int numMoved size - index - 1; if (numMoved 0) { System.arraycopy(entries, index 1, entries, index, numMoved); } entries[--size] null; return removed; } // 获取指定位置的元素 SuppressWarnings(unchecked) public T get(int index) { if (index 0 || index size) { throw new IndexOutOfBoundsException(); } return (T) entries[index]; } // 获取元素数量 public int size() { return size; } // 获取字节大小简化使用元素数量代替 public int getBytes() { return size * 16; // 估算值 } // 压缩列表是否已满 public boolean isFull() { return size ZIPLIST_MAX_ENTRIES; } // 确保容量 private void ensureCapacity() { if (size capacity) { capacity * 2; entries Arrays.copyOf(entries, capacity); } } // 转换为数组用于分裂 public Object[] toArray() { return Arrays.copyOf(entries, size); } // 从数组构建 public static T ZipListT fromArray(Object[] array) { ZipListT zl new ZipList(); for (Object obj : array) { SuppressWarnings(unchecked) T value (T) obj; zl.push(value); } return zl; } } // 快速列表节点 class QuickListNode { QuickListNode prev; // 前驱指针 QuickListNode next; // 后继指针 ZipListT zl; // 指向压缩列表 int count; // 压缩列表中元素个数 int sz; // 压缩列表的字节大小 public QuickListNode(ZipListT zl) { this.zl zl; this.count zl.size(); this.sz zl.getBytes(); } // 压缩节点将相邻的小压缩列表合并 public void compress() { // 这里简化实现实际 Redis 使用 LZF 压缩算法 // 可以记录压缩状态这里只做标记 } // 解压节点 public void decompress() { // 如果被压缩恢复原始数据 } } private QuickListNode head; // 头节点 private QuickListNode tail; // 尾节点 private long count; // 所有节点中元素总数 private int len; // quicklistNode 节点数 private int fill; // 单个节点最大容量元素个数 private int compressDepth; // 压缩深度两端不被压缩的节点数 // 配置常量 private static final int FILL_MAX 10000; // 最大填充因子 private static final int COMPRESS_MAX 10000; // 最大压缩深度 public QuickList() { this(512, 0); // 默认每个节点最多 512 个元素不压缩 } public QuickList(int fill, int compressDepth) { this.fill Math.min(fill, FILL_MAX); this.compressDepth Math.min(compressDepth, COMPRESS_MAX); this.len 0; this.count 0; // 创建初始的空压缩列表节点 ZipListT initialZl new ZipList(); this.head new QuickListNode(initialZl); this.tail head; this.len 1; } // 在头部添加元素 public void lpush(T value) { // 尝试添加到头节点的压缩列表 if (head.zl.push(value)) { head.count; head.sz head.zl.getBytes(); count; return; } // 头节点已满创建新节点 ZipListT newZl new ZipList(); newZl.push(value); QuickListNode newNode new QuickListNode(newZl); // 更新链表结构 newNode.next head; head.prev newNode; head newNode; len; count; // 检查是否需要压缩 checkCompress(); } // 在尾部添加元素 public void rpush(T value) { // 尝试添加到尾节点的压缩列表 if (tail.zl.push(value)) { tail.count; tail.sz tail.zl.getBytes(); count; return; } // 尾节点已满创建新节点 ZipListT newZl new ZipList(); newZl.push(value); QuickListNode newNode new QuickListNode(newZl); // 更新链表结构 newNode.prev tail; tail.next newNode; tail newNode; len; count; // 检查是否需要压缩 checkCompress(); } // 从头部弹出元素 public T lpop() { if (count 0) { return null; } // 从头节点的压缩列表获取元素 T value head.zl.delete(0); head.count--; head.sz head.zl.getBytes(); count--; // 如果头节点为空删除它除非是最后一个节点 if (head.count 0 len 1) { QuickListNode next head.next; next.prev null; head next; len--; } return value; } // 从尾部弹出元素 public T rpop() { if (count 0) { return null; } // 从尾节点的压缩列表获取元素 T value tail.zl.delete(tail.count - 1); tail.count--; tail.sz tail.zl.getBytes(); count--; // 如果尾节点为空删除它除非是最后一个节点 if (tail.count 0 len 1) { QuickListNode prev tail.prev; prev.next null; tail prev; len--; } return value; } // 在指定位置插入元素 public void insert(int index, T value) { if (index 0 || index count) { throw new IndexOutOfBoundsException(); } // 如果插入到末尾使用 rpush if (index count) { rpush(value); return; } // 找到对应的节点和位置 NodePosition pos findNodeByIndex(index); // 尝试插入到压缩列表 if (pos.node.zl.insert(pos.localIndex, value)) { pos.node.count; pos.node.sz pos.node.zl.getBytes(); count; // 如果插入后压缩列表过大需要分裂 if (pos.node.count fill) { splitNode(pos.node); } return; } // 插入失败可能是压缩列表限制需要特殊处理 handleInsertFallback(pos, value); } // 获取指定位置的元素 public T get(int index) { if (index 0 || index count) { throw new IndexOutOfBoundsException(); } NodePosition pos findNodeByIndex(index); return pos.node.zl.get(pos.localIndex); } // 查找节点和局部索引 private NodePosition findNodeByIndex(int index) { // 决定从头还是尾开始查找优化 boolean fromHead index count / 2; QuickListNode current fromHead ? head : tail; int accumulated 0; if (fromHead) { // 从头开始 while (current ! null accumulated current.count index) { accumulated current.count; current current.next; } return new NodePosition(current, index - accumulated); } else { // 从尾开始 accumulated (int) count; while (current ! null) { accumulated - current.count; if (accumulated index) { return new NodePosition(current, index - accumulated); } current current.prev; } } throw new IndexOutOfBoundsException(); } // 节点位置信息 private class NodePosition { QuickListNode node; int localIndex; NodePosition(QuickListNode node, int localIndex) { this.node node; this.localIndex localIndex; } } // 分裂节点当压缩列表过大时 private void splitNode(QuickListNode node) { int splitIndex node.count / 2; // 获取后半部分的元素 Object[] secondHalf new Object[node.count - splitIndex]; for (int i splitIndex; i node.count; i) { secondHalf[i - splitIndex] node.zl.get(i); } // 创建新节点 ZipListT newZl ZipList.fromArray(secondHalf); QuickListNode newNode new QuickListNode(newZl); // 更新原节点 for (int i node.count - 1; i splitIndex; i--) { node.zl.delete(i); } node.count splitIndex; node.sz node.zl.getBytes(); // 插入新节点到链表中 newNode.next node.next; newNode.prev node; if (node.next ! null) { node.next.prev newNode; } node.next newNode; // 如果分裂的是尾节点更新尾指针 if (node tail) { tail newNode; } len; // 检查是否需要压缩 checkCompress(); } // 处理插入失败的情况压缩列表限制 private void handleInsertFallback(NodePosition pos, T value) { // 这里简化处理创建新的压缩列表节点 ZipListT newZl new ZipList(); newZl.push(value); QuickListNode newNode new QuickListNode(newZl); // 插入新节点到链表中 newNode.prev pos.node.prev; newNode.next pos.node; if (pos.node.prev ! null) { pos.node.prev.next newNode; } else { head newNode; // 更新头指针 } pos.node.prev newNode; len; count; } // 检查并执行压缩 private void checkCompress() { if (compressDepth 0 || len compressDepth * 2) { return; // 不压缩或节点太少 } // 压缩两端之外的节点 QuickListNode node head; int skipped 0; // 跳过两端的节点 for (int i 0; i compressDepth node ! null; i) { node node.next; skipped; } // 压缩中间节点 while (node ! null skipped len - compressDepth) { node.compress(); node node.next; skipped; } } // 获取元素总数 public long size() { return count; } // 获取节点数量 public int nodeCount() { return len; } // 遍历所有元素 public void forEach(ConsumerT consumer) { QuickListNode current head; while (current ! null) { for (int i 0; i current.zl.size(); i) { consumer.accept(current.zl.get(i)); } current current.next; } } // 转换为数组 SuppressWarnings(unchecked) public T[] toArray() { if (count 0) { return (T[]) new Object[0]; } T[] result (T[]) new Object[(int) count]; int index 0; QuickListNode current head; while (current ! null) { for (int i 0; i current.zl.size(); i) { result[index] current.zl.get(i); } current current.next; } return result; } // 调试信息 public void printStructure() { System.out.println( QuickList 结构信息 ); System.out.println(总元素数: count); System.out.println(节点数: len); System.out.println(填充因子: fill); System.out.println(压缩深度: compressDepth); QuickListNode current head; int nodeIndex 0; while (current ! null) { System.out.printf(节点 %d: 元素数%d, 大小%d字节, 前驱%s, 后继%s\n, nodeIndex, current.count, current.sz, current.prev ! null ? 有 : 无, current.next ! null ? 有 : 无); current current.next; } } } ​ // 函数式接口Java 8 FunctionalInterface interface ConsumerT { void accept(T t); }2.6 整数集合IntSet集合的底层实现之一当元素都是整数且数量较少时。/** * Redis 整数集合的 Java 实现 */ public class IntSet { private static final int ENCODING_INT16 2; private static final int ENCODING_INT32 4; private static final int ENCODING_INT64 8; private int encoding; // 编码方式 private int length; // 元素个数 private byte[] contents; // 元素数组 public IntSet() { this.encoding ENCODING_INT16; this.length 0; this.contents new byte[0]; } // 添加元素可能触发升级 public void add(int value) { // 检查是否需要升级编码 int requiredEncoding requiredEncoding(value); if (requiredEncoding encoding) { upgradeAndAdd(value); } else { // 插入排序保持有序 insertValue(value); } } // 确定值所需的编码 private int requiredEncoding(int value) { if (value Short.MIN_VALUE value Short.MAX_VALUE) { return ENCODING_INT16; } else if (value Integer.MIN_VALUE value Integer.MAX_VALUE) { return ENCODING_INT32; } else { return ENCODING_INT64; } } // 升级编码并添加新元素 private void upgradeAndAdd(int value) { int oldEncoding encoding; int newEncoding requiredEncoding(value); // 创建新的字节数组 byte[] newContents new byte[length * newEncoding]; // 复制并转换旧数据 for (int i 0; i length; i) { int oldValue getValue(i, oldEncoding); setValue(newContents, i, oldValue, newEncoding); } this.encoding newEncoding; this.contents newContents; // 添加新值 insertValue(value); } // 在有序位置插入值 private void insertValue(int value) { // 查找插入位置 int pos 0; while (pos length getValue(pos, encoding) value) { pos; } // 如果值已存在不插入 if (pos length getValue(pos, encoding) value) { return; } // 创建新数组 byte[] newContents new byte[(length 1) * encoding]; // 复制前半部分 System.arraycopy(contents, 0, newContents, 0, pos * encoding); // 插入新值 setValue(newContents, pos, value, encoding); // 复制后半部分 if (pos length) { System.arraycopy(contents, pos * encoding, newContents, (pos 1) * encoding, (length - pos) * encoding); } this.contents newContents; this.length; } // 获取指定位置的整数值 private int getValue(int index, int enc) { int offset index * enc; switch (enc) { case ENCODING_INT16: return (contents[offset] 8) | (contents[offset 1] 0xFF); case ENCODING_INT32: return (contents[offset] 24) | ((contents[offset 1] 0xFF) 16) | ((contents[offset 2] 0xFF) 8) | (contents[offset 3] 0xFF); default: return 0; // 简化处理 } } // 设置指定位置的整数值 private void setValue(byte[] array, int index, int value, int enc) { int offset index * enc; switch (enc) { case ENCODING_INT16: array[offset] (byte) ((value 8) 0xFF); array[offset 1] (byte) (value 0xFF); break; case ENCODING_INT32: array[offset] (byte) ((value 24) 0xFF); array[offset 1] (byte) ((value 16) 0xFF); array[offset 2] (byte) ((value 8) 0xFF); array[offset 3] (byte) (value 0xFF); break; } } public boolean contains(int value) { // 二分查找 int left 0, right length - 1; while (left right) { int mid left (right - left) / 2; int midValue getValue(mid, encoding); if (midValue value) { return true; } else if (midValue value) { left mid 1; } else { right mid - 1; } } return false; } }升级机制当新元素无法用当前编码保存时自动升级编码。三、Redis 对象系统3.1 RedisObject所有 Redis 数据都被封装为 redisObject。/** * RedisObject 的 Java 表示 */ public class RedisObjectT { // 数据类型枚举 enum RedisType { STRING, LIST, SET, ZSET, HASH } // 编码方式枚举 enum Encoding { RAW, // 简单动态字符串 INT, // 整数编码的字符串 HT, // 字典 ZIPLIST, // 压缩列表 LINKEDLIST, // 双向链表 INTSET, // 整数集合 SKIPLIST // 跳跃表 } private RedisType type; // 数据类型 private Encoding encoding; // 编码方式 private long lru; // LRU 信息或 LFU 计数 private int refCount; // 引用计数 private T ptr; // 指向实际数据结构的指针 // 对象共享池缓存 0-9999 的整数对象 private static final RedisObjectInteger[] INTEGER_SHARED_OBJECTS; static { INTEGER_SHARED_OBJECTS new RedisObject[10000]; for (int i 0; i 10000; i) { INTEGER_SHARED_OBJECTS[i] new RedisObject( RedisType.STRING, Encoding.INT, i ); } } public RedisObject(RedisType type, Encoding encoding, T ptr) { this.type type; this.encoding encoding; this.lru System.currentTimeMillis(); // 简化使用时间戳 this.refCount 1; // 初始引用计数为1 this.ptr ptr; } // 创建字符串对象根据内容选择编码 public static RedisObjectObject createStringObject(String value) { try { // 尝试解析为整数 int intValue Integer.parseInt(value); // 如果在共享范围内返回共享对象 if (intValue 0 intValue 10000) { INTEGER_SHARED_OBJECTS[intValue].refCount; return INTEGER_SHARED_OBJECTS[intValue]; } return new RedisObject(RedisType.STRING, Encoding.INT, intValue); } catch (NumberFormatException e) { // 对于短字符串可能使用 EMBSTR这里简化处理 if (value.length() 44) { // EMBSTR 编码Redis 中字符串对象和SDS结构分配在连续内存 return new RedisObject(RedisType.STRING, Encoding.RAW, value); } else { return new RedisObject(RedisType.STRING, Encoding.RAW, value); } } } // 创建列表对象根据条件选择编码 public static RedisObjectObject createListObject(Object[] elements) { boolean useZiplist true; // 检查是否满足压缩列表条件 if (elements.length 512) { useZiplist false; } else { for (Object element : elements) { if (element instanceof String) { String str (String) element; if (str.length() 64) { useZiplist false; break; } } } } if (useZiplist) { return new RedisObject(RedisType.LIST, Encoding.ZIPLIST, elements); } else { return new RedisObject(RedisType.LIST, Encoding.LINKEDLIST, new java.util.LinkedList(java.util.Arrays.asList(elements))); } } // 增加引用计数 public void incrRefCount() { this.refCount; } // 减少引用计数为0时释放内存 public void decrRefCount() { this.refCount--; if (refCount 0) { // 释放资源这里简化处理 this.ptr null; } } // 获取对象类型 public String getTypeDescription() { switch (type) { case STRING: return string; case LIST: return list; case SET: return set; case ZSET: return zset; case HASH: return hash; default: return unknown; } } // 获取对象编码 public String getEncodingDescription() { switch (encoding) { case RAW: return raw; case INT: return int; case HT: return hashtable; case ZIPLIST: return ziplist; case LINKEDLIST: return linkedlist; case INTSET: return intset; case SKIPLIST: return skiplist; default: return unknown; } } }3.2 类型与编码映射Redis 数据结构Java 对应类主要区别SDS (字符串)String/StringBuilderSDS 有长度属性和空间预分配字典 (哈希表)HashMap/LinkedHashMapRedis 使用渐进式 rehash跳跃表TreeMap跳跃表 vs 红黑树支持范围查询压缩列表ArrayList (简化版)连续内存节省内存整数集合HashSetInteger自动升级编码保持有序快速列表LinkedList ArrayList组合结构平衡内存和性能四、Redis 持久化原理4.1 RDBRedis Database原理在指定时间间隔内生成数据集的快照。触发方式手动执行 SAVE阻塞或 BGSAVE后台配置文件中的 save 规则主从复制时文件结构--------------------------------------------------------- | REDIS | RDB版本 | 数据 | EOF(结束符) | CRC64校验和 | ---------------------------------------------------------优缺点优点紧凑的单文件适合备份和灾难恢复恢复速度快缺点可能丢失最后一次快照后的数据大数据量时 fork 操作耗时4.2 AOFAppend Only File原理记录每个写操作命令以日志形式追加。写回策略always同步写回每个写命令都立即 fsynceverysec每秒写回默认no由操作系统决定重写机制目的压缩 AOF 文件大小原理创建新的 AOF 文件包含重建当前数据集所需的最少命令触发手动执行 BGREWRITEAOF 或自动触发auto-aof-rewrite-percentage优缺点优点数据安全性更高最多丢失一秒数据缺点文件通常比 RDB 大恢复速度较慢五、Redis 单线程模型5.1 事件循环import java.io.IOException; import java.nio.channels.*; import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; /** * Redis 单线程事件循环的 Java 简化实现 */ public class RedisEventLoop { // 事件类型 enum FileEventType { READABLE, // 可读事件 WRITABLE // 可写事件 } // 文件事件结构 static class FileEvent { SelectableChannel channel; FileEventType type; ConsumerSelectableChannel handler; FileEvent(SelectableChannel channel, FileEventType type, ConsumerSelectableChannel handler) { this.channel channel; this.type type; this.handler handler; } } // 时间事件结构 static class TimeEvent { long id; // 事件ID long when; // 执行时间毫秒 long period; // 执行周期0表示一次性 Runnable handler; // 事件处理器 TimeEvent(long id, long when, long period, Runnable handler) { this.id id; this.when when; this.period period; this.handler handler; } } // I/O 多路复用器选择简化版只使用 Selector private Selector selector; // 事件处理器映射 private MapSelectableChannel, FileEvent readEvents new HashMap(); private MapSelectableChannel, FileEvent writeEvents new HashMap(); // 时间事件 private MapLong, TimeEvent timeEvents new ConcurrentHashMap(); private long nextTimeEventId 1; private PriorityQueueTimeEvent timeEventQueue new PriorityQueue(Comparator.comparingLong(e - e.when)); // 停止标志 private volatile boolean stop false; // 事件循环线程 private Thread eventLoopThread; // 延迟执行的任务队列用于线程安全 private BlockingQueueRunnable deferredTasks new LinkedBlockingQueue(); public RedisEventLoop() throws IOException { this.selector Selector.open(); } /** * 主事件循环 */ public void main() { eventLoopThread Thread.currentThread(); System.out.println(Redis 事件循环启动线程: eventLoopThread.getName()); while (!stop) { try { // 1. 执行延迟任务在事件处理前执行 processDeferredTasks(); // 2. 处理时间事件Redis 的 beforeSleep 钩子 processTimeEvents(); // 3. 等待并处理 I/O 事件 processFileEvents(); // 4. 再次处理延迟任务在事件处理后执行 processDeferredTasks(); } catch (Exception e) { System.err.println(事件循环异常: e.getMessage()); e.printStackTrace(); } } System.out.println(事件循环停止); } /** * 处理延迟任务线程安全 */ private void processDeferredTasks() { Runnable task; while ((task deferredTasks.poll()) ! null) { try { task.run(); } catch (Exception e) { System.err.println(延迟任务执行失败: e.getMessage()); } } } /** * 处理时间事件 */ private void processTimeEvents() { long now System.currentTimeMillis(); // 处理所有到期的定时事件 while (!timeEventQueue.isEmpty()) { TimeEvent event timeEventQueue.peek(); if (event.when now) { break; } // 移除并执行 timeEventQueue.poll(); try { // 执行事件处理器 event.handler.run(); } catch (Exception e) { System.err.println(时间事件执行失败: e.getMessage()); } // 如果是周期性事件重新加入队列 if (event.period 0) { event.when now event.period; timeEventQueue.offer(event); } else { // 一次性事件从映射中移除 timeEvents.remove(event.id); } } } /** * 处理文件事件I/O 事件 */ private void processFileEvents() throws IOException { // 计算最近的定时事件时间 long timeout calculateTimeout(); // 等待 I/O 事件 if (timeout 0) { selector.select(timeout); } else { selector.selectNow(); } // 处理就绪的 SelectionKey SetSelectionKey selectedKeys selector.selectedKeys(); IteratorSelectionKey iterator selectedKeys.iterator(); while (iterator.hasNext()) { SelectionKey key iterator.next(); iterator.remove(); if (key.isValid()) { // 处理可读事件 if (key.isReadable()) { FileEvent event readEvents.get(key.channel()); if (event ! null) { try { event.handler.accept(event.channel); } catch (Exception e) { System.err.println(读事件处理失败: e.getMessage()); closeChannel(key.channel()); } } } // 处理可写事件 if (key.isWritable()) { FileEvent event writeEvents.get(key.channel()); if (event ! null) { try { event.handler.accept(event.channel); } catch (Exception e) { System.err.println(写事件处理失败: e.getMessage()); closeChannel(key.channel()); } } } } } } /** * 计算 select 超时时间 */ private long calculateTimeout() { if (timeEventQueue.isEmpty()) { return 1000; // 没有定时事件等待1秒 } long now System.currentTimeMillis(); TimeEvent nextEvent timeEventQueue.peek(); long nextTime nextEvent.when; if (nextTime now) { return 0; // 立即执行 } return Math.min(nextTime - now, 1000); } /** * 注册文件事件 */ public void createFileEvent(SelectableChannel channel, FileEventType type, ConsumerSelectableChannel handler) { // 确保在事件循环线程中执行 if (Thread.currentThread() eventLoopThread) { doCreateFileEvent(channel, type, handler); } else { deferredTasks.offer(() - doCreateFileEvent(channel, type, handler)); } } private void doCreateFileEvent(SelectableChannel channel, FileEventType type, ConsumerSelectableChannel handler) { try { channel.configureBlocking(false); FileEvent event new FileEvent(channel, type, handler); if (type FileEventType.READABLE) { readEvents.put(channel, event); channel.register(selector, SelectionKey.OP_READ); } else { writeEvents.put(channel, event); channel.register(selector, SelectionKey.OP_WRITE); } System.out.println(注册 type 事件: channel); } catch (Exception e) { System.err.println(注册文件事件失败: e.getMessage()); } } /** * 删除文件事件 */ public void deleteFileEvent(SelectableChannel channel, FileEventType type) { if (Thread.currentThread() eventLoopThread) { doDeleteFileEvent(channel, type); } else { deferredTasks.offer(() - doDeleteFileEvent(channel, type)); } } private void doDeleteFileEvent(SelectableChannel channel, FileEventType type) { try { if (type FileEventType.READABLE) { readEvents.remove(channel); SelectionKey key channel.keyFor(selector); if (key ! null) { key.interestOps(key.interestOps() ~SelectionKey.OP_READ); } } else { writeEvents.remove(channel); SelectionKey key channel.keyFor(selector); if (key ! null) { key.interestOps(key.interestOps() ~SelectionKey.OP_WRITE); } } System.out.println(删除 type 事件: channel); } catch (Exception e) { System.err.println(删除文件事件失败: e.getMessage()); } } /** * 创建时间事件 */ public long createTimeEvent(long delayMs, long periodMs, Runnable handler) { long id nextTimeEventId; long when System.currentTimeMillis() delayMs; TimeEvent event new TimeEvent(id, when, periodMs, handler); deferredTasks.offer(() - { timeEvents.put(id, event); timeEventQueue.offer(event); System.out.println(创建时间事件 # id , 执行时间: when); }); return id; } /** * 删除时间事件 */ public void deleteTimeEvent(long id) { deferredTasks.offer(() - { TimeEvent event timeEvents.remove(id); if (event ! null) { timeEventQueue.remove(event); System.out.println(删除时间事件 # id); } }); } /** * 关闭通道 */ private void closeChannel(SelectableChannel channel) { try { readEvents.remove(channel); writeEvents.remove(channel); channel.close(); System.out.println(关闭通道: channel); } catch (IOException e) { System.err.println(关闭通道失败: e.getMessage()); } } /** * 停止事件循环 */ public void stop() { this.stop true; if (selector.isOpen()) { selector.wakeup(); // 唤醒阻塞的 select } } /** * 获取事件循环统计信息 */ public MapString, Object getStats() { MapString, Object stats new HashMap(); stats.put(readEvents, readEvents.size()); stats.put(writeEvents, writeEvents.size()); stats.put(timeEvents, timeEvents.size()); stats.put(deferredTasks, deferredTasks.size()); stats.put(isRunning, !stop); return stats; } }5.2 I/O 多路复用Redis 根据操作系统选择不同的 I/O 多路复用技术epollLinuxkqueueFreeBSD/ macOSselect跨平台效率较低5.3 文件事件与时间事件文件事件处理客户端连接、命令请求等时间事件处理 serverCron 等定时任务六、Redis 内存管理6.1 内存分配器Redis 默认使用 jemalloc 或 libc 的 malloc可通过配置选择。6.2 内存回收策略过期键删除策略惰性删除访问键时检查过期时间如过期则删除定期删除定期随机检查数据库删除过期键内存淘汰策略maxmemory-policynoeviction不淘汰返回错误默认allkeys-lru所有键中移除最近最少使用的键volatile-lru在设置了过期时间的键中移除最近最少使用的键allkeys-random随机移除任意键volatile-random在设置了过期时间的键中随机移除volatile-ttl移除即将过期的键6.3 对象共享Redis 会共享 0-9999 的整数值对象以节省内存。七、Redis 高可用架构7.1 主从复制复制流程从服务器发送 SYNC 命令主服务器执行 BGSAVE 生成 RDB 文件同时缓冲期间的写命令主服务器发送 RDB 文件给从服务器从服务器载入 RDB 文件主服务器发送缓冲的写命令给从服务器后续命令通过命令传播保持同步Redis 2.8 的 PSYNC完整重同步和 SYNC 相同部分重同步主从断开后只同步断开期间的命令7.2 哨兵模式Sentinel功能监控检查主从服务器是否正常运行通知通过 API 向管理员发送故障通知自动故障转移主服务器故障时自动升级从服务器为主服务器配置提供者客户端连接时返回当前主服务器地址选举过程Raft 算法变种7.3 Redis Cluster数据分片采用哈希槽hash slot共 16384 个槽。节点通信Gossip 协议每个节点维护集群元数据。故障检测主观下线PFAIL一个节点认为另一个节点不可用客观下线FAIL超过半数的节点认为某节点不可用故障转移从节点通过选举成为新的主节点。八、Redis 使用场景与最佳实践8.1 典型应用场景缓存热点数据缓存减轻数据库压力会话存储分布式会话共享排行榜使用 ZSET 实现实时排行榜消息队列使用 LIST 实现简单的消息队列分布式锁使用 SETNX 实现分布式锁计数器使用 INCR 实现原子计数器社交关系使用 SET 实现关注列表、共同好友等8.2 性能优化建议键名设计简短但可读使用冒号分隔层次批量操作使用 pipeline 减少网络往返避免大键单个键值不宜过大建议 ≤ 1MB合理设置过期时间避免内存泄漏监控慢查询使用 SLOWLOG 命令连接池客户端使用连接池减少连接开销8.3 监控指标内存使用率命中率keyspace_hits / (keyspace_hits keyspace_misses)连接数每秒命令数持久化状态主从同步延迟九、Redis 7.x 新特性函数FunctionsRedis 脚本的替代品支持持久化存储多部分 AOF将 AOF 分为基础文件和增量文件ACL 改进更细粒度的权限控制命令性能优化许多命令的性能得到提升共享池改进更好的内存共享机制
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

万户高端网站建设科技之全球垄断

拒绝学习枯燥!AI带你解锁校园趣味学习新姿势✨还在为背古诗、记公式头大?遇到作业难题没人讲解?策划校园活动没灵感?举个手!🙋♂️很多学生都觉得学习“枯燥又费力”,校园活动策划“无从下手”。…

张小明 2026/1/7 17:44:54 网站建设

网站数据库如何导入数据库文件wordpress怎么用vue

还在为看不懂外文游戏而苦恼吗?XUnity.AutoTranslator让你的游戏瞬间变中文!这款强大的Unity游戏翻译工具能够实时翻译游戏中的文本内容,彻底消除语言障碍,让每款游戏都成为你的专属中文版本。 【免费下载链接】XUnity.AutoTransl…

张小明 2026/1/7 14:51:35 网站建设

北京网站建设团队WordPress 云锁

在 Java 中,String 是最常用的核心类之一,用于表示不可变的字符序列,属于 java.lang 包(无需手动导入)。以下从核心特性、常用操作、内存原理、常见陷阱等维度全面解析:一、核心特性1. 不可变性&#xff08…

张小明 2026/1/5 19:01:42 网站建设

潍坊建设局网站wordpress文章目录

互联网档案馆(Archive.org)作为全球最大的数字图书馆,保存了无数珍贵的网络历史资料。internetarchive项目提供了一个强大的Python和命令行接口,让开发者能够通过简单的命令直接访问和管理这个庞大的数字宝库。无论您是研究人员、…

张小明 2026/1/5 21:38:30 网站建设

旅游网站设计模板贵阳网站建设费用多少网帮你

动态界面与布局引擎:构建灵活用户界面的全面指南 1. 动态用户界面概述 在Windows编程中,动态向窗口添加控件是常见需求。在.NET中,运行时创建控件与设计时创建控件差异不大,因为每个控件都是通过代码创建的。比如,Visual Studio在设计时添加和配置控件时,会生成相应代码…

张小明 2026/1/5 21:38:31 网站建设

洪山网站建设自适应网站建设多少钱

At.js测试实战指南:5个步骤掌握高质量自动化测试 【免费下载链接】At.js Add Github like mentions autocomplete to your application. 项目地址: https://gitcode.com/gh_mirrors/at/At.js At.js是一个功能强大的jQuery插件,能够为你的应用程序…

张小明 2026/1/5 21:38:32 网站建设