Redis持久化、事务机制、淘汰策略及过期key监听

一.redis数据同步问题
  1. redis与mysql数据同步问题怎么解决?
      ①.直接通过手动清理redis的缓存,然后重新查询数据;
      ②.采用mq订阅mysql的 binlog日志文件增量同步到redis中,整个过程是采用异步同步,达到数据最终一致性;
      ③.使用alibaba的 canal框架,也是通过mq订阅mysql的binlog日志来完成的,也是实现最终一致性;
二.redis 持久化机制
  1. redis持久化机制?
      持久化机制有两种:aof和rdb(默认的)

  2. 增量同步于全量同步?
      全量同步:周期性的或者是定时的去把数据全部同步一下,同步频率不高,存在丢失数据的情况,比如说 每天晚上11点开始全量同步,过了11点以后的数据还未同步时,发生意外比如断电之类的就会造成数据丢失;
      增量同步:就是当有一个操作的时候就会进行同步,同步频率特别高,同时对服务器的压力也是比较大的,但是能保证数据不丢失,比如说往redis里存储一条数据,同时会把数据持久化到磁盘中;

  3. redis持久化机制aof和rdb区别?
    RDB
      ①.采用的定时持久化机制,但是服务器因为某种原因宕机后可能数据丢失;
      ②.redis默认采用rdb的方式实现数据持久化;
      ③.以快照的形式将数据持久化到硬盘,记录在dump.rdb二进制文件中;
      ④.通过查询redis.conf配置文件可以看到rdb默认是同时采用三种策略进行数据同步
    rdb同步数据三种策略

    save 900 1 表示在900秒后如果有一次key的操作就同步数据到dump.rdb文件;
    save 300 10 表示在300秒后如果有10次key的操作就同步数据到dump.rdb文件;
    save 60 10000 表示在60秒内如果有10000次key的操作就同步数据到dump.rdb文件;
    ⑤.原理:通过一个子进程将操作数据写入到临时的dump.rdb文件中,当到达同步策略是就将临时文件覆盖掉dump.rdb文件达到最终的rdb;

AOF
  ①.开启方式:在redis.conf配置文件中找到 appendonly no 把no改为yes就开始aof了;
aof默认是没有开启的
开启aop

  ②.在aof中redis有三种策略,默认采用的是 appendsync everysec
aof策略

     appendfsync always:每次有数据修改时都会写入AOF文件,能够保证数据不丢失,同时效率相对比较低;

     appendfsync everysec:每秒钟同步一次,会先把数据执行命令放入缓存区,每秒同步一次,效率非常高,同时如果发生宕机或其他故障会丢失1秒的数据;

    appendfsync no: 效率非常高,但是不会持久化数据;

  ③.aop是把数据的执行命令存入了aof文件中,不会记录get命令,重启时会执行aof中命令以恢复数据;如下图所示:
在这里插入图片描述

  ④.redis默认采用每秒同步策略,这个策略相对于每次同步效率高,同时就算数据丢失也只有一秒钟的数据,所以建议使用appendfsync everysec同步策略;
  

三.redis淘汰策略
  1. redis 缓存淘汰策略:
      redis默认是没有设置内存阈值的,也就是redis会根据服务器剩余内存大小当做阈值。
      设置阈值方法:在redis.conf配置文件中找到maxmemory 把注释去掉并设置阈值大小;
    默认没有设置阈值
    redis淘汰策略有如下几种:(LRU的意思是:Least Recently Used最近最少使用的,LFU的意思是:Least Frequently Used最不常用的)
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.

volatile-lru:在设置了过期时间的key中,优先移出最近未使用的key;
allkeys-lru:在所有key中,优先移出最近未使用的key;(推荐)
volatile-lfu:在设置了过期时间的key中选择最不常用的。
allkeys-lfu:在所有的key中选择最不常用的。(不区分是否携带过期时间)
volatile-random:在设置了过期时间的key中,随机移出某个key;
allkeys-random:在所有的key中,随机移出某个key;
volatile-ttl:在设置了过期时间的key中,具有更早过期时间的key优先移出;
noeviction:当内存使用达到阈值的时候,申请内存会直接报错;

四.redis 过期key监听
  1. redis key 自动过期机制
    ①.开启redis key 过期自动监听机制,在redis.conf配置文件中找到 notify-keyspace-events Ex 把注释去掉即可,默认是未开启的,如下图:
    默认是未开启的
    开启redis事件监听
    ②.监听redis过期key实现事件回调demo
    ⑴.定义redisConfig类,配置一个RedisMessageListenerContainer类型的@bean
@Configuration
public class RedisConfig {

	@Bean
	public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
		container.setConnectionFactory(connectionFactory);
		return container;
	}
}

⑵.定义一个redis key过期监听类继承KeyExpirationEventMessageListener ,重写onMessage()方法,onMessage方法中就是执行回调的方法根据自己逻辑写

@Component
public class RedisKeyExpirationListenerextends extends KeyExpirationEventMessageListener {

