
客户端特点
客户端服务端通信
TCP链接 发送报文
自写客户端

- 建立 Socket 连接:和 Redis 服务端建立 TCP 连接,获取输入 / 输出流。
- 发送命令(OutputStream 写入) :按照 RESP 协议的格式,把 Redis 命令(如
set/get)拼接成二进制数据,发送给服务端。- 接收响应(InputStream 读取):读取 Redis 服务端返回的二进制数据,解析成字符串输出。
客户端初始化构造方法
java
public class MyClient {
private Socket socket;
private OutputStream write;
private InputStream read;
public MyClient(String host, int port) throws IOException {
// 建立TCP连接
socket = new Socket(host, port);
// 获取输出流,用于发送命令
write = socket.getOutputStream();
// 获取输入流,用于接收响应
read = socket.getInputStream();
}
}
public void set(String key, String val) throws IOException {
StringBuffer sb = new StringBuffer();
// 1. 表示这是一个包含3个参数的数组(set命令 + key + value)
sb.append("*3").append("\r\n");
// 2. 第一个参数:"SET" 命令
sb.append("$3").append("\r\n"); // 字符串长度:3
sb.append("SET").append("\r\n"); // 字符串内容
// 3. 第二个参数:key(动态长度)
sb.append("$").append(key.getBytes().length).append("\r\n");
sb.append(key).append("\r\n");
// 4. 第三个参数:value(动态长度)
sb.append("$").append(val.getBytes().length).append("\r\n");
sb.append(val).append("\r\n");
// 发送命令到服务端
write.write(sb.toString().getBytes());
// 接收响应
byte[] bytes = new byte[1024];
read.read(bytes);
System.out.println(new String(bytes));
}
public void get(String key) throws IOException {
StringBuffer sb = new StringBuffer();
// 1. 表示这是一个包含2个参数的数组(get命令 + key)
sb.append("*2").append("\r\n");
// 2. 第一个参数:"GET" 命令
sb.append("$3").append("\r\n");
sb.append("GET").append("\r\n");
// 3. 第二个参数:key(动态长度)
sb.append("$").append(key.getBytes().length).append("\r\n");
sb.append(key).append("\r\n");
write.write(sb.toString().getBytes());
byte[] bytes = new byte[1024];
read.read(bytes);
System.out.println(new String(bytes));
}
public static void main(String[] args) throws IOException {
MyClient client = new MyClient("192.168.44.181", 6379);
client.set("qingshan", "2673");
client.get("qingshan");
}
从 Redis 服务端读取数据 ,把读到的二进制数据放进上面的小篮子 bytes 里。
Redis客户端

| 配置 | 作用 |
|---|---|
| Jedis | A blazingly small and sane redis java client(体系非常小,但是功能很完善) |
| Lettuce | Advanced redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.(高级客户端,支持线程安全、异步、反应式编程、支持集群、哨兵、pipeline、编解码) |
| Redisson | distributed and scalable Java data structures on top of Redis server(基于 Redis 服务实现的 Java 分布式可扩展的数据结构) |
jerdis


| 连接池类型 | 适用场景 | 核心特点 |
|---|---|---|
JedisPool |
单机 Redis | 基础连接池,管理单个 Redis 节点的连接 |
ShardedJedisPool |
Redis 分片集群(客户端分片) | 基于一致性哈希,支持数据分片存储 |
JedisSentinelPool |
Redis 哨兵集群 | 自动从哨兵节点获取主节点地址,实现主从切换 |
java
public static void ordinaryPool(){
// 1. 创建连接池,指定 Redis 地址和端口
JedisPool pool = new JedisPool("192.168.44.181",6379);
// 2. 从连接池获取 Jedis 实例
Jedis jedis = pool.getResource();
// 3. 执行 Redis 命令
jedis.set("qingshan","平平无奇盆鱼宴");
System.out.println(jedis.get("qingshan"));
// 4. 归还连接(try-with-resources 可自动关闭)
jedis.close();
}
public static void sharedPool() {
// 1. 配置连接池参数
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 2. 定义分片节点信息
JedisShardInfo shardInfo1 = new JedisShardInfo("192.168.44.181", 6379);
List<JedisShardInfo> infoList = Arrays.asList(shardInfo1);
// 3. 创建分片连接池
ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, infoList);
// 4. 获取分片连接并操作
ShardedJedis jedis = jedisPool.getResource();
jedis.set("qingshan","分片测试");
System.out.println(jedis.get("qingshan"));
jedis.close();
}
public static void sentinelPool() {
// 1. 定义主节点名称和哨兵节点地址
String masterName = "redis-master";
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.44.186:26379");
sentinels.add("192.168.44.187:26379");
sentinels.add("192.168.44.188:26379");
// 2. 创建哨兵连接池
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels);
// 3. 执行 Redis 命令(自动获取当前主节点连接)
pool.getResource().set("qingshan", "哨兵" + System.currentTimeMillis() + "盆鱼宴");
System.out.println(pool.getResource().get("qingshan"));
}
java
发布订阅(Pub/Sub)
事务(Transaction)
Lua 脚本
客户端分片
哨兵(Sentinel)
集群(Cluster)
管道(Pipeline)
获取master 分片原理


