1.Jedis:
Redis官方推荐使用Jedis操作Redis,Jedis的方法和Redis的指令一一对应。
Jedis和lettuce都是可以操作redis的平台
使用时:
导包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
创建工具类?
public class JedisUtil {
private static final JedisPool jedisPool;
static {
//设置基本属性
GenericObjectPoolConfig<Jedis> poolConfig = new JedisPoolConfig();
//最大连接数
poolConfig.setMaxTotal(50);
//最大空闲数
poolConfig.setMaxIdle(10);
jedisPool = new JedisPool(poolConfig, "XXX", 6379, 2000, "XXX");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
测试使用:
/**
* 测试 使用封装的工具类来使用redis
*/
@Test
void test03(){
Jedis jedis = JedisUtil.getJedis();
jedis.lpush("lol","abc");
jedis.lpush("lol","def");
jedis.lpush("lol","ghj");
//弹出
System.out.println(jedis.lrange("lol", 0, -1));
}
?因为Jedis采用的直连,多个线程操作是不安全的,如果想要避免,得使用jedis pool连接池 ,并且不支持异步
2.lettuce
Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例
依赖:
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
具体没咋用过
3.RedisTemplate?
redistemplate是spring框架对jedis和lettuce的封装。让spring框架体系能够更加方便的接入redis的功能。
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
这个依赖包中就包括了jedis 和 lettuce的依赖
基础使用:
先配置自定义RedisTemplate 因为底层默认JDK二进制序列化 不能跨平台使用?
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.json());
template.setHashKeySerializer(RedisSerializer.string());
template.setHashValueSerializer(RedisSerializer.json());
return template;
}
String Hash List Set Zset 基础使用:
/**
* String -- API
*/
@Test
void test07(){
//通用指令
//exists
System.out.println(redisTemplate.hasKey("a"));
System.out.println(redisTemplate.hasKey("b"));
//ttl
System.out.println(redisTemplate.getExpire("a", TimeUnit.SECONDS));
System.out.println(redisTemplate.getExpire("b", TimeUnit.SECONDS));
//expire
System.out.println(redisTemplate.expire("a", 100,TimeUnit.SECONDS));
System.out.println(redisTemplate.getExpire("a", TimeUnit.SECONDS));
redisTemplate.persist("a");
System.out.println(redisTemplate.getExpire("a", TimeUnit.SECONDS));
}
/**
* String -- API2
*/
@Test
void test08(){
//keys
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("abc","456");
System.out.println(redisTemplate.keys("*"));
//dbsize
System.out.println(redisTemplate.countExistingKeys(redisTemplate.keys("*")));
redisTemplate.delete("aaa");
System.out.println(redisTemplate.keys("*"));
System.out.println(redisTemplate.countExistingKeys(redisTemplate.keys("*")));
}
/**
* Hash --API
*/
@Test
void test09(){
HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
//hset
hashOperations.put("number","NO1","金钱");
hashOperations.put("number","NO2","亲情");
hashOperations.put("number","NO3","爱情");
//hmset
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("number","123");
hashMap.put("age","25");
hashOperations.putAll("hmset",hashMap);
//hget
Object o = hashOperations.get("number", "NO1");
System.out.println("获取number集合中 no1-key的值:"+o);
//hkeys
Set<Object> number = hashOperations.keys("number");
System.out.println("获取number集合中的所有keys:"+number);
//hvals
List<Object> number1 = hashOperations.values("number");
System.out.println("获取number集合中的所有values:"+number1);
//hdel
hashOperations.delete("hmset","age");
//获取键值对
System.out.println("获取hmset的键值对"+hashOperations.entries("hmset"));
hashOperations.put("hmset","age",20);
System.out.println("获取hmset的age-value值"+hashOperations.get("hmset","age"));
//hincrby 使变量中的键以double值的大小进行自增长。
hashOperations.increment("hmset1","number",1);
System.out.println(hashOperations.get("hmset1","number"));
}
/**
* LIST --API
*/
@Test
void test(){
ListOperations<String, Object> opsForList= redisTemplate.opsForList();
//lpush rpush
opsForList.rightPushAll("rpush","a","b","c","d");
//lrange
System.out.println("取rpush集合中的所有值:"+opsForList.range("rpush", 0, -1));
//lpop rpop
/*Long size = opsForList.size("rpush");
for (int i = 0; i < size; i++) {
System.out.println("从rpush集合中弹出:"+opsForList.leftPop("rpush"));
}*/
Object object = null;
while (opsForList.leftPop("rpush") != null){
System.out.println("从rpush集合中弹出:"+opsForList.leftPop("rpush"));
}
}
/**
* Set --Api
*/
@Test
void test105(){
SetOperations<String, Object> opsForSet = redisTemplate.opsForSet();
//sadd
opsForSet.add("set1", "a","b","c","d","c","a");
//smembers
System.out.println(opsForSet.members("set1"));
//scard
System.out.println(opsForSet.size("set1"));
//sismember
System.out.println(opsForSet.isMember("set1","a"));
System.out.println(opsForSet.isMember("set1","x"));
//随机选取
//srandrem
System.out.println(opsForSet.randomMembers("set1", 3));
System.out.println(opsForSet.randomMembers("set1", 3));
System.out.println(opsForSet.randomMembers("set1", 3));
System.out.println(opsForSet.members("set1"));
System.out.println(opsForSet.pop("set1", 1));
System.out.println(opsForSet.pop("set1", 1));
System.out.println(opsForSet.members("set1"));
}
/**
* Zset --API
*/
@Test
void test11(){
ZSetOperations<String, Object> opsForZset = redisTemplate.opsForZSet();
//zadd
opsForZset.add("zset","数学",80);
opsForZset.add("zset","物理",70);
opsForZset.add("zset","英语",140);
System.out.println("zset中的全部key:"+opsForZset.range("zset",0,-1));
Set<ZSetOperations.TypedTuple<Object>> zset = opsForZset.rangeWithScores("zset", 0, -1);
for (ZSetOperations.TypedTuple<Object> o :zset) {
System.out.println(o.getValue());
System.out.println(o.getScore());
}
}
/**
* Zset --API 方式二:
* Arrays.asList()将数组转换为集合后,底层其实还是数组,它返回的是Arrays的一个内部类,体现了适配器模式。
* 传递的数组必须是对象数组,而不是基本类型。
*/
@Test
void test12(){
ZSetOperations<String, Object> opsForZSet = redisTemplate.opsForZSet();
Set<ZSetOperations.TypedTuple<Object>> zset2 = new HashSet<>();
DefaultTypedTuple<Object> t1 = new DefaultTypedTuple<>("历史", 80.0);
DefaultTypedTuple<Object> t2 = new DefaultTypedTuple<>("政治", 68.0);
DefaultTypedTuple<Object> t3 = new DefaultTypedTuple<>("地理", 90.0);
opsForZSet.add("zset2",new HashSet<>(Arrays.asList(t1,t2,t3)));
}
4.Redission
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括Bitset, Set, MultiMap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish/Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service。Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。(官话)
暂时我用的Redission还不多 只在使用分布式锁时用了下?
在一些场合中 比如在集群,多条指令,就需要分布式锁来进行控制Redis安全性
?
Redission依赖:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置类:
/**
* Redisson 分布式锁
*/
@Configuration
public class RedissonConfiguration {
@Bean
public RedissonClient redissonClient(){
Config config=new Config();
config.useSingleServer().setAddress("redis://xxx");
config.useSingleServer().setPassword("xxx");
return Redisson.create(config);
}
}
?实际使用:
但是这样会使效率变低(比如秒杀)
/**
* 使用分布式锁
*/
@RequestMapping("/lock")
public void incr2(){
RLock mylock = redissonClient.getLock("mylock");
mylock.lock();
//过期解锁 无需调用unlock
//mylock.lock(10, TimeUnit.SECONDS);
//尝试加锁 最多等待2秒 8s过期自动解锁
//boolean b = mylock.tryLock(2, 8, TimeUnit.SECONDS);
ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
Object num = opsForValue.get("num");
if(num == null){
opsForValue.set("num","1");
}else{
int count=Integer.parseInt(num.toString());
count++;
opsForValue.set("num", count+"");
}
mylock.unlock();
}
在使用分布式锁时需要注意:
? ? 1.设置过期时间:当前应用加锁,如果宕机,可以正常解锁 ?? ?2.解锁只能解自己的锁,不能解别人的锁:线程id ?? ?3.执行结束删除锁:判断+删除,两个指令中间可能会被打断执行,可能删除别人的锁。解决:Lua脚本:可以将多个Redis指令写成一组,一次发送,同时执行,保证原子性
|