蚂蚁集团春招秋招面试高频基础题 - Java 社招真题系列(02)

第一题: Redis数据

1.1 基础数据类型

// Redis核心数据类型演示
public class RedisDataTypes {
    
    // String: 最基础的键值存储
    @Test
    public void stringOperations() {
        jedis.set("user:name", "张三");           // 字符串
        jedis.incr("page:views");               // 整数计数
        jedis.set("price", "99.99");            // 浮点数
        jedis.setbit("online:users", 100, true); // 位操作
        
        // 应用场景:缓存、计数器、分布式锁、Session存储
    }
    
    // Hash: 结构化数据存储
    @Test
    public void hashOperations() {
        jedis.hset("user:1001", "name", "李四");
        jedis.hset("user:1001", "age", "25");
        jedis.hset("user:1001", "email", "lisi@example.com");
        
        Map<String, String> user = jedis.hgetAll("user:1001");
        // 应用场景:用户信息、商品属性、配置项
    }
    
    // List: 双端队列
    @Test
    public void listOperations() {
        // 消息队列
        jedis.lpush("queue:email", "email1@test.com");
        jedis.rpush("queue:email", "email2@test.com");
        
        // 阻塞获取
        List<String> result = jedis.blpop(5, "queue:email");
        
        // 应用场景:消息队列、时间轴、最新列表
    }
    
    // Set: 无序唯一集合
    @Test
    public void setOperations() {
        jedis.sadd("tags:java", "spring", "mybatis", "redis");
        jedis.sadd("tags:python", "django", "flask", "redis");
        
        // 交集运算
        Set<String> common = jedis.sinter("tags:java", "tags:python");
        
        // 应用场景:标签系统、好友关系、去重、抽奖
    }
    
    // Sorted Set: 有序集合
    @Test
    public void zsetOperations() {
        // 排行榜
        jedis.zadd("leaderboard", 1500, "player1");
        jedis.zadd("leaderboard", 1200, "player2");
        jedis.zadd("leaderboard", 1800, "player3");
        
        // 获取前3名
        Set<String> top3 = jedis.zrevrange("leaderboard", 0, 2);
        
        // 应用场景:排行榜、延迟队列、范围查询
    }
}

1.2 特殊数据类型

类型基于类型特点典型场景
BitmapString位操作,节省内存签到统计、在线用户、布隆过滤器
HyperLogLogString基数估算,标准误差0.81%UV统计、独立用户计数
GEOSorted Set地理位置索引LBS服务、附近的人
Stream专用结构消息流,支持消费组消息队列、事件溯源
public class RedisSpecialTypes {
    
    // Bitmap操作
    @Test
    public void bitmapOperations() {
        // 用户签到(用户ID作为偏移量)
        jedis.setbit("signin:20240301", 1001, true);
        jedis.setbit("signin:20240301", 1002, true);
        
        // 统计签到人数
        long count = jedis.bitcount("signin:20240301");
        
        // 查找第一个签到用户
        long firstUser = jedis.bitpos("signin:20240301", true);
    }
    
    // HyperLogLog操作
    @Test
    public void hyperLogLogOperations() {
        // 网站UV统计
        jedis.pfadd("uv:20240301", "user1", "user2", "user3");
        jedis.pfadd("uv:20240302", "user2", "user4", "user5");
        
        // 估算UV(每个HLL只占12KB)
        long uv = jedis.pfcount("uv:20240301");
        
        // 合并多天数据
        jedis.pfmerge("uv:march", "uv:20240301", "uv:20240302");
    }
    
    // GEO操作
    @Test
    public void geoOperations() {
        // 添加地理位置
        jedis.geoadd("restaurants", 116.397128, 39.916527, "全聚德");
        jedis.geoadd("restaurants", 116.405285, 39.904989, "东来顺");
        
        // 查找附近5km餐厅
        List<GeoRadiusResponse> nearby = jedis.georadius(
            "restaurants", 116.400000, 39.910000, 5, GeoUnit.KM);
        
        // 计算两点距离
        Double distance = jedis.geodist("restaurants", "全聚德", "东来顺", GeoUnit.KM);
    }
    
    // Stream操作
    @Test
    public void streamOperations() {
        // 发布消息
        StreamEntryID msgId = jedis.xadd("order:stream", 
            StreamEntryID.NEW_ENTRY,
            Map.of("orderId", "12345", "status", "created", "amount", "99.99"));
        
        // 创建消费组
        jedis.xgroupCreate("order:stream", "payment-group", StreamEntryID.LAST_ENTRY, false);
        
        // 消费消息
        Map<String, StreamEntryID> streams = Map.of("order:stream", StreamEntryID.UNRECEIVED_ENTRY);
        List<Map.Entry<String, List<StreamEntry>>> result = 
            jedis.xreadGroup("payment-group", "consumer1", 1, 1000, false, streams);
    }
}

