Redis从入门到精通(二十一)Redis最佳实践(二)mset、pipeline、慢查询优化、内存划分

文章目录

    • 前言
    • 7.2 批处理优化
      • 7.2.1 命令执行流程
      • 7.2.2 mset
      • 7.2.3 Pipeline
      • 7.2.4 集群下的批处理
        • 7.2.4.1 问题与解决方案
        • 7.2.4.2 基于Spring的串行化执行
    • 7.3 服务器端优化
      • 7.3.1 持久化配置
      • 7.3.2 慢查询优化
        • 7.3.2.1 什么是慢查询
        • 7.3.2.2 如何查看慢查询
      • 7.3.3 命令及安全配置
      • 7.3.4 内存划分和内存配置
      • 7.3.5 集群or主从?
        • 7.3.5.1 集群完整性问题
        • 7.3.5.2 集群带宽问题
        • 7.3.5.3 其他问题
        • 7.3.5.4 结论

前言

Redis最佳实现系列文章:

Redis从入门到精通(二十)Redis最佳实践(一)优雅的Key结构、拒绝BigKey

7.2 批处理优化

7.2.1 命令执行流程

客户端与Redis服务器交互时,单个命令的执行流程如下:

N条命令的执行流程:

Redis处理指令是很快的,主要花费的时候在于网络传输,于是很容易就想到可以将多条指令批量的传输给Redis:

7.2.2 mset

Redis提供了msethmset这样的命令,可以实现批量插入数据。 例如利用mset一次性批量插入1000条数据:

@Test
public void testMset() {long b = System.currentTimeMillis();String[] arr = new String[2000];for (int i = 0; i < 2000; i++) {arr[i] = "test:mset:key_" + i;arr[i+1] = "value_" + i;i++;}jedis.mset(arr);long e = System.currentTimeMillis();System.out.println("time: " + (e - b));
}

控制台打印执行时间:

time: 7

此时Redis中的数据:

7.2.3 Pipeline

mset虽然可以批处理,但是却只能操作部分数据类型,因此如果有对复杂数据类型的批处理需要,建议使用Pipeline,例如:

@Test
public void testPipeline() {long b = System.currentTimeMillis();// 创建管道Pipeline pipeline = jedis.pipelined();for (int i = 1; i <= 1000; i++) {// 放入命令到管道pipeline.set("test:pipeline:key_" + i, "value_" + i);}pipeline.sync();long e = System.currentTimeMillis();System.out.println("time: " + (e - b));
}

控制台打印执行时间:

time: 119

此时Redis中的数据:

7.2.4 集群下的批处理

7.2.4.1 问题与解决方案

mset或Pipeline批处理命令在一次请求中一般会携带多条命令,而如果此时Redis是一个集群,那批处理命令的多个Key必须落在一个插槽中,否则就会导致执行失败。

这样的要求其实很难实现,因为在批处理时可能一次要插入很多条数据,这些数据很有可能落在不相同的节点上,这就会导致报错了。

解决这个问题,有4种方案:

7.2.4.2 基于Spring的串行化执行
@Test
public void testMset() {// MSET写数据Map<String, String> map = new HashMap<>(3);map.put("name", "Rose");map.put("age", "21");map.put("sex", "Female");stringRedisTemplate.opsForValue().multiSet(map);// MGET取数据List<String> strings = stringRedisTemplate.opsForValue().multiGet(Arrays.asList("name", "age", "sex"));strings.forEach(System.out::println);
}

运行单元测试结果如下:

Rose
21
Female

此时Redis种的数据:

7.3 服务器端优化

7.3.1 持久化配置

