前戏 | docker搭建redisHA集群
搭建 redis 集群
#docker-compose.yml 配置
version: '3.1'
services:
master:
image: redis
container_name: redis-master
ports:
- 6379:6379
slave1:
image: redis
container_name: redis-slave-1
ports:
- 6380:6379
command: redis-server --slaveof redis-master 6379
slave2:
image: redis
container_name: redis-slave-2
ports:
- 6381:6379
command: redis-server --slaveof redis-master 6379
搭建 Sentinel 集群
#docker-compose.yml 配置
version: '3.1'
services:
sentinel1:
image: redis
container_name: redis-sentinel-1
ports:
- 26379:26379
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel1.conf:/usr/local/etc/redis/sentinel.conf
sentinel2:
image: redis
container_name: redis-sentinel-2
ports:
- 26380:26379
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel2.conf:/usr/local/etc/redis/sentinel.conf
sentinel3:
image: redis
container_name: redis-sentinel-3
ports:
- 26381:26379
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf
修改 Sentinel 配置文件
在sentinel集群的docker-compose.yml同级目录下,需要三份 sentinel.conf 配置文件,分别为 sentinel1.conf,sentinel2.conf,sentinel3.conf,配置文件内容相同
port 26379
dir /tmp
# 自定义集群名,其中 127.0.0.1 为 redis-master 的 ip,6379 为 redis-master 的端口,2 为最小投票数(因为有 3 台 Sentinel 所以可以设置成 2)
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
START | USE
备注
jedis版本:spring-boot-starter-data-redis 2.1.6.RELEASE
完整代码: https://github.com/yonyong/MySpring/tree/master/myredis
精华代码
package top.yonyong.myredis.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import top.yonyong.myredis.common.constant.RedisConstant;
import top.yonyong.myredis.common.redis.zset.PersonVoSet;
import top.yonyong.myredis.entity.Person;
import top.yonyong.myredis.service.RedisService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @Author yonyong
* @Date 2020/6/29 10:21
* @Version 1.0.0
**/
@Service
@Slf4j
public class RedisServiceImpl implements RedisService {
@Resource
RedisTemplate redisTemplate;
private Person yonyong = Person.builder()
.uid(542121321)
.age(23)
.name("yonyong")
.sex('男')
.salary(BigDecimal.valueOf(10000))
.build();
private Person smallfei = yonyong,
smallxu = yonyong,
bluesky = yonyong;
@SuppressWarnings("unchecked")
public Object fun() {
String value = "hello";
Set set = new HashSet();
set.add(111);
set.add(222);
set.add("shit");
redisTemplate.opsForValue().set(RedisConstant.STRING_KEY, "value", 1, TimeUnit.HOURS);
final ValueOperations valueOperations = redisTemplate.opsForValue();
final SetOperations setOperations = redisTemplate.opsForSet();
return null;
}
@SuppressWarnings("unchecked")
public void funString() {
redisTemplate.setValueSerializer(new StringRedisSerializer());
final ValueOperations valueOperations = redisTemplate.opsForValue();
String strA = "holly shit";
valueOperations.set(RedisConstant.STRING_STRA, strA);
// valueOperations.set(RedisConstant.STRING_STRB,person);
valueOperations.set(RedisConstant.STRING_STRC, "10");
valueOperations.increment(RedisConstant.STRING_STRC, 10);
}
/**
* 列表的头部(左边)或者尾部(右边)
*/
@SuppressWarnings("unchecked")
public void funList() {
final ListOperations listOperations = redisTemplate.opsForList();
/**默认push的方式是left 返回总个数
* 放1 : 1
* 放2: 2 1
* 放3 : 3 2 1
* 放4 : 4 3 2 1
*/
listOperations.leftPushAll(RedisConstant.LIST_LEFT, yonyong, smallfei, smallxu, bluesky);
/**right的push 返回总个数
* 放1 : 1
* 放2: 1 2
* 放3 : 1 2 3
* 放4 : 1 2 3 4
*/
final Long pushCount = listOperations.rightPushAll(RedisConstant.LIST_RIGHT, yonyong, smallfei, smallxu, bluesky);
//跟leftPush是同样的操作,唯一的不同是,当且仅当key存在时,才会更新key的值。如果key不存在则不会对数据进行任何操作。 返回总个数
final Long listLeft1 = listOperations.leftPushIfPresent(RedisConstant.LIST_LEFT, yonyong);
//删除集合左边第一个元素 返回被删除的元素
final Person listLeft = (Person) listOperations.leftPop(RedisConstant.LIST_LEFT);
//在2分钟里移除集合中左边的第一个元素,返回被删除的元素. 如果超过等待的时间仍没有元素则退出。
final Object list = listOperations.leftPop(RedisConstant.LIST_LEFT, 2, TimeUnit.MINUTES);
//从左往右删 2个 value为 person的 返回删除的个数
final Long deletCount1 = listOperations.remove(RedisConstant.LIST_LEFT, 2, yonyong);
//从右往左删 2个 value为 person的 返回删除的个数
final Long deletCount2 = listOperations.remove(RedisConstant.LIST_LEFT, -1, yonyong);
//删除所有 value为 person的 返回删除的个数
final Long deletCount3 = listOperations.remove(RedisConstant.LIST_LEFT, 0, yonyong);
//只保留从左至右第2个元素到第四个元素,其他元素全部删除 无返回值(void)
listOperations.trim(RedisConstant.LIST_RIGHT, 1, 3);
//获取listLeft集合中从左至右二个元素
final Person getPerson = (Person) listOperations.index(RedisConstant.LIST_LEFT, 1);
final List<Person> listLeft2 = listOperations.range(RedisConstant.LIST_LEFT, 0, 100);
//返回listRight集合的元素个数
final Long totalCount = listOperations.size(RedisConstant.LIST_RIGHT);
//设置第1个为smallxu 无返回值
listOperations.set(RedisConstant.LIST_RIGHT, 1, smallxu);
listOperations.getOperations().delete(RedisConstant.LIST_LEFT);
listOperations.getOperations().delete(RedisConstant.LIST_RIGHT);
}
@SuppressWarnings("unchecked")
public void funHash() {
final HashOperations hashOperations = redisTemplate.opsForHash();
Map<String, Object> map = new HashMap<>();
map.put("smallfei", smallfei);
map.put("smallxu", smallxu);
map.put("bluesky", bluesky);
//put 没有返回值
hashOperations.put(RedisConstant.HASH_MAP, "person", yonyong);
hashOperations.putAll(RedisConstant.HASH_MAP1, map);
//获取所有redis中map1中的键值对中的所有key
final Set map1 = hashOperations.keys(RedisConstant.HASH_MAP1);
//获取所有redis中map1中的键值对中的所有value
final List map12 = hashOperations.values(RedisConstant.HASH_MAP1);
//获取所有redis中map1中的键值对中的所有key和value
final Map map11 = hashOperations.entries(RedisConstant.HASH_MAP1);
final Object o = hashOperations.get(RedisConstant.HASH_MAP1, "smallfei");
}
@SuppressWarnings("unchecked")
public void funSet() {
final SetOperations setOperations = redisTemplate.opsForSet();
Set set = new HashSet();
//添加四个person示例到key为set的redis里 发明会添加的个数
final Long addRows = setOperations.add(RedisConstant.SET_SET, yonyong, smallfei, smallxu);
final Long addRows1 = setOperations.add(RedisConstant.SET_SET1, smallfei, smallxu, bluesky);
//获得set 和 set1 不同value,返回不同value的集合
final Set<Person> diffSet = setOperations.difference(RedisConstant.SET_SET, RedisConstant.SET_SET1);
//获得set 和 set1 不同value,并将不同value的集合存入到key为diffSet的redis里,返回不同value的数量
final Long aLong = setOperations.differenceAndStore(RedisConstant.SET_SET, RedisConstant.SET_SET1, RedisConstant.SET_SET_DIFF);
//获得set 和 set1 相同的alue,返回相同的value的集合
final Set intersect = setOperations.intersect(RedisConstant.SET_SET, RedisConstant.SET_SET1);
//获得set 和 set1 相同的value,并将相同的value的集合存入到key为sameSet的redis里,返回相同的value的数量
final Long aLong1 = setOperations.intersectAndStore(RedisConstant.SET_SET, RedisConstant.SET_SET1, RedisConstant.SET_SET_SAME);
//获得sameSet的value集合
final Set sameSet = setOperations.members(RedisConstant.SET_SET_SAME);
}
@SuppressWarnings("unchecked")
public void funZset() {
final ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//按薪水排序 方式一 一个一个塞
final Boolean addStatus = zSetOperations.add(RedisConstant.ZSET_SET, yonyong, yonyong.getSalary().doubleValue());
final Boolean add1 = zSetOperations.add(RedisConstant.ZSET_SET, smallfei, smallfei.getSalary().doubleValue());
final Boolean add2 = zSetOperations.add(RedisConstant.ZSET_SET, smallxu, smallxu.getSalary().doubleValue());
final Boolean add3 = zSetOperations.add(RedisConstant.ZSET_SET, bluesky, bluesky.getSalary().doubleValue());
//按薪水排序 方式二 塞集合 这里需要一个PersonVoSet实现ZSetOperations.TypedTuple接口,重写compare方法,向下造型,不需要重写hashcode和equals
Set<ZSetOperations.TypedTuple> set2 = new HashSet();
ZSetOperations.TypedTuple item = new PersonVoSet(yonyong,yonyong.getSalary().doubleValue());
ZSetOperations.TypedTuple item1 = new PersonVoSet(smallfei,smallfei.getSalary().doubleValue());
ZSetOperations.TypedTuple item2 = new PersonVoSet(smallxu,smallxu.getSalary().doubleValue());
ZSetOperations.TypedTuple item3 = new PersonVoSet(bluesky,bluesky.getSalary().doubleValue());
set2.add(item);
set2.add(item1);
set2.add(item2);
set2.add(item3);
zSetOperations.add(RedisConstant.ZSET_SET1,set2);
//两种添加之后的结果都是 smallfei(-1500) bluesky(5000) smallxu(7000) yonyong(10000) 从小到大依次排序
//给yonyong 涨薪 10000元 返回涨薪后的工资
final Double score = zSetOperations.incrementScore(RedisConstant.ZSET_SET1, yonyong, 10000);
//返回yonyong的薪资排名,因为是最高,从零开始,排第三, 返回的是排名3
final Long rank = zSetOperations.rank(RedisConstant.ZSET_SET, yonyong);
//返回ZSET_SET 从0-100的所有集合 不带有score
final Set range = zSetOperations.range(RedisConstant.ZSET_SET, 0, 100);
//返回薪水5000-1000的所有集合,min和max两边都是闭区间 不带有score
final Set set = zSetOperations.rangeByScore(RedisConstant.ZSET_SET, 5000, 10000);
//返回薪水5000-1000的集合 tempSet,从tempSet中的第0个开始,取2两数据,返回这两条数据的集合 不带有score
final Set set1 = zSetOperations.rangeByScore(RedisConstant.ZSET_SET, 5000, 10000, 0, 2);
//返回ZSET_SET 从0-100的所有集合 带有score
final Set set3 = zSetOperations.rangeWithScores(RedisConstant.ZSET_SET, 0, 100);
//返回薪水5000-1000的所有集合,min和max两边都是闭区间 带有score
final Set set4 = zSetOperations.rangeByScoreWithScores(RedisConstant.ZSET_SET, 5000, 10000);
//返回薪水5000-1000的集合 tempSet,从tempSet中的第0个开始,取2两数据,返回这两条数据的集合 带有score
final Set set5 = zSetOperations.rangeByScoreWithScores(RedisConstant.ZSET_SET, 5000, 10000, 0, 2);
//返回yonyong排序的相反,yonyong的薪水为最高10000,rank为3 现在为对应的相反最低 0
final Long aLong = zSetOperations.reverseRank(RedisConstant.ZSET_SET, yonyong);
//将ZSET_SET中的第0-100个元素倒置,返回倒置后的ZSET_SET 不带score
final Set set6 = zSetOperations.reverseRange(RedisConstant.ZSET_SET, 0, 100);
final Set set7 = zSetOperations.reverseRangeWithScores(RedisConstant.ZSET_SET, 0, 100);
//返回薪资在5000-10000的个数 两个都是闭区间
final Long count = zSetOperations.count(RedisConstant.ZSET_SET, 5000, 10000);
}
/**
* 是用来做基数统计的算法 统计一批数据中的不重复元素的个数
*/
public void funHyperLogLog() {
final HyperLogLogOperations hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
System.out.println(hyperLogLogOperations.add(RedisConstant.HyperLogLog_A, yonyong, smallfei, smallxu, smallfei, yonyong, bluesky));
//print 4
System.out.println(hyperLogLogOperations.size(RedisConstant.HyperLogLog_A));
}
/**
* 空间操作
*/
public void funGeo() {
final GeoOperations geoOperations = redisTemplate.opsForGeo();
}
/**
* 集群操作
*/
public void funCluster() {
final ClusterOperations clusterOperations = redisTemplate.opsForCluster();
}
/**
* 测试无限输入参数
*
* @param persons
*/
public void test(Person... persons) {
for (int i = 0; i < persons.length; i++) {
Person person = persons[i];
System.out.println(person.getName());
}
}
/**
* 当bean创建完成的时候,会后置执行@PostConstruct修饰的方法
*/
@PostConstruct
public void initBean() {
smallfei = smallfei.toBuilder()
.name("small fei")
.age(25)
.salary(BigDecimal.valueOf(-1500))
.build();
smallxu = smallxu.toBuilder()
.name("small xu")
.age(24)
.salary(BigDecimal.valueOf(7000))
.build();
bluesky = bluesky.toBuilder()
.name("blue sky")
.age(25)
.salary(BigDecimal.valueOf(5000))
.build();
}
/**
* bean销毁前
*/
@PreDestroy
@SuppressWarnings("unchecked")
public void destoryBean() throws IllegalAccessException {
final Class<RedisConstant> redisConstantClass = RedisConstant.class;
final Field[] declaredFields = redisConstantClass.getDeclaredFields();
for (Field field : declaredFields) {
final String key = (String) field.get(RedisConstant.class);
final Boolean delete = redisTemplate.delete(key);
if (delete) {
System.out.println("delete redis key: " + key + " status: success!");
} else {
//not exist or ex
System.err.println("delete redis key: " + key + " status: fail!");
}
}
}
}