redis安装
1.现在地址 https://redis.io/download/,可以根据自己需求下载对应的版本,我目前使用的是 redis-5.0.14.tar.gz
2. 下载后上传到服务器的指定目录 (目录可以自定义)
3.解压安装
tar -zxvf redis-5.0.14.tar.gz
注意:安装前 需要安装gcc等环境
yum install -y gcc
进入 redis-5.0.14 目录下 执行 make install 进行安装
也可以指定安装目录 通过 make PREFIX = /usr/local/redis install 指定安装目录
4.安装成功后 ,后又提示输出,或者在你的目录下生成很多新的文件
5.修改配置文件进行启动
# 修改配置
daemonize yes #设置为后台启动
protected‐mode no #关闭保护模式,开启的话,只有本机才可以访问redis
# 需要注释掉bind
#bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户 端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)
# 启动服务
src/redis‐server redis.conf
# 验证启动是否成功
ps ‐ef | grep redis
# 进入redis客户端
src/redis‐cli
# 退出客户端
quit
# 退出redis服务:
(1)pkill redis‐server
(2)kill 进程号
(3)src/redis‐cli shutdown
redis在项目中的使用(Java客户端api使用)
由于目前我们项目使用的是springboot构建,所以api这里都是采用redisTemplate去使用的
依赖引入
<!--缓存依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
序列化设置:
## 客户端的key ,value 序列化方式
@Configuration
public class RedisTemplateConfig {
@Bean("redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
//序列化所有类包括jdk提供的
ObjectMapper objectMapper = new ObjectMapper();
//设置序列化的域(属性,方法etc)以及修饰范围,Any包括private,public 默认是public的
//ALL所有方位,ANY所有修饰符
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//enableDefaultTyping 原来的方法存在漏洞,2.0后改用如下配置
//指定输入的类型
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL);
//如果java.time包下Json报错,添加如下两行代码
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
serializer.setObjectMapper(objectMapper);
return serializer;
}
}
String类型操作常用的api
@Slf4j(topic = "RdisTemplateTest")
@SpringBootTest(classes = {SpringbootCacheDemoApplication.class})
public class RdisTemplateTest{
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
public void redisCommandStringTest(){
// 设置 无过期时间的
redisTemplate.opsForValue().set("test","abc");
// 设置带过期时间的
redisTemplate.opsForValue().set("name","yulang",10, TimeUnit.MINUTES);
// 用value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量
//offset 开始。不存在的 key 当作空白字符串处理。
redisTemplate.opsForValue().set("name","abc",1);
// 获取key的值
Object name = redisTemplate.opsForValue().get("name");
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
valueOperations.set("name1","yulang1", Duration.ofMinutes(1));
// bitmap 操作 可以用来统计某个用户是 那天是否签到了
valueOperations.setBit("java",1,true);
valueOperations.setBit("java",2,true);
valueOperations.setBit("java",3,true);
Boolean operationsBit = valueOperations.getBit("java", 6);
log.info("{} - {} - {} -{}",valueOperations.getBit("java", 1),valueOperations.getBit("java", 2),valueOperations.getBit("java", 3),operationsBit);
valueOperations.setBit("zhangsan",20230301,true);
valueOperations.setBit("zhangsan",20230302,true);
valueOperations.setBit("zhangsan",20230303,true);
Boolean aBoolean = valueOperations.getBit("zhangsan", 20230304);
// true 表示已经存在的记录
log.info("aBoolean: {} ,20230302 -{} " ,aBoolean,valueOperations.getBit("zhangsan", 20230303) );
// 使用的是 setNX 命令 存在的key 不会再次插入
Boolean setIfAbsent = valueOperations.setIfAbsent("test", "new name");
Boolean setIfAbsent2 = valueOperations.setIfAbsent("test1", "new name");
log.info("setIfAbsent - setIfAbsent2 : {} - {}",setIfAbsent,setIfAbsent2);
// set 存在key就重新更新 ,不存在的key不插入
Boolean test1 = valueOperations.setIfPresent("test1", "333");
Boolean test2 = valueOperations.setIfPresent("test2", "444");
log.info("setIfAbsent - setIfAbsent2 : {} - {}",test1,test2);
// 自增id操作
Long increment = valueOperations.increment("LONG-ID");
log.info("increment : {} - {}",increment,valueOperations.increment("LONG-ID"));
// set批量设置(批量操纵无法设置每个key的过期时间), 批量操作也可使用 Pipelined 操作 ,命令跟灵活可以设置key过期时间
Map<String,Object> map =new HashMap<>();
map.put("demo","1");
map.put("demo2","1");
map.put("demo3","1");
valueOperations.multiSet(map);
}
/**
* RedisPipelined 插入 不保证原子性
* @author 余浪
*/
private void testRedisPipelined() {
RedisSerializer<Object> valueSerializer = (RedisSerializer<Object>) redisTemplate.getValueSerializer();
List<Object> objectList = redisTemplate.executePipelined(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
RedisStringCommands redisStringCommands = connection.stringCommands();
RedisSerializer<Object> valueSerializer = (RedisSerializer<Object>) redisTemplate.getValueSerializer();
for (int i=0 ;i < 100; i++){
byte[] serializeValue = valueSerializer.serialize("Pipelined——"+ i);
redisStringCommands.set((i+ "_key").getBytes(), serializeValue, Expiration.seconds(1000), RedisStringCommands.SetOption.UPSERT);
}
return null;
}
});
log.info("objectList: 返回操作的数据");
}
}
hash类型操作
@Test
public void redisCommandHashTest(){
// hash 类型相关操作
HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
String product_id = "123";
// 存储一个商品的分类和品牌 无法单独设置设置过期时间
hashOperations.put(product_id,"brand","华为");
hashOperations.put(product_id,"category","5G手机");
hashOperations.put(product_id,"specification","大内存");
// 获取品牌的信息
Object brand = hashOperations.get(product_id, "brand");
// 获取这个产品下的所有的字段信息
Map<Object, Object> objectMap = hashOperations.entries(product_id);
log.info("brand : {}--- objectMap:{}",brand , objectMap );
// 只能通过顶级key 设置一个统一的过期时间
redisTemplate.expire(product_id,1000,TimeUnit.MINUTES);
//批量操作 也可以通过Pipelined 进行操作
Map<String,Object> map =new HashMap<>();
map.put("price",120);
map.put("name","ceshh");
hashOperations.putAll(product_id,map);
//判断 价格的属性 是否在 product_id 这个KEY中
hashOperations.hasKey(product_id,"price");
hashOperations.delete(product_id,"price");
// 异步删除key
redisTemplate.unlink(Arrays.asList(product_id));
}
3.list常用操作
@Test
public void redisCommandListTest(){
// list 集合操作
ListOperations<String, Object> listOperations = redisTemplate.opsForList();
String key = "productId";
// 从右开始插入
listOperations.rightPush(key,1);
listOperations.rightPush(key,2);
// lInsert 在key 该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插 入到该元素的前面还是后面
// Long rightPush(K key, V pivot, V value); api 默认是 AFTER (后面插入)
// 在 1 后面插入 12 ,如果没有1这个value 则不插入
listOperations.rightPush(key,1,12);
// 批量插入
listOperations.rightPushAll(key,2,3,4);
// 获取指定索引的元素
listOperations.index(key,1);
// 从做开始插入
listOperations.leftPush(key,99);
// key 存在就插入 不存在不插入
listOperations.leftPushIfPresent(key,88);
// 从右边移出一个元素 原子操作 ,可以使用 这个做一些简单的队列
Object rightPop = listOperations.rightPop(key);
}
4.set操作
@Test
public void redisCommandSetTest(){
//set 相关操作 不重复无序的元素
SetOperations<String, Object> opsForSet = redisTemplate.opsForSet();
String setKey ="SET-KEY";
// 添加元素
opsForSet.add(setKey,1,2,3,4);
// 移出元素为 1 的元素
opsForSet.remove(setKey,1);
// 输出 2,3,4
Set<Object> members = opsForSet.members(setKey);
log.info("members1 : {}" ,members);
// 判断元素是否存在
Boolean member = opsForSet.isMember(setKey, 3);
log.info("member : {}" ,member);
String setKey2 ="SET-KEY2";
Long add = opsForSet.add(setKey2, 2, 3, 4, 5, 6);
log.info("members2 : {}" ,opsForSet.members(setKey2));
List<String> list = Arrays.asList(setKey2,setKey);
// 集合差集 第一个集合是大集合 - 第二个小集合 = 差集
Set<Object> difference = opsForSet.difference(list);
log.info("difference: {}",difference);
// 集合的交集
Set<Object> intersect = opsForSet.intersect(list);
log.info("intersect: {}",intersect);
// 属于a 或者属于 b 集合的数据 并集
Set<Object> objects = opsForSet.union(list);
log.info("objects: {}",objects);
// 获取集合中元素个数
Long size = opsForSet.size(setKey);
log.info("size: {}",size);
// 从集合中移出一个元素 可以使用这个 做抽奖的功能
Object pop = opsForSet.pop(setKey);
log.info("pop: {} - {}",pop,opsForSet.members(setKey));
// 批量移出 多个元素
List<Object> objectList = opsForSet.pop(setKey, 2);
}
5.zset使用
@Test
public void redisCommandZsetTest(){
// zset 有序的集合 不重复
ZSetOperations<String, Object> opsForZSet = redisTemplate.opsForZSet();
String zkey = "sort-key";
opsForZSet.add(zkey,"java",99);
opsForZSet.add(zkey,"php",80);
opsForZSet.add(zkey,"golang",85);
// 按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
// ZREVRANGE:按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素
Set<Object> objects = opsForZSet.range(zkey, 0, -1);
log.info("objects: {} - {}",objects,objects.size());
// 查询排名前三的书
Set<Object> reverseRange = opsForZSet.reverseRange(zkey, 0, 2);
// 给某个元素加上多少分
Double java = opsForZSet.incrementScore(zkey, "java", 100);
log.info("java: {} - {}",java,objects.size());
// 获取某个元素的分数
Double score = opsForZSet.score(zkey, "java");
log.info("score: {} - {}",score,objects.size());
// 删除某个元素
opsForZSet.remove(zkey,"php");
//[php, golang, java] - 3 unbounded
Set<ZSetOperations.TypedTuple<Object>> tupleSet = opsForZSet.rangeByScoreWithScores(zkey, 0, 200);
log.info("objectSet: {} - {}",tupleSet,tupleSet.size());
Set<Object> rangeByScore = opsForZSet.rangeByScore(zkey, 80, 100);
log.info("rangeByScore: {} - {}",rangeByScore,rangeByScore.size());
// 获取元素的排名 从小到大
Long rank = opsForZSet.rank(zkey, "java");
log.info("rank: {} - {}",rank);
String zkey2 = "sort-key2";
opsForZSet.add(zkey2,"张三",99);
opsForZSet.add(zkey2,"李四",80);
//将 key1 和 key2 的合并的结果放到 新的key中
opsForZSet.unionAndStore(zkey,zkey2,"new-key");
}
6.geo 相关操作
@Test
public void redisCommandGEOTest(){
GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();
String key = "cn";
// geoadd:添加地理位置的坐标。
Map<Object,Point> stringPointMap = new HashMap<>();
stringPointMap.put("wuhan",new Point(114.02919,30.58203));
stringPointMap.put("beijing",new Point(116.23128,40.22077));
stringPointMap.put("shanghai",new Point(121.48941,31.40527));
stringPointMap.put("changsha",new Point(112.98626,28.25591));
geoOperations.add(key,stringPointMap);
// geopos:获取地理位置的坐标。
List<Point> wuchang = geoOperations.position(key, "wuhan");
log.info("wuchang : {}" ,wuchang);
// geodist:计算两个位置之间的距离。
Distance distance = geoOperations.distance(key, "wuhan", "changsha");
log.info("distance: {}" ,distance.getValue() + " - " + distance.getUnit());
Distance distance1 = geoOperations.distance(key, "wuhan", "changsha", RedisGeoCommands.DistanceUnit.KILOMETERS);
log.info("distance1: {}" ,distance1.getValue() + " - " + distance1.getUnit());
//georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
// 定义返回结果参数,如果不指定默认只返回content即保存的member信息
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
.newGeoRadiusArgs().includeDistance().includeCoordinates()
.sortAscending()
.limit(100);
GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults = geoOperations.radius(key, "wuhan",
new Distance(3000, RedisGeoCommands.DistanceUnit.KILOMETERS),args);
log.info("geoResults : {}", geoResults.getContent() + "-- " + geoResults.getAverageDistance().getValue());
//georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults1 = geoOperations.radius(key, new Circle(new Point(114.02919,30.58203),
new Distance(1000,Metrics.KILOMETERS)),args);
log.info("geoResults1 : {}", geoResults1.getContent() + "-- " + geoResults1.getAverageDistance().getValue());
//geohash:返回一个或多个位置对象的 geohash 值
List<String> jiangxia = geoOperations.hash(key, "shanghai");
}
7.lua的简单操作
@Test
public void redisLuaScript(){
// 确保操作的原子性
String script = "local isExists = redis.call('exists',KEYS[1]) \n"+
"if isExists == 1 then \n" +
" redis.call('DEL',KEYS[1]) \n" +
" redis.call('INCR', KEYS[2]) \n" +
" redis.call('EXPIRE', KEYS[2], ARGV[1]) \n" +
" return 1 \n" +
" else \n"+
" redis.call('INCR', KEYS[2]) \n" +
" redis.call('EXPIRE', KEYS[2], ARGV[1]) \n" +
" return 0 end";
// 对应的key参数 按照 key[1],key[2] 顺序添加
List<String> listKeys = Arrays.asList("user","user:lock");
// 加载lua脚本
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(script,Long.class);
// 执行lua脚本 参数1: script脚本, key的集合, 参数集合
Long resultLong = redisTemplate.execute(redisScript, listKeys, 1000);
}