二、内部编码结构(底层实现)

2.1 核心数据结构

// SDS (Simple Dynamic String) - 所有字符串的底层实现
public class SDS {
    private int len;        // 字符串长度
    private int alloc;      // 分配的总空间
    private byte flags;     // 标志位,标识SDS类型
    private char[] buf;     // 字符数组
    
    // 优势:
    // 1. O(1)获取长度
    // 2. 二进制安全
    // 3. 预分配机制减少内存重分配
    // 4. 兼容C字符串函数
}

// Dict (字典) - Hash表实现
public class Dict {
    private DictHashTable[] ht = new DictHashTable[2]; // 双hash表
    private long rehashIndex = -1; // rehash进度
    
    // 渐进式rehash - 避免阻塞
    public void rehash(int n) {
        // 每次最多迁移n个桶
        while (n-- > 0 && ht[0].used != 0) {
            // 迁移一个非空桶到ht[1]
            migrateBucket();
        }
        
        // 迁移完成
        if (ht[0].used == 0) {
            ht[0] = ht[1];
            ht[1] = new DictHashTable();
            rehashIndex = -1;
        }
    }
}

// IntSet - 整数集合优化
public class IntSet {
    private int encoding;    // INT16/INT32/INT64
    private int length;      // 元素数量
    private byte[] contents; // 有序数组
    
    // 自动升级编码
    public void upgrade(long value) {
        int oldEncoding = encoding;
        encoding = getEncodingForValue(value);
        
        // 重新分配内存并复制数据
        resizeAndCopy(oldEncoding, encoding);
    }
}

2.2 压缩数据结构

// ZipList - 压缩列表(Redis 7.0前使用)
public class ZipList {
    /*
     * 内存布局:
     * <zlbytes><zltail><zllen><entry1><entry2>...<zlend>
     * 
     * 特点:
     * - 内存连续,缓存友好
     * - 支持从两端遍历
     * - 存在连锁更新问题
     */
    
    public static class ZipListEntry {
        private int prevLen;     // 前一个entry的长度
        private int encoding;    // 编码和长度信息
        private byte[] data;     // 实际数据
    }
}

// ListPack - 新压缩结构(Redis 7.0+)
public class ListPack {
    /*
     * 解决ZipList的连锁更新问题
     * 每个entry存储自身长度而非前一个entry长度
     */
    
    public static class ListPackEntry {
        private int encoding;    // 编码类型
        private byte[] data;     // 数据
        private int backLen;     // 当前entry长度(支持反向遍历)
    }
}

// QuickList - List的实际实现
public class QuickList {
    private QuickListNode head, tail;
    private int count;       // 总元素数
    private int len;         // 节点数
    private int compressDepth; // 压缩深度
    
    public static class QuickListNode {
        private QuickListNode prev, next;
        private byte[] ziplist;      // 压缩列表
        private int count;           // 节点元素数
        private boolean compressed;  // 是否LZ4压缩
    }
    
    // 配置参数
    // list-max-ziplist-size -2  # 每个节点最大8KB
    // list-compress-depth 0     # 不压缩头尾节点
}

// SkipList - 跳跃表
public class SkipList {
    private static final int MAX_LEVEL = 32;
    private static final double PROBABILITY = 0.25;
    
    private SkipListNode header;
    private SkipListNode tail;
    private int level;     // 当前最高层
    private long length;   // 节点数量
    
    public static class SkipListNode {
        private String member;           // 成员
        private double score;            // 分值
        private SkipListNode backward;   // 后退指针
        private SkipListLevel[] levels;  // 各层的前进指针和跨度
    }
    
    // 随机层数生成
    private int randomLevel() {
        int level = 1;
        while (Math.random() < PROBABILITY && level < MAX_LEVEL) {
            level++;
        }
        return level;
    }
}

三、编码选择策略

3.1 自动编码转换机制

public class RedisEncodingStrategy {
    
