Skip to content

daichangya/MiniRedis

Repository files navigation

微信公众号

扫码关注微信公众号,Java码界探秘。 Java码界探秘

https://jsdiff.com/

MiniRedis (java-redis)

基于Java实现的Redis服务器,完全兼容Redis协议(RESP),支持Redis的核心功能。

目录

项目概述

MiniRedis是一个用Java实现的Redis服务器,旨在提供一个学习和理解Redis内部工作原理的平台。项目采用模块化设计,实现了Redis的核心功能,包括:

  • 完整的数据结构支持:String、List、Set、Hash、ZSet(有序集合)
  • 持久化机制:支持RDB快照和AOF日志两种持久化方式
  • 主从复制:支持Redis主从复制功能
  • 高级特性:事务、Lua脚本执行、发布订阅、键过期等
  • 协议兼容:完全兼容Redis协议(RESP),可以使用标准Redis客户端连接

项目状态

  • 版本:1.0-SNAPSHOT
  • Java版本:JDK 8+
  • 状态:开发中,核心功能已实现

技术栈

  • Java 8+:核心开发语言
  • Maven:项目构建和依赖管理
  • Netty 4.1.53:高性能网络通信框架
  • LuaJ:Lua脚本执行引擎
  • Logback:日志框架
  • Guava:Google核心库,提供工具类
  • Apache Commons:通用工具库

项目架构

架构图

                       +---------------+  
                       |   客户端      |  
                       +-------+-------+  
                               |  
                    +----------+----------+  
                    |   Netty网络层      |  
                    +----------+----------+  
                                |  
                +-----------------+------------+  
                |   java-redis 服务器         |  
                +-----------------+------------+  
                  |                 |         |  
          +-------v-----+   +-------v-----+   +-----v-----+  
          |命令解析模块 |   |数据处理模块 |   |持久化模块 |  
          +---------------+   +---------------+   +---------+  
                  |                 |                 |  
          +-------+-------+   +-------+-------+   +-------+-------+  
          |协议编解码     |   |存储引擎       |   |RDB/AOF |  
          +---------------+   +---------------+   +---------------+

模块说明

项目采用多模块Maven架构,各模块职责如下:

模块 职责说明
java-redis30 主服务器实现模块,包含命令处理、服务器上下文、客户端会话管理等核心功能
redis-protocol Redis协议(RESP)编解码实现,处理客户端请求和服务器响应
redis-storage 数据存储层,提供Dict接口和DictValue数据结构实现
redis-persistence 持久化实现,支持RDB快照和AOF日志的读写
redis-client Java客户端实现,提供与服务器交互的客户端API
remoting-netty4 基于Netty的网络通信层,处理TCP连接和消息传输
redis-common 通用工具类和数据结构,如ZSet、BytesKey等
redis-context 服务器上下文配置,包含DBConfig等配置类
persistence-collections 持久化集合实现,提供Copy-on-Write数据结构
redis-jmh 性能基准测试模块
redis-original 原始实现(可能为历史版本)

核心组件

  1. 命令处理流程

    • 客户端通过Netty发送Redis协议命令
    • RedisServerInitializer初始化Channel Pipeline
    • RedisCommandHandler处理解码后的命令
    • CommandSuite根据命令名称查找对应的命令实现
    • 命令执行后返回响应给客户端
  2. 数据存储

    • 使用Dict接口作为数据存储抽象
    • 每个数据库(RedisDb)包含一个主字典和一个过期字典
    • 支持多种数据类型:String、List、Set、Hash、ZSet
  3. 持久化机制

    • RDB:定期快照,将内存数据序列化到磁盘
    • AOF:追加日志,记录所有写操作
    • 支持启动时自动加载持久化文件

快速开始

环境要求

  • JDK 8 或更高版本
  • Maven 3.6 或更高版本
  • 操作系统:Windows、Linux、macOS

构建项目

# 克隆项目(如果从Git仓库)
git clone <repository-url>
cd MiniRedis

# 编译项目
mvn clean compile

# 打包项目
mvn clean package

运行服务器

# 方式1:直接运行主类
cd java-redis30
mvn exec:java -Dexec.mainClass="com.daicy.redis.RedisServer"

# 方式2:运行打包后的jar
java -cp target/java-redis30-1.0-SNAPSHOT.jar:../target/classes com.daicy.redis.RedisServer

服务器默认监听端口:6380

启动成功后,你会看到:

redis server is up! localhost:6380

测试连接

使用标准Redis客户端连接测试:

# 使用redis-cli(需要指定端口)
redis-cli -p 6380

# 测试命令
127.0.0.1:6380> PING
PONG
127.0.0.1:6380> SET key value
OK
127.0.0.1:6380> GET key
"value"