Redis的持久化虽然可以保证数据安全,但也会带来很多额外的开销,因此持久化一般要遵循下列建议:

  • 用来做缓存的Redis实例尽量不要开启持久化功能

  • 建议关闭RDB持久化功能,使用AOF持久化

  • 利用脚本定期在slave节点做RDB,实现数据备份

  • 设置合理的rewrite阈值,避免频繁的bgrewrite

  • 配置no-appendfsync-on-rewrite = yes禁止在rewrite期间做AOF,避免因AOF引起的阻塞

  • 部署有关建议:

    • Redis实例的物理机要预留足够内存,应对fork和rewrite
    • 单个Redis实例内存上限不要太大,例如4G或8G
    • 不要与CPU密集型应用部署在一起
    • 不要与高硬盘负载应用一起部署。例如:数据库、消息队列。

7.3.2 慢查询优化

7.3.2.1 什么是慢查询

并不是说很慢的查询才是慢查询,而是在Redis执行时,耗时超过某个阈值的命令,称为慢查询。

慢查询的危害:由于Redis是单线程的,所以当客户端发出指令后,它们都会进入到Rdis底层的queue来执行,如果此时有一些慢查询的数据,就会导致大量请求阻塞,从而引起报错。

  • 慢查询的阈值可以通过 slowlog-log-slower-than 配置指定,单位是微秒。默认是10000,建议1000。
127.0.0.1:6379> CONFIG GET slowlog-log-slower-than
1) "slowlog-log-slower-than"
2) "10000"
  • 慢查询会被放入慢查询日志中,日志的长度有上限,可以通过 slowlog-max-len 配置指定,本质是一个队列。默认是128,建议1000。
127.0.0.1:6379> CONFIG GET slowlog-max-len
1) "slowlog-max-len"
2) "128"
  • 要修改这两个配置可以使用 config set 命令:
127.0.0.1:6379> CONFIG set slowlog-log-slower-than 1000
OK
127.0.0.1:6379> CONFIG set slowlog-max-len 1000
OK
7.3.2.2 如何查看慢查询

查看慢查询日志列表可以使用以下名:

  • slowlog len:查询慢查询日志长度
  • slowlog get [n]:读取n条慢查询日志
  • slowlog reset:清空慢查询列表

7.3.3 命令及安全配置

Redis默认情况下,会绑定在0.0.0.0:6379,这样将会使Redis服务暴露到公网上,如果Redis没有开启身份认证,则可以导致任意用户在可以访问目标服务器的情况下未授权访问Redis以及读取Redis的数据

攻击者在未授权访问Redis的情况下,还可以利用Redis的相关方法,在Redis服务器上写入公钥,进而可以使用对应私钥直接登录目标服务器。

更详细的漏洞描述和重现方式见:https://cloud.tencent.com/developer/article/1039000

漏洞出现的核心的原因有以下几点:

  • Redis未设置密码
  • 利用了Redis的config set命令动态修改Redis配置
  • 使用了root账号权限启动Redis

为了避免这样的漏洞,可以参照以下建议:

  • Redis一定要设置密码
  • 禁止使用下面命令:keys、flushall、flushdb、config set等命令,可以利用rename-command禁用。
  • bind:限制网卡,禁止外网网卡访问
  • 开启防火墙
  • 不要使用root账户启动Redis
  • 尽量不使用默认的端口

7.3.4 内存划分和内存配置

当Redis内存不足时,可能导致Key频繁被删除、响应时间变长、QPS不稳定等问题。Redis的内存占用一般包括三个方面:

  • 1)数据内存:Redis最主要的部分,存储Redis的键值信息,主要问题是BigKey问题、内存碎片问题。

    • 碎片问题:Redis底层分配有自己的分配策略。例如当前Key只需要10个字节,此时分配8字节肯定不够,那么底层就会分配16个字节,多出来的6个字节就不能被使用,也就时产生了碎片。
  • 2)进程内存:Redis主进程本身运⾏需要占⽤的内存,如代码、常量池等等;这部分内存⼤约⼏兆,在⼤多数⽣产环境中与Redis数据占⽤的内存相⽐可以忽略。

  • 3)缓冲区内存:一般包括客户端缓冲区、AOF缓冲区、复制缓冲区等。客户端缓冲区又包括输入缓冲区和输出缓冲区两种。这部分内存占用波动较大,不当使用BigKey,可能导致内存溢出。