    // String类型编码选择
    public void stringEncoding() {
        /*
         * 编码选择策略:
         * 1. 整数值且在long范围内 -> OBJ_ENCODING_INT
         * 2. 字符串长度 <= 44字节 -> OBJ_ENCODING_EMBSTR
         * 3. 字符串长度 > 44字节 -> OBJ_ENCODING_RAW
         */
        
        jedis.set("int_key", "12345");        // -> OBJ_ENCODING_INT
        jedis.set("short_key", "hello");      // -> OBJ_ENCODING_EMBSTR  
        jedis.set("long_key", "very long string over 44 bytes..."); // -> OBJ_ENCODING_RAW
        
        // 验证编码
        String encoding = jedis.objectEncoding("int_key"); // "int"
    }
    
    // Hash类型编码选择
    public void hashEncoding() {
        /*
         * 小Hash:同时满足以下条件使用ziplist/listpack
         * - 元素数量 <= hash-max-ziplist-entries (默认512)
         * - 所有键值长度 <= hash-max-ziplist-value (默认64字节)
         * 
         * 大Hash:使用hashtable
         */
        
        Map<String, String> smallHash = new HashMap<>();
        smallHash.put("name", "张三");
        smallHash.put("age", "25");
        jedis.hmset("user:small", smallHash); // -> ziplist/listpack
        
        // 大量数据或长字符串 -> hashtable
        for (int i = 0; i < 1000; i++) {
            jedis.hset("user:large", "field" + i, "value" + i);
        }
    }
    
    // List类型编码选择
    public void listEncoding() {
        /*
         * List统一使用QuickList
         * 配置参数:
         * - list-max-ziplist-size: 控制每个节点大小
         *   正数:节点最大元素数
         *   负数:节点最大字节数(-1=4KB, -2=8KB, -3=16KB, -4=32KB, -5=64KB)
         * - list-compress-depth: 压缩深度,0表示不压缩
         */
        
        // 所有List操作都是QuickList
        jedis.lpush("mylist", "item1", "item2", "item3");
        
        // 查看编码
        String encoding = jedis.objectEncoding("mylist"); // "quicklist"
    }
    
    // Set类型编码选择
    public void setEncoding() {
        /*
         * 小Set:元素都是整数且数量 <= set-max-intset-entries (默认512) -> intset
         * 大Set或包含非整数 -> hashtable
         */
        
        // 整数集合 -> intset
        jedis.sadd("numbers", "1", "2", "3", "100", "200");
        System.out.println(jedis.objectEncoding("numbers")); // "intset"
        
        // 包含字符串 -> hashtable
        jedis.sadd("mixed", "1", "hello", "3");
        System.out.println(jedis.objectEncoding("mixed")); // "hashtable"
    }
    
    // ZSet类型编码选择
    public void zsetEncoding() {
        /*
         * 小ZSet:同时满足以下条件使用ziplist/listpack
         * - 元素数量 <= zset-max-ziplist-entries (默认128)
         * - 所有成员长度 <= zset-max-ziplist-value (默认64字节)
         * 
         * 大ZSet:使用skiplist + hashtable
         */
        
        // 小有序集合 -> ziplist/listpack
        jedis.zadd("small_rank", 100, "user1");
        jedis.zadd("small_rank", 200, "user2");
        System.out.println(jedis.objectEncoding("small_rank")); // "ziplist"
        
        // 大有序集合 -> skiplist
        for (int i = 0; i < 200; i++) {
            jedis.zadd("large_rank", i, "user" + i);
        }
        System.out.println(jedis.objectEncoding("large_rank")); // "skiplist"
    }
}

3.2 配置优化建议

# redis.conf 关键配置

# Hash类型优化
hash-max-ziplist-entries 512    # ziplist最大元素数
hash-max-ziplist-value 64       # ziplist最大value长度

# List类型优化  
list-max-ziplist-size -2        # 每个quicklist节点最大8KB
list-compress-depth 0           # 不压缩头尾节点(0=不压缩)

# Set类型优化
set-max-intset-entries 512      # intset最大元素数

# ZSet类型优化
zset-max-ziplist-entries 128    # ziplist最大元素数
zset-max-ziplist-value 64       # ziplist最大member长度

# HyperLogLog优化
hll-sparse-max-bytes 3000       # 稀疏表示最大字节数

四、场景选型策略

4.1 实战选择指南

public class RedisTypeSelector {
    