分布式锁
| 需求 | 说明 |
|---|---|
| 互斥性 | 同一时间只有一个客户端能持有锁 |
| 防死锁 | 客户端崩溃后,锁能自动释放 |
| 防误释放 | 只有持有锁的客户端才能释放锁 |


Redis Pipeline
Redis 是单线程处理命令的,客户端每发一条命令,都要等待响应后才能发下一条,这种「一问一答」模式在批量操作时性能极差:
-
假设一次网络延迟 1ms,客户端 1 秒最多只能发送 1000 条命令。
-
批量操作 10 万个 key 时,网络开销会成为性能瓶颈。
-
Pipeline 允许客户端一次性发送多条命令,Redis 批量执行后一次性返回所有结果,大幅减少网络往返次数:
-
原本 N 条命令需要 N 次网络往返,Pipeline 只需 1 次。
-
性能提升非常明显,适合批量写入、批量查询场景。

| ✅ 推荐场景 | ❌ 不适合场景 |
|---|---|
| 批量写入数据(如初始化缓存、数据迁移) | 操作依赖前一条命令的结果(如先 GET 再 DEL) |
| 批量读取数据(如一次查 100 个 key) | 需要立即知道命令执行结果是否成功 |
| 对结果实时性要求不高的场景 | 高并发下的单条关键业务操作 |
- 命令执行顺序:Pipeline 会按发送顺序执行命令,不会打乱顺序。
- 内存消耗:客户端缓存命令会占用内存,批量过大时可能导致 OOM。
- 服务端压力:一次性发送大量命令,会导致 Redis 服务端短时间负载飙升。
- 非原子性 :Pipeline 只是批量发送命令,不保证原子性(中途出错时前面的命令依然会执行,和事务不同)。
java
public void batchSetWithPipeline(Jedis jedis, Map<String, String> data) {
Pipeline pipeline = jedis.pipelined();
// 把所有命令缓存起来
for (Map.Entry<String, String> entry : data.entrySet()) {
pipeline.set(entry.getKey(), entry.getValue());
}
// 一次性发送并执行所有命令
pipeline.sync();
}
Lettuce
- 线程安全:完全解决了 Jedis 线程不安全的问题,多个线程可共享同一个连接实例。
- 多模式支持:支持同步、异步和响应式(Reactive)编程模式。
- 底层实现:基于 Netty 框架构建,采用非阻塞 I/O,性能高。
- 高级功能支持:支持 Redis 全部高级特性,包括发布订阅、事务、Lua 脚本、Sentinel、集群、Pipeline 及连接池。
- 自 2.x 版本起,Lettuce 成为 Spring Boot 默认的 Redis 客户端,替换了 Jedis。
- 无需手动管理连接,直接通过
RedisTemplate即可操作 Redis。 - 依赖配置(Spring Boot 项目中直接引入即可):
- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
- **Lettuce 为什么是线程安全的?**因为它基于 Netty 的连接实例,使用非阻塞 I/O,多个线程可以共享同一个连接而不会出现并发问题。
Redisson
Redisson 是一个基于 Redis 实现的 Java 驻内存数据网格(In-Memory Data Grid),提供了丰富的分布式可扩展 Java 数据结构,比如分布式 Map、List、Queue、Set 等,无需自己额外搭建服务。
- 高性能:基于 Netty 实现,采用非阻塞 I/O,支持异步请求。
- 全场景支持:支持连接池、Pipeline、Lua 脚本、Redis Sentinel、Redis 集群,主从、哨兵、集群均兼容。
- 事务替代方案:不支持 Redis 原生事务,官方建议用 Lua 脚本替代。
- Spring 集成友好 :可直接配置和注入
RedissonClient。
Redisson 内置了多种开箱即用的分布式锁和同步器,大幅降低了分布式场景的开发复杂度:
- 可重入锁(Reentrant Lock)
- 公平锁(Fair Lock)
- 联锁(MultiLock)
- 红锁(RedLock)
- 读写锁(ReadWriteLock)
- 信号量(Semaphore)
- 可过期性信号量(PermitExpirableSemaphore)
- 闭锁(CountDownLatch)
| 特性 | Jedis | Lettuce | Redisson |
|---|---|---|---|
| 线程安全 | 不安全(需连接池) | 安全 | 安全 |
| 底层实现 | 阻塞 I/O | Netty 非阻塞 I/O | Netty 非阻塞 I/O |
| 编程模式 | 同步 | 同步 / 异步 / 响应式 | 同步 / 异步 |
| Spring Boot 默认版本 | 1.x 及之前 | 2.x+ | 第三方依赖 |
| 分布式支持 | 需手动实现 | 基础支持 | 开箱即用(锁、集合等) |
| 适用场景 | 简单单机场景 | 通用高并发场景 | 复杂分布式场景 |
Redisson 分布式锁的本质,是用 Redis 的 HASH 数据结构 + Lua 脚本,实现了可重入、防误删、自动续期的分布式锁。
java
public static void main(String[] args) throws InterruptedException {
RLock rLock = redissonClient.getLock("updateAccount");
// 参数说明:等待时间、锁持有时间、时间单位
if (rLock.tryLock(100, 10, TimeUnit.SECONDS)) {
try {
// 执行业务逻辑
} finally {
rLock.unlock();
}
}
}
加锁源码:Lua 脚本解析
Redisson 加锁的核心逻辑,是通过一段 Lua 脚本原子执行,避免并发问题:
java
-- KEYS[1]: 锁名称
-- ARGV[1]: 锁过期时间(毫秒)
-- ARGV[2]: 线程标识(客户端ID:线程ID)
-- 1. 锁不存在:直接创建锁(HASH 结构,field为线程标识,value为重入次数)
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 2. 锁已存在,且是当前线程持有:重入次数+1,续期
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 3. 锁已存在,且不是当前线程持有:返回锁的剩余过期时间
return redis.call('pttl', KEYS[1]);
- 核心特点:原子执行,整个加锁过程不会被其他命令打断。
- 可重入实现:用
HASH结构记录每个线程的重入次数,重入时hincrby次数 + 1。
释放锁同样通过 Lua 脚本原子执行,保证「判断 + 删除 + 通知」的原子性:
java
-- KEYS[1]: 锁名称
-- KEYS[2]: 释放锁的通知频道
-- ARGV[1]: 释放锁的消息标识
-- ARGV[2]: 锁过期时间
-- ARGV[3]: 线程标识
-- 1. 锁已不存在:直接发布释放通知
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
-- 2. 锁存在,但不是当前线程持有:直接返回
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil;
end;
-- 3. 锁存在,且是当前线程持有:重入次数-1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
-- 4. 重入次数>0:只续期,不删除锁
if (counter > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
-- 5. 重入次数=0:删除锁并发布释放通知
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
- 防误删实现:只有
hexists判断是当前线程持有的锁,才会执行删除操作。- 通知机制:锁释放后通过
publish发布消息,其他等待的线程可以立即尝试获取锁。
- 业务没执行完,锁到期了怎么办?
Redisson 提供了 WatchDog(看门狗) 机制:
- 当
leaseTime不设置(默认 30 秒)时,看门狗会每隔 10 秒自动续期锁的过期时间。- 只有当前线程还持有锁,就会一直续期,直到业务执行完成。
- 集群模式下,多个 master 加锁导致重复加锁怎么办?
Redisson 会自动将锁的操作路由到同一个 master 节点,避免跨节点重复加锁。
- Redis master 挂了,锁会不会丢失?
- 主从同步有延迟,极端情况下 master 挂了可能会导致锁丢失。
- 高可用场景可以使用 Redisson 的
RedLock红锁算法,向多个独立的 Redis 节点加锁,提高可靠性。
| 特性 | Jedis 手动实现 | Redisson 内置实现 |
|---|---|---|
| 可重入 | 需自己实现(记录线程标识 + 重入次数) | 原生支持,HASH 结构自动维护 |
| 防误删 | 需 Lua 脚本保证原子判断 + 删除 | 内置 Lua 脚本实现 |
| 自动续期 | 需自己实现定时任务续期 | WatchDog 自动续期 |
| 集群支持 | 需自己处理节点路由 | 自动适配哨兵 / 集群模式 |
| 锁类型 | 仅支持简单互斥锁 | 支持公平锁、读写锁、联锁、红锁等多种类型 |
