1、Pipeline功能
Pileline是redis提供的一种批处理通信协议的能力,目的是节约网络传输时间。通过一次发送多次请求命令,从而减少网络传输的时间,Pipeline不具备事务性。原理如图:
测试代码如下:(注意看输出结果,就明白了)
import redis
# 创建 Redis 连接
r = redis.Redis(host='localhost', port=6379, socket_timeout=5)
# 创建 Pipeline 对象
pipe = r.pipeline()
# 往 Pipeline 中加入多个命令
pipe.set("key1", "value1")
pipe.set("key2", "value2")
pipe.get("key1")
# 执行 Pipeline 中的所有命令并获取结果
results = pipe.execute()
print(results) # 输出[True, True, b'value1']
2、事务
把用户定义的一些数据库操作,看成一个整体,要么全部执行,要么全部不执行。
2.1、方案一:MULTI、EXEC
redis提供了MULTI、EXEC、WATCH和DISCARD命令来实现事务,在一个事务中,所有命令被收集起来以原子性执行。
相关命令
MULTI:事务开始,之后的命令会被放入一个队列,不会立即执行
EXEC:执行事务
DISCARD:取消事务
WATCH:检测key的变动,若在事务执行中,key变动则取消事务;在事务开启前调用。
测试代码:multi~exec之间的代码具有事务性,保证原子性
import redis
# 创建 Redis 连接
r = redis.Redis(host='localhost', port=6379, socket_timeout=5)
# 创建 Pipeline 对象
pipe = r.pipeline()
# 往 Pipeline 中加入多个命令
pipe.watch("key1")
# multi标记事务开始
pipe.multi()
pipe.set("key1", "value1")
pipe.set("key2", 10)
pipe.incr("key2")
pipe.get("key1")
# 执行 Pipeline 中的所有命令并获取结果
results = pipe.execute()
print(results) # 输出类似 [True, True, 11, b'value1']
2.2、方案二:lua脚本(工作中用的多)
redis中加载了一个lua虚拟机,用来执行lua脚本。redis确保lua脚本的执行是原子性的,使用lua脚本可以在一个请求中执行多个redis命令,并且这些命令以原子性执行,要么全部执行,要么全部不执行。使用EVAL和EVALSHA执行脚本
相关命令
1、EVAL script numkeys key [key ...] arg [arg ...]
script 是你想要执行的 Lua 脚本代码。
numkeys 是脚本将要使用的键的数量。这很重要,因为 Redis 需要知道哪些键是该脚本的一 部分,以便正确地处理锁和其他并发控制机制。
key [key ...] 是提供给脚本的键列表。
arg [arg ...] 是传递给脚本的额外参数,它们不是键。
2、EVALSHA sha1 numkeys key [key ...] arg [arg ...]
sha1 是之前通过 EVAL 命令执行过的 Lua 脚本的 SHA1 校验和。如果 Redis 已经缓存了这个脚本,它可以直接执行而不需要重新解析脚本文本。
其余参数与 EVAL 相同。
eval测试代码
import redis
# 创建 Redis 连接
r = redis.Redis(host='localhost', port=6379, socket_timeout=5)
# 定义 Lua 脚本
lua_script = """
local key = KEYS[1]
local value = tonumber(ARGV[1]) -- 将传入的参数转换为数字
redis.call('set', key, value)
local retrieved_value = redis.call('get', key)
local doubled_value = tonumber(retrieved_value) * 2 -- 将获取到的值转换为数字后乘以2
return doubled_value
"""
# 使用 EVAL 执行 Lua 脚本,传入键名 'mykey'
result = r.eval(lua_script, 1, 'mykey', 100)
print(f"The result is: {result}")
evalsha测试代码:
# 从文件中读取 lua脚本内容
cat test1.lua | redis-cli script load --pipe
# 加载 lua脚本字符串 生成 sha1
> script load 'local val = KEYS[1]; return val'
"b8059ba43af6ffe8bed3db65bac35d452f8115d8"
# 检查脚本缓存中,是否有该 sha1 散列值的lua脚本
> script exists "b8059ba43af6ffe8bed3db65bac35d452f8115d8"
1) (integer) 1
# 清除所有脚本缓存
> script flush
OK
# 如果当前脚本运行时间过长(死循环),可以通过 script kill 杀死当前运行的脚本
> script kill
(error) NOTBUSY No scripts in execution right now
3、ACID特性
redis本身不是一个完全符合ACID(原子性,一致性、隔离性、持久性)特性的数据库,而是提供了一些特性来接近这些原则。
A(原子性):
D(持久性)
4、同步连接
redis同步连接指客户端和redis服务端的连接方式,每个命令都是同步执行。意味着客户端发送一个命令给redis服务器时,必须要等服务器响应,才能继续下一条指令。
4.1、hiredis(C库)
redis源码中有带hiredis,hiredis是一个用于解析Redis协议的C库,该库中有同步API、异步API等。使用该库可以实现C语言操作Redis数据库,C++操作Redis可参考redis-plus-plus库。
hiredis路径:redis\deps\hiredis # 相对redis源码
hiredis编译
cd hiredis
make
sudo make install
测试代码:
gcc redis-test-sync.c -o redis-test-sync -lhiredis
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <hiredis/hiredis.h>
int current_tick() {
int t = 0;
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (int)ti.tv_sec * 1000;
t += ti.tv_nsec / 1000000;
return t;
}
int main(int argc, char **argv) {
unsigned int j, isunix = 0;
redisContext *c;
redisReply *reply;
const char *hostname = "127.0.0.1";
int port = 6379;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
// 连接redis服务器
c = redisConnectWithTimeout(hostname, port, timeout);
if (c == NULL || c->err) {
if (c) {
printf("Connection error: %s\n", c->errstr);
redisFree(c);
} else {
printf("Connection error: can't allocate redis context\n");
}
exit(1);
}
int num = (argc > 1) ? atoi(argv[1]) : 1000;
int before = current_tick();
for (int i=0; i<num; i++) {
// 发送redis命令
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
}
int used = current_tick()-before;
printf("after %d exec redis command, used %d ms\n", num, used);
/* Disconnects and frees the context */
redisFree(c);
return 0;
}
5、异步连接
异步连接允许客户端在发送命令后不等待响应,可以继续执行其他操作。实现异步连接需要一个 Redis 驱动,该驱动需要将 Redis 连接融入项目中的 Reactor 模式进行管理。
这部分还未理解很深,可参考网上其他链接。
学习链接:https://github.com/0voice