一,Redis事务操作
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
特点:
- Redis事务没有隔离级别的概念:批量操作在发送Exec命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的跟新,事务外查询不能看到。
- Redis不保证原子性:Reids中,单条命令式原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍然会被执行。
multi #开启事务
exec #提交事务
Discard #取消事务
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 a1 QUEUED 127.0.0.1:6379> set k2 a2 QUEUED 127.0.0.1:6379> set k3 a3 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> exec 1) OK 2) OK 3) OK 4) "a2"
错误不影响其他结果
127.0.0.1:6379> multi OK 127.0.0.1:6379> incr k1 QUEUED 127.0.0.1:6379> set k1 2 QUEUED 127.0.0.1:6379> set k2 3 QUEUED 127.0.0.1:6379> set k4 2 QUEUED 127.0.0.1:6379> exec 1) (error) ERR value is not an integer or out of range 2) OK 3) OK 4) OK 127.0.0.1:6379>
二,Redis 乐观锁
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
watch key #监视字段,当字段数据发生变动,事务执行失败
在watch监视之后,在另一个redis窗口修改了money数据导致事务执行失败
127.0.0.1:6379> watch money OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> decrby money 1000 QUEUED 127.0.0.1:6379> INCRby money 122 QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors.
失败后,通过unwatch释放锁,重新watch获取最新的值
127.0.0.1:6379> UNWATCH OK 127.0.0.1:6379> watch money OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> decrby money 1000 QUEUED 127.0.0.1:6379> incrby money 122 QUEUED 127.0.0.1:6379> exec 1) (integer) 100 2) (integer) 222
三,Jedis
jedis 是redis官方推荐的java连接开发工具!使用java来操作redis中间件。
jiedis:采用直连,多个线程操作的话,不安全,如果想要避免不安全,使用jedis pool 连接池!更像BIO模式
1.打开Windows本地redis-server
2.下载maven依赖
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.6.3</version> </dependency>
3.
连接本地redis,auth连接密码
Jedis jedis=new Jedis("127.0.0.1",6379); jedis.auth("ccd");
开启事务,把命令都写到事务中,如果发生异常取消事务。
try { multi.set("name", "ccd"); multi.sadd("fruit","banner","apple","watermelon"); multi.exec(); }catch (Exception e) { multi.discard(); } System.out.printf( jedis.get("name")); System.out.printf("sadd:"+ jedis.smembers("fruit")); }
四, lettuce
1,配置
采用netty,实例可以在多个线程中共享,不存在线程不安全的情况,可以减少线程数据,更像NIO模式
1).启动redis
2).yml配置
spring: redis: host: 127.0.0.1 port: 6379 database: 1 #默认打开数据库
jedis语法和redisTemplate https://www.cnblogs.com/z-sir/p/13664221.html 参考
2,序列化
自定义注解redisconfig
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class redisconfig { //自定义redisTemplate @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); //配置具体的序列化方式 //json 序列化配置 Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper om=new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); objectJackson2JsonRedisSerializer.setObjectMapper(om); //String 的序列化 StringRedisSerializer stringRedisSerializer= new StringRedisSerializer(); //key 采用String 的序列化方式 template.setKeySerializer(stringRedisSerializer); //hash的key 也采用string 的序列化方式 template.setHashKeySerializer(stringRedisSerializer); //value 序列化采用jackson template.setValueSerializer(stringRedisSerializer); template.afterPropertiesSet(); return template; } }
直接复制粘贴到自己的redisconfig
测试代码
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; @SpringBootTest class LettuceNewApplicationTests { @Autowired @Qualifier("redisTemplate") //多个依赖,指定一个 private RedisTemplate redisTemplate; @Test void contextLoads() { User user=new User(1,"daads"); redisTemplate.opsForValue().set("name",user); System.out.printf(""+ redisTemplate.opsForValue().get("name")); } }
最后报错了
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `com.example.lettuce_new.User` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
内容已经set进去了但是无法使用get读取
最后在自己的实体类 写一个空的构造方法
最后输出
User{id=1, name='daads'}
redis中的内容
"[\"com.example.lettuce_new.User\",{\"name\":\"daads\",\"id\":1}]"
无乱码序列化成功
五,redis持久化操作
1.RDB
优势:
1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。
缺点:
1)不能保证数据的精准性,系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
2)RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
配置:
当key发生变化会保存
900秒内至少有一个key发生变化则dump内存快照
300秒内至少有10个key发生变化则dump内存快照
60秒内至少有1w个key发生变化则dump内存快照
在redis.confg中修改你保存文件的名称
dump快照
保存机制:
1)在满足规则的情况下,会自动保存
2)在执行flushall命令时,会保存
3)save命令可以保存
4)shutdown命令可以选择保存不保存
恢复数据:只要将dump.rdb导入到redis文件夹中,redis-server启动时会自动恢复数据。
2,AOF
优势:
(1)AOF可以更好的保护数据不丢失,一般AOF会以每隔1秒,通过后台的一个线程去执行一次fsync操作,如果redis挂掉了,最多丢失1秒的数据。
(2)AOF以append-only的模式写入,所以没有任何的磁盘寻址的开销,写入性能非常的高。
缺点:
(1)aof储存的是命令,对于一个100次的增加操作,rdb只储存结果,aof要储存100次的命令,所以内存占用高。
(2)aof恢复数据比rdb慢。
配置:
appendonly 默认为no要在redis.conf中开启
appendfilename 生成aof的名称
默认为一秒执行一次
always 执行一个命令存储一次
everysec 一秒执行一次
no 由操作系统自动调度刷磁盘
数据可视.且数据可以修改
重启redis-server,进入cli报错
使用redis-check-aof --fix 修复文件
再次重启server进入cli成功 。但是有一条数据被删除了
六,redis主从复制
是指将一台redis服务器的数据,复制到其他redis服务器。前者称为主节点,后者为从节点。
数据传递是单向的只能从主节点传到从节点。默认情况下都是主节点。
主从复制的作用主要包括:
1,数据冗余:主从复制实现了数据的热备份
2,负载均衡:主从复制的基础上,配合读写分离,可以由主节点实现写,从节点实现读,分担服务器负载,在写少读多的情况下可以大大提高redis的服务器并发量
3,故障恢复:当主节点宕机,可以直接使用从节点变成主机。手动配置或使用哨兵自动检查
[root@ccd redis-5.0.7]# redis-cli 127.0.0.1:6379> info replication #查看主机 # Replication role:master #查看主从 connected_slaves:0 #没有从机 master_replid:2eba218c20027e0c88da76a6e19db823bdb13c93 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
从机
127.0.0.1:6381> slaveof 127.0.0.1 6380 #配置他的主 OK 127.0.0.1:6381> info replication # Replication role:slave #变成从机 master_host:127.0.0.1 master_port:6380 master_link_status:up master_last_io_seconds_ago:2 master_sync_in_progress:0 slave_repl_offset:14 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:7f09953890e93c62d7811ea9989d7139030c35ba master_replid2:0000000000000000000000000000000000000000 master_repl_offset:14 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:14
主机
127.0.0.1:6380> info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6381,state=online,offset=696,lag=0 #从机数据 slave1:ip=127.0.0.1,port=6379,state=online,offset=696,lag=0 master_replid:7f09953890e93c62d7811ea9989d7139030c35ba master_replid2:0000000000000000000000000000000000000000 master_repl_offset:696 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:696
主从都有
# Replication role:slave master_host:127.0.0.1 master_port:6380 master_link_status:up master_last_io_seconds_ago:4 master_sync_in_progress:0 slave_repl_offset:59258 slave_priority:100 slave_read_only:1 connected_slaves:1 slave0:ip=127.0.0.1,port=6379,state=online,offset=59258,lag=1 #从机 master_replid:7f09953890e93c62d7811ea9989d7139030c35ba #主机 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:59258 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:59258
主机宕机,让从机变成主机
[root@ccd redis-5.0.7]# redis-cli -p 6380 127.0.0.1:6380> shutdown save not connected> [root@ccd redis-5.0.7]# redis-cli -p 6381 slaveof no one #指定从机变成主机 OK [root@ccd redis-5.0.7]# redis-cli -p 6381 127.0.0.1:6381> info replication # Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6379,state=online,offset=60084,lag=0 master_replid:27d1a1dd144d9f7e9e0255091aff4f223bba60cc master_replid2:7f09953890e93c62d7811ea9989d7139030c35ba master_repl_offset:60084 second_repl_offset:60057 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:60084 127.0.0.1:6381>
此时宕机的主机恢复也没有从机