Redis系列-1 Redis介绍

背景:

本文介绍Redis相关知识,包括Redis的使用、单线程机制、事务、内存过期和淘汰机制。后续将在《Redis系列-2 Redis持久化机制》中介绍Redis基于RDB和AOF的持久化机制;在《Redis系列-3 Redis缓存问题》中介绍缓存击穿、缓存穿透、缓存雪崩等问题;在《Redis系列-4 Redis集群》介绍主从、哨兵和Cluster集群相关的内容。

1.Redis介绍

C语言开发的、基于内存的、跨平台的非关系型数据库(Nosql)。Redis基于键值对存储结果,键只能为字符串,值的类型有:字符串String、散列 Hash、列表 List、集合 Set、有序集合 Sorted Set。因其基于内存而脱离了常规数据库的IO操作,从而具备高性能的读写(10W/s的读和 8W/s写)。核心处理使用单线程进行,避免了并发问题,以及减少了线程切换带来的性能开销。
redis作为内存数据库,可用于存放缓存数据。在高并发场景中存放热点数据,可以提高数据访问速度,也可以缓解数据库压力;除此之外,分布式锁也是Redis的一个应用场景。
Memcached 作为Nosql,常用于Redis做比较, 存在以下几个方面的差异:
[1] 效率: Redis基于单线程(memcached使用多线程),读写小数据时,Redis性能超过Memcached; 处理较大数据时,Memcached性能超过Redis
[2] 值的类型:redis支持多种数据类型; Memcached 仅支持字符串
[3] 持久化:Redis支持RDB和AOF两种持久化策略,宕机后可恢复数据; Memcached纯内存存储,不支持持久化
[4] 数据同步和分布式:redis基于持久化数据提供了实例之间的数据同步,Redis支持主从复制和数据分区,适用于构建分布式系统 而Memcached不支持
[5] 事务和Lua脚本: Redis支持事务和Lua脚本,可以执行复杂操作; Memcached 不支持
总之,Memcached适用于大量并发读和简单键值存储的场景,而Redis适用于复杂数据结构、数据有持久化或分布式要求的场景等。

2.Java使用方式

可借助docker快速安装redis, 准备redis.conf配置文件:

dir ./
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000


appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec


requirepass xxxxxxx
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-use-rdb-preamble yes
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes

使用docker安装redis环境:

# 准备配置文件和持久化数据存放路径
mkdir -p /path/to/redisdata
touch /path/to/redis.conf

# 启动docker容器
docker run --restart=always
-p 16379:6379
--name myredis
-v /path/to/redis.conf:/etc/redis/redis.conf 
-v /path/to/redisdata:/data 
-d redis:7.0.12 redis-server /etc/redis/redis.conf

运行后,docker的工作目录为/data,因此可在宿主机的/path/to/redisdata目录下查看持久化数据。
Java中存在以下三种使用Redis方式。

2.1 Jedis

Jedis提供了比较全面的Redis命令(同步的API)支持,本质上是通过socket直连Redis服务器,一个Jedis对象对应一个连接,因此Jedis对象本身是线程不安全的。以下通过案例介绍使用方式。
引入pom依赖:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version>
</dependency>

案例:

