目录
虚拟内存
Redis 的虚拟内存(VM, Virtual Memory)是一种数据存储技术,它允许 Redis 在物理内存不足时,将部分数据临时存储到磁盘上。这项技术主要用于处理大量数据时减少内存的使用,以及提高数据的持久性。
但是:这种设计带来了一些问题:
- 性能开销::虚拟内存的使用增加了额外的磁盘I/O操作,这会显著降低Redis的性能,因为磁盘访问速度远不如内存访问速度。
- 复杂性::虚拟内存功能的管理增加了Redis运行的复杂性,尤其是在数据管理和性能调优方面。
Redis 4.0 以后的版本中,虚拟内存功能不再是默认启用的,因为现代服务器通常有足够的物理内存来存储所有数据。然而,如果你的应用程序需要处理大量数据,并且希望减少内存使用,你可以考虑手动启用虚拟内存功能。
事务机制
Redis 的事务机制是一种将多个命令打包执行的能力,确保这些命令要么全部执行,要么全部不执行,从而保持操作的原子性。事务在 Redis 中通过 MULTI
、EXEC
、WATCH
和 DISCARD
等命令实现。
缺点:
- 不支持回滚:如果事务执行过程中发生错误,Redis会继续执行剩余的命令而不是回滚整个事务。
- 不保证隔离性:即在事务执行期间,其他客户端可能可以读取到事务部分执行的结果。
相信有很多小伙伴到这里会问了:不支持回滚怎么保证原子性?其实你犯了一个错误,错误的将数据库ACID的原子性理解成了并发编程的原子性。数据库中事务是指“要么都执行,要么都回滚”。而并发并编程的原子性是指“操作不可拆分,不被中断”。
Pipeline
Redis 的 Pipeline(管道)是一种技术,它允许客户端将多个 Redis 命令打包在一起,然后一次性发送到服务器。使用 Pipeline 可以显著提高客户端和 Redis 服务器之间的通信效率,因为它减少了往返次数(即减少了网络延迟的影响)。
优点:
-
减少网络往返:通过将多个命令一次性发送,而不是发送一个命令后等待响应再发送下一个命令,可以减少网络往返次数。
-
提高性能:Pipeline 可以显著提高执行多个命令的性能,因为它减少了等待时间。
-
原子性:在同一个 Pipeline 中发送的所有命令会作为一个整体一起执行,这意味着它们是原子性的。不过,请注意,这并不保证跨多个 Pipeline 的原子性。
-
脚本执行:Pipeline 可以用于执行 Lua 脚本,这可以进一步减少网络往返,因为 Lua 脚本可以包含多个命令。
缺点:
- 不保证原子性:他的多个命令都是独立执行的,Redis并不保证这些命令可以以不可分割的原子操作进行执行。这是Pipeline和Redis的事务的最大的区别。
- 你不能使用那些需要在命令执行前知道上一个命令结果的命令(如
WATCH
和事务命令)。
使用场景:
- 当你需要执行多个键值操作,如
SET
、GET
、INCR
、LPUSH
等。 - 当你需要批量获取或设置多个键的值。
- 当你需要执行一系列命令,而这些命令可以一次性发送以提高效率。
使用Jedis可以实现Redis的事务和Pipeline
// ---------事务-----------
Jedis jedis = new Jedis("localhost", 6379)
// 开始事务
jedis.multi();
// 在事务中执行多个命令
jedis.set("key1", "value1");
jedis.set("key2", "value2");
jedis.incr("counter"); // 假设 counter 是一个已经存在的计数器键
// 执行事务,并获取所有命令的响应结果
List<Object> results = jedis.exec();
if (results != null) {
// 遍历结果
for (Object result : results) {
System.out.println(result);
}
} else {
// 如果事务因为某些原因被中断(如 Watch 失败),则结果将为 null
System.out.println("事务执行被中断");
}
// 关闭 Jedis 连接
jedis.close();
// ---------Pipeline-----------
Jedis jedis = new Jedis("localhost", 6379); // 创建 Jedis 实例连接到 Redis
Pipeline pipeline = jedis.pipelined(); // 开启 Pipeline
// 向 Pipeline 添加命令
pipeline.set("key1", "value1");
pipeline.get("key1");
pipeline.incr("counter");
// 执行 Pipeline 并获取所有响应
List<Object> results = pipeline.syncAndReturnAll();
// 处理结果
String value1 = (String) results.get(0); // key1 的值
String value2 = (String) results.get(1); // key1 的值(通过 get 命令获取)
Long counter = (Long) results.get(2); // counter 的新值
// 关闭 Jedis 连接
jedis.close();
// Redission也可以实现事务和pipeline
// 假设redissonClient已经创建并配置好连接
RedissonClient redisson = Redisson.create()
RTransaction transaction = redisson.getTransaction();
RPipeline pipeline = redisson.pipeline();
Lua
Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放。Redis 的 Lua 脚本是一种在 Redis 服务器上执行 Lua 代码的能力。Redis 从 2.6 版本开始内置了对 Lua 脚本的支持。使用 Lua 脚本,你可以实现以下目标:
-
原子性:Lua 脚本作为一个单独的命令执行,保证了脚本中的命令序列是原子性的。这意味着脚本中的所有命令要么全部执行,要么全部不执行,中间不会有其他命令插入。(也就是说,Redis保证以原子方式执行Lua脚本,但是不保证脚本中所有操作要么都执行或者都回滚。)
Lua脚本可以保证原子性,因为Redis会将Lua脚本封装成一个单独的事务,而这个单独的事务会在Redis客户端运行时,由Redis服务器自行处理并完成整个事务,如果在这个进程中有其他客户端请求的时候,Redis将会把它暂存起来,等到 Lua 脚本处理完毕后,才会再把被暂存的请求恢复。
-
减少网络开销:通过将多个命令打包在 Lua 脚本中一次性发送到服务器,可以减少客户端和服务器之间的网络往返次数。
-
复用性:客户端发送的 Lua 脚本会被缓存在 Redis 中,其他客户端可以复用这一脚本而不需要重新发送相同的逻辑代码。
-
执行复杂逻辑:Lua 脚本允许在 Redis 服务器上执行复杂的业务逻辑,而不需要在客户端进行多重逻辑处理。
Lua与事务的区别:Redis的事务在执行过程中,如果有某一个命令失败了,是不影响后续命令的执行的;而Lua脚本中,如果执行过程中某个命令执行失败了,是会影响后续命令执行的。
乐观锁
用WATCH命令可以实现乐观锁:这个命令一旦运行,他会确保只有在 WATCH 监视的键在调用 EXEC 之前没有改变时,后续的事务才会执行。
WATCH cainiao
GET cainiao
MULTI
set cainiao <从GET获得的值 + 任务增量>
EXEC