Java操作Redis
1. 使用Jedis
Jedis Client是Redis官网推荐的一个面向java客户端库, 里面实现了对各类API进行封装调用。 添加依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.8</version>
</dependency>
代码编写:
public static void main(String[] args) {
//连接本地的 Redis 服务,自己的ip和端口和密码
Jedis jedis = new Jedis("127.0.0.1",6379);
// 如果 Redis 服务设置了密码,需要下面这行,没有就不需要
// jedis.auth("111111");
//key
Set<String> keys = jedis.keys("*");
for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
System.out.println(key);
}
System.out.println("jedis.exists====>"+jedis.exists("k2"));
System.out.println(jedis.ttl("k1"));
//String
//jedis.append("k1","myreids");
System.out.println(jedis.get("k1"));
jedis.set("k4","k4_redis");
System.out.println("----------------------------------------");
jedis.mset("str1","v1","str2","v2","str3","v3");
System.out.println(jedis.mget("str1","str2","str3"));
//list
System.out.println("----------------------------------------");
//jedis.lpush("mylist","v1","v2","v3","v4","v5");
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
System.out.println(element);
}
//set
jedis.sadd("orders","jd001");
jedis.sadd("orders","jd002");
jedis.sadd("orders","jd003");
Set<String> set1 = jedis.smembers("orders");
for (Iterator iterator = set1.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
jedis.srem("orders","jd002");
System.out.println(jedis.smembers("orders").size());
//hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","138xxxxxxxx");
map.put("address","rocket");
map.put("email","zzyybs@126.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
System.out.println(element);
}
//zset
jedis.zadd("zset01",60d,"v1");
jedis.zadd("zset01",70d,"v2");
jedis.zadd("zset01",80d,"v3");
jedis.zadd("zset01",90d,"v4");
List<String> zset01 = jedis.zrange("zset01", 0, -1);
zset01.forEach(System.out::println);
}
运行结果:
2. 使用Lettuce
Lettuce是一个Redis的Java驱动包,Lettuce翻译为生菜。底层基于Netty的异步非阻塞IO模型,在高并发场景下性能表现出色。
添加pom依赖:
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.4.2.RELEASE</version>
</dependency>
代码编写:
public static void main(String[] args) {
//使用构建器 RedisURI.builder
RedisURI uri = RedisURI.builder()
.redis("127.0.0.1")
.withPort(6379)
// .withAuthentication("default","111111")
.build();
//创建连接客户端
RedisClient client = RedisClient.create(uri);
StatefulRedisConnection conn = client.connect();
//操作命令api
RedisCommands<String,String> commands = conn.sync();
//keys
List<String> list = commands.keys("*");
for(String s : list) {
logger.info("key:{}",s);
}
//String
commands.set("k1","1111");
String s1 = commands.get("k1");
System.out.println("String s ==="+s1);
//list
commands.lpush("myList2", "v1","v2","v3");
List<String> list2 = commands.lrange("myList2", 0, -1);
for(String s : list2) {
System.out.println("list ssss==="+s);
}
//set
commands.sadd("mySet2", "v1","v2","v3");
Set<String> set = commands.smembers("mySet2");
for(String s : set) {
System.out.println("set ssss==="+s);
}
//hash
Map<String,String> map = new HashMap<>();
map.put("k1","138xxxxxxxx");
map.put("k2","rocket");
map.put("k3","zzyybs@126.com");//课后有问题请给我发邮件
commands.hmset("myHash2", map);
Map<String,String> retMap = commands.hgetall("myHash2");
for(String k : retMap.keySet()) {
System.out.println("hash k="+k+" , v=="+retMap.get(k));
}
//zset
commands.zadd("myZset2", 100.0,"s1",110.0,"s2",90.0,"s3");
List<String> list3 = commands.zrange("myZset2",0,10);
for(String s : list3) {
System.out.println("zset ssss==="+s);
}
//sort
SortArgs sortArgs = new SortArgs();
sortArgs.alpha();
sortArgs.desc();
List<String> list4 = commands.sort("myList2",sortArgs);
for(String s : list4) {
System.out.println("sort ssss==="+s);
}
//关闭
conn.close();
client.shutdown();
}
运行结果:
Jedis和Lettuce的区别
Jedis和Lettuce都是Redis的客户端,它们都可以连接Redis服务器,但是在SpringBoot2.0之后默认都是使用的Lettuce这个客户端连接Redis服务器。因为当使用Jedis客户端连接Redis服务器的时候,每个线程都要拿自己创建的Jedis实例去连接Redis客户端,当有很多个线程的时候,不仅开销大需要反复的创建关闭一个Jedis连接,而且也是线程不安全的,一个线程通过Jedis实例更改Redis服务器中的数据之后会影响另一个线程;
但是如果使用Lettuce这个客户端连接Redis服务器的时候,就不会出现上面的情况,Lettuce底层使用的是Netty,当有多个线程都需要连接Redis服务器的时候,可以保证只创建一个Lettuce连接,使所有的线程共享这一个Lettuce连接,这样可以减少创建关闭一个Lettuce连接时候的开销;而且这种方式也是线程安全的,不会出现一个线程通过Lettuce更改Redis服务器中的数据之后而影响另一个线程的情况;
3. 使用RedisTemplate
3.1 连接单机
添加pom依赖:
<!--SpringBoot与Redis整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置application.yml
spring:
redis:
database: 0
host: 127.0.0.1
port: 6379
lettuce:
pool:
enabled: true
max-active: 8
max-wait: -1ms
min-idle: 0
max-idle: 8
编写业务:
@Service
public interface OrderService {
void addOrder();
String getOrderById(Integer id);
}
@Service
public class OrderServiceImpl implements OrderService {
public static final String ORDER_KEY = "order:";
Logger logger = LoggerFactory.getLogger(OrderService.class);
@Resource
private RedisTemplate redisTemplate;
@Override
public void addOrder() {
int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
String orderNo = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(ORDER_KEY + keyId, "京东订单" + orderNo);
// set集合
redisTemplate.opsForSet().add("set:"+ORDER_KEY + keyId, "京东订单1" + orderNo, "京东订单2" + orderNo);
// list
redisTemplate.opsForList().set("list:"+ORDER_KEY + keyId, 0, "京东订单0" + orderNo);
redisTemplate.opsForList().set("list:"+ORDER_KEY + keyId, 1, "京东订单1" + orderNo);
redisTemplate.opsForList().set("list:"+ORDER_KEY + keyId, 2, "京东订单2" + orderNo);
// map
redisTemplate.opsForHash().put("map:"+ORDER_KEY + keyId, "userId:1", "京东订单0" + orderNo);
redisTemplate.opsForHash().put("map:"+ORDER_KEY + keyId, "userId:1", "京东订单1" + orderNo);
// zset
redisTemplate.opsForZSet().add("zset:"+ORDER_KEY + keyId, "京东订单0" + orderNo, 20);
redisTemplate.opsForZSet().add("zset:"+ORDER_KEY + keyId, "京东订单1" + orderNo, 10);
logger.info("=====>编号" + keyId + "的订单流水生成:{}", orderNo);
}
@Override
public String getOrderById(Integer id) {
return (String) redisTemplate.opsForValue().get(ORDER_KEY + id);
}
}
public class OrderController {
@Resource
private OrderService orderService;
@RequestMapping(value = "/order/add", method = RequestMethod.POST)
public void addOrder() {
orderService.addOrder();
}
@RequestMapping(value = "/order/{id}", method = RequestMethod.GET)
public String findUserById(@PathVariable Integer id) {
return orderService.getOrderById(id);
}
}
运行发现: 原因是默认情况下,RedisTemplate使用JdkSerializationRedisSerializer进行序列化,指定序列化方式后,问题修复,添加RedisConfig.java:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
//设置key序列化方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
运行结果显示:
D:\redis-windows-7.2.4>chcp 65001
D:\redis-windows-7.2.4>redis-cli.exe --raw
127.0.0.1:6379> get order:914
"京东订单6fb45361-d5ca-494c-81bc-5fe3c5a270f6"
3.2 连接集群
调整application.yml:
spring:
redis:
cluster:
nodes: 192.168.111.175:6381,192.168.111.175:6382,192.168.111.172:6383,192.168.111.172:6384,192.168.111.174:6385,192.168.111.174:6386
# 获取失败 最大重定向次数
max-redirects: 3
lettuce:
pool:
enabled: true
max-active: 8
max-wait: -1ms
min-idle: 0
max-idle: 8
cluster:
refresh:
# 当Redis集群节点发生变化后,Lettuce默认是不会刷新节点拓扑
#支持集群拓扑动态感应刷新,自适应拓扑刷新是否使用所有可用的更新,默认false关闭
adaptive: true
#定时刷新
period: 2000
重启应用。
4. 使用Redisson
除了提供基础Redis操作功能,额外提供分布式锁、分布式集合、分布式任务调度、分布式事务管理等企业级功能。底层和Lettuce一样基于Netty的异步非阻塞IO模型。添加依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.24.3</version>
</dependency>
代码编写:
public static void main(String[] args) {
Config config = new Config();
// config.useMasterSlaveServers() 用于主从模式
// config.useClusterServers() 用于集群模式
// config.useReplicatedServers() 主要用于Azure或者AWS云Redis
config.useSingleServer() // 单节点模式
.setAddress(PlatformConstant.REDIS_LIST)
.setPassword(PlatformConstant.REDIS_PASS)
.setDatabase(2)
.setConnectionPoolSize(10)
.setConnectionMinimumIdleSize(5);
RedissonClient client = Redisson.create(config);
// 基础操作
// string
// 获取 RBucket 对象,指定键名
RBucket<String> bucket = client.getBucket("myStringKey");
// 设置字符串值
bucket.set("Hello, Redisson!");
System.out.println(client.getBucket("myStringKey").get());
// list
client.getList("myList").addAll(List.of(1, 2, 3, 4, 5));
System.out.println(client.getList("myList").readAll().size());
// map
client.getMap("myMap").put("name", "jack");
// set
client.getSet("mySet").addAll(List.of(1, 1, 2, 4, 5));
// order set
client.getScoredSortedSet("myZSet").add(1.0, 1);
// bitmap
RBitSet bitSet = client.getBitSet("myBitmap");
// 设置位值
bitSet.set(0, true); // 将第 0 位设置为 1
bitSet.set(1, false); // 将第 1 位设置为 0
bitSet.set(2, true); // 将第 2 位设置为 1
// 统计设置为 1 的位的数量
System.out.println("Number of set bits: " + bitSet.cardinality());
// hyperloglog
RHyperLogLog<Object> hyperLogLog = client.getHyperLogLog("myHyperloglog");
// 添加元素到 HyperLogLog 中
hyperLogLog.add("element1");
hyperLogLog.add("element2");
hyperLogLog.add("element3");
hyperLogLog.add("element1"); // 重复元素不会影响基数统计
// 估算唯一元素的数量
long estimatedCount = hyperLogLog.count();
System.out.println("不重复的元素个数: " + estimatedCount);
// geo
RGeo<String> geo = client.getGeo("myGeo");
// 添加地理位置信息
// 依次为经度、纬度、成员名称
geo.add(116.4074, 39.9042, "Beijing");
geo.add(121.4737, 31.2304, "Shanghai");
geo.add(113.2807, 23.1291, "Guangzhou");
// 计算两个地理位置之间的距离
Double distance = geo.dist("Beijing", "Shanghai", GeoUnit.KILOMETERS);
System.out.println("北京到上海的距离(公里): " + distance);
// 根据给定的经纬度和半径查询附近的成员
// 依次为经度、纬度、半径值、半径单位
GeoSearchArgs geoSearchArgs = GeoSearchArgs.from(116.4074, 39.9042).radius(1000, GeoUnit.KILOMETERS);
List<String> nearByMembers = geo.search(geoSearchArgs);
System.out.println("距离北京 1000 公里内的城市: " + nearByMembers);
}
运行结果: