Redis知识体系总结(2021版,Java面试基础问题

(2)但是这些缓存的数据仍然需要持久化,也就是存入数据库之中,所以在一个请求操作完Redis的读写之后,会去判断该高速读写的业务是否结束,这个判断通常会在秒杀商品为0,红包金额为0时成立,如果不成立,则不会操作数据库;如果成立,则触发事件将Redis的缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。

五、Redis代码实例


1、Java整合Redis

(1)导入pom


<dependency>

    <groupId>redis.clients</groupId>

    <artifactId>jedis</artifactId>

    <version>3.2.0</version>

</dependency>

(2)编写Java主方法

调用Redis中的ping方法,惊现异常:

开始的时候以为是防火墙的问题,后来通过查看redis状态发现IP地址不对,不应该是127.0.0.1

修改redis.conf

注意:需要注意的是在修改redis.conf时,①注掉bind 127.0.0.1;②需要将本机访问保护模式设置为no;③此时可以配置多个ip

(3)再次执行主方法,执行成功!

2、五大数据类型代码实例


package com.guor.redis;

 

import redis.clients.jedis.Jedis;

 

import java.util.List;

import java.util.Set;

 

public class JedisTest01 {

    public static void main(String[] args) {

        test05();

    }

 

    private static void test01(){

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        String value = jedis.ping();

        System.out.println(value);

        //添加

        jedis.set("name","GooReey");

        //获取

        String name = jedis.get("name");

        System.out.println(name);

 

        jedis.set("age","30");

        jedis.set("city","dalian");

        //获取全部的key

        Set<String> keys = jedis.keys("*");

        for(String key : keys){

            System.out.println(key+" --> "+jedis.get(key));

        }

 

        //加入多个key和value

        jedis.mset("name1","zs","name2","ls","name3","ww");

        List<String> mget = jedis.mget("name1", "name2");

        System.out.println(mget);//[zs, ls]

    }

 

    //list

    private static void test02(){

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        jedis.lpush("key1","01","02","03");

        List<String> values = jedis.lrange("key1",0,-1);

        System.out.println(values);//[03, 02, 01]

    }

 

    //set

    private static void test03(){

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        jedis.sadd("username","zs","ls","ww");

        Set<String> names = jedis.smembers("username");

        System.out.println(names);//[ww, zs, ls]

    }

 

    //hash

    private static void test04(){

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        jedis.hset("users","age", "20");

        String hget = jedis.hget("users","age");

        System.out.println(hget);

    }

 

    //zset

    private static void test05(){

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        jedis.zadd("china",100d,"shanghai");

        Set<String> names = jedis.zrange("china",0,-1);

        System.out.println(names);//[shanghai]

    }

}

3、手机验证码功能代码实例


package com.guor.redis;

 

import redis.clients.jedis.Jedis;

 

import java.util.Random;

 

public class PhoneCode {

    public static void main(String[] args) {

        verifyCode("10086");//795258

        getRedisCode("10086","795258");//success.

    }

 

    //1、生成6位数字验证码

    public static String getCode(){

        Random random = new Random();

        String code = "";

        for (int i = 0; i < 6; i++) {

            int rand = random.nextInt(10);

            code += rand;

        }

        return code;//849130

    }

 

    //2、每个手机每天只能发送三次,验证码放到redis中,设置过期时间

    public static void verifyCode(String phone){

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        //拼接key

        //手机发送次数key

        String countKey = "VerifyCode" + phone + ":count";

        //验证码key

        String codeKey = "VerifyCode" + phone + ":code";

        //每个手机每天只能发送三次

        String count = jedis.get(countKey);

        if(count == null){

            //设置过期时间

            jedis.setex(countKey,24*60*60,"1");

        }else if(Integer.parseInt(count)<=2){

            //发送次数+1

            jedis.incr(countKey);

        }else if(Integer.parseInt(count)>2){

            System.out.println("今天的发送次数已经超过三次");

            jedis.close();

        }

 

        String vCode = getCode();

        jedis.setex(codeKey,120,vCode);

        jedis.close();

    }

 

    //3、验证码校验

    public static void getRedisCode(String phone, String code){

        //从redis中获取验证码

        Jedis jedis = new Jedis("192.168.194.131", 6379);

        //验证码key

        String codeKey = "VerifyCode" + phone + ":code";

        String redisCode = jedis.get(codeKey);

        if(redisCode.equals(code)){

            System.out.println("success.");

        }else{

            System.out.println("error");

        }

        jedis.close();

    }

}

当超过三次时:

4、SpringBoot整合Redis

(1)建工程,引入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">

    <modelVersion>4.0.0</modelVersion>

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.2.1.RELEASE</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.guor</groupId>

    <artifactId>redisspringboot</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <name>redisspringboot</name>

    <description>Demo project for Spring Boot</description>

    <properties>

        <java.version>1.8</java.version>

    </properties>

    <dependencies>

        <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>

 

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-redis</artifactId>

            <version>2.4.5</version>

        </dependency>

 

        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->

        <dependency>

            <groupId>org.apache.commons</groupId>

            <artifactId>commons-pool2</artifactId>

            <version>2.9.0</version>

        </dependency>

 

    </dependencies>

 

    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

 

</project>

(2)配置类

application.properties


# Redis数据库索引(默认为0)

spring.redis.database=0

# Redis服务器地址

spring.redis.host=192.168.194.131

# Redis服务器连接端口

spring.redis.port=6379

# Redis服务器连接密码(默认为空)

spring.redis.password=

# 连接池最大连接数(使用负值表示没有限制)

spring.redis.jedis.pool.max-active=20

# 连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.jedis.pool.max-wait=-1

# 连接池中的最大空闲连接

spring.redis.jedis.pool.max-idle=10

# 连接池中的最小空闲连接

spring.redis.jedis.pool.min-idle=0

# 连接超时时间(毫秒)

spring.redis.timeout=1000

RedisConfig


package com.guor.redisspringboot.config;

 

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.fasterxml.jackson.annotation.PropertyAccessor;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;

import org.springframework.cache.annotation.EnableCaching;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheConfiguration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.cache.RedisCacheWriter;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.*;

 

import java.time.Duration;

 

@EnableCaching

@Configuration

public class RedisConfig {

    @Bean

    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(factory);

 

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();

        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

//        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        jackson2JsonRedisSerializer.setObjectMapper(om);

 

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式

        template.setKeySerializer(stringRedisSerializer);

        // hash的key也采用String的序列化方式

        template.setHashKeySerializer(stringRedisSerializer);

        // value序列化方式采用jackson

        template.setValueSerializer(jackson2JsonRedisSerializer);

        // hash的value序列化方式采用jackson

        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();

        return template;

    }

 

    /**

     * 基于SpringBoot2 对 RedisCacheManager 的自定义配置

     * @param redisConnectionFactory

     * @return

     */

    @Bean

    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

        //初始化一个RedisCacheWriter

        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);

        //设置CacheManager的值序列化方式为json序列化

        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();

        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);

        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);

 

        //设置默认超过时期是1天

        defaultCacheConfig.entryTtl(Duration.ofDays(1));

        //初始化RedisCacheManager

        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);

    }

}

(3)控制类测试


package com.guor.redisspringboot.controller;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 

@RestController

@RequestMapping("/redisTest")

public class RedisTestController {

 

    @Autowired

    private RedisTemplate redisTemplate;

 

    @GetMapping

    public String getRedis(){

        redisTemplate.opsForValue().set("name","zs");

        String name = (String) redisTemplate.opsForValue().get("name");

        return name;

    }

}

(4)测试

六、Redis事务


众所周知,事务是指“一个完整的动作,要么全部执行,要么什么也没有做”。

在聊redis事务处理之前,要先和大家介绍四个redis指令,即multi、exec、discard、watch。这四个指令构成了redis事务处理的基础。

1.multi用来组装一个事务;

2.exec用来执行一个事务;

3.discard用来取消一个事务;

4.watch用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。


redis> multi 

OK

redis> INCR id

QUEUED

redis> INCR id

QUEUED

redis> INCR id

QUEUED

redis> exec

1) (integer) 1

2) (integer) 2

3) (integer) 3

我们在用multi组装事务时,每一个命令都会进入内存中缓存起来,QUEUED表示缓存成功,在exec时,这些被QUEUED的命令都会被组装成一个事务来执行。

对于事务的执行来说,如果redis开启了AOF持久化的话,那么一旦事务被成功执行,事务中的命令就会通过write命令一次性写到磁盘中去,如果在向磁盘中写的过程中恰好出现断电、硬件故障等问题,那么就可能出现只有部分命令进行了AOF持久化,这时AOF文件就会出现不完整的情况,这时,我们可以使用redis-check-aof工具来修复这一问题,这个工具会将AOF文件中不完整的信息移除,确保AOF文件完整可用。

有关事务,大家经常会遇到的是两类错误:

1.调用EXEC之前的错误

2.调用EXEC之后的错误

“调用EXEC之前的错误”,有可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redis都会进行记录,在客户端调用EXEC时,redis会拒绝执行这一事务。(这时2.6.5版本之后的策略。在2.6.5之前的版本中,redis会忽略那些入队失败的命令,只执行那些入队成功的命令)。


127.0.0.1:6379> multi

OK

127.0.0.1:6379> hello world //错误指令

(error) ERR unknown command 'hello world'

127.0.0.1:6379> ping

QUEUED

127.0.0.1:6379> exec

(error) EXECABORT Transaction discarded because of previous errors.

而对于“调用EXEC之后的错误”,redis则采取了完全不同的策略,即redis不会理睬这些错误,而是继续向下执行事务中的其他命令。这是因为,对于应用层面的错误,并不是redis自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行。我们也来看一个例子:


127.0.0.1:6379> multi

OK

127.0.0.1:6379> set age 23

QUEUED

//age不是集合,所以如下是一条明显错误的指令

127.0.0.1:6379> sadd age 15 

QUEUED

127.0.0.1:6379> set age 29

QUEUED

127.0.0.1:6379> exec //执行事务时,redis不会理睬第2条指令执行错误

1) OK

2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

3) OK

127.0.0.1:6379> get age

"29" //可以看出第3条指令被成功执行了

好了,我们来说说最后一个指令“watch”,这是一个很好用的指令,它可以帮我们实现类似于“乐观锁”的效果,即CAS(check and set)。

watch本身的作用是“监视key是否被改动过”,而且支持同时监视多个key,只要还没真正触发事务,watch都会尽职尽责的监视,一旦发现某个key被修改了,在执行exec时就会返回nil,表示事务无法触发。


127.0.0.1:6379> set age 23

OK

127.0.0.1:6379> watch age //开始监视age

OK

127.0.0.1:6379> set age 24 //在EXEC之前,age的值被修改了

OK

127.0.0.1:6379> multi

OK

127.0.0.1:6379> set age 25

QUEUED

127.0.0.1:6379> get age

QUEUED

127.0.0.1:6379> exec //触发EXEC

(nil) //事务无法被执行

七、Redis持久化的两种方式


redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;

最后的内容

在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个40k,没做准备只有30k+,你们懂那种感觉吗)

如何准备面试?

1、前期铺垫(技术沉积)

程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。

下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习Java,应对大厂面试官的灵魂追问,有需要的话就戳这里:蓝色传送门打包带走吧。

这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!

  • Java程序员必看《Java开发核心笔记(华山版)》

  • Redis学习笔记

  • Java并发编程学习笔记

四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇

  • Java程序员必看书籍《深入理解 ava虚拟机第3版》(pdf版)

  • 大厂面试必问——数据结构与算法汇集笔记

其他像Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s等等我都整理好,这里就不一一展示了。

2、狂刷面试题

技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。

①大厂高频45道笔试题(智商题)

②BAT大厂面试总结(部分内容截图)

③面试总结

3、结合实际,修改简历

程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

(pdf版)

[外链图片转存中…(img-u0h777Wg-1628418060457)]

  • 大厂面试必问——数据结构与算法汇集笔记

[外链图片转存中…(img-ojvva0qD-1628418060458)]

其他像Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s等等我都整理好,这里就不一一展示了。

[外链图片转存中…(img-bygdx4os-1628418060459)]

2、狂刷面试题

技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。

①大厂高频45道笔试题(智商题)

[外链图片转存中…(img-7GCYmO1Z-1628418060460)]

②BAT大厂面试总结(部分内容截图)

[外链图片转存中…(img-giBG3V5q-1628418060461)]

[外链图片转存中…(img-LhUXH6aQ-1628418060461)]

③面试总结

[外链图片转存中…(img-88pNdkyY-1628418060462)]

[外链图片转存中…(img-fnpA9UOS-1628418060462)]

3、结合实际,修改简历

程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

[外链图片转存中…(img-6f3lWiRF-1628418060463)]

以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

以上文章中,提及到的所有的笔记内容、面试题等资料,均可以免费分享给大家学习,有需要的话就戳这里打包带走吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值