• Java学习总结(答案版)


    4.14 学习总结:

    按照以日为维度查询一周中每一天的统计数 使用DATE_FORMAT(r.createDate,‘%Y-%m-%d’)最后要group by DATE_FORMAT(r.createDate,‘%Y-%m-%d’)
    按照以月为维度查询半年中每月的统计数 使用DATE_FORMAT(r.createDate,‘%Y-%m’)最后要group by DATE_FORMAT(r.createDate,‘%Y-%m’)
    mysql中使用date_add()函数

    4.15学习总结

    了解到MySql的日期函数date_sub(now(), interval 7 day)
    比如now() 得到当前时间是 2022-01-15 22:36:43
    那么date_sub(now(), interval 7 day) 得到的时间就是 2022-01-08 22:36:43
    current_date 获取当前时间,精确到日

    4.18 学习总结:

    (重点:@PostMapper)在restful风格里面 传参里面的参数必须序列化,要不然只会传入其格式例如:json/application
    @PostMapper @RequestBody @Valid DynamicTrendAnalysisParam queryParam
    查询时不能全部查出,试试分俩个sql写出来

    4.19学习总结:

    数据概括:(查询慢的sql)select 查询 关联字段放到where 比如 u.tenanatId=s.tenantId 还有查询时间范围拿到where里面
    类型分析: 按照type进行分析就要group by type 关键字on(里面只要关联字段)

    4.20学习总结:

    热度分析:SaleManRankReportServiceImpl 296行
    Set typeEnumSet = new HashSet<>();
    typeEnumSet.add(ClewTypeEnum.STYLE);
    typeEnumSet.add(ClewTypeEnum.PHOTO);
    typeEnumSet.add(ClewTypeEnum.RECOM_ATTIRE);
    typeEnumSet.add(ClewTypeEnum.IMG_TEXT);

    for (ClewTypeEnum typeEnum:typeEnumSet){
    if(map.containsKey(typeEnum)){
    DynamicHeatAnalysisVo dynamicHeatAnalysisVo= map.get(typeEnum);
    voList.add(dynamicHeatAnalysisVo) ;
    }else{
    DynamicHeatAnalysisVo dynamicHeatAnalysisVo= new DynamicHeatAnalysisVo(typeEnum,null);
    voList.add(dynamicHeatAnalysisVo) ;
    }
    }

    4.25学习总结:

    1462行CustomerMapper.xml
    关于在controller中传参把wrapper作为参数传递
    https://blog.csdn.net/weixin_42260782/article/details/122232629?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165085451516781483756079%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165085451516781483756079&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-1-122232629.142v9control,157v4control&utm_term=mapper.xml%E4%B8%ADew%E6%98%AF%E5%95%A5%E6%84%8F%E6%80%9D&spm=1018.2226.3001.4187

    List 转 Map<String, T>
    List 转 Map<String, List>
    List 转 Map<String, Count>
    List<Map<String, Object>> 转 Map<String, List<Map<String, Object>>>

    List 转 Map<String, List>
    使用groupingBy根据gender属性分组
    https://blog.csdn.net/winterking3/article/details/116457573?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165088493416782350981412%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165088493416782350981412&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-4-116457573.nonecase&utm_term=stream%E6%B5%81%E5%A4%84%E7%90%86list%E8%BD%ACmap&spm=1018.2226.3001.4450

    4.27学习总结:

    Jenkins+Docker的操作
    https://blog.csdn.net/hlkt521/article/details/107188180?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165103454016781432988457%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=165103454016781432988457&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-107188180.142v9control,157v4control&utm_term=jenkins&spm=1018.2226.3001.4187

    5.9 学习总结:

    在进行模糊查询时(查询货号或者商品名称)
    不加括号: AND st.styleName LIKE CONCAT( ‘%’, ‘ww’, ‘%’ ) OR ( st.styleId LIKE CONCAT( ‘%’, ‘ww’, ‘%’ ))
    加了括号:AND (st.styleName LIKE CONCAT( ‘%’, ‘ww’, ‘%’ ) OR ( st.styleId LIKE CONCAT( ‘%’, ‘ww’, ‘%’ )))

    区别: 加入括号只能查货号 和 产品名称
    不加括号:只能查询商品名称 或者货号 而且不加括号数据比加了括号的数据要多

    不加括号:or前面条件失效,后面条件生效

    https://blog.csdn.net/liyue071714118/article/details/108656396?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E4%B8%ADand%E5%92%8Cor%E7%9A%84%E5%8C%BA%E5%88%AB&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-2-108656396.nonecase&spm=1018.2226.3001.4187

    判断一个数据是否为0 if(!xxx){}

    面试题

    1.多线程

    1.1 为什么会发生线程安全问题?你在项目中是怎么解决的?

    CPU在多个线程间切换,可能导致某些重要的指令不能完整执行,出现数据的问题。

    出现线程安全问题的三个条件:

    • 多个线程
    • 同一个时间
    • 执行同一段指令或修改同一个变量
    /**
     * 银行转账的案例
     */
    public class BankDemo {
    
        //模拟100个银行账户
        private int[] accounts = new int[100];
    
        {
            //初始化账户
            for (int i = 0; i < accounts.length; i++) {
                accounts[i] = 10000;
            }
        }
    
        /**
         * 模拟转账
         */
        public void transfer(int from,int to,int money){
            if(accounts[from] < money){
                throw new RuntimeException("余额不足");
            }
            accounts[from] -= money;
            System.out.printf("从%d转出%d%n",from,money);
            accounts[to] += money;
            System.out.printf("向%d转入%d%n",to,money);
            System.out.println("银行总账是:" + getTotal());
        }
    
        /**
         * 计算总余额
         * @return
         */
        public int getTotal(){
            int sum = 0;
            for (int i = 0; i < accounts.length; i++) {
                sum += accounts[i];
            }
            return sum;
        }
    
        public static void main(String[] args) {
            BankDemo bank = new BankDemo();
            Random random = new Random();
            //模拟多次转账过程
            for (int i = 0; i < 50; i++) {
                new Thread(() -> {
                    int from = random.nextInt(100);
                    int to = random.nextInt(100);
                    int money = random.nextInt(2000);
                    bank.transfer(from,to,money);
                }).start();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    线程安全问题的解决方法

    解决方法:给程序上锁,让当前线程完整执行一段指令,执行完释放锁,其它线程再执行

    几种不同上锁方法:

    • 同步方法
    • 同步代码块
    • 同步锁

    同步方法

    给方法添加synchronized关键字

    作用是给整个方法上锁

    过程:

    当前线程调用方法后,方法上锁,其它线程无法执行,调用结束后,释放锁。

        /**
         * 模拟转账
         */
        public synchronized void transfer(int from,int to,int money){
            if(accounts[from] < money){
                throw new RuntimeException("余额不足");
            }
            accounts[from] -= money;
            System.out.printf("从%d转出%d%n",from,money);
            accounts[to] += money;
            System.out.printf("向%d转入%d%n",to,money);
            System.out.println("银行总账是:" + getTotal());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    锁对象:

    • 非静态方法 --> this
    • 静态方法 —> 当前类.class

    同步代码块

    粒度比同步方法小,粒度越小越灵活,性能更高

    给一段代码上锁

    synchronized(锁对象){
    	代码
    }
    
    • 1
    • 2
    • 3

    锁对象,可以对当前线程进行控制,如:wait等待、notify通知;

    任何对象都可以作为锁,对象不能是局部变量

            //同步代码块
            synchronized (lock) {
                accounts[from] -= money;
                System.out.printf("从%d转出%d%n", from, money);
                accounts[to] += money;
                System.out.printf("向%d转入%d%n",to,money);
                System.out.println("银行总账是:" + getTotal());
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    synchronized的基本的原理:

    一旦代码被synchronized包含,JVM会启动监视器(monitor)对这段指令进行监控

    线程执行该段代码时,monitor会判断锁对象是否有其它线程持有,如果其它线程持有,当前线程就无法执行,等待锁释放

    如果锁没有其它线程持有,当前线程就持有锁,执行代码

    底层汇编实现:

    monitorenter
    ....
    monitorexit
    
    • 1
    • 2
    • 3

    同步锁

    在java.concurrent并发包中的

    Lock接口

    基本方法:

    • lock() 上锁
    • unlock() 释放锁

    常见实现类

    • ReentrantLock 重入锁
    • WriteLock 写锁
    • ReadLock 读锁
    • ReadWriteLock 读写锁

    使用方法:

    1. 定义同步锁对象(成员变量)
    2. 上锁
    3. 释放锁
    //成员变量
    Lock lock = new ReentrantLock();
    
    //方法内部上锁
    lock.lock();
    try{
    	代码...
    }finally{
    	//释放锁
    	lock.unlock();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三种锁对比:

    • 粒度

      同步代码块/同步锁 < 同步方法

    • 编程简便

      同步方法 > 同步代码块 > 同步锁

    • 性能

      同步锁 > 同步代码块 > 同步方法

    • 功能性/灵活性

      同步锁(有更多方法,可以加条件) > 同步代码块 (可以加条件) > 同步方法

    1.2 并发与并行,线程与进程、

        - 串行    
          多个指令依次执行
       - 并发
         每个线程单独执行一段指令,一个cpu在线程间切换(并不是同时执行)
       - 并行
         多个CPU内核同时执行多个线程,线程是同时执行的
           -  进程是程序执行相关资源(CPU、内存、磁盘等)分配的最小单元
       
      进程之间是相互独立的,有自己的内存空间
      
     - 线程是CPU资源分配的最小单元
      
      进程包含一个或多个线程
      
      线程需要的资源更少,可以看做是一种轻量级的进程
      
      线程会共享进程中的内存,线程也有独立的空间(栈、程序计数器)
      
      线程相互通信更加方便
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1.3 如何创建多线程

       1. 继承Thread类
       2. 实现Runnable接口
       3. 实现Callable接口
       4. 使用线程池
    
    • 1
    • 2
    • 3
    • 4

    1.4 线程池你了解多少?

        作用:回收利用线程资源
    
        线程是一种宝贵的系统资源,执行完任务后会死亡,如果有大量任务需要处理,需要                   频繁的创建和销毁线程,造成系统性能降低。
    
        线程池会保存一定量的线程,线程执行完任务后,会回到线程池中,等待下一个任                       务,节省系统资源,提升性能
        
        #### 线程池的使用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    顶层接口:Executor

    • execute(Runnable) 启动线程执行一个任务
    ExecutorService
    继承 Executor

    添加线程池管理方法,如:shutdown()、shutdownNow()

    Executors

    用于创建线程池的工具类

    主要的方法

    方法名说明
    newCachedThreadPool()创建长度不限的线程池
    newFixedThreadPool(int )创建固定长度的线程池
    newSingleThreadExecutor()创建单一个数的线程池
    newScheduledThreadPool(int)创建可以调度的线程池

    1.5 你看过关于多线程的源码?

          建议自己百度白嫖+自己理解!!!
    
    • 1

    1.6 线程的生命周期

    线程几种状态:
    
    - 新建 NEW
    - 准备/就绪 START
    - 运行 RUNNING
    - 阻塞 BLOCKING
    - 死亡 DEAD
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bgjRdMTG-1656029061357)(C:\Users\Dinghao\AppData\Roaming\Typora\typora-user-images\1655864557955.png)]

    线程的常用方法

    常用方法

    方法介绍
    start()启动
    stop()停止(禁用,可能导致线程死锁等问题),停止线程可以让run执行结束
    String getName()获得线程的名字
    setName(String)设置线程名字
    sleep(long)进入睡眠,毫秒
    setPriority(int)设置线程的优先级(1~10从低到高)越高抢CPU几率更高
    setDaemon(boolean)设置为后台线程 true ,后台线程是为其它线程服务的,如果没有其它线程存在,就自动死亡;使用案例:GC就是一种后台线程
    join()线程的加入(合并)让其它线程先执行完,再执行自己的指令

    2.锁相关的问题

    2.1 synchronized  与  volatile 的区别?
        volatile是一个类型修饰符(type specifier);
        volatile,它能够使变量在值发生改变时能尽快地让其他线程知道;
        关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且只能修改变量,而synchronized可以修饰方法,以及代码块;
        多线程访问volatile不会发生阻塞,而synchronized会出现阻塞;
        volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步;
        关键字volatile解决的下变量在多线程之间的可见性;而synchronized解决的是多线程之间资源同步问题。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7


    ​ 2.2 什么是乐观锁,悲观锁?
    ​ https://blog.csdn.net/firstcode666/article/details/122789487?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165594641016780357273021%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=165594641016780357273021&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-122789487-null-null.142v20control,157v15new_3&utm_term=%E4%BB%80%E4%B9%88%E6%98%AF%E4%B9%90%E8%A7%82%E9%94%81%EF%BC%8C%E6%82%B2%E8%A7%82%E9%94%81&spm=1018.2226.3001.4187


    ​ 2.3 锁有哪几种(锁的分类)?
    ​ https://blog.csdn.net/m0_60092917/article/details/118880821?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165594644416781667853241%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165594644416781667853241&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-2-118880821-null-null.142v20control,157v15new_3&utm_term=+%E9%94%81%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%EF%BC%88%E9%94%81%E7%9A%84%E5%88%86%E7%B1%BB%EF%BC%89&spm=1018.2226.3001.4187

    3.java集合

    ypora-user-images\1655945696558.png)]](https://img-blog.csdnimg.cn/ccb0b087060a4faab383505a18d33d3a.png)

    3.1你在项目中一般用的啥集合?

    ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0EqFlGGo-1656029061361)(C:\Users\Dinghao\AppData\Roaming!在这里插入图片描述

    在这里插入图片描述

     public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // 判断容量 扩容
            elementData[size++] = e; //存数据到数组中
            return true;
        }
        
        private void ensureCapacityInternal(int minCapacity) {
        	//保证新集合的容量不能低于10
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    		//扩容
            ensureExplicitCapacity(minCapacity);
        }
        
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
            //判断当前容量是否超过数组的长度
            if (minCapacity - elementData.length > 0)
                grow(minCapacity); //扩容
        }
        
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量是原来容量的1.5倍
            if (newCapacity - minCapacity < 0)  //保证新容量不会低于传入的参数
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0) //保证新容量不能超过上限 Integer.MAX_VALUE - 8
                newCapacity = hugeCapacity(minCapacity);
            //使用新容量创建新数组,将原数组的数据复制过去,将新数组赋值给原数组的引用
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3.2那种场景应该用那种集合你知道吗?

    大伙建议自己百度

    3.3 说说你对list set map 的理解?

    自己的理解(自己的口述)

    4.反射

    4.1 什么是反射(是什么)?

    Java中的一种高级技术,能够在程序运行时能够解析出任意类和对象的属性和方法,可以动态调用类和对象的所有属性和方法。

    可以用于开发非常灵活和通用的代码

    应用场景:开发各种框架,如:Spring、MyBatis等

    4.2 反射的优缺点(为什么)?

    优点 : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利

    缺点 :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

    4.3 反射的应用场景(怎么用)?

    像咱们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。

    但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。

    这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。

    5.redis

    5.1 redis 在项目中做啥用的?

    ​ 主要做数据缓存

    5.2怎么配置redis集群?

    5.3redis的优缺点?为什么不用MongoDB?

    https://blog.csdn.net/qq_42992919/article/details/96129147?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165594659716781667889047%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165594659716781667889047&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-2-96129147-null-null.142v20control,157v15new_3&utm_term=redis%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9%EF%BC%9F%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E7%94%A8MongoDB%EF%BC%9F&spm=1018.2226.3001.4187

    5.4 redis 的5 种存储数据结构?

    Redis的数据以key-value方式存储。

    数据类型有:

    • string 字符串(简单的key-value数据,适合保存单个数据,如:用户数量、库存数、商品数)

    • hash 哈希(适合保存复杂类型数据,如:用户对象)

    • list 列表(链表结构,适合保存有序的、可重复的数据,如:商品列表、评论列表)

    • set 无序集合(适合保存无序的,不可重复的数据)

    • zset 有序集合(适合保存有序的,不可重复的数据)

      5.5 redis 雪崩,击穿,穿透?

    1)缓存击穿

    高并发的情况下,短时间内缓存会被穿过,请求直接打到数据库上,可能导致数据库压力过大。

    解决方案:对代码上锁(双重检查锁)

    2)缓存穿透

    高并发的情况下,如果查询不存在的数据,因为缓存和数据库都不存在,请求都会打到数据库上,可能导致系统崩溃。

    解决方案:

    ​ 1) 保存不存在的数据到缓存中,设置一定过期时间

    ​ 2) 布隆过滤器(直接过滤掉不存在数据的请求) 不能准确判断是否存在数据,能准确判断数据不存在

    3)缓存雪崩

    高并发的情况下,缓存服务器重启或热点数据同时过期,全部访问数据库,导致数据库宕机

    解决方案:

    ​ 1)配置缓存集群

    ​ 2)尽量给热点数据设置不一样的过期时间,相对均匀

    解决代码

     public Goods getGoodsById(Long id){
            ValueOperations<String, Object> ops = redisTemplate.opsForValue();
            Object value = ops.get(TYPE + id);
            //外层先读缓存,缓存如果有,就不执行同步块
            if(value == null) {
                synchronized (this) {
                    //1) 以id为键查询Redis缓存,如果能查到就返回数据,结束
                    value = ops.get(TYPE + id);
                    //2)如果查不到,就查询数据库
                    if (value == null) {
                        System.out.println("缓存不存在,查询数据库");
                        // 数据库查到,缓存到Redis,返回
                        Goods goods = this.getById(id);
                        if (goods != null) {
                            System.out.println("数据库存在,保存到缓存");
                            ops.set(TYPE + id, goods);
                        } else {
                            System.out.println("数据库不存在,返回null");
                            //保存空数据到缓存中,设置过期时间
                            ops.set(TYPE + id,new Goods(),30, TimeUnit.SECONDS);
                        }
                        return goods;
                    } else {
                        System.out.println("缓存存在,返回" + value);
                        //如果能查到就返回数据,结束
                        return (Goods) value;
                    }
                }
            }
            System.out.println("缓存存在,返回" + value);
            return (Goods) value;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    5.6 redis 缓存数据过期了,你怎么处理?(类似于即将登录过期,然而用户还在网页或者某个功能点上)

    做一个热点模块比如泡泡教育平台你那个广告模块(你进入网站就广告图片开始时,就把登录时的token携带进入微服务,可以解决这个问题啦,这个广告模块是实时运行的,每次刷新或者运行功能,就会刷新token的过期时间,不断刷新就可以一直携带有效的token)

    5.7redis除了做缓存还能干嘛?

    https://blog.csdn.net/wujialv/article/details/109325092?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165594721916782246457908%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165594721916782246457908&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-1-109325092-null-null.142v20control,157v15new_3&utm_term=redis%E9%99%A4%E4%BA%86%E5%81%9A%E7%BC%93%E5%AD%98%E8%BF%98%E8%83%BD%E5%B9%B2%E5%98%9B%E3%80%81&spm=1018.2226.3001.4187

    5.8 了解过redis源码嘛?

    https://blog.csdn.net/qqqwwweeerasd/article/details/124464919?ops_request_misc=&request_id=&biz_id=102&utm_term=%E4%BA%86%E8%A7%A3%E8%BF%87redis%E6%BA%90%E7%A0%81%E5%98%9B&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-124464919.142v20control,157v15new_3&spm=1018.2226.3001.4187

    6 RabbitMQ

     6.1什么是mq?(干嘛用的)
       消息队列(Message Queue)简称MQ,简单来说就是一种消息的容器
    
       作用是采用FIFO(先入先出)的方式实现程序之间(服务、进程、线程)的消息通信。
    
     6.2 mq 怎么使用(如何在项目中配置)
         自己百度
    
     6.3 mq它的作用是干啥(保持缓存与数据库数据一致)?
          我们还是用了 RabbitMQ 来实现了 ES 和 Mysql 数据的同步。其中实现的 步骤为我们生成了一个 topic 主题类型的交换机,然后分别绑定了两条队列在该交换机上,两条队列分别设置对应 的 key,一个队列用于增改,一个队列用于删除。当我们的后台对 Mysql 的数据进行了增改或删除时就会向对应 key 的队列中发送消息,消息内容就是操作的数据,然后我们的 Es 服务方会对我们的 MQ 的两条队列进行实时的 监听。当在对应的队列中收到消息,就会执行操作,对我们的 es 执行增改或查询的操作。这样我们就实现了 ES 和 Mysql 的数据同步。
          
     6.4 mq 有几种消息队列?(5种)1:1((能者多劳),1 : n,工作,路由,主题
        RabbitMQ提供了多种消息模型,官网上第6种是RPC不属于常规的消息队列。
        属于消息模型的是前5种:
    
        1. 简单的一对一模型
        2. 工作队列模型 ,一个生产者将消息分发给多个消费者
        3. 发布/订阅模型 ,生产者发布消息,多个消费者同时收取
        4. 路由模型 ,生产者通过关键字发送消息给特定消费者
        5. 主题模型 ,路由模式基础上,在关键字里加入了通配符
     6.5 mq 消息丢失怎么处理?
        1. 生产者到交换机
        2. 交换机到队列
        3. 队列到消费者
        解决方案:
    
       1. 针对 生产者到交换机
    
       confirm 机制  确定生产者将消息投递到交换机
    
       2. 针对 交换机到队列
    
       return 机制  交换机发送消息到队列失败会执行回调
    
       3. 针对 队列到消费者
    
       消息没发 --> 消息持久化
    
       消息发了,不知道是否被消费 ---> 手动确认 ack
    ## Confirm&Return机制
    
    ```
    rabbitmq:
      publisher-confirm-type: correlated  # 启动confirm机制
      publisher-returns: true # 启动return机制
    ```
    
    配置类添加
    
    ```
        @Bean
        public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
            RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
            //设置confirm机制的回调
            rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
                @Override
                public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                    if (ack) {
                        log.info("消息{}发送到Exchange成功!!!",correlationData);
                    } else {
                        log.error("消息{}发送到Exchange失败,原因:{}",correlationData,cause);
                    }
                }
            });
            //设置return机制的回调
            rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            	//交换机发送消息到队列失败回调
                @Override
                public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                    log.error("消息发送到Queue失败!{}",message);
                }
            });
            return rabbitTemplate;
        }
    ```
    
    生产者发送消息,设置消息的id
    
    ```
    rabbitTemplate.convertAndSend(RabbitMQConfig.COURSE_EXCHANGE,
                    RabbitMQConfig.KEY_COURSE_SAVE, JSON.toJSONString(course),
                    new CorrelationData(UUID.randomUUID().toString()))
    ```
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83


    ​ 6.6 消息幂等性?
    ​ 重复消费问题:

    队列发送消息给消费者,因为网络原因,消费者没有收到,队列重复发送,网络恢复后会发送多个相同的消息给消费者

        可能导致问题:
    
        重复添加数据等非幂等性业务问题
    
        非幂等性业务(每次操作获得不同结果,如:添加)
    
        幂等性业务(每次操作结果相同,如:更新、删除)
    
        消息有唯一id,保存redis,手动确认
    
        解决方案:
    
        1. 给每个消息添加id
        2. 消费者获得消息后,通过id查询redis,判断该id是否存在
        3. 如果不存在,就修改该id的value为1,执行业务,进行手动确认
        4. 如果存在,就不执行业务
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16



    ​ Redis的setnx命令

    setnx key value

        如果该键不存在,就设置键和值,返回1
    
        如果该键存在,直接返回
    
    • 1
    • 2
    • 3

    7 项目问题

     7.1拿到需求你会怎么做?
        首先将需求和自己的领导对一下,自己要干嘛,写那几个接口,那个数据库里面那几张表
        然后分析表之间的关系,画出逻辑题以及要查询的有效字段。
        然后分析sql,分析业务。
        写完sql完成50%,开始写service层代码(各种各样的校验)。
        写完接口,自己测试,生成测试集合
        推送接口给前端或者Fuller对接口
    
     7.2你会独立处理自己写的bug嘛?
        debug 你必须会,不会建议重新学完debug在学java里面的idea
    
     7.3当你要用没有用的知识点来做项目(你没学)你准备几天完成工作?
         首先要分析是用啥东西来搞,类似于评估项目周期。然后给你自己定个目标(结合自己的学习能力看)。
         比如郭龙2天学完python(学到java中的ssm)就是把基础过完两天。然后找几个类似的项目看看,分析为什么这么做。如果是你,你会这么做。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    8.mysql

    8.1 mysql索引有了解嘛?

    ​ 是一种特殊的数据,保存实际数据的位置,通过索引能快速定位到实际的数据,提高查询速度。

    ​ 如:书籍目录、楼层索引

    优缺点

    优点:大大提高查询速度

    缺点:

    ​ 1) 占用存储空间

    ​ 2) 降低增删改数据

    ​ 3) 创建时比较耗时

    索引的分类

    按功能分类

    1. 普通索引

      建在某一个列上,提高按该列查询速度,没有特别约束,表中可以建多个

      语法:

      create index 索引名 on 表名(列名);
      
      • 1
    2. 主键索引

      建主键时,同时创建,约束是不能重复、不能为空、表只能有一个

    3. 唯一索引

      建在某一个列上,提高按该列查询速度同时该列不能重复,表中可以建多个

      create unique index 索引名 on 表名(列名);
      
      • 1
    4. 全文索引

      用于长文本的列(text)

      create fulltext index 索引名 on 表名(列名);
      
      • 1
    5. 组合索引

      用于多个列的查询

      create index 索引名 on 表名(列名1,列名2,列名3);
      
      • 1

      最左前缀原则:把最重要的列放到左边

      查询时如果没有最左边的列,索引就失效

      列1生效

      列1,列2 生效

      列1,列2,列3 生效

      列2,列3 失效

    索引的使用

    explain 查询语句;

    分析查询语句,是否能正常使用索引

    最重要的:type、key、ref

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bCNDlB63-1656029061370)(C:\Users\Dinghao\AppData\Roaming\Typora\typora-user-images\1655950368758.png)]

    使用场景

    1. 数据量特别大(百万级别)
    2. 列没有太多空值
    3. 列没有很多重复
    4. 列经常用于查询和排序
    
    • 1
    • 2
    • 3
    • 4

    失效情况

    1. like ‘%xxx’ 模糊查询%在前面
    2. != 情况
    3. 使用or,or两边每个列都需要有索引,否则失效
    4. 使用组合索引,没有使用最左边的列
    5. 条件中出现表达式或函数 , 如: age - 1 = 19、year(birth_day) = 1990
    6. is null 或 is not null、in

    8.2 它的数据结构是啥,为什么是b树,b+树?

    B-Tree和B+Tree

    B-Tree属于平衡搜索多叉树,分为根节点、枝节点、叶子节点,每个节点由:键、数据、指针组成

    指针指向下面的子节点,搜索时采用二分查找,效率比较高。

    查询效率和树的高度(高度越高、效率越低)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nM01ZYrj-1656029061371)(C:\Users\Dinghao\AppData\Roaming\Typora\typora-user-images\1655950383953.png)]

    B+Tree是B-Tree升级版

    将数据全部放到叶子节点,非叶子节点只保存键和指针,磁盘块就能保存更多节点,降低了树的高度,大大提升了查找效率。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6EgEAeP-1656029061372)(C:\Users\Dinghao\AppData\Roaming\Typora\typora-user-images\1655950397103.png)]

    8.3 b树和b+树的区别?

    百度

    8.4 mysql 事务(acid)

    https://blog.csdn.net/l1394049664/article/details/81814090?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165595057616781432931223%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=165595057616781432931223&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-81814090-null-null.142v20control,157v15new_3&utm_term=mysql%E4%BA%8B%E5%8A%A1%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%89%B9%E6%80%A7&spm=1018.2226.3001.4187

    8.5mysql有哪些可以优化的?

    架构优化

    ​ 系统规模大,用户量、数据量大

    - MySQL集群,多台MySQL服务器
    
    - 分库分表
    
    • 1
    • 2
    • 3

    ​ 垂直分库

    ​ 将相关的表,放入不同的数据库,如将电商数据库分为:订单数据库、库存数据库

    ​ 水平分库

    ​ 将一个数据库的数据,分开存储到不同的服务器上

    ​ 垂直分表

    ​ 将表的字段分开,放到不同的表中

    ​ 如:商品表分为商品和详情表

    ​ 某些数据在分页中查看,某些大的数据text\blob放详情表

    ​ 水平分表

    ​ 数据量特别大,会严重影响查询

    ​ 将一个表的数据分到多张表中

    • 主从复制

      多台MYSQL服务器,分为主(master)和从(slave)服务器

      一主多从,多主多从

    -  读写分离
    
    • 1

    ​ 建立在MySQL主从复制的基础上,分为主数据库和从数据库

    ​ 主数据库负责写(增删改)从数据库负责读(查询)

    ​ 主数据库写入数据后,会记录到bin-log文件中,从数据库会将bin-log中的数据同步过来

    ​ 使用数据库中间件:Sharding-jdbc、myCat等

    设计优化

    • 范式

      ​ 优点: 规范数据库设计,消除冗余,方便数据的修改

      ​ 缺点: 降低查询效率

      反范式 在表中加入冗余字段

      ​ 提高查询效率

      表的设计按具体情况范式和反范式结合使用

    • 选用合适的存储引擎

      存储引擎是数据库中存储数据的方式,如存储的位置、存储约束、数据结构等的结合

      不同的存储引擎有不同的功能和性能

      常用存储引擎:

      • InnoDB
      • MyIsam
      • Memory (不能持久化)
      • Blackhole
      • Performance Schema

      不同点:

      InnoDBMyIsam
      事务支持不支持
      查询性能以前低于MyIsam,目前差不多
      支持表锁和行锁,并发性能高支持表锁
      外键支持不支持
      行数保存不保存,需要用count(*)保存查询数据
      索引非聚簇索引(索引和实际数据不在一起)聚簇索引(索引和实际数据在一起)

      一般情况下使用InnoDB

    • 字段优化

      1. 主键

        必须给表设置主键id

        尽量不用业务字段,使用没有意义字段如(int自增)

        int自增效率高于UUID

      2. 数据类型

        字符串 尽量使用varchar,不使用char (varchar存储空间更小,数据少查询效率高)

        尽量使用小的数据类型,如:性别 varchar(1) 男 女 --> int 1 0 --> small int --> tiny int (1字节 -128~127)

      3. 尽量加not null约束

    使用优化

    ​ 1) 索引

    ​ 数据量大,使用索引

    ​ 介绍索引的应用场景、分类

    ​ 2) 缓存

    ​ Redis缓存、MyBatis缓存

    ​ 3) 使用连接池

    ​ Druid\Hikari\c3p0\dbcp

    ​ 4) 分页查询

    查询优化

    ​ 1. 查询之前,使用explain查看执行计划

    ​ 2. 尽量避免select *

    ​ 3. 尽量避免where中使用<>和!=

    ​ 4. 尽量避免where中使用is null和is not null

    ​ 5. 尽量避免where中列避免使用函数和表达式 where 1 + age = 20 where max(age) = 20

    ​ 6. 尽量避免模糊查询时,通配符在前面,如 name like ‘%xx’

    ​ 7. 尽量使用exists代替in

    1. 尽量避免where中使用or,union 代替or

      select name from student where age = 20 or address =‘武汉’

      ==>

      select name from student where age = 20

      union

      select name from student where address =‘武汉’

    8.6 出现脏数据?你怎么解决?

    ​ 看具体的业务场景

    8.7 sql 语句要了解

    看自己能力

    8.8 行转列你会吗?

    drop table if exists student;
    create table student(
    	stu_id int primary key auto_increment,
    	stu_name varchar(20),
    	stu_gender varchar(20),
    	stu_age int
    );
    drop table if exists course;
    create table course(
    	course_id int primary key auto_increment,
    	course_name varchar(20)
    );
    drop table if exists score;
    create table score(
    	score_id int primary key auto_increment,
    	stu_id int,
    	course_id int,
    	score int,
    	constraint fk_stu_id foreign key (stu_id) references student(stu_id),
    	constraint fk_course_id foreign key (course_id) references course(course_id)
    );
    insert into student(stu_name,stu_gender,stu_age)
    values('张三','男',15),('李四','男',15),('王五','男',15),('赵六','男',15);
    insert into course(course_name)
    values('语文'),('数学'),('英语');
    insert into score(stu_id,course_id,score)
    values(1,1,80),(1,2,82),(1,3,84),(2,1,60),
    (2,2,70),(2,3,86),(3,1,83),(3,2,77),(3,3,89);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    原始数据:

    学号 姓名 课程 成绩
    001 张三 语文 60
    001 张三 数学 89
    001 张三 英语 88
    002 李四 语文 88
    002 李四 数学 66
    002 李四 英语 90

    行转列的效果:

    学号 姓名 语文 数学 英语
    001 张三 60 89 88
    002 李四 88 66 90

    1) 完成多表连接

    -- 1. 三表连接
    select s.stu_id 学号,s.stu_name 姓名,c.course_name 课程,sc.score
    from student s left join score sc on s.stu_id = sc.stu_id
    left join course c on c.course_id = sc.course_id;
    
    • 1
    • 2
    • 3
    • 4

    2) 将课程名称作为列

    使用Case End语句

    语法:

    case 列名
    	when 值1 then 结果1
    	when 值2 then 结果2
    	else 缺省结果
    end
    
    case
    	when 条件1 then 结果1
    	when 条件2 then 结果2
    	else 缺省结果
    end
    
    -- 2. 使用case-END
    select s.stu_id 学号,s.stu_name 姓名,
    case c.course_name
    	when '语文' then sc.score 
    	else 0
    end 语文,
    case c.course_name
    	when '数学' then sc.score 
    	else 0
    end 数学,
    case c.course_name
    	when '英语' then sc.score 
    	else 0
    end 英语
    from student s left join score sc on s.stu_id = sc.stu_id
    left join course c on c.course_id = sc.course_id;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1. 求最大值
    -- 4. 求最大值或总和
    select s.stu_id 学号,s.stu_name 姓名,
    max(
    case c.course_name
    	when '语文' then sc.score 
    	else 0
    end) 语文,
    max(case c.course_name
    	when '数学' then sc.score 
    	else 0
    end) 数学,
    max(case c.course_name
    	when '英语' then sc.score 
    	else 0
    end) 英语
    from student s left join score sc on s.stu_id = sc.stu_id
    left join course c on c.course_id = sc.course_id
    group by s.stu_id;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    完成多表连接

    -- 1. 三表连接
    select s.stu_id 学号,s.stu_name 姓名,c.course_name 课程,sc.score
    from student s left join score sc on s.stu_id = sc.stu_id
    left join course c on c.course_id = sc.course_id;
    
    • 1
    • 2
    • 3
    • 4

    2) 将课程名称作为列

    使用Case End语句

    语法:

    case 列名
    	when 值1 then 结果1
    	when 值2 then 结果2
    	else 缺省结果
    end
    
    case
    	when 条件1 then 结果1
    	when 条件2 then 结果2
    	else 缺省结果
    end
    
    -- 2. 使用case-END
    select s.stu_id 学号,s.stu_name 姓名,
    case c.course_name
    	when '语文' then sc.score 
    	else 0
    end 语文,
    case c.course_name
    	when '数学' then sc.score 
    	else 0
    end 数学,
    case c.course_name
    	when '英语' then sc.score 
    	else 0
    end 英语
    from student s left join score sc on s.stu_id = sc.stu_id
    left join course c on c.course_id = sc.course_id;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1. 求最大值
    -- 4. 求最大值或总和
    select s.stu_id 学号,s.stu_name 姓名,
    max(
    case c.course_name
    	when '语文' then sc.score 
    	else 0
    end) 语文,
    max(case c.course_name
    	when '数学' then sc.score 
    	else 0
    end) 数学,
    max(case c.course_name
    	when '英语' then sc.score 
    	else 0
    end) 英语
    from student s left join score sc on s.stu_id = sc.stu_id
    left join course c on c.course_id = sc.course_id
    group by s.stu_id;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    iceberg建表与参数
    如何提升新闻公关发稿的效果?
    计算机二级Python题目13
    C语言之字符函数&字符串函数篇(1)
    虚拟机三种网络模式
    springSecurity自定义认证逻辑,重写登陆,token过滤,退出
    【Linux学习】- POSIX多线程技术
    工控上位机程序为什么只能用C语言?
    如何打造智慧公厕管理系统,提升公共厕所智能化服务质量?
    VueCli入门
  • 原文地址:https://blog.csdn.net/weixin_44953152/article/details/125438347