    public String selectDataType(String scenario) {
        // 缓存场景
        if (scenario.contains("缓存")) {
            if (scenario.contains("对象")) return "Hash";   // 结构化对象
            if (scenario.contains("计数")) return "String"; // 简单计数
            return "String"; // 默认字符串缓存
        }
        
        // 排序场景
        if (scenario.contains("排行") || scenario.contains("排序")) {
            return "ZSet"; // 有序集合
        }
        
        // 队列场景
        if (scenario.contains("队列") || scenario.contains("消息")) {
            if (scenario.contains("延迟")) return "ZSet";   // 延迟队列
            if (scenario.contains("持久")) return "Stream"; // 可靠消息队列
            return "List"; // 简单队列
        }
        
        // 去重场景
        if (scenario.contains("去重") || scenario.contains("唯一")) {
            if (scenario.contains("计数")) return "HyperLogLog"; // 基数统计
            return "Set"; // 精确去重
        }
        
        // 统计场景
        if (scenario.contains("统计")) {
            if (scenario.contains("签到") || scenario.contains("状态")) return "Bitmap";
            if (scenario.contains("UV") || scenario.contains("基数")) return "HyperLogLog";
            return "String"; // 简单计数
        }
        
        // 位置场景
        if (scenario.contains("位置") || scenario.contains("地理")) {
            return "GEO";
        }
        
        return "String"; // 默认选择
    }
    
    // 性能对比测试
    @Test
    public void performanceComparison() {
        // 不同数据结构的性能特点
        
        // String: 最快的单值操作
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            jedis.set("key" + i, "value" + i);
        }
        long stringTime = System.currentTimeMillis() - start;
        
        // Hash: 适合多字段对象
        start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            jedis.hmset("hash" + i, Map.of(
                "field1", "value1",
                "field2", "value2",
                "field3", "value3"
            ));
        }
        long hashTime = System.currentTimeMillis() - start;
        
        System.out.printf("String操作: %dms, Hash操作: %dms\n", stringTime, hashTime);
    }
}

4.2 内存优化策略

public class RedisMemoryOptimization {
    
    // 选择合适的数据结构节省内存
    @Test
    public void memoryOptimizationDemo() {
        
        // ❌ 低效方式:每个用户一个String key  
        for (int i = 1; i <= 1000; i++) {
            jedis.set("user:" + i + ":name", "user" + i);
            jedis.set("user:" + i + ":age", String.valueOf(20 + i % 50));
            jedis.set("user:" + i + ":email", "user" + i + "@example.com");
        }
        // 内存消耗:每个key都有额外的过期字典开销
        
        // ✅ 高效方式:Hash存储用户信息
        for (int i = 1; i <= 1000; i++) {
            Map<String, String> user = Map.of(
                "name", "user" + i,
                "age", String.valueOf(20 + i % 50), 
                "email", "user" + i + "@example.com"
            );
            jedis.hmset("user:" + i, user);
        }
        // 内存节省:Hash使用ziplist编码时内存更紧凑
        
        // ✅ 位操作优化:用Bitmap替代Set存储用户状态
        // 10万用户在线状态,Set需要约1.2MB,Bitmap仅需12.5KB
        for (int userId = 1; userId <= 100000; userId++) {
            if (userId % 3 == 0) { // 模拟1/3用户在线
                jedis.setbit("online:users", userId, true);
            }
        }
        
        // 统计在线用户数
        long onlineCount = jedis.bitcount("online:users");
        System.out.println("在线用户数: " + onlineCount);
    }
}

第二题: Redis为什么快

一、内存存储 + 合理的线程模型

1.1 纯内存操作优势

// 性能对比(近似值)
public class StoragePerformance {
    private static final long CPU_CACHE_ACCESS = 1;      // 1ns
    private static final long MEMORY_ACCESS = 100;       // 100ns  
    private static final long SSD_ACCESS = 100_000;      // 100μs
    private static final long DISK_ACCESS = 10_000_000;  // 10ms
    
    // Redis内存访问比磁盘快约5个数量级(10^5倍)
    public void performanceComparison() {
        System.out.println("内存 vs SSD: " + (SSD_ACCESS / MEMORY_ACCESS) + "倍");
        System.out.println("内存 vs 机械硬盘: " + (DISK_ACCESS / MEMORY_ACCESS) + "倍");
    }
}

1.2 单线程模型优势(Redis 6.0之前)

// Redis单线程模型伪代码
public class RedisEventLoop {
    
    public void eventLoop() {
        while (running) {
            // 1. I/O多路复用获取就绪事件
            List<Event> events = epoll.wait(timeout);
            
            // 2. 处理网络事件
            for (Event event : events) {
                if (event.isReadable()) {
                    handleRead(event.getClient());
                } else if (event.isWritable()) {
                    handleWrite(event.getClient());
                }
            }
            
            // 3. 处理定时事件
            processTimeEvents();
            
            // 4. 处理阻塞事件(如BLPOP)
            processBlockedClients();
        }
    }
    
