Redis晋级之路!!

发布于:2024-06-23 ⋅ 阅读:(57) ⋅ 点赞:(0)

本节pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>my-code-note-for-java</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>


    <modelVersion>4.0.0</modelVersion>
    <groupId>com.augus</groupId>
    <artifactId>springbootAddRedis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootAddRedis</name>
    <description>springbootAddRedis</description>


    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.2</spring-boot.version>
    </properties>
    <dependencies>
        <!--SpringBoot与Redis整合依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!--guava Google 开源的 Guava 中自带的布隆过滤器-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

        <!--jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.3.1</version>
        </dependency>

        <!--lettuce-->
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>6.2.1.RELEASE</version>
        </dependency>


        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.2.3</version>
        </dependency>
        <!--persistence-->
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0.2</version>
        </dependency>


<!--    springboot  demo     -->

        <!--        springboot        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--        mybatis        -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!--        mybatis-plus        -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

        <!--        lombok        -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--        swagger        -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>




    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.example.springboot.SpringbootApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

什么是Redis

Redis是一个功能强大、高性能的内存数据库。
Redis支持持久化、事务、管道、发布订阅、主从控制、集群。
通常,使用Redis进行数据缓存、会话存储、实时分析、分布式锁等功能。

Redis十大数据结构

① String

基本操作
set/setnx/setex/setpx/setexat/setpxat/setkeepttl
mset/mget:一次设置/获取多个kv
setrange/getrange:设置/获取key的0-x的索引
incr/decr:如果value为数字+1/-1
append: 向value中追加内容

进阶操作
命令合并、分布式锁

②List

List底层是双端链表,对两段的操作性能很高,通过索引下标操作中间的节点性能会较差。List容量是232-1,大概是40多亿,主要功能是push/pop,一般用于栈、队列、消息队列等场景。

**应用场景:**微信公众号,订阅的公众号发布的新文章会插入到自己的list中 lpush likearticle:用户id 公众号id

基本操作
lpush:将一个或多个值插入到列表的头部。如果键不存在,会创建一个空列表并进行插入操作。多个值按从左到右的顺序依次插入到列表头部。
rpush:将一个或多个值插入到列表的尾部。如果键不存在,会创建一个空列表并进行插入操作。多个值按从左到右的顺序依次插入到列表尾部。
lrange:返回列表中指定范围内的元素。start 和 stop 是索引,索引从 0 开始,负索引表示从列表末尾开始计数(-1 表示最后一个元素)。
lpop:移除并返回列表的第一个元素(头部)。
rpop:移除并返回列表的最后一个元素(尾部)。
lindex:通过索引获取列表中的元素。索引从 0 开始,负索引表示从列表末尾开始计数(-1 表示最后一个元素)。
rindex
llen:返回列表的长度。
lrem:从列表中移除前 count 个值等于 value 的元素。count 的值可以是正数、负数或零:
ltrim:对列表进行修剪,只保留指定范围内的元素,删除其余的元素。start 和 stop 是索引。
linsert before/after:在列表中指定元素的前面或后面插入一个新元素。如果指定的元素不存在,则不进行任何操作。
del:删除指定的键及其关联的值。

Redis持久性

AOF和RDB

AOF会把每个写操作都写入日志中,Redis重启时,可以通过重新执行日志文件中的写操作来恢复数据。
RDB通过将某个时间点的快照存储起来,Redis重启时,可以通过快照文件来实现数据的恢复。
相比之下,AOF在数据一致性上优于RDB,但是RDB在恢复速度、占用磁盘空间、性能方面都优于AOF。

如何使用AOF和RDB,纯缓存模式

对于持久性的关闭,我们都可以通过修改配置文件的方法来实现。
在将两种持久化都关闭的情况下,我们仍然可以使用命令的方法,来让两种方法对redis的数据进行保存。

Redis事务

Redis事务是不保证原子性的。
但是Redis可以将事务中的操作全部抛弃。
Redis的事务规则是,如果事务中有报错则全部不执行,如果事务中没有报错就全部执行,但是错误的操作不会阻碍后续操作的执行。
同时,Redis还提供了watch的操作,可以监控某个key,使得key在watch期间不能被修改。

Redis管道

是为了解决每次操作都需要经历往返的RTT所造成的性能消耗问题。
管道就是一次性执行多条命令的操作,redis只需要进行一次回复。

Redis订阅发布

订阅一个channel,同一个channel可以有多个订阅者。
channel发布信息的时候所有的订阅者都可以收到消息,但是由于channel只负责发送消息,也不管消息是否安全到达,也不会保存发送的消息,所以基本没什么应用场景。

Redis主从复制

redis的主从复制是master以写为主,slave以读为主,当master数据发生改变的时候,自动将新的数据异步同步到其他slave。

Redis哨兵

功能
主从监控:监控主从redis库运行是否正常
消息通知:哨兵可以将故障转移的结果发送给客户端
故障转移:如果master异常,则会进行主从切换。将其中一个slave作为新的master
配置中心:客户端通过连接哨兵来获取当前redis服务的主节点地址

哨兵监听
哨兵的配置来说,建议配置奇数个的三个以上的哨兵节点,所有哨兵节点都去监听master。当主节点故障之后,哨兵节点进行投票选举新的master,票数多的slave当选新的master。

选举规则
一般来说选举通过偏移量决定,即选取同步了新操作越多的slave被选取。

选举后的操作
选举结束后,哨兵会自动的修改配置文件,将异常的master降级为slave。

Redis集群

集群是支持多个master的场景的,同时集群有自适应的哨兵设计,所以不需要自己设计哨兵了。
集群的设计,开启多个redis实例,通过命令构建集群中的关系,设置主从关系。

槽位slot
redis设计16384个槽位,通过CRC16算法进行一致性哈希。

为什么是16384个槽位
选择16384个槽位,而不是更多的槽位,主要原因是
① 16384个槽位基本可以所有场景下的需求,这个数量的槽位可以满足1000个redis节点进行分配。作者认为一般不会超过1000个redis节点,如果超过1000个redis节点,会导致网络过于拥堵,网络也会难以管理。
② 如果选择65536个槽位,每一次心跳包的消息头大小到达8KB的大小,发送的心跳包就太大了。
③ 同时也是为了适应CRC16算法。

CRC16算法
这个算法是为了解决分布式缓存数据变动和映射问题而提出的。
CRC16希算法具有很好的容错性和拓展性。
但是CRC16算法可能会出现数据倾斜的问题(即当redis节点较少的时候,数据会跟多的堆积到某一台服务器上)


Redis高级

Redis的多线程

Redis的多线程是对于网络I/O请求进行多线程的操作,但是Redis主线程仍然采用单线程的方式。

好处
对Redis主线程采用单线程的方式,可以避免多线程情况下需要加锁保证线程安全而导致模型实现复杂的问题。

为什么Redis单线程仍然这么快

基于内存的操作
数据结构简单
多路复用和非阻塞的I/O
避免了上下文切换:单线程模型可以避免上下文切换和多线程的竞争问题,而且单线程不会出现死锁的情况。

BigKey问题

当key大于10KB就算作BigKey,String最大为512MB

危害
内存不均、集群迁移困难
超时删除
网络流量阻塞

如何删除BigKey
一般采用渐进式的方法删除

缓存双写一致性

如果qps小于1000的情况下,我们可以使用先更新数据库,再更新缓存的方法来达到目的,读取数据的时候可以使用双检加锁的方式。

当qps很大的时候,使用alibaba开源中间件canal。
canal通过读取mysql的binlog来获取数据库的更新,如果数据库更新了那么canal可以及时同步到缓存中。


网站公告

今日签到

点亮在社区的每一天
去签到