《Redis:NoSQL演进之路与Redis深度实践解析》(二)

简介: 《Redis:NoSQL演进之路与Redis深度实践解析》(二)

《Redis:NoSQL演进之路与Redis深度实践解析》(一)+https://developer.aliyun.com/article/1625021

Hyperloglog命令

网页的访问,一个人访问一个网站多次,但是还是算作一个人!

如果允许容错,那么一定使用 Hyperloglog

127.0.0.1:6379> pfadd mykey a b c d e f g h i j#创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey#统计mykey元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j z x c v b n m#创建第二组元素mykey2
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 9
127.0.0.1:6379> pfmerge mykey3 mykey mykey2合并两组mykey mykey2=》mykey3并集
OK
127.0.0.1:6379> pfcount mykey3#看并集的数量!
(integer) 15
127.0.0.1:6379>
Bitmap命令

位存储

统计用户信息、活跃,不活跃;登录,未登录;打卡,未打卡!两个状态的场景,都可以用Bitmaps!

Bitmap位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态!

127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> getbit sign 3#查看某一天是否打卡
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
127.0.0.1:6379> bitcount sign#统计这周的打卡记录
(integer) 3
127.0.0.1:6379>

以上是用,bitmap来记录周一到周日打卡!

周一:1 周二:0 周三:0 周四:1…

事务

Redis事务的本质:一组命令的集合!一个事务中所有命令都会被序列化,在事务执行过程中,会按照顺序执行!

Redis事务没有没有隔离级别的概念!

Redis单条命令式保存原子性的,但是事务不保证原子性!

Redis的事务:

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)
正常执行事务
127.0.0.1:6379> multi #开启事务,就跟点了一个鞭炮一样
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379>
放弃事务
127.0.0.1:6379> multi#开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard#取消事务
OK
127.0.0.1:6379> get k4#事务队列中的命令都不会被执行!
(nil)
127.0.0.1:6379>
编译型异常【代码有问题,命令有问题,事务中所有命令不再执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3#错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec#执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5#所有命令都不会被执行~
(nil)
127.0.0.1:6379>
运行时异常I/O(如果事务队列中存在语法错误,那么执行命令的时候,其他命令可以正常执行,错误命令抛出异常~)
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1#会执行失败~
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range#虽然第一条命令执行报错了,但是其他命令依旧正常执行!
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379>
总结

Redis支持乐观锁。

悲观锁

  • 很悲观,认为什么时候都会出现问题,无论做什么都会加锁

乐观锁

  • 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人去修改过这个数据
  • 获取Version
  • 更新的时候比较version

Jedis

Jedis是Redis官方推出的可以用Java操作Redis的连接开发工具!属于Java操作Redis的中间件

Ping测试
  • 连接数据库
  • 操作命令
  • 断开连接
<dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>5.1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.43</version>
        </dependency>
    </dependencies>
package com.linghu;
import redis.clients.jedis.Jedis;
/**
 * @author linghu
 * @date 2024/1/9 11:33
 */
public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        System.out.println(jedis.ping());
    }
}

常用API

所有的API命令都是上面对应学过的Redis命令,一个都没有变化!

  • String
  • List
  • Set
  • Hash
  • ZSet
package com.linghu;
import org.json.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
/**
 * @author linghu
 * @date 2024/1/9 11:33
 */
public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","linghu");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toString();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i=1/0;//代码抛出异常,执行失败!
            multi.exec();//执行事务!
        } catch (Exception e) {
            multi.discard();//放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();//关闭连接
        }
    }
}

SpringBoot整合

1、导入依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.7.10</version>
        </dependency>
2、配置连接
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试
package com.linghu;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
 * @author linghu
 * @date 2024/1/15 11:02
 */
@SpringBootTest(classes = Redis02SpringbootApplicationTest.class)
@RunWith(SpringRunner.class)
@ActiveProfiles(profiles = {"dev"})
public class Redis02SpringbootApplicationTest {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Test
    public void contextLoads(){
        redisTemplate.opsForValue().set("mykey","关注令狐");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
    }
}

Redis持久化

Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能。【主从设备中经常放到从设备上】

RDB(Redis DataBase)

什么是RDB?

RDB是Redis的一种数据持久化到磁盘的策略,是一种以内存快照形式保存Redis数据的方式。所谓快照,就是把某一时刻的状态以文件的形式进行全量备份到磁盘,这个快照文件就称为RDB文件,其中RDB是Redis DataBase的缩写。

AOF(Append Only File)

Redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。【不怎么使用】

AOF 保存的是 appendonly.aof文件

优点:

  • 每一次修改都同步,文件的完整会更加好!
  • 每秒同步一次,可能会丢失一秒的数据
  • 从不同步,效率最高的!

缺点:

  • 相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢!
  • Aof 运行效率也要比 rdb 慢,所以我们redis默认的配置就是rdb持久化

Redis发布订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发送者pub发送消息,订阅者sub接收消息。微信,微博,关注系统。

下图展示了频道channel1,以及订阅这个频道的三个客户端-client2、client5和client1之间的关系:

订阅端:

127.0.0.1:6379> subscribe linghu #订阅一个频道linghu
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "linghu"
3) (integer) 1
1) "message"
2) "linghu"
3) "hello,linghu"#收到的推送的消息

发送端:

127.0.0.1:6379> publish linghu "hello,linghu"#发布者发布消息到频道上
(integer) 1
127.0.0.1:6379>

哨兵模式

当主服务器宕机后,需要手动把一台从服务器切换成主服务器,这就需要人工干预,费时费力。这个时候提出了 哨兵模式

Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。

哨兵模式是一种特殊模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是 哨兵通过发送命令,等待Redis服务器响应,从而监控运行多个Redis实例。

哨兵的两个作用:

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵检测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他从服务器,修改配置文件,让他们切换主机。

一个哨兵对Redis服务器进行监控可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

主观下线

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用

客观下线

当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机

优点:

  • 哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
  • 主从可以切换,故障可以转移,系统的可用性就会更好
  • 哨兵模式就是主从模式的升级,手动到自动,更加健壮!

缺点:

  • Redis 不好啊在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦!
  • 实现哨兵模式的配置其实是很麻烦的,里面有很多选择!

Redis缓存穿透和雪崩

目前企业中对Redis缓存技术的运用,如下:

客户端首先请求Redis缓存,查询到了直接获取;查询不到就会去数据库中直接查询。

缓存穿透

用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透 。

解决方案
1、布隆过滤器

我们可以把布隆过滤器理解为一个set集合,我们可以通过add往里面添加元素,通过contains来判断是否包含某个元素。

客户端发送过来的数据会首先进行校验,不符合规则进行丢弃,从而避免了对底层存储层系统的查询压力。

2、缓存空对象

当存储层没有被命中(客户端查询不到数据),即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再进行访问这个数据将会从缓存中获取,保护了后端数据源。

以上两种方法会存在问题:

  • 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
  • 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响
缓存击穿

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞 。

解决方案
1、设置热点数据永不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题

2、加互斥锁

使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁 。

缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!

解决方案
1、Redis高可用

多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群

2、限流降级

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量

3、数据预热

在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

目录
相关文章
|
2天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1529 4
|
18小时前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
29天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
6天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
525 20
|
2天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
186 2
|
9天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
9天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
486 5
|
8天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
318 2
|
5天前
|
XML 安全 Java
【Maven】依赖管理,Maven仓库,Maven核心功能
【Maven】依赖管理,Maven仓库,Maven核心功能
200 2