public class JedisDemo {
    public static void main(String[] args) {
        // Redis 服务器配置
        String host = "127.0.0.1";
        int port = 6379;
        String password = "123456";

        try (Jedis jedis = new Jedis(host, port)) {
            // 校验密码
            jedis.auth(password);
            // 操作1:添加键值对
            jedis.set("keyStr", "myStr");
            // 操作2:根据键取值
            String value = jedis.get("keyStr");
            System.out.println("键 keyStr 的值为: " + value);
   // 操作3:根据键删除键值对
            jedis.del("keyStr");
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

2.2 Redisson

Redission提供了很多分布式功能,如常见的分布式锁。
引入pom依赖:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.16.1</version>
</dependency>

案例:

public class RedissonDemo {
    public static void main(String[] args) throws InterruptedException {
        RedissonClient redisson = getRedissonClient();
        RLock lock = redisson.getLock("myLock");
        // 4. 加锁操作
        lock.lock();
        try {
            // 模拟耗时操作
            System.out.println("Begin...");
            Thread.sleep(1000 * 10);
            System.out.println("End.");
        } finally {
            // 5. 释放锁
            lock.unlock();
        }
        redisson.shutdown();
    }

    private static RedissonClient getRedissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
}

2.3 Lettuce

Lettuce基于Netty框架实现,使用非阻塞IO与Redis服务器通信,是一个高性能的Redis客户端。SpringBoot提供了Starter, 可以在SpringBoot项目中轻松引入。
引入pom依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>

在application.yml中添加配置信息:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    #Redis使用的数据库
    database: 0
    #连接超时事件毫秒
    timeout: 18000
    lettuce:
      pool:
        #连接池最大连接数
        max-active: 20
        #最大阻塞等待时间
        max-idle: 5
        #连接池最小空闲连接
        min-idle: 0

添加配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
 
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

案例:

@SpringBootTest
public class MyRedisTest {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testMyRedis() {
        String key = "key001";
        String value = "value001";
        redisTemplate.opsForValue().set(key,value);
        assertEquals(value, redisTemplate.opsForValue().get(key));
    }
}

上面通过redisTemplate对象通过不同方法封装了对不同数据类型的处理:
[1] opsForValue()中封装了操作String类型的方法;
[2] opsForList()中封装了操作List类型的方法;
[3] opsForSet()中封装了操作Set类型的方法;
[4] opsForHash()中封装了操作Hash类型的方法;
[5] opsForZSet()中封装了操作ZSet类型的方法.

3.单线程机制

Redis的核心工作由单线程完成,即使用一个线程完成对内存数据库的读写,由此保证了线程安全性。
虽然在Redis6.0(2020)后引入了多线程机制,但是仅将IO通信部分委托给多线程(IO多路复用),核心部分仍为单线程(后用核心线程表示)。由此,将redis核心线程从IO中解脱出来,专门进行内存数据库的读写,接收和发送由多线程这个管家来完成。
在这里插入图片描述

如上图所示,selector线程阻塞监听客户端的请求(修改和查询数据库),当有请求准备完成后,通知核心线程去操作数据库,操作的结果也通过建立好的通道返回给客户端。
上图本质上就是NIO+核心线程的模型,核心线程读写数据库。
再说一下为什么核心线程不用多线程,现在服务器都是多CPU、多核心,只要处理好线程安全问题,完全可以使用多线程处理核心业务(数据库读写):一方面,现有的Redis处理的读写速度是8~10W每秒,即使再堆CPU内核和线程也无法继续提高数据库操作速度,本质原因是短板效应——内存和IO的速度远低于CPU,尽管通过多路复用IO已经缓解了IO部分的效率问题;另一方面,线程的切换和线程安全问题可能会带来性能开销和复杂的程序设计问题。

4.Redis事务

Redis中指令都是单线程执行的,因此每个指令原子的、隔离的和持久性的。当需要将多个指令串起来执行时,需要用到Redis事务机制。
Redis中的事务属于伪事务,不具备原子性和一致性,仅具备隔离性和持久性。通过Redis事务API可以将多个命令串起来输入到Redis中,Redis会依次执行这些指令,当有指令执行失败时,不会停止和回滚,而是忽略异常继续执行下一个指令,从而丧失了原子性和一致性。多个客户端向Redis服务器发送事务指令时,Redis服务器会按照提交给Redis的顺序依次执行各个事务( Redis的事务可以保证一个事务内的命令依次执行而不被其他命令插入 ),从而保证了事务之间的隔离性和持久性。
Redis事务通过MULTI、EXEC、DISCARD三个指令来支持事务,
MULTI与DISCARD指令组合时,不会执行中间的指令(直接丢弃);MULTI与EXEC执行组合,将多个读写操作进行串联:

53:0>multi
"OK"
53:0>set "key1" "value1"
"QUEUED"
53:0>set "key2" "value2"
"QUEUED"
53:0>exec
1) "OK"
2) "OK"
3) "OK"
4) "OK"
5) "OK"
53:0>get key1
"value1"
53:0>get key2
"value2"
53:0>

在执行事务前,可以通过watch监听感兴趣的键是否发送变化。watch属于乐观锁,当事务命令被执行时,会比较watch关注的键是否在此阶段(watch执行到事务执行之间)是否发送了变化;如果有变化,则取消执行事务;否则会正常执行事务;最后(无论是否执行事务)取消watch监控。

5.数据过期

redis可以对数据添加过期时间,数据过期后会被redis删除。关于过期数据的删除, 存在以下三种策略:
[1] 立即删除:
当数据过期后,立即被redis删除;可以保证内存的最大新鲜度,即内存里的数据都是有效的。由于每条数据过期,都会执行删除操作,极大占用了CPU资源。
[2] 惰性删除
当数据过期后,不会立即删除,直到下次数据被访问时才会删除。该策略对CPU资源比较友好,但是内存中会存在大量已过期的数据。
[3] 定期删除
立即删除对内存友好而CPU不友好,而惰性删除对CPU友好而内存不友好,定期删除策略在二者中做了折中处理。
定期扫描过期数据,扫描到过期数据时立即删除,定期之外的数据仍采用惰性删除策略。