功能特性

数据结构支持

  • String(字符串):基本的键值对存储
  • List(列表):有序的字符串列表,支持双向操作
  • Set(集合):无序的字符串集合,支持集合运算
  • Hash(哈希表):字段-值映射表
  • ZSet(有序集合):带分数的有序集合

持久化

  • RDB持久化

    • 支持SAVEBGSAVE命令
    • 定期自动保存(可配置)
    • 启动时自动加载RDB文件
  • AOF持久化

    • 记录所有写操作
    • 启动时重放AOF日志恢复数据
    • 支持AOF文件压缩

主从复制

  • 支持SLAVEOF命令配置主从关系
  • 支持SYNC命令进行全量同步
  • 支持增量复制(通过AOF传播)

事务

  • 支持MULTIEXECDISCARD命令
  • 事务中的命令会被队列化,直到EXEC执行
  • 支持事务回滚

Lua脚本

  • 支持EVALEVALSHA命令
  • 使用LuaJ引擎执行Lua脚本
  • 支持脚本缓存和SHA1校验

发布订阅

  • 支持SUBSCRIBEUNSUBSCRIBEPUBLISH命令
  • 支持模式订阅:PSUBSCRIBEPUNSUBSCRIBE
  • 支持频道和模式两种订阅方式

键管理

  • 支持键过期:EXPIREPEXPIRETTLPTTL
  • 支持键删除:DEL
  • 支持键查询:EXISTSKEYSTYPE
  • 支持键重命名:RENAME

位操作

  • SETBIT:设置指定位的值
  • GETBIT:获取指定位的值
  • BITCOUNT:统计指定位范围内1的个数

支持的命令

String(字符串)命令

命令 说明 示例
GET 获取键的值 GET key
SET 设置键值对 SET key value

List(列表)命令

命令 说明 示例
LPUSH 从左边推入元素 LPUSH list value
RPUSH 从右边推入元素 RPUSH list value
LPOP 从左边弹出元素 LPOP list
RPOP 从右边弹出元素 RPOP list
LLEN 获取列表长度 LLEN list
LRANGE 获取列表范围 LRANGE list 0 -1
LINDEX 获取指定索引的元素 LINDEX list 0
LSET 设置指定索引的元素 LSET list 0 value

Set(集合)命令

命令 说明 示例
SADD 添加元素到集合 SADD set member
SMEMBERS 获取集合所有成员 SMEMBERS set
SREM 从集合移除元素 SREM set member
SISMEMBER 判断元素是否在集合中 SISMEMBER set member
SCARD 获取集合元素数量 SCARD set
SPOP 随机弹出元素 SPOP set
SRANDMEMBER 随机获取元素 SRANDMEMBER set
SINTER 求交集 SINTER set1 set2
SUNION 求并集 SUNION set1 set2
SDIFF 求差集 SDIFF set1 set2

Hash(哈希表)命令

命令 说明 示例
HSET 设置哈希字段值 HSET hash field value
HGET 获取哈希字段值 HGET hash field
HGETALL 获取所有字段和值 HGETALL hash
HDEL 删除哈希字段 HDEL hash field
HEXISTS 判断字段是否存在 HEXISTS hash field
HKEYS 获取所有字段名 HKEYS hash
HVALS 获取所有值 HVALS hash
HLEN 获取字段数量 HLEN hash
HMSET 批量设置字段 HMSET hash f1 v1 f2 v2
HMGET 批量获取字段 HMGET hash f1 f2

ZSet(有序集合)命令

命令 说明 示例
ZADD 添加成员及分数 ZADD zset 1.0 member
ZRANGE 按索引范围获取成员 ZRANGE zset 0 -1
ZREVRANGE 反向按索引范围获取 ZREVRANGE zset 0 -1
ZRANGEBYSCORE 按分数范围获取 ZRANGEBYSCORE zset 0 100
ZREM 删除成员 ZREM zset member
ZCARD 获取成员数量 ZCARD zset
ZINCRBY 增加成员分数 ZINCRBY zset 1.0 member

Key(键)命令

命令 说明 示例
DEL 删除键 DEL key
EXISTS 判断键是否存在 EXISTS key
EXPIRE 设置过期时间(秒) EXPIRE key 60
PEXPIRE 设置过期时间(毫秒) PEXPIRE key 60000
TTL 获取剩余时间(秒) TTL key
PTTL 获取剩余时间(毫秒) PTTL key
PERSIST 移除过期时间 PERSIST key
KEYS 查找匹配的键 KEYS *
RANDOMKEY 随机获取一个键 RANDOMKEY
RENAME 重命名键 RENAME old new
TYPE 获取键的类型 TYPE key

