缓存服务 (Cache) 可以为您提供高性能的缓存集群,目前支持 Redis standalone, Redis cluster 和 Memcached 缓存。本指南旨在介绍如何创建、使用及管理缓存服务(Cache)。
在青云上,您可以很方便的创建和管理一个缓存集群。一个缓存集群包含多个缓存节点,Redis 支持一主多从、多主多从架构,Memcached 支持多个独立缓存节点。 另外,我们提供在线扩容、自动备份和监控告警等功能来帮助您更好的管理集群。集群将运行于私有网络内,结合青云提供的高性能硬盘,在保障高性能的同时兼顾您的数据安全。
注解
为了保障数据安全,缓存服务需要运行在受管的私有网络中。 所以在创建一个缓存服务之前,您至少需要拥有一个路由器和一个受管私有网络,受管私有网络需要和路由器连接,并开启 DHCP 服务(默认开启)。
Redis 是一个基于内存、键值对的开源存储系统,并且支持数据持久化。本例中,将介绍如何使用和管理 Redis standalone (一主多从)缓存服务, Redis cluster (多主多从)服务请见下一章节。目前 Redis standalone 支持版本 2.8.17 和 3.0.5。
Redis standalone 缓存服务本身支持数据持久化,您还可以通过备份功能对缓存服务进行定期备份,从而对关键时间点的数据进行保护。 缓存服务支持自动备份、手动备份、基于备份创建新缓存等功能。 备份功能是块级别的 (block level),最大程度保证数据安全, 同时备份过程不会影响正在运行的缓存服务。
1)配置自动备份
除了自动备份,您可以在需要的时候随时对缓存服务进行手动备份。
![]()
和青云其他资源的备份相同,每个缓存最多有两条备份链,每条备份链可以有多个备份节点,包含一个全量节点和多个增量节点。 更多关于备份功能的介绍可以参见 备份与恢复指南 。
![]()
我们提供了强大的监控和告警功能,帮助用户更好的管理和维护运行中的 Redis 缓存服务。
1)监控服务
我们提供了多达十几项的监控数据,来帮助用户随时了解缓存服务的运行状态和各项指标。 每一项监控都提供了历史监控和实时监控,最短时间间隔可以达到 10s。 Redis 服务的监控数据是通过 Redis 的 Info 命令来进行采集的。 我们提供的监控项目为如下:
- 节点资源监控
- 带宽监控: 监控缓存节点的网卡出/入流量。
- 基础资源监控: 包括 CPU、内存、硬盘等监控
- 缓存服务监控
- 内存监控: 缓存服务的实际内存使用率,对应 used_memory 字段。
- Get操作: Get 相关操作的总数。
- Set操作: Set 相关操作的总数。
- Key类型操作数: Key类型操作数的总数。
- String类型操作数: String类型操作数的总数。
- List类型操作数: List类型操作数的总数。
- String类型操作数: String类型操作数的总数。
- Hash类型操作数: Hash类型操作数的总数。
- Set类型操作数: Set类型操作数的总数。
- Sorted Set类型操作数: Sorted Set类型操作数的总数。
- 总连接数: 建立总连接数,对应 total_connections_received 字段。
- 当前连接数: 活跃的连接数,对应 connected_clients 字段。
- 查询命中数: 查询的命中个数,对应 keyspace_hits 字段。
- 查询未命中数: 查询的未命中个数,对应 keyspace_misses 字段。
- 查询命中率: 查询命中率,对应 keyspace_hits / ( keyspace_hits + keyspace_misses )。
- 总Key个数: 缓存中总的 key 个数,所有 db 的 key 个数总和。
- 已过期Key个数: 缓存中已过期 Key 个数,对应 expired_keys 字段。
- 被拒绝Key个数: 缓存中被拒绝 Key 个数,对应 evicted_keys 字段。当缓存内存不足时,会根据用户配置的 maxmemory-policy 来选择性地删除一些 key 来保护内存不溢出。
下图显示了一些监控的图表:
![]()
2)告警服务
我们支持对每个缓存节点配置监控告警策略,当发生异常时,会触发用户设定的告警阈值,并发送短信和邮件通知给用户。 缓存目前支持的监控告警规则有”内存使用率”、”被拒绝的 key 个数”等:
![]()
更多关于告警服务的内容可以参见 监控告警 。
我们通过缓存配置组来管理缓存服务的配置。缓存服务和缓存配置组是解耦的,您可以创建多个独立的缓存配置组,并应用到不同缓存服务。 缓存服务在运行过程中,也可以随时变更缓存配置组。
1)创建新的缓存配置组
- port: 缓存服务端口,默认为 6379。
- appendonly: 是否开启可持久化功能,yes 表示开启(默认),no 表示禁用。注意,当未启用可持久化时,缓存数据会因为缓存服务重启而丢失。
- appendfsync: 可持久化方式,everysec 表示每秒主动将数据刷新到磁盘(默认),always 表示每次插入操作都会刷新到磁盘(强一致性, 但是性能会差),no 表示依赖操作系统来刷新磁盘。
- maxmemory-policy: 指定当内存满时对 key 的删除策略,默认为 volatile-lru,从过期的数据集中挑选最近最少使用的数据淘汰。
- requirepass: 指定 redis 的访问密码。设置该参数后,整个 redis 中每个节点的访问都需要该密码,该密码也作为节点之间同步的密码。当该参数为空时,表示不需要密码。
- slowlog-log-slower-than: 慢日志阈值设置,-1为禁止,0为记录所有的命令,正数值为记录执行时间大于该值的操作。详情见 Redis 官方文档。
- slowlog-max-len: 记录慢日志长度。
- latency-monitor-threshold: 延迟监控阈值设定,0为禁止监控。详情见 Redis 官方文档。
- disable-commands: 指定需要禁用的 redis 命令,需要禁用的多个命令之间用”,”号分隔。
其他更多的配置项解释请参见 Redis 官方文档。
为了方便用户将已有的 Redis 数据迁移至青云的 Redis 中,或者从旧版本升级到新版本,我们提供了一个特殊的缓存配置项 “slaveof-host” 来实现这一目标。 首先假设用户已有的 Redis 服务为一台青云外部的主机,服务 IP 为 1.2.3.4,端口为默认端口 6379 (源 Redis 服务端口必须是 6379)。 新创建的缓存的节点位于某私有网络中,主机 IP 为 192.168.100.2,端口也为 6379。
注解
slaveof-host 方式只对 Redis standalone 有效,对 Redis cluster 无效
通过 Redis 命令来查看新节点的数据是否已经和已有节点数据同步,一种简单的方式是比较两者的键值个数是否相等。
恢复新节点对应的配置项 “slaveof-host”,重新设置为空 “”,断开同步关系,让新节点重新成为缓存服务的主节点。
注解
修改完后,我们需要进行 “保存”,并点击 “应用” 让新的配置生效。
我们可以通过 Redis 自带的性能测试工具 redis-benchmark 来进行性能测试。
我们的测试对象为 8GB 内存的 Redis 缓存服务,如下为性能型 Redis 测试结果:
# redis-benchmark -q -n 1000000
PING_INLINE: 89919.97 requests per second
PING_BULK: 83049.58 requests per second
SET: 82365.54 requests per second
GET: 85207.91 requests per second
INCR: 80211.76 requests per second
LPUSH: 86117.80 requests per second
LPOP: 86355.79 requests per second
SADD: 93414.30 requests per second
SPOP: 101040.72 requests per second
LPUSH (needed to benchmark LRANGE): 92165.90 requests per second
LRANGE_100 (first 100 elements): 41301.83 requests per second
LRANGE_300 (first 300 elements): 11453.05 requests per second
LRANGE_500 (first 450 elements): 7625.67 requests per second
LRANGE_600 (first 600 elements): 6178.48 requests per second
MSET (10 keys): 49193.23 requests per second
我们的测试对象为 8GB 内存的 Redis 缓存服务,如下为超高性能型 Redis 测试结果:
# redis-benchmark -q -n 1000000
PING_INLINE: 146348.59 requests per second
PING_BULK: 138350.86 requests per second
SET: 141743.45 requests per second
GET: 142918.39 requests per second
INCR: 146993.97 requests per second
LPUSH: 148367.95 requests per second
LPOP: 145201.11 requests per second
SADD: 124455.51 requests per second
SPOP: 112637.98 requests per second
LPUSH (needed to benchmark LRANGE): 107642.62 requests per second
LRANGE_100 (first 100 elements): 51392.74 requests per second
LRANGE_300 (first 300 elements): 18827.07 requests per second
LRANGE_500 (first 450 elements): 12450.97 requests per second
LRANGE_600 (first 600 elements): 9635.77 requests per second
MSET (10 keys): 73292.29 requests per second
为了更好的管理 Redis 服务,我们默认禁用一些 Redis 的命令,禁用的命令列表如下:
青云 Redis 集群提供原生的开源 Redis 3.x,除了继续支持以前的一主多从外,还支持多主多从,每个主所在分片 (shard) 平均分摊 16384 个 slots, 增加或删除主节点系统会自动平衡 slots (因为需要迁移数据,时间会有点长)。并且集群支持 HA, 即当某个主节点异常,它的从节点会自动切换成主节点。
当缓存服务创建完成之后,我们可以进行连接测试。
1)检查集群状态
在同一私网中创建一台 Linux 主机,您可能需要先装一些依赖包 (如Ubuntu下apt-get install tcl ruby 和 gem install redis), 然后请 下载 Redis 3.x, 解压后进入 Redis src目录,执行以下命令 (假设 Redis cluster 其中一个节点的 IP 为 192.168.100.13, 端口为 6379)。
./redis-trib.rb check 192.168.100.13:6379
然后您能看到如下的集群信息
Connecting to node 192.168.100.13:6379: OK
Connecting to node 192.168.100.11:6379: OK
Connecting to node 192.168.100.10:6379: OK
Connecting to node 192.168.100.14:6379: OK
Connecting to node 192.168.100.12:6379: OK
Connecting to node 192.168.100.15:6379: OK
>>> Performing Cluster Check (using node 192.168.100.13:6379)
S: f6092dbdb25b6d80416232e50ccd2022860086b0 192.168.100.13:6379
slots: (0 slots) slave
replicates b2d75900b6427f6fbf8ec1a61ee301a2c8f73a6d
M: d3377079e01391b9d16ea699c79453e15f5aa132 192.168.100.11:6379
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: b2d75900b6427f6fbf8ec1a61ee301a2c8f73a6d 192.168.100.10:6379
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 9774f5ff6477eaecb6794395ed726d0f06257c60 192.168.100.14:6379
slots: (0 slots) slave
replicates d3377079e01391b9d16ea699c79453e15f5aa132
M: 704514eb7fa135dd003533568ae9f7babda9464e 192.168.100.12:6379
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 22b3f49a6b87403faeeb1219881e63096802eb6a 192.168.100.15:6379
slots: (0 slots) slave
replicates 704514eb7fa135dd003533568ae9f7babda9464e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
如果发现集群出现异常,比如出现 [ERR] Nodes don’t agree about configuration! 可以尝试用如下命令修复
./redis-trib.rb fix 192.168.100.13:6379
如果发现各分片的 slots 分配不平均,也可以用如下命令平衡一下 (从两个分片迁移 1000 个 slots 到第三个分片里)
./redis-trib.rb reshard --from d3377079e01391b9d16ea699c79453e15f5aa132,b2d75900b6427f6fbf8ec1a61ee301a2c8f73a6d
--to 704514eb7fa135dd003533568ae9f7babda9464e --slots 1000 --yes 192.168.100.13:6379
2)Java 客户端读写数据示例
首先 下载 Jedis 库和 Apache Commons Pool 依赖库。 把下载下来的 commons-pool2-2.0.jar 和 jedis-2.7.3.jar 放到同一目录下如 lib/, 创建 TestRedisCluster.java,内容如下。 然后编译、执行该 Java 程序(假设一个分片的主从节点分别是 192.168.100.10, 192.168.100.13, 端口均为 6379)。
javac -cp :./lib/* TestRedisCluster.java
java -cp :./lib/* TestRedisCluster 192.168.100.10, 192.168.100.13 6379
import java.util.Set;
import java.util.HashSet;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.HostAndPort;
public class TestRedisCluster {
public static void main(String[] args) {
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
//Jedis Cluster will attempt to discover cluster nodes automatically
jedisClusterNodes.add(new HostAndPort(args[0], Integer.valueOf(args[2])));
jedisClusterNodes.add(new HostAndPort(args[1], Integer.valueOf(args[2])));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
int len = str.length();
int loop = 0;
while (loop <= 100) {
loop += 1;
for (int i = 1; i < len; i++) {
int end = len - i;
for (int j = 0; j < end; j++) {
for (int k = j+1; k < end; k++) {
String key = str.substring(j, j+1) +
str.substring(k, k+i) + "_" +
str.substring(i, i+1);
String value = key + "_value";
jc.set(key, value);
String v = jc.get(key);
if (!value.equals(v)) {
System.out.println("Not equal: key[" + key + "], value[" +
value + "], v[" + v + "]");
}
System.out.println(key + "," + value);
}
}
}
}
jc.close();
}
}
注解
这是示例代码,不承担任何责任。更多的 Redis 客户端请见 Redis 官方网站。
3) Hash Tags Keys
Redis 集群采用 CRC16 算法对 key 值哈希到 16384 个 slots 中的一个,因此不同的 key 可能分散到不同的节点中,对于想固定一类 key 值到某一个节点,如按业务分类,可以采用 Hash Tags,下面是从 Redis 文档 摘录的解释。
In order to implement hash tags, the hash slot is computed in a different way. Basically if the key contains a “{...}” pattern only the substring between { and } is hashed in order to obtain the hash slot. However since it is possible that there are multiple occurrences of { or } the algorithm is well specified by the following rules:
Then instead of hashing the key, only what is between the first occurrence of { and the first occurrence of } on its right are hashed.
Examples:
在缓存服务运行过程中,会出现服务能力不足或者容量不够的情况,可以通过扩容来解决,或者服务能力过剩时可以删除节点。在纵向扩容中, 服务需要重启,所以这个时候业务需要停止。在横向伸缩中,数据会发生迁移,但并不影响业务的正常运行。
1)增加集群分片 (shard)
2)增加集群从节点
3)删除集群分片 (shard)
如果写服务能力或容量过剩,也可以删除多余的节点组,即删除主节点和它的所有从节点,删除的过程中系统会自动迁移数据到其它节点中,因此时间会稍长一点。
4)删除集群从节点
如果读服务能力过剩,您也可以删除多余的从节点。删除的时候需要从每个主节点下选择同样数目的从节点,从而保证整个集群不会是一个“畸形”。
5)增加缓存容量
迁移数据既包括 Redis standalone 之间也包括从 Redis Standalone 到 Redis Cluster。
1)从 Redis standalone 迁移数据到 Redis cluster
Redis 3.x 提供了一个从 Redis standalone (包括旧版本 2.8.17) 迁移数据到 Redis cluster 的工具 redis-trib.rb, 请 下载 Redis 3.x, 解压后进入 Redis src目录, 执行以下命令 (假设 Redis standalone 的主节点 IP 为 192.168.100.11,端口为 6379, Redis cluster 其中一个 节点的 IP 为 192.168.100.20, 端口为 6379。
./redis-trib.rb import --from 192.168.100.11:6379 192.168.100.20:6379
注解
在做迁移之前建议对原 Redis standalone 做备份,因为上述操作是对数据进行迁移而不是拷贝。
2)从 Redis 2.8.17 迁移数据到 Redis 3.0.5
青云 Redis 服务也支持从旧版本 Redis standalone 迁移数据到新版本 Redis standalone, 具体操作参见 迁移, 您也可以用slaveof-host的方式同步数据,详情请见 slaveof-host 迁移。
Redis 集群由于是多节点分片且支持 HA,因此目前不支持备份功能,如果有需要用户需自行备份数据。
青云提供了集群的图形化展示,方便用户更直观的查看集群状态和进行各项操作。部分截图如下:
用户可以在图形中进行以下操作:
Memcached 是一个开源、分布式的内存缓存系统。本例中,将介绍如何使用和管理 Memcached 缓存服务。
在本例中,我们将介绍如何创建一个 Memcached 缓存服务。
第一步:选择基本配置
第二步:配置网络
步骤同 创建 Redis 缓存服务 ,不再赘述。
第三步:创建成功
步骤同 创建 Redis 缓存服务 ,不再赘述。
第四步:连接缓存
在缓存服务运行过程中,会出现服务能力不足或者容量不够的情况,您都可以通过扩容来解决。
1)增加缓存节点
Memcached 缓存服务支持多个缓存节点。当容量或者性能不足时,您可以通过增加缓存节点来提升。 如图所示,我们在原有集群基础上新增两个节点,并在创建时为新节点指定 IP。
注解
如果服务能力过剩,您也可以删除多余的缓存节点。
![]()
注解
默认的 Memcached 客户端使用简单 Hash 来进行数据分片,当增加或删除节点时可能会造成大量的缓存失效。可以采用支持一致性 Hash 算法的 Memcached 客户端来避免这个问题,例如 hash_ring
2)增加缓存容量
我们提供了强大的监控和告警功能,帮助用户更好的管理和维护运行中的 Memcached 缓存集群。
1)监控服务
我们提供了多达十几项的监控数据,来帮助用户随时了解缓存服务的运行状态和各项指标。 每一项监控都提供了历史监控和实时监控,最短时间间隔可以达到10s。 Memcached 服务的监控数据是通过 Memcached 的 stats 命令来进行采集的。 我们提供的监控项目为如下:
- 节点监控
- 带宽监控: 监控缓存节点的网卡出/入流量。
- 缓存服务监控
- 内存监控: 缓存服务的实际内存使用率,对应 bytes 字段。
- Get操作: Get 相关操作的总数。
- Set操作: Set 相关操作的总数。
- Delete操作:Delete 相关操作的总数。
- Incr操作: Incr 相关操作的总数。
- Decr操作: Decr 相关操作的总数。
- Touch操作: Touch 相关操作的总数。
- Cas操作: Cas 相关操作的总数。
- Flush操作: Flush 相关操作的总数。
- 总连接数: 建立总连接数,对应 total_connections 字段。
- 当前连接数: 活跃的连接数,对应 curr_connections 字段。
- 查询命中数: 查询的命中个数,对应 get_hits 字段。
- 查询未命中数: 查询的未命中个数,对应 get_misses 字段。
- 查询命中率: 查询命中率,对应 get_hits / ( get_hits + get_misses )。
- 总Key个数: 缓存中总的 Key 个数,对应 curr_item 字段。
- 使用召回内存的Key个数: 缓存中使用召回内存的 Key 个数,对应 reclaimed 字段。
- 被拒绝Key个数: 缓存中被拒绝 Key 个数,对应 evictions 字段。当缓存内存不足时,会根据 LRU 算法来删除一些 key。
下图显示了一些监控的图表:
![]()
2)告警服务
我们支持对每个缓存节点配置监控告警策略,当发生异常时,会触发用户设定的告警阈值,并发送短信和邮件通知给用户。 缓存目前支持的监控告警规则有”内存使用率”、”已过期 Key 个数”等:
![]()
更多关于告警服务的内容可以参见 监控告警 。
我们通过缓存配置组来管理缓存服务的配置。缓存服务和缓存配置组是解耦的,您可以创建多个独立的缓存配置组,并应用到不同缓存服务。 缓存服务在运行过程中,也可以随时变更缓存配置组。
1)创建新的缓存配置组
- port: 缓存服务端口,默认为 11211。
- udp_port: 缓存服务的 UDP 端口,默认为 11211,0 为禁用 UDP 服务端口。
- cas_disabled: 禁用 CAS (Check and Set), 0 为 启用,1 为禁用,默认为 0。
- error_on_memory_exhausted: 内存不足时的行为,0 为删除 key,1 为返回错误,默认为 0。
其他更多的配置项解释请参见 Memcached 官方文档。