redis的发布订阅
什么是发布订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端可以订阅任意数量的频道。
Redis的发布和订阅
客户端订阅频道发布的消息
频道发布消息 订阅者就可以收到消息
发布订阅的代码实现:
第一步:打开一个客户端订阅cc;
关键字:subscribe 自定义频道名字
第二步:客户端 ,发布信息;(在另一台虚拟机上发布信息);
关键字:publish 频道名字 发布内容
返回1,是订阅者数量;
第三步:打开第一个客户端可以看到另一个客户端发送的消息;
发布订阅模式,订阅多个频道;
redis事务
事务简介
可以一次执行多个命令,本质是一组命令的集合。一个事务中的 所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。
单独的隔离的操作
官网说明
https://redis.io/docs/interact/transactions/
MULTI、EXEC、DISCARD、WATCH。这四个指令构成了 redis 事务处理的基础。
执行事务的关键字:
1.MULTI 用来组装一个事务;将命令存放到一个队列里面;
2.EXEC 用来执行一个事务;//commit
3.DISCARD 用来取消一个事务;回滚//rollback
4.WATCH 用来监视一些 key,一旦这些 key 在事务执行之前被改变,则取消事务的执行。
例:
在事务执行之前 如果监听的key的值有变化就不能执行;
在事务执行之前 如果监听的key的值没有变化就能执行;
有关事务,遇到的两种错误:
1.调用 EXEC 之前的错误
“调用 EXEC 之前的错误”,有可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redis 都会进行记录,在客户端调用 EXEC 时,redis 会拒绝执行这一事务
第一种错误:Exec之前就出现错误,执行的时候所有的语句都不执行;
第二种错误:Exec之后出现错误,除错误的语句不执行外,其他语句正常执行;
类型不同,第一条语句是String字符串类型;而第二条语句把String字符串类型作为Set集合类型进行缓存,是不行的;
注:而对于“调用 EXEC 之后的错误”,redis 则采取了完全不同的策略,即 redis 不会理睬这些错误,而是继续向下执行事务中的其他命令。这是因为,对于应用层面的错误,并不是 redis 自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行;
Watch 类似mysql中的乐观锁
redis事务冲突
悲观锁:select * from biao where 1=1 for update;
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,
这样别人想拿这个数据就会block直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制,
比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁:update uuuu set moner-=8000 where version=1 1.1
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,
每次去拿数据的时候都认为别人不会修改,所以不会上锁,
但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,
可以使用版本号等机制。乐观锁适用于多读的应用类型,
这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
watch
“WATCH”可以帮我们实现类似于“乐观锁”的效果,即 CAS(check and set)。
WATCH 本身的作用是“监视 key 是否被改动过”,而且支持同时监视多个 key,只要还没真正触发事务,WATCH 都会尽职尽责的监视,一旦发现某个 key 被修改了,在执行 EXEC 时就会返回 nil,表示事务无法触发。
事务回滚:
redis的使用
java操作redis
第一步:创建java项目;
第二步:添加redis的依赖;
<!--redis依赖 jedis是redis官方推荐使用的客户端-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
相关的API
第三步:测试java是否可以连接到redis;
添加单个值,并获取;ping成功返回PONG;
redis设置的有密码的,连接是加上密码;
package com.aaa;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class MyTest {
JedisPool pool = null;
@BeforeEach
public void aa(){
//使用连接池连接redis数据库
pool = new JedisPool("192.168.146.8",6379);
}
@Test
public void cc(){
try (Jedis jedis = pool.getResource()) {
jedis.auth("zhk");
jedis.set("k1", "8899");
String k1 = jedis.get("k1");
System.out.println("k1的值为:" + k1);
String ping = jedis.ping();
System.out.println("测试是否同" + ping);
}
}
package com.aaa;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Set;
public class MyTest {
JedisPool pool = null;
@BeforeEach
public void aa(){
//使用连接池连接redis数据库
pool = new JedisPool("192.168.146.8",6379);
}
@Test
public void cc(){
try (Jedis jedis = pool.getResource()) {
jedis.auth("zhk");
jedis.set("k1", "8899");
String k1 = jedis.get("k1");
System.out.println("k1的值为:" + k1);
jedis.setex("age",30,"80");
//设置选择数据库
jedis.select(2);
//设置键值对
jedis.set("name","100");
jedis.set("name1","200");
//获取所以的key值
Set<String> keys = jedis.keys("*");
System.out.println(keys.size());
for (String key:keys) {
System.out.println(key);
}
//判断key是否存在
System.out.println(jedis.exists("name"));
//判断key的过期时间
System.out.println(jedis.ttl("age"));
//获取key对应的值
System.out.println(jedis.get("name1"));
String ping = jedis.ping();
System.out.println("测试是否同" + ping);
}
/*public static void main(String[] args) {
//使用连接池快速连接数据库
JedisPool pool = new JedisPool("192.168.146.8",6379);
try (Jedis jedis = pool.getResource()) {
jedis.auth("zhk");
jedis.set("k1","8899");
String k1 = jedis.get("k1");
System.out.println("k1的值为:"+k1);
String ping = jedis.ping();
System.out.println("测试是否同"+ping);
}*/
/* //设置连接的服务器 端口号默认是6379
// 服务器的默认值是localhost
Jedis jedis = new Jedis("192.168.146.8",6379);
//redis连接密码
jedis.auth("zhk");
jedis.set("k1","8899");
String k1 = jedis.get("k1");
System.out.println("k1的值为:"+"k1");
//通过ip地址是否可以ping成功,成功返回PONG
String s = jedis.ping();
System.out.println(s);*/
}
}
String类型
redis整合Springboot项目
第一步:创建springboot项目;
第二步:加入springboot依赖;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
t添加配置文件和配置yml文件
Spring缓存注解
1.配置文件
/*redis的缓存*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}