UDBKV 使用手册
文档名称:《UDBKV使用手册》
对象:DBA/运维/系统管理员/开发人员
关键字列表
| 关键字 | 解释 |
|---|---|
| sentinel | udbkv的哨兵模式。 |
| cluster | udbkv的集群模式。 |
阅读说明
本文档为通用指导文档,用户在参考的时候,对如下情况可能需要根据实际进行替换修改:
IP地址
目录名称、目录路径
用户名称
特别提醒:
在udbkv_cli客户端中,可以通过 help cmd 快速获取该命令的帮助信息。
内存数据库概述
Udbkv内存数据库是九有推出的一种专门用于内存中的数据结构存储,用作数据库、缓存、消息代理和流引擎。Udbkv提供数据结构,如字符串、哈希、列表、集合、带有范围查询的排序集合、位图、超级日志、地理空间索引和流。Udbkv具有内置的复制、Lua脚本、LRU淘汰策略、事务和不同级别的磁盘持久性,并通过Udbkv Sentinel和Udbkv Cluster的自动分区提供高可用性。
您可以对这些类型运行原子操作,比如追加到字符串;递增哈希中的值;将元素推送到列表中;计算集合的交、并、差;或者获取排序集合中排名最高的成员。
为了实现最佳性能,Udbkv使用内存中的数据集。根据您的使用情况,Udbkv可以通过定期将数据集转储到磁盘或通过将每个命令附加到基于磁盘的日志来持久化您的数据。如果您只需要一个功能丰富、网络化的内存缓存,也可以禁用持久性。
Udbkv支持异步复制,具有快速无阻塞同步和自动重新连接,并在网络断裂时进行部分重新同步。 Udbkv还包括:
事务
发布/订阅
Lua脚本
有限生存时间的keys
keys的LRU淘汰策略
自动故障转移
你可以在大多数编程语言中使用Udbkv。
Udbkv是用ANSI C编写的,可以在大多数POSIX系统上工作,比如Linux *、BSD和Mac OS X,没有外部依赖性。Linux和OS X是Udbkv开发和测试最多的两个操作系统,我们推荐使用Linux进行部署。
环境准备
系统要求
redhat/centos 系列建议选择7.6及以上版本
unvdb 用户需要 sudo 权限
数据盘建议使用一个挂载点,如
/data
创建用户
在root用户下执行:useradd unvdb 进行unvdb用户的创建。
执行:passwd unvdb 修改unvdb用户密码。
配置用户sudo权限
在安全允许的条件下,建议给unvdb用户加上sudo权限。
root用户执行visudo 进行编辑界面,找到 Allow root to run any commands anywhere所在行,在行下面添加:
```
unvdb ALL=(ALL) ALL
```
保存文件退出。su - unvdb 切换到unvdb用户,执行sudo id,预期会提示输入用户密码,输出为:
```
uid=0(root) gid=0(root) groups=0(root)
```
表示sudo权限添加成功。
配置用户limit参数
编辑/etc/security/limits.conf 文件,配置unvdb用户参数:
```
unvdb soft nproc 65536
unvdb hard nproc 65536
unvdb soft nofile 278528
unvdb hard nofile 278528
unvdb soft stack unlimited
unvdb soft core unlimited
unvdb hard core unlimited
unvdb soft memlock 250000000
unvdb hard memlock 250000000
```
保存文件后,执行su - unvdb切换到unvdb用户,执行ulimit -a 检查是否生效。
内核参数调整
在参数文件/etc/sysctl.conf中设置参数
```
net.core.somaxconn = 1024
vm.overcommit_memory = 1
```
修改参数后使用命令sysctl -p使之生效。
关闭透明大页功能
此处的操作系统是CentOS Linux release 7.6
查看透明大页是否开启
```
cat /sys/kernel/mm/transparent_hugepage/enabled
```
在/etc/rc.local中加入下面配置:
```
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
```
CentOS 7下关闭透明大页
```
# ls -ll /etc/rc.local
lrwxrwxrwx. 1 root root 13 Mar 15 2019 /etc/rc.local -> rc.d/rc.local
# ls -lrt /etc/rc.d/rc.local
-rw-r--r--. 1 root root 599 Jan 20 10:42 /etc/rc.d/rc.local
# chmod +x /etc/rc.d/rc.local
```
修改上面配置并不能立即禁用透明大页,需要重启操作系统或临时使用下面命令关闭透明大页。
```
echo never > /sys/kernel/mm/transparent_hugepage/enabled
```
安装库依赖
如果使用到TLS就需要yum -y install openssl openssl-devel
安装
获取包
联系九有数据库获取安装包udbkv-24.1.1-linux-x86_64.zip(x86_64包)或udbkv-24.1.1-linux-aarch64.zip(arm64包)。
安装步骤
```
cd /home/tom/
unzip udbkv-24.1.1-linux-x86_64.zip
cd udbkv-24.1.1-linux-x86_64
./setup udbkv [针对非sentinel实例]
./setup sentinel [针对sentinel实例]
```
下面的例子是执行./setup udbkv的结果:
[tom@localhost udbkv-24.1.1-linux-x86_64]$ ./setup.sh udbkv
```
===============================================================================
Welcome
-------
This installer will guide you through the installation of UnvDB.
Version: 24.1
Web: http://www.unvdb.com
Tel: 0755-84185890
You may quit the installer by typing "Exit",
or press <ENTER> key to continue:
===============================================================================
License Agreement
-----------------
Installation and Use of UnvDB Requires Acceptance of the Following
License Agreement:
UniversalDB (Shenzhen) Technology Co., Ltd.
"SOFTWARE END-USER LICENSE AGREEMENT"
The "software product" includes computer software, and may include
associated media, documentation.
This "software product" includes any upgrade and supplemental
materials to the original "software product" provided by UnvDB.
SOFTWARE PRODUCT LICENSE
The "software product" is under the protection of Copyrights
and International Conventions.
You may disagree the license by typing "Exit",
or press <ENTER> key to continue:
===============================================================================
Choose Data Folder
------------------
Please choose a data folder for this installation.
The data folder must be empty and writable.
Default Data Folder: /home/tom/udbkv-data
Enter the absolute path,
or press <ENTER> key to accept the default,
: /home/tom/replication/data7001 [此处为安装中交互界面用户输入的数据存放目录,默认为当前安装路径的上级录下的udbkv-data]
===============================================================================
Port
----
Please enter UnvDB listen port.
Port (Default: 6789): 7001 [此处为安装中交互界面用户输入的端口,默认为6789]
===============================================================================
Daemonize
--------
Please enter UnvDB daemonize ("yes" or "no").
daemonize (Default: yes): [此处为安装中交互界面用户输入的是否后台启动,默认为yes]
===============================================================================
Ready To Install
----------------
It will start to install UnvDB.
Product Name: UnvDB v24.1
Data Folder: /home/tom/replication/data7001
Port: 7001
Username:
You may quit the installer by typing "Exit",
or press <ENTER> key to continue:
===============================================================================
Installation Complete
---------------------
Congratulations. UnvDB has been successfully installed to:
/home/xiangxiangbing/udbkv/test/replication/data7001
It is a demo version now, free use for 90 days.
To run as official version,
Contact UniversalDB(Shenzhen) Technology Co.,Ltd.
And copy license file to the installed path.
If you want to register UnvDB as service, please run
sudo /home/tom/replication/data7001/service.sh
or start UnvDB without service, please run
/home/tom/replication/data7001/start.sh
```
启动数据库
安装完成后,可以使用start.sh启动数据库:
```
/home/tom/replication/data7001/start.sh
```
根据实际情况合理配置、修改udbkv.conf[针对非sentinel实例]或者sentinel.conf[sentinel实例]配置文件,目前的安装中已经修改常见配置:比如port、dir、pidfile、logfile、daemonize等。 针对不同的场景的不同配置,参考后面部分的sentinel模式和cluster模式。
安装完后之后会在安装路径下生成一个env.sh环境变量设置的脚本,如果使用udbkv-cli命令的之前执行下source env.sh。
停止数据库
使用stop.sh停止数据库:
```
/home/tom/replication/data7001/stop.sh
```
也可以手动用udbkv-cli命令停止数据库:
```
udbkv-cli -h $IP -p $UDBKVPORT shutdown
```
$IP和$UDBKVPORT分别为待关闭实例的IP和端口。
sentinel模式
简介
当不使用Udbkv集群时,Udbkv Sentinel为Udbkv提供高可用性。
Udbkv Sentinel还提供其他辅助任务,如监控、通知,并充当客户端的配置提供者。这是宏观层面的Sentinel能力的完整列表:
监控。Sentinel不断检查您的主实例和副本实例是否按预期工作。
通知。Sentinel可以通过API通知系统管理员或其他计算机程序,被监控的Udbkv实例之一出现了问题。
自动故障转移。如果主服务器未按预期工作,Sentinel可以启动故障转移过程,将一个副本提升为主节点,将其他副本重新配置为使用新的主节点,并通知使用Udbkv服务器的应用程序在连接时要使用的新地址。
配置提供。Sentinel充当客户端服务发现的权威来源:客户端连接到Sentinel,以请求负责给定服务的当前Udbkv主机的地址。如果发生故障转移,Sentinels将报告新地址。
Sentinel作为一个分布式系统
Udbkv Sentinel是一个分布式系统:
Sentinel本身被设计为在一个配置中运行,在该配置中有多个Sentinel进程一起协作。多个Sentinel流程协同工作的优势如下:
当多个哨兵一致认为给定的主机不再可用时,执行故障检测。这降低了误判的概率。
即使不是所有的Sentinel进程都在工作,Sentinel也会工作,使系统对故障具有健壮性。毕竟,拥有一个本身就是单点故障的故障转移系统并不有趣。
Sentinel、Udbkv实例(主和副本)以及连接到Sentinel和Udbkv的客户端的总和也是一个具有特定属性的更大的分布式系统。在本文档中,将从理解Sentinel的基本属性所需的基本信息开始,逐步介绍一些概念,然后介绍更复杂的信息(可选),以便理解Sentinel的确切工作原理。
Sentinel快速入门
Udbkv Sentinel包含在Udbkv。
运行Sentinel
如果您正在使用udbkv-sentinel可执行文件(或者如果您有一个与udbkv-server可执行文件同名的符号链接),您可以使用以下命令行运行Sentinel:
```
udbkv-sentinel /path/to/sentinel.conf
```
否则,您可以直接使用udbkv-server可执行文件,在Sentinel模式下启动它:
```
udbkv-server /path/to/sentinel.conf --sentinel
```
两种方式都一样。 但是运行Sentinel时必须使用配置文件,因为系统将使用该文件来保存当前状态,以便在重启时重新加载。如果没有给出配置文件或者配置文件路径不可写,Sentinel将拒绝启动。Sentinel默认运行监听TCP端口26789的连接,因此要让Sentinel工作,服务器的端口26789必须打开,以接收来自其他Sentinel实例的IP地址的连接。否则哨兵之间不能通信,不能就该做什么达成一致,因此故障转移将永远不会执行。
部署Sentinel之前需要了解的基本知识
对于健壮的部署,至少需要三个Sentinel实例。
这三个Sentinel实例应该放在被认为会以独立方式发生故障的计算机或虚拟机中。例如,不同的物理服务器或虚拟机在不同的可用性区域上运行。
Sentinel + Udbkv分布式系统不保证在故障期间保留已确认的写入,因为Udbkv使用异步复制。然而,有一些方法可以部署Sentinel,将丢失写入的窗口限制在特定时刻,同时也有其他不太安全的方法来部署它。
你需要客户端的Sentinel支持。流行的客户端库有Sentinel支持,但不是全部。
如果您不经常在开发环境中进行测试,那么就没有什么HA设置是安全的,如果能够在生产环境中运行,那就更好了。你可能有一个错误的配置,只有在太晚的时候才会变得明显(凌晨3点,你的主节点停止工作)。
配置Sentinel
Udbkv源代码发行版包含一个名为sentinel.conf的文件,这是一个自我记录的示例配置文件,您可以使用它来配置sentinel,但是一个典型的最小配置文件如下所示:
```
sentinel monitor mymaster 127.0.0.1 6789 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6790 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
```
您只需要指定要监视的主节点,给每个独立的主节点(可能有任意数量的副本)一个不同的名称。不需要指定副本,副本是自动发现的。Sentinel将使用有关副本的附加信息自动更新配置(以便在重启时保留这些信息)。在故障转移期间,每次副本升级为主节点时,以及每次发现新的Sentinel时,都会重写配置。上面的配置示例主要监视两组Udbkv实例,每组都由一个主实例和数量不确定的副本组成。一组实例称为mymaster,另一组称为resque。
sentinel monitor语句参数的含义如下:
```
sentinel monitor <master-name> <ip> <port> <quorum>
```
为了清楚起见,让我们逐行检查配置选项的含义:
第一行用于告诉Udbkv监视一个名为mymaster的主机,它的ip地址127.0.0.1和端口6789,quorum为2。一切都很明显,除了quorum的这个参数:
quorum是需要就主节点不可达的事实达成一致的哨兵的数量,以便真正将主节点标记为故障,并最终在可能的情况下启动故障转移过程。
然而,quorum仅用于检测故障。为了实际执行故障转移,一个哨兵需要被选举为故障转移的领导者,并被授权继续进行。这只有在大多数哨兵程序投票的情况下才会发生。 例如,如果您有5个Sentinel进程,并且给定主节点的quorum为值2,则情况如下:
如果两个哨兵同时认为主节点不可达,其中一个将尝试启动故障转移。
如果总共至少有三个Sentinels可到达,故障转移将被授权并实际开始。 实际上,这意味着在故障期间,如果大多数Sentinel进程无法通信,Sentinel永远不会启动故障转移(也就是说,少数分区中没有故障转移)。
其他哨兵选项
其他选项几乎都是以下形式:
```
sentinel <option_name> <master_name> <option_value>
```
并用于以下目的:
down-after-milliseconds是Sentinel开始认为某个实例已关闭时,该实例不可访问(不回复我们的PINGs或回复时出现错误)的时间(以毫秒为单位)。
parallel-syncs设置故障转移后可以重新配置为同时使用新主节点的副本节点的数量。该数字越小,完成故障切换过程所需的时间就越长,但是,如果副本节点被配置为服务旧数据,您可能不希望所有副本节点同时与主节点重新同步。虽然副本节点的复制过程大多是非阻塞的,但有时会停止从主节点加载批量数据。您可能希望通过将此选项的值设置为1来确保一次只有一个副本不可访问。 其他选项在本文档的其余部分进行了描述,并在Udbkv发行版附带的示例sentinel.conf文件中进行了记录。 可以在运行时修改配置参数:
使用SENTINEL SET修改特定于主节点的配置参数。
使用SENTINELCONFIG SET配置集修改全局配置参数。 更多信息参见运行时重新配置Sentinel部分。
哨兵部署示例
现在您已经了解了关于Sentinel的基本信息,您可能想知道应该将Sentinel进程放在哪里,需要多少个Sentinel进程等等。本节展示了几个示例部署。 我们使用文本图形以图形格式向您展示配置示例,这是不同符号的含义:
+--------------------+
| This is a computer |
| or VM that fails |
| independently. We |
| call it a "box" |
+--------------------+
我们在盒子里写下他们正在运行的内容:
+--------------------+
| Udbkv master M1 |
| Udbkv Sentinel S1 |
+--------------------+
不同的盒子用线连接起来,表示它们能够通信:
+-------------+ +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+ +-------------+
网络分区显示为使用斜线:
+-------------+ +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+ +-------------+
还要注意:
Masters被称为M1,M2,M3,…,Mn。
Replicas被称为R1,R2,R3,…,Rn (R代表replica)。
Sentinels被称为S1,S2,S3,…,Sn。
Clients有C1,C2,C3,…,Cn。
当一个实例因为Sentinel操作而改变角色时,我们把它放在方括号内,因此[M1]意味着一个实例因为Sentinel的干预而成为主实例。
请注意,我们永远不会展示仅使用两个sentinel的设置,因为sentinel总是需要与大多数人进行通信,以便启动故障转移。
示例1:三个盒子的基本设置
这是一个非常简单的设置,其优点是可以简单地调整以获得额外的安全性。它基于三个盒子,每个盒子运行Udbkv进程和Sentinel进程。
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
Configuration: quorum = 2
如果主节点M1失败,S2和S3将同意失败,并能够授权故障转移,使客户端能够继续运行。 在每个Sentinel设置中,由于Udbkv使用异步复制,因此总是存在丢失一些写入的风险,因为给定的确认写入可能无法到达提升为主节点的副本节点。但是,在上述设置中,由于客户端与旧的主服务器分开,因此风险更高,如下图所示:
+----+
| M1 |
| S1 | <- C1 (writes will be lost)
+----+
|
/
/
+------+ | +----+
| [M2] |----+----| R3 |
| S2 | | S3 |
+------+ +----+
在这种情况下,网络分区隔离了旧的主节点M1,因此副本节点R2被提升为主节点。然而,与旧主节点在同一分区的客户端,如C1,可以继续向旧主节点写入数据。这些数据将永远丢失,因为当分区修复时,主节点将被重新配置为新主节点的副本,从而丢弃其数据集。
使用以下Udbkv复制功能可以缓解此问题,该功能允许在主节点检测到它不再能够将其写入传输到指定数量的副本节点时停止接受写入。
```
min-replicas-to-write 1
min-replicas-max-lag 10
```
使用上面的配置(有关更多信息,请参见udbkv发行版中自己注释的udbkv.conf示例),当Udbkv实例作为主节点时,如果它不能写入至少一个副本,它将停止接受写入。由于复制是异步的,因此无法写入实际上意味着副本已断开连接,或者在超过指定的max-lag秒数后未向我们发送异步确认。使用这种配置,上述示例中的旧Udbkv主节点M1将在10秒后变得不可用。当分区修复后,Sentinel配置将收敛到新的配置,客户端C1将能够获取有效的配置,并继续使用新的主节点。然而,没有免费的午餐。通过这种改进,如果两个副本关闭,主节点将停止接受写入。这是一种权衡。
示例2:客户端盒中的Sentinel
有时我们只有两个Udbkv boxes可用,一个用于主节点,一个用于副本节点。在这种情况下,示例1中的配置是不可行的,因此我们可以采用下面的方法,在客户端所在的位置放置哨兵:
```
+----+ +----+
| M1 |-------+-----| R1 |
| | | | |
+----+ | +----+
|
+------------+------------+
| | |
| | |
+----+ +----+ +----+
| C1 | | C2 | | C3 |
| S1 | | S2 | | S3 |
+----+ +----+ +----+
Configuration: quorum = 2
```
在这个部署中,哨兵和客户端是一样的:如果大多数客户端都可以到达主节点,那就没问题。这里的C1、C2、C3都是普通客户端,这并不意味着C1标识一个与连接到Udbkv的客户端。它更像是一个应用服务器,或者类似的东西。
如果运行M1和S1的机器出现故障,故障转移将顺利进行,但是很容易看出不同的网络分区会导致不同的行为。例如,如果客户端和Udbkv服务器之间的网络断开,Sentinel将无法设置,因为Udbkv主节点和副本节点都不可用。请注意,如果C3与M1进行分区(使用上述网络几乎不可能,但更有可能使用不同的布局,或者由于软件层的故障),我们会遇到与示例1中描述的类似问题,不同之处在于,这里我们没有办法打破对称性,因为只有一个副本和主服务器,所以当主节点与其副本节点断开连接时,它不能停止接受查询,否则主节点在副本节点故障期间将永远不可用。
因此,这是一个有效的设置,但示例1中的设置具有一些优势,例如Udbkv的HA系统与Udbkv本身运行在相同的机器中,这可能更易于管理,并且能够限制少数分区中的主机可以接收写入的时间。
快速教程
在本文的下一节中,将逐步介绍关于Sentinel API、配置和语义的细节。然而,对于想尽快使用该系统的人来说,本节是一个教程,展示了如何配置3个Sentinel实例并与之交互。这里我们假设实例在端口5000、5001、5002上执行。我们还假设您在端口6789有一个正在运行的Udbkv主节点,在端口6790有一个副本节点。假设您在个人计算机上运行模拟,我们将在本教程中使用IPv4环回地址127.0.0.1。
主从复制中副节点的udbkv.conf配置文件应该如下所示:
```
port 6790
replicaof 127.0.0.1 6789
```
三个Sentinel配置文件应该如下所示:
port 5000
sentinel monitor mymaster 127.0.0.1 6789 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
其他两个配置文件完全相同,但使用5001和5002作为端口号。关于上述配置,有几点需要注意:
主节点集称为mymaster。它标识主节点及其副本。由于每个主节点集都有不同的名称,Sentinel可以同时监控不同的主节点和副本集。
quorum配置为2(sentinel monitor配置指令的最后一个参数)。
down-after-milliseconds值是5000毫秒,也就是5秒,所以一旦我们在这段时间内没有收到来自pings的任何回复,主机就会被检测为失败。
一旦你启动了三个哨兵,你会看到他们记录的一些信息,比如:
+monitor master mymaster 127.0.0.1 6789 quorum 2
这是一个Sentinel事件,如果您订阅了稍后在Pubsub消息一节中指定的事件名称,您可以通过Pub/Sub接收此类事件。Sentinel在故障检测和故障转移期间生成并记录不同的事件。
向哨兵询问主节点的情况
对于Sentinel,首先要做的最明显的事情是检查它所监视的主节点是否运行良好:
```
$ udbkv-cli -p 5000
127.0.0.1:5000> sentinel master mymaster
1)"name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6789"
7) "runid"
8) "953ae6a589449c13ddefaee3538d356d287f509b"
9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "735"
19) "last-ping-reply"
20) "735"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "126"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "532439"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "1"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"
```
如您所见,它打印了许多关于主节点的信息。有一些是我们特别感兴趣的:
num-other-sentinels是2,所以我们知道Sentinel已经为这个主节点检测到了另外两个sentinels。如果您检查日志,您将看到生成的+sentinel事件。
flags为master。如果主节点关闭,我们可以在这里看到s_down或o_down标志。
num-slaves被正确地设置为1,因此Sentinel还检测到有一个副本连接到我们的主节点。
为了更深入地了解这个实例,您可能希望尝试以下两个命令:
SENTINEL replicas mymaster
SENTINEL sentinels mymaster
第一个将提供关于连接到主节点的副本的类似信息,第二个提供关于其他Sentinels的信息。
获取当前主节点的地址
正如我们已经指出的,Sentinel还充当希望连接到一组主节点和副本节点的客户端的配置提供者。由于可能的故障转移或重新配置,客户端不知道谁是给定实例集的当前活动主节点,因此Sentinel导出一个API来询问这个问题:
```
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1)"127.0.0.1"
2) "6789"
```
测试故障转移
在这一点上,我们的哨兵部署准备接受测试。我们可以直接kill我们的主节点,然后检查配置是否改变。为此,我们可以这样做:
```
udbkv-cli -p 6789 DEBUG sleep 30
```
这个命令将使我们的主节点不再可达,睡眠30秒。基本上就是模拟一个主节点因为某种原因挂起。如果您检查Sentinel日志,您应该能够看到许多操作:
每个Sentinel通过+sdown事件检测到主节点停机。
这个事件后来升级为+odown,这意味着多个哨兵都同意主节点不可达的事实。
哨兵投票选出将开始第一次故障转移尝试的哨兵。
发生故障转移。
如果您再次询问mymaster的当前主地址是什么,这次我们最终会得到不同的回答:
```
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1)"127.0.0.1"
2) "6790"
```
此时,您可以直接创建您的Sentinel部署,或者阅读更多内容来理解所有Sentinel命令和内部机制。
哨兵API
Sentinel提供了一个API来检查其状态,检查受监控的主节点和副本节点的健康状况,订阅以接收特定的通知,并在运行时更改Sentinel的配置。 默认情况下,Sentinel使用TCP端口26789运行(注意,6789是普通的Udbkv端口)。Sentinel使用Udbkv协议接受命令,因此您可以使用udbkv-cli或任何其他未经修改的Udbkv客户端来与Sentinel通信。可以直接查询Sentinel,从它的角度检查被监控Udbkv实例的状态,查看它知道的其他Sentinel,等等。或者,使用Pub/Sub,可以在每次发生某个事件时从Sentinels接收推送式通知,比如故障转移或实例进入错误状态等等。
哨兵命令
SENTINEL命令是Sentinel的主要API。以下是它的子命令列表:
SENTINEL CONFIG GET
获取全局SENTINEL配置参数的当前值。指定的名称可以是通配符,类似于Udbkv CONFIG GET命令。 SENTINEL CONFIG SET
设置全局SENTINEL配置参数的值。 SENTINEL CKQUORUM
检查当前SENTINEL配置是否能够达到对主节点进行故障转移所需的法定数量,以及授权故障转移所需的多数数量。该命令应该在监控系统中使用,以检查Sentinel部署是否正常。 SENTINEL FLUSHCONFIG强制SENTINEL在磁盘上重写其配置,包括当前的Sentinel状态。通常,Sentinel会在每次状态发生变化时重写配置(在重启后保留在磁盘上的状态子集的上下文中)。但是,有时配置文件可能会因为操作错误、磁盘故障、软件包升级脚本或配置管理器而丢失。在这些情况下,强制Sentinel重写配置文件是很方便的。即使先前的配置文件完全丢失,该命令也能工作。
SENTINEL FAILOVER
强制进行故障转移,就像主节点不可达一样,并且不需要征求其他Sentinel的同意(但是将发布新版本的配置,以便其他Sentinel更新其配置)。 SENTINEL GET-MASTER-ADDR-BY-NAME
返回具有该名称的主节点的ip和端口。如果此主节点的故障转移正在进行或成功终止,它将返回提升的副本节点的ip和端口。 SENTINEL INFO-CACHE返回主节点和副本节点的缓存信息输出。
SENTINEL IS-MASTER-DOWN-BY-ADDR检查从当前Sentinel的角度看,ip:port指定的主节点是否关闭。该命令主要供内部使用。
SENTINEL MASTER
显示指定主节点的状态和信息。 SENTINEL MASTERS显示受监控的主节点及其状态的列表。
SENTINEL MONITOR 开始哨兵的监视。更多信息请参考运行时重新配置Sentinel部分。
SENTINEL MYID 返回哨兵实例的ID。
SENTINEL PENDING-SCRIPTS该命令返回关于挂起脚本的信息。
SENTINEL REMOVE停止哨兵的监视。更多信息请参考运行时重新配置Sentinel部分。
SENTINELREPLICAS
显示此主节点的副本列表及其状态。 SENTINEL SENTINELS
显示该主节点的Sentinel实例列表及其状态。 SENTINEL SET设置Sentinel的监控配置。更多信息请参考运行时重新配置Sentinel部分。
SENTINEL SIMULATE-FAILURE(crash-after-election|crash-after-promotion|help)该命令模拟不同的Sentinel崩溃场景。
SENTINEL RESET
该命令将重置所有具有匹配名称的主节点。模式参数是一个glob样式的模式。重置过程会清除主节点中任何以前的状态(包括正在进行的故障转移),并删除已经发现并与主节点关联的每个副本和sentinel。 出于连接管理的目的,Sentinel支持Udbkv命令的以下子集: ACL 该命令管理Sentinel访问控制列表。有关更多信息,请参考Sentinel访问控制列表认证。
AUTH对客户端连接进行身份验证。有关更多信息,请参考AUTH命令和配置带身份验证的Sentinel实例部分。
CLIENT该命令管理客户端连接。有关更多信息,请参考其子命令的页面。
COMMAND 该命令返回有关命令的信息。有关更多信息,请参考命令命令及其各种子命令。
HELLO切换连接的协议。有关更多信息,请参考HELLO命令。
INFO返回关于Sentinel服务器的信息和统计数据。有关更多信息,请参见INFO命令。
PING这个命令只是返回PONG。
ROLE该命令返回字符串“sentinel”和被监控主机的列表。有关更多信息,请参考角色命令。
SHUTDOWN关闭Sentinel实例。 最后,Sentinel还支持SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE和PUNSUBSCRIBE命令。有关更多详细信息,请参考发布/订阅消息部分。
运行时重新配置Sentinel
Sentinel提供了一个API来添加、删除或更改给定主节点的配置。请注意,如果您有多个Sentinel,您应该将所有更改应用到您的实例,以便Udbkv Sentinel正常工作。这意味着改变单个哨兵的配置不会自动将改变传播到网络中的其他哨兵。以下是用于更新Sentinel实例配置的SENTINEL子命令列表。
SENTINEL MONITOR
< ip > 该命令告诉Sentinel开始监控具有指定name、ip、port和quorum的新主节点。它与sentinel.conf配置文件中的sentinel monitor配置指令相同,不同之处在于您不能使用主机名作为ip,而是需要提供IPv4或IPv6地址。 SENTINEL REMOVE
用于删除指定的主节点:主节点将不再被监控,并将完全从Sentinel的内部状态中删除,因此它将不再被SENTINEL masters列出,依此类推。 SENTINEL SET
[
以下是SENTINEL SET命令的示例,该命令用于修改名为objects-cache的主节点的down-after-milliseconds:
```
SENTINEL SET objects-cache-master down-after-milliseconds 1000
```
如前所述,SENTINEL SET可用于设置所有可在启动配置文件中设置的配置参数。此外,还可以只更改quorum配置,而无需使用SENTINEL REMOVE和SENTINEL MONITOR删除并重新添加主节点,只需使用:
```
SENTINEL SET objects-cache-master quorum 5
```
请注意,没有等效的GET命令,因为SENTINEL MASTER以简单的解析格式(作为字段/值对数组)提供所有配置参数。Sentinel还允许获取和设置全局配置参数,这些参数仅在之前的配置文件中受支持。
SENTINEL CONFIG GET
获取全局SENTINEL配置参数的当前值。指定的名称可以是通配符,类似于Udbkv CONFIG GET命令。 SENTINEL CONFIG SET
设置全局SENTINEL配置参数的值。 可以操作的全局参数包括: resolve-hostnames, announce-hostnames.
announce-ip, announce-port.
sentinel-user, sentinel-pass.
添加或删除哨兵
向部署中添加新的Sentinel是一个简单的过程,因为Sentinel实现了自动发现机制。您需要做的就是启动新的Sentinel来监控当前活动的主节点。在10秒钟内,哨兵将获得其他哨兵的名单和附属于主节点的副本集。如果您需要一次添加多个哨兵,建议一个接一个地添加,等待所有其他哨兵都已经知道第一个后再添加下一个。这对于仍然保证多数只能在分区的一边实现是有用的,以防在添加新的哨兵的过程中发生故障。这可以通过以30秒的延迟添加每个新的Sentinel来容易地实现,并且在没有网络分区的情况下。在处理的最后,可以使用SENTINEL MASTER mastername命令来检查是否所有的Sentinel都同意监控主节点的Sentinel总数。
移除哨兵稍微复杂一点:哨兵永远不会忘记已经看到的哨兵,即使它们很长时间都无法到达,因为我们不想动态地改变授权故障转移和创建新配置号所需的多数。因此,为了删除Sentinel,应该在没有网络分区的情况下执行以下步骤:
停止要删除的哨兵的哨兵进程。
向所有其他哨兵实例发送SENTINEL RESET 命令(如果只想重置一个主节点,可以使用确切的主节点名称,而不是命令)。一个接一个,在实例之间等待至少30秒。
通过检查每个哨兵的SENTINEL MASTER mastername的输出,检查所有哨兵是否同意当前活动的哨兵数量。
删除旧的主节点或无法访问的副本
哨兵永远不会忘记某个主节点的副本,即使他们很长时间都不可达。这很有用,因为哨兵应该能够在网络分区或故障事件后正确地重新配置返回的副本。此外,在故障转移后,故障转移的主节点被虚拟地添加为新主节点的副本,这样,一旦新主节点再次可用,它将被重新配置为新主节点的副本。 然而,有时你想从哨兵监控的副本列表中永久删除一个副本(可能是旧主节点)。为此,您需要向所有哨兵发送SENTINEL RESET mastername命令:它们将在接下来的10秒内刷新副本列表,只添加那些从当前主信息输出中正确复制的副本。
发布订阅消息
客户端可以使用Sentinel作为Udbkv兼容的发布/订阅服务器(但不能使用PUBLISH),以便SUBSCRIBE or PSUBSCRIBE频道并获得特定事件的通知。频道名称与事件名称相同。例如,名为+sdown的通道将接收与进入SDOWN (SDOWN意味着从您正在查询的Sentinel的角度来看实例不再可达)条件的实例相关的所有通知。要获取所有消息,只需使用PSUBSCRIBE *进行订阅。
下面是使用这个API可以接收的通道和消息格式的列表。第一个字是通道/事件名称,其余的是数据的格式。注意:如果指定了instance details,则意味着提供了以下参数来标识目标实例:
```
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
```
这部分表示了主节点(从@参数到结尾)是可选的,只有当实例本身不是主节点才指定。
+reset-master
-重置主节点。 +slave
-检测到并连接了新的副本。 +failover-state-reconf-slaves
-故障转移状态更改为reconf-slaves状态。 +failover-detected
-检测到由另一个Sentinel或任何其他外部实体启动的故障转移(连接的副本变成了主节点)。 +slave-reconf-sent
leader sentinel向此实例发送REPLICAOF命令,以便为新副本重新配置它。 +slave-reconf-inprog
-正在重新配置的副本显示为新的主ip:port对的副本,但同步过程尚未完成。 +slave-reconf-done
-副本服务器现在与新的主服务器同步。 -dup-sentinel
-指定主节点的一个或多个sentinel因重复而被删除(例如,当Sentinel实例重新启动时会发生这种情况)。 +sentinel
-检测到并附加了该节点的新sentinel。 +sdown
-指定的实例现在主观上处于关闭状态。 -sdown
-指定的实例不再处于主观关闭状态。 +odown
-指定的实例现在客观上处于关闭状态。 -odown
-指定的实例不再处于客观关闭状态。 +new-epoch
-当前epoch已更新。 +try-failover
-新的故障转移正在进行中,等待多数选举。 +elected-leader
-赢得指定epoch的选举,可以进行故障转移。 +failover-state-select-slave
-新的故障转移状态为select-slave:我们正在尝试寻找合适的副本进行升级。 no-good-slave
-没有要提升的良好副本。目前,我们将在一段时间后尝试,但这可能会改变,在这种情况下,状态机将完全中止故障转移。 selected-slave
-我们找到了要升级的指定良好副本。 failover-state-send-slave of-noone
-我们正在尝试将提升的副本重新配置为主副本,等待它进行切换。 failover-end-for-timeout
-故障转移因超时而终止,副本最终将被配置为新的主节点的副本。 failover-end
-故障转移成功终止。所有副本似乎都被重新配置为用新的主节点进行复制。 switch-master
-主节点新IP和地址是配置更改后指定的IP和地址。这是大多数外部用户感兴趣的消息。 +tilt进入Tilt模式。
-tilt 退出Tilt模式。
处理-BUSY状态的
当Lua脚本运行的时间超过配置的Lua脚本时间限制时,Udbkv实例将返回-BUSY错误。当这种情况在触发故障转移之前发生时,Udbkv Sentinel将尝试发送SCRIPT KILL命令,仅当脚本为只读时才会成功。如果该实例在这次尝试后仍处于错误状态,它最终将被故障转移。
副本优先级
Udbkv实例有一个名为replica-priority的配置参数。Udbkv副本实例在其INFO输出中公开该信息,Sentinel使用该信息从可用于故障转移主节点的副本中挑选一个副本:
如果副本优先级设置为0,副本永远不会提升为主节点。
Sentinel更喜欢优先级较低的副本。
例如,如果在当前主节点的同一个数据中心有一个副本S1,在另一个数据中心有另一个副本S2,可以将S1的优先级设置为10,将S2的优先级设置为100,这样,如果主节点出现故障,而S1和S2都可用,S1将是首选。
Sentinel和Udbkv认证
作为一种安全措施,当主节点配置为要求客户端进行身份验证时,副本也需要知道凭据,以便与主节点进行身份验证,并创建用于异步复制协议的主节点-副本连接。
Udbkv访问控制列表验证
用户身份验证和权限通过访问控制列表(ACL)进行管理。 为了让Sentinel在配置了ACL的情况下连接到Udbkv服务器实例,Sentinel配置必须包括以下指令:
```
sentinel auth-user <master-name> <username>
sentinel auth-pass <master-name> <password>
```
其中
```
127.0.0.1:6789> ACL SETUSER sentinel-user ON >somepassword allchannels +multi +slaveof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill
```
Udbkv纯密码验证
在引入ACL之前,可以使用以下配置指令来实现身份验证:
requirepass在主节点中,为了设置身份验证密码,并确保实例不会处理未经身份验证的客户端的请求。
masterauth在副本中,为了副本向主节点进行身份验证,从而正确地从主节点复制数据。
使用Sentinel时,没有单个主节点,因为在故障转移后,副本可能会充当主节点的角色,旧的主节点可以重新配置,以便充当副本,所以您要做的是在所有实例(主节点和副本)中设置上述指令。 这通常也是一种合理的设置,因为您不想只保护主服务器中的数据,而在副本服务器中也可以访问相同的数据。但是,在不常见的情况下,您需要一个无需身份验证即可访问的副本,您仍然可以这样做:将副本优先级设置为零,以防止该副本被提升为主节点,并在该副本中仅配置masterauth指令,而不使用requirepass指令,以便未经身份验证的客户端可以读取数据。为了让sentinel在配置了requirepass时连接到Udbkv服务器实例,Sentinel配置必须包括sentinel auth-pass指令,格式如下:
```
sentinel auth-pass <master-name> <password>
```
使用身份验证配置Sentinel实例
Sentinel实例本身可以通过要求客户端通过AUTH命令进行身份验证来保护。请注意,Sentinel的身份验证配置应该应用于您的部署中的每个实例,并且所有实例应该使用相同的配置。此外,ACL和纯密码身份验证不应一起使用。
Sentinel访问控制列表认证
使用ACL保护Sentinel实例的第一步是防止任何未经授权的访问。要做到这一点,您需要禁用默认超级用户(或者至少用强密码设置它)并创建一个新的超级用户,并允许它访问发布/订阅通道:
```
127.0.0.1:5000> ACL SETUSER admin ON >admin-password allchannels +@all
OK
127.0.0.1:5000> ACL SETUSER default off
OK
```
Sentinel使用默认用户连接到其他实例。您可以使用以下配置指令提供另一个超级用户的凭据:
```
sentinel sentinel-user <username>
sentinel sentinel-pass <password>
```
其中
```
127.0.0.1:5000> ACL SETUSER sentinel-user ON >user-password -@all +auth +client|getname +client|id +client|setname +command +hello +ping +role +sentinel|get-master-addr-by-name +sentinel|master +sentinel|myid +sentinel|replicas +sentinel|sentinels
```
Sentinel纯密码身份验证
要将Sentinel与纯密码身份验证结合使用,请将requirepass配置指令添加到所有Sentinel实例中,如下所示:
```
requirepass "your_password_here"
```
当这样配置时,哨兵将做两件事:
客户端需要输入密码才能向哨兵发送命令。这是显而易见的,因为这就是这种配置指令在Udbkv中的一般工作方式。
此外,这个Sentinel实例将使用为访问本地Sentinel而配置的相同密码,以便对它所连接的所有其他Sentinel实例进行身份验证。
这意味着您必须在所有Sentinel实例中配置相同的requirepass密码。这样,每个哨兵都可以与其他哨兵通信,而不需要为每个哨兵配置访问所有其他哨兵的密码,这是非常不切实际的。 在使用此配置之前,请确保您的客户端库可以向Sentinel实例发送AUTH命令。
哨兵客户端实施
Sentinel需要显式的客户端支持,除非系统被配置为执行一个脚本,该脚本将所有请求透明地重定向到新的主节点(虚拟IP或其他类似系统)。
cluster模式
简介
Udbkv通过名为Udbkv Cluster的部署拓扑进行水平扩展。本主题将教您如何在生产环境中设置、测试和操作Udbkv集群。您将从最终用户的角度了解Udbkv Cluster的可用性和一致性特征。
Udbkv Cluster提供了一种运行Udbkv安装的方法,其中数据会自动在多个Udbkv节点上进行分片。Udbkv Cluster还在分区期间提供一定程度的可用性,实际上,就是在一些节点出现故障或无法通信时继续运行的能力。但是,如果发生更大的故障(例如,当大多数主节点不可用时),群集将变得不可用。
因此,借助Udbkv Cluster,您可以:
在多个节点之间自动分割数据集。
当一部分节点出现故障或无法与集群的其余部分通信时,继续操作。
Udbkv集群TCP端口
每个Udbkv集群节点都需要两个开放的TCP连接:用于服务客户端的Udbkv TCP端口,例如6789,以及称为集群总线端口的第二个端口。默认情况下,集群总线端口通过将10000加到数据端口(例如16789)来设置;但是,您可以在集群端口配置中覆盖它。
集群总线是一种使用二进制协议的节点到节点通信通道,由于带宽和处理时间都很少,因此更适合在节点之间交换信息。节点使用集群总线进行故障检测、配置更新、故障转移授权等。客户端不应该尝试与集群总线端口通信,而应该使用Udbkv命令端口。但是,请确保在防火墙中打开这两个端口,否则Udbkv集群节点将无法通信。
要使Udbkv集群正常工作,您需要为每个节点提供:
客户端通信端口(通常为6789)用于与客户端进行通信,并对需要访问群集的所有客户端以及使用客户端端口进行关键迁移的所有其他群集节点开放。
群集总线端口必须可以从所有其他群集节点到达。
如果不同时打开两个TCP端口,您的群集将无法正常工作。
Udbkv集群数据分片
Udbkv Cluster不使用一致性散列,而是一种不同形式的分片,其中每个键在概念上都是我们称之为散列槽的一部分。
Udbkv集群中有16384个哈希槽,为了计算给定密钥的哈希槽,我们只需取密钥的CRC16模16384。
Udbkv集群中的每个节点负责哈希槽的一个子集,例如,您可能有一个包含3个节点的集群:
节点A包含从0到5500的散列槽。
节点B包含从5501到11000的散列槽。
节点C包含从11001到16383的哈希槽。
这使得添加和删除集群节点变得很容易。例如,如果我想添加一个新的节点D,我需要将一些哈希槽从节点A、B、C移动到D。同样,如果我想从集群中删除节点A,我可以将由A提供的哈希槽移动到B和C。一旦节点A为空,我就可以将其从集群中完全删除。
将哈希槽从一个节点移动到另一个节点不需要停止任何操作;因此,添加和删除节点,或者更改节点持有的哈希槽的百分比,都不需要停机。
Udbkv集群支持多个键操作,只要单个命令执行(或整个事务,或Lua脚本执行)中涉及的所有键都属于同一个哈希槽。用户可以通过使用一个称为哈希标签的特性,强制多个键成为同一个哈希槽的一部分。
Udbkv Cluster规范中记录了散列标签,但是要点是,如果键中的{}括号之间有一个子串,那么只有串中的内容被散列。例如,键user:{123}:profile和user:{123}:account保证在同一个散列槽中,因为它们共享同一个散列标签。因此,您可以在同一个多键操作中对这两个键进行操作。
Udbkv集群主副本模型
为了在主节点的子集出现故障或无法与大多数节点通信时保持可用,Udbkv Cluster使用主副本模型,其中每个哈希槽都有1个(主节点本身)到N个副本(N-1个额外的副本节点)。
在具有节点A、B、C的示例集群中,如果节点B出现故障,集群将无法继续运行,因为我们无法再提供5501-11000范围内的哈希槽。 但是,在创建集群时(或稍后),我们会向每个主节点添加一个副本节点,因此最终的集群由作为主节点的A、B、C以及作为副本节点的A1、B1和C1组成。这样,如果节点B出现故障,系统可以继续运行。
节点B1复制B,而B出现故障,群集会将节点B1提升为新的主节点,并将继续正常运行。 但是,请注意,如果节点B和B1同时出现故障,Udbkv Cluster将无法继续运行。
Udbkv集群一致性保证
Udbkv集群不能保证强一致性。实际上,这意味着在某些情况下,Udbkv Cluster可能会丢失系统向客户端确认的写入。
Udbkv群集可能丢失写入的第一个原因是因为它使用异步复制。这意味着在写入期间会发生以下情况:
你的客户写信给主节点B。
主节点B回复你的客户端OK。
主节点B将写操作传播到其副本B1、B2和B3。
如您所见,B在回复客户端之前不会等待来自B1,B2和B3的确认,因为这对于Udbkv来说是一个令人难以承受的延迟惩罚,因此,如果您的客户端写入了某些内容,B会确认该写入,但在能够将该写入发送到其副本之前会崩溃,其中一个副本(未接收到该写入)会被提升为主副本,从而永远丢失该写入。
这与大多数配置为每秒将数据刷新到磁盘的数据库所发生的情况非常相似,所以这是一种您已经能够推理的场景,因为过去在不涉及分布式系统的传统数据库系统的经验。类似地,您可以通过强制数据库在回复客户端之前将数据刷新到磁盘来提高一致性,但是这通常会导致非常低的性能。这相当于Udbkv集群中的同步复制。
基本上,需要在性能和一致性之间进行权衡。
Udbkv Cluster在绝对需要时支持同步写入,通过WAIT命令实现。这使得丢失写入的可能性大大降低。但是,请注意,即使使用同步复制,Udbkv Cluster也不会实现强一致性:在更复杂的故障情况下,无法接收写入的副本总是有可能被选为主服务器。
另一个值得注意的场景是Udbkv Cluster将丢失写入,这发生在网络分区期间,其中客户端与少数实例(至少包括一个主节点)隔离。
以我们的6节点集群为例,它由A、B、C、A1、B1和C1组成,有3个主服务器和3个副本服务器。还有一个客户端,我们称之为Z1。
在分区发生之后,可能在分区的一侧我们有A、C、A1、B1、C1,而在另一侧我们有B和Z1。 Z1仍然能够写入B,B将接受它的写入。如果分区在很短的时间内修复,集群将正常继续。但是,如果分区持续足够长的时间,以便B1在分区的多数端提升为主节点,则Z1在此期间发送给B的写操作将会丢失。
注意:Z1能够向B发送的写入量有一个最大窗口:如果分区的多数端已经过了足够的时间来选举一个副本作为主节点,那么少数端的每个主节点都将停止接受写入。 这个时间量是Udbkv集群的一个非常重要的配置指令,称为节点超时。节点超时后,主节点被视为出现故障,可以由它的一个副本替换。类似地,在节点超时后,主节点无法检测到大多数其他主节点,它将进入错误状态并停止接受写入。
Udbkv集群配置参数
我们将创建一个示例集群部署。在继续之前,我们先介绍一下Udbkv Cluster在udbkv.conf文件中引入的配置参数。
cluster-enabled<yes/no>:如果是,则在特定Udbkv实例中启用Udbkv集群支持。否则,该实例照常作为独立实例启动。
cluster-config-file
:请注意,尽管该选项的名称如此,但它并不是用户可编辑的配置文件,而是Udbkv集群节点在每次发生更改时自动保存集群配置(基本上是状态)的文件,以便能够在启动时重新读取它。该文件列出了集群中的其他节点、它们的状态、持久变量等等。由于接收到一些消息,该文件通常会在磁盘上被重写和刷新。 cluster-node-timeout <毫秒>:Udbkv群集节点在不被视为失败的情况下不可用的最长时间。如果主节点在指定的时间内无法访问,它将被其副本故障转移。这个参数控制Udbkv集群中其他重要的东西。值得注意的是,在指定时间内无法到达大多数主节点的每个节点都将停止接受查询。
cluster-slave-validity-factor
:如果设置为零,副本将始终认为自己是有效的,因此将始终尝试对主服务器进行故障转移,而不管主服务器和副本服务器之间的链路保持断开的时间长短。如果该值为正值,则最长断开连接时间的计算方法是节点超时值乘以该选项提供的因子,如果该节点是副本节点,则当主链路断开连接的时间超过指定的时间量时,它不会尝试启动故障切换。例如,如果节点超时设置为5秒,有效性因子设置为10,则与主服务器断开连接超过50秒的副本服务器将不会尝试对其主服务器进行故障转移。请注意,任何非零值都可能导致Udbkv集群在主服务器出现故障后不可用,如果没有副本服务器能够对其进行故障转移的话。在这种情况下,只有当原始主节点重新加入集群时,集群才会恢复可用状态。 cluster-migration-barrier
:主服务器将保持连接的副本的最小数量,以便另一个副本迁移到不再被任何副本覆盖的主服务器。有关更多信息,请参见本教程中有关副本迁移的相应部分。 cluster-require-full-coverage <yes/no>:如果此选项设置为yes(默认情况下),则如果某个百分比的key空间未被任何节点覆盖,群集将停止接受写入。如果该选项设置为no,即使只能处理关于key子集的请求,群集仍将为查询提供服务。
cluster-allow-reads-when-down <yes/no>:如果默认设置为no,则当集群被标记为失败时,Udbkv集群中的节点将停止为所有流量提供服务,无论是在节点无法达到主节点的法定数量时,还是在未达到完全覆盖时。这可以防止从不知道群集中更改的节点读取潜在的不一致数据。此选项可以设置为yes,以允许在失败状态期间从节点读取,这对于希望优先考虑读取可用性但仍希望防止不一致写入的应用程序非常有用。它还可以用于只有一个或两个分片的Udbkv集群,因为它允许节点在主节点出现故障但无法自动故障转移时继续提供写入服务。
创建和使用Udbkv集群
要创建和使用Udbkv集群,请按照下列步骤操作:
创建Udbkv集群
与集群互动
对集群进行重分片
测试故障转移
手动故障转移
添加新节点
移除节点
副本迁移
升级Udbkv群集中的节点
迁移到Udbkv集群
但是,首先,要熟悉创建集群的要求。
创建Udbkv集群的要求
要创建一个集群,首先需要有几个空的Udbkv实例在集群模式下运行。 至少在udbkv.conf文件中设置以下指令:
```
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
```
要启用集群模式,请将cluster-enabled指令设置为yes。每个实例还包含存储该节点配置的文件路径,默认情况下是nodes.conf。它只是在启动时由Udbkv集群实例生成,并在每次需要时进行更新。
请注意,按预期工作的最小集群必须包含至少三个主节点。对于部署,我们强烈建议使用六节点集群,其中有三个主服务器和三个副本服务器。 您可以在本地测试这一点,方法是创建以下目录,这些目录以您将在任何给定目录中运行的实例的端口号命名。 例如:
```
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
```
在从7000到7005的每个目录中创建一个udbkv.conf文件。作为配置文件的模板,只需使用上面的小例子,但是要确保根据目录名用正确的端口号替换端口号7000。 您可以按如下方式启动每个实例,每个实例在单独的终端选项卡中运行:
```
cd 7000
udbkv-server ./udbkv.conf
```
您将从日志中看到,每个节点都为自己分配了一个新的ID:
```
[82462] 16 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
```
这个ID将被这个特定的实例永久使用,以便该实例在集群的上下文中具有唯一的名称。每个节点都记得使用这个id的所有其他节点,而不是通过IP或端口。IP地址和端口可能会改变,但唯一的节点标识符在节点的整个生命周期内永远不会改变。我们把这个标识符简称为节点ID。
创建Udbkv集群
既然我们已经运行了许多实例,那么您需要通过向节点写入一些有意义的配置来创建您的集群。 您可以手动配置和执行单个实例。让我们回顾一下如何手动操作。 要创建群集,请运行:
```
udbkv-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
```
这里使用的命令是create,因为我们想要创建一个新的集群。选项–cluster-replicas 1意味着我们希望为每个创建的主服务器创建一个副本。其他参数是我想用来创建新集群的实例的地址列表。udbkv-cli将建议一个配置。键入yes接受建议的配置。集群将被配置和加入,这意味着实例将被引导以相互通信。最后,如果一切顺利,您将看到如下消息:
```
[OK] All 16384 slots covered
```
这意味着至少有一个主实例服务于16384个可用插槽中的每一个。
与集群互动
要连接到Udbkv集群,您需要一个支持集群的Udbkv客户端。请参阅您选择的客户端的文档,以确定其群集支持。 您还可以使用udbkv-cli命令行实用程序来测试Udbkv集群:
```
$ udbkv-cli -c -p 7000
127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
127.0.0.1:7000> get foo
> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
127.0.0.1:7002> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
```
注意:如果您使用脚本创建了集群,您的节点可能会监听不同的端口,默认情况下从30001开始。
udbkv-cli集群支持非常基本,因此它总是利用udbkv群集节点能够将客户端重定向到正确节点的事实。严肃的客户端可以做得更好,缓存散列槽和节点地址之间的映射,直接使用正确的连接到正确的节点。只有当集群配置发生变化时,才会刷新映射,例如在故障切换之后,或者在系统管理员通过添加或删除节点来更改集群布局之后。
对集群进行重分片
现在,我们准备尝试集群重分片。重分片基本上意味着将哈希槽从一组节点移动到另一组节点。与集群创建一样,使用udbkv-cli实用程序完成的。
要开始重分片,只需键入:
```
udbkv-cli --cluster reshard 127.0.0.1:7000
```
您只需指定一个节点,udbkv-cli将自动查找其他节点。
目前,udbkv-cli只能在管理员的支持下进行重分片,您不能只是说将5%的插槽从这个节点移动到另一个节点(但这比较容易实现)。所以从问题开始。第一个问题是你想移动多少个哈希槽:
```
How many slots do you want to move (from 1 to 16384)?
```
我们可以尝试重新分片1000个散列槽。 那么udbkv-cli需要知道重分片的目标是什么,也就是将接收哈希槽的节点。我将使用第一个主节点,即127.0.0.1:7000,但是我需要指定实例的节点ID。udbkv-cli已经将它显示在一个列表中,但是如果需要,我总是可以使用以下命令找到节点的ID:
```
$ udbkv-cli -p 7000 cluster nodes | grep myself
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460
```
我的目标节点是97a3a64667477371c4479320d683e4c8db5858b1。 现在你会被询问你想从哪个节点获取这些keys。我将只输入all,以便从所有其他主节点获取一些哈希槽。 最终确认后,您将看到udbkv-cli将从一个节点移动到另一个节点的每个插槽的消息,并且将为从一端移动到另一端的每个实际键打印一个点。在重新分级结束时,您可以使用以下命令测试集群的运行状况:
```
udbkv-cli --cluster check 127.0.0.1:7000
```
所有的槽将照常被覆盖,但是这次127.0.0.1:7000的主节点将有更多的哈希槽,大约6461个。 可以自动地执行重分片,而不需要以交互方式手动输入参数。这可以通过使用如下命令行来实现:
```
udbkv-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
```
如果您可能经常重分片,这允许建立一些自动化,但是目前udbkv-cli没有办法自动重新平衡集群,检查集群节点之间的keys分布,并根据需要智能地移动哈希槽。将来会添加此功能。 –cluster-yes选项指示集群管理器对命令的提示自动回答“是”,允许它以非交互模式运行。请注意,也可以通过设置REDISCLI_CLUSTER_YES环境变量来激活此选项。
测试故障转移
为了触发故障转移,我们可以做的最简单的事情(这也是分布式系统中可能发生的语义上最简单的故障)是使单个进程崩溃,在我们的例子中是单个主进程。
我们可以识别一个主节点,并使用以下命令使其崩溃:
```
$ udbkv-cli -p 7000 cluster nodes | grep master
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
```
7000,7001,7002是主节点。让我们用DEBUG SEGFAULT命令使节点7002崩溃:
```
$ udbkv-cli -p 7002 debug segfault
Error: Server closed the connection
```
我们现在可以检查故障转移后的集群设置(注意,在此期间我重新启动了崩溃的实例,以便它作为副本重新加入集群):
```
$ udbkv-cli -p 7000 cluster nodes
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected
```
CLUSTER NODES命令的输出可能看起来比较多,但它实际上非常简单,由以下标记组成:
节点ID
ip:port
flags:master, replica, myself, fail, …
如果是副本服务器,则为主服务器的节点ID
仍在等待回复的最后一次未决PING的时间。
最后一次收到PONG的时间。
此节点的配置epoch。
到此节点的连接状态。
装载的哈希槽…
手动故障转移
有时,强制进行故障转移而不会在主服务器上造成任何问题是很有用的。例如,要升级其中一个主节点的Udbkv进程,最好对其进行故障转移,使其成为对可用性影响最小的副本。 Udbkv Cluster使用CLUSTER FAILOVER命令支持手动故障转移,该命令必须在要进行故障转移的主服务器的一个副本服务器上执行。手动故障转移比较特殊,与实际主节点故障导致的故障转移相比更安全。它们以一种避免过程中数据丢失的方式发生,即仅当系统确定新的主节点处理了来自旧主节点的所有复制流时,才将客户端从原始主节点切换到新主节点。
这是您执行手动故障切换时在复制副本日志中看到的内容:
```
# Manual failover user request accepted.
# Received replication offset for paused master manual failover: 347540
# All master replication stream processed, manual failover can start.
# Start of election delayed for 0 milliseconds (rank #0, offset 347540).
# Starting a failover election for epoch 7545.
# Failover election won: I'm the new master.
```
基本上,连接到我们正在进行故障切换的主节点的客户端会被停止。同时,主节点将其复制偏移量发送给副本节点,等待副本节点到达自己的偏移量。当达到复制偏移量时,故障转移开始,旧的主节点被告知配置切换。当客户端在旧主节点上解除阻塞时,它们将被重定向到新主节点。
注意:要将副本提升为主节点,它必须首先被群集中的大多数主节点视为副本节点。否则,它无法赢得故障转移选举。如果副本刚刚添加到群集(请参见添加新节点作为副本),您可能需要等待一段时间才能发送CLUSTER FAILOVER命令,以确保群集中的主节点知道新的副本。
添加新节点
添加新节点基本上是添加一个空节点,然后将一些数据移入其中的过程,要么它是新的主节点,要么它是副本,设置为已知节点的副本。 我们将展示两者,从添加一个新的主实例开始。 在这两种情况下,要执行的第一步是添加一个空节点。
这就像在端口7006(我们已经为现有的6个节点使用了从7000到7005的端口)中启动一个新节点一样简单,除了端口号之外,其他节点使用的配置都是相同的,因此为了符合我们为以前的节点使用的设置,您应该做的是:
在您的终端应用程序中创建新标签。
进入集群测试目录。
创建一个名为7006的目录。
在内部创建一个udbkv.conf文件,类似于用于其他节点的文件,但使用7006作为端口号。
最后,使用以下命令启动服务器../udbkv-server ./udbkv.conf
此时,服务器应该正在运行。现在,我们可以像往常一样使用udbkv-cli将节点添加到现有集群中。
```
udbkv-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
```
如您所见,我使用了add-node命令,将新节点的地址指定为第一个参数,将集群中一个随机存在的节点的地址指定为第二个参数。 实际上,udbkv-cli在这里帮不了我们什么,它只是向节点发送一个CLUSTER MEET消息,这也可以手动完成。然而,udbkv-cli在运行之前也会检查集群的状态,因此,即使您知道内部是如何工作的,也最好始终通过udbkv-cli执行集群操作。 现在,我们可以连接到新节点,查看它是否真的加入了集群:
```
udbkv 127.0.0.1:7006> cluster nodes
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected
f093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383
```
请注意,由于该节点已经连接到集群,它已经能够正确地重定向客户端查询,并且一般来说是集群的一部分。然而,与其他主节点相比,它有两个特点:
它不保存数据,因为它没有分配哈希槽。
因为它是没有分配哈希槽的主节点,所以当副本节点希望成为主节点时,它不参与选举过程。
现在,可以使用udbkv-cli的重分片特性为该节点分配哈希槽。正如我们在上一节中已经做的那样,展示这一点基本上是没有用的,没有区别,这只是一个以空节点为目标的重分片。
添加新节点作为副本
添加新副本有两种方式。一个明显的方法是再次使用udbkv-cli,但使用–cluster-slave选项,如下所示:
```
udbkv-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave
```
请注意,这里的命令行与我们用来添加新主节点的命令行完全相同,因此我们没有指定要将副本节点添加到哪个主节点。在这种情况下,udbkv-cli会将新节点作为随机主节点的副本添加到副本较少的主节点中。 但是,您可以使用以下命令行来指定您希望新副本以哪个主节点为目标:
```
udbkv-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
```
这样,我们将新的副本分配给特定的主节点。 将副本添加到特定主节点的一种更为手动的方法是将新节点添加为空主节点,然后使用CLUSTER REPLICATE命令将其转换为副本。如果该节点是作为副本添加的,但您希望将其作为不同主节点的副本移动,也可以这样做。 例如,要为节点127.0.0.1:7005添加一个副本,该节点当前正在为范围11423-16383内的哈希槽提供服务,其节点ID为3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,我需要做的就是连接新节点(已经作为空主节点添加)并发送命令:
```
udbkv 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
```
现在,我们有了这组哈希槽的新副本,集群中的所有其他节点都已经知道了(需要几秒钟来更新它们的配置)。我们可以使用以下命令进行验证:
```
$ udbkv-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
f093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connected
```
节点3c3a0c…现在有两个副本,分别运行在端口7002(现有端口)和7006(新端口)上。
移除节点
要删除复制副本节点,只需使用udbkv-cli的del-node命令:
udbkv-cli --cluster del-node 127.0.0.1:7000 `<node-id>`
第一个参数只是群集中的一个随机节点,第二个参数是要删除的节点的ID。 您也可以用同样的方式删除主节点,但是为了删除主节点,它必须是空的。如果主节点不为空,您需要将数据从它重新共享到所有其他主节点。 删除主节点的另一种方法是在它的一个副本上执行手动故障转移,并在它变成新主节点的副本后删除该节点。显然,当您想要减少集群中主节点的实际数量时,这没有帮助,在这种情况下,需要重分片。有一种特殊情况,您希望删除故障节点。您不应该使用del-node命令,因为它会尝试连接到所有节点,并且您会遇到“连接被拒绝”错误。相反,您可以使用call命令:
udbkv-cli --cluster call 127.0.0.1:7000 cluster forget `<node-id>`
该命令将在每个节点上执行CLUSTER FORGET命令。
副本迁移
在Udbkv集群中,只需使用以下命令,您就可以随时将副本节点重新配置为使用不同的主节点进行复制:
```
CLUSTER REPLICATE <master-node-id>
```
但是,有一种特殊的情况,您希望副本自动从一个主节点移动到另一个主节点,而不需要系统管理员的帮助。副本的自动重新配置称为副本迁移,能够提高Udbkv集群的可靠性。
在某些情况下,您可能希望让集群副本从一个主节点移动到另一个主节点,这是因为通常Udbkv集群的抗故障能力与连接到给定主节点的副本数量一样强。例如,如果主节点及其副本节点同时出现故障,每个主节点都有一个副本节点的集群将无法继续运行,原因很简单,因为没有其他实例拥有主服务器节点所服务的哈希槽的副本。然而,虽然网络分割可能同时隔离多个节点,但许多其他类型的故障,如单个节点本地的硬件或软件故障,是不太可能同时发生的一类非常值得注意的故障,因此,在每个主节点都有一个副本的集群中,副本可能在凌晨4点被终止,而主节点在凌晨6点被终止。这仍然会导致集群无法继续运行。
为了提高系统的可靠性,我们可以选择向每个主节点添加额外的副本,但这是昂贵的。副本迁移允许将更多副本添加到几个主节点上。因此,您有10个主节点,每个主节点有一个副本,总共有20个实例。但是,例如,您添加了3个实例作为某些主节点的副本,因此某些主节点将有多个副本。
使用副本迁移时,如果主节点没有副本,则具有多个副本的主节点中的副本将迁移到孤立的主节点。因此,在您的副本节点在凌晨4点关闭后,如我们上面的示例所示,另一个副本节点将取代它的位置,当主节点在凌晨5点也发生故障时,仍然可以选择一个副本节点,以便集群可以继续运行。简而言之,关于副本迁移,您应该知道些什么?
群集将尝试从在给定时刻拥有最多副本的主节点迁移副本。
要从副本迁移中获益,您只需在集群中的单个主节点上再添加几个副本即可,而与哪个主节点无关。
有一个控制副本迁移功能的配置参数,称为cluster-migration-barrier:您可以在Udbkv Cluster提供的示例udbkv.conf文件中了解更多信息。
升级Udbkv群集中的节点
升级副本节点很容易,因为您只需要停止节点,然后用Udbkv的更新版本重新启动它。如果存在使用副本节点扩展读取的客户端,当给定副本不可用时,它们应该能够重新连接到不同的副本。
升级主节点稍微复杂一点,建议的过程是:
使用CLUSTER FAILOVER来触发主节点到其副本节点之一的手动故障转移。(请参阅本主题中的“手动故障转移”。)
等待主节点变成副本节点。
最后,像升级复制副本一样升级节点。
如果希望主节点成为刚刚升级的节点,请触发新的手动故障转移,以便将升级后的节点恢复为主节点。 按照此过程,您应该依次升级一个节点,直到所有节点都升级完毕。
迁移到Udbkv集群
愿意迁移到Udbkv集群的用户可能只有一个主节点,或者可能已经使用了预先存在的分片设置,其中使用一些内部算法或由他们的客户端库或Udbkv代理实现的分片算法在N个节点之间分割keys。在这两种情况下,都可以轻松地迁移到Udbkv集群,但是最重要的细节是应用程序是否使用多键操作,以及如何使用。有三种不同的情况:
不使用多键操作,或事务,或涉及多个键的Lua脚本。键是独立访问的(即使是通过事务或Lua脚本将多个命令(关于同一个键)组合在一起访问)。
使用多键操作或事务,或涉及多个键的Lua脚本,但只与具有相同散列标签的键一起使用,这意味着一起使用的键都具有{…}恰好相同的子字符串。例如,下面的多键操作是在同一个散列标签的上下文中定义的:SUNION {user:1000}.foo {user:1000}.bar。
涉及多个键的多键操作、或事务、或Lua脚本与没有明确的或相同的散列标签的键名一起使用。
第三种情况不是由Udbkv集群处理的:应用程序需要被修改,以便不使用多键操作或者只在相同散列标签的上下文中使用它们。案例1和案例2都包括在内,所以我们将把重点放在这两个案例上,它们以相同的方式处理,所以在文档中不会进行区分。
假设您将预先存在的数据集分到N个主节点,如果您没有预先存在的分片的话N就为1,则需要执行以下步骤来将您的数据集迁移到Udbkv Cluster:
停止你的客户端。目前不可能自动实时迁移到Udbkv集群。您也许能够在您的应用程序/环境的上下文中编排一个实时迁移。
使用BGREWRITEAOF命令为所有N个主节点生成一个AOF文件,并等待AOF文件完全生成。
把你的AOF文件从aof-1保存到aof-N。此时,如果您愿意,可以停止旧的实例(这很有用,因为在非虚拟化部署中,您经常需要重用相同的计算机)。
创建一个由N个主节点和零个副本组成的Udbkv集群。后面您将添加副本。确保所有节点都使用AOF文件来实现持久性。
停止所有集群节点,用预先存在的AOF文件替换它们的AOF文件,第一个节点用aof-1,第二个节点用aof-2,直到aof-N。
用新的AOF文件重启你的Udbkv集群节点。他们会抱怨说,根据他们的配置,有些键不应该在那里。
使用udbkv-cli –cluster fix命令来修复群集,以便根据每个节点是否具有认证的哈希槽来迁移键。
在最后使用udbkv-cli –cluster check来确保您的集群正常。
重新启动修改后的客户端,以使用Udbkv集群感知客户端库。
还有一种将数据从外部实例导入Udbkv集群的替代方法,即使用udbkv-cli –cluster import命令。该命令将正在运行的实例的所有键(从源实例中删除键)移动到指定的预先存在的Udbkv集群中。
TLS
Udbkv支持SSL/TLS作为可选特性,需要在编译时启用。
生成证书
要使用TLS运行Udbkv测试套件,您需要对TCL的TLS支持(即CentOS上的tcl包)。
1.运行./ops/gen-test-certs.sh来生成根CA和服务器证书。
手动运行
要使用TLS模式手动运行Udbkv服务器(假设调用了gen-test-certs.sh,因此样本证书/密钥可用):
```
udbkv-server --tls-port 6789 --port 0 \
--tls-cert-file ./tests/tls/udbkv.crt \
--tls-key-file ./tests/tls/udbkv.key \
--tls-ca-cert-file ./tests/tls/ca.crt
```
要使用udbkv-cli连接到此Udbkv服务器:
```
udbkv-cli --tls \
--cert ./tests/tls/udbkv.crt \
--key ./tests/tls/udbkv.key \
--cacert ./tests/tls/ca.crt
```
证书配置
为了支持TLS,Udbkv必须配置一个X.509证书和一个私钥。此外,在验证证书时,有必要指定一个CA证书捆绑文件或路径作为受信任的根。为了支持基于DH的密码,还可以配置DH params文件。例如:
```
tls-cert-file /path/to/udbkv.crt
tls-key-file /path/to/udbkv.key
tls-ca-cert-file /path/to/ca.crt
tls-dh-params-file /path/to/udbkv.dh
```
TLS监听端口
tls-port配置指令允许在指定端口上接受SSL/TLS连接。这是对侦听TCP连接端口的补充,因此可以同时使用TLS和非TLS连接访问不同端口上的Udbkv。 您可以指定端口0来完全禁用非TLS端口。要仅在默认Udbkv端口上启用TLS,请使用:
```
port 0
tls-port 6789
```
客户端证书身份验证
默认情况下,Udbkv使用相互TLS,并要求客户端使用有效证书进行身份验证(根据ca-cert-file或ca-cert-dir指定的可信根ca进行身份验证)。 您可以使用tls-auth-clients no来禁用客户端身份验证。
Replication
Udbkv主服务器以相同的方式处理客户端和副本服务器的连接,因此上述tls-port和tls-auth-clients指令也适用于复制链路。在副本服务器端,必须指定tls-replication yes,以便将tls用于到主服务器的传出连接。
Cluster
使用Udbkv集群时,使用tls-cluster yes为集群总线和跨节点连接启用tls。
Sentinel
Sentinel从通用Udbkv配置继承了其网络配置,因此上述所有内容也适用于Sentinel。当连接到主服务器时,Sentinel将使用tls-replication指令来确定是否需要tls或非TLS连接。 此外,完全相同的tls-replication指令将确定Sentinel的端口(接受来自其他Sentinel的连接)是否也支持tls。也就是说,当且仅当启用了tls复制时,Sentinel才会配置tls端口。
附加配置
附加的TLS配置可用于控制TLS协议版本、密码和密码套件等的选择。更多信息请参考udbkv.conf。
性能考虑因素
TLS向通信堆栈添加了一层,由于向/从SSL连接写入/读取、加密/解密和完整性检查而产生了开销。因此,使用TLS会导致每个Udbkv实例可实现的吞吐量降低。
限制
TLS目前不支持I/O线程。
客户端命令使用说明
主要介绍常见的客户端命令列表以及操作。
用法
udbkv-cli[OPTIONS] [cmd [arg…]]
描述
Udbkv命令行界面用于管理、故障排除和Udbkv试验。在交互模式下,udbkv-cli具有基本的行编辑功能,可以提供熟悉的输入体验。
要在特殊模式下启动程序,您可以使用几个选项,包括:
模拟副本并打印它从主节点接收的复制流。
检查Udbkv服务的延迟并显示统计数据。
请求延迟样本和频率的文本图形。
本主题涵盖udbkv-cli的不同方面,从最简单的开始,到更高级的功能结束。
选项
-h hostname:服务器主机名(默认值:127.0.0.1)。
-p port:服务器端口(默认值:6789)。
-t timeout:以秒为单位的服务器连接超时(允许小数)。默认超时为0,表示没有限制,具体取决于操作系统。
-s socket:服务器套接字(覆盖主机名和端口)。
-a password:连接到服务器时使用的密码。您还可以使用REDISCLI_AUTH环境变量来更安全地传递该密码。(如果两者都使用,则该参数优先。)
--user username:用于发送ACL样式“AUTH username pass”。需要-a。
--pass password:与new --user选项一致的-a的别名。
--askpass:强制用户从STDIN输入带有掩码的密码。如果使用此参数,将忽略-a和REDISCLI_AUTH环境变量。
-u uri:格式为redis://user:password @ host:port/dbnum的服务器uri。用户、密码和数据库号是可选的。对于没有用户名的身份验证,请使用用户名“default”。对于TLS,使用schema “udbkvs”。
-r repeat:执行指定的命令N次。
-i interval:当使用-r时,每个命令等待的间隔秒数。可以指定亚秒时间,如-i 0.1。此间隔也用于每个周期的扫描和统计。以及每100个周期的--bigkeys、--memkeys和--hotkeys。
-n db:数据库号。
-2:以RESP2协议模式启动会话。
-3:在RESP3协议模式下启动会话。
-x:从STDIN中读取最后一个参数(参见下面的示例)。
-X:从STDIN中读取参数(参见下面的示例)。
-d delimiter:原始格式的响应批量之间的分隔符(默认:\n)。
-D delimiter:原始格式的响应之间的分隔符(默认:\n)。
-c:启用群集模式(跟随-ASK和-MOVED重定向)。
-e:命令执行失败时返回退出错误代码。
-4:在DNS查找中首选IPv4而不是IPv6。
-6:在DNS查找中首选IPv6而不是IPv4。
--tls:建立安全的tls连接。
--sni host:TLS的服务器名称指示。
--cacert file:要验证的CA证书文件。
--cacertdir dir:存储可信CA证书的目录。如果未指定cacert或cacertdir,将应用默认的系统级受信任根证书配置。
--insecure:通过跳过证书验证来允许不安全的TLS连接。
--cert file:用来进行身份验证的客户端证书。
--key file:用于身份验证的私钥文件。
--tls-ciphers list:按照优先级从高到低的顺序设置首选密码列表(TLSv1.2及更低版本),用冒号(":")分隔。有关此字符串语法的更多信息,请参见ciphers(1ssl)联机帮助页。
--tls-ciphersuites list:按照优先级从高到低的顺序设置首选密码套件(TLSv1.3)的列表,用冒号(":")分隔。有关此字符串语法的更多信息,特别是TLSv1.3 ciphersuites的信息,请参见ciphers(1ssl)联机帮助页。
--raw:对回复使用raw格式(当STDOUT不是tty时为默认)。
--no-raw:强制格式化输出,即使STDOUT不是tty。
--quoted-input:强制输入作为带引号的字符串处理。
--csv:以csv格式输出。
--json:以json格式输出(默认为RESP3,如果要与RESP2一起使用,请使用-2)。
--quoted-json:与--json相同,但是产生ASCII安全的带引号的字符串,而不是Unicode。 --show-pushes yes|no:是否打印RESP3推送消息。当STDOUT是tty时默认启用,但可以用--show-pushes no覆盖。
--stat:打印关于服务器:内存,客户端,...
--latency:进入特殊模式,连续采样延迟。如果您在交互式会话中使用此模式,它会一直运行,显示实时统计数据。否则,如果指定了--raw或--csv,或者如果您将输出重定向到非TTY,它将对1秒的延迟进行采样(您可以使用-i来更改间隔),然后生成一个输出并退出。
--latency-history:类似于--latency,但跟踪延迟随时间的变化。默认时间间隔是15秒。用-i改变它。
--latency-dist:将延迟显示为光谱图,需要xterm 256色。默认时间间隔是1秒。用-i改变它。 --lru-test keys:模拟一个80-20分布的缓存工作负载。
--replica:模拟显示从主节点接收的命令的副本。
--rdb filename:将rdb转储从远程服务器传输到本地文件。使用文件名“-”写入标准输出。
--functions-rdb filename:类似于--rdb,但是在获取rdb转储文件时只获取函数(而不是键)。
--pipe:将原始RESP协议从stdin传输到服务。
--pipe-timeout n:在--pipe模式下,如果在发送完所有数据后出现错误,则中止。在n秒内没有收到回复。默认timeout:30。使用0永远等待。
--bigkeys:寻找具有许多元素(复杂性)的键的示例键。
--memkeys:寻找消耗大量内存的键的示例键。
--memkeys-samples n:寻找消耗大量内存的键的示例键。并定义要取样的关键元素的数量
--hotkeys:寻找热键的示例键。仅在maxmemory-policy为*lfu时有效。
--scan:使用scan命令列出所有键。
--pattern pat:使用--scan、--bigkeys或--hotkeys选项时的按键模式(默认:*)。
--count count:使用--scan、--bigkeys或--hotkeys时的计数选项(默认值:10)。
--quoted-pattern pat:与--pattern相同,但是指定的字符串可以加引号,以便传递一个非二进制安全的字符串。
--intrinsic-latency sec:运行测试来测量固有的系统延迟。测试将运行指定的秒数。
--eval file:在file中使用Lua脚本发送一个eval命令。
--ldb:与--eval一起使用,启用服务器Lua调试器。
--ldb-sync-mode:类似于--ldb,但使用同步Lua调试器,在这种模式下,服务被阻塞,脚本更改不会从服务内存回滚。
--cluster command[args...][opts...]:集群管理器命令和参数(见下文)。
--verbose:详细模式。
--no-auth-warning:在命令行界面上使用密码时不显示警告消息。
--help:输出帮助并退出。
--version:输出版本并退出。
集群管理器命令
对于Udbkv集群的管理,使用以下语法:
```
udbkv-cli --cluster command[args...][opts...]
Command Args --------------------------------------------------------------------------------
create host1:port1 ... hostN:portN
--cluster-replicas <arg>
check <host:port> or <host> <port> - separated by either colon or space
--cluster-search-multiple-owners
info <host:port> or <host> <port> - separated by either colon or space
fix <host:port> or <host> <port> - separated by either colon or space
--cluster-search-multiple-owners
--cluster-fix-with-unreachable-masters
reshard <host:port> or <host> <port> - separated by either colon or space
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
rebalance <host:port> or <host> <port> - separated by either colon or space
--cluster-weight <node1=w1...nodeN=wN>
--cluster-use-empty-masters
--cluster-timeout <arg>
--cluster-simulate
--cluster-pipeline <arg>
--cluster-threshold <arg>
--cluster-replace
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
del-node host:port node_id
call host:port command arg arg .. arg
--cluster-only-masters
--cluster-only-replicas
set-timeout host:port milliseconds
import host:port
--cluster-from <arg>
--cluster-from-user <arg>
--cluster-from-pass <arg>
--cluster-from-askpass
--cluster-copy
--cluster-replace
backup host:port backup_directory
help
```
连接
第一步是连接Udbkv。大多数编程语言都有客户端连接器。您也可以使用udbkv-cli命令行界面进行连接。以下示例显示了如何连接到在localhost (-h 127.0.0.1)上运行并监听默认端口(-p 6789)的Udbkv服务:
```
$ udbkv-cli -h 127.0.0.1 -p 6789
```
存储和检索数据
您可以在本地编程环境中使用相同的数据类型,但在Udbkv中是在服务器端。与字节数组类似,字符串存储字节序列,包括文本、序列化对象、计数器值和二进制数组。下面的示例显示了如何设置和获取字符串值:
```
127.0.0.1:6789> SET bike:1 "test"
OK
127.0.0.1:6789> GET bike:1
"test"
```
哈希表相当于字典(字典或哈希表)。除此之外,您还可以使用哈希来表示普通对象和存储计数器分组。以下示例说明了如何设置和访问对象的字段值:
```
127.0.0.1:6789> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4800
(integer) 4
127.0.0.1:6789> HGET bike:1 model
"Deimos"
127.0.0.1:6789> HGET bike:1 price
"4800"
127.0.0.1:6789> HGETALL bike:1
1)"model"
2)"Deimos"
3)"brand"
4)"Ergonom"
5)"type"
6)"Enduro bikes"
7)"price"
8) "4800"
```
扫描键空间
Udbkv中的每个项目都有一个唯一的键。所有项目都存在于Udbkv键空间中。您可以通过scan命令扫描Udbkv键空间。下面是一个扫描前100个前缀为bike的键的示例:
```
127.0.0.1:6789> SCAN 0 MATCH "bike:*" COUNT 100
1)"0"
2) 1) "bike:4"
2)"bike:3"
3)"bike:1"
4)"bike:2"
```
SCAN返回游标位置,允许您迭代地扫描下一批键,直到到达游标值0。
批量加载
批量加载是用大量预先存在的数据加载Udbkv的过程。理想情况下,您希望快速高效地执行此操作。本文档描述了在Udbkv中批量加载数据的一些策略。
使用RESP协议进行批量装载
使用常规的Udbkv客户端来执行批量加载并不是一个好主意,原因有几个:一个接一个地发送命令的方法很慢,因为每个命令都需要往返时间。可以使用流水线操作,但是对于大量记录的加载,您需要在读取响应的同时写入新的命令,以确保尽可能快地插入。只有一小部分客户端支持非阻塞I/O,并且不是所有的客户端都能够以有效的方式解析回应以最大化吞吐量。出于所有这些原因,将数据大量导入Udbkv的首选方法是以原始格式生成包含Udbkv协议的文本文件,以便调用插入所需数据所需的命令。
例如,如果我需要生成一个大型数据集,其中有数十亿个键,格式为:`keyN -> ValueN’,我将创建一个包含Udbkv协议格式的以下命令的文件:
```
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
```
udbkv-cli实用程序支持旨在执行批量加载的管道模式。 使用管道模式运行的命令如下所示:
```
cat data.txt | udbkv-cli --pipe
```
这将产生类似如下的输出:
```
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000000
```
udbkv-cli实用程序还将确保只将从udbkv实例收到的错误重定向到标准输出。
生成RESP协议
RESP协议的生成和解析极其简单。然而,为了生成用于批量加载的协议,您不需要理解协议的每个细节,而只是每个命令都以下列方式表示:
```
*<args><cr><lf>
$<len><cr><lf>
<arg0><cr><lf>
<arg1><cr><lf>
...
<argN><cr><lf>
```
其中
```
*3<cr><lf>
$3<cr><lf>
SET<cr><lf>
$3<cr><lf>
key<cr><lf>
$5<cr><lf>
value<cr><lf>
```
或者表示为带引号的字符串:
```
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"
```
您需要为批量加载生成的文件只是由以上述方式表示的命令一个接一个地组成。
以下Ruby函数生成有效的协议:
```
def gen_redis_proto(*cmd)
proto = ""
proto << "*"+cmd.length.to_s+"\r\n"
cmd.each{|arg|
proto << "$"+arg.to_s.bytesize.to_s+"\r\n"
proto << arg.to_s+"\r\n"
}
proto
end
puts gen_redis_proto("SET","mykey","Hello World!").inspect
```
使用上面的函数,可以很容易地生成上面例子中的键值对,程序如下:
```
(0...1000).each{|n|
STDOUT.write(gen_redis_proto("SET","Key#{n}","Value#{n}"))
}
```
我们可以在udbkv-cli管道中直接运行该程序,以便执行我们的第一个批量导入会话。
```
$ ruby proto.rb | udbkv-cli --pipe
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000
```
客户端库
不同的语言有不同的客户端库来访问Udbkv。Udbkv兼容redis7.0以及以上版本,因此可以使用Redis客户端来进行访问Udbkv。Redis目前正在与几个主流语言客户端库(jedis (Java)、node-redis (NodeJS)、redis-py (Python)和 go-redis (Go))的社区维护人员直接合作,建立文档、用户界面、治理和安全性等方面的一致性。
我们推荐如下的各种语言的客户端以及版本,用户也可以自己使用其它支持Redis的第三方客户端库。
Java
客户端库jedis,版本5.1.3,支持redis6.0以上版本,支持java8/11/17,对集群和非集群有不同的接口。
Python
客户端库redis-py,版本5.0.7,支持redis5.0以上版本,支持python3.7,对集群和非集群有不同的接口。
Go
客户端库go-redis,版本9.6.1,对集群和非集群有不同的接口。
NodeJS
客户端库node-redis,版本5.0.7,支持redis5.0以上版本,对集群和非集群有不同的接口。
C/C++
客户端库hiredis,版本1.2.0,用于非集群的接口,udbkv服务器端已经内置该库。
客户端库hiredis-cluster,版本0.13.0,用于集群的接口,该库依赖hiredis库。
数据迁移工具ud_migrate
简介
redis-shake是阿里云Redis团队开源的用于Redis数据迁移和数据过滤的工具。ud_migrate在redis-shake开源软件的基础上进行了适配和增强,为udbkv定制。ud_migrate用于将redis(或者兼容redis)中的数据迁移到udbkv,也可以用于udbkv不同系统之间数据的迁移。
ud_migrate提供以下特性:
Redis兼容性:兼容从 2.8 到 7.2的 Redis 版本,并支持各种部署方式,包括单机,主从,哨兵和集群。
多种导出模式:支持 PSync,RDB 和 Scan导出模式。
数据处理:通过自定义脚本实现数据过滤和转换。
前提要求
已经安装udbkv24.1.3以及以后的版本。
安装如下golang依赖: yum install go
在安装生成的环境变量设置文件env.sh中增加export PATH=$PATH:/usr/bin,其中/usr/bin是golang安装后的执行程序路径。
使用
迁移模式介绍
目前ud_migrate有三种迁移模式:PSynC、RDB和SCAN,分别对应sync_reader、rdb_reader和scan_reader。
对于从备份中恢复数据的场景,可以使用rdb_reader。
对于数据迁移场景,优先选择sync_reader。一些云厂商没有提供PSync协议支持,可以选择scan_reader。
对于长期的数据同步场景,ud_migrate目前没有能力承接,因为PSync协议并不可靠,当复制连接断开时,ud_migrate将无法重新连接至源端数据库。如果对于可用性要求不高,可以使用scan_reader。如果写入量不大,且不存在大key,也可以考虑scan_reader。
不同模式各有优缺点,需要查看各Reader章节了解更多信息。
下面以源redis迁移到目标udbkv中为例,介绍三种迁移模式的用法。
假设源数据库为 Redis实例,单机:
源redis:192.168.4.46:6379
目标数据库为 udbkv实例,集群,其中一个节点:
目标udbkv:192.168.4.147:6879
sync方式
从安装包的share目录下,拷贝文件migrate.toml.sample,更名为migrate_sync.toml并修改配置:
```
[sync_reader]
address = "192.168.4.46:6379"
[redis_writer]
cluster = true
address = "192.168.4.147:6879"
```
要启动ud_migrate,运行以下命令:
```
ud_migrate /yourpath/migrate_sync.toml
```
rdb方式
从安装包的share目录下,拷贝文件migrate.toml.sample,更名为migrate_rdb.toml并修改配置:
```
[rdb_reader]
filepath = "/tmp/dump.rdb" [这个为rdb文件的,是从源redis拷贝过来的]
[redis_writer]
cluster = true
address = "192.168.4.147:6879"
```
要启动ud_migrate,运行以下命令:
```
ud_migrate /yourpath/migrate_rdb.toml
```
scan方式
从安装包的share目录下,拷贝文件migrate.toml.sample,更名为migrate_scan.toml并修改配置:
```
[scan_reader]
address = "192.168.4.46:6379"
count = 100 [用户根据实际情况修改]
[redis_writer]
cluster = true
address = "192.168.4.147:6879"
```
要启动ud_migrate,运行以下命令:
```
ud_migrate /yourpath/migrate_scan.toml
```
相关参考
ud_migrate的详细说明和使用,可以参考redis-shake官方文档。
UDBKV 快速入门
先期准备
1. unvdb 用户需要 sudo 权限。
// 创建 unvdb 用户
[root@localhost /]# useradd unvdb
[root@localhost /]# passwd unvdb
// 给 unvdb 用户配置sudo权限
[root@localhost /]# visudo
// 在 ## Allow root to run any commands anywhere 行下添加如下内容
---
unvdb ALL=(ALL) ALL
---
// 验证 sodu 权限
[root@localhost /]# su unvdb
[unvdb@localhost /]$ sudo id
---
uid=0(root) gid=0(root) 组=0(root)
---
2. 配置用户limit参数
[root@localhost /]# vi /etc/security/limits.conf
// 在文件末尾添加如下内容
---
unvdb soft nproc 65536
unvdb hard nproc 65536
unvdb soft nofile 278528
unvdb hard nofile 278528
unvdb soft stack unlimited
unvdb soft core unlimited
unvdb hard core unlimited
unvdb soft memlock 250000000
unvdb hard memlock 250000000
---
// 切换到 unvdb 用户,验证配置
[unvdb@localhost /]$ ulimit -a
3. 内核参数调整
[root@localhost /]# vi /etc/sysctl.conf
// 在文件末尾添加如下内容
---
net.core.somaxconn = 1024
vm.overcommit_memory = 1
---
// 执行如下命令,使配置生效
[root@localhost /]# sysctl -p
安装
所有命令均在 unvdb 用户下执行
确保已经安装了 unzip 命令: yum -y install unzip
如果使用到TLS就需要: yum -y install openssl openssl-devel
联系技术支持获取 udbkv 安装包
./setup udbkv [针对非sentinel实例]
./setup sentinel [针对sentinel实例]
解压安装包
$ unzip udbkv-24.1.2-linux-x86_64.zip
$ cd udbkv-24.1.2-linux-x86_64
安装 udbkv
// 在主节点和从节点上安装 udbkv
// 我们在同一机器上部署管理节点和数据节点
// 192.168.4.147是安装机器的IP地址
[unvdb@localhost udbkv-24.1.1-linux-x86_64]$ ./setup.sh udbkv
---
根据需要设置,也可选择默认值.
例如:
/data/udbkv-data-mgr 6789
/data/udbkv-data-cn1 7789
---
修改配置文件
$ vi udbkv-data-mgr/udbkv.conf
---
bind 192.168.4.147
---
$ vi udbkv-data-cn1/udbkv.conf
---
bind 192.168.4.147
replicaof 192.168.4.147 6789
---
启动 udbkv 数据库
$ /data/udbkv-data-mgr/start.sh
$ /data/udbkv-data-cn1/start.sh
验证 udbkv 数据库启动
$ source udbkv-data-mgr/env.sh
$ udbkv-cli -h 192.168.4.147 -p 6789
192.168.4.147:6789>
退出 udbkv 数据库
192.168.4.147:6789> exit
关闭 udbkv 数据库
$ /data/udbkv-data-mgr/stop.sh
重启 udbkv 数据库
$ /data/udbkv-data-mgr/restart.sh
安装 sentinel
[unvdb@localhost udbkv-24.1.1-linux-x86_64]$ ./setup.sh sentinel
---
// 根据需要设置,也可选择默认值.
// 至少需要三个Sentinel实例, 例如:
/data/udbkv-data-s5001 192.168.4.147:5001
/data/udbkv-data-s5002 192.168.4.147:5002
/data/udbkv-data-s5003 192.168.4.147:5003
---
配置 sentinel 节点
// 检查sentinel实例配置文件:
// 例如:/data/udbkv-data-s5001/sentinel.conf
---
port 5001
sentinel monitor mymaster 192.168.4.147 6789 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 600
---
启动 sentinel
$ source /data/udbkv-data-s5001/env.sh
$ /data/udbkv-data-s5001/start.sh
...
验证 sentinel
$ udbkv-cli -h 192.168.4.147 -p 5001
-- 向哨兵询问主节点的情况
192.168.4.147:5001> sentinel master mymaster
...
-- 关于连接到主节点的副本的类似信息
192.168.4.147:5001> SENTINEL replicas mymaster
...
-- 关于其他Sentinels的信息
192.168.4.147:5001> SENTINEL sentinels mymaster
...
-- 获取当前主节点的地址
192.168.4.147:5001> SENTINEL get-master-addr-by-name mymaster
...
测试故障转移
如果使用 DEBUG sleep需要 udbkv-data-s5001/udbkv.conf中的 enable-debug-command 值不为 no
在这一点上,我们的哨兵部署准备接受测试。我们可以直接kill我们的主节点,然后检查配置是否改变。
// 测试前
192.168.4.147:5001> SENTINEL get-master-addr-by-name mymaster
---
1) "192.168.4.147"
2) "6789"
---
$ udbkv-cli -h 192.168.4.147 -p 6789 DEBUG sleep 30
// 或者 kill 192.168.4.147:6789 进程后重启
// $ udbkv-data-mgr/restart.sh
// 测试后
192.168.4.147:5001> SENTINEL get-master-addr-by-name mymaster
---
1) "192.168.4.147"
2) "7789"
---
刷新/重置副本列表
// 将在接下来的10秒内刷新副本列表,只添加那些从当前主信息输出中正确复制的副本
192.168.4.147:5001> SENTINEL RESET mymaster
---
(integer) 1
---
运行时重新配置Sentinel
Sentinel是运行时配置的,这意味着可以在运行时更改配置。
请注意,如果有多个Sentinel,改变单个哨兵的配置不会自动将改变传播到网络中的其他哨兵。
// 列出所有已知的主节点及其名称
$ udbkv-cli -h 192.168.4.147 -p 5001 SENTINEL masters
// 修改名为 mymaster 的主节点的down-after-milliseconds:
192.168.4.147:5001> SENTINEL SET mymaster down-after-milliseconds 1000
// 还可以只更改quorum配置
192.168.4.147:5001> SENTINEL SET mymaster quorum 2
添加或删除Sentinel
添加Sentinel
Sentinel是自动发现的,因此,您需要做的就是启动新的Sentinel来监控当前活动的主节点。(如果您需要一次添加多个哨兵,建议一个接一个地添加,等待所有其他哨兵都已经知道第一个后再添加下一个。以防在添加新的哨兵的过程中发生故障。)
删除Sentinel
停止要删除的哨兵的哨兵进程。
向所有其他哨兵实例发送 SENTINEL RESET * 命令(如果只想重置一个主节点,可以使用确切的主节点名称,而不是*命令)。一个接一个,在实例之间等待至少30秒。
通过检查每个哨兵的 SENTINEL MASTER mastername 的输出,检查所有哨兵是否同意当前活动的哨兵数量。
Sentinel访问控制列表验证
使用ACL保护Sentinel实例的第一步是防止任何未经授权的访问。要做到这一点,您需要禁用默认超级用户(或者至少用强密码设置它)并创建一个新的超级用户,并允许它访问发布/订阅通道:
// 可以在配置指令提供另一个超级用户的凭据:
$ vi udbkv-data-s5001/sentinel.conf
---
sentinel sentinel-user myuser
sentinel sentinel-pass 123456
---
// 或者在Sentinel实例上创建具有最低权限的超级用户user1 ,并设置密码为 123456
192.168.4.147:5001> ACL SETUSER user1 on >123456 ~* +@all
OK
// 或者给user3提供最低控制权限的凭据。例如:
192.168.4.147:5001> ACL SETUSER user2 ON >somepassword allchannels +ping +subscribe +role +publish +info +client|setname +client|kill
OK
// 禁用默认超级用户。
192.168.4.147:5001> ACL SETUSER default off
OK
// 删除用户
192.168.4.147:5001> ACL DELUSER user2
(integer) 1
安装 cluster
[unvdb@localhost udbkv-24.1.1-linux-x86_64]$ ./setup.sh udbkv
---
根据需要设置,也可选择默认值.
对于健壮的部署,至少需要6个cluster实例,
其中有三个主服务器和三个副本服务器。
例如:
/data/udbkv-data-c7001 192.168.4.147:7001
/data/udbkv-data-c7002 192.168.4.147:7002
/data/udbkv-data-c7003 192.168.4.147:7003
/data/udbkv-data-c7004 192.168.4.147:7004
/data/udbkv-data-c7005 192.168.4.147:7005
/data/udbkv-data-c7006 192.168.4.147:7006
---
cluster 配置
$ vi udbkv-data-c7001/udbkv.conf
---
port 7001
replica-announce-ip 192.168.4.147
protected-mode no
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 5000
appendonly yes
-- 同样配置其他cluster节点
-- 修改配置文件后重启实例
创建Udbkv集群
既然我们已经运行了许多实例,那么您需要通过向节点写入一些有意义的配置来创建您的集群。 您可以手动配置和执行单个实例。让我们回顾一下如何手动操作。 要创建群集,请运行:
$ udbkv-cli --cluster create 192.168.4.147:7001 192.168.4.147:7002 192.168.4.147:7003 192.168.4.147:7004 192.168.4.147:7005 192.168.4.147:7006 --cluster-replicas 1
---
-- 在询问是否使用当前配置时输入:yes
-- 如果一切顺利,将看到如下消息:
[OK] All 16384 slots covered.
-- 这意味着至少有一个主实例服务于16384个可用插槽中的每一个。
---
与集群互动
可以使用udbkv-cli命令行实用程序来测试Udbkv集群:
$ udbkv-cli -c -h 192.168.4.147 -p 7001
192.168.4.147:7001> set foo bar
> Redirected to slot [12182] located at 192.168.4.147:7003
OK
192.168.4.147:7003>
192.168.4.147:7003> set hello world
-> Redirected to slot [866] located at 192.168.4.147:7001
OK
192.168.4.147:7001>
192.168.4.147:7001> get foo
-> Redirected to slot [12182] located at 192.168.4.147:7003
"bar"
192.168.4.147:7003>
192.168.4.147:7003> get hello
-> Redirected to slot [866] located at 192.168.4.147:7001
"world"
对集群进行重分片
现在,我们准备尝试集群重分片。重分片基本上意味着将哈希槽从一组节点移动到另一组节点。与集群创建一样,使用udbkv-cli实用程序完成的。
// 获取主节点信息
$ udbkv-cli -h 192.168.4.147 -p 7001 cluster nodes | grep myself
// 测试集群的运行状况:
$ udbkv-cli --cluster check 192.168.4.147:7001
// 以交互方式手动输入参数,进行集群重分片
$ udbkv-cli --cluster reshard 192.168.4.147:7001
// 或者,自动地执行重分片
// $ udbkv-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
测试故障转移
我们可以识别一个主节点,并使用以下命令使其崩溃:
// 识别主节点
$ udbkv-cli -c -h 192.168.4.147 -p 7001 cluster nodes | grep master
---
结果显示 7003 7002 7001
---
// 识别从节点
$ udbkv-cli -c -h 192.168.4.147 -p 7001 cluster nodes | grep slave
---
结果显示 7005 7004 7006
---
// kill 192.168.4.147:7002 进程,或者
$ udbkv-cli -h 192.168.4.147 -p 7002 debug segfault
Error: Server closed the connection
// 重启节点7002
$ udbkv-data-c7002/restart.sh
// 等待一段时间,检查故障转移后的集群设置
$ udbkv-cli -c -h 192.168.4.147 -p 7001 cluster nodes
---
结果显示:
7003 7005 7001 为主节点
7004 7006 7002 为副本节点
---
手动故障转移
Udbkv Cluster使用CLUSTER FAILOVER命令支持手动故障转移,该命令必须在要进行故障转移的主服务器的一个副本服务器上执行。手动故障转移比较特殊,与实际主节点故障导致的故障转移相比更安全。它们以一种避免过程中数据丢失的方式发生,即仅当系统确定新的主节点处理了来自旧主节点的所有复制流时,才将客户端从原始主节点切换到新主节点。
// 手动故障转移前7002是7005的从节点
$ udbkv-cli -c -h 192.168.4.147 -p 7002 info replication
// 手动故障转移
$ udbkv-cli -h 192.168.4.147 -p 7002 CLUSTER FAILOVER
OK
// 手动故障转移后7002是7005主节点
$ uudbkv-cli -c -h 192.168.4.147 -p 7002 info replication
添加新节点
现在,我们准备添加一个新的节点。
– 添加新实例/data/udbkv-data-c7007 192.168.4.147:7007 – 修改配置文件并启动
// 添加新节点到集群
$ udbkv-cli --cluster add-node 192.168.4.147:7007 192.168.4.147:7001
[OK] New node added correctly.
// 等待一段时间,检查集群设置
$ udbkv-cli -h 192.168.4.147 -p 7001 cluster nodes | grep master
---
结果显示:
b07bff3c4bb210d4dd26303b5a3404769eff67e7 192.168.4.147:7002@17002master - 0 1722413375000 9 connected 0-14 5461-10927
b3384064b197a72a588a56b5e546c52564781cef 192.168.4.147:7007@17007master - 0 1722413375552 0 connected
de66a072dcf77575808bd6e9d8ad19ce34e8dc76 192.168.4.147:7003@17003master - 0 1722413375000 3 connected 10928-16383
9a756681e6059c44a5cbb51f0e373314cc633389 192.168.4.147:7001@17001 myself,master - 0 1722413374000 1 connected 15-5460
---
与其他主节点相比,新节点 192.168.4.147:7007 它有两个特点:
它不保存数据,因为它没有分配哈希槽。
因为它是没有分配哈希槽的主节点,所以当副本节点希望成为主节点时,它不参与选举过程
所以,我们可以使用udbkv-cli的重分片特性为该节点分配哈希槽。
$ udbkv-cli --cluster reshard 192.168.4.147:7007
添加新节点作为副本
添加新副本有两种方式
一个明显的方法是再次使用udbkv-cli,但使用–cluster-slave选项,如下所示:
-- 已创建cluster新实例/data/udbkv-data-c7008 192.168.4.147:7008 并启动
// 随机分配主节点
$ udbkv-cli --cluster add-node 192.168.4.147:7008 192.168.4.147:7001 --cluster-slave
// 指定主节点: 提供指定的主节点的node-id:
$ udbkv-cli --cluster add-node 192.168.4.147:7008 192.168.4.147:7001 --cluster-slave --cluster-master-id <7001-master-id>
另一种更为手动的方法是将新节点添加为空主节点,然后使用CLUSTER REPLICATE命令将其转换为副本。
// 将新节点 192.168.4.147:7009 添加为空主节点
$ udbkv-cli --cluster add-node 192.168.4.147:7009 192.168.4.147:7001
// 连接到新节点(192.168.4.147:7009)
// 使用CLUSTER REPLICATE命令将其转换为副本,并为7001添加一个副本
192.168.4.147:7009> cluster replicate <7001-master-id>
// 验证:
$ udbkv-cli -h 192.168.4.147 -p 7001 cluster nodes | grep slave
$ udbkv-cli -h 192.168.4.147 -p 7001 cluster nodes | grep master
移除节点
// 删除节点7009和7010
$ udbkv-cli --cluster del-node 192.168.4.147:7001 '<7009-node-id>'
$ udbkv-cli --cluster del-node 192.168.4.147:7001 '<7010-node-id>'
– 1. 删除空主节点
// 删除空主节点:第一个参数只是群集中的一个随机节点,第二个参数是要删除的节点的ID。
// 删除空主节点:7010
$ udbkv-cli --cluster del-node 192.168.4.147:7001 '<7010-node-id>'
– 2. 删除非空主节点
在它的一个副本上执行手动故障转移,并在它变成新主节点的副本后删除该节点。
// 故障转移前:7010 节点作为 7011 的主节点
$ udbkv-cli -h 192.168.4.137 -p 7011 CLUSTER FAILOVER
-- 删除 7010 节点
$ udbkv-cli --cluster del-node 192.168.4.147:7001 '<7010-node-id>'
– 3. 删除故障主节点
// 该命令将在每个节点上执行集群忽略CLUSTER FORGET命令
// 删除故障主节点:7012
$ udbkv-cli --cluster call 192.168.4.147:7001 cluster forget `<7012-master-id>`
副本迁移
在Udbkv集群中,只需使用以下命令,您就可以随时将副本节点重新配置为使用不同的主节点进行复制:
CLUSTER REPLICATE
// 查看主从关系
// 迁移前:7007 作为 7001 的从节点
$ udbkv-cli -c -h 192.168.4.147 -p 7007 INFO replication
// 迁移7007到主节点7002
// 迁移后:7007 作为 7002 的从节点
$ udbkv-cli -c -h 192.168.4.147 -p 7007
192.168.4.147:7007> CLUSTER REPLICATE <7002-master-id>
JAVA 客户端
Jedis V5.1.3
在pom.xml中添加依赖
// C:\build64\project\jedis_test\pom.xml
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.3/version>
</dependency>
</dependencies>
单节点测试
Java测试代码
//C:\build64\project\jedis_test\src\main\java\com\unvdb\Main.java
package com.unvdb;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.args.ListPosition;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
public class Main {
public static void test_BasicOperations(Jedis jedis) {
System.out.println("\n\n【基础操作测试】");
// 查看服务是否运行
String ping = jedis.ping();
System.out.println("UDB-KV 连接测试: " + ping);
// 设置键值对
jedis.set("key1", "swawdwa");
// 获取存储的数据并输出
System.out.println("udbkv 存储的 key1 为: " + jedis.get("key1"));
// 修改键名
jedis.rename("key1", "key2");
System.out.println("udbkv 重命名的 key2 为: " + jedis.get("key2"));
// 判断键是否存在,如果存在返回1,不存在返回0
boolean ifExists = jedis.exists("key1");
System.out.println("key1是否存在:" + ifExists);
// 查看键对应的value的类型
String type = jedis.type("key2");
System.out.println("key2的类型:" + type);
// 设置过期时间,以秒为单位
jedis.expire("key2", 10);
// 查看有效时间,以秒为单位
long ttl = jedis.ttl("key2");
System.out.println("key2的有效时间(10s):" + ttl);
// 删除键及对应的值
jedis.del("key2");
}
public static void test_DataTypeOfString(Jedis jedis) {
System.out.println("\n\n【UDBKV 数据类型测试】 字符串类型测试:");
// 1. set 设置键值
jedis.set("string_key", "This is a string");
// 2. get: 获取key对应的string值,如果key不存在返回nil
String stringKey = jedis.get("string_key");
System.out.println("设置/获取键值: " + stringKey);
// 3. SETEX 设置键值及过期时间,以秒为单位
jedis.setex("sleep_key", 3, "This is a sleep string");
String sleepKey1 = jedis.get("sleep_key");
System.out.println("sleep前sleep_key为: " + sleepKey1);
try{
Thread.sleep(3001);
} catch (Exception e) {
throw new RuntimeException("Exception: "+e);
}
String sleepKey2 = jedis.get("sleep_key");
System.out.println("sleep后sleep_key为: " + sleepKey2);
// 4. setnx:如果key不存在,设置key对应string类型的值。如果key已经存在,返回0。
long result1 = jedis.setnx("string_key", "This is a string X1");
long result2 = jedis.setnx("string_key1", "This is a string X2");
if (result1 == 0) {
System.out.println("setnx:string_key 已存在,不再设置。");
}
if (result2 != 0) {
String string_key1 = jedis.get("string_key1");
System.out.println("setnx:string_key1 不存在,所以设置为: " + string_key1);
}
// 5. getsetkey : 先获取key的值,再设置key的值。
jedis.getSet("string_key", "This is a modified string XX");
System.out.println("getset: 修改后string_key为:: " + jedis.get("string_key"));
// 6. MSET 设置多个键值
jedis.mset("string_key", "This is a string XXX", "string_key2", "This is a string XXX2");
// 7. MGET 根据多个键获取多个值
String multiKey = jedis.mget("string_key", "string_key2").toString();
System.out.println("设置/获取多个键值: " + multiKey);
// 8 msetnx: 一次设置多个key的值,但是不会覆盖已经存在的key.
long result4 = jedis.msetnx("string_key", "This is a string XXXX", "string_key3", "This is a string XXXX3");
System.out.println(result4);
String mylist2 = jedis.mget("string_key", "string_key3").toString();
System.out.println("msetnx 获取一次性设置的多个键值对2:" + mylist2);
// 9. INCR 运算:对key的值做++操作,并返回新的值。incr一个不存在的key,则设置key值为1
long result5 = jedis.incr("string_key3");
System.out.println("INCR不存在的string_key3为: " + result5);
// 10. INCRBY 将key对应的value加整数
long reIncrBy = jedis.incrBy("string_key3",2);
System.out.println("INCRBY string_key3 2后: " + reIncrBy);
// 11. DECR 将key对应的value减1,decr一个不存在key,则设置key值为-1
long result6 = jedis.decr("string_key3");
System.out.println("DECR string_key3后为: " + result6);
// 12. DECRBY 将key对应的value减整数
long result7 = jedis.decrBy("string_key3", 3);
System.out.println("string_key3的key值-3后为: " + result7);
// 13. incrbykey 对key加上指定值,key不存在时候会设置key,并认为原来的value 是0
System.out.println("incrbykey前 string_key3为: " + jedis.get("string_key3"));
jedis.incrBy("string_key3", 3);
System.out.println("incrbykey string_key3 3后为 : " + jedis.get("string_key3"));
// 14. decrbykey :对key减去指定值
jedis.decrBy("string_key3", 3);
System.out.println("decrbykey string_key3 3为 : " + jedis.get("string_key3"));
// 15. APPEND 追加值
jedis.append("string_key", " APPEND");
System.out.println("APPEND string_key后: " + jedis.get("string_key"));
// 16. STRLEN 获取值长度
long result8 = jedis.strlen("string_key");
System.out.println("string_key的长度为: " + result8);
}
public static void test_DataTypeOfHash(Jedis jedis) {
System.out.println("\n\n【UDBKV 数据类型测试】 哈希类型测试:");
// 1. HSET 设置单个属性
// 2. HGET 获取一个属性的值
String hashkey = "user:1000";
jedis.hset(hashkey, "name", "lily");
String name = jedis.hget(hashkey, "name");
System.out.println("设置/获取单个属性的值: " + name);
Map<String, String> hashMap = new HashMap<>();
hashMap.put("email", "Lily@unvdb.com");
hashMap.put("age", "20");
// 3. HMSET 设置多个属性
// 4. HMGET 获取多个属性的值
jedis.hmset(hashkey, hashMap);
List<String> hashmget = jedis.hmget(hashkey, "email", "age");
System.out.println("设置/获取多个属性的值: " + hashmget);
// 5. HGETALL 获取所有属性和值
Map<String, String> hashgetall = jedis.hgetAll(hashkey);
System.out.println("获取所有属性和值: " + hashgetall);
// 6. HKEYS 获取所有的属性
Set<String> hashkeys = jedis.hkeys(hashkey);
System.out.println("获取所有的属性: " + hashkeys);
// 7. HLEN 返回包含属性的个数
long hashlen = jedis.hlen(hashkey);
System.out.println("包含属性的个数: " + hashlen);
// 8. HVALS 获取所有值
List<String> hashvalues = jedis.hvals(hashkey);
System.out.println("所有值: " + hashvalues);
// 9. HEXISTS 判断属性是否存在
boolean hashexits = jedis.hexists(hashkey, "email");
System.out.println("判断email属性是否存在: " + hashexits);
// 10. HDEL 删除属性及值
long hashdel = jedis.hdel(hashkey, "age");
System.out.println("删除age属性及值: " + hashdel);
// 11. HSTRLEN 返回值的字符串长度
long hashstrlen = jedis.hstrlen(hashkey, "name");
System.out.println("name值的字符串长度: " + hashstrlen);
}
public static void test_DataTypeOfList(Jedis jedis) {
System.out.println("\n\n【UDBKV 数据类型测试】 列表类型测试:");
// 1. LPUSH 在头部插入数据
jedis.lpush("list_key", "element1");
// 2. 在尾部插入数据
jedis.rpush("list_key", "element3");
// 3. 在一个元素的前|后插入新元素
jedis.linsert("list_key", ListPosition.BEFORE, "element3", "element2");
jedis.linsert("list_key", ListPosition.AFTER, "element3", "element4");
jedis.rpush("list_key", "element5");
jedis.rpush("list_key", "element6");
jedis.rpush("list_key", "element7");
String listRange1 = jedis.lrange("list_key", 0, 4).toString();
System.out.println("初始 list_key 为: " + listRange1);
// 4. 设置指定索引的元素值
String result4 = jedis.lset("list_key", 1, "element2-X");
System.out.println("设置指定索引的元素值:" + result4);
// 5. 移除并且返回 key 对应的 list 的第一个元素
String leftKey = jedis.lpop("list_key");
System.out.println("移除的第一个元素为: " + leftKey);
// 6. 移除并返回存于 key 的 list 的最后一个元素
String rightKey = jedis.rpop("list_key");
System.out.println("移除的最后一个元素为: " + rightKey);
// 7. 返回存储在 key 的列表里指定范围内的元素
List<String> listRange2 = jedis.lrange("list_key", 0, 7);
System.out.println("移除第一个和最后一个元素后 list_key 为: " + listRange2.toString());
// 8. 裁剪列表
String lisTrim = jedis.ltrim("list_key", 0, 2);
System.out.println("裁剪列表: " + lisTrim);
// 9. 返回存储在 key 里的list的长度
long listLen = jedis.llen("list_key");
System.out.println("裁剪后list_key的长度: " + listLen);
// 10. 返回列表里索引对应的元素
String listIndex = jedis.lindex("list_key", 0);
System.out.println("索引1对应的元素: " + listIndex);
}
public static void test_DataTypeOfSet(Jedis jedis) {
System.out.println("\n\n【UDBKV 数据类型测试】 集群类型测试:");
// 1. SADD 添加元素
jedis.sadd("fruits", "apple");
jedis.sadd("fruits", "banana");
jedis.sadd("fruits", "orange");
jedis.sadd("fruitsS", "banana");
jedis.sadd("fruitsS", "lichee");
// 2. SMEMBERS 返回key集合所有的元素
Set<String> setMembers1 = jedis.smembers("fruits");
Set<String> setMembers2 = jedis.smembers("fruitsS");
System.out.println("fruits 集合所有的元素: " + setMembers1);
System.out.println("fruitsS 集合所有的元素: " + setMembers2);
// 3. SCARD 返回集合元素个数
long setCard = jedis.scard("fruits");
System.out.println("fruits集合元素个数: " + setCard);
// 4. SINTER 求多个集合的交集
Set<String> setInter = jedis.sinter("fruits", "fruitsS");
System.out.println("两个集合的交集: " + setInter);
// 5. SDIFF 求某集合与其它集合的差集
Set<String> setDiff = jedis.sdiff("fruits", "fruitsS");
System.out.println("两个集合的差集: " + setDiff);
// 6. SUNION 求多个集合的合集
Set<String> setUnion = jedis.sunion("fruits", "fruitsS");
System.out.println("两个集合的合集: " + setUnion);
// 7. SISMEMBER 判断元素是否在集合中
boolean setIsMember = jedis.sismember("fruits", "apple");
System.out.println("apple元素是否在fruits集合中:" + setIsMember);
}
public static void test_DataTypeOfZSet(Jedis jedis) {
System.out.println("\n\n 【UDBKV 数据类型测试】 有序集合类型测试:");
// 1. ZADD 添加
jedis.zadd("fruits1", 1, "peach");
jedis.zadd("fruits1", 2, "apple");
jedis.zadd("fruits1", 3, "banana");
jedis.zadd("fruits1", 4, "orange");
jedis.zadd("fruits1", 5, "lichee");
// 2. ZRANGE 返回指定范围内的元素
List<String> zsetRange = jedis.zrange("fruits1", 0, 5);
System.out.println("范围0~5的元素: " + zsetRange);
// 3. ZCARD 返回元素个数
long zsetCard = jedis.zcard("fruits1");
System.out.println("元素个数: " + zsetCard);
// 4. ZCOUNT 返回有序集key中,score值在min和max之间的成员
long zsetCcount = jedis.zcount("fruits1", 1, 3);
System.out.println("score值在0和3之间的成员: " + zsetCcount);
// 5. ZSCORE 返回有序集key中,成员member的score值
double zsetScore = jedis.zscore("fruits1","lichee");
System.out.println("成员lichee的score值:" + (int)zsetScore);
}
public static void main(String[] args) {
// 创建 Jedis 对象,指定服务器的 ip 和端口
try(Jedis jedis = new Jedis("192.168.4.147", 6789)){
System.out.println("UDB-KV 测试");
jedis.flushDB();
// 基本操作测试
test_BasicOperations(jedis);
// 数据类型操作测试
test_DataTypeOfString(jedis);
test_DataTypeOfHash(jedis);
test_DataTypeOfList(jedis);
test_DataTypeOfSet(jedis);
test_DataTypeOfZSet(jedis);
} catch (RuntimeException e) {
throw new RuntimeException(e);
}
}
}
集群类型测试
#C:\build64\project\jedis_test\src\main\java\com\unvdb\UdbkvClusterTest.java
package com.unvdb;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class UdbkvClusterTest {
public static Set<HostAndPort> convertNodeStringsToHostAndPorts(Set<String> nodeStrings) {
Set<HostAndPort> hostAndPorts = new HashSet<HostAndPort>();
for (String nodeString : nodeStrings) {
// 每个节点字符串都是 "ip:port" 的格式
String[] parts = nodeString.split(":");
if (parts.length == 2) {
String ip = parts[0];
int port = Integer.parseInt(parts[1]);
hostAndPorts.add(new HostAndPort(ip, port));
}
}
return hostAndPorts;
}
public static void main(String[] args) {
System.out.println("【UDB-KV Cluster测试】");
Set<HostAndPort> nodes = new HashSet<HostAndPort>() {
{
add(new HostAndPort( "192.168.4.147", 7001));
}};
GenericObjectPoolConfig<Connection> poolConfig =new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(100);
poolConfig.setMaxIdle(20);
poolConfig.setMinIdle(5);
poolConfig.setTestOnBorrow(true);
// 创建JedisCluster对象
try (JedisCluster jedisCluster = new JedisCluster(nodes,poolConfig)) {
// 判断连接情况
if (jedisCluster.ping().equals("PONG")) {
System.out.println("【UDB-KV Cluster测试】连接成功");
jedisCluster.set("testKey", "1001");
System.out.println("testKey = "+jedisCluster.get("testKey"));
// 获取集群节点
Set<String> nodeStrings = jedisCluster.getClusterNodes().keySet();
Set<HostAndPort> nodes1 = convertNodeStringsToHostAndPorts(nodeStrings);
System.out.println("\n\n1. UDBKV Cluster Nodes:");
for (HostAndPort node : nodes1) {
System.out.println(" " + node.getHost() + ":" + node.getPort());
}
// 迁移测试
try(Jedis jedis46 = new Jedis("192.168.4.46", 6379)) {
// 获取源数据
Set<String> keyList1 = jedis46.keys("*");
System.out.println("KeyList :" + keyList1);
// 循环写入集群
for (String key : keyList1) {
jedisCluster.set(key, jedis46.get(key));
System.out.println("写入 "+key + " = "+jedisCluster.get(key));
}
}
// 集群基本操作
try(Jedis jedis = new Jedis("192.168.4.147", 7001)){
//test_NodeBasicOperations(jedis);
// 获取客户端列表
String clientList = jedis.clientList();
System.out.println("\n\n2. UDBKV client list:");
System.out.println(clientList);
// 查看集群状态
String clusterInfo = jedis.clusterNodes();
System.out.println("\n\n3. UDBKV Cluster Info:");
System.out.println(clusterInfo);
// 查看槽分布
List<Object> slotsList = jedis.clusterSlots();
System.out.println("\n\n4. UDBKV Cluster Slots:");
for (Object slot : slotsList) {
System.out.println(slot);
}
// 查看节点key数量
long keys = jedis.keys("*").size();
System.out.println("\n\n5. UDBKV Cluster Keys:");
System.out.println(keys);
// 查看key对应槽
long keySlot = jedis.clusterKeySlot("testKey");
System.out.println("\n\n6. UDBKV Cluster KeySlot:");
System.out.println(keySlot);
//
List<String> acllist = jedis.aclList();
System.out.println("\n\n7. UDBKV Cluster AclList:");
System.out.println(acllist);
// 查看节点基本信息
String nodeInfo = jedis.info();
System.out.println("\n\n8. UDBKV Cluster NodeInfo:");
System.out.println(nodeInfo);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
} catch (JedisConnectionException e) {
System.out.println(e.getMessage());
throw new RuntimeException(e.toString());
}
}
}