	@Autowired
	private OrderMapper orderMapper;
	
	public RedisKeyExpirationListenerextends(RedisMessageListenerContainer listenerContainer) {
		super(listenerContainer);
	}

	@Override
	public void onMessage(Message message, @Nullable byte[] pattern) {

		String key = message.toString();
		System.out.println("失效key:" + key);
		
		OrderEntity order = orderMapper.getOrder(key);
		if(order != null) {
			orderMapper.updateByOrderNo(order.getOrderNo(), 2);
		}
		

	}
}

⑶.定义业务逻辑OrderController类

@RestController
public class OrderController {

	@Autowired
	private OrderMapper orderMapper;
	
	@Resource
	private RedisTemplate<String, Integer> redisTemplate;
	
	@RequestMapping("/addOrder")
	public String addOrder() {
		
		System.out.println("创建订单。。。");
		OrderEntity orderEntity = new OrderEntity();
		orderEntity.setOrderState(0);
		String orderNo = UUID.randomUUID().toString();
		orderEntity.setOrderNo(orderNo);
		orderMapper.addOrder(orderEntity);
		redisTemplate.boundValueOps(orderNo).set(orderEntity.getOrderId());
		redisTemplate.boundValueOps(orderNo).expire(10l, TimeUnit.SECONDS);
		return "success";
	}
}

⑷.定义OrderMapper和OrderEntity类

@Mapper
public interface OrderMapper {

	@Insert("insert into `order` (order_state,order_no) values (#{orderEntity.orderState},#{orderEntity.orderNo}) ")
	public Integer addOrder(@Param("orderEntity") OrderEntity orderEntity);
	
	@Select("select * from `order` where order_no = #{orderNo}")
	public OrderEntity getOrder(String orderNo);
	
	@Update("update `order` set order_state = @{orderState} where order_no = #{orderNo}")
	public Integer updateByOrderNo(@Param("orderNo")String orderNo,@Param("orderState")Integer orderState);
}
public class OrderEntity {

	private Integer orderId;
	
	private Integer orderState;
	
	private String orderNo;

	
	public Integer getOrderId() {
		return orderId;
	}


	public void setOrderId(Integer orderId) {
		this.orderId = orderId;
	}


	public String getOrderNo() {
		return orderNo;
	}


	public void setOrderNo(String orderNo) {
		this.orderNo = orderNo;
	}



	public Integer getOrderState() {
		return orderState;
	}



	public void setOrderState(Integer orderState) {
		this.orderState = orderState;
	}


	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((orderId == null) ? 0 : orderId.hashCode());
		result = prime * result + ((orderNo == null) ? 0 : orderNo.hashCode());
		result = prime * result + ((orderState == null) ? 0 : orderState.hashCode());
		return result;
	}


	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		OrderEntity other = (OrderEntity) obj;
		if (orderId == null) {
			if (other.orderId != null)
				return false;
		} else if (!orderId.equals(other.orderId))
			return false;
		if (orderNo == null) {
			if (other.orderNo != null)
				return false;
		} else if (!orderNo.equals(other.orderNo))
			return false;
		if (orderState == null) {
			if (other.orderState != null)
				return false;
		} else if (!orderState.equals(other.orderState))
			return false;
		return true;
	}


	@Override
	public String toString() {
		return "OrderEntity [orderId=" + orderId + ", orderState=" + orderState + ", orderNo=" + orderNo + "]";
	}

	
	
}

五.redis事务
  1. redis 事务,redis事务没有回滚,只有取消提交事务,事务没有提交之前是查询不到数据的;
    ①.Multi 开启事务,EXEC 提交事务;假如有A客户端使用multi开启事务以后对一个key进行赋值以后,然后又有B客户端也对这个key进行操作的时候,如果B客户端也开启了multi事务那么那个客户端事务最后提交,key的结果为那个事务时设置的值,如果B客户端没有开启事务,对这个key做操作后,A客户端没有提交事务时查询的结果为B客户端设置的值,但是当A客户端提交事务后,其结果就变成了提交事务后的结果,也就是说Multi开启事务不影响其他客户端对key进行任何操作,但是值为最后提交事务时的值;
    ②.watch 命令:可以监听一个或多个key,当开启事务后监听key是否发生了改变,如果没有发生改变可以提交事务,如果发生了改变则事务不会提交,底层就是采用版本号乐观锁实现的;
    ③.discard 命令:取消提交事务,redis中没有回滚事务,只有取消提交事务
    ④.命令使用方式
watch age
multi
set age 18
exec 和 discard 二选一

⑤.redis取消事务与mysql回滚事务的区别?
  mysql中当开启事务的时候会对该行数据上行锁,其他线程不能在操作该数据,提交事务或者是回滚事务都会释放行锁,并完成事务或者是取消事务,而redis中并没有行锁的概念,开启事务后其他线程照样可以对该数据进行操作,所以redis取消事务就好了 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值