6.内存淘汰

Redis可通过maxmemory配置修改Redis占有内存的最大值。 当内存告急时(达到maxmemory)时,Redis通过内存淘汰机制保证服务继续运行。根据是否删除、键的选择(是否设置过期时间)、删除方式(随机、LFU、LRU)的组合,有如下8种淘汰策略(通过maxmemory-policy进行配置):
[1] noeviction:默认的配置,支持查看和删除数据,拒绝所有写入操作并返回客户端错误消息;

[2] volatile-ttl:在设置了过期时间的key中, 淘汰过期时间剩余最短的;
[3] volatile-lru: 在设置了过期时间的key中, 根据LRU淘汰;
[4] volatile-lfu: 在设置了过期时间的key中, 根据LFU淘汰;
[5] volatile-random: 在设置了过期时间的key种, 随机淘汰;

[6] allkeys-lru: 从所有 key 中使用 LRU 算法进行淘汰;
[7] allkeys-lfu: 从所有 key 中使用 LFU 算法进行淘汰;
[8] allkeys-random: 从所有 key中随机淘汰。

其中: LRU(Least Recently Used)表示最近最少使用,LFU(Least Frequently Used)表示最不经常使用。

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

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

相关文章

快速排序(java细节实现)

目录 快速排序: Hoare版: 挖坑法 快速排序的优化 快速排序的非递归实现 小结 从小到大排序 快速排序: 基本思想为&#xff1a;任取待排序元素序列中的某元素作为基准值&#xff0c;按照该排序码将待排序集合分割成两子序列&#xff0c;左子序列中所有元素均小于基准值&…

C++:AVL树

概念&#xff1a; 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查 找元素相当于在顺序表中搜索元素&#xff0c;效率低下。 如图所示&#xff0c;搜索二叉树不能面对右边的树&#xff0c;这种极端的情况&#xf…

[iOS]从拾遗到Runtime(上)

[iOS]从拾遗到Runtime(上) 文章目录 [iOS]从拾遗到Runtime(上)写在前面名词介绍instance 实例对象class 类对象meta-class 元类对象为什么要有元类&#xff1f; runtimeMethod(objc_method)SEL(objc_selector)IMP 类缓存(objc_cache)Category(objc_category) 消息传递消息传递的…

【how2j JQuery部分】课后题答案及相关笔记

