介绍
在其核心上,redis是一个单线程系统。要水平扩展,您需要使用称为 redis cluster 的部署拓扑,该拓扑自动将数据自动划分跨多个redis节点。 Redis不是一致的哈希(这是其他系统中常用技术),而是采用了另一种碎片形式,其中每个键在概念上都是 hash slot的一部分。。
从单个redis实例转移到redis群集后,您就可以进入分布式系统的世界。它有其优点,但也有很多限制:
- 碎片/节点管理 - 这包括添加/删除节点,重新平衡数据等。
- 处理最终的一致性和可能的数据丢失-REDIS复制是异步的,这可能会导致复制品的过时读取。更糟糕的是,由于主要节点故障和分裂脑场景可能会导致数据丢失。
- 应用程序级别的复杂性 - REDIS群集不支持属于不同插槽的密钥上的多键操作。这样做会导致(in)著名的
CROSSSLOT
错误。
就像"The Best Code is No Code At All"一样,理想的分布式系统也许是不需要分发的系统。在Redis的情况下,您可以使用蜻蜓等单节点解决方案来实现此目标。
多亏了它的API compatibility,蜻蜓可以用作Redis的 drop-in替换。蜻蜓解决了Redis群集的大多数缺点,从而大大降低了操作的复杂性和提高可靠性。
- 蜻蜓依赖于垂直缩放单个节点。这使其比Redis群集和其他多节点方法更快,更便宜,更可预测。
- 与Redis不同,Dragonfly有一个multi-threaded, shared-nothing architecture,可以垂直扩展到每个实例的记忆。
在此博客文章中,您将学习如何将数据从redis群集迁移到单节点的蜻蜓实例。我们将使用Dragonfly的emulated cluster mode,该emulated cluster mode更容易将您现有的应用程序从Redis群集迁移到Dragonfly。我们将使用sample application来演示迁移过程并逐步介绍一切。
先决条件
开始之前,请确保已安装以下内容:
- Docker和Docker Compose
- redis cli
- RIOT Redis(用于迁移)
- Go programming language(版本1.18或更高版本)
克隆存储库并导航到正确的目录:
git clone git@github.com:dragonflydb/redis-cluster-application-example.git
cd redis-cluster-application-example
启动蜻蜓和雷迪斯集群
要保持简单,您将使用Docker组成:
docker compose -p migrate-to-dragonflydb up
验证Docker容器正在运行:
docker compose -p migrate-to-dragonflydb ps
预期输出:
NAME COMMAND SERVICE STATUS PORTS
migrate-to-dragonflydb-dragonfly-1 "entrypoint.sh drago…" dragonfly running (healthy) 0.0.0.0:6380->6379/tcp
migrate-to-dragonflydb-rediscluster-1 "docker-entrypoint.s…" rediscluster running 6379/tcp, 0.0.0.0:7000-7002->7000-7002/tcp
从redis容器中,启动一个redis群集:
docker exec -it migrate-to-dragonflydb-rediscluster-1 /bin/bash
./start-cluster.sh
# expected output
Redis Cluster ready
此时,您应该有:
- 在port
6380
上访问的蜻蜓实例2 - 在
7000
,7001
和7002
上访问的三节点REDIS群集可访问
开始迁移过程之前,让我们快速查看示例应用程序。
应用概述
您可以在/app
下访问源代码here。
该应用程序是用GO编写的Web服务,可通过Go Redis客户端提供REDIS的数据。该应用程序在启动期间将示例数据加载到redis群集中 - 这是存储为HASH
的虚拟用户数据。
// main.go
func loadData() {
fmt.Println("loading sample data into redis.....")
user := map[string]string{}
for i := 0; i < 100; i++ {
key := "user:" + strconv.Itoa(i)
name := "user-" + strconv.Itoa(i)
email := name + "@foo.com"
user["name"] = name
user["email"] = email
err := client.HMSet(context.Background(), key, user).Err()
if err != nil {
log.Fatal("failed to load data", err)
}
}
fmt.Println("data load complete")
}
它还提供了一个REST API来获取用户数据的ID。
func main() {
r := mux.NewRouter()
r.HandleFunc("/{id}", func(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
hashName := "user:" + id
fmt.Println("getting data for", hashName)
var user User
err := client.HMGet(context.Background(), hashName, "name", "email").Scan(&user)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = json.NewEncoder(w).Encode(user)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
log.Fatal(http.ListenAndServe(":8080", r))
}
应用程序运行后,您可以使用任何HTTP
client(例如curl
)使用应用程序公开的API查询REDIS的数据 - 稍后会详细介绍。
此博客文章使用RIOT-Redis,这是Redis的数据迁移工具。它不使用REPLICAOF
命令,而是使用DUMP
&RESTORE
或基于类型的replication(例如GET
和SET
)在snapshot
或live
模式中实现客户端复制。
为迁移做准备
1.启动客户端应用程序
在新终端中:
cd app
export REDIS_HOSTS="localhost:7000,localhost:7001,localhost:7002"
export LOAD_DATA=true
go run main.go
该应用程序将将种子数据(100 HASH
es)加载到源redis群集中。您应该看到以下输出:
loading sample data into redis.....
data load complete
2.验证源redis群集中的数据
数据负载完成后,请连接到redis群集中的每个节点,然后检查键的数量:
redis-cli -p 7000 -c DBSIZE
# output
(integer) 28
redis-cli -p 7001 -c DBSIZE
# output
(integer) 36
redis-cli -p 7002 -c DBSIZE
# output
(integer) 36
如果添加它们,它们应该是100
-与我们在启动过程中加载的应用程序相同。
您可以使用curl
:
测试应用程序
curl -X GET http://localhost:8080/1
# expected output
{"name":"user-1","email":"user-1@foo.com"}
curl -X GET http://localhost:8080/2
# expected output
{"name":"user-2","email":"user-2@foo.com"}
您已经验证了该应用程序是否按预期工作。
将数据从redis群集迁移到单个节点蜻蜓实例(在群集模式下)
1.验证蜻蜓中的数据
redis-cli -p 6380 -c DBSIZE
# expected output
(integer) 0
正如预期的那样, 在蜻蜓实例中。
2.使用RIOT-REDIS工具启动迁移
使用以下riot-redis
命令:
riot-redis --info -h localhost -p 7000 --cluster replicate-ds -h localhost -p 6380 --batch 10
我们使用redis群集作为源(请注意端口号和--cluster
标志)。这将迁移数据从redis cluster节点localhost:7000
到蜻蜓实例localhost:6380
。 --batch
标志指定要迁移的密钥数。
您应该看到迁移作业的类似输出(时间和其他统计信息可能有所不同):
Executing step: [snapshot-replication]
Job: [SimpleJob: [name=scan-reader]] launched with the following parameters: [{}]
Executing step: [scan-reader]
Scanning 0% │ │ 0/10 (0:00:00 / ?) ?/s
Step: [scan-reader] executed in 79ms
Closing with items still in queue
Job: [SimpleJob: [name=scan-reader]] completed with the following parameters: [{}] and the following status: [COMPLETEScanning 100% │██████████████████████████████████████████████████████████████│ 100/100 (0:00:00 / 0:00:00) ?/s
Step: [snapshot-replication] executed in 856ms
Executing step: [verification]
Job: [SimpleJob: [name=RedisItemReader]] launched with the following parameters: [{}]
Executing step: [RedisItemReader]
Verifying 0% │ │ 0/10 (0:00:00 / ?) ?/s
Step: [RedisItemReader] executed in 147ms
Closing with items still in queue
Job: [SimpleJob: [name=RedisItemReader]] completed with the following parameters: [{}] and the following status: [COMPVerifying 100% │██████████████████████████████│ 100/100 (0:00:00 / 0:00:00) ?/s >0 T0 ≠0 ⧗0
Verification completed - all OK
Step: [verification] executed in 368ms
Job: [SimpleJob: [name=snapshot-replication]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 1s443ms
3.验证蜻蜓中的数据
redis-cli -p 6380 -c DBSIZE
# expected output
(integer) 100
redis-cli -p 6380 -c HMGET user:42 name email
# expected output
1) "user-42"
2) "user-42@foo.com"
如果迁移成功,您应该在蜻蜓实例中看到100
项目。
4.将应用程序切换到使用蜻蜓
在启动应用程序之前,请关闭上一个实例以避免端口8080
Collision
export REDIS_HOSTS="localhost:6380"
export LOAD_DATA=false
go run main.go
# expected output
connected to redis localhost:6380
请注意,我们更改了REDIS_HOSTS
值以指向Dragonfly,并且我们不再使用数据加载选项。这只会启动Web服务器并提供来自Dragonfly的数据。
您可以这样测试:
curl -X GET http://localhost:8080/4
curl -X GET http://localhost:8080/5
curl -X GET http://localhost:8080/42
该应用程序将继续按预期工作并提供来自蜻蜓的数据。
我们所做的只是指向蜻蜓而不是Redis群集,而无需更改代码。
完成本教程中的步骤后,请使用此命令停止redis群集和蜻蜓实例:
docker compose -p migrate-to-dragonflydb down -v
结论和下一步
在本教程中,我们使用RIOT-REDIS工具将数据从Redis群集迁移到蜻蜓。无需更改任何应用程序代码!多亏了Draginfly Cluster Mode,单个蜻蜓实例可以实现与多节点redis群集相同的容量。您使用的迁移技术取决于源群集。这可能与REPLICAOF
命令(Dragonfly supports a primary/secondary replication model),MIGRATE
命令,甚至是自定义应用程序。
由于蜻蜓的硬件效率,您可以在一个小的8GB
实例上运行一个节点实例,并垂直缩放到带有64
内核的大型768GB
机器。这降低了基础架构成本,并降低了分布式数据系统固有的复杂性。您可以使用Dragonfly,而不是花费时间和精力来维持REDIS群集,而是可以使用与单节点Redis相同的语义,以及在需要时模仿Redis群集的能力。
在撰写本文时,Dragonfly has implemented more than 200 Redis commands代表了绝大多数用例的良好覆盖范围。
如果您想了解更多信息,请查看我们在how to fire up a Dragonfly instance上的文档,带上client of your choice并开始使用熟悉的Redis commands建造!