ShuangChenYue ShuangChenYue
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
  • 基础

  • Mysql

  • Redis

    • Redis总结
    • Redis小记
      • 三种缓存读写(更新)策略
        • 旁路缓存策略
        • 读写穿透策略
        • 写回策略(异步写入)
        • 三者谁的线程安全度更高?
  • 数据库
  • Redis
霜晨月
2023-07-31
目录

Redis小记

# 在 redis 中,存储序列化后的对象,为什么 String 相比较于 Hash 存储更节省内存?

在内存存储方面,String 相比于 Hash 存储更节省内存的原因是因为 String 使用了字符数组(char[])来存储字符串的内容,而 Hash 存储通常是使用一个哈希表来存储键值对。

  1. String 使用字符数组存储:在 Java 中,String 对象使用字符数组来存储字符串的内容,每个字符占用两个字节(16 位)。对于较短的字符串,字符数组可以直接存储整个字符串内容,没有额外的开销。
  2. Hash 存储使用哈希表:哈希表是一种数据结构,它由一系列的桶(bucket)组成,每个桶中存储一个键值对。对于较小的哈希表,每个桶可能只存储一个键值对,这样会有一定的空间开销。而对于较大的哈希表,可能会有较多的桶未被使用,也会造成一定的空间浪费。

因此,对于存储较短的字符串或者只包含单个字符的字符串,使用 String 存储相对更节省内存。而对于存储大量的键值对,使用 Hash 存储可能更合适,因为它可以支持快速的查找和插入操作。

# 利用 SETNX key value 命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。为什么?

  1. 还应该加个过期时间,比如 SET lock_key unique_value NX PX 10000
  2. 使用更成熟、稳定的分布式锁算法,比如基于 Redlock(红锁)或者 Redisson 等开源分布式锁实现库。

红锁算法要求满足两个条件:如果有超过半数的 Redis 节点成功的获取到了锁,并且总耗时没有超过锁的有效时间,那么就是加锁成功

# 三种缓存读写(更新)策略

# 旁路缓存策略

写:

  • 先更新 db
  • 然后删除 cache

读:

  • 先从 cache 中读取数据,命中就返回
  • cache 中读取不到的话,就从 db 中读取数据返回,然后(db)再把数据写入 cache 中

问题:

1、在写数据的过程中,可以先删除 cache,后更新 db 么?

答:不可以,因为这样会导致数据库和缓存数据不一致的问题。

举例:请求 1 先写数据 A,请求 2 随后读数据 A 的情况。

假设请求 1 先把 cache 中的 A 数据删除 -> 请求 2 从 db 中读取数据并写入 cache 中 -> 请求 1 再把 db 中的 A 数据更新,此时就会产生数据库和缓存数据不一致的问题。

db 的是新数据,cache 的是旧数据。

2、在写数据的过程中,先更新 db,后删除 cache 就没有问题了吗?

答:可能会有问题,但出现的概率非常小,因为【缓存】的写入速度比【数据库】的写入速度快很多。

举例:

请求 A 更新完数据库,但还未删除缓存,此时请求 B 命中了缓存并返回,就导致了数据不一致。

出现的概率低:是因为缓存的写入非常快,中间的时间差非常短,通常只有几毫秒或者几十毫秒。

缺陷:

1、首次请求数据一定不在 cache 的问题

解决方法:将热点数据提前放入 cache 中。

2、写操作比较频繁的话导致 cache 中的数据会被频繁被删除,会影响缓存命中率

解决方法:

  • 更新数据的时候同样更新缓存,只是在更新缓存前需要先加一个分布式锁,从而保证线程安全。(强一致场景)
  • 给缓存加一个较短的过期时间。(可以短暂允许数据不一致场景)

为什么会线程不安全?

因为在多线程或分布式环境下,对数据的并发更新可能会导致线程不安全的情况。当多个线程同时尝试更新缓存的数据时,如果没有进行同步控制,就会产生以下问题:

  1. 竞态条件:多个线程同时读取缓存数据,并在此基础上进行更新,但由于读取和写入操作不是原子性的,可能导致数据的不一致性。
  2. 脏数据:在并发情况下,多个线程同时更新缓存,其中一个线程的更新可能会覆盖其他线程的更新,导致数据出现错误。
  3. 缓存不一致:由于缓存的失效机制或其他原因,缓存中的数据可能落后于数据库中的数据,这样读取到的数据就会是旧数据。

# 读写穿透策略

原则:应用程序只和缓存交互,不再和数据库交互。

比较少见,因为常用的分布式缓存组件都不提供【写入数据库】和【自动加载数据库中的数据】的功能。

写:

  • 先查 cache,如果数据不存在,(缓存组件)直接更新 db,然后返回。
  • 如果数据存在,则先更新 cache,然后缓存组件同步更新 db。

读:

  • 从 cache 中读取数据,读取到就返回。
  • 读取不到,就由缓存组件先从数据库查数据并写入缓存组件,最后返回响应。

# 写回策略(异步写入)

读写的情况

与【读写穿透策略】相似,都是由 cache 服务(组件)来负责 cache 和 db 的读写。

不同点

原则:只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。

Write Back(写回)策略:在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。

适合【写多】的场景。(比如浏览量,点赞量)

# 三者谁的线程安全度更高?

读写穿透策略。

Write Through 策略在写操作时,会先更新数据库,然后再更新缓存。这样做的好处是确保了数据库和缓存的数据始终保持一致,避免了数据不一致的问题。在并发情况下,当多个线程同时写入数据时,由于先更新数据库再更新缓存,可以保证不会出现数据不一致的情况。

另一方面,Write Back(写回)策略在写操作时,只更新缓存而不更新数据库,然后通过批量异步更新的方式来更新数据库。虽然 Write Back 策略在高并发场景下可以提高写入性能,但是由于在更新缓存时不更新数据库,可能会导致数据库和缓存之间的数据不一致。这种情况下,如果多个线程同时写入数据,可能会出现数据覆盖或者丢失的情况,从而降低了线程安全度。

上次更新: 2024/6/3 14:54:44
Redis总结

← Redis总结

Theme by Vdoing | Copyright © 2023-2024 霜晨月
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式