浅谈Redis

一、Redis的简介

1.开源免费的缓存中间件,性能高,读可达110000次/s,写可达81000次/s。
2.redis的单线程讨论:
    V4.0之前:是单线程的,所有任务处理都在一个线程内完成.
    V4.0:引入多线程,异步线程用于处理一些耗时较长的任务,如异步删除命令unlink,对BigKey的删除.
    V6.0:在核心网络模型中引入多线程,进一步提高多核CPU利用率,但核心业务部分(命令处理)依旧是单线程.
Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小、网络宽带。
多线程会导致上下文切换,线程安全问题要引入锁机制,复杂度高,造成性能折扣。
Redis用非阻塞IO,IO多路复用的执行方式,单线程轮询描述符,将数据库的开、关、读、写都转成事件,减少线程切换和竞争。
Redis用hash结构,读取速度快。对数据存储进行了优化,压缩表(对数据压缩)、跳表(有序的数据结构加快读写速度)等。

IO多路复用:利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
文件描述符(File Descriptor): 
    简称FD,是一个从0 开始的无符号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,如常规文件、视频、硬件设备等,当然也包括网络套接字(Socket)。

IO多路复用

 

二、Redis的使用经验

1.慢查询

统计比较慢的时间段指的是命令执行这个步骤。可能存的value过大、命令较多、网络延时等。
预设阀值(默认10000微秒(10毫秒)): slowlog-log-slower-than
最多保存多少条慢查询日志(默认128): slowlog-max-len
#slowlog-log-slower-than=0 #会记录所有的命令
#slowlog-log-slower-than<0 #对于任何命令都不会进行记录。
// 使用config set
config set slowlog-log-slower-than 20000
config set slowlog-max-len 1000         #指定服务器最多保存多少条慢查询日志
config rewrite                         #对启动 Redis 服务器时所指定的 redis.conf 文件进行改写
// 常用命令
SLOWLOG len   #查看当前慢查询记录有多少条
SLOWLOG get   #列出当前所有慢查询
SLOWLOG get 10  #列出慢查询的前10条
slowlog reset  #清理慢查询日志

2.服务器CPU100%的原因和解决方案

1.redis连接数过高
redis的默认链接数是10000,如果没有更改这个值一般可以忽略,毕竟访问不大的话不会到这么多
2.数据持久化导致的阻塞
redis是可以持久化的,而redis持久化会采取LZF算法进行压缩,这种方式会减少磁盘的存储大小,而通过这种方式是需要消耗cpu的。
rdbcompression yes 表示压缩算法是打开的
还有3个关键的参数,这里解释一下:
save 900 1 //表示每15分钟且至少有1个key改变,就触发一次持久化
save 300 10 //表示每5分钟且至少有10个key改变,就触发一次持久化
save 60 10000 //表示每60秒至少有10000个key改变,就触发一次持久化
因为redis持久化的动作会记录日志,我们首先找出出问题的时间段里的持久化内容大小
3.主从存在频繁全量同步
查看主从之间网络是否有延迟,如果在一台机器上无需考虑
4.value值过大
5.redis慢查询

3.Pipeline

一次链接,批处理工具。网络延迟越大效果越好。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.io.IOException;
 
/**
 * 测试 Pipeline
 * @Author: JunSouth
 */
public class PipelineTest {
    public static void main(String[] args) throws IOException {
        Jedis client = new Jedis("127.0.0.1", 6379);
        long startPipe = System.currentTimeMillis();
		//获取 Pipeline 
        Pipeline pipe = client.pipelined();
        pipe.multi();
        for (int i = 0; i < 100000; i++) {
            pipe.set("pipe" + i, i + "" );
        }
		// 执行
        pipe.exec();
		// 关闭
        pipe.close();
        long endPipe = System.currentTimeMillis();
        System.out.println("pipeline set 操作时间 " + (endPipe - startPipe));
        for (int i = 0; i < 100000; i++) {
            client.set("normal" + i, i + "");
        }
        System.out.println("普通 set 操作时间 : " + (System.currentTimeMillis() - endPipe));
    }
}

