Redis 作为内存数据结构存储快速入门指南
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/get-started/data-store/
代码示例图例
以下代码示例展示了如何使用不同的编程语言和客户端库执行相同的操作:
- Redis CLI:Redis 的命令行界面
- C#(同步):StackExchange.Redis 同步客户端
- C#(异步):StackExchange.Redis 异步客户端
- Go:go-redis 客户端
- Java(同步 - Jedis):Jedis 同步客户端
- Java(异步 - Lettuce):Lettuce 异步客户端
- Java(响应式 - Lettuce):Lettuce 响应式/流式客户端
- JavaScript(Node.js):node-redis 客户端
- PHP:Predis 客户端
- Python:redis-py 客户端
- Rust(同步):redis-rs 同步客户端
- Rust(异步):redis-rs 异步客户端
每个代码示例都展示了跨不同语言的相同基本操作。具体语法和模式因语言和客户端库而异,但底层的 Redis 命令和行为保持一致。
本快速入门指南将向您展示如何:
- 开始使用 Redis
- 在 Redis 中通过键存储数据
- 从 Redis 中通过键检索数据
- 扫描键空间以查找匹配特定模式的键
本文中的示例引用了一个简单的自行车库存。
设置
开始使用 Redis 的最简单方式是使用 Redis Cloud:
- 创建一个免费账户。
2. 按照说明创建一个免费数据库。
您也可以选择遵循安装指南在本地计算机上安装 Redis。
连接
第一步是连接到 Redis。您可以在本文档站的工具部分中找到有关连接选项的更多详细信息。以下示例展示了如何连接到运行在 localhost(-h 127.0.0.1)并监听默认端口(-p 6379)的 Redis 服务器:
基础篇:连接到 Redis 服务器
难度: 初级
命令: REDIS-CLI
可用语言: Redis CLI, C#, Go, Java(同步 - Jedis), JavaScript(Node.js), Python
Redis CLI
> redis-cli -h 127.0.0.1 -p 6379C#
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
var ft = db.FT();
var json = db.JSON();Go
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // 无密码
DB: 0, // 使用默认 DB
Protocol: 2,
})Java(同步 - Jedis)
RedisClient jedis = RedisClient.create("localhost", 6379);JavaScript(Node.js)
const client = createClient();
client.on('error', err => console.log('Redis Client Error', err));
await client.connect();Python
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)您可以从 Redis Cloud 数据库配置页面复制并粘贴连接详情。以下是一个托管在 AWS 区域 us-east-1 并监听端口 16379 的云数据库的连接字符串示例:redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com:16379。连接字符串的格式为 host:port。您还必须复制并粘贴云数据库的用户名和密码,然后将凭证传递给客户端,或在连接建立后使用 AUTH 命令。
存储和检索数据
Redis 代表远程字典服务器(Remote Dictionary Server)。您可以使用与本地编程环境中相同的数据类型,但在服务器端的 Redis 中。
与字节数组类似,Redis 字符串存储字节序列,包括文本、序列化对象、计数器值和二进制数组。以下示例展示了如何设置和获取字符串值:
基础篇:使用 SET 和 GET 命令设置和检索字符串值
难度: 初级
可用语言: Redis CLI, C#, Go, Java(同步 - Jedis), JavaScript(Node.js), Python
Redis CLI
SET bike:1 "Process 134"
GET bike:1C#
using NRedisStack.Tests;
using StackExchange.Redis;
public class SetGetExample
{
public void Run()
{
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
bool status = db.StringSet("bike:1", "Process 134");
if (status)
Console.WriteLine("成功添加了一辆自行车。");
var value = db.StringGet("bike:1");
if (value.HasValue)
Console.WriteLine("自行车的名称是:" + value + "。");
}
}Go
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func ExampleClient_Set_and_get() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // 无密码
DB: 0, // 使用默认 DB
})
err := rdb.Set(ctx, "bike:1", "Process 134", 0).Err()
if err != nil {
panic(err)
}
fmt.Println("OK")
value, err := rdb.Get(ctx, "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Printf("自行车的名称是 %s", value)
}Java(同步 - Jedis)
package io.redis.examples;
import redis.clients.jedis.RedisClient;
public class SetGetExample {
public void run() {
RedisClient jedis = RedisClient.create("redis://localhost:6379");
String status = jedis.set("bike:1", "Process 134");
if ("OK".equals(status)) System.out.println("成功添加了一辆自行车。");
String value = jedis.get("bike:1");
if (value != null) System.out.println("自行车的名称是:" + value + "。");
jedis.close();
}
}JavaScript(Node.js)
import { createClient } from 'redis';
const client = createClient();
client.on('error', err => console.log('Redis Client Error', err));
await client.connect().catch(console.error);
await client.set('bike:1', 'Process 134');
const value = await client.get('bike:1');
console.log(value);
// 返回 'Process 134'
await client.close();Python
"""
数据结构存储快速入门页面的代码示例:
https://redis.io/docs/latest/develop/get-started/data-store/
"""
import redis
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
res = r.set("bike:1", "Process 134")
print(res)
# >>> True
res = r.get("bike:1")
print(res)
# >>> "Process 134"哈希相当于字典(dicts 或 hash maps)。除其他用途外,您可以使用哈希来表示普通对象并存储计数器的分组。以下示例说明了如何设置和访问对象的字段值:
基础篇:使用 HSET 设置多个字段、HGET 检索单个字段、HGETALL 一次性检索所有字段来存储和检索哈希数据结构
难度: 初级
命令: HSET, HGET, HGETALL
复杂度:
- HSET: O(1)
- HGET: O(1)
- HGETALL: O(N)
可用语言: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Ruby, Rust(异步), Rust(同步)
Redis CLI
> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972
(integer) 4
> HGET bike:1 model
"Deimos"
> HGET bike:1 price
"4972"
> HGETALL bike:1
1) "model"
2) "Deimos"
3) "brand"
4) "Ergonom"
5) "type"
6) "Enduro bikes"
7) "price"
8) "4972"C#
db.HashSet("bike:1", [
new("model", "Deimos"),
new("brand", "Ergonom"),
new("type", "Enduro bikes"),
new("price", 4972)
]);
Console.WriteLine("已创建哈希");
// 已创建哈希
var model = db.HashGet("bike:1", "model");
Console.WriteLine($"型号: {model}");
// 型号: Deimos
var price = db.HashGet("bike:1", "price");
Console.WriteLine($"价格: {price}");
// 价格: 4972
var bike = db.HashGetAll("bike:1");
Console.WriteLine("bike:1");
Console.WriteLine(string.Join("\n", bike.Select(b => $"{b.Name}: {b.Value}")));
// Bike:1:
// model: Deimos
// brand: Ergonom
// type: Enduro bikes
// price: 4972Go
hashFields := []string{
"model", "Deimos",
"brand", "Ergonom",
"type", "Enduro bikes",
"price", "4972",
}
res1, err := rdb.HSet(ctx, "bike:1", hashFields).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 4
res2, err := rdb.HGet(ctx, "bike:1", "model").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> Deimos
res3, err := rdb.HGet(ctx, "bike:1", "price").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 4972
cmdReturn := rdb.HGetAll(ctx, "bike:1")
res4, err := cmdReturn.Result()
if err != nil {
panic(err)
}
fmt.Println(res4)
// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]
type BikeInfo struct {
Model string `redis:"model"`
Brand string `redis:"brand"`
Type string `redis:"type"`
Price int `redis:"price"`
}
var res4a BikeInfo
if err := cmdReturn.Scan(&res4a); err != nil {
panic(err)
}
fmt.Printf("型号: %v, 品牌: %v, 类型: %v, 价格: $%v\n",
res4a.Model, res4a.Brand, res4a.Type, res4a.Price)
// >>> 型号: Deimos, 品牌: Ergonom, 类型: Enduro bikes, 价格: $4972Java(异步 - Lettuce)
Map<String, String> bike1 = new HashMap<>();
bike1.put("model", "Deimos");
bike1.put("brand", "Ergonom");
bike1.put("type", "Enduro bikes");
bike1.put("price", "4972");
CompletableFuture<Void> setGetAll = asyncCommands.hset("bike:1", bike1).thenCompose(res1 -> {
System.out.println(res1); // >>> 4
return asyncCommands.hget("bike:1", "model");
}).thenCompose(res2 -> {
System.out.println(res2); // >>> Deimos
return asyncCommands.hget("bike:1", "price");
}).thenCompose(res3 -> {
System.out.println(res3); // >>> 4972
return asyncCommands.hgetall("bike:1");
})
.thenAccept(System.out::println)
// >>> {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}
.toCompletableFuture();Java(响应式 - Lettuce)
Map<String, String> bike1 = new HashMap<>();
bike1.put("model", "Deimos");
bike1.put("brand", "Ergonom");
bike1.put("type", "Enduro bikes");
bike1.put("price", "4972");
Mono<Long> setGetAll = reactiveCommands.hset("bike:1", bike1).doOnNext(result -> {
System.out.println(result); // >>> 4
});
setGetAll.block();
Mono<String> getModel = reactiveCommands.hget("bike:1", "model").doOnNext(result -> {
System.out.println(result); // >>> Deimos
});
Mono<String> getPrice = reactiveCommands.hget("bike:1", "price").doOnNext(result -> {
System.out.println(result); // >>> 4972
});
Mono<List<KeyValue<String, String>>> getAll = reactiveCommands.hgetall("bike:1").collectList().doOnNext(result -> {
System.out.println(result);
// >>> [KeyValue[type, Enduro bikes], KeyValue[brand, Ergonom],
// KeyValue[price, 4972], KeyValue[model, Deimos]]
});Java(同步 - Jedis)
Map<String, String> bike1 = new HashMap<>();
bike1.put("model", "Deimos");
bike1.put("brand", "Ergonom");
bike1.put("type", "Enduro bikes");
bike1.put("price", "4972");
Long res1 = jedis.hset("bike:1", bike1);
System.out.println(res1); // 4
String res2 = jedis.hget("bike:1", "model");
System.out.println(res2); // Deimos
String res3 = jedis.hget("bike:1", "price");
System.out.println(res3); // 4972
Map<String, String> res4 = jedis.hgetAll("bike:1");
System.out.println(res4); // {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}JavaScript(Node.js)
const res1 = await client.hSet(
'bike:1',
{
'model': 'Deimos',
'brand': 'Ergonom',
'type': 'Enduro bikes',
'price': 4972,
}
)
console.log(res1) // 4
const res2 = await client.hGet('bike:1', 'model')
console.log(res2) // 'Deimos'
const res3 = await client.hGet('bike:1', 'price')
console.log(res3) // '4972'
const res4 = await client.hGetAll('bike:1')
console.log(res4)
/*
{
brand: 'Ergonom',
model: 'Deimos',
price: '4972',
type: 'Enduro bikes'
}
*/PHP
$res1 = $r->hmset('bike:1', [
'model' => 'Deimos',
'brand' => 'Ergonom',
'type' => 'Enduro bikes',
'price' => 4972,
]);
echo $res1 . PHP_EOL;
// >>> 4
$res2 = $r->hget('bike:1', 'model');
echo $res2 . PHP_EOL;
// >>> Deimos
$res3 = $r->hget('bike:1', 'price');
echo $res3 . PHP_EOL;
// >>> 4972
$res4 = $r->hgetall('bike:1');
echo json_encode($res3) . PHP_EOL;
// >>> {"name":"Deimos","brand":"Ergonom","type":"Enduro bikes","price":"4972"}Python
res1 = r.hset(
"bike:1",
mapping={
"model": "Deimos",
"brand": "Ergonom",
"type": "Enduro bikes",
"price": 4972,
},
)
print(res1)
# >>> 4
res2 = r.hget("bike:1", "model")
print(res2)
# >>> 'Deimos'
res3 = r.hget("bike:1", "price")
print(res3)
# >>> '4972'
res4 = r.hgetall("bike:1")
print(res4)
# >>> {'model': 'Deimos', 'brand': 'Ergonom', 'type': 'Enduro bikes', 'price': '4972'}Ruby
res1 = r.hset('bike:1', {
'model' => 'Deimos',
'brand' => 'Ergonom',
'type' => 'Enduro bikes',
'price' => 4972
})
puts res1 # 4
res2 = r.hget('bike:1', 'model')
puts res2 # Deimos
res3 = r.hget('bike:1', 'price')
puts res3 # 4972
res4 = r.hgetall('bike:1')
puts res4.inspect
# {"model"=>"Deimos", "brand"=>"Ergonom", "type"=>"Enduro bikes", "price"=>"4972"}Rust(异步)
let hash_fields = [
("model", "Deimos"),
("brand", "Ergonom"),
("type", "Enduro bikes"),
("price", "4972"),
];
if let Ok(res) = r.hset_multiple("bike:1", &hash_fields).await {
let res: String = res;
println!("{res}"); // >>> OK
}
match r.hget("bike:1", "model").await {
Ok(res) => {
let res: String = res;
println!("{res}"); // >>> Deimos
},
Err(e) => {
println!("获取 bike:1 model 时出错: {e}");
return;
}
};
match r.hget("bike:1", "price").await {
Ok(res) => {
let res: String = res;
println!("{res}"); // >>> 4972
},
Err(e) => {
println!("获取 bike:1 price 时出错: {e}");
return;
}
};
match r.hgetall("bike:1").await {
Ok(res) => {
let res: Vec<(String, String)> = res;
println!("{res:?}");
// >>> [("model", "Deimos"), ("brand", "Ergonom"), ("type", "Enduro bikes"), ("price", "4972")]
},
Err(e) => {
println!("获取 bike:1 时出错: {e}");
return;
}
};Rust(同步)
let hash_fields = [
("model", "Deimos"),
("brand", "Ergonom"),
("type", "Enduro bikes"),
("price", "4972"),
];
if let Ok(res) = r.hset_multiple("bike:1", &hash_fields) {
let res: String = res;
println!("{res}"); // >>> OK
}
match r.hget("bike:1", "model") {
Ok(res) => {
let res: String = res;
println!("{res}"); // >>> Deimos
},
Err(e) => {
println!("获取 bike:1 model 时出错: {e}");
return;
}
};
match r.hget("bike:1", "price") {
Ok(res) => {
let res: String = res;
println!("{res}"); // >>> 4972
},
Err(e) => {
println!("获取 bike:1 price 时出错: {e}");
return;
}
};
match r.hgetall("bike:1") {
Ok(res) => {
let res: Vec<(String, String)> = res;
println!("{res:?}");
// >>> [("model", "Deimos"), ("brand", "Ergonom"), ("type", "Enduro bikes"), ("price", "4972")]
},
Err(e) => {
println!("获取 bike:1 时出错: {e}");
return;
}
};您可以在本文档站的数据类型部分获得可用数据类型的完整概述。每种数据类型都有命令允许您操作或检索数据。命令参考提供了详细的解释。
扫描键空间
Redis 中的每个条目都有一个唯一键。所有条目都位于 Redis 键空间中。您可以通过 SCAN 命令扫描 Redis 键空间。以下是一个扫描前 100 个带有前缀 bike: 的键的示例:
SCAN 0 MATCH "bike:*" COUNT 100SCAN 返回一个游标位置,允许您迭代扫描下一批键,直到达到游标值 0。
后续步骤
您可以通过阅读以下额外的快速入门指南来利用 Redis 处理更多使用场景: