redis 二. string 应用场景及底层分析

2023-11-05

一. 简单命令示例

  1. String字符串类型,一个key对应value最大可以存512
  2. 简单命令使用示例
//1.单个键值对插入与获取
set k1 v1
get k1

//2.一次多个键值对插入与获取
mset k1 v1 k2 v2 k3 v3
mget k1 k2 k3

//3.递增数字 INCR key,每执行一次该命令,对应该key的值累计加1
//存储 ki 对应value 为1
set ki 1
//每执行一次对应ki的值累计加1(思考该方法可以用在什么功能上? 点赞量)
INCR ki 

//4.递减数字 DECR key,每执行一次该命令,对应该key的值累计减1
DECR ki

//5.获取字符串长度
STRLEN k1

//6.分布式锁
setnx key value

//EX: 指定key多少秒后过期
//PX: 指定key多好毫秒后过期
//NX: 当key不存在时才创建,等同于setnx
//XX: 当key不存在时才创建,存在则覆盖
#set key value [EX seconds][PX milliseconds][NX|XX]
//使用举例
set orderK aaa ex 10 nx

二. java 操作示例

基础

  1. 操作 String 类型,二进制安全的。意思是可以包含任何数据。比如jpg图片或者序列化的对象 ,最基本的数据类型,一个键最大能存储512MB。
	@Test
    public void test01() {

        //创建操作redis中String类型数据的对象 ValueOperations
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();

        //1.添加数据set()添加数据(如果添加这个数据在库中已经存在就变为了修改)
        valueOperations.set("username", "mingming");

        /*2.以层级关系目录形式添加数据*/
        valueOperations.set("user:01", "wwww");
        valueOperations.set("user:01", "cccc");

        //3.添加多条数据multiSet()
        //map中的key为redis的key,map中的值为redis的值
        Map<String,String> stringMap=new HashMap<>();
        stringMap.put("数据1","值1");
        stringMap.put("数据2","值2");
        stringMap.put("数据3","值3");

        valueOperations.multiSet(stringMap);

        //4.查询
        Object username=valueOperations.get("username");

        //5.查询多条,以集合中的数据为key进行查询
        List<String> keyStr=new ArrayList<>();
        keyStr.add("数据1");
        keyStr.add("数据2");
        keyStr.add("数据3");
        List<String> valL=valueOperations.multiGet(keyStr);

        //6.删除指定key数据
        stringRedisTemplate.delete("username");

        //6.模糊匹配
        Set<String> keys = stringRedisTemplate.keys("noteUserListenedPoi:" + "*");

        //7.批量删除
        stringRedisTemplate.delete(keys);

    }

setnx() 与 getset()

  • setnx() 向redis中存储一个key-value,如果redis库中已存在,当前存储失败返回0,如果不存在,存储成功,返回1
  • get(key) 获取key的值,如果存在,则返回;如果不存在,则返回nil;
  • getset()命令:这个命令主要有两个参数 getset(key, newValue)。该方法是原子的,对key设置newValue这个值,并且返回key原来的旧值。
 	@Autowired
    public Jedis jedis;

    @Autowired
    public JedisPool jedisPool;

    @Test
    public void test() {
        //1.建立redis连接
        Jedis conn = jedisPool.getResource();

        long currentTime = System.currentTimeMillis();//当前时
        String lockTimeDuration = String.valueOf(currentTime);
        //将当前时间作为value 存储到redis中,存储成功返回1,否则返回0
        Long i = jedis.setnx("keys", lockTimeDuration);
    }

三. 使用场景举例

  1. 分布式锁 setnx
  2. 点赞 DECR

统计点击次数

  1. 使用String类型,与incr 命令实现喜欢的文章,热搜案例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ArticleController {

    public static final String likeK = "acticle";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 使用String类型,调用increment(key)对一个key进行累计加1,用来统计点击量等
     * 注意点: 该方式适用与qps不高的小厂,假设qps超过10万,例如热搜,一瞬间可能会打爆redis
     * //解决方式一: 有些统计精确度不高的,该累计数字到一定程度后就不回继续累计直接返回,例如显示"10w+"
     * //解决方式二: 适用redis的set类型,时间片去解决
     * @param acticleId 
     * @return
     */
    @GetMapping("/likeActicle")
    public Long likeArticle(String acticleId) {
        String key = likeK + acticleId;
        long l = stringRedisTemplate.opsForValue().increment(key);
        return l;
    }
}

