
在如今的分布式系统、高并发场景中,Redis绝对是绕不开的存在——它既是高性能的缓存中间件,也是灵活的键值数据库,甚至能承担消息队列、分布式锁等多种角色。很多开发者日常都会用Redis,但大多停留在“set/get”的基础用法,对它为什么快、支持哪些核心场景、背后靠什么架构和算法实现这些特性,却了解不深。今天就来一篇干货,从核心定位切入,逐步拆解Redis的功能、特性、架构、算法,结合版本演进和典型应用,帮大家真正读懂Redis的核心技术体系。
Learn and share.

在如今的分布式系统、高并发场景中,Redis绝对是绕不开的存在——它既是高性能的缓存中间件,也是灵活的键值数据库,甚至能承担消息队列、分布式锁等多种角色。很多开发者日常都会用Redis,但大多停留在“set/get”的基础用法,对它为什么快、支持哪些核心场景、背后靠什么架构和算法实现这些特性,却了解不深。今天就来一篇干货,从核心定位切入,逐步拆解Redis的功能、特性、架构、算法,结合版本演进和典型应用,帮大家真正读懂Redis的核心技术体系。

一、核心瓶颈
主要限制:内存容量(单节点内存上限)、网络带宽(高并发下网络I/O瓶颈)、单核CPU性能(单线程主线程瓶颈)。
最近阅读了Redis6.2源码,添加了一些注释,感兴趣的同学可以看下。
https://github.com/neohope/NeoRedisSrc
Redis的分片技术一般是通过客户端或代理来实现的
1、用jedis实现分片的时候,服务端不需要做任何配置即可
package com.djhu.redis.test;
import java.util.ArrayList;
import java.util.List;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
public class JedisShardTest
{
public static void main(String[] args)
{
List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>();
jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6379));
jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6380));
ShardedJedis sharded = new ShardedJedis(jedisShardInfoList);
sharded.set("key01", "a");
sharded.set("key02", "b");
sharded.set("key03", "c");
sharded.set("key04", "d");
sharded.set("key05", "e");
System.out.println(sharded.get("key03"));
}
}
2、用Jedis连接池实现分片
package com.djhu.redis.test;
import java.util.ArrayList;
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.util.Hashing;
import redis.clients.util.Sharded;
public class JedisSharedFactory
{
// 最大可用连接数,默认值为8,如果赋值为-1则表示不限制
private static int MAX_TOTAL = 256;
// 最大空闲连接数,默认值为8
private static int MAX_IDLE = 32;
// 最小空闲连接数
private static int MIN_IDLE = 4;
// 最大等待连接毫秒数,默认值为-1表示永不超时
private static int MAX_WAIT = 3000;
// 连接redis超时时间
private static int TIMEOUT = 3000;
// true表示验证连接
private static boolean TEST_ON_BORROW = true;
//连接池
private static ShardedJedisPool jedisPool = null;
public static void initJedisPool()
{
try
{
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_TOTAL);
config.setMaxIdle(MAX_IDLE);
config.setMinIdle(MIN_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>();
jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6379));
jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6380));
jedisPool = new ShardedJedisPool(config, jedisShardInfoList,Hashing.MURMUR_HASH,Sharded.DEFAULT_KEY_TAG_PATTERN);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public synchronized static ShardedJedis getConnection()
{
try
{
if (jedisPool != null)
{
ShardedJedis resource = jedisPool.getResource();
return resource;
} else
{
return null;
}
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
public static void returnResource(final ShardedJedis jedis)
{
if (jedis != null)
{
jedis.close();
}
}
public static void main(String[] args)
{
initJedisPool();
ShardedJedis redis = getConnection();
redis.set("key10", "j");
redis.set("key11", "k");
redis.set("key12", "l");
redis.set("key13", "m");
redis.set("key14", "n");
System.out.print(redis.get("key12"));
returnResource(redis);
}
}
1、源码如下
package com.djhu.redis.test;
import java.util.Set;
import java.util.HashSet;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class JedisClusterTest
{
public static void main(String[] args)
{
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6379));
jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6380));
jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6381));
jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6382));
jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6383));
jedisClusterNodes.add(new HostAndPort("172.16.172.4", 7384));
//JedisCluster cluster = new JedisCluster(jedisClusterNodes,3000,1000);
JedisCluster cluster = new JedisCluster(jedisClusterNodes);
cluster.set("key10", "j");
cluster.set("key11", "k");
cluster.set("key12", "l");
cluster.set("key13", "m");
cluster.set("key14", "n");
System.out.println(cluster.get("key12"));
}
}
2、如果遇到下面错误,主要是因为建立cluster时,ip用了127.0.0.1。用其他ip重建一下cluster,就可以解决了。
Exception in thread "main" redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections? at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:34) at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68) at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85) at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68) at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85) at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68) at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:29) at redis.clients.jedis.JedisCluster.set(JedisCluster.java:75)
1、关闭cluster全部节点
2、删除所有nodes.conf文件
3、开启全部节点
4、依次flushall
5、重建集群即可
1、redis3的cluster是基于ruby的,所以要安装好ruby,然后安装redis的gem
gem install redis
2、然后配置6份redis,修改配置如下
| 节点 | ||||||
| dbm6379 | dbm6380 | dbm6381 | dbs6382 | dbm6383 | dbm6384 | |
| 配置文件 | dbm6379/conf/redis.conf | dbm6380/conf/redis.conf | dbm6381/conf/redis.conf | dbs6382/conf/redis.conf | dbm6383/conf/redis.conf | dbm6384/conf/redis.conf |
| port | 6379 | 6380 | 6381 | 6382 | 6383 | 6384 |
| logfile | “/home/neohope/DB/redis-3.0.4/cluster/dbm6379/logs/redis_log.log” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6380/logs/redis_log.log” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6381/logs/redis_log.log” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6382/logs/redis_log.log” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6383/logs/redis_log.log” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6384/logs/redis_log.log” |
| dir | “/home/neohope/DB/redis-3.0.4/cluster/dbm6379/data” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6380/data” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6381/data” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6382/data” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6383/data” | “/home/neohope/DB/redis-3.0.4/cluster/dbm6384/data” |
| cluster-enabled | yes | yes | yes | yes | yes | yes |
| cluster-config-file | nodes-6379.conf | nodes-6380.conf | nodes-6381.conf | nodes-6382.conf | nodes-6383.conf | nodes-6384.conf |
| cluster-node-timeout | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 |
| cluster-migration-barrier | 1 | 1 | 1 | 1 | 1 | 1 |
| cluster-require-full-coverage | yes | yes | yes | yes | yes | yes |
3、启动redis
#!/bin/sh ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6379/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6380/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6381/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6382/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6383/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6384/conf/redis.conf & echo $!
4、配置cluster
#!/bin/sh #这里最好不要用127.0.0.1做地址 ~/DB/redis-3.0.4/bin/redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
5、测试redis cluster
~/DB/redis-3.0.4/bin/redis-cli -c -p 6379 set key01 a set key02 b set key03 c set key04 d dbsize keys * get key03
6、关闭redis
#!/bin/sh ~/DB/redis-3.0.4/bin/redis-cli -p 6379 shutdown ~/DB/redis-3.0.4/bin/redis-cli -p 6380 shutdown ~/DB/redis-3.0.4/bin/redis-cli -p 6381 shutdown ~/DB/redis-3.0.4/bin/redis-cli -p 6382 shutdown ~/DB/redis-3.0.4/bin/redis-cli -p 6383 shutdown ~/DB/redis-3.0.4/bin/redis-cli -p 6384 shutdown