35.Java使用Redis
- 本节介绍如何在 Java 中使用 Redis步骤,首先需要确定已经安装了 Redis 服务及 Java Redis 驱动,并且能够成功运行 Java 程序。
Jedis:是Redis官方推荐的java连接开发工具!使用Java操作Redis中间件!如果你要使用java操作redis,那么一定要对Jedis十分的熟悉。
35.1 安装Redis驱动
- 在 Java 环境下操作 Redis ,需要安装相应的 Redis 驱动程序,也就是jedis.jar 包,然后将该驱动添加至 Java 的 classpath 中。
- 若项目是基于 maven 构建的,那么可以直接导入 maven 坐标,如下所示:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
- 之后就可以进行三步走:
- 建立链接
- 命令测试
- 断开链接
35.2 连接Redis服务器
- 安装完 Redis 的驱动程序后,接下来就是连接 Redis 服务器。最简单的连接方式,就是通过 Jedis 对象连接。代码如下:
注意之前redis的环境配置:
远程链接 redis的要注意:
- 1、修改conf文件,把 bind 127.0.0.1那行注释掉,把protected-mode改为 no。
- 修改改daemonize为守护进程。
- 远程连接修改完配置文件之后一定要重启redis服务,不要直接exit退出。
需要开启linux服务器的防火墙,加入6379端口放通;2.需要放通服务器安全组的6379端口
Jedis 所有的命令就是我们之前学习的所有指令!所以之前的指令学习很重要!
import org.junit.Test;
//引入Redis驱动程序
import redis.clients.jedis.Jedis;
public class TestRedis {
@Test
public void testRedisConnection(){
//连接本地的 Redis 服务,new Jedis对象即可
//Jedis jedis = new Jedis("localhost",6379);
//连接远程的 Redis 服务
Jedis jedis = new Jedis("121.x.x.x", 6379);
// 如果设置 Redis 服务的密码,需要进行验证,若没有则可以省去
jedis.auth("123456");
System.out.println("链接成功!");
//查看服务是否运行
System.out.println("服务正在运行!"+jedis.ping());
}
}
- 首先确保 Redis 驱动包引入路径正确的,然后编译并运行 Java 程序,输出结果如下:
链接成功!
服务正在运行!PONG
Process finished with exit code 0
35.3 Java Redis字符串
- Redis String 类型操作,示例如下:
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class TestRedis {
@Test
public void testRedisSetGet(){
Jedis jedis = new Jedis("121.x.x.x", 6379);
jedis.auth("123456");
System.out.println("链接成功!");
//设置 redis 字符串数据
jedis.set("name","tony");
// 获取存储的数据并输出
System.out.println("获取key值new:"+jedis.get("name"));
System.out.println("获取key值num:"+jedis.get("num"));
System.out.println("获取key的过期时间:"+jedis.ttl("num"));
jedis.mset("age","18","love","football");
System.out.println("获取vals"+jedis.mget("num","age","love"));
}
}
- 译并运行上述程序,输出结果如下:
链接成功!
获取key值new:tony
获取key值num:55
35.4 Java Redis列表
- Redis List 列表操作,示例如下:
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.List;
public class TestRedis {
//Redis List 列表操作
@Test
public void testRedisList(){
//连接 Redis 服务
Jedis jedis = new Jedis("121.x.x.x", 6379);
jedis.auth("123456");
System.out.println("链接成功!");
//删除已存在的list同名key
if (jedis.llen("mycourselist")>0) {
jedis.del("mycourselist");
}
//存储数据到列表中
jedis.lpush("mycourselist","java");
jedis.lpush("mycourselist","php");
jedis.lpush("mycourselist","python");
jedis.lpush("mycourselist","c#");
// 获取存储的数据并输出
List<String> mycourselist = jedis.lrange("mycourselist", 0, -1);
for (String mycourse:mycourselist) {
System.out.println("list列表mycourselist中的元素:"+mycourse);
}
}
}
- 编译并运行上述程序,输出结果如下:
链接成功!
list列表mycourselist中的元素:c#
list列表mycourselist中的元素:python
list列表mycourselist中的元素:php
list列表mycourselist中的元素:java
Process finished with exit code 0
35.5 Java Redis键
-
查看所有 key,示例如下:
import org.junit.Test; import redis.clients.jedis.Jedis; import java.util.Iterator; import java.util.Set; public class TestRedis { //查看所有 key @Test public void testRedisKeys(){ //连接Redis 服务 Jedis jedis = new Jedis("121.x.x.x", 6379); jedis.auth("123456"); System.out.println("链接成功!"); // 获取数据并输出 Set<String> keys = jedis.keys("*"); Iterator<String> it = keys.iterator(); while (it.hasNext()) { key = it.next(); System.out.println("redis服务中的元素key:"+key); if (jedis.type(key).toLowerCase().equals("string")) { System.out.println("该元素:"+key+":"+jedis.get(key)); } } } }
-
编译并运行上述程序。输出结果如下:
链接成功!
redis服务中的元素key:myhash
redis服务中的元素key:city
redis服务中的元素key:num
该元素:num:55
redis服务中的元素key:lesson
redis服务中的元素key:weight
redis服务中的元素key:user:1
redis服务中的元素key:url
该元素:url:www.baidu.com
redis服务中的元素key:name
该元素:name:tony
redis服务中的元素key:mycourselist
redis服务中的元素key:course
redis服务中的元素key:mybit
该元素:mybit:�
redis服务中的元素key:us:name
redis服务中的元素key:value
redis服务中的元素key:user
Process finished with exit code 0
35.6 其他类型set/hash
- set类型操作
@Test
public void testRedisSet(){
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.sadd("lesson","C++","python","hadoop");
jedis.sadd("lesson","K8S");
Set<String> lessons = jedis.smembers("lesson");
Iterator<String> iterator = lessons.iterator();
while (iterator.hasNext()){
System.out.println("lesson内容:"+iterator.next());
}
jedis.srem("lesson","K8S");
System.out.println("lesson个数:"+jedis.scard("lesson"));
}
--结果--
lesson内容:c#
lesson内容:python
lesson内容:C++
lesson内容:java
lesson内容:K8S
lesson内容:lua
lesson内容:php
lesson内容:hadoop
lesson内容:sql
lesson个数:8
Process finished with exit code 0
- Hash类型操作
@Test
public void testRedisHash(){
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.hset("user:id:1","name","zs");
HashMap<String, String> map = new HashMap<>();
map.put("name","tony");
map.put("age","18");
map.put("addr","beijing");
map.put("sex","boy");
jedis.hmset("user:id:2",map);
List<String> result = jedis.hmget("user:id:2", "name", "age", "addr");
for (String ele :
result) {
System.out.println("user:id:2内容:"+ele);
}
System.out.println("user:id:1是否存在:"+jedis.exists("user:id:1"));
}
--结果--
user:id:2内容:tony
user:id:2内容:18
user:id:2内容:beijing
user:id:1是否存在:true
- zset类型取出来的仅有值,没有对应的分数
@Test
public void testRedisZset() {
Jedis jedis = new Jedis("121.x.x.x", 6379);
jedis.auth("123456");
System.out.println("链接成功!");
//删除已存在的list同名zset类型的key
if (jedis.zcard("height")>0) {
jedis.del("height");
}
jedis.zadd("height",160,"ll");
jedis.zadd("height",160,"zs");
jedis.zadd("height",170,"hh");
jedis.zadd("height",180,"lz");
jedis.zadd("height",185,"ls");
jedis.zadd("height",190,"yz");
jedis.zadd("height",210,"tony");
List<String> heights = jedis.zrange("height", 0, -1);
for (String height : heights) {
System.out.println("zset集合height中的元素:" + height);
}
}
--结果--
链接成功!
zset集合height中的元素:ll
zset集合height中的元素:zs
zset集合height中的元素:hh
zset集合height中的元素:lz
zset集合height中的元素:ls
zset集合height中的元素:yz
zset集合height中的元素:tony
35.7 java redis事务
-
redis事务操作比较重要,需要着重讲:
-
实例操作:需要fastjson需要将jar包提前导入项目。
@Test
public void testTx01(){
JSONObject jsonObject = new JSONObject();
jsonObject.put("name","zs");
JSONObject jsonObject2 = new JSONObject();
jsonObject2.put("name","ls");
Jedis jedis = new Jedis("121.x.x.x", 6379);
jedis.auth("xxxxx");
/*监控key,如果修改了key,则后续事物被放弃
jedis.watch("num");
jedis.set("num","20");
jedis.unwatch();*/
//开启事务
Transaction multi = jedis.multi();
Response<String> user=null;
try {
multi.set("user1",jsonObject.toJSONString());
multi.set("user2",jsonObject2.toJSONString());
user = multi.get("user1");
multi.exec(); //执行事务
}catch (Exception ex){
multi.discard(); //关闭事务
ex.printStackTrace();
}finally {
System.out.println("user1:"+jedis.get("user1"));
System.out.println("user2:"+jedis.get("user2"));
System.out.println(user.get());
jedis.close(); //关闭链接
}
}
##结果
user1:{"name":"zs"}
user2:{"name":"ls"}
{"name":"zs"}
- 测试事务在不同异常下命令的执行情况。
@Test
public void testTx02(){
JSONObject jsonObject = new JSONObject();
jsonObject.put("name","zs");
JSONObject jsonObject2 = new JSONObject();
jsonObject2.put("name","ls");
Jedis jedis = new Jedis("121.x.x.x", 6379);
jedis.auth("xxxxx");
System.out.println("name类型:"+jedis.type("name")+":值:"+jedis.get("name"));
jedis.del("user1","user2");
//开启监控,实现乐观锁
jedis.watch("user1");
//开启事务
Transaction multi = jedis.multi();
//取消异常捕捉机制
multi.set("user1",jsonObject.toJSONString());
//int i = 1 / 0; //算数异常,导致事务中其他命令中断执行
multi.incrBy("name",10); //命令异常,事务中其他命令可以正常执行
multi.set("user2",jsonObject2.toJSONString());
multi.exec(); //执行事务
//虽然给字符串incrBy命令报错了,但是其他依旧正常执行成功了!
System.out.println("user1:"+jedis.get("user1"));
System.out.println("user2:"+jedis.get("user2"));
jedis.close(); //关闭链接
}
//算数异常
name类型:string:值:tony
java.lang.ArithmeticException: / by zero
//命令异常
name类型:string:值:tony
user1:{"name":"zs"}
user2:{"name":"ls"}
- 简单的资金转移例子
//首先标记了键aAmount,而后检查余额是否足够,不足就取消标记,并不作扣减; 足够的话,就启动事务进行更新操做
//若是在此期间键aAmount被其它人修改, 那在提交事务(执行exec)时就会报错, 程序中一般能够捕获这类错误再从新执行一次,直到成功
@Test
public void testTx03(){
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("aAmount","100");
jedis.set("bAmount","20");
System.out.println("操作前A账户余额"+jedis.get("aAmount"));
System.out.println("操作前B账户余额"+jedis.get("bAmount"));
Integer aAmount; //A账户余额
Integer bAmount; //B账户余额
Integer transferSmount = 10; //转移金额
jedis.watch("aAmount");
//jedis.set("aAmount","200"); //可以模拟其他线程修改该变量
int aAmount1 = Integer.parseInt(jedis.get("aAmount"));
if (aAmount1 <transferSmount){
System.out.println("--unmondify--");
jedis.unwatch();
}else {
System.out.println("--mondify--");
Transaction multi = jedis.multi();
multi.decrBy("aAmount",transferSmount);
multi.incrBy("bAmount",transferSmount);
multi.exec();
}
System.out.println("操作后A账户余额"+jedis.get("aAmount"));
System.out.println("操作后B账户余额"+jedis.get("bAmount"));
}
--结果--
操作前A账户余额100
操作前B账户余额20
--mondify--
操作后A账户余额90
操作后B账户余额30
35.8 主从复制
-
redis 6379,6380都启动,先各自启动运行
-
jedis设置主从读写同步
//主从复制
@Test
public void testSlave(){
Jedis jedis_m = new Jedis("127.0.0.1", 6379);
System.out.println(jedis_m.get("title"));
//jedis_m.auth("123456");
Jedis jedis_s = new Jedis("127.0.0.1", 6380);
//jedis_s.auth("123456");
jedis_s.replicaof("127.0.0.1", 6379);
jedis_m.set("m_key1","m_val");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(jedis_s.get("m_key1"));
}
--结果--
redis
m_val
- 从服务器会进行和主机同步操作
35.9 jedis线程池JedisPool
- 单例封装JedisPoolUtil,实例化JedisPool
public class JedisPoolUtil {
//被volatile修饰的变量不会被本地线程缓存,对该变量的读写都是直接操作共享内存
private static volatile JedisPool jedisPool = null;
public JedisPoolUtil(){}
public static JedisPool getJedisPoolInstance() {
if (null == jedisPool){
synchronized (JedisPoolUtil.class){
if (null == jedisPool){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(2000);
jedisPoolConfig.setMaxIdle(32);
jedisPoolConfig.setTestOnBorrow(true);
jedisPoolConfig.setMaxWait(Duration.ofMinutes(100000));
jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1");
}
}
}
return jedisPool;
}
}
#将jedis返回JedisPool
public static void release(JedisPool jedisPool,Jedis jedis){
if (jedis != null) {
jedisPool.returnResource(jedis);
}
}
- 测试
@Test
public void testJedisPool(){
JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = null;
try {
jedis = jedisPoolInstance.getResource();
jedis.set("num","12");
}catch (Exception ex){
ex.printStackTrace();
}finally {
System.out.println("num值:"+jedis.get("num"));
JedisPoolUtil.release(jedisPoolInstance,jedis);
}
}
--结果--
num值:12
- 配置总结:JedisPool的配置参数大部分是由JedisPoolConfig的对应项来赋值的。
maxActive:控制一个pool可分配多少个jedis实例,经过pool.getResource()来获取;若是赋值为-1,则表示不限制;若是pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted
maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例
whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采起的操做;
默认有三种:
WHEN_EXHAUSTED_FAIL:表示无jedis实例时,直接抛出NoSuchElementException
WHEN_EXHAUSTED_BLOC:则表示阻塞住,或者达到maxWait时抛出JedisConnectionException;
WHEN_EXHAUSTED_GROW:则表示新建一个jedis实例,也就说设置的maxActive无用;
maxWait:表示当borrow一个jedis实例时,最大的等待时间,若是超过等待时间,则直接抛JedisConnectionException;
testOnBorrow:得到一个jedis实例的时候是否检查链接可用性(ping());若是为true,则获得的jedis实例均是可用的;
testOnReturn:return 一个jedis实例给pool时,是否检查链接可用性(ping())
testWhileIdle:若是为true,表示有一个idle object evitor空闲对象检测器线程对idle object进行扫描,若是validate验证失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
timeBetweenEvictionRunsMillis:表示idle object evitor两次扫描之间要sleep的毫秒数;
numTestsPerEvictionRun:表示idle object evitor每次扫描的最多的对象数;
minEvictableIdleTimeMillis:表示一个对象至少停留在idle状态的最短期,而后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基础上,加入了至少minIdle个对象已经在pool里面了。若是为-1,evicted不会根据idle time驱逐任何对象。若是minEvictableIdleTimeMillis>0,则此项设置无意义,且只有在timeBetweenEvictionRunsMillis大于0时才有意义;
lifo:borrowObject返回对象时,是采用DEFAULT_LIFO(last in first out,即相似cache的最频繁使用队列),若是为False,则表示FIFO队列;
其中JedisPoolConfig对一些参数的默认设置以下:
testWhileIdle=true
minEvictableIdleTimeMills=60000
timeBetweenEvictionRunsMillis=30000
numTestsPerEvictionRun=-1