    private void handleRead(RedisClient client) {
        // 读取命令 -> 解析 -> 执行 -> 返回结果
        // 全程无锁,无线程切换开销
        String command = client.readCommand();
        Object result = executeCommand(command);
        client.writeResponse(result);
    }
}

单线程优势

  • 避免线程切换开销(每次切换约1-10μs)
  • 无需复杂的锁机制
  • 指令执行顺序可预测
  • 简化内存模型,避免缓存一致性问题

二、高效数据结构实现

2.1 核心数据结构优化

// SDS(Simple Dynamic String)实现
public class SDS {
    private int len;        // 当前字符串长度
    private int alloc;      // 分配的总长度
    private byte flags;     // 类型标志
    private char[] buf;     // 字符数组
    
    // O(1)时间复杂度获取长度,避免strlen()的O(n)遍历
    public int length() {
        return len;
    }
    
    // 预分配策略,减少内存重新分配
    public void append(String str) {
        if (len + str.length() > alloc) {
            // 小于1MB时翻倍,大于1MB时每次增加1MB
            int newSize = len < 1024 * 1024 ? 
                (len + str.length()) * 2 : 
                len + str.length() + 1024 * 1024;
            resize(newSize);
        }
        // 追加操作
    }
}

2.2 压缩数据结构

// ziplist压缩列表实现原理
public class ZipList {
    // 内存布局:<zlbytes><zltail><zllen><entry1><entry2>...<zlend>
    
    public static class ZipListEntry {
        private int prevLen;     // 前一个entry长度
        private int encoding;    // 编码类型和长度
        private byte[] data;     // 实际数据
    }
    
    // 针对小数据量优化,连续内存访问友好
    public Object get(int index) {
        // 由于连续存储,缓存命中率高
        return decodeEntry(findEntry(index));
    }
}

// quicklist:ziplist + 双向链表的混合结构
public class QuickList {
    private QuickListNode head;
    private QuickListNode tail;
    private int count;       // 总元素数量
    private int len;         // 节点数量
    
    public static class QuickListNode {
        private QuickListNode prev, next;
        private byte[] ziplist;    // 压缩的ziplist
        private int count;         // 该节点元素数量
        private boolean compressed; // 是否LZ4压缩
    }
}

2.3 数据结构选择策略

数据类型小数据量结构大数据量结构切换阈值配置
Stringembstrraw44字节
Hashziplisthashtablehash-max-ziplist-entries 512
hash-max-ziplist-value 64
Listquicklistquicklistlist-max-ziplist-size -2
list-compress-depth 0
Setintsethashtableset-max-intset-entries 512
ZSetziplistskiplist+hashtablezset-max-ziplist-entries 128
zset-max-ziplist-value 64

三、I/O多路复用机制

3.1 事件驱动模型

// Redis事件处理核心逻辑
public class RedisServer {
    private EventLoop eventLoop;
    private Map<Integer, RedisClient> clients;
    
    public void initServer() {
        // 根据系统选择最优I/O多路复用实现
        if (isLinux()) {
            eventLoop = new EpollEventLoop();      // Linux: epoll
        } else if (isMacOS()) {
            eventLoop = new KqueueEventLoop();     // macOS: kqueue  
        } else {
            eventLoop = new SelectEventLoop();     // 通用: select
        }
    }
    
    // 主事件循环
    public void serverMain() {
        while (running) {
            // 计算最近定时器触发时间
            long timeout = getTimeToNextTimeout();
            
            // 等待I/O事件,最多等待timeout毫秒
            List<Event> events = eventLoop.poll(timeout);
            
            // 处理I/O事件
            for (Event event : events) {
                processEvent(event);
            }
            
            // 处理定时事件(过期key清理、持久化等)
            processTimeEvents();
        }
    }
}

3.2 非阻塞网络处理

public class RedisNetworking {
    
    // 非阻塞读取客户端数据
    public void readFromClient(RedisClient client) {
        try {
            ByteBuffer buffer = client.getInputBuffer();
            int bytesRead = client.getChannel().read(buffer);
            
            if (bytesRead > 0) {
                // 解析Redis协议命令
                List<String[]> commands = parseRedisProtocol(buffer);
                for (String[] cmd : commands) {
                    processCommand(client, cmd);
                }
            } else if (bytesRead == 0) {
                // 数据未就绪,继续等待
                return;
            } else {
                // 连接关闭
                closeClient(client);
            }
        } catch (IOException e) {
            closeClient(client);
        }
    }
    
