> 文章列表 > 从代码使用角度介绍redis

从代码使用角度介绍redis

从代码使用角度介绍redis

网上关于redis的介绍,很多理论性的内容了,但是当我想要真正在编程中使用redis时才发现对于初学者而言,理论到实践还是有很大的跨度的。首先第一步,我如何找到合适的库,或者自己写轮子去使用?

redis本质是对内存中数据的管理,它本身也是一个程序。 所以,从代码层面使用redis,就是使用编码语言调用redis的API 和redis进行网络通讯。

python的使用方式,请参考我redis专栏中的另一篇文章。这里不再赘述。相比更简单直观的python ,c++使用redis的方式更加细致划分。这里我们介绍两个库:cpp_redishiredis

首先介绍两个库:
cpp_redishiredis 都是 Redis 的客户端库,它们通过网络协议与Redis服务器进行通信,使用的是Redis提供的API。

具体来说,cpp_redishiredis 在操作系统层面上与Redis进行通信的方式有所不同:

  1. cpp_redis
    cpp_redis 是C++语言编写的Redis客户端库,基于 Boost.Asio 网络库实现。它通过 TCP/IP 协议连接Redis服务器,并使用Redis提供的协议与服务器进行通信。cpp_redis 支持异步和同步两种模式,可以通过回调函数或者 Future 对象获取结果。

  2. hiredis
    hiredis 是C语言编写的Redis客户端库,它使用标准的 socket API进行网络通信。hiredis支持同步和异步两种模式,同步模式下会阻塞调用线程直到操作完成,而异步模式则需要设置回调函数来获取结果。hiredis支持多个Redis服务器的负载均衡,可以自动切换到可用的服务器。

另外对于技术选型请参考作者以下经验:

cpp_redis和hiredis都是Redis的C++客户端库,它们都提供了类似的API来访问Redis服务器。但是它们之间也有一些区别,如下:

  1. 技术架构:cpp_redis基于Boost.Asio实现异步I/O,而hiredis则使用了自己的事件处理机制。

  2. 可移植性:cpp_redis旨在提供跨平台支持,因为它可以在Windows和Linux系统上运行,而hiredis则更专注于Unix-like系统。

  3. API设计:cpp_redis的API采用模板和lambda表达式,比较现代化;hiredis则使用传统的C函数调用方式。

  4. 功能特性:cpp_redis集成了Redis Sentinel和Cluster模式的支持,而hiredis仅限于普通模式。

  5. 性能表现:两个库的性能相当,但cpp_redis应该会更快一些,因为它使用了异步I/O,这样可以更高效地利用底层套接字。

总之,两个库都非常优秀,选择哪一个取决于你的需求和偏好。如果你需要更丰富的功能支持和跨平台兼容性,那么cpp_redis可能是更好的选择;如果你需要更简单和稳定的库,并且只在Unix-like系统上运行,那么hiredis可能更适合你。


那么接下来开始实际的代码介绍:

非基础的高级特性

redis的事务功能

Redis的事务是通过MULTI、EXEC、DISCARD和WATCH四个命令实现的,其中MULTI和EXEC必须成对使用。

下面是一个简单的C++代码示例,说明如何使用Redis的事务特性:

#include <iostream>
#include <cstring>
#include <hiredis/hiredis.h>int main() {redisContext *c = redisConnect("127.0.0.1", 6379);if (c == NULL || c->err) {std::cerr << "Connection error: " << c->errstr << std::endl;return 1;}// 开始事务redisReply *reply = (redisReply*)redisCommand(c, "MULTI");freeReplyObject(reply);// 执行多个命令reply = (redisReply*)redisCommand(c, "SET key1 value1");freeReplyObject(reply);reply = (redisReply*)redisCommand(c, "SET key2 value2");freeReplyObject(reply);// 提交事务并获取结果reply = (redisReply*)redisCommand(c, "EXEC");if (reply == NULL || reply->type != REDIS_REPLY_ARRAY) {std::cerr << "Transaction error: " << c->errstr << std::endl;return 1;} // 输出结果for (size_t i = 0; i < reply->elements; i++) {std::cout << "Result " << i << ": " << reply->element[i]->str << std::endl;}freeReplyObject(reply);// 关闭连接redisFree(c);return 0;
}

在上面的代码中,我们首先使用redisConnect()函数连接到本地Redis实例。然后,我们使用MULTI命令开启一个新的事务,并依次执行两个SET命令以将两个键值对存储在Redis中。最后,我们使用EXEC命令提交事务并获取结果。

需要注意的是,如果在事务执行期间有其他客户端修改了被WATCH命令监视的键,则提交事务时Redis将返回一个错误响应,事务中所有的操作都将被回滚。例如:

// 开始事务
reply = (redisReply*)redisCommand(c, "MULTI");
freeReplyObject(reply);// 监视 key1 
reply = (redisReply*)redisCommand(c, "WATCH key1");
freeReplyObject(reply);// 开始另一个客户端修改 key1
// ...
// ...// 尝试修改 key1
reply = (redisReply*)redisCommand(c, "SET key1 value1_new");
freeReplyObject(reply);// 提交事务,因为 key1 被其他客户端修改,所以这里会返回一个错误响应
reply = (redisReply*)redisCommand(c, "EXEC");
if (reply == NULL || reply->type != REDIS_REPLY_ERROR) {std::cerr << "Transaction error: " << c->errstr << std::endl;return 1;
}

Redis是一种高性能的键值存储数据库,除了基本的读写操作,还有以下特性:

发布/订阅功能:

Redis支持发布/订阅模式,可以通过PUBLISH命令向指定频道发布消息,SUBSCRIBE命令可以订阅频道并接收到发布的消息。