4.BigKey问题

1.String类型的Key,它的值为5MB(数据过大);
2.List类型的Key,它的列表数量为20000个(列表数量过多);
3.ZSet类型的Key,它的成员数量为10000个(成员数量过多);
4.Hash格式的Key,它的成员数量虽然只有1000个但这些成员的value总大小为100MB(成员体积过大);

redis-cli --bigkeys  #连接Redis查找BigKey
redis-cli -h 127.0.0.1 -p 6379 --bigkeys   #不连Redis查找BigKey
1.BigKey的危害
    1.阻塞请求
    2.阻塞网络
    3.影响主从同步、主从切换
2.BigKey的解决
    1、对大Key进行拆分(用mget批量获取)
    2、对大Key进行清理(通过unlink命令安全的删除大Key甚至特大Key)
    3、监控Redis的内存、网络带宽、超时等指标
    4、定期清理失效数据
    5、压缩value

5.缓存同步策略

1.设置有效期: 查时没有则更新,设置有效期,到期自动删除。
2.同步双写: 修改数据时直接修改缓存,通过事务保持数据一致。
3.异步通知: 修改数据库后发布通知,再修改缓存。

MQ异步通知
Canal异步通知

 

多级缓存方案

 三、Redis的一些原理

1.主从的数据问题

一次从服务完整数据同步:
新的从服务器连接时,主服务器会执行一次 bgsave 命令生成一个 RDB 文件,
然后再以 Socket 的方式发送给从服务器。从服务器收到 RDB 文件之后再把所有的数据加载到自己的程序中,完成了一次全量的数据同步。
二次从服务数据同步:
主服务器会把离线之后的从服务写入命令,存储在一个特定大小队列中,队列保证先进先出的执行顺序,当从服务器重写恢复上线之后。主服务会判断离线这段时间内的命令是否还在队列中,如果在就直接把队列中的数据发送给从服务器,这样就避免了完整同步的资源浪费。
存储离线命令的队列大小默认是 1MB,可自行修改队列大小的配置项 repl-backlog-size。
主从数据一致:
当从服务器已经完成和主服务的数据同步之后,再新增的命令会以异步的方式发送至从服务器,在这个过程中主从同步会有短暂的数据不一致。
从服务器只读性
默认情况下,处于复制模式的主服务器既可以执行写操作也可以执行读操作,而从服务器则只能执行读操作。
可以在从服务器上执行 config set replica-read-only no 命令,使从服务器开启写模式,但需要注意以下几点:
    在从服务器上写的数据不会同步到主服务器;
    当键值相同时主服务器上的数据可以覆盖从服务器;
    在进行完整数据同步时,从服务器数据会被清空;

2.哨兵的工作

监控: 持续监控 master 、slave 是否健康,是否处于预期工作状态。
主从动态切换: 当 Master 运行故障,哨兵启动自动故障恢复流程:从 slave 中选择一台作为新 master。
通知机制: 竞选出新的master之后,通知客户端与新 master 建立连接;slave 从新的 master 中 replicaof,保证主从数据的一致性。
基于 pub/sub 机制实现哨兵集群之间、客户端的通信;
通过 INFO 命令获取 slave 列表, 哨兵与 slave 建立连接;通过哨兵的 pub/sub,实现了与客户端和哨兵之间的事件通知。
1.监控: 
哨兵模式启用的时候,同步启用Sentinel进程。sentinel程会向所有的master 和 slaves 以及其他sentinel进程 发送心跳包(1s一次),看看是否正常返回响应。
如果master、slave 没有在规定的时间内响应 sentinel 的 PING 命令, sentinel 会认为该节点已挂。
PING 命令的回复有两种情况:
有效: 返回 +PONG、-LOADING、-MASTERDOWN 任何一种;
无效: 有效回复之外的回复。
主观下线:某个 sentinel 节点的判断,并不是 sentinel 集群的判断。
客观下线:大部分是 sentinel 节点都对主节点的下线做了下线判断,那么就是客观的。
2.主从动态切换:
sentinel 的一个很重要工作,就是从多个slave中选举出一个新的master。当然,这个选举的过程会比较严谨,需要通过 筛选 + 综合评估 方式进行选举,
1 筛选
过滤掉下线或者断线,没有回复哨兵ping响应的从节点。
评估实例过往的网络连接状况 down-after-milliseconds,如果一定周期内从库和主库经常断连,而且超出了一定的阈值(如 10 次),则该slave不予考虑。
2 综合评估
对于剩下健康的节点按顺序进行综合评估了。
3.信息通知:
等推选出最新的master之后,后续所有的写操作都会进入这个master中。要尽快通知到所有的slave,让他们重新 replacaof 到 master上,重新建立runID和slave_repl_offset。