Redis提供了一些命令,用于查询Redis目前的内存分配状态:

  • info memory:查看内存分配状态

  • memory usage key:查看某个key的内存
127.0.0.1:6379> memory usage test:pipeline:key_195
(integer) 80

内存缓冲区常见的有三种:

  • 复制缓冲区:主从复制的repl_backlog_buf,如果太小可能导致频繁的全量复制,影响性能。可以通过repl-backlog-size配置来设置,默认1mb。
  • AOF缓冲区:AOF执行rewrite之前的缓冲区。无法设置容量上限。
  • 客户端缓冲区:分为输入缓冲区和输出缓冲区,输入缓冲区最大1G且不能设置,输出缓冲区可以设置。

一般复制缓冲区和AOF缓冲区不会有问题,可能有问题的是客户端缓冲区。

客户端缓冲区指的就是客户端向Redis发送命令时,用来缓存命令的一个输入端缓冲区,以及Redis向客户端返回数据的输出缓存区。输入缓冲区最大1G且不能设置,一般不会有问题,如果超过了这个空间,Redis会直接断开,因为此时此刻就代表着Redis处理不过来了。

因此输入端缓冲区并不需要担心,要担心的时输出端缓冲区。 客户端缓冲区的配置如下:

如上图所示,client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>配置的几个参数含义如下:

  • <class>:客户端类型,包括normal(普通客户端)、replica(主从复制客户端)、pubsub(PubSub客户端)
  • <hard limit>:缓冲区上限,超过hard limit后断开客户端
  • <soft limit> <soft seconds>:持续时间限制,当内存大小达到soft limit,并持续soft seconds秒后,断开客户端

7.3.5 集群or主从?

集群虽然具备高可用特性,能实现自动故障恢复,但是如果使用不当,也会存在一些问题:

7.3.5.1 集群完整性问题

在Redis的默认配置中,如果发现任意一个插槽不可用,则整个集群都会停止对外服务。

在实际开发中,最重要的是可用性,即使有slot不能使用,Redis集群也应该可以对外提供服务。为此,需要把如下配置修改成no:

# 默认为yes
cluster-require-full-coverage no
7.3.5.2 集群带宽问题

集群节点之间会不断的互相Ping来确定集群中其它节点的状态。每次Ping携带的信息至少包括:插槽信息、集群状态信息等。

集群中节点越多,集群状态信息数据量也越大,10个节点的相关信息可能达到1kb,此时每次集群互通需要的带宽会非常高,这样会导致集群中大量的带宽都会被Ping信息所占用。

解决方案主要有:

  • 避免大集群,集群节点数不要太多,最好少于1000,如果业务庞大,则建立多个集群。
  • 避免在单个物理机中运行太多Redis实例。
  • 配置合适的cluster-node-timeout值。
7.3.5.3 其他问题
  • 数据倾斜问题
  • 客户端性能问题
  • 命令的集群兼容性问题
  • lua和事务问题
7.3.5.4 结论

单体Redis(主从Redis)已经能达到万级别的QPS,并且也具备很强的高可用特性。如果主从能满足业务需求的情况下,所以如果不是在万不得已的情况下,尽量不搭建Redis集群。

本节完。本节所涉及的代码和资源可从git仓库下载:https://gitee.com/weidag/redis_learning.git

更多内容请查阅分类专栏:Redis从入门到精通

感兴趣的读者还可以查阅我的另外几个专栏:

  • SpringBoot源码解读与原理分析(已完结)
  • MyBatis3源码深度解析(已完结)
  • 再探Java为面试赋能(持续更新中…)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android studio gradle 下载不下来的问题

有时候由于网络问题&#xff0c;会发现 android-studio 或 idea 下载不懂 gradle&#xff0c;可以修改 gradle-wrapper.properties 配置文件中的 distributionUrl 选项来解决这个问题。 distributionUrlhttps\://mirrors.cloud.tencent.com/gradle/gradle-5.1.1-all.zip 这个选…