服务器命令

命令 说明 示例
PING 测试连接 PING
ECHO 回显消息 ECHO message
QUIT 关闭连接 QUIT
SELECT 选择数据库 SELECT 0
INFO 获取服务器信息 INFO
DBSIZE 获取键数量 DBSIZE
FLUSHDB 清空当前数据库 FLUSHDB
SAVE 同步保存RDB SAVE
BGSAVE 异步保存RDB BGSAVE
SLAVEOF 配置主从关系 SLAVEOF host port
SYNC 同步数据 SYNC

事务命令

命令 说明 示例
MULTI 开始事务 MULTI
EXEC 执行事务 EXEC
DISCARD 取消事务 DISCARD

Lua脚本命令

命令 说明 示例
EVAL 执行Lua脚本 EVAL "return redis.call('get', 'key')" 0
EVALSHA 执行缓存的脚本 EVALSHA sha1 0
SCRIPT 脚本管理 SCRIPT LOAD "..."

发布订阅命令

命令 说明 示例
SUBSCRIBE 订阅频道 SUBSCRIBE channel
UNSUBSCRIBE 取消订阅 UNSUBSCRIBE channel
PUBLISH 发布消息 PUBLISH channel message
PSUBSCRIBE 模式订阅 PSUBSCRIBE pattern
PUNSUBSCRIBE 取消模式订阅 PUNSUBSCRIBE pattern

位操作命令

命令 说明 示例
SETBIT 设置指定位 SETBIT key 0 1
GETBIT 获取指定位 GETBIT key 0
BITCOUNT 统计位为1的数量 BITCOUNT key

配置说明

DBConfig配置

服务器通过DBConfig类进行配置,支持链式构建:

DBConfig config = DBConfig.builder()
    .withPersistence()              // 启用持久化
    .withNotifications()             // 启用键空间通知
    .withOffHeapCache()            // 启用堆外缓存
    .withRdbCcompression()         // 启用RDB压缩
    .build();

配置选项

配置项 说明 默认值
numDatabases 数据库数量 16
persistenceActive 是否启用持久化 true
rdbFile RDB文件路径 dump.rdb
aofFile AOF文件路径 appendonly.aof
syncPeriod 同步周期(秒) 60
cleanPeriod 清理周期(秒) 30
notificationsActive 是否启用通知 false
offHeapActive 是否启用堆外缓存 false

自定义配置示例

// 禁用持久化
DBConfig config = DBConfig.builder()
    .withoutPersistence()
    .build();

// 自定义持久化文件路径
DBConfig config = DBConfig.builder()
    .withPersistence()
    .setRdbFile("/path/to/custom.rdb")
    .setAofFile("/path/to/custom.aof")
    .setSyncPeriod(30)  // 30秒同步一次
    .build();

使用示例

基本命令示例

# 连接服务器
redis-cli -p 6380

# String操作
127.0.0.1:6380> SET name "MiniRedis"
OK
127.0.0.1:6380> GET name
"MiniRedis"

# List操作
127.0.0.1:6380> LPUSH list "item1" "item2" "item3"
(integer) 3
127.0.0.1:6380> LRANGE list 0 -1
1) "item3"
2) "item2"
3) "item1"

# Hash操作
127.0.0.1:6380> HSET user name "John" age "30"
(integer) 2
127.0.0.1:6380> HGETALL user
1) "name"
2) "John"
3) "age"
4) "30"

# Set操作
127.0.0.1:6380> SADD tags "java" "redis" "netty"
(integer) 3
127.0.0.1:6380> SMEMBERS tags
1) "java"
2) "redis"
3) "netty"

# ZSet操作
127.0.0.1:6380> ZADD scores 100 "Alice" 90 "Bob" 95 "Charlie"
(integer) 3
127.0.0.1:6380> ZRANGE scores 0 -1 WITHSCORES
1) "Bob"
2) "90.0"
3) "Charlie"
4) "95.0"
5) "Alice"
6) "100.0"

Java客户端使用示例

import com.daicy.redis.client.RedisClient;
import com.daicy.redis.client.RedisCommand;
import com.daicy.redis.protocal.BulkRedisMessage;

// 创建客户端
RedisClient client = new RedisClient();

// 执行命令
RedisCommand setCmd = new RedisCommand("SET", "key", "value");
client.send(setCmd);

RedisCommand getCmd = new RedisCommand("GET", "key");
BulkRedisMessage response = (BulkRedisMessage) client.send(getCmd);
System.out.println(response.getString()); // 输出: value

// 关闭客户端
client.shutdown();

事务示例