    // Redis协议解析(RESP - Redis Serialization Protocol)
    private List<String[]> parseRedisProtocol(ByteBuffer buffer) {
        // *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
        // 解析为:["SET", "key", "value"]
        return protocolParser.parse(buffer);
    }
}

四、Redis 6.0+ I/O多线程优化

4.1 多线程架构设计

// Redis 6.0 I/O线程模型
public class RedisIOThreads {
    private final int ioThreadsNum;
    private final ExecutorService ioThreadPool;
    private final BlockingQueue<IOTask> taskQueue;
    
    public RedisIOThreads(int threadNum) {
        this.ioThreadsNum = threadNum;
        this.ioThreadPool = Executors.newFixedThreadPool(threadNum);
        this.taskQueue = new LinkedBlockingQueue<>();
    }
    
    // 主线程分发I/O任务
    public void distributeIOTasks(List<RedisClient> clients) {
        for (RedisClient client : clients) {
            if (client.hasPendingRead()) {
                taskQueue.offer(new ReadTask(client));
            }
            if (client.hasPendingWrite()) {
                taskQueue.offer(new WriteTask(client));
            }
        }
        
        // 等待所有I/O线程完成
        waitForIOCompletion();
        
        // 主线程串行执行命令(保证线程安全)
        for (RedisClient client : clients) {
            processCommands(client);
        }
    }
    
