Redis持久化,管道,事务操作

redis持久化

因为redis数据都在内存,需要持久化保证数据因为故障不丢失。
redis持久化有两种:AOF和RDB。
RDB:是数据库的全量备份
AOF: 是指令的增量备份

RDB快照原理

redis使用os的多进程COW(copy on write)机制来实现快照持久化

AOF原理

AOF日志存储的是redis服务器变更指令序列。那么一个空的redis实例就可以通过重新执行所有AOF日志中的指令恢复内存数据
先执行指令,再存储日志

  1. 提供bgrewriteaof对aof日志进行瘦身,原理是:开辟一个子进程对内存进行遍历,转化为redis操作指令,然后序列化到一个新得AOF日志文件,然后将操作期间的增量AOF日志数据累加到新的AOF日志中
    实质是过滤那些过期的键
  2. linux的glibc提供了fsync(int fd)函数,它可以将指定文件的内容强制从内核缓存刷到磁盘。执行非常慢,通常是每隔1s执行一次fsync操作
    一般redis主节点不会执行持久化操作,从节点进行备份

管道

大多人对 Redis 管道有一个误解,他们以为这是 Redis 服务器提供的一种特别的技术,有了这种技术就可以加速 Redis 的存取效率。

分析:redis client和server之间连续执行多条指令,每次执行都是一个请求和响应过程。而分析交互流程发现,写操作是写到本地发送缓冲区就可以。而读操作需要等待消息通过网络路由到本地接收缓冲区才能读到。
针对这种情况,Redis引入了管道技术,管道技术可以将多次操作的指令放在一次传输中进行,然后再将多次操作的结果一次性返回,当需要大量存取数据时,使用管道技术可以大大减少网络消耗时间。

批量存储数据

 Jedis jedis = new Jedis("192.168.195.128",6379);
 Pipeline pipeline = jedis.pipelined();
  for(int i = 0;i<1000;i++){
     String content = i + "";
     pipeline.set(content,content);
 }
 pipeline.sync();

批量读取数据,所有操作的返回结果饭封装为response到对象中。

Map<String,Response> responses = new LinkedHashMap<String, Response>();
for(int i = 0;i<1000;i++){
     String content = i + "";
     Response<String> response = pipeline.get(content);
     responses.put(content,response);
}
pipeline.sync();
for(String key:responses.keySet()){
    System.out.println("key:"+key + ",value:" + responses.get(key).get());
}

redis事务

单线程redis可以保证所有指令的原子性执行
multi:表示事务的开始。exec:表示事务的执行。discard:表示事务的丢弃。
redis事务原理:redis的事务指令都会缓冲到服务器的事务队列中,服务器收到exec指令后,执行整个事务队列。
redis执行事务却不能回滚所有操作,所以不能保证原子性
redis事务常常和pipeline一起使用,将多次IO操作压缩为单词IO操作,减少网络读写的时间消耗。

watch操作

redis分布式锁是一种悲观锁,而redis的watch操作是一种乐观锁。
watch原理:
watch在事务开始时会盯住1个或多个关键变量,当事务开始执行时,redis判断关键变量自从watch之后是否被修改了,若被人动过exec指令就返回null告诉客户端执行事务失败,然后客户端就可以不断进行自旋操作。

## 必须在 multi 之前就盯住关键变量
## 多线程修改账户余额,使用watch锁住此资源
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TransactionDemo {

  public static void main(String[] args) {
    Jedis jedis = new Jedis();
    String userId = "abc";
    String key = keyFor(userId);
    jedis.setnx(key, String.valueOf(5));  # setnx 做初始化
    System.out.println(doubleAccount(jedis, userId));
    jedis.close();
  }

  public static int doubleAccount(Jedis jedis, String userId) {
    String key = keyFor(userId);
    while (true) {
      jedis.watch(key);
      int value = Integer.parseInt(jedis.get(key));
      value *= 2; // 加倍
      Transaction tx = jedis.multi();
      tx.set(key, String.valueOf(value));
      List<Object> res = tx.exec();
      if (res != null) {
        break; // 成功了
      }
    }
    return Integer.parseInt(jedis.get(key)); // 重新获取余额
  }

  public static String keyFor(String userId) {
    return String.format("account_%s", userId);
  }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值