四. 底层分析

  1. 在redis中一个string最大存储512

  2. 注意点String同一个数据类型有三种编码格式: int, embstr, raw (出现三种数据类型的原因是为了精确的利用并解决内存)

  3. int: 保存long型(长整型)的64位,8个字节有符号整数,最多19位,例如"set key 11"存储的是一个纯数字类型,到底层就使用int编码格式

  4. redis在启动时会预先建立10000个分别存储了0-9999的redisObject共享变量,当我们set 字符串为0-10000之间的话则使用共享变量即可,不需要再建立新对象,并且当前存储的数字字符串如果不超过"9223372036854775807"使用int编码格式,既redisObject中type=“REDIS_STRING”, encoding=“OBJ_ENCODING_INT”,整个结构如下
    在这里插入图片描述

  5. 注意点有个范围,当set 的数字超过"9223372036854775807"再次查看这个key的编码就会变为embstr
    在这里插入图片描述

  6. 补充点: 只有整数才会使用int编码格式,如果是浮点数,redis会现将浮点数转化为字符串值,然后进行保存
    在这里插入图片描述

  7. embstr编码格式又叫做SDS简单动态字符串,保存长度小于44字节的字符串,embstr既embedded string 又叫做嵌入式字符串,此时redisObject中的type=“OBJ_STRING”, encoding =“OBJ_ENCODING_ENBSTR”,整个redisObject结构在原int基础上嵌入了sds结构,如下图
    在这里插入图片描述

  8. raw编码格式: 保存大于44字节的字符串

  9. 注意点:在编码格式为embstr时,embsetr实际是只读的,当我们对embstr格式数据进行修改时,会先将其转换为raw格式,既所有修改后embstr格式的数据都是raw,type=“REDIS_STRING”, encoding=“OBJ_ENCODING_RAW”
    在这里插入图片描述
    在这里插入图片描述

  10. 示例在这里插入图片描述

  11. 总结:
    在这里插入图片描述

SDS 嵌入式动态字符串

  1. redis使用c编写,在c语言中使用buf数组存储字符串,而redis在c基础上重新封装了一个存储字符串的结构也就是SDS ,有叫做简单动态字符串
    在这里插入图片描述
  2. SDS结构解释: (只是拿sdshdr8做个示例,在sds.h源码中有各个该结构)
    在这里插入图片描述
  3. 查看sds.h源码发现提供了多种sdshdr5,8,16,64…存储字符串接口,为什么没有选择c的buf数组存储字符串,而是分装了多个sdshdr结构体
    在这里插入图片描述

1)通过不同sdshdX结构体存储不同大小的字符串,所以提供了多个sdshdX结构体
2)封装新结构体sds,存储字符串的好处是: c语言中没有java中的String类型,在c中适用字符串时通过char[]数组实现的,假设想要获取一个字符串的长度,需要从头开始遍历,到’\0’,而sds结构体中有一个len属性,表示当前字符串的长度,可以在O(1)情况下直接拿到,不需要像c以前一样遍历一遍字符串才可以
3)sds结构体中有alloc属性表示字符串最大字节长度,free属性,表示以分配而未使用的空间大小,这样就不需要考虑内存分配的问题
在这里插入图片描述

再次总结

  1. redis使用c编写, c语言中没有字符串类型, 封装了专门的SDS对象,用来存储,内部存在
  1. buf[]数组属性: 字节数组,用于保存实际数据
  2. len属性:当前保存数据的buf数组长度
  3. free属性: 数组中未使用的字节数量
  4. alloc属性: 当前字符串数组总共占用分配内存大小
  1. 基于以上属性sds动态字符串具有了以下优点:
  1. 常数复杂度获取字符串长度: 在获取字符串长度时,可以直接通过sds的len属性获取,复杂度为0(1)
  2. 减少缓冲区溢出: 修改sds时,会检查 free属性剩余空间的大小,如果不足将会额外申请(也就是空间预分配机制)
  3. 实现了空间预分配机制: 在对SDS 进行修改后会判断(也就是下面的扩容策略)