    // I/O线程工作逻辑
    private class IOWorker implements Runnable {
        @Override
        public void run() {
            while (running) {
                try {
                    IOTask task = taskQueue.take();
                    task.execute(); // 只处理网络读写,不执行命令
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
}

4.2 多线程配置优化

# redis.conf 配置
io-threads 4                    # I/O线程数量(建议为CPU核心数)
io-threads-do-reads yes         # 开启读多线程
// 多线程性能测试对比
public class RedisPerformanceTest {
    
    @Test
    public void compareIOThreadsPerformance() {
        // 单线程模式
        RedisConfig singleThread = new RedisConfig();
        singleThread.setIOThreads(1);
        
        // 多线程模式  
        RedisConfig multiThread = new RedisConfig();
        multiThread.setIOThreads(4);
        multiThread.setIOThreadsDoReads(true);
        
        // 在高并发场景下,多线程模式可提升20-30%性能
        // 但CPU使用率也会相应增加
    }
}

五、内存管理优化

5.1 内存分配器优化

// Redis内存管理策略
public class RedisMemoryManager {
    
    // 使用jemalloc减少内存碎片
    static {
        System.loadLibrary("jemalloc");
    }
    
    // 对象共享池(节省内存)
    private static final RedisObject[] SHARED_INTEGERS = 
        new RedisObject[10000]; // 0-9999的整数对象
    
    static {
        for (int i = 0; i < SHARED_INTEGERS.length; i++) {
            SHARED_INTEGERS[i] = new RedisObject(REDIS_STRING, i);
        }
    }
    
    // 获取共享整数对象
    public RedisObject getSharedInteger(int value) {
        if (value >= 0 && value < SHARED_INTEGERS.length) {
            return SHARED_INTEGERS[value];
        }
        return new RedisObject(REDIS_STRING, value);
    }
    
    // 内存淘汰策略实现
    public void evictMemory() {
        while (getCurrentMemoryUsage() > maxMemory) {
            switch (maxMemoryPolicy) {
                case "allkeys-lru":
                    evictLRU(getAllKeys());
                    break;
                case "volatile-lru":
                    evictLRU(getVolatileKeys());
                    break;
                case "allkeys-lfu":
                    evictLFU(getAllKeys());
                    break;
                // ... 其他策略
            }
        }
    }
}

5.2 编码优化

// Redis对象编码优化
public class RedisObject {
    private int type;        // 数据类型
    private int encoding;    // 编码类型
    private Object ptr;      // 指向实际数据
    private int lru;         // LRU时间戳
    
    // 编码类型常量
    public static final int OBJ_ENCODING_RAW = 0;        // 原始字符串
    public static final int OBJ_ENCODING_INT = 1;        // 整数
    public static final int OBJ_ENCODING_HT = 2;         // 哈希表
    public static final int OBJ_ENCODING_ZIPMAP = 3;     // 压缩映射
    public static final int OBJ_ENCODING_LINKEDLIST = 4; // 链表
    public static final int OBJ_ENCODING_ZIPLIST = 5;    // 压缩列表
    public static final int OBJ_ENCODING_INTSET = 6;     // 整数集合
    public static final int OBJ_ENCODING_SKIPLIST = 7;   // 跳表
    public static final int OBJ_ENCODING_EMBSTR = 8;     // 嵌入式字符串
    public static final int OBJ_ENCODING_QUICKLIST = 9;  // 快速列表
    
    // 自动编码转换
    public void tryObjectEncoding() {
        if (type == REDIS_STRING && encoding == OBJ_ENCODING_RAW) {
            String str = (String) ptr;
            
            // 尝试转换为整数编码
            try {
                long value = Long.parseLong(str);
                if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
                    this.encoding = OBJ_ENCODING_INT;
                    this.ptr = (int) value;
                    return;
                }
            } catch (NumberFormatException e) {
                // 继续其他编码尝试
            }
            
            // 短字符串使用embstr编码
            if (str.length() <= 44) {
                this.encoding = OBJ_ENCODING_EMBSTR;
                // embstr将RedisObject和字符串数据存储在连续内存中
            }
        }
    }
}

六、持久化性能优化

6.1 RDB快照优化

public class RDBPersistence {
    
    // fork子进程进行RDB持久化
    public void saveRDB() {
        int childPid = fork();
        
        if (childPid == 0) {
            // 子进程:执行RDB保存
            try {
                saveRDBFile();
                System.exit(0);
            } catch (Exception e) {
                System.exit(1);
            }
        } else if (childPid > 0) {
            // 父进程:继续处理客户端请求
            // COW机制确保数据一致性
            waitForChild(childPid);
        } else {
            // fork失败
            throw new RuntimeException("Failed to fork child process");
        }
    }
    
    // COW优化:写时复制
    private void copyOnWrite() {
        // 父子进程共享内存页面
        // 只有在写操作时才复制页面
        // 大大减少内存使用和fork时间
    }
}

6.2 AOF优化

public class AOFPersistence {
    private BufferedWriter aofWriter;
    private final String AOF_FSYNC_NO = "no";
    private final String AOF_FSYNC_ALWAYS = "always";  
    private final String AOF_FSYNC_EVERYSEC = "everysec";
    
    // AOF写入策略
    public void writeAOF(String command) {
        try {
            aofWriter.write(command);
            
            switch (aofFsyncPolicy) {
                case AOF_FSYNC_ALWAYS:
                    aofWriter.flush();    // 每次都刷盘,最安全但最慢
                    break;
                case AOF_FSYNC_EVERYSEC:
                    // 后台线程每秒刷盘一次,平衡性能和安全性
                    scheduleFlush();
                    break;
                case AOF_FSYNC_NO:
                    // 由操作系统决定何时刷盘,最快但可能丢失数据
                    break;
            }
        } catch (IOException e) {
            handleAOFError(e);
        }
    }
    
    // AOF重写优化
    @Async
    public void rewriteAOF() {
        // 创建子进程进行AOF重写
        // 重写期间的新命令写入AOF重写缓冲区
        // 重写完成后合并缓冲区内容
    }
}

七、网络协议优化

7.1 Pipeline批处理

public class RedisPipeline {
    
    @Test
    public void pipelinePerformanceTest() {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 普通模式:每个命令一次网络往返
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            jedis.set("key" + i, "value" + i);
        }
        long normalTime = System.currentTimeMillis() - start;
        
        // Pipeline模式:批量发送,减少网络往返
        start = System.currentTimeMillis();
        Pipeline pipeline = jedis.pipelined();
        for (int i = 0; i < 10000; i++) {
            pipeline.set("key" + i, "value" + i);
        }
        pipeline.sync(); // 一次性执行所有命令
        long pipelineTime = System.currentTimeMillis() - start;
        
        System.out.printf("普通模式: %dms, Pipeline模式: %dms, 提升: %.2fx\n", 
            normalTime, pipelineTime, (double) normalTime / pipelineTime);
        // 典型结果:Pipeline模式快5-10倍
    }
}

7.2 Lua脚本优化

public class RedisLuaScript {
    
    // 原子性计数器脚本
    private static final String INCR_WITH_LIMIT_SCRIPT = 
        "local current = redis.call('GET', KEYS[1]) or '0' " +
        "if tonumber(current) < tonumber(ARGV[1]) then " +
        "  return redis.call('INCR', KEYS[1]) " +
        "else " +
        "  return nil " +
        "end";
    