127.0.0.1:6380> MULTI
OK
127.0.0.1:6380> SET key1 "value1"
QUEUED
127.0.0.1:6380> SET key2 "value2"
QUEUED
127.0.0.1:6380> EXEC
1) OK
2) OK

发布订阅示例

# 终端1:订阅频道
127.0.0.1:6380> SUBSCRIBE news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1

# 终端2:发布消息
127.0.0.1:6380> PUBLISH news "Hello World"
(integer) 1

# 终端1会收到消息
1) "message"
2) "news"
3) "Hello World"

Lua脚本示例

127.0.0.1:6380> EVAL "return redis.call('get', 'key')" 0
"value"

127.0.0.1:6380> EVAL "local val = redis.call('get', KEYS[1]); return val" 1 key
"value"

开发指南

如何添加新命令

  1. 创建命令类

java-redis30/src/main/java/com/daicy/redis/command/下创建命令类:

package com.daicy.redis.command.string;

import com.daicy.redis.Request;
import com.daicy.redis.annotation.Command;
import com.daicy.redis.annotation.ParamLength;
import com.daicy.redis.annotation.ReadOnly;
import com.daicy.redis.command.DBCommand;
import com.daicy.redis.protocal.RedisMessage;
import com.daicy.redis.storage.RedisDb;

@Command("mycommand")  // 命令名称
@ParamLength(1)        // 参数数量
@ReadOnly              // 如果是只读命令
public class MyCommand implements DBCommand {
    
    @Override
    public RedisMessage execute(RedisDb db, Request request) {
        String param = request.getParamStr(0);
        // 实现命令逻辑
        return new BulkRedisMessage("result");
    }
}
  1. 注册命令

META-INF/services/com.daicy.redis.command.RedisCommand文件中添加命令类:

com.daicy.redis.command.string.MyCommand
  1. 重新编译
mvn clean compile

命令注解说明

注解 说明 示例
@Command 指定命令名称 @Command("get")
@ParamLength 指定参数数量 @ParamLength(1)
@ParamType 指定参数类型 @ParamType(DataType.STRING)
@ReadOnly 标记为只读命令 @ReadOnly
@TxIgnore 事务中忽略此命令 @TxIgnore
@PubSubAllowed 允许在发布订阅模式下使用 @PubSubAllowed

模块依赖关系

java-redis30
├── redis-protocol      (协议编解码)
├── redis-storage       (数据存储)
├── redis-persistence   (持久化)
├── redis-client        (客户端)
├── remoting-netty4     (网络通信)
├── redis-context       (配置)
└── redis-common        (通用工具)

redis-persistence
├── redis-storage
└── redis-protocol

redis-client
├── redis-protocol
└── remoting-netty4

项目结构

MiniRedis/
├── java-redis30/              # 主服务器模块
│   ├── src/main/java/
│   │   └── com/daicy/redis/
│   │       ├── command/        # 命令实现
│   │       │   ├── string/    # 字符串命令
│   │       │   ├── list/      # 列表命令
│   │       │   ├── set/       # 集合命令
│   │       │   ├── hash/      # 哈希命令
│   │       │   ├── zset/      # 有序集合命令
│   │       │   ├── key/       # 键命令
│   │       │   ├── db/        # 服务器命令
│   │       │   ├── transaction/ # 事务命令
│   │       │   ├── lua/       # Lua脚本命令
│   │       │   └── pubsub/    # 发布订阅命令
│   │       ├── handler/       # 请求处理器
│   │       ├── event/         # 事件通知
│   │       └── RedisServer.java  # 服务器入口
│   └── pom.xml
├── redis-protocol/             # 协议模块
├── redis-storage/              # 存储模块
├── redis-persistence/          # 持久化模块
├── redis-client/               # 客户端模块
├── remoting-netty4/            # 网络通信模块
├── redis-common/               # 通用模块
├── redis-context/              # 上下文模块
├── persistence-collections/     # 持久化集合模块
├── redis-jmh/                  # 性能测试模块
├── pom.xml                     # 父POM
└── README.md                   # 项目文档

关键类说明

  • RedisServer:服务器启动入口
  • DefaultRedisServerContext:服务器上下文,管理数据库、客户端、命令等
  • RedisDb:数据库实例,包含数据字典和过期字典
  • CommandSuite:命令注册表,管理所有命令
  • PersistenceManager:持久化管理器,处理RDB和AOF
  • ReplicationManager:复制管理器,处理主从复制
  • RedisCommandHandler:命令处理器,处理客户端请求

贡献

欢迎提交Issue和Pull Request!

许可证

本项目采用MIT许可证。

参考资源


注意:本项目主要用于学习和研究目的,不建议在生产环境使用。

About

基于Java实现的 redis-server

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages