Redis笔记(4)

目录

事务

管道

发布/订阅(了解)

Redis复制(replica)

哨兵(sentinel)监控

集群分片

集群算法-分片-槽位slot:

配置Redis集群:

集群读写:

节点从属调整

主从扩容

主从缩容

注意:

Springboot整合redis

springboot连接redis常见问题

Jedis使用​编辑

lettuce使用

集成redistemplate

redistemplate连接单机:

redistemplate连接集群:


事务

redis的事务没有回滚操作

 常用命令:

1.discard:取消事务

2.exec:执行事务中的命令

3.multi:标记事务开启

4.unwatch:取消watch命令对使用key的监视

5.watch key [key…]:监视一个或多个key,如果在事务执行前这个key被其他命令修改,事务被打断

 正常执行流程:multi,cmd1,cmd2,……,exec

放弃事务流程:multi,cmd1,cmd2,……,discard

一条出错全部放弃流程(语法出错如忘记写value:set k1):multi,cmd1,出错cmd,exec

出错命令不影响其他命令流程(运行时异常,逻辑有问题):multi,set email a@qq.com,incr email,exec

watch监控流程:先监控在开启事务multi

  

管道

发布/订阅(了解)

Redis复制(replica)

就是主从复制,master以写为主,slave以读为主,当master数据变化时,自动将新的数据异步同步到其他slave数据库。

功能:1.读写分离 2.容灾恢复 3.数据备份 4.水平扩容支撑高并发      

配置:slave数据库配置master数据库,如果master有密码,slave要配置masterauth来设置校验密码,否则master拒绝访问请求。

操作:

配置文件:

指定端口: 

从机配置主机:

启动redis后查看:

哨兵(sentinel)监控

主从复制中当master数据库挂了就无法再往redis中进行写操作,因为slave集群中无法选举出一个新的master,所以引入了哨兵监控。

哨兵的作用:

1、监控redis的运行状态,包括master和slave

2、当master宕机了,能自动将一个slave切换为新的master

sentinel.conf:

redis数据库和redis哨兵是两种不同的程序

启动命令:

        启动redis数据库:redis-server /redis6379.conf(配置文件) 默认端口:6379

        启动redis哨兵:redis-sentinel /sentinel26379.conf(配置文件) 默认端口:26379

                             或者:redis-server /sentinel26379.conf --sentinel

哨兵运行流程:正常运行-》sdown主观下线(存在哨兵没有在规定时间内收到master的心跳)-》odown客观下线(所有哨兵讨论后(达到quorum)确认没有收到心跳)-》在哨兵中选择leader哨兵-》由leader通过故障切换流程选出新的master。 

选举leader哨兵的方法:Raft算法

        Raft算法基本思路:先到先得,即一轮选举中,哨兵a向b发送成为leader的申请,如果b未同意过其他哨兵,则同意a为leader。

选举新master的原理图:

哨兵的使用建议:

集群分片

哨兵监控+主从复制 并不能保证数据零丢失,因此有了集群分片

集群 与 哨兵+主从 是替换关系

集群算法-分片-槽位slot:

slot槽位映射一般使用3中办法:

        1、哈希取余分区:

                缺点:扩容后或者某个节点挂了容易导致之前存储key的大规模的混乱

        2、一致性哈希算法分区:

                 优点:容错性 , 扩展性

                 缺点:数据倾斜问题

        3、哈希槽分区。

Redis集群不能保证数据强一致性:当客户端在master完成写操作后,如果master还没来得及向slave复制数据就突然挂了,那么当slave被切换为master时,数据就会不一致。

配置Redis集群:

        1.分别为每个redis数据库上创建一个对应的配置文件: 

        2.文件内容:

        3.以集群方式启动每个redis服务:

        4.建立主从关系:这个命令随机设置主从关系

        5.可以依次打开每个redis检查主从关系:

        6.也可以使用 cluster nodes 查看集群关系:

集群读写:

key会先计算:crc16(key)%16384 得到 槽位, 对应的槽位要到相应的redis节点

如果槽位不在当前redis节点会报错

解决方法:防止路由失效加参数-c,这样会将key存放到相应的redis节点

        -c:以集群模式运行客户端 

节点从属调整

redis集群中master挂了后,会自动将slave提升为master,原来的master恢复后变为slave,可以使用下面的命令切换master和slave。

命令:cluster failover
redis-cli 的从机将 与其主机调换角色。
该命令只能在群集slave节点执行,让slave节点进行一次人工故障切换。

主从扩容

用哪个redis启动的集群,在进行扩容的时候,指定的ip和端口,必须为那个redis

将启动的redis数据库加到集群中:

检查集群情况:

重新分派槽位:

设置分派数量:(可以设置为 16384 / master数)

再次检查集群情况:

为新的master分配slave:

主从缩容

1.先清除slave

 slave成功删除

2.把槽位重新分派

 

检查集群情况:要删除的master变成了一个master的slave

3.删除master

检查集群情况:

注意:

Springboot整合redis

就像java要访问mysql需要一些中间操作:jdbc,jdbctemplate,mybatis;

java访问redis也需要,一般使用3种:jedis,lettuce,RedisTemplate(推荐)

jedis存在一些安全问题,lettuce能力比jedis更强

RedisTemplate就是spring对lettuce的封装。

springboot连接redis常见问题

        注释bind配置或设置为bind 0.0.0.0

        保护模式设置为no

        Linux系统的防火墙设置

        redis的ip地址和密码是否正确

        记得写访问redis的服务器端口号和auth密码

Jedis使用

操作步骤:

创建一个java项目:redis-project

导入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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qiu</groupId>
    <artifactId>redis-project</artifactId>
    <version>1.0-SNAPSHOT</version>
<!--    设置为springboot工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.10</version>
        <relativePath/>
    </parent>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
<!--        springboot 通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!--        jedis依赖-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.3.1</version>
        </dependency>
<!--        通用基础配置-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>
</project>

 编写配置文件:application.properties

server.port=7777
spring.application.name=redis_study

 入门程序:



import redis.clients.jedis.Jedis;
import redis.clients.jedis.args.ListPosition;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class JedisDemo {
    public static void main(String[] args) {
        //通过ip和端口获取redis的connection
        Jedis jedis = new Jedis("192.168.234.127",6379);
        //指定redis的密码
        jedis.auth("123456");
        //测试是否连通
        jedis.flushDB();
        System.out.println("jedis.ping() = " + jedis.ping());
        //keys  *
        Set<String> keys = jedis.keys("*");
        System.out.println("keys = " + keys.toString());
        //string
        System.out.println("============string===============");
        jedis.set("k1","v1");
        String s = jedis.get("k1");
        System.out.println("s = " + s);
        jedis.mset("k2", "v2", "k3", "v3");
        List<String> mget = jedis.mget("k1", "k2", "k3");
        System.out.println("mget = " + mget.toString());

        //列表
        System.out.println("============列表===============");
        jedis.lpush("l1","l11","l12","l13");
        List<String> l1 = jedis.lrange("l1", 0, -1);
        System.out.println("l1 = " + l1.toString());
        jedis.linsert("l1", ListPosition.AFTER,"l11","l11.5");
        List<String> l11 = jedis.lrange("l1", 0, -1);
        System.out.println("l1 = " + l11.toString());

        //hash
        System.out.println("============hash===============");
        jedis.hset("h1","name","qiu");
        String hget = jedis.hget("h1", "name");
        System.out.println("hget = " + hget);
        Map<String,String> map = new HashMap<String,String>();
        map.put("name","liu");
        map.put("age","23");
        map.put("addr","广东");
        jedis.hset("h2",map);
        List<String> hmget = jedis.hmget("h2", "name", "addr", "age");
        System.out.println("hmget = " + hmget.toString());

        //set
        System.out.println("============set===============");
        jedis.sadd("set1","setv1","setv2","setv3");
        Set<String> set1 = jedis.smembers("set1");
        System.out.println("set1. = " + set1.toString());
        long set11 = jedis.scard("set1");
        System.out.println("set11 = " + set11);
        jedis.srem("set2","setv1");
        jedis.sadd("set2","setv1","set2v2");
        Set<String> set2 = jedis.smembers("set2");
        System.out.println("set2 = " + set2.toString());
        Set<String> sdiff = jedis.sdiff("set1", "set2");
        System.out.println("sdiff = " + sdiff.toString());


        //zset
        System.out.println("============zset===============");
        jedis.zadd("z1",1d,"v1");
        jedis.zadd("z1",2d,"v2");
        jedis.zadd("z1",0d,"v3");
        List<String> z1 = jedis.zrange("z1", 0, -1);
        System.out.println("z1 = " + z1.toString());
    }
}

lettuce使用

导入lettuce依赖


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


import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class LettuceDemo {
    public static void main(String[] args) {
        //使用构建器链式编程来builder RedisURI
        RedisURI uri = RedisURI.builder().redis("192.168.234.127").withPort(6379)
                .withAuthentication("default","123456").build();
        //创建连接客户端
        RedisClient client = RedisClient.create(uri);
        StatefulRedisConnection connect = client.connect();

        //通过connect创建操作命令
        RedisCommands cmd = connect.sync();

        //命令
        cmd.set("k1","v1");

        //关闭资源
        connect.close();
        client.shutdown();


    }
}

集成redistemplate

redistemplate连接单机:

添加依赖:


<!--        springboot与redis整合依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

spring与redis整合包:

spring-boot-starter-data-redis

swagger:调用微服务接口的工具

配置文件修改:

server.port=7777
spring.application.name=redis_study
# logging
logging.level.root = info
logging.level.com.qiu.redis = info
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n

# swagger
spring.swagger2.enadble=true

spring.mvc.pathmatch.matching-strategy=ant_path_matcher

# redis 单机
spring.redis.database=0

spring.redis.host=192.168.234.127
spring.redis.port=6379
spring.redis.password=123456
#连接池
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

编写配置类:

swagger配置类:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Value("${spring.swagger2.enabled}")
    private Boolean enadbled;

    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(enadbled)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.qiu.redis"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("springboot使用swagger构建api接口文档  "+ DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now()))
                .description("springboot+redis整合")
                .version("1.0")
                .build();
    }
}

编写控制层和业务层代码:

service:


@Service
@Slf4j
public class OrderService {
    private static final String ORDER_KEY = "ord:";
    @Resource
    private RedisTemplate redisTemplate;

//    添加京东订单
    public void addOrder(){
        int keyId = ThreadLocalRandom.current().nextInt(1000)+1;
        String serialNo = UUID.randomUUID().toString();

        String key = ORDER_KEY+keyId;
        String value = "京东订单"+serialNo;

        //string类型
        redisTemplate.opsForValue().set(key,value);
        log.info("********key:{}",key);
        log.info("********value:{}",value);
    }
    //获取订单
    public String getOrderById(Integer keyId){
        return (String) redisTemplate.opsForValue().get(ORDER_KEY+keyId);
    }

}

controller:


@RestController
@Slf4j
@Api(tags = "订单接口")
public class OrderController {

    @Resource
    private OrderService orderService;

    @ApiOperation("新增订单")
    @PostMapping(value = "/order/add")
    public void addOrder(){
        orderService.addOrder();
    }

    @ApiOperation("按照keyid查询订单")
    @PostMapping("order/{keyId")
    public void getOederById(@PathVariable Integer keyId){
        String orderById = orderService.getOrderById(keyId);
        System.out.println("orderById = " + orderById);
    }


}

启动服务

访问:

但此时访问redis数据库:

序列化问题

解决方法1:不使用RedisTemplate,使用他的子类StringRedisTemplate

解决方法2:配置springboot项目中redis配置类

@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;
    }
}

redistemplate连接集群:

这时可以正常操作,但如果redis服务有一个宕机了,springboot无法动态感知集群的最新消息(即redis已经将slave升级为master但springboot不知道)

 第一种方法的操作:(不推荐)

第三种方法操作:

 在application配置文件中添加

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

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

相关文章

模拟面试题

一、IO多路复用的原理 将多个阻塞任务的文件描述符&#xff0c;统一放到一个检测容器中&#xff0c;然后用一个阻塞函数进行管理&#xff0c;如果检测容器有一个或多个文件描述符对应的事件产生&#xff0c;就会解除阻塞&#xff0c;进而去执行相应的函数。 二、实现IO多路复用…

数据表练习

思维导图 面试题答问1、IO多路复用的引入目的和原理 目的&#xff1a;在有操作系统时&#xff0c;可以用多线程和进程完成任务并发执行&#xff0c;没有操作系统的情况下可以使用IO多路复用技术来进行任务并发。 原理&#xff1a;将多个阻塞任务的文件描述符统一放到一个检查容…

大屏动效合集更更更之实现百分比环形

实现效果 参考链接&#xff1a; https://pslkzs.com/demo/pie/demo1.php 写在最后&#x1f352; 源码&#xff0c;关注&#x1f365;苏苏的bug&#xff0c;&#x1f361;苏苏的github&#xff0c;&#x1f36a;苏苏的码云

MySQL索引的创建与基本用法

MySQL索引 MySQL索引是一种数据结构&#xff0c;用于提高查询数据的效率。MySQL索引可以被看作是数据库表的“目录”。就像书籍的目录帮助我们快速找到特定章节的位置一样&#xff0c;数据库索引帮助数据库快速找到特定数据记录的位置。 MySQL索引的类型与创建方法 MySQL索引…

如何优化前端项目的 SEO

在当今数字化时代&#xff0c;网站对于企业的重要性不言而喻。然而&#xff0c;一个优秀的网站如果在搜索引擎中排名靠后&#xff0c;将无法吸引到足够的流量和用户。因此&#xff0c;优化前端项目的SEO已经成为了网站拓展业务、提升品牌知名度的必经之路。 响应式设计与移动优…

Android14 - AMS之Activity启动过程(1)

Android14 - AMS之Activity启动过程&#xff08;2&#xff09;-CSDN博客 ​​​​​​​ Android14 - AMS之Activity启动过程&#xff08;3&#xff09;-CSDN博客 我们以Context的startActivity场景&#xff08;option null&#xff0c; FLAG_ACTIVITY_NEW_TASK&#xff09;来…

以题为例浅谈双指针算法

什么是双指针算法 双指针是指在遍历元素时&#xff0c;不是使用单个指针进行遍历而是使用两个指针进行访问&#xff0c;从而达到相应目的&#xff1b;注意这个指针不是c语言中那个指向地址的指针&#xff1b; 双指针分类 双指针分为对撞指针和快慢指针&#xff1b; 对撞指针…

ServletConfig和ServletContext

ServletConfig接口 在Servlet运行期间&#xff0c;需要一些配置信息&#xff0c;这些信息都可以在WebServlet注解的属性中配置。当Tomcat初始化一个Servlet时&#xff0c;会将该Servlet的配置信息封装到一个ServletConfig对象中&#xff0c;通过调用init(ServletConfig config…

手写一个跳表,跪了。。。

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团、蚂蚁、得物的面试资格&#xff0c;遇到很多很重要的相关面试题&#xff1a; 手写一个跳表&#xff1f; redis为什…

蓝桥刷题--四元组问题和肖恩的投球游戏加强版

1.四元组问题 我的这个代码有点问题&#xff0c;我也找不出来&#xff0c;哪位大佬指正一下 // 四元组问题 //思路 // 是否存在 a < b < c < d, 使得nums[d] < nums[c] < nums[a] < nums[b] //分别维护二元组 (a, b) 和 (c, d), 对合法 b 维护前缀 max 的 n…

QT_day2:页面设计使用ui

1、自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面。&#xff08;不要使用课堂上的图片和代码&#xff0c;自己发挥&#xff0c;有利于后面项目的完成&#xff09; 要求&#xff1a; 1. 需要使用Ui界面文件进行界面设计 2. ui界面上的组件相关设置&#xff0c…

C++多线程都可以对同一个全局变量读写,不设置锁应该没关系吧?

C多线程都可以对同一个全局变量读写&#xff0c;不设置锁应该没关系吧&#xff1f; 是这个意思吗&#xff1a;某个线程只操作一个二进制位&#xff0c;不会有其他线程和它抢着操作同一个二制进位&#xff0c;这种情况下&#xff0c;是否需要锁&#xff1f; 在开始前我有一些资…

openGauss学习笔记-248 openGauss性能调优-使用Plan Hint进行调优-Plan Hint调优概述

文章目录 openGauss学习笔记-248 openGauss性能调优-使用Plan Hint进行调优-Plan Hint调优概述248.1 功能描述248.2 支持范围248.3 注意事项248.4 示例 openGauss学习笔记-248 openGauss性能调优-使用Plan Hint进行调优-Plan Hint调优概述 Plan Hint为用户提供了直接影响执行计…

小白也可以轻松学大模型 RAG:FlagEmbedding 重排序

RAG模型已经取得了显著的进展&#xff0c;但其性能仍然受到排序质量的限制。在实践中&#xff0c;我们发现重排序技术能够有效地改善排序的效果&#xff0c;从而进一步提升RAG模型在问答任务中的表现。 重排序的作用 与传统的嵌入模型不同&#xff0c;重排序器&#xff08;rera…

Tomcat启动报错 因为在清除过期缓存条目后可用空间仍不足

在Tomcat部署路径下的./conf/context.xml配置文件的标签内添加如下内容&#xff1a; <Resources cachingAllowed"true" cacheMaxSize"100000" />

【高频SQL题目进阶版】1440.计算布尔表达式的值

错误答案&#xff1a; 我一开始以为&#xff0c;既然都能拿到值了&#xff0c; 那么把它们合并起来就可以了。后来发现不行。。 1 concat concat(v1.value ,operator ,v2.value) 它不是一个条件&#xff0c;而只是一个字符串 。 而if (condition, true_value, false_value)…

【Leetcode】top 100 链表

基础知识补充 单向链表结构&#xff1a;item存储数据 next指向下一结点地址 head保存首地址 class Node(object): # 创建结点def __init__(self, item): self.item item # item存放数据元素self.next None # next是下一个…

ginblog博客系统/golang+vue

ginblog博客系统 前台&#xff1a; 后台&#xff1a; Gitee的项目地址&#xff0c;点击进入下载 注意&#xff1a; 数据库文件导入在model里面&#xff0c;直接导入即可。 admin和front前后台系统记住修改https里的地址为自己的IP地址&#xff1a; front同上。

考研数学老师怎么选❓看这一篇就够了

张宇、汤家凤、武忠祥、李永乐、杨超、王式安、方浩这些老师都有自己擅长的细分 比如张宇老师&#xff0c;杨超&#xff0c;汤家凤&#xff0c;武忠祥老师的高数讲的很好&#xff0c;李永乐老师是线代的神&#xff0c;王式安、方浩概率论讲的很好&#xff0c;所以对于不同的学…