zoukankan      html  css  js  c++  java
  • 关于redis的知识梳理

    思考引入

      mybatis 的二级缓存级别是mapper 其作用域是namespace ,sqlSession  共享同一个namespace下所有的mapper,二级缓存开启在单个应用的时候,是可以减少数据库的压力;但是由于其缓存是在本地,容易产生脏数据,所有呢在分布式应用中,是不建议开启的,如果分布式下使用缓存,建议使用redis;

    1.redis是个什么东西

      redis是个缓存中间件吧(错 片面 ),

      --是非关系型数据库(没有表格 行 列) Nosql 可以

    2.为什么要用redis啊,如果不用redis会产生什么样的影响我们不想看到,使用的redis 会解决我们什么样的痛点、  

      高并发的时候,redis为数据库分担压力,防止数据库宕机;

      --可以达到每秒8万次的写,11万的读

    3.我们在什么时候使用redis啊,redis的使用场景,这些场景是由于其哪些特性决定的

      由于其高速的读取能力可以 作为缓存

      由于其可以进行持久化到硬盘,也可以作为数据库

    redis概念梳理

    1.rdb aof 

      这个是redis持久化方式,默认RDB

      rdb:redis 回fork创建一个线程 将数据写到一个文件中,存在时间差,最后一次持久化的数据有可能不是最新的,因此适合数据不是非常敏感

      aop:是将所有的命令都记录下来,需要手动开启

    2.6379

      redis 默认端口

    3.redis-benchamark 

      压力测试工具

    4. 内存操作,单线程

      redis虽然是单线程的 但是由于其是基于内存操作的,一般意义CUP的效率 是 高于内存的,但是多线程在cup中操作在上下文切换的的时候存在耗时消耗,虽然redis是单线程 基于内存操作,但是效率还是很高的。

    5.为什么记不住五种常用基本类型,我理解的redis存储的类型就是类似 map<string,Object> key是string类型,value是多种类型 是吗?

         String类型   String(k),String(v)

        无序set集合  String(k),【str,str,str..】(v)

        有序zset集合 String(k),【str1,str2,str3..】(v)

        无序哈希HASH (mini版redis)  String(k),【<k,v>,<k,v>....】(v)

       有序双向列表list  String(k),【....str1,str2,str1....】(v)

    6.redis的事务特殊的事务 只是一组命令的集合,不能保证原子性,但是单个命令是具有原子性的

    7.Jedis是Redis官方推荐的连接工具

      但是springBoot 2.x之后 lettuce Redis连接工具的原因是什么?

      因为jedis 线程是不安全的,lettuce是线程安全的

    8.RedisTemplate是什么东西? 

      Jedis是Redis官方推荐的面向Java的操作Redis的客户端,

      而RedisTemplate是SpringDataRedis中对JedisApi的高度封装(的客户端)。

      SpringDataRedis相对于Jedis来说可以方便地更换Redis的Java客户端,比Jedis多了自动管理连接池的特性,方便与其他Spring框架进行搭配使

     9.使用redisTemplate为什么要使用序列化

      1.首先序列化解决的问题是  java对象 在传输和存储中 可以正确成功的 读取。

      2.因为redisTemplate 默认使用的是 JDK的序列化 所有会存在乱码的现象,为了解决乱码问题 所以,使用JSON进行序列化,K 使用String,value 使用jackson

      3.我们自己定义一个RedisTemplate 来替换 默认的redisTemplate

    Redis 的进阶问题

    1.Redis的发布订阅 实际上是 redis的一个功能 自带有方法,redis客户端 是redis的 发布者 也是订阅者,然后呢redis服务端内部使用队列进行对发布者发布的消息,进行推送

    图片

      

    2.redis服务端主从复制(一主二从),读写分离(主写,从读)的架构图

    图片

     

     主机可以写,从机不能写只能读(默认权限)!主机中的所有信息和数据,都会自动被从机保存!

    3.哨兵模式  主要用来监控主机是否故障,如果故障自动将从库 转为主库 原型图

    图片

    4.redis 在访问量大并发量的的时候 容易出现的问题以及解决方案

      1.缓存雪崩(宕机了)由于大量的失效或者redis宕机 解决方案:redis集群部署,限流降级,数据预热

            数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中

      2.缓存击穿(点) 在key失效的一瞬间 大量请求去访问DB数据库了 ;解决方案 设置key不过期

      3.缓存穿透,因为redis中不存在key 所以都去查询数据库了  解决方案 布隆过滤器 在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;还有查询到的控制也将其缓存起来,但是有可能造成数据不同步

           

    5.分布式为什么要使用redis 用redis 主要解决什么问题?

      1.什么是分布式:分布式是将一个系统 拆分成多个系统 ,并且多个系统独立运行,他们共同完成系统功能

      2.redis可以实现分布式锁,可以把整个集群当成一个应用去处理,因为redis是独立应用的第三方,可以实现跨jvm通过互斥机制去控制共享资源

      3.redis的互斥机制 是通过 方法 setnx 来实现的 ,key存在返回0 ,不存在 设置v 并且返回1

      4.设置锁和过期时间,防止锁被无线占用;UUID防止误删除锁;LUA脚本保证了执行redis命令组的原则性;

        /**
         * 最终加强分布式锁
         *
         * @param key key值
         * @return 是否获取到
         * @throws Exception 
         */
        public void lock(String key,String UUID) throws Exception {
            int time = 0;
             while(true){
                if (!hasKey(LOCK_PREFIX + key)) {
                    if (set(LOCK_PREFIX + key,UUID, LOCK_EXPIRE)) {
                        logger.info("RedisTools________lock________加锁成功!锁ID:"+LOCK_PREFIX + key);
                        break;
                    }
                }
                try {
                    Thread.sleep(LOCK_WITE);
                    logger.info("RedisTools________lock________"+LOCK_PREFIX+key+"有锁!"+ LOCK_WITE + "秒后重试!");
                } catch (InterruptedException e) {
                }
                time++;
                if(time==20){
                    throw new Exception("RedisTools________lock________lockerror:"+LOCK_PREFIX+key+"验证锁尝试20次失败!");
                }
             }
        }


    public boolean releaseDistributedLock(String lockKey, String requestId){
            Jedis jedis = null;
            try {
                jedis = this.jedisPool.getResource();
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
                if(RELEASE_SUCCESS.equals(result)){
                    return true;
                }
            }catch (Exception e){
                logger.error(e);
            }finally {
                if(jedis != null){
                    jedis.close();
                }
            }
            return false;
        }

    6.线程和进程的区别以及线程的生命周期,线程锁和进程锁,以及应用锁(分布式锁的区别)

      1.线程是cup执行(调度)最小单元,同一类线程共享代码和数据空间; 进程 是资源分配的最小单元,进程有独立的代码和数据空间(进程上下文),进程在切换的时候开销大。

       2.线程的基本状态

    • 就绪状态:线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。处于就绪状态的线程,随时可能被CPU调度执行。
    • 运行状态: 线程已获得CPU,正在运行。
    • 阻塞状态: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

     3.声明周期 创建 就绪 运行 阻塞 销毁 

      1.sleep() (暂停线程,不会释放锁,睡眠结束,线程继续执行,线程自动释放锁

      2.wait()  属于Object类,而不属于Thread类,wait()会先释放锁,再执行等待的动作。由于wait()所等待的对象必须先锁住,因此,它只能用在同步化程序段或者同步化方法内,否则,会抛出异常IllegalMonitorStateException.

          wait与notify必须配合synchronized使用,因为调用之前必须持有锁,wait会立即释放锁,notify则是同步块执行完了才释放

      3.yield() 方法 该方法与sleep()类似,只是不能由用户指定暂停多长时间,但此时线程任然处于可执行状态,随时可以再次分得cpu时间片。yield()方法只能使同优先级的线程有执行的机会。(暂停当前正在执行的线程,并执行其他线程,且让出的时间不可知,不会释放锁)

      4.join()方法

        当主线程开启一个或多个子线程的时候,使用join方法,必须等该线程运行结束,主线程或其他子线程才由阻塞状态转为可执行状态。

    public class ThreadTset {
    
        public static void test() {
    
            Thread t1 = new Thread (() -> {
                for (int i = 0; i < 5; i++) {
                    System.out.println ("A----" + i);
                }
            });
    
            Thread t2 = new Thread (() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println ("B----" + j);
                }
            });
    
            t1.start ();
            try {
                t1.join ();
            } catch (InterruptedException e) {
                e.printStackTrace ();
            }
            t2.start ();
    
        }
    
        public static void main(String[] args) {
            test ();
        }
        
    }

    执行结果

    A----0
    A----1
    A----2
    A----3
    A----4
    B----0
    B----1
    B----2
    B----3
    B----4

    
    

    Process finished with exit code 0



     分析:主线程开始运行t1线程,线程已经进入就绪状态,接着TI线程join方法被调用,主线程处于阻塞状态,等待t1线程执行完毕,主线程状态变为可运行状态,t2线程被执行。

    线程锁:

    synchronized

     多线程可以同时运行多个任务但是当多个线程同时访问共享数据时,可能导致数据不同步,甚至错误!  so,不使用线程锁, 可能导致错误

    大家都不陌生,主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行该段代码。

    当有多个线程访问同一对象的加锁方法/代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。但是,其余线程是可以访问该对象中的非加锁代码块的。

    进程锁:

    也是为了控制同一操作系统中多个进程访问一个共享资源,

    只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的,

    但是可以使用本地系统的信号量控制(操作系统基本知识)。

    分布式锁:

    redis

        

     redis面试题整理

    问题:redis 什么时候存和清空对应key的数据,如果第一次查询的时候,如果缓存中没有,这个时候需要查询数据库并且返回到redis中,别的请求过来,直接从缓存中那,如果数据更新了呢,是不是要去删除

        这个查询出来的Entry呢 ,还有一种情况就是,如果这个key值 是通过 基础数据计算出来的,我们更新了基础数据,怎么快速确定你影响的key呢?还有更新数据 后 删除key 还是 先删除key 再去修改数据呢?

        最大的问题是没有用redis 去解决实际的需求和问题? 

    参考:redis是什么?

  • 相关阅读:
    JS 缓动动画封装函数
    JS 实现商品图片放大镜(大图)效果
    JS 实现春节倒计时效果
    HMTL+CSS 实现图片旋转木马效果
    HTML 解决video标签播放视频看不见&能听到声音看不到画面等问题
    GoLang Json数据的的序列化与反序列化
    GoLang 解决VsCode中提示错误 go: cannot find main module, but found .git/config in D:XXXsrcXXX to create a module there, run: cd .. && go mod init
    Flutter 容器(3)
    Flutter 容器 (2)
    Flutter 容器 (1)
  • 原文地址:https://www.cnblogs.com/nextgg/p/15497257.html
Copyright © 2011-2022 走看看