四、Redis常见问题

缓存雪崩
同一时间内大量缓存失效,导致大量的请求打在DB上。
1.key的过期时间尽量错开。
2.主从+哨兵的集群。

缓存穿透
大量请求根本不存在的key(库中也没有),导致大量的请求打在DB上。
1.缓存空值或缺省值
2.布隆过滤器(BloomFilter),在查询时先查BloomFilter的key是否存在。

缓存击穿
热点key过期,导致大量的请求打在DB上
1.不设置过期时间
2.过期前提前更新
3.加互斥锁,某个key只让一个线程查询,阻塞其他线程.
4.缓存屏障

缓存预热
项目刚上线,提前把热key加到Redis里。

缓存降级(弃卒保帅)
保证主服务可用,将其他次要访问的数据进行缓存降级。
一般返回个温馨提示语。

LRU算法

public class LRUCache<k, v> {
    //容量
    private int capacity;
    //当前有多少节点的统计
    private int count;
    //缓存节点
    private Map<k, Node<k, v>> nodeMap;
    private Node<k, v> head;
    private Node<k, v> tail;
    public LRUCache(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException(String.valueOf(capacity));
        }
        this.capacity = capacity;
        this.nodeMap = new HashMap<>();
        //初始化头节点和尾节点,用哨兵模式减少判断头结点和尾节点为空的代码
        Node headNode = new Node(null, null);
        Node tailNode = new Node(null, null);
        headNode.next = tailNode;
        tailNode.pre = headNode;
        this.head = headNode;
        this.tail = tailNode;
    }
 
    public void put(k key, v value) {
        Node<k, v> node = nodeMap.get(key);
        if (node == null) {
            if (count >= capacity) {
                //先移除一个节点
                removeNode();
            }
            node = new Node<>(key, value);
            //添加节点
            addNode(node);
        } else {
            //移动节点到头节点
            moveNodeToHead(node);
        }
    }
 
    public Node<k, v> get(k key) {
        Node<k, v> node = nodeMap.get(key);
        if (node != null) {
            moveNodeToHead(node);
        }
        return node;
    }
 
    private void removeNode() {
        Node node = tail.pre;
        //从链表里面移除
        removeFromList(node);
        nodeMap.remove(node.key);
        count--;
    }
 
    private void removeFromList(Node<k, v> node) {
        Node pre = node.pre;
        Node next = node.next;
 
        pre.next = next;
        next.pre = pre;
 
        node.next = null;
        node.pre = null;
    }
 
    private void addNode(Node<k, v> node) {
        //添加节点到头部
        addToHead(node);
        nodeMap.put(node.key, node);
        count++;
    }
 
    private void addToHead(Node<k, v> node) {
        Node next = head.next;
        next.pre = node;
        node.next = next;
        node.pre = head;
        head.next = node;
    }
 
    public void moveNodeToHead(Node<k, v> node) {
        //从链表里面移除
        removeFromList(node);
        //添加节点到头部
        addToHead(node);
    }
    class Node<k, v> {
        k key;
        v value;
        Node pre;
        Node next;
        public Node(k key, v value) {
            this.key = key;
            this.value = value;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/20531.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux三种网络模式 | 仅主机、桥接、NAT

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Linux三种网络模式 仅主机模式&#xff1a;虚拟机只能访问物理机&#xff0c;不能上网 桥接模式&#xff1a;虚拟机和物理机连接同一网络&#xff0c;虚拟机和物理机…

Docker的四种网络模式

1.Host 模式 通常来讲&#xff0c;启动新的Docker容器&#xff0c;都会分配独立的Network Namespace隔离子系统&#xff0c;如果在运行是指定为host模式&#xff0c;那么Docker容器将不会获得一个独立的Network Namespace&#xff0c;而是和宿主机共用一个Network Namespace子…

山东专升本计算机第六章-数据库技术

数据库技术 SQL数据库与NOSQL数据库的区别 数据库管理系统 考点 6 数据库管理系统的组成和功能 组成 • 模式翻译 • 应用程序的翻译 • 交互式查询 • 数据的组织和存取 • 事务运行管理 • 数据库的维护 功能 • 数据定义功能 • 数据存取功能 • 数据库运行管理…

【图像基础知识】常见图像格式

文章目录 1 简介2 RGB3 BGR4 YUV4.1 YUV常见格式4.2 YUV420详解4.3 NV12 5 Gray6 图像格式之间的转换7 参考链接 原文来自于地平线开发者社区&#xff0c;未来会持续发布深度学习、板端部署的相关优质文章与视频&#xff0c;如果文章对您有帮助&#xff0c;麻烦给点个赞&#x…

Sentinel : 服务容错(降级熔断、流量整形)

什么是服务雪崩&#xff1f; 服务雪崩效应是一种因“服务提供者的不可用”&#xff08;原因&#xff09;导致“服务调用者不可用”&#xff08;结果&#xff09;&#xff0c;并将不可用逐渐放大的现象。 我来用一个模拟场景带你感受一下服务雪崩的厉害之处。假设我有一个微服…

20 散列表的查找

散列表的查找 简介&#xff1a;散列表&#xff08;也成哈希表&#xff09;是一种高效的数据结构&#xff0c;他可以在平均复杂度为O(1)的情况下实现查找、插入和删除操作。 哈希表的基本思想是根据关键字的值来计算其应存储的位置。这个计算过程就是通过哈希函数来实现的。 根…

深度学习-第T5周——运动鞋品牌识别

深度学习-第T5周——运动鞋品牌识别 深度学习-第T5周——运动鞋品牌识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目3、查看数据 四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签 2、数据可视化3、检查数据4、配置数据集 …

西瓜书读书笔记整理(三)—— 第二章 模型评估与选择

第二章 模型评估与选择 第 2 章 模型评估与选择2.1 经验误差与过拟合1. 错误率 / 精度 / 误差2. 训练误差 / 经验误差 / 泛化误差3. 过拟合 / 欠拟合4. 学习能力5. 模型选择 2.2 评估方法1. 评估方法概述2. 留出法3. 交叉验证法4. 自助法5. 调参 / 最终模型 2.3 性能度量1. 回归…

Ada语言学习(1)Basic Knowledge

文章目录 说在前头命名注释数字变量变量类型signed integersEnumerationsFloating Points 类型重用&#xff08;继承&#xff09;类型转换 运算符属性&#xff08;Attributes&#xff09;练习 说在前头 本系列教程将会通过提问的方式来完成整个学习过程&#xff0c;因为当你能…

86盒IP对讲一键报警器

86盒IP对讲一键报警器 86盒IP对讲一键报警器&#xff1a;革命性保障生命安全的利器&#xff01; 随着科技的飞速发展&#xff0c;我们的生活变得越来越方便和智能化。而86盒IP对讲一键报警器更是在这种背景下应运而生。这款产品不仅无缝对接各种手机APP&#xff0c;也可以在智…

RabbitMQ --- 惰性队列、MQ集群

一、惰性队列 1.1、消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度&#xff0c;就会导致队列中的消息堆积&#xff0c;直到队列存储消息达到上限。之后发送的消息就会成为死信&#xff0c;可能会被丢弃&#xff0c;这就是消息堆积问题。 解决消息堆积有三种…

互联网大厂测开面试记,二面被按地上血虐,所幸Offer已到手

在互联网做了几年之后&#xff0c;去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好&#xff0c;更重要的是&#xff0c;它是对你专业能力的背书&#xff0c;大厂工作背景多少会给你的简历增加几分竞争力。 如何备战面试的&#xff1f; 第一步&#xff1a;准备简历 …

vue3回到上一个路由页面

学习链接 Vue Router获取当前页面由哪个路由跳转 在Vue3的setup中如何使用this beforeRouteEnter 在这个路由方法中不能访问到组件实例this&#xff0c;但是可以使用next里面的vm访问到组件实例&#xff0c;并通过vm.$data获取组件实例上的data数据getCurrentInstance 是vue3提…

51单片机也可以移植RTOS

说起RTOS移植&#xff0c;我们首先会想到32位单片机。 那么51单片机可以移植RTOS吗&#xff1f; 我的答案是&#xff0c;只要资源够用&#xff08;ROM空间、RAM空间&#xff09;&#xff0c;可以移植。 前提是你对RTOS的实现原理非常清楚&#xff0c;并且可以自己完成移植工作…

数据可视化大屏的页面布局以及自适应

在做数据可视化大屏之前&#xff0c;我们需要考虑到页面的布局问题以及页面缩放自适应问题&#xff0c;下面分别就这两个方面讲解。 页面布局 类似这种页面区块的明显划分&#xff0c;常用的布局方式有两种&#xff1a; 1、flex布局 2、grid布局 grid布局 grid布局可以按区块…

深度学习用于医学预后-第二课第四周5-10节-为个体患者制定风险评估模型

文章目录 相对风险按风险对患者进行排序个体与基线风险吸烟者与不吸烟者年龄对风险的影响 在本课中&#xff0c;您将学习 Cox 比例风险模型(Cox Proportional Hazards Model)。您将了解 Cox 模型如何考虑患者变量来比较不同患者的风险&#xff0c;使用他们的患者概况。 但到目前…

mysql增量备份

目录 一、修改配置文件&#xff0c;开启增量备份功能 &#xff08;1&#xff09;查看是否已经开启了 &#xff08;2&#xff09;修改配置文件开启 &#xff08;3&#xff09;增量记录文件 二、还原增量备份 &#xff08;1&#xff09;修改了数据 &#xff08;2&#xff…

nginx keepalive 高可用原理和实操

文章目录 前言一、nginxkeepalive搭建高可用服务方案&#xff1f;二、方案解析1.keepalive是什么2.nginx是什么 三、keepalive与nginx环境安装四、高可用配置实例总结 前言 一、nginxkeepalive搭建高可用服务方案&#xff1f; 使用nginx-keepalived双机热备机制&#xff0c;vi…

【云计算•云原生】4.云原生之什么是Kubernetes

文章目录 Kubernetes概念Kubernetes核心概念集群podConfigMap Kubernetes架构master节点的组件worker节点组件 Kubernetes网络架构内部网络外部网络 k8s各端口含义 Kubernetes概念 K8S就是Kubernetes&#xff0c;Kubernetes首字母为K&#xff0c;末尾为s&#xff0c;中间一共有…

BEV(0)---Transformer

1 Transformer Transformer是一个Sequence to Sequence model&#xff0c;特别之处在于它大量用到了self-attention&#xff0c;替代了RNN&#xff0c;既考虑了Sequence的全局信息也解决了并行计算的问题。 1.1 self-attention&#xff1a; ①. 输入x1 ~ x4为一个sequence&…