    public Long incrWithLimit(String key, int limit) {
        Jedis jedis = new Jedis();
        
        // 脚本在Redis服务端执行,保证原子性
        // 避免多次网络往返
        Object result = jedis.eval(INCR_WITH_LIMIT_SCRIPT, 
            Collections.singletonList(key), 
            Collections.singletonList(String.valueOf(limit)));
            
        return (Long) result;
    }
    
    // 脚本缓存优化
    private final Map<String, String> scriptShaCache = new ConcurrentHashMap<>();
    
    public Object evalOptimized(String script) {
        String sha = scriptShaCache.computeIfAbsent(script, this::loadScript);
        try {
            return jedis.evalsha(sha);  // 使用SHA执行,避免重复传输脚本内容
        } catch (JedisDataException e) {
            // SHA不存在,重新加载脚本
            sha = loadScript(script);
            return jedis.evalsha(sha);
        }
    }
}

八、性能监控与调优

8.1 性能指标监控

@Component
public class RedisPerformanceMonitor {
    
    // 监控Redis性能指标
    @Scheduled(fixedDelay = 5000)
    public void monitorRedisPerformance() {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            String info = jedis.info();
            
            // 解析关键性能指标
            Map<String, String> metrics = parseInfo(info);
            
            // QPS监控
            long instantaneousOpsPerSec = Long.parseLong(
                metrics.getOrDefault("instantaneous_ops_per_sec", "0"));
            
            // 内存使用率
            long usedMemory = Long.parseLong(
                metrics.getOrDefault("used_memory", "0"));
            long maxMemory = Long.parseLong(
                metrics.getOrDefault("maxmemory", "0"));
            double memoryUsageRatio = maxMemory > 0 ? 
                (double) usedMemory / maxMemory : 0;
            
            // 连接数
            int connectedClients = Integer.parseInt(
                metrics.getOrDefault("connected_clients", "0"));
            
            // 命中率
            long keyspaceHits = Long.parseLong(
                metrics.getOrDefault("keyspace_hits", "0"));
            long keyspaceMisses = Long.parseLong(
                metrics.getOrDefault("keyspace_misses", "0"));
            double hitRatio = (keyspaceHits + keyspaceMisses) > 0 ? 
                (double) keyspaceHits / (keyspaceHits + keyspaceMisses) : 0;
            
            // 告警检查
            if (memoryUsageRatio > 0.8) {
                alertService.sendAlert("Redis内存使用率过高: " + memoryUsageRatio);
            }
            if (hitRatio < 0.9) {
                alertService.sendAlert("Redis缓存命中率过低: " + hitRatio);
            }
        }
    }
    
    // 慢查询监控
    public void monitorSlowQueries() {
        try (Jedis jedis = new Jedis()) {
            List<Slowlog> slowlogs = jedis.slowlogGet();
            for (Slowlog log : slowlogs) {
                if (log.getExecutionTime() > 1000) { // 超过1ms的查询
                    logger.warn("慢查询检测: 命令={}, 耗时={}μs, 时间={}", 
                        log.getArgs(), log.getExecutionTime(), 
                        new Date(log.getTimeStamp() * 1000));
                }
            }
        }
    }
}

8.2 性能调优建议

public class RedisOptimizationGuide {
    
    // 数据结构选择优化
    public void optimizeDataStructure() {
        /*
         * 1. 小Hash使用ziplist编码
         * hash-max-ziplist-entries 512
         * hash-max-ziplist-value 64
         * 
         * 2. 大Hash考虑拆分
         * user:1001:profile -> user:1001:basic, user:1001:settings
         * 
         * 3. 避免大key
         * 单个key不超过10KB,集合类型元素数量不超过5000
         */
    }
    
    // 内存优化配置
    public void optimizeMemory() {
        /*
         * 1. 启用内存淘汰策略
         * maxmemory 2gb
         * maxmemory-policy allkeys-lru
         * 
         * 2. 开启key过期
         * 合理设置TTL,避免内存泄漏
         * 
         * 3. 使用压缩
         * 对于大字符串考虑客户端压缩
         */
    }
    
    // 网络优化
    public void optimizeNetwork() {
        /*
         * 1. 使用连接池
         * JedisPoolConfig config = new JedisPoolConfig();
         * config.setMaxTotal(200);
         * config.setMaxIdle(50);
         * 
         * 2. 启用Pipeline
         * 批量操作使用pipeline减少网络往返
         * 
         * 3. 合理的超时设置
         * 连接超时、读写超时设置合理值
         */
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值