目录
NoSQL
NoSQL特点
常见的NoSQL数据库
redis
MongoDB
行式存储数据库
列式存储数据库
redis简介
安装redis
redis启动服务
前台启动
提取信息
后台启动
启动方法
通过客户端连接redis
redis的关闭
redis数据库相关知识
操作数据库命令集
redis的多路复用
多路复用的机制
redis的key基本操作
redis数据类型
String类型
基本操作
String的数据结构
List类型(单键多值)
常用命令
List的数据结构
Set集合
基本操作
set集合数据结构
Hash类型
常用命令
hash数据结构
Zset有序集合
常用命令
zset底层使用2个数据结构
redis6新数据类型
Bitmaps类型
基本命令
HyperLogLog类型
常用命令
Geospatial类型
基本命令
redis配置文件
redis的发布订阅
具体操作
jedis操作redis数据库
导入依赖
连接测试
关于key的基本操作
list类型简单操作
set简单操作
hash简单操作
zset的简单操作
连接池实现redis操作数据库
SpringBoot整合redis
引入依赖
配置文件内配置redis
创建redis配置类
基于string类型的操作
基于hash类型操作
基于list的操作
基于set的操作
事务和锁机制
redis与mysql中锁机制
基本指令
理解
实践过程
jedis中的经典案例
总结
redis事务的三个特性
redis持久化
redis中的两种持久化方式
RDB
备份的执行过程
理解
dump.rdb配置文件
调用命令手动生成快照
rdb持久化优势
rdb持久化劣势
AOF
AOF配置文件
AOF文件的修复
Rewrite压缩
重写原理
重写流程
AOF持久化流程
AOF优势
AOF劣势
主从复制
主从复制的优势
创建一主两从
主从复制的原理
哨兵模式(观察者模式)
哨兵模式的实践
进阶配置
主服务器选举规则
redis集群
搭建三主三从
16384 slots covered含义
根据key来计算插槽,那么一次添加多个key结果
插槽基本命令
redis.conf内的配置
集群的jedis开发
含义:NoSQL(not only sql),意为不仅仅是sql,泛指非关系型数据库,其内部的数据存储在内存中;nosql不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大增加了数据库的扩展能力。
redis官网:https://redis.io/
进入安装目录:cd /usr/local/bin
敲击命令:redis-server
注意:前台启动后此窗口便不能再进行其他操作了,关了服务就没了
理解:该启动方式我们看不到上面的界面,并且,我们关闭窗口后那么他还可以继续使用
敲击:redis-cli
测试redis的连通状态:敲击ping后回应pong那么表示已经连通
- 为具体的key赋值为value:set key value
- 取出对应key的value值:get key
- 删除指定的key数据:del key(删除成功则返回1,否则返回0;可以清空多个)
- 删除指定的key数据:unlink key(删除成功则返回1,否则返回0)
- 注意:del直接删除,而unlink的删除他仅将其从keys内删除,真正的删除会在后续异步执行
- 查看该数据库中的所有key:keys *
- 查看key对应的value具体数据类型:type key
- 查看key是否存在:exists key(存在则返回1,否则返回0)
- 为key设置过期时间为10秒钟:expire key 10(成功则返回1,否则返回0)
- 为key设置过期时间为10000ms:pexpire key 10000(成功则返回1,否则返回0)
- 查看key的剩余过期时间:ttl key(-1表示永不过期,-2表示已过期)
- 取消key的过期时长:persist key
redis的key的数据类型为string;value的数据类型支持5种:String(字符串)、List(集合)、Set(集合)、Hash(哈希)、Zset(有序集合)
- 为key赋值为value:set key value(若key存在则value覆盖)
- 为key赋值为value:setnx key value(若key存在,则赋值失败)
- 取出key的值:get key
- 为key后面追加hello:append key hello(成功返回key的长度)
- 获得key对应value的长度:strlen key
- 为key值递增1:incr key(返回递增后的值)
- 为key值递减1:decr key(返回递减后的值)
- 为key值递增n:incrby key n(返回递增后的值)
- 为key值递减n:decrby key n(返回递减后的值)
- 注意:对于递增/递减操作,那么要递增的值必须为数字类型
- 设置k1值为v1,k2值为v2:mset k1 v1 k2 v2
- 设置k1值为v1,k2值为v2:msetnx k1 v1 k2 v2(里面的key有任何一个存在则都不会设置成功)
- 获取k1和k2的值:mget k1 k2
- 删除k1和k2的值:del k1 k2
- 获取value的范围内的字符:getrange k1 2 3(获取value的第3个到第4个间的字符,包前包后)
- 在k1的第2个索引处设置hello:setrange k1 2 hello(返回value的长度)
- 设置值时顺便设置10s过期时间:setex key 10 value
- 新值换旧值:getset key value(返回是之前的值)
String的数据结构为简单动态字符串,是可以修改的字符串,采用预分配冗余空间的方式来减少内存的频繁分配
如图所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1m时,扩容都是加倍现有空间,若超过1m,扩容时只会多扩1m的空间,但字符串value最大空间为512m
- 从左边插入多值:lpush key v1 v2 v3
- 从右边插入多值:rpush key v1 v2 v3
- 从左边吐出一个值:lpop key
- 从右边吐出一个值:rpop key
- 按照索引从左到右获得元素:lrange key 0 -1(0到-1表示取所有值)
- 从k1右边取值插入k2左边:rpoplpush k1 k2
- 根据索引下标获得指定元素:lindex key 2(获得第二个元素)
- 获取列表的长度:llen key
- 在key中v1前面插入新值:linsert key before v1 newv1(若在后面就把before换为after)
- 从左边删除n个v1:lrem key n v1
- 将列表下标为index的值替换为value:lset key index value
- 从key集合中保留下标为1到结束的数据:ltrim key 1 -1
Set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动去重的,redis的set是string类型的无序集合,他的底层其实上就是一个value为null的hash表,所以添加、删除、查找的复杂度都是O(1)
- 将多个值加入到key集合:sadd key v1 v2 v3
- 取出key集合中的值:smembers key
- 判断key集合中是否包含value:sismember key value(包含返回1,否则返回0)
- 返回key集合中元素的个数:scard key
- 删除key集合中的v1和v2:srem key v1 v2
- 随机从集合中吐出一个值:spop key
- 随机从集合中显示n个值:srandmember key n
- 把集合中的value值从k1集合移动到k2集合:smove k1 k2 value
- 返回两个集合中的交集元素:sinter k1 k2
- 返回两个集合中的并集元素:sunion k1 k2
- 返回k1集合中不包含k2的元素:sdiff k1 k2
set数据结构是dict字典,字典是用哈希表实现的,redis的内部也是使用hash结构,所有的value都指向同一个内部值
Redis hash是一个键值对集合,其是一个string类型的field和value的映射表,hash特别适用于存储对象,类似java里面的Map
- 将user对象进行存储:hset user name lili age 23(hmset也可以)
- 取出user的name:hget user name(只能取出一个属性)
- 获得user的多个属性:hmget user name age
- 取出user的所有数据:hgetall user
- 判断user的name属性是否存在:hexists user name(存在返回1,不存在返回0)
- 列出该user的所有field:hkeys user
- 列出user集合中的所有属性的值:hvals user
- 为user的age属性加2操作:hincrby user age 2
- 为user对象添加属性与值:hsetnx user sex man(若属性存在,则添加失败)
- 删除user的age与name属性:hdel user age name
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable
zset与普通set集合十分相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分(score),这个评分被用来按照从最低分到最高分的方式排序集合中的成员。集合中的成员是唯一的,但评分可以重复
注意:zset内的元素会默认根据score评分从小到大排序
- 将一个或多个元素以及对应的score加入到有序集合key中:zadd key 100 java 200 c++ 300 php
- 返回有序集合key下标在开始到结束之间的元素:zrange key 0 -1 [withscores]
- 注意:后面添加withscore后那么就会把他们对应的评分也显示
- 取出key中评分在150-400之间的值:zrangebyscore key 150 400 [withscores]
- 从大到小显示key中score值为700-200之间的值:zrevrangebyscore key 700 200 [withscores]
- 将key中的java元素的评分增加50:zincrby key 50 java
- 删除集合下指定值的元素:zrem key php
- 统计key中评分为100-200之间值的个数:zcount key 100 200
- 获取集合中成员的个数:zcard key
- 返回该值在集合中的索引:zrank key java
有序链表——查询慢,省空间
跳跃表(建索引)——查询快,费空间
- 设置某个偏移量的值(0/1):set key 19 1
- 理解:设置偏移量为19的地方的值为1(偏移量就理解成索引)
- 取出偏移量为1对应的值:getbit key 1(返回1下标处的值)
- 统计key偏移量在下标为[1,2]值处为1的总和:bitcount key 1 2
- 向key中加入数据:pfadd key v1 v2 v3(若value相同则添加失败,返回0,否则返回1)
- 统计key中value的数量:pfcount key
- 将k1和k2的value加到k3里:pfmerge k3 k1 k2
redis 3.2中增加了对GEO类型的支持。GEO、Geographic,地理信息的缩写。该类型就是元素的二维坐标,在地图上就是经纬度。redis基于该类型提供了经纬度设置,查询,范围查询,距离查询,经纬度hash等常见操作
- 加上海地理信息:geoadd china:city 121.47 31.23 shanghai
- 同时加上海和深圳:geoadd china:city 121.47 31.23 shanghai 114.05 22.52 shenzhen
- 获得指定地区的坐标值:geopos china:city shanghai shenzhen
- 获取两个位置之间的直线距离以km为单位:geodist chain:city shanghai shenzhen km
- 以给定的经纬度为中心,找出某一半径内的元素:georadius china:city 110 30 1000 km
- 理解:计算出东京110度北纬30度在里面方圆1000km以内的城市
注意:
配置大小单位,开头定义了一些基本的度量单位,只支持byte,不支持bit;大小写不敏感
理解:include包含其他文件内容
bind=127.0.0.1:只能接收本机的访问请求,不写的情况下无限制接收任何ip地址的访问
protect-mode no:保护模式开启情况下,redis只允许接受本机的响应
port 6379:端口号默认6379
tcp-backlog 511:backlog为一个连接队列
注意:backlog队列总和=未完成三次握手的队列+已完成三次握手的队列
timeout 0:redis连接永不超时(秒为单位)
tcp-keepalive 300:每隔300s检测一次连接是否存活
daemonize yes:是否支持redis后台启动,yes表示是
pidfile /var/run/redis_6379.pid:将进程号设置到该文件中保存
loglevel notice:设置日志级别
logfile "":设置日志的输出文件路径,默认为空
database 16:redis默认有16个库
内存淘汰策略
redis发布订阅是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息,redis客户端可以订阅任意数量的频道
理解:发送者可以发送很多频道,若订阅者订阅发布者的所有频道,那么当发布者发布时,订阅者一定能收到消息,若订阅者只订阅发布者的a频道,而发布者向b频道发送消息,那么订阅者便不能收到消息
第二个客户端就收到了
前提:配置文件中配置允许远程连接
redis.clients
jedis
4.2.3
public static void main(String[] args) {
//创建jedis的对象(远程访问redis服务)
Jedis jedis = new Jedis("192.168.126.129",6379);
//测试是否连接上redis,若成功,返回pong
String ping = jedis.ping();
System.out.println(ping);
}
注意:连接时应先关闭防火墙
public static void main(String[] args) {
//创建jedis的对象(远程访问redis服务)
Jedis jedis = new Jedis("192.168.126.129",6379);
//jedis.auth("123456");密码
//选择1号数据库
jedis.select(1);
//添加元素
jedis.set("name", "lili");
//获得该数据库下所有的key
Set keys = jedis.keys("*");
//查看keys的个数
System.out.println(keys.size());
for (String key : keys) {
System.out.println(key);
}
//判断name是否存在
System.out.println(jedis.exists("name"));
//查看name的过期时间
System.out.println(jedis.ttl("name"));
//获得name的值
System.out.println(jedis.get("name"));
//为name设置过期时间10s
jedis.expire("name", 10);
//删除name
jedis.del("name");
//设置多个key-value
jedis.mset("age","23","sex","man");
List mget = jedis.mget("age", "sex");
System.out.println(mget);
jedis.close();
}
@Test
public void test1(){
//创建jedis的对象(远程访问redis服务)
Jedis jedis = new Jedis("192.168.126.129",6379);
//添加元素
jedis.lpush("k1", "v1","v2","v3");
//取出元素
List k1 = jedis.lrange("k1", 0, -1);
System.out.println(k1);
//其他的一样(太多了没必要)
}
@Test
public void test2(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.sadd("people", "lili","lala","lan");
Set people = jedis.smembers("people");
System.out.println(people);
}
@Test
public void test3(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.hset("user", "name","lili");
String hget = jedis.hget("user", "name");
System.out.println(hget);
//hmset,hmget可以操作多个
}
@Test
public void test4(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.zadd("china", 100d, "shanghai");
Set china = jedis.zrange("china", 0, -1);
System.out.println(china);
}
注意:以上太多方法,不再一一演示,基本大同小异,照猫画虎即可
注意:依赖还是用上面的
public static void main(String[] args) {
//定义连接池配置
JedisPoolConfig config = new JedisPoolConfig();
//设置最大连接数
config.setMaxTotal(1000);
//设置最大空闲数
config.setMaxIdle(60);
//创建连接池对象
JedisPool jedisPool = new JedisPool(config,"192.168.126.129",6379);
//从池中获取连接
Jedis resource = jedisPool.getResource();
//resource.auth("root");密码
String ping = resource.ping();
System.out.println(ping);
resource.close();
jedisPool.close();
}
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
2.6.0
#application.yml文件内
server:
port: 8080
spring:
redis:
host: 192.168.126.129
port: 6379
#redis的默认数据库
database: 0
timeout: 1800000
lettuce:
pool:
#连接池最大连接数:20
max-active: 20
#最大等待时间
max-wait: -1
#最大空闲连接数
max-idle: 5
#最小空闲连接数
min-idle: 0
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//基于字符串操作对象
ValueOperations value = redisTemplate.opsForValue();
//设置值
value.set("name", "lili");
//从redis内获取值
String name = (String) value.get("name");
System.out.println(name);
}
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test1(){
//基于hash操作对象
HashOperations hash = redisTemplate.opsForHash();
//设置值
hash.put("user", "name", "lili");
//取值
Map user = hash.entries("user");
System.out.println(user);
}
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test2(){
//基于list操作
ListOperations list = redisTemplate.opsForList();
list.leftPush("k1", "v1");
list.leftPushAll("k1", "k2","k3");
List k1 = list.range("k1", 0, -1);
System.out.println(k1);
}
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test3(){
SetOperations set = redisTemplate.opsForSet();
set.add("setKey1", "A","B","C","C");
Object members=set.members("setKey1");
System.out.println("setKeys="+members);
}
含义:redis事务是一个单独的隔离操作:事务中的所有命令都会被序列化,按照顺序的执行。事务在执行过程中,不会被其他客户端发送来的命令请求所打断
redis事务的主要作用:串联多个命令,防止别的命令插队
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,redis会将之前的命令队列中的命令依次执行
注意:
注意:使用时先引入redis客户端依赖
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.set("tony", "300");
//监视tony
jedis.watch("tony");
//开启事务
Transaction multi = jedis.multi();
try {
multi.incrBy("tony", 100);
multi.exec();
}catch (Exception ex){
//若抛异常则取消事务
multi.discard();
}finally {
jedis.close();
}
}
注意:只要在key值被监视和提交事务之间的时间段内name值发生变化,那么提交后的返回值为nil
在并发执行时,为了更好的保证事务的4个特性,通常会对事务加锁,redis为了性能采用了乐观锁的方式进行事务控制,它使用watch命令监视给定的key,当exec时,若监视的key从调用watch后发生了变化,则整个事务会失败。也可以调用watch多次监视多个key。
注意:
持久化:把内存中的数据写到磁盘中
持久化原因:redis为内存数据库,内存中的东西在断电时可能会丢失
含义:在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的快照,它恢复时是将快照直接读到内存中
redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束,再用这个临时文件替换掉之前的文件。整个过程中,主进程不进行任意的io操作,这就确保了极高的性能;若需要进行大规模数据恢复,且对于数据恢复的完整性不是非常敏感,那么RDB的方式要比AOF的方式更加高效。RDB的唯一缺点就是最后一次持久化的数据可能丢失
redis持久化后会生成一个文件,文件名字默认为dump.rdb,但redis并不是直接就把数据写到dump.rdb文件中,而是先创建一个子进程(进程名为fork),然后,当持久化时先在里面创建一个临时的文件,先将数据同步到临时区域中,当数据同步完成之后便会将里面的内容覆盖到dump.rdb文件中(目的:防止数据不完整)——(写时复制技术)
dbfilename dump.rdb:持久化文件名为dump.rdb
dir ./:在redis的启动目录(/usr/local/bin)中生成这个rdb文件
stop-writes-on-bgsave-error yes:当redis无法写入磁盘的话,那么直接关闭redis的写操作
rdbcompression yes:持久化文件时,持久化文件进行压缩操作
rdbchecksum yes:检查数据的完整性(是否损坏)
save 60 1000:每隔60s至少有1000个key发生变化,那么就生成一个dump.rdb快照
含义:通过记录写操作日志的方式记录redis数据的一种持久化机制,此机制默认是关闭的,此方式将每一个收到的命令都通过wiite函数增加到日志文件的最后(读操作不记录),redis重启会读取该文件,重新构建数据
appendonly yes:AOF文件开启(默认为no)
appendfilename "appendonly.aof":AOF文件名为appendonly.aof
appendfsync everysec:每秒记录日志一次,若宕机,本秒内的数据可能丢失
no-appendfsync-on-rewrite yes:在重写期间,对新写入的不进行持久化,暂存在内存中,重写完再持久化
auto-aof-rewrite-percentage 100:文件达到100%时重写启动
auto-aof-rewrite-min-size 64mb:AOF日志文件启动重写的文件最小值为64mb
注意:
AOF采用文件的追加方式,文件会越来越大为避免此种情况,新增了重写机制,当AOF文件的大小超过了所设定的阈值,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后在rename),redis4.0版本后的重写,实质上就是把rdb的快照以二进制的形式附在新的aof的头部,作为已有的历史数据,替换掉原来的流水账操作
主进程fork出子进程进行重写,子进程遍历redis内存中的数据到临时文件,子进程写完该临时文件后向主进程发信号,由主进程把该临时文件中的数据写入到新的aof文件中,进而用新aof文件替换就aof文件
注意:官方推荐用dump.rdb作为第一步的数据恢复,然后用appendonly.aof作为第二步的数据增量
含义:主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主(也可以读),slave以读为主(只能读)
关闭AOF:appendonly no(不关也可以,AOF文件改个名,别都一样)
复制redis.conf文件到myredis(文件夹名随意起)文件夹中起名也叫redis.conf
创建3个配置文件:redis6379.conf、redis6380.conf、redis6381.conf
在3个配置文件中按照格式写入内容
#从外面引入redis.conf配置文件
include /root/myredis/redis.conf
#将该文件的进程号设置到该路径保存
pidfile /var/run/redis_6379.pid
#设置端口号
port 6379
#设置持久化rdb文件名
dbfilename dump6379.rdb
启动三台redis服务器
通过端口号连接redis:redis-cli -p 6379
启动三台redis,查看三台主机的运行情况:info replication
由此观之三台机器全是主机
配置主从(我要6379为主节点)
除了6379端口以外的服务执行命令:slaveof 主节点ip地址 主节点端口号(slaveof 127.0.0.1 6379)
由此观之成功(主机可以读写,从机只能读)
注意:
薪火相传:一个主服务器,一个次主服务器,一个从服务器;从服务器只能从次主服务器中同步数据,次主服务器只能从主服务器中同步数据(白话:一个从服务器再挂一个从服务器)
反客为主:当一个master宕机后,后面的slave可以立即升为master,其后面的salve不用做任何修改(薪火相传的前提下,次主服务器升主)
(反客为主)主机挂掉后敲击命令:slaveof no one
含义:反客为主自动版,能够后台监控主机是否故障,若故障了则根据投票数自动将从库转为主库
自定义/myredis(特定目录下就可以)目录下新建sentinel.conf文件(名字必须为这个)
sentinel monitor mymaster 127.0.0.1 6379 1
#注意:mymaster为给监控对象起的名称,1为至少有多少个哨兵同意迁移的数量
启动哨兵:redis-sentinel sentinel.conf
注意:若哨兵选出了新主服务器,那么原主机若重启则会为新主机的从服务器
sentinel monitor mymaster 127.0.0.1 6380 1
#后台运行
daemonize yes
#运行日志所在文件
logfile "/root/myredis/sentinel_log.log"
#主节点失效30秒后被认为失效
sentinel down-after-milliseconds mymaster 30000
启动哨兵:redis-sentinel sentinel.conf
注意:无中心化集群(任何一台服务器都可以作为集群的入口)
制作6个实例,6379、6380、6381、6389、6390、6391(文件名随意:redis端口号.conf)
关闭AOF:appendonly no(不关也可以,AOF文件改个名,别都一样)
复制redis.conf文件到myredis(文件夹名随意起)文件夹中起名也叫redis.conf
在6个配置文件中按格式写如下内容
include /root/myredis/redis.conf
pidfile "/var/run/redis_6379.pid"
port 6379
dbfilename "dump6379.rdb"
#开启集群
cluster-enabled yes
#集群配置文件名字
cluster-config-file node-6379.conf
#设置节点失联时间,超过该时间集群节点会自动进行主从切换
cluster-node-timeout 15000
使用查找替换修改另外5个文件(:%s/6379/6380)
理解:将linux文件内的6379转换为6380
启动6个redis服务:(redis-server redis6379.conf)
将6个节点合成一个集群
注意:组合之前,应确保所有的redis实例启动后,nodes-xxxx.conf文件都能正常生成
合体:
进入之前redis的解压目录的src目录:/root/redis-7.0.4/src/在该目录里执行命令:
redis-cli --cluster create --cluster-replicas 1 192.168.126.129:6379 192.168.126.129:6380 192.168.126.129:6381 192.168.126.129:6389 192.168.126.129:6390 192.168.126.129:6391
注意:
由此观之:因为我的create --cluster-replicas 1配置导致自动实现3主三从的最简单方式
注意:若集群中的主节点挂掉,那么从节点便会升为主节点,若主节点再启动,那么他会变为从机
之前我已经说过,该集群为无中心化集群(任何一个节点都可以作为集群的入口,并且他们之间可以互相访问)
集群连接:redis-cli -c -p 6379
注意:-c表示集群方式连接,集群方式连接用任何一个节点都可以
在节点内查看集群中节点的信息:cluster nodes
注意:最后面有插槽信息,请仔细看(一主一从分配一段插槽范围)
在节点内查看集群基本的信息:cluster info
查看key的插槽值:cluster keyslot key
查看5474插槽内有多少key:cluster countkeysinslot 5474
在5474插槽中返回10个slot的键:cluster getkeysinslot 5474 10
前提:添加redis客户端依赖
public static void main(String[] args) {
//创建对象
//集群入口节点——当然,也可以创建一个set集合放入多个地址与端口号的hostAndPost,但redis集群是无中心化的,所以一个就够了
//用任何一个节点都能连上我们的集群
HostAndPort hostAndPort = new HostAndPort("192.168.126.129", 6379);
JedisCluster jedisCluster = new JedisCluster(hostAndPort);
//进行操作
jedisCluster.set("k1", "v1");
String k1 = jedisCluster.get("k1");
System.out.println(k1);
jedisCluster.close();
}