【opencv】dnn示例-vit_tracker.cpp 使用OpenCV库和ViTTrack模型实现的视频追踪程序

这段代码是一个使用OpenCV库和ViTTrack模型实现的视频追踪程序。程序通过摄像头或视频文件获取图像序列&#xff0c;并对选定的目标对象进行实时追踪。 代码主要分为以下几个部分&#xff1a; 导入必要的库&#xff1a;程序开始时先导入了iostream&#xff0c;cmath以及相关Ope…

TCP-IP详解卷一:协议 第2章 链路层——阅读总结——

该内容适合程序员查看 第2章 链路层 2.1 引言 链路层有三个目的&#xff1a; 1.为IP模块发送和接收IP数据报&#xff1b; 2.为ARP模块发送ARP请求和接收ARP应答&#xff1b; 3.为RARP发送RARP请求和接收RARP应答&#xff1b; 2.2 以太网和IEEE 802封装 以太网&#xff1a;带…

视频教程下载:用ChatGPT快速提升股票投资能力

学完此视频后可以获得&#xff1a; 学习如何使用人工智能/Chatgpt进行基础/快速/高级财务与研究分析 学习如何使用人工智能/Chatgpt对任何公司进行定性投资研究 学习如何使用人工智能/Chatgpt对任何公司进行定量投资研究 学习如何使用人工智能/Chatgpt创建、预测和分析财务…

OpenHarmony UI动画-recyclerview_animators

简介 带有添加删除动画效果以及整体动画效果的list组件库 下载安装 ohpm install ohos/recyclerview-animatorsOpenHarmony ohpm 环境配置等更多内容&#xff0c;请参考如何安装OpenHarmony ohpm 包 使用说明 引入组件库 import { RecyclerView } from "ohos/recycler…

SOLIDWORKS Composer如何使用3D工具实现更真实的动画效果

当我们使用SOLIDWORKS composer创建动画时&#xff0c;往往会涉及到产品的安装与拆解&#xff0c;现实生活中我们在拆卸组装产品的时候&#xff0c;我们往往需要一些工具的协助&#xff0c;比如扳手、螺丝刀等等&#xff0c;那么我们如何在虚拟动画中也将这一过程以逼真的形式展…

前端项目的导入和启动

安装依赖 前端安装依赖只需要在控制台执行“npm i”即可。Tips&#xff1a;当我们执行的时候&#xff0c;有时候会很慢。可以考虑使用yarn或者pnpm。然而使用yarn或者pnpm有时候有一些莫名其妙的问题。所以还是得使用npm&#xff0c; 这个时候可以通过更换镜像源为淘宝镜像源。…

安卓xml存储读取和sharedpreferences文件存储读取

起因今天有人问到我 xml文件存储读取和sharedpreferences读写该咋做&#xff0c;能不能帮忙写个案例&#xff0c;这里我简单写出一个案例&#xff0c;一下是全部的代码 一、首先引入 权限 <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE&q…

OpenHarmony UI开发-ohos-svg

简介 ohos-svg是一个SVG图片的解析器和渲染器&#xff0c;解析SVG图片并渲染到页面上。它支持大部分 SVG 1.1 规范&#xff0c;包括基本形状、路径、文本、样式和渐变,它能够渲染大多数标准的 SVG 图像。ohos-svg的优点是性能好、内存占用低。 效果展示 SVG图片解析并绘制: …

【网站项目】新生报到系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

RIP最短路实验(华为)

思科设备参考&#xff1a;RIP最短路实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 RIP&#xff08;Routing Information Protocol&#xff0c;路由信息协议&#xff09;是一种基于距离矢量的内部网关协议&#xff0c;工作原理是每个路由器周期性地向邻居路由器发…

C++笔记:类和对象(二)

对象和引用 代码演示&#xff1a; #include<iostream> using namespace std;class A { public :A() default;A(const A&) default; private :};class B { public :B(A &a) : a(a) {} private :A &a; };class C { public :C() default;C(const C &) …