练习题 <script src"jquery.min.js"></script><script>$(function(){$(tr:odd).css({"background-color":"#f8f8f8"});}); </script> <style> table{border-collapse:collapse;width:90%;} tr{border-bottom-sty…

安捷伦E4991A美国原装二手KEYSIGHT、E4990A阻抗分析仪

商品品牌&#xff1a;安捷伦Agilent/是德KEYSIGHT 商品型号&#xff1a;E4990A 商品价格&#xff1a;面议或电议 商品详情&#xff1a; Agilent E4990A阻抗分析仪&#xff0c;20 Hz 至 10/20/30/50/120 MHz 主要特性与技术指标 5 种频率选件&#xff1b;20 Hz 至 10/20/30/50/1…

C++学习————第十天(string的基本使用)

1、string 对象类的常见构造 (constructor)函数名称 功能说明&#xff1a; string() &#xff08;重点&#xff09; 构造空的string类对象&#xff0c;即空字符串 string(const char* s) &#xff08;重点&#xff09;…

Java_从入门到JavaEE_11

一、抽象类及抽象方法 1.认识抽象类及抽象方法 应用场景&#xff1a;当一个方法必须在父类中出现&#xff0c;但是这个方法又不好实现&#xff0c;就把该方法变成抽象方法&#xff0c;交给非抽象的子类去实现 实例&#xff1a; //抽象类 public abstract class 类名{//抽象方…

Ansible----playbook模块之templates模块、tags模块、roles模块

目录 引言 一、templates模块 &#xff08;一&#xff09;关键信息 &#xff08;二&#xff09;实际操作 1.定义主机组 2.设置免密登录 3.分别建立访问目录 4.定义模板文件 5.创建playbook文件 6.执行剧本 7.验证结果 二、tags模块 &#xff08;一&#xff09;创建…

stm32_RTC_2_HAL——stm32CudeMX

介绍 RTC&#xff08;实时时钟&#xff09;不仅仅提供计数功能&#xff0c;它是一个完整的时钟和日历模块&#xff0c;用于提供日期和时间信息。RTC 能够提供年、月、日、星期、时、分、秒等时间信息&#xff0c;并且通常具有闹钟功能&#xff0c;可以用于定时唤醒或触发事件。…

Qt | QLineEdit 类(行编辑器)

01、上节回顾 Qt | QComboBox(组合框)02、QLineEdit 1、QLineEdit 类是 QWidget 类的直接子类,该类实现了一个单行的 输入部件,即行编辑器,见右图 2、验证器(QValidator 类)和输入掩码简介:主要作用是验证用户输入的字符是否符合验证器 的要求,即限制对用户的输入,比…

详细介绍ARM-ORACLE Database 19c数据库下载

目录 1. 前言 2. 获取方式 2.1 ORACLE专栏 2.2 ORACLE下载站点 1. 前言 现有网络上已有非常多关于ORACLE数据库机下载的介绍&#xff0c;但对于ARM平台的介绍不多&#xff0c;借此机会我将该版的下载步骤做如下说明&#xff0c;希望能够一些不明之人提供帮助和参考 2. 获…

【STM32G474】利用Cpp编写STM32代码后,Cubemx修改配置后代码报错147个error,如何处理?

问题描述 打开Cubemx&#xff0c;添加TIM7用于定时器精准延时&#xff0c;生成代码后&#xff0c;Keil提示有147个error。 之前是Cubemx是没有问题的&#xff0c;是利用Cpp编写stm32&#xff08;将Keil改为Version6&#xff09;后才导致Cubemx配置失败&#xff1a; debug成功…

[学习笔记]CyberDog小米机器狗 开发学习

1、机器狗本身是UbuntuROS2系统 2、控制机器人只需要了解lcm和Ros topic通讯 3、传感器数据&#xff08;包括一些imu(/imu)、激光雷达(/scan)&#xff09;会进行topic的一个广播。 仿真环境通信接口&#xff1a; -命令输入(见后续运控说明) 运控lcm数据接口 Motion man…

Gmail邮箱怎么注册?2024年完整指南(包含跳过手机号验证)

一、为什么要注册Gmail邮箱&#xff1f; 全球通用性&#xff1a;Gmail是一个全球性的邮件服务平台&#xff0c;被广泛认可和信赖。因为客户对于Gmail的接受度高&#xff0c;无需担心邮件被自动标记为垃圾邮件。 整合营销工具&#xff1a;通过Gmail账号&#xff0c;你可以轻松…

CleanMyMac X 4.15.3 版本发布

CleanMyMac X 4.15.3 版本发布&#xff0c;一款苹果 macOS 系统好用的伴侣软件&#xff0c;其包含 1.一键深度清理。2.系统垃圾专清。3.大/旧文件专清。4.系统提速。5.性能悬浮窗。6.恶意软件防护。7.隐私保护。8.软件卸载器。9.软件更新器等 9 大功能&#xff0c;为您的苹果电…

Flask-HTTP请求、响应、上下文、进阶实验

本节主要目录如下&#xff1a; 一、请求响应循环 二、HTTP请求 2.1、请求报文 2.2、Request对象 2.3、在Flask中处理请求 2.4、请求钩子 三、HTTP响应 3.1、响应报文 3.2、在Flask中生成响应 3.3、响应格式 3.4、Cookie 3.5、session&#xff1a;安全的Cookie 四、…

[公开课学习]台大李宏毅-自注意力机制 Transformer

自注意力机制 存在一些问题&#xff0c;将vector set/sequence作为input&#xff0c;例如&#xff1a; 文字处理&#xff1a;将文字用one-hot表示&#xff0c;或者向量空间的向量表示&#xff0c;然后进行翻译任务等语音处理&#xff1a;25ms音频作为一个向量&#xff0c;10m…

初识C++ · 模板初阶

目录 1 泛型编程 2 函数模板 3 类模板 1 泛型编程 模板是泛型编程的基础&#xff0c;泛型我们碰到过多次了&#xff0c;比如malloc函数返回的就是泛型指针&#xff0c;需要我们强转。 既然是泛型编程&#xff0c;也就是说我们可以通过一个样例来解决类似的问题&#xff0c…

pytorch基础: torch.unbind()

1. torch.unbind 作用 说明&#xff1a;移除指定维后&#xff0c;返回一个元组&#xff0c;包含了沿着指定维切片后的各个切片。 参数&#xff1a; tensor(Tensor) – 输入张量dim(int) – 删除的维度 2. 案例 案例1 x torch.rand(1,80,3,360,360)y x.unbind(dim2)print(&…

gitlab集群高可用架构拆分部署

目录 前言 负载均衡器准备 外部负载均衡器 内部负载均衡器 (可选)Consul服务 Postgresql拆分 1.准备postgresql集群 手动安装postgresql插件 2./etc/gitlab/gitlab.rb配置 3.生效配置文件 Redis拆分 1./etc/gitlab/gitlab.rb配置 2.生效配置文件 Gitaly拆分 1.…
最新文章