Redis底层探秘之旅

一.Redis之分布式缓存
  Redis最常见的用法就是作为分布式缓存中间件,一般在一个完整的请求链路中作为数据库前面的缓冲地带,防止在高并发情况下数据库性能扛不住而使整个服务崩溃。

数据库性能规格列表:
Redis底层探秘之旅_第1张图片
二.缓存的适用场景-读多写少
  缓存本质上就是将数据从速度慢的介质加载到速度快的介质。(典型的用法如硬盘数据加载到内存)
缓存又可以分为如下几类:

  • 单机JVM缓存:HashMap,谷歌的Guava包…等
  • 分布式缓存中间件:Redis,MemCached…等
  • K/V型的Nosql内存数据库

Redis底层探秘之旅_第2张图片

三.Redis底层通讯原理-RESP
  Redis是一个c/s模式的TCP Server,像Web开发中,浏览器与服务端交互的协议通常是HTTP协议一样,那么Redis Client和Redis Server之间是如何交互的呢?是否也遵循着某种协议?
Redis底层探秘之旅_第3张图片
  Redis 定义了 RESP(Redis Serialization Protocol,Redis 序列化协议)实现客户端与服务端的通信,协议本身很简洁,请求格式如下:

          *参数个数 \r\n
          $第一个参数长度 \r\n
          第一个参数值 \r\n
          *$第N个参数长度 \r\n
          第N个参数值 \r\n

响应格式如下:

状态回复:第一个字节为“+”
错误回复:第一个字节为“-”
整数回复:第一个字节为“:”
字符串回复:第一个字节为“$”
多条字符串回复:第一个字节为“*

优点:

  1. 内容简单,解析快
  2. 网络传输快
  3. 网卡压力小
  4. 单位时间传输的命令次数多

四.模拟Redis客户端与服务端交互
  既然知道了通讯协议,那我们自己也可以按照协议约定的格式模拟客户端与服务端的交互。

package com.hong.service;

import java.io.IOException;
import java.net.Socket;

/**
 * 模拟Redis客户端与服务端交互的原理
 */
public class RedisClient {

    Socket connection;

    public RedisClient() throws IOException {
        connection = new Socket("127.0.0.1",6379);
    }

    /**
     * 模拟客户端 set key value操作
     * @param key
     * @param value
     */
    public void set(String key,String value) throws IOException {
        /**
         * 协议约定如下:
         * *参数个数 \r\n
         * $第一个参数长度 \r\n
         * 第一个参数值 \r\n
         * $第N个参数长度 \r\n
         * 第N个参数值 \r\n
         *
         * 补充说明:很多高性能的框架都是自定义协议的,为什么不采用通用的HTTP协议呢?
         * 效率。HTTP协议本身并不是高效的,它包含请求头 请求行 cookie等一大堆数据,
         * 而Redis为了追求高效的读写,采取自定义的简单协议。
         */
        // 1.协议组装,注意一定要getBytes(),否则传输中文会报错,因为网络传输时以字节为单位的
        StringBuilder sb = new StringBuilder();
        sb.append("*3").append("\r\n")
          .append("$3").append("\r\n")
          .append("SET\r\n")
          .append("$").append(key.getBytes().length).append("\r\n")
          .append(key).append("\r\n")
          .append("$").append(value.getBytes().length).append("\r\n")
          .append(value).append("\r\n");

        /**
         *3
         $3
         SET
         $5
         hello
         $5
         redis

         sb的输出将会被记录在aof文件中
         */
        System.out.println(sb);

       //2.将按协议组装好的数据发送给redis server
        connection.getOutputStream().write(sb.toString().getBytes());

        //3.读取redis server的响应
        byte[] response = new byte[1024];
        connection.getInputStream().read(response);
        System.out.println(new String(response));//+OK
    }

    public static void main(String[] args) throws IOException {
        // 推导法--用已知推理未知
        // 网络交互 -socket-BIO/NIO
       /* Socket socket = new Socket("127.0.0.1",6379);
        // 要想与redis server通信,就要遵循redis通信协议RESP
        socket.getOutputStream().write("hello redis\r\n".getBytes());

        byte[] response = new byte[1024];
        socket.getInputStream().read(response);
        System.out.println(new String(response));*/

        RedisClient redisClient = new RedisClient();
        redisClient.set("hello","redis");
    }
}

打印结果如下:

*3
$3
SET
$5
hello
$5
redis

+OK

管道机制:pipeline
Redis底层探秘之旅_第4张图片
Redis底层探秘之旅_第5张图片

/**
     * 管道机制批量操作
     *
     * @param key
     * @param value
     * @throws IOException
     */
    public void pipelineSet(String key, String value) throws IOException {
        StringBuilder command = new StringBuilder();
        command.append("*3").append("\r\n")
                .append("$3").append("\r\n")
                .append("SET\r\n")
                .append("$").append(key.getBytes().length).append("\r\n")
                .append(key).append("\r\n")
                .append("$").append(value.getBytes().length).append("\r\n")
                .append(value).append("\r\n");

        // 不断发送数据
        connection.getOutputStream().write(command.toString().getBytes());
    }

    /**
     * 获取管道执行的结果
     *
     * @throws IOException
     */
    public void pipelineResponse() throws IOException {
        byte[] response = new byte[1024];
        connection.getInputStream().read(response);
        System.out.println("pipelineSet接收到响应:" + new String(response));
    }

 public static void main(String[] args) throws IOException {
        RedisClient redisClient = new RedisClient();
        /**
         * 对比使用常规的set操作和pipelineSet操作10000条数据耗时
         */
        long s0 = System.currentTimeMillis();

      /*  for (int i=0;i<10000;i++){
            redisClient.set("hello_" + i, "redis");
        }
        System.out.println("set操作耗时:" + (System.currentTimeMillis() - s0) + "ms"); // 970ms
*/
        for (int i=0;i<10000;i++){
            redisClient.pipelineSet("hello_" + i, "redis");
        }
        System.out.println("pipelineSet操作耗时:" + (System.currentTimeMillis() - s0) + "ms"); // 280ms
    }

检测数据是否set成功:
Redis底层探秘之旅_第6张图片

五.技术的互通性
Redis底层探秘之旅_第7张图片
Redis底层探秘之旅_第8张图片
六.Redis中蕴含的分布式开发技术

  • 客户端与服务端之间的RPC技术
  • 集群中一致性Hash算法的应用
  • 分布式一致性协议Raft协议在哨兵选举中的应用
  • 高可用分布式系统的设计思路

你可能感兴趣的:(Redis)