3.1 newlen 如果小于 1MB时,会分配 alloc 等于 2 * newlen,此时 free = len, 举例: SDS修改后len变为13个字节,程序也会分配 13 字节的未使用空间,SDS的 buf 数组此时长度将变成 13 + 13 + 1 = 27 字节(额外的一字节用于保存空字符)
3.2 newlen 如果大于等于 1MB时,会分配 1MB 的未使用空间。举例: SDS修改后len 变成30MB,程序会分配 1MB 的未使用空间,SDS 的 buf 数组的实际长度将为 30MB + 1MB + 1byte

  1. 惰性释放: 当sds需要缩短字符串时, 并不立即回收内存,而是使用 free 属性,将这些字节的数量记录起来,下次使用时再回收
  2. 二进制安全: SDS的API都是二进制安全的,存放在buf数组里的数据
  1. 根据空间分配机制了解到sds的扩容策略:
  1. 若剩余空闲长度 avail 大于新增内容的长度 addlen,直接在数组 buf 末尾追加即可,无须扩容
  2. 若剩余空闲长度 avail 小于或等于新增内容的长度 addlen,则分情况讨论:新增后总长度 len+addlen <1MB 的,按新长度的 2倍 扩容;新增后总长度 len+addlen > 1MB 的,按新长度加上 1MB 扩容。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redis 二. string 应用场景及底层分析 的相关文章

  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • Spring Batch 多线程 - 如何使每个线程读取唯一的记录?

    这个问题在很多论坛上都被问过很多次了 但我没有看到适合我的答案 我正在尝试在我的 Spring Batch 实现中实现多线程步骤 有一个包含 100k 条记录的临时表 想要在 10 个线程中处理它 每个线程的提交间隔为 300 因此在任何时
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 如何找到给定字符串的最长重复子串

    我是java新手 我被分配寻找字符串的最长子字符串 我在网上研究 似乎解决这个问题的好方法是实现后缀树 请告诉我如何做到这一点或者您是否有任何其他解决方案 请记住 这应该是在 Java 知识水平较低的情况下完成的 提前致谢 附 测试仪字符串
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 在mockito中使用when进行模拟ContextLoader.getCurrentWebApplicationContext()调用。我该怎么做?

    我试图在使用 mockito 时模拟 ContextLoader getCurrentWebApplicationContext 调用 但它无法模拟 here is my source code Mock org springframewo
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • 动态动态规划(DDP)

    1 Problem E Codeforces 一 题目大意 给你一个无向图 第i和i 1条边的权值是w i 问你每个点不在自己原本的点的代价是多少 会有q组询问 表示修改第i条边的权值 二 解题思路 可以观察到 完成这个操作需要每条边经过两
  • [LeetCode]初级算法-字符串- 实现strStr()

    标题 实现strStr 实现 strStr 函数 给定一个 haystack 字符串和一个 needle 字符串 在 haystack 字符串中找出 needle 字符串出现的第一个位置 从0开始 如果不存在 则返回 1 示例 1 输入 h
  • linux防火墙启动、停止、查看

    停止 防火墙 service iptables stop 启动防火墙 service iptables start 查看防火墙配置 iptables L n 修改的内容只是暂时保存在内存中 如果重启后还要生效 则要保存一下 service
  • Web Storage是什么?Web Storage详解

    Web Storag是HTML5引入的一个非常重要的功能 可以将数据存储在本地 如保存用户的偏好设置 复选框的选中状态 文本框默认填写的值等 用户在浏览器中刷新网页时 网页通过Web Storage就可以知道用户之前所做的一些修改 而不需要
  • Autoware 1.14(WSL2) 与LG SVL Simulator(Win11)联合仿真

    参考Couldn t find executable named rqt lgsvl simulator configurator below home autoware Autoware ros in li4692625的博客 CSDN博
  • Hyperledger Fabric核心配置文件(1)

    1 core yaml core yaml配置文件是Peer节点的示例配置文件 具体路径在fabric samples config目 录下 该core yaml示例配置文件共指定了如下六大部分内容 1 日志部分 日志记录级别有6种 CRI
  • JDBC访问数据库

    一 简介 JDBC 全称 Java DataBase Connection 数据库连接技术 可以根据驱动包连接不同类型的数据库 二 JDBC API JDBC API是java中位于java sql包下的一个数据库访问统一接口 通过它来跟数
  • 无监督学习KMeans学习笔记和实例

    KMeans算法是一种简单的算法 能够快速 高效的对数据集进行聚类 一般只要通过几次迭代即可 KMeans可以作为一种聚类工具 同时也可以作为一种降维的方式进行特征降维 KMeans可以通sklearn cluster kmeans中进行调
  • 测试 开发 5 年从外包 18K 跳槽去字节 28K+12,啃完这份笔记你也可以

    软件测试是一个付出就有回报的工作 可能很多人会说软件测试就是吃青春饭 然而其他工作又何尝不是 没有哪一家公司养尸位素餐之人 大龄员工有被辞退的 也有没被辞退的 干任何职业 抱着一劳永逸的心态 在岗位上开始混的中青年 早就该辞了 粉丝小王转行
  • 测试方法——边界值法

    边界值测试方法 边界值方法是一种比较常用的测试方法 在很多软件测试中都会应用到 一 应用条件 只要有输入框输入数据的地方 就可以用边界值这一方法来测试 一般与等价类划分共同使用 找到有效数值和无效数值之间的分界点及其两边的点进行测试 二 测
  • Jmeter进阶使用指南-使用参数化

    Apache JMeter是一个广泛使用的开源负载和性能测试工具 在进行性能测试时 我们经常需要模拟不同的用户行为和数据 这时候 参数化就显得尤为重要 此文主要介绍如何在JMeter中使用参数化 什么是参数化 参数化是一种将静态值替换为动态
  • 深入理解HashMap和LinkedHashMap的区别

    简介 我们知道HashMap的变量顺序是不可预测的 这意味着便利的输出顺序并不一定和HashMap的插入顺序是一致的 这个特性通常会对我们的工作造成一定的困扰 为了实现这个功能 我们可以使用LinkedHashMap LinkedHashM
  • 【配电变电站的最佳位置和容量】基于遗传算法的最优配电变电站放置(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 基于遗传算法的最优配电变电站放置 为了实现
  • iseacms1.0漏洞复现

    iseacms1 0漏洞复现 作者 admin 时间 2021 06 29 分类 漏洞复现 Index php源码 文件包含 参数转义了 0 利用方式有限在网站目录有phpinfo php文件的前提下 payload为 index r te
  • 【十三】Nacos 服务注册和配置中心

    目录 Nacos 初识 Nacos 服务部署 注册中心服务部署 服务提供者注册到Nacos 服务消费者从Nacos获取服务 负载均衡 Nacos 服务详解 实列服务详情详解 Nacos 初识 Nacos Dynamic Naming and
  • Android 输入框的输入提示效果(AutoCompleteTextView)

    在一些体验较好的APP中 输入框输入时会有相应的提示 让人能够很快的通过点击提示进入下一步 这里 我就通过自己构思 实现了一个通过 SharedPreferences 保存的输入提示 demo 实现 1 实现一个 SharedPrefere
  • opencv项目实战(二)——文档扫描OCR识别

    一 项目描述 二 代码详解 2 1 预定义参数 2 2 辅助函数 2 3 文档矫正 2 4 文档识别 三 项目完整代码 一 项目描述 目的 将图片中的文档矫正 并识别文档内容 输入与输出 方法流程 核心思想 采用tesseract ocr进
  • 前端在vue2框架中导出PDF

    1 需求 导出具有页眉页脚 页码的Pdf 并且解决Pdf分割的问题 2 实现思路 该需求主要的难点在于分页的时候容易出现分割问题 并且要将页眉页脚加进去 实现的大概思路 1 先使用jsPDF html2canvas将页面可以导出 2 第一页
  • PHP 8突破性变化

    新的PHP首要版别PHP8估计将于2020年底发布 它现在正处于十分活泼的开发中 所以在接下来的几个月里 开发速度和开发进程或许会有很大的改动 在这篇文章中 我会罗列出PHP8中会发作的一些改动 新功能 性能改善和突破性改动 由于PHP8是
  • redis 二. string 应用场景及底层分析

    String 字符串类型 一 简单命令示例 二 java 操作示例 基础 setnx 与 getset 三 使用场景举例 统计点击次数 四 底层分析 SDS 嵌入式动态字符串 再次总结 一 简单命令示例 String字符串类型 一个key对