下面是使用C++ Redis客户端库cpp_redis示例代码展示如何进行发布和订阅:

#include <iostream>
#include <cpp_redis/cpp_redis>int main() {cpp_redis::subscriber sub;sub.connect("127.0.0.1", 6379, [](const std::string& host, std::size_t port, cpp_redis::subscriber::connect_state status) {if (status == cpp_redis::subscriber::connect_state::dropped) {std::cout << "client disconnected from " << host << ":" << port << std::endl;}});sub.subscribe("my_channel", [](const std::string& channel, const std::string& msg) {std::cout << "Received message: " << msg << " on channel: " << channel << std::endl;});sub.commit();cpp_redis::publisher pub;pub.connect("127.0.0.1", 6379, [](const std::string& host, std::size_t port, cpp_redis::publisher::connect_state status) {if (status == cpp_redis::publisher::connect_state::dropped) {std::cout << "client disconnected from " << host << ":" << port << std::endl;}});pub.publish("my_channel", "Hello world!");pub.commit();return 0;
}

Lua脚本支持:

Redis支持Lua脚本,可以通过EVAL和EVALSHA命令执行Lua脚本。下面是一个示例,将数字递增1并返回结果:

#include <iostream>
#include <cpp_redis/cpp_redis>int main() {cpp_redis::client client;client.connect("127.0.0.1", 6379, [](const std::string& host, std::size_t port, cpp_redis::client::connect_state status) {if (status == cpp_redis::client::connect_state::dropped) {std::cout << "client disconnected from " << host << ":" << port << std::endl;}});// 将数字递增1并返回结果client.eval("return redis.call('INCR', 'mykey')", [](cpp_redis::reply& reply) {std::cout << "Result: " << reply.as_integer() << std::endl;});client.sync_commit();return 0;
}

过期时间:

Redis支持设置键的过期时间,可以通过EXPIRE命令设置键的过期时间(秒),也可以通过PEXPIRE命令设置毫秒级别的过期时间。

下面是一个示例代码,将键的过期时间设置为10秒:

#include <iostream>
#include <cpp_redis/cpp_redis>int main() {cpp_redis::client client;client.connect("127.0.0.1", 6379, [](const std::string& host, std::size_t port, cpp_redis::client::connect_state status) {if (status == cpp_redis::client::connect_state::dropped) {std::cout << "client disconnected from " << host << ":" << port << std::endl;}});// 将键设置为123,并设置过期时间为10秒client.set("mykey", "123");client.expire("mykey", 10);client.sync_commit();return 0;
}

基础的数据结构读写方式

  1. 字符串类型

Redis 中字符串类型的基本操作包括 SET、GET、INCR、DECR 等。例如,可以使用以下 C++ 代码来设置和获取字符串类型的键值对:

redisReply* reply = static_cast<redisReply*>(redisCommand(context, "SET key value"));
freeReplyObject(reply);
reply = static_cast<redisReply*>(redisCommand(context, "GET key"));
std::cout << "value: " << reply->str << std::endl;
freeReplyObject(reply);
  1. 哈希表类型

Redis 中哈希表类型的基本操作包括 HSET、HGET、HDEL、HLEN 等。例如,可以使用以下 C++ 代码来设置和获取哈希表类型的键值对:

redisReply* reply = static_cast<redisReply*>(redisCommand(context, "HSET hash key value"));
freeReplyObject(reply);
reply = static_cast<redisReply*>(redisCommand(context, "HGET hash key"));
std::cout << "value: " << reply->str << std::endl;
freeReplyObject(reply);
  1. 列表类型

Redis 中列表类型的基本操作包括 LPUSH、RPUSH、LPOP、RPOP、LLEN 等。例如,可以使用以下 C++ 代码来设置和获取列表类型的值:

redisReply* reply = static_cast<redisReply*>(redisCommand(context, "LPUSH list value1"));
freeReplyObject(reply);
reply = static_cast<redisReply*>(redisCommand(context, "LPUSH list value2"));
reply = static_cast<redisReply*>(redisCommand(context, "LRANGE list 0 -1"));
for (int i = 0; i < reply->elements; ++i) {std::cout << "value: " << reply->element[i]->str << std::endl;
}
freeReplyObject(reply);
  1. 集合类型

Redis 中集合类型的基本操作包括 SADD、SREM、SMEMBERS、SISMEMBER、SCARD 等。例如,可以使用以下 C++ 代码来设置和获取集合类型的值:

redisReply* reply = static_cast<redisReply*>(redisCommand(context, "SADD set value1"));
freeReplyObject(reply);
reply = static_cast<redisReply*>(redisCommand(context, "SADD set value2"));
reply = static_cast<redisReply*>(redisCommand(context, "SMEMBERS set"));
for (int i = 0; i < reply->elements; ++i) {std::cout << "value: " << reply->element[i]->str << std::endl;
}
freeReplyObject(reply);
  1. 有序集合类型

Redis 中有序集合类型的基本操作包括 ZADD、ZREM、ZRANGE、ZSCORE、ZCARD 等。例如,可以使用以下 C++ 代码来设置和获取有序集合类型的值:

redisReply* reply = static_cast<redisReply*>(redisCommand(context, "ZADD zset 1 value1"));
freeReplyObject(reply);
reply = static_cast<redisReply*>(redisCommand(context, "ZADD zset 2 value2"));
reply = static_cast<redisReply*>(redisCommand(context, "ZRANGE zset 0 -1"));
for (int i = 0; i < reply->elements; ++i) {std::cout << "value: " << reply->element[i]->str << std::endl;
}
freeReplyObject(reply);

服务器高级架构体系:https://ke.qq.com/course/417774?flowToken=1010783