面试题记1

面试宝典:

具体问题具体分析

一、java基础(集合,线程,锁):

String、StringBuffer、StringBuilder的区别:

String 底层是数组,所以长度不可变,如果想改变String就只能新建数组。

StringBuffer 底层也是数组,不过StirngBuffer作为一个对象类,里面封装了许多方法,可以对String 进行一系列操作。不过StringBuffer线程安全,因为它的方法都使用了同步关键字synchronized进行同步。所以速度较慢。StringBuilder线程不安全,但速度较快,适合单线程。

重写和重载的有什么区别?

重写会有继承关系,子类对父类的方法进行重写,其返回值,参数都不变,可以理解为对父类的方法内进行逻辑增强。重载是对一个方法进行重构,返回类型,参数都可以改变,可以想象多态。

java 基本数据类型哪些,以及所占字节数

int 4字节,short 2字节,Boolean 1字节,long 8字节,char 2字节,byte 1字节,flout 4字节,double 8字节

java中基本数据类型和引用数据类型的区别

引用数据类型包括:类,接口,数组。

是基于基本类型构建的数据类型。

  1. 存储方式:基本数据类型的值直接存储在栈上,引用数据类型存储的是对象的引用,对象的数据存储在堆上。
  2. 大小和范围:基本数据类型具有固定的大小和范围,而引用数据类型的大小和内存使用情况取决于对象本身的大小。
  3. 默认值:基本数据类型有默认值,例如int类型的默认值是0,boolean类型的默认值是false,而引用数据类型的默认值是null。
  4. 传递方式:基本数据类型的传递是按值传递(值被拷贝),而引用数据类型的传递是按引用传递(引用被拷贝)。
  5. 操作和方法:基本数据类型可以直接进行操作和运算,而引用数据类型需要通过对象的方法来进行操作。
  6. 区别于赋值和比较操作:基本数据类型进行赋值和比较操作是直接针对数据值进行的,而引用数据类型进行赋值和比较操作是针对引用进行的。

面向对象的基本特征

封装,继承,多态:

封装是指将数据和方法组合成一个可以独立使用的单位,对外部隐藏数据的具体实现细节。通过封装,我们可以将数据和相关的操作方法封装在一个类中,对外暴露出简单易用的接口。封装提供了信息隐藏和数据访问的控制,使得程序具有更好的安全性和可维护性。

继承是指一个类(子类)可以派生(继承)另一个类(父类)的属性和方法。通过继承,子类可以从父类继承已有的代码,并且可以重新定义或扩展这些代码。这样可以实现代码的重用、减少重复编写和提高代码的可维护性和拓展性。继承还可以建立类之间的层次关系,实现多级继承和多态的基础。

通过多态,我们可以利用父类或接口类型的引用来引用子类或实现类的对象,从而提高代码的灵活性和可扩展性

说一下 Java 8 新特性

Lambda表达式和Stream流

集合:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQU7VcLu-1686886241348)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/java%E9%9B%86%E5%90%88%E6%B1%87%E6%80%BB%E5%9B%BE.jpg)]

ArrayList底层原理

初始长度为0,当有数据插入后会判定是不是达到原来数组长度的0.8,如果达到就进行扩容。

HashMap底层原理

1.7以前是数组加链表,后来发现用链表存储也会在查询过程中消耗大量的时间,所以从1.8开始就采用数组加链表加红黑树,原理也很简单,数据是通过Hash算法进行位置选定的,当产生hash冲突的时候,会使用链地址法。开始是链表,数组初始长度为16,大于0.75时会进行扩容,链表大于8,总数居大于64时,会把链表变成红黑树。

hashMap 跟 hashtable 的区别

Hashtable是Java中提供的另一个散列表实现,与HashMap类似,也是基于哈希表的数据结构。不同之处在于Hashtable是线程安全的,这是由以下几个因素造成的:

  1. 同步机制:Hashtable内部使用synchronized关键字来保证对内部数据的操作是同步的。每个公共方法都会同步整个Hashtable对象,确保在同一时刻只有一个线程可以修改Hashtable的内部状态,从而避免了多线程同时修改数据的问题。

  2. 锁粒度:Hashtable的同步机制是基于对象级别的,也就是说,每个方法都需要获取Hashtable对象的锁,这意味着同一时刻只能有一个线程访问Hashtable的任何一个公共方法。虽然保证了线程安全,但也会导致在多线程并发操作下的性能问题。

  3. 线程安全的迭代器:在遍历Hashtable时,Iterator会获取Hashtable的锁,并且还使用了一个标记字段modCount来检测是否有其他线程对Hashtable进行了修改。如果发现有其他线程修改了Hashtable,迭代器会抛出ConcurrentModificationException异常,从而避免了在遍历过程中出现数据不一致的情况。

  4. 直接使用synchronized关键字实现:与HashMap使用的concurrentHashMap比较,Hashtable则是直接使用synchronized关键字来实现同步,相对简单直接但性能较差。

虽然Hashtable是线程安全的,但由于使用了全局锁的方式进行同步,效率比较低下。在Java8之后,推荐使用ConcurrentHashMap来替代Hashtable,后者在多线程环境下性能更优。

ConcurrentHashMap 的分段锁怎么实现

ConcurrentHashMap是Java中提供的线程安全的哈希表实现,它可以支持多线程并发访问和修改,相比于Hashtable,ConcurrentHashMap具有更好的性能。

ConcurrentHashMap的一些关键特性如下:

  1. 分段锁:ConcurrentHashMap内部使用了分段锁(Segment),每个锁对应着一个桶(Segment)。不同的线程可以同时访问不同的桶,从而实现了并发访问的能力。与Hashtable使用的是一个全局锁相比,分段锁可以降低锁的粒度,提高并发性能。在Java8之后的版本中,分段锁被废弃,采用了更高效的CAS(Compare and Swap)算法以及无锁的方法来实现并发操作。

  2. 线程安全的操作:ConcurrentHashMap的基本操作(get、put、remove)都是线程安全的。它可以支持并发的读取操作,多个线程可以同时读取不同的桶,而不会产生冲突。对于写入操作,分段锁确保了线程之间的同步,同一时刻只有一个线程可以修改同一个桶。

  3. 并发度调整:ConcurrentHashMap允许用户调整其并发度(concurrency level)。并发度是指同时可以支持的线程数。默认情况下,ConcurrentHashMap的并发度为16。通过调整并发度,可以平衡线程安全和并发性能之间的关系。

  4. 不会抛出ConcurrentModificationException:与HashMap不同,ConcurrentHashMap的迭代器不会抛出ConcurrentModificationException异常,它能够安全地遍历Map的元素,即使在迭代过程中有其他线程对ConcurrentHashMap进行了修改。

需要注意的是,虽然ConcurrentHashMap提供了线程安全的操作,但并不保证在所有情况下都能保证数据的一致性。对于复合操作,例如get和put的组合,如果多个线程同时执行,仍然需要额外的同步处理来保证数据的正确性。

总之,ConcurrentHashMap是一个高效且线程安全的哈希表实现,适用于多线程并发读写的场景,能够提供更好的性能和可靠性。

线程:

线程和进程的区别,什么是线程和进程
1.进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,在windows中,xx.exe就是一个进程。
2线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
3.进程与线程的区别:线程具有许多传统进程所具有的特征,故又称为轻型进程(Light-Weight Process)或进程元;而把传统的进程称为重型进程(Heavy-Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。
4.根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
5.资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
6.包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权讲程或者轻量级讲程。

7.内存分配:同一进程的线程共享本地进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
8.影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程就死掉了。所以多进程要比多线程健壮。
9.执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

线程的七大参数:
  1. corePoolSize:线程池的核心线程数。核心线程数是线程池中保持活动状态的线程数量,即使线程处于空闲状态,也不会被回收。
  2. maximumPoolSize:线程池的最大线程数。最大线程数是线程池中允许创建的最大线程数量。
  3. keepAliveTime:线程的空闲时间。当线程池中的线程数量超过核心线程数时,空闲的线程在等待任务到来时的最长等待时间。超过这个时间,线程会被回收。
  4. unit:keepAliveTime的时间单位。可以是纳秒、微秒、毫秒、秒等。
  5. workQueue:任务队列。用于保存待执行的任务。线程池会从队列中取出任务来执行。
  6. threadFactory:线程工厂。为线程池提供创建新线程的方式。
  7. handler:任务拒绝处理器。当线程池无法处理新任务时,该处理器会被调用。
线程池的使用步骤:

初始化线程池

提交任务

任务调度

执行任务

线程释放

资源回收

在项目中使用多线程的场景:
  1. 订单处理:当用户下单时,订单需要进行一系列的处理,例如库存检查、支付验证、生成订单号等,这些处理可能需要调用其他系统或服务,可能会花费较长的时间。为了提高用户体验和订单处理效率,可以将订单处理过程放入一个线程池中,每个订单对应一个线程,通过多线程并发地处理订单,可以加快订单处理速度。
  2. 数据分析和处理:在大数据分析和处理中,需要对大量的数据进行计算和分析。如果是串行处理,会导致整个过程非常耗时。可以使用多线程来并发地处理数据,每个线程负责处理一部分数据,最后将结果合并。例如,可以将一批数据拆分为多个部分,每个线程处理其中一部分数据,最后再将各个线程的结果合并,从而加快数据的处理速度。
  3. 文件上传和处理:在文件上传的过程中,可能同时上传多个文件,如果使用单线程处理,会导致其他文件需要等待。可以将文件上传过程放入一个线程池中,每个文件对应一个线程,通过多线程并发地处理文件上传,可以提高文件上传的效率。另外,如果在上传过程中需要对文件进行处理(例如压缩、转码等),也可以使用多线程并发地处理每个文件。
怎么解决并发问题:
  1. 加锁:使用锁机制(如synchronized关键字或Lock接口的实现类)来实现线程同步,保证某一时刻只有一个线程可以访问共享资源。通过加锁可以避免数据竞争和并发冲突,确保线程安全性。
  2. 使用线程安全的数据结构:在多线程环境下,使用线程安全的数据结构(如ThreadLocal、ConcurrentHashMap等)来管理共享数据,这些数据结构内部实现了线程同步的机制,可以保证线程安全性。

synchronized和ReentrantLock的区别
  1. 性能:在低并发的情况下,synchronized的性能通常比ReentrantLock要好,因为synchronized是JVM内置的关键字,而ReentrantLock是使用Java实现的。但在高并发的情况下,ReentrantLock的性能可能更好,因为它使用的是可重入锁的概念,可以灵活地控制锁的获取和释放。

  2. 灵活性:ReentrantLock相比synchronized更灵活,它提供了一些额外的功能。例如,可以通过设置公平性来控制锁的获取顺序;可以使用tryLock()方法尝试获取锁,避免线程阻塞;可以实现分离锁的功能,即一个锁上可以有多个条件等待;可以使用lockInterruptibly()方法实现可中断的锁获取等。

  3. 锁获取方式:synchronized是隐式锁,当线程进入同步代码块或同步方法时,会自动获取锁,并在退出同步代码块或同步方法时自动释放锁而ReentrantLock是显式锁,需要手动的通过lock()方法获取锁,并通过unlock()方法释放锁

  4. 锁的获取顺序:synchronized是非公平锁,即当多个线程同时竞争获取锁时,JVM会随机选择一个线程获取锁。而ReentrantLock可以通过设置公平性来控制锁的获取顺序,默认为非公平锁。

  5. 可重入性:synchronized和ReentrantLock都支持可重入性,即一个线程可以多次获取同一个锁,而不会出现死锁。但值得注意的是,synchronized是自动释放锁的,而ReentrantLock需要显式调用unlock()方法来释放锁。

根据具体的需求和场景,可以选择适合的锁机制来实现线程同步和保证线程安全性。对于简单的同步需求,synchronized是更为简单和方便的选择;而对于复杂的同步需求或需要更高的灵活性和性能时,ReentrantLock会更适合。

并发关键字synchronized的作用?

1.synchronized 是用来控制线程同步的,控制synchronized代码段不被多个线程同时执行,synchronized可以修饰类,方法,变量。

2.在java早期版本中,synchronized属于重量级锁,效率不高。因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的,java的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮助实现,而操作系统实现线程之间的切换时需要从"用户态"->"内核态",这个切换时间成本时相对较高的,这也是早期synchronized效率一般的原因

3.从java 6 官方队JVM层面对synchronized 进行了较大优化,jdk1.6对锁的实现引入了大量的优化,如自旋锁,适应性自旋锁、锁粗化......来减少锁操作的开销

说说自己是怎么使用synchronized 关键字,在项目中用到了吗?

答: synchronized关键字最主要的三种使用方式:

1.修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁;

2.修饰静态方法:也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是当前类的成员(static表明是该类的一个静态资源)。

2.1.所以假设 线程A调用一个实例对象的非静态synchronized 方法,而线程B需要调用这个实例对象所属类的静态synchronized 方法,是允许的,不会发生互斥现象。因为访问静态synchronized 方法占用的锁是当前类的锁,而访问非静态synchronized 方法占用的锁是当前实例对象的锁。

3.修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁;

总结一下: synchronized 关键字加到 static静态方法和synchronized(class)代码块上都是给Class类上锁synchronized 关键字加到实例方法上是给对象实例上锁。提醒一下!尽量不要使用synchronized(String yupi)。因为 JVM当中,字符串常量池具有缓存功能~

项目中的使用举例:“双重检验锁方式实现单例模式”(线程安全)

public class Singleton {
    private volatile static Singleton uniqueInstance;//构造函数

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        //先判断对象是否已经实例化,没有才加锁
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

ThreadLocal 什么?

ThreadLocal,即线程本地变量。如果你创键了一个 ThreadLocal 变量,那么 访问这个变量每个线程都会有这个变量一个本地拷贝,多个线程操作这个 变量✁时候,实际操作自己本地内存里面的变量,从而起到线程隔离✁作用, 避免了线程安全问题。

JVM专栏基础:

垃圾回收:

jvm怎么判断一个对象是不是垃圾

具体来说,JVM会从根对象(例如:栈变量、静态变量)开始,递归遍历所有可达对象,并将它们标记为活动对象。然后,JVM扫描堆中的所有对象,将未被标记的对象认定为垃圾对象,并由垃圾回收器回收。

JVM判断一个对象是否为垃圾对象的过程如下:

  1. 引用计数算法(Reference Counting):JVM中的某些垃圾回收器可能使用引用计数算法来判断对象是否为垃圾。该算法通过给每个对象维护一个引用计数器,当有新的引用指向对象时,计数器增加;当引用被释放或销毁时,计数器减少。当计数器为0时,对象被判定为垃圾对象。但是这种算法容易出现循环引用的情况,导致对象无法被回收。
  2. 可达性分析算法(Reachability Analysis):可达性分析是JVM主要使用的垃圾回收算法。根据该算法,JVM从一组根对象开始,通过遍历引用链,将所有可达对象标记为存活对象,未被标记的对象则被判定为垃圾对象。该算法能够解决循环引用的问题,并且是主流垃圾回收算法的选择之一。

总结来说,JVM判断一个对象是否为垃圾对象的原则是通过可达性分析算法,检查是否存在可达的引用链,如果没有引用链指向该对象,则被认为是垃圾对象,在垃圾回收时会被清理掉。

注意的点: “volatile关键字”:为了防止指令重排(保证多线程下是线程安全的),因为生成一个新的Singleton需要分配内存空间、初始化、指向分配内存地址;

二、Mysql(sql优化):

sql优化:

1、索引优化

2、避免返回不必要的数据

3、适当分批量进行

4、优化 sql 结构

5、分库分表

6、读写分离

在实际开发中,进行SQL优化可以提高数据库查询性能和响应速度。以下是一些常见的SQL优化技巧:

  1. 索引优化:确保表中的关键字段有合适的索引,索引可以加快检索速度。但是需要注意的是,过多的索引也会影响写入操作的性能,因此需要在性能和写操作之间做出权衡。
索引优化是提高数据库查询性能的关键步骤之一。以下是索引优化的具体步骤:

1. 分析查询需求:首先需要分析常见的查询需求,了解哪些查询是频繁执行的,哪些查询耗时较长。根据查询需求的特点选择优化策略。

2. 选择适当的索引字段:根据查询需求选择合适的索引字段,通常是经常用于查询条件的字段。选择字段时需要综合考虑字段的选择性和查询频率。选择性是指索引字段具有足够的区分度,能够过滤出较小的数据集。查询频率是指该索引字段在查询中的使用频率。

3. 避免过多的索引:虽然索引可以提高查询性能,但过多的索引会增加存储和维护的开销,还可能导致更新操作变慢。只为常见的查询需求创建索引,并且避免重复索引(例如,字段A已经被索引,就不需要再为(A,B)这样的联合索引再创建一个索引)。

4. 联合索引的使用:对于有多个限制条件的查询,可以考虑创建联合索引,以提高查询效率。在创建联合索引时,需要根据查询条件的顺序进行安排,优先考虑选择性高的字段作为联合索引的前缀字段。

5. 适当使用覆盖索引:覆盖索引是指索引本身就包含了查询所需的字段,不需要去表中获取数据。使用覆盖索引可以减少磁盘IO操作,提高查询性能。

6. 定期维护和优化索引:随着业务数据的不断变化,索引的使用效果可能会发生变化。需要定期进行索引的维护和优化,例如重新组织索引、重新评估索引的选择性等。

7. 监控和测试:在进行索引优化操作后,需要根据数据库的监控数据进行测试和验证。通过对比优化前后的性能指标,确定优化策略的有效性。

需要注意的是,索引优化是一个复杂的过程,需要综合考虑数据库的架构、查询需求和业务特点。不同的数据库系统和版本可能有不同的优化方式和策略,因此具体的索引优化步骤和方法可能会有所不同。建议在实际应用中,结合具体的情况进行索引优化。另外,在进行索引优化之前,需要备份数据库,以防出现意外情况。
  1. 查询优化:尽量减少查询语句中的字段数目、表数目和联接数,只查询所需的字段。同时,使用合适的查询条件,避免全表扫描。避免使用子查询和函数:子查询和函数的使用会增加数据库的负载,尽量使用连接或其他优化的方式来替代。
  2. 缓存数据:将频繁使用的数据缓存在应用程序中,减少对数据库的访问。使用缓存可以显著提高读取性能,但需要注意缓存数据的更新机制和缓存数据的一致性。
  3. 使用合适的数据类型:选择合适的数据类型可以减少存储和计算资源的消耗。例如,使用整型来存储整数数据,避免使用长整型或浮点型。
  4. 批量操作和批量提交:对于批量的数据操作,尽量使用批量操作的方式,减少与数据库的交互次数。批量提交可以提高写入性能,合理控制事务的大小和提交的频率。

以上是一些常见的SQL优化技巧,具体的优化策略和方法需要根据实际情况进行选择和实施。同时也需要注意在优化过程中进行测试和验证,确保优化操作的正确性和有效性。

1.4、索引的概念

索引的分类:

分为:聚集索引和非聚集索引

聚集索引是主键索引,非聚集索引是其他的索引,比如b树索引,唯一索引,普通索引。。。

MySQL 索引使用有哪些注意事项呢(创建索引的原则)

从三个方面回答:索引哪些情况会失效,索引不适合哪些场景,索引一些规则特性

索引失效:

1.查询条件包含or,可能导致索引失效

2.如果字段类型是字符串,where时一定用引号括起来,否则索引失效

3.like 通配符可能导致索引失效。

4、联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。

5、在索引列上使用 mysql 的内置函数,索引失效。 

6、 对索引列运算(如,+、-、*、/),索引失效。 

7、 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。 

8、索引字段上使用 is null, is not null,可能导致索引失效。 

9、左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。 

10、mysql 估计使用全表扫描要比使用索引快,则不使用索引。

索引不适合哪些场景

1、数据量少的不适合加索引 

2、更新比较频繁的也不适合加索引 

3、区分度低的字段不适合加索引(如性别)

索引的一些潜规则 

1、覆盖索引 

2、回表 

3、索引数据结构(B+树) 

4、最左前缀原则 

5、索引下推

mysql遇到死锁是怎么解决的

我一般是这样解决的: 

1、查看死锁日志 show engine innodb status; 

2、找出死锁 Sql 

3、分析 sql 加锁情况 

4、模拟死锁案发 

5、分析死锁日志 

innodb行锁,

如何优化查询过程中的数据访问

访问数据太多导致查询性能下降,首先要确定应用程序是否在检索大量超过需要的数据,可能是太多行或列确认MySQL服务器是否在分析大量不必要的数据行避免犯下SQL语句错误。
1)查询不需要的数据。解决办法:使用limit解决。
2)多表关联返回全部列。解决办法:指定列名。

3)总是返回全部列。
解决办法:
3.1)避免使用select*重复查询相同的数据。
3.2)可以缓存数据,下次直接读取缓存是否在扫描额外的记录
3.3)使用explain进行分析,如果发现查询需要扫描大量的数据,但只返回少数的行,可以通过试试下面方法优化:
使用索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结
果。或修改数据库范式。或是重写SQL语句,让优化器以更优的方式执行查询

Hash索引和B+索引有什么区别

实现原理:

Hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值对,之后进行回表查询获得实际数据。
B+树底层实现是多路平衡查找树。对于每一次查询都是从根节点触发,查找到叶子节点可以获得所查找的键值,然后根据查询判断是否需要回表查询数据。

不同之处:

1、hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询。因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。而B+树的所有节点遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围查询。
2、hash索引不支持使用索引进行排序,原理同上。
3、hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。
4、hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
5、hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能较差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。
因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度。而不需要使用hash索引

8 limit 1000000,10 加载很慢的话,你是怎么解决的呢

1.如果是按照顺序查询的,可以返回上一次查询的最大记录偏移量

2.建议跟业务讨论,有没有必要查这么后的分页啦。因为绝大多数用户都不会往 后翻太多页。

3.

Mysql 中in和exist的区别
都是范围查询

数据库最费劲的就是跟程序链接释放。假设链接了两次,每次做上百万次的数据集查询,查完就走,这样就只做了两次;相反建立了上百万次链接,申请链接释放反复重复,这样系统就受不了了。即 mysql 优化原则,就是小表驱动大
表,小的数据集驱动大的数据集,从而让性能更优。因此,我们要选择最外层循环小的,也就是,如果 B 的数据量小于 A,适合使用 in,如果 B 的数据量大于 A,即适合选择 exists,这就是 in 和 exists 的区
别

三、SSM框架:

SpringBean的生命周期:

对象创建

1、从xml配置的Bean,@Bean注解,或者Java代码BeanDefinitionBuilder中读取Bean的定义**,实例化Bean对象**;

2、设置Bean的属性

3、注入Aware的依赖(BeanNameAware,BeanFactoryAware,ApplicationContextAware);

4、执行通用的方法前置处理,方法: BeanPostProcessor.postProcessorBeforeInitialization()

5、执行 InitalizingBean.afterPropertiesSet() 方法

6、执行Bean自定义的初始化方法init,或者 @PostConstruct 标注的方法;

7、执行方法BeanPostProcessor.postProcessorAfterInitialization()

8、创建对象完毕;

对象销毁

9、执行 DisposableBean.destory() 方法;

10、执行自定义的destory方法或者 @PreDestory 标注的方法;

11、销毁对象完毕

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4ZmTtba-1686886241350)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.jpg)]

Bean的生命周期(作用域)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bbXIIuFC-1686886241352)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/bean%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F.jpg)]

循环依赖

是什么:

循环依赖就是循环引用,两个或者多个bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则他们最终反映为一个环。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UzjX2dN1-1686886241353)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8.jpg)]

Spring中循环依赖场景有:
(1)构造器的循环依赖
(2)field属性的循环依赖。

怎么解决循环依赖:

从bean初始化步骤我们可以知道,循环依赖主要发生在构造器循环依赖和field循环依赖。

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;

earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;

singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。

getSingleton方法的大概处理过程为:

1 判断singletonObjects单例池中是否存在,存在则返回
2 不存在则判断earlySingletonObjects缓存中是否存在,存在则返回
3 不存在则判断singletonFactories缓存中是否存在,不存在则返回null
4 存在则通过该存储工厂创建出最终的bean
5 将该bean加入earlySingletonObjects缓存并从singletonFactories缓存中中移除

总结

当一个Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于Java的引用传递),这也说明了另外一点,基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。还要说明一点,Spring默认的Bean Scope是单例的,而三级缓存中都包含singleton,可见是对于单例Bean之间的循环依赖的解决,Spring是通过三级缓存来实现的

IOC:

控制反转:将原本自己手动创建对象的过程交给Spring容器来管理,IOC底层容器就是对象工厂(BeanFactory接口),是基于反射来实现的。IOC实际上就是一个Map用来存放键和值。

AOP:

面向切面编程:把通用功能封装起来,便于减少系统的重复代码,降低耦合,并且有利于未来的可拓展性和可维护性。就是不修改源代码的情况下,在主干功能中添加新功能。

实现原理(JDK和CGLIB)

基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理

SpringMVC运行流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXzdCDne-1686886241354)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/Springmvc%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B2.jpg)]

1,客户端发送请求到这个DispatcherServlet前端控制器

2,前端控制器会通过这个HandlerMapping处理器映射器,找到合适的处理器,就是通过这个输入的url,找到对应的handler

3,返回处理器的执行链,里面会包含多个拦截器的信息,以及需要查找的处理器handler的信息

4,找处理器适配器HandlerAdapter,这一步开始就会去调用handler里面的方法

5,通过执行这个handler里面的方法,会去找具体的controller方法

6,找到具体的controller之后,会返回一个modelAndView给这个HanderAdapter给处理器适配器

7,处理器适配器获取到ModelAndView之后,会将这个结果返回给DispatcherServlet前端控制器

8,通过这个ViewResolver视图解析器进行解析这个ModelAndView

9,解析完成之后,会将这个view返回给前端DispatcherServlet前端控制器

10,将model中的数据填充到这个view视图里面,最后去渲染视图

Mybatis的$和#:

1、#{ }是预编译处理,MyBatis在处理#{ }时,它会将sql中的#{ }替换为?,然后调用PreparedStatement的set方法来赋值,传入字符串后,会在值两边加上单引号,如上面的值 “4,44,514”就会变成“ ‘4,44,514’ ”;

2、 是字符串替换, M y B a t i s 在处理 { }是字符串替换, MyBatis在处理 是字符串替换,MyBatis在处理{ }时,它会将sql中的${ }替换为变量的值,传入的数据不会加两边加上单引号

使用${ }会导致sql注入,不利于系统的安全性!

事务传播行为:

传播行为含义
PROPAGATION_REQUIRED表示当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则,会启动一个新的事务
PROPAGATION_SUPPORTS表示该方法不需要事务上下文,但是如果存在当前的事务的话,那么该方法会在这个事务汇总运行
PROPAGATION_MANDATORY表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW表示当前方法必须运行在它自己的事务中。一个新的事务将被启动,如果存在当前事务,在该方法执行期间,当期事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED表示当前方法不应该运行在事务中,如果存在当前事务,在该方法运行期间,当前事务将被挂起,如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER表示当前方法不应该运行在事务上下文中,如果当前正有一个事务在运行,则抛出异常
PROPAGATION_NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独的提交或回滚。如果当前事务不存在,那么其行为和 PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的,可以参考资源管理器的文档来确认它们是够支持嵌套事务

事务的7种传播行为

Spring事务传播行为

Mybatis的原理:

mybatis是一个用Java编写的持久层框架,它使用ORM实现了结果集的封装。它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等烦杂过程

Configuration MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中
SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等
ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler 负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
MappedStatement MappedStatement维护一条<select|update|delete|insert>节点的封装
SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql 表示动态生成的SQL语句以及相应的参数信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z2y6Rwg6-1686886241356)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/mybatis%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84.jpg)]

mybatis执行sql语句的流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mXBzHK0T-1686886241357)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/mybatis%E6%89%A7%E8%A1%8Csql%E8%AF%AD%E5%8F%A5%E7%9A%84%E6%B5%81%E7%A8%8B.jpg)]

SpringBoot的装配原理

四、redis

redis的数据结构:

String,Hash,set,List,zset

缓存击穿,缓存穿透,缓存雪崩:

穿透:

指查询一个一定不存在数据,由于缓存是不命中时需要从数据库 查询,查不到数据则不写入缓存,这将导致这个不存在数据每次请求都要到 数据库去查询,进而给数据库带来压力。

解决方法:

 1.如果是非法请求,我们在 API 入口,对参数进行校验,过滤非法值。

2.如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。但是如有有写 请求进来话,需要更新缓存哈,以保证缓存一致性,同时,最后给缓存设置适当 过期时间。(业务上比较常用,简单有效)

 3.使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过 滤器判断值是否存在,存在才继续往下查。

击穿

指热点key 在某个时间点过期✁时候,而恰好在这个时间点对这个 Key 有大量并发请求过来,从而大量请求打到

解决方法:



1.使用互斥锁方案。缓存失效时,不是立即去加载 db 数据,而是先使用某些带成 功返回原子操作命令,如(Redis  setnx)去操作,成功时候,再去加载 db 数据库数据和设置缓存。否则就去重试获取缓存。 

2. “永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去 更新和设置过期时间。

雪崩

指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接 访问数据库,引起数据库压力过大甚至 down 机。
为什么redis的核心功能是单线程,性能还这么高?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJi10yKh-1686886241357)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/redis%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%AB.jpg)]

1.完全基于内存,绝大部分请求是存粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作时间复杂度都是O(1)

2.采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多进程切换而消耗CPU,不用去考虑各种锁问题,不存在加锁释放锁的操作,没有因为可能出现死锁出现性能消耗。

3.使用多路I/O复用模式,非阻塞I/O

4.使用底层模式不同,他们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

数据库和redis的一致性问题:

保证MySQL和Redis之间的一致性是一个挑战,因为它们是两个不同的数据存储系统,有不同的数据模型和一致性机制。以下是一些常用的方法来保证MySQL和Redis之间的一致性:

  1. 强一致性要求下,使用分布式事务:可以使用两阶段提交(2PC)或者补偿事务(TCC)等分布式事务协议来确保MySQL和Redis操作的一致性。这样可以在保证数据库和缓存同时成功或失败的情况下,保持数据的一致性。

  2. 弱一致性要求下,使用消息队列:将MySQL和Redis的操作通过消息队列进行异步化处理。当MySQL的数据发生变化时,将变化的数据作为消息发送到消息队列,并使用消费者将这些消息应用到Redis中。这样虽然可能会出现短暂的不一致期,但通过消息队列可以保证最终的一致性。

  3. 选择合适的数据同步策略:可以使用binlog或者Canal等工具实时同步MySQL的变更到Redis中。这样可以保持MySQL和Redis的数据一致性,但需要付出额外的系统资源和网络开销。

  4. 在应用层中实现双写机制:在应用程序中同时操作MySQL和Redis,确保两边的数据保持一致。这需要编写额外的代码逻辑来处理数据的更新操作,并确保更新操作的原子性和一致性。

需要注意的是,不同的场景和业务需求对一致性要求的程度有所不同,选择合适的一致性方案需要综合考虑系统性能、可用性和一致性的权衡。无论采取哪种策略,都需要仔细评估和测试,以确保数据的一致性和系统的可靠性。

Redis过期策略和内存淘汰策略:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyuQWVbQ-1686886241358)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/redis%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5.jpg)]

Redis的持久化机制是什么?各自的优缺点?
Redis提供两种持久化机制:RDB(默认)和AOF机制。
RDB:是Redis DataBase缩写快照
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件dump.rdb。通过配置文件中的save参数来定义快照的周期

优点:
1.只有一个文件dump.rdb,方便持久化

2.容灾性好,一个文件可以保存到安全的硬盘
3.性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了Redis的高性能
4.相对于数据集较大时,比AOF的启动效率更高
缺点:
1.数据安全性低,RDB是间隔一段时间进行持久化,如果持久化之间Redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

AOF:持久化

AOF持久化(即Append Only file)持久化方式,则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复优点:
1.数据安全,AOF持久化可以配置appendfsync属性,有always,每进行一次命令操作就记录到AOF文件中—次
2.通过append模式写文件,即使中途服务器宕机,可以通过Redis-checkaof 工具解决数据一致性问题3.AOF机制的rewrite模式。AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)
缺点:
1.AOF文件比RDB文件大,且恢复速度慢2.数据集较大的时候,启动比RDB效率低
优缺点是什么?
1.AOF文件比RDB更新频率高,优先使用AOF还原数据。

2.AOF比RDB更安全也更大
3.RDB性能比AOF好
4.如果两个都配了优先加载AOF



怎么选择RDB和AOF
1.如果数据不能丢失,RDB 和 AOF 混用:就是内存快照以一定频率执行,两次快照之间,再使用 AOF 记录这期间✁所有命令操作.
2.如果只作为缓存使用,可以承受几分钟✁数据丢失✁话,可以只使用 RDB。
3.如果只使用 AOF,优先使用 everysec ✁写回策略。

如何解决Redis的并发竞争key问题

多个系统同时对一个Key进行操作。但是,执行后的顺序和我们期望的顺序可能会不—样,这也导致了结果的不同

我使用过的方案:分布式锁(zookeeper和Redis都可以实现分布式锁)。这里提醒一下!!! (如果不存在Redis的并发竞争Key问题,不要使用分布式锁,这样会影响性能)基于zookeeper临时有序节点可以实现的分布式锁。
大致思路:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。然后判断一下是否获取锁,判断获取锁也很简单,只需要判断有序节点中序号小的那-个。当释放锁时,只需将这个瞬时节点删除即可。同时也能避免服务宕机导致的锁无法释放,而产生的死锁问题。业务流程走完之后,删除对应的子节点释放锁~
在真正实践当中,当然还是要以可靠性为主,我是推荐使用zookeeper! !



什么是热点Key
在 Redis 中,我们把访问频率高✁ key,称为热点key
导致问题:
由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常服务。

解决方法:
1.Redis 集群扩容:增加分片副本,均衡读流量;
2.将热 key 分散到不同✁服务器中;
3.使用二级缓存,即 JVM 本地缓存,减少 Redis ✁读请求。
Redis常用场景
1.缓存

我们一提到 redis,自然而然就想到缓存,国内外中大型✁网站都离不开缓存。合理✁利用缓存,比如缓存热点数据,不仅可以提升网站✁访问速度,还可以降低数据库 DB ✁压力。并且,Redis 相比于 memcached,还提供了丰富✁数据结构,并且提供 RDB 和 AOF 等持久化机制,强✁一批。

2.排行榜
当今互联网应用,有各种各样✁排行榜,如电商网站✁月度销量排行榜、社交APP ✁礼物排行榜、小程序✁投票排行榜等等。Redis 提供✁ zset 数据类型能够实现这些复杂✁排行榜
比如,用户每天上传视频,获得点赞✁排行榜可以这样设计:
获得点赞用zset添加,点赞量上升用zincrby修改,用户作弊用zrem删除,展示获得赞最多的用zrevrangebyrank

3.计数器应用
视频的播放数、网站的浏览数。这些播放数、浏览数一般要求实时✁,每一次播放和浏览都要做加 1✁操作,如果并发量很大对于传统关系型数据✁性能是一种挑战。Redis 天支持计数功能而且计数✁性能也非常好,可以说是计数器系统✁重要选择。

4.共享Session
如果一个分布式 Web 服务将用户✁ Session 信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以使用 Redis 将用户✁ Session 进行集中管理,每次用户更新或者查询登录信息都直接从Redis
中集中获取。


5.分布式锁

几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源✁并发访问✁技术难题,如秒杀、下单减库存等场景。
用 synchronize 或者reentrantlock 本地锁肯定是不行✁。如果是并发量不大话,使用数据库✁悲观锁、乐观锁来实现没啥问题。但是在并发量高✁场合中,利用数据库锁来控制资源✁并发访问,会影响数据库✁性能。实际上,可以用 Redis ✁ setnx 
6.社交网络
赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站✁必备功能,由于社交网站访问量通常比较大,而且传统✁关系型数据不太适保存 这种类型✁数据,Redis 提供✁数据结构可以相对比较容易地实现这些功能。
7.消息队列
8.位操作
用于数据量上亿✁场景下,例如几亿用户系统✁签到,去重登录次数统计,某用户是否在线状态等等。腾讯 10 亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?千万别说给每个用户✁立一个 key,然后挨个记(你可以算一下需要✁内存会很恐怖,而且这种类似✁需求很多。这里要用到位操作——使用 setbit、getbit、bitcount 命令。原理是:redis 内构✁一个足够长✁数组,每个数组元素只能是 0 和 1 两个值,然后这个数组✁下标 index 用来表示用户id(必须是数字哈),那么很显然,这个几亿长✁大数组就能通过下标和元素值(0 和 1)来构✁一个记忆系统。

单点登录(共享Session)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zLGKKec2-1686886241361)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/%E5%85%B1%E4%BA%ABsession.jpg)]

key的设计:

随机生成

因为用户信息是对象,建议使用hash数据结构,占用内存更少,且支持对单个字段的增删改查。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lDKND1SD-1686886241361)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/%E5%9F%BA%E4%BA%8Esession%E7%9A%84%E7%99%BB%E5%BD%95.jpg)]

注意事项
1.存入Redis的数据一定要设置过期时间!
2.存入 Redis的数据尽量保证精简和安全,比如存入用户信息时可以移除密码等敏感数据
3.已登录用户访问系统后,记得刷新token过期时间(续期)。并且访问任何路径时都要刷新token,而不仅是需要登录的路径。可以新增1层独立的拦截器来实现token刷新,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oXz3mT7C-1686886241362)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/%E7%99%BB%E5%BD%95%E6%8B%A6%E6%88%AA%E5%99%A8.jpg)]

缓存:

缓存更新策略:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wRAMGM5K-1686886241363)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/%E7%BC%93%E5%AD%98%E6%9B%B4%E6%96%B0%E7%AD%96%E7%95%A5.jpg)]

主动更新缓存的几种方式:

cache aside:以数据库的数据为准,业务层自己来控制缓存的写入

write / read through:使用现成的数据写入服务,让服务来维护数据库和缓存的一致性,我们只用写数据即

write behind cache:先更新缓存,通过异步线程定期将缓存的数据持久化(同步到数据库中)

是选择删除缓存还是更新缓存?
建议选择删除缓存,等待下次查询自动设置缓存,做到随用随取,可以避免每次更新数据库后都更新缓存的无效写操作。

为什么选择分布式全局ID生成器

1.对于订单这种数据,数据库自增的规律性太明显,会暴露一些信息(比如根据昨日和今日的订单号差值看出销量)
2.数据量过大时,不同表的id分别自增,容易出现id冲突

秒杀业务:

核心流程:判断日期和库存、扣减库存、创建订单。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oRzoiEJW-1686886241363)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/%E7%A7%92%E6%9D%80%E6%B5%81%E7%A8%8B.jpg)]

秒杀库存信息和商品信息最好是独立的两张表

😸微服务组件:

1、Nacos

🐷nacos是什么

主要功能包括服务发现服务健康检查动态配置管理服务元数据管理等。在微服务架构中,服务的数量和变化非常快速,Nacos 提供了对服务的自动发现和管理,避免了手工维护服务清单和配置信息的繁琐工作,同时可以提供服务路由和流量控制等功能。具体来说,Nacos 在微服务中的作用包括:

  1. 服务注册与发现:微服务可以将自己注册到 Nacos 的服务注册中心,其他微服务就可以通过查询服务注册中心来查找和访问该服务

  2. 动态配置管理:可以让微服务在运行时动态获取配置信息,而不需要重启应用或重新部署服务。同时,可以实现不同环境、不同服务的动态配置管理,为微服务灰度发布、AB 测试等提供了支持。

  3. 服务健康检查:可以定时检查微服务的健康状态,从而发现并剔除不可用的服务,保证系统的高可用性。

  4. 服务路由和流量控制:可以对微服务的请求进行流量控制和管理,保证系统的稳定性和安全性。

综上所述,Nacos 是一款非常强大的微服务注册中心和配置中心,能够极大地简化微服务的部署和管理,提高系统的可靠性和可维护性。

nacos注册中心流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wrp35aJl-1686886241364)(https://zjava.oss-cn-beijing.aliyuncs.com/photo/nacos%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E6%B5%81%E7%A8%8B.jpg)]

2、Ribbon

Ribbon是Netflix开源的一个负载均衡框架,主要用于在微服务架构中实现客户端的负载均衡和故障转移。下面是我对Ribbon的理解:

  1. 负载均衡:Ribbon可以将客户端请求均匀地分发到多个目标服务实例上,从而实现负载均衡。在微服务架构中,一个服务通常会有多个实例来提供高可用性和性能的需求。Ribbon可以根据可配置的规则和策略,自动选择一个可用的目标服务实例来处理每个请求。

  2. 客户端控制:Ribbon是一个客户端负载均衡器,与服务端无关。客户端通过Ribbon来选择目标服务实例,无需服务端的额外配置。这种方式使得客户端具有更大的灵活性和控制权,能够根据自身需求自主决策选择服务实例。

  3. 故障转移:当某个目标服务实例不可用或出现故障时,Ribbon能够自动切换到其他可用的实例,实现故障转移。Ribbon提供了多种故障转移策略,如轮询、随机、权重等,可以根据实际需求进行配置。

  4. 自定义规则和策略:Ribbon支持自定义的负载均衡规则和策略。开发者可以根据自己的需求,实现自定义的负载均衡算法,或者指定特定的规则来控制服务实例的选择。例如,可以基于服务实例的性能指标、健康状况等进行动态调整和选择。

  5. 与Spring Cloud集成:Ribbon是Spring Cloud框架的一部分,与其他组件(如Eureka、Feign等)紧密集成。通过在Spring Boot应用中引入相应的依赖和配置,可以快速实现基于Ribbon的负载均衡功能。

总的来说,Ribbon是一个强大的客户端负载均衡框架,能够帮助开发者实现服务实例的负载均衡和故障转移。它的灵活性和可定制性使得开发者能够根据实际需求对负载均衡策略进行配置和扩展,提高系统的可靠性和性能。

3、feign

  1. 声明式的HTTP客户端:Feign允许开发者通过定义接口的方式来声明服务间的调用,而无需编写实际的HTTP请求代码。开发者只需要定义一个接口,并使用Feign的注解来描述接口的请求方法、参数、路径等,Feign将自动根据注解生成对应的HTTP请求。
  2. 与Spring Cloud集成:Feign是Spring Cloud框架的一部分,可以与其他组件(如Eureka、Ribbon、Hystrix等)紧密集成。通过在Spring Boot应用中引入相应的依赖和配置,可以快速实现基于Feign的服务间调用,无需编写繁琐的HTTP请求和请求处理代码。
  3. 自动负载均衡:Feign可以与Ribbon集成,实现自动的负载均衡。在Feign接口中,可以使用服务名来定义服务的调用地址,而不需要具体的主机和端口。Feign将会根据服务名在注册中心(如Eureka)中查找可用的服务实例,并通过Ribbon进行负载均衡。
  4. 服务降级与熔断:Feign可以与Hystrix集成,实现服务的降级与熔断。通过在Feign接口的方法上添加Hystrix的注解,可以配置服务的降级逻辑和熔断策略。当调用的服务不可用或失败时,Hystrix将会触发降级操作,并返回预设的默认值或执行备选逻辑。
  5. 日志与监控:Feign提供了可配置的请求日志功能,可以记录服务间的调用细节。通过开启请求日志,可以方便地进行服务的监控和故障排查。

feign和dubbo的区别

Feign和Dubbo都是分布式服务框架,主要用于实现微服务架构。它们的区别主要包括以下几个方面:

1. 通信协议:Feign基于HTTP协议,Dubbo基于RPC协议。HTTP协议是一种基于文本的协议,而RPC协议是一种二进制协议,因此在传输效率上,Dubbo比Feign更快。

2. 注册中心:Feign不需要注册中心,客户端直接通过HTTP请求调用微服务。Dubbo需要注册中心,客户端先从注册中心获取服务提供者的地址,再通过RPC调用微服务。

3. 编程模型:Feign采用声明式接口的方式进行编程,不需要手动创建调用代理。Dubbo需要手动编写调用的接口和代理实现类。

4. 接口声明:Feign的接口声明和SpringMVC的接口声明非常相似,容易上手。Dubbo的接口声明有一定的学习曲线,需要掌握一定的Dubbo注解使用方法。

5. 服务限流与降级:Dubbo支持服务的限流、熔断、降级等机制,可以避免单个微服务出现故障时导致整体系统崩溃。Feign没有此功能,需要依赖第三方组件来实现。

总之,Feign适合简单的微服务场景,Dubbo适合中大型的、复杂的微服务场景,具体选择哪个框架需要结合具体的业务需求和技术架构来综合考虑。

4、getway

网关(Gateway)是一个位于客户端和服务器之间的中间层,用于处理客户端请求和服务器响应。下面是我对网关(Gateway)的理解:

  1. 统一入口:网关可以作为系统的统一入口,客户端请求首先会经过网关进行处理。网关可以提供一种集中式的API管理方式,对外暴露一组统一的API接口,隐藏了系统内部的微服务架构,从而提供更好的安全性和可维护性。

  2. 请求路由:网关可以根据请求的URL路径或其他条件,将请求转发给不同的微服务。通过配置路由规则,网关能够实现请求的动态路由,将请求导向不同的后端服务。

  3. 服务发现和负载均衡:网关通常与服务注册中心(如Eureka)和负载均衡组件(如Ribbon)集成,可以动态地发现可用的服务实例,并根据负载均衡策略将请求分发给合适的服务实例。这样可以确保请求被平均分配到各个服务实例上,提高系统的性能和可伸缩性。

  4. 安全认证和授权:网关可以集成认证和授权机制,对请求进行身份验证和授权处理。网关可以进行用户登录认证、请求签名校验、访问权限验证等操作,保障系统的安全性。

  5. 限流和熔断:网关可以实现请求的限流和熔断功能,通过配置限流规则和熔断策略,对请求进行控制和管理。当请求达到设定的上限或后端服务出现故障时,网关可以拒绝或降级请求,防止系统被过载压力或故障拖垮。

  6. 缓存和日志:网关可以对请求和响应进行缓存和日志记录,提供更快的响应时间和更好的故障排查能力。缓存可以减轻后端服务的压力,提高系统性能;日志可以记录请求的详细信息,方便进行调试和监控。

总的来说,网关是一个位于客户端和服务器之间的中间层,通过统一入口、请求路由、服务发现和负载均衡、安全认证和授权、限流和熔断、缓存和日志等功能,提供了一种统一的、安全可靠的服务访问方式。网关在微服务架构中起到了重要的作用,提高了系统的可扩展性、性能和安全性。

5、seata

Seata是一个开源的分布式事务解决方案,旨在解决分布式系统中的数据一致性问题。下面是我对Seata的理解:

  1. 分布式事务支持:Seata 提供了分布式事务管理的能力,支持 ACID(原子性、一致性、隔离性和持久性)特性。它的核心理念是将分布式事务抽象为一个全局事务和多个参与者事务,通过协调器协调各个参与者的提交和回滚操作,确保事务的一致性。

  2. 强一致性保证:Seata采用了两阶段提交(2PC)的协议来保证分布式事务的强一致性。在全局事务的提交阶段,会向各个参与者发送提交请求,只有当所有参与者反馈成功后,协调器才会发出最终的提交指令。如果有任何一个参与者反馈失败,协调器将会发出回滚指令,保证数据的一致性。

  3. 高性能与高可用:Seata通过事务日志在高可用存储中持久化全局事务的状态,从而实现了高可用性。此外,Seata还提供了高性能的事务记录和事务管理,通过减少网络通信和优化协议的方式,提高了分布式事务的性能。

  4. 多种模式的支持:Seata支持多种事务模式,包括 AT(自动补偿型)模式、TCC(尝试-确认-取消)模式和Saga模式。不同的模式适用于不同的业务场景,可以根据实际需求选择合适的模式。

  5. 广泛的应用场景:Seata可以广泛应用于各种需要分布式事务的场景,如电商交易、订单支付、库存管理等。通过引入Seata,开发者可以简化开发过程,降低分布式事务的实现复杂度,并提供数据一致性的保证。

总的来说,Seata是一个开源的分布式事务解决方案,通过提供分布式事务管理、强一致性保证、高性能与高可用、多种模式支持等功能,帮助开发者处理分布式系统中的数据一致性问题。Seata在实际应用场景中具有较广泛的适用性,可以有效解决分布式事务的挑战。

seata的核心组成和使用步骤

Sentinel是一款用于微服务的流量控制和熔断降级的框架,它的核心组成包括:

1. Dashboard:可视化的控制台,可以用它实时监控应用的流量和指标,以及进行流量控制和熔断降级的配置。

2. Sentinel Client:即网关和客户端SDK,它是实现流量控制和熔断降级的核心组件,可以作为独立模块运行,也可以集成到具体的应用中。

3. Rule配置:Sentinel通过Rule配置来实现流量控制和熔断降级。Rule是一组基于QPS、线程数等指标的规则,可以实现API级别、方法级别、参数级别的流量控制和熔断降级。

使用Sentinel的步骤如下:

1. 引入Sentinel的依赖,配置Sentinel的相关参数,例如Dashboard的地址、应用名称等。

2. 配置Sentinel规则,例如API的并发数、平均响应时间等规则,通过配置可以实现流量控制、降级等功能。

3. 启动Sentinel Client,可以将Sentinel Client作为独立的模块运行,也可以将其集成到具体的应用中。

4. 运行应用程序并访问API,通过Dashboard来监控API的请求和响应情况,根据监控结果适时调整规则,以实现流量控制和熔断降级功能。

总之,Sentinel是一个可扩展、高可用的流量控制和熔断降级框架,可以为微服务架构提供重要的支持。对于使用Sentinel的开发者来说,需要了解其核心组成并按照步骤进行规则配置和启动,从而实现流量控制和熔断降级的功能。

6、sentinel

Sentinel是阿里巴巴开源的一款高可用的、轻量级的流量控制和熔断降级框架,用于保护分布式系统和微服务架构的稳定性和可靠性。下面是我对Sentinel的理解:

  1. 流量控制:Sentinel可以通过设置规则来限制系统的流量,包括并发数、QPS(每秒请求数)等。通过流量控制,可以保护系统免受大量的请求冲击,防止系统过载,提高系统的稳定性。

  2. 熔断降级:Sentinel提供了熔断降级的功能,可以在系统出现故障或异常情况时,自动地对某些服务或接口进行熔断或降级处理,避免故障的扩散和影响。通过熔断降级,可以提高系统的容错性和可靠性。

  3. 实时监控和统计:Sentinel可以实时监控系统的流量、延迟、错误率等指标,提供实时的系统状态和度量报告。通过监控和统计,可以及时了解系统的运行状况,进行故障排查和性能优化。

  4. 规则动态调整:Sentinel的规则可以动态地调整,无需重启应用即可生效。这样可以方便地根据实际情况对系统进行调整和优化,提高系统的灵活性和适应性。

  5. 多种应用场景:Sentinel可以广泛应用于各种分布式系统和微服务架构中,如API网关、微服务网关、RPC服务调用等。它可以保护系统免受突发流量的冲击,提高服务的稳定性和可靠性。

总的来说,Sentinel是一款强大的流量控制和熔断降级框架,通过流量控制、熔断降级、实时监控和统计等功能,保护分布式系统和微服务架构的稳定性和可靠性。Sentinel具有较广泛的应用场景,可以帮助开发者构建健壮的分布式系统和微服务架构。

微服务总结:

当谈论微服务架构中的组件时,通常会提到以下几个重要的组件:

  1. 服务注册与发现:微服务架构中,服务实例的管理和发现是至关重要的。服务注册与发现组件(如Consul、Eureka、Nacos等)负责管理和维护每个微服务实例的信息,包括IP地址、端口号、健康状况等。它们提供了服务发现的能力,使得其他微服务能够动态地发现和调用各个服务实例。
  2. 负载均衡:微服务架构中,如果一个服务有多个实例,负载均衡组件(如Nginx、Ribbon、Zuul等)可以将请求在不同的实例之间平均分配,以达到提高系统的吞吐量和可用性的目的。负载均衡可以基于不同的算法(如轮询、权重、一致性哈希等)进行实现。
  3. 网关:在微服务架构中,网关(如Spring Cloud Gateway、Zuul等)充当了对外的入口门户,负责路由、认证、鉴权、限流等功能。它可以统一管理和分发请求到不同的微服务,同时提供应用层面的安全保护和访问控制。
  4. 分布式配置管理:在微服务架构中,分布式配置管理组件(如Spring Cloud Config、Apollo等)用于集中管理各个微服务的配置信息。通过将配置信息存储在集中式的存储中,并提供实时的配置更新和变更通知,可以方便地对微服务进行配置的管理和调整。
  5. 分布式消息队列:微服务架构中,分布式消息队列(如Kafka、RabbitMQ等)可以用于实现微服务之间的异步通信和解耦。通过将消息发布到消息队列,不同的微服务可以根据自己的需要消费消息,并进行相应的处理。这种消息驱动的方式带来了更好的伸缩性和灵活性。

以上是微服务架构中常见的几个重要组件。它们共同协作,构成了一个高度可伸缩且可靠的微服务系统,提供了分布式系统中所需的关键功能。这些组件的合理选择和使用能够显著提高微服务系统的性能、可用性和可维护性。

场景题:

nel可以广泛应用于各种分布式系统和微服务架构中,如API网关、微服务网关、RPC服务调用等。它可以保护系统免受突发流量的冲击,提高服务的稳定性和可靠性。

总的来说,Sentinel是一款强大的流量控制和熔断降级框架,通过流量控制、熔断降级、实时监控和统计等功能,保护分布式系统和微服务架构的稳定性和可靠性。Sentinel具有较广泛的应用场景,可以帮助开发者构建健壮的分布式系统和微服务架构。

微服务总结:

当谈论微服务架构中的组件时,通常会提到以下几个重要的组件:

  1. 服务注册与发现:微服务架构中,服务实例的管理和发现是至关重要的。服务注册与发现组件(如Consul、Eureka、Nacos等)负责管理和维护每个微服务实例的信息,包括IP地址、端口号、健康状况等。它们提供了服务发现的能力,使得其他微服务能够动态地发现和调用各个服务实例。
  2. 负载均衡:微服务架构中,如果一个服务有多个实例,负载均衡组件(如Nginx、Ribbon、Zuul等)可以将请求在不同的实例之间平均分配,以达到提高系统的吞吐量和可用性的目的。负载均衡可以基于不同的算法(如轮询、权重、一致性哈希等)进行实现。
  3. 网关:在微服务架构中,网关(如Spring Cloud Gateway、Zuul等)充当了对外的入口门户,负责路由、认证、鉴权、限流等功能。它可以统一管理和分发请求到不同的微服务,同时提供应用层面的安全保护和访问控制。
  4. 分布式配置管理:在微服务架构中,分布式配置管理组件(如Spring Cloud Config、Apollo等)用于集中管理各个微服务的配置信息。通过将配置信息存储在集中式的存储中,并提供实时的配置更新和变更通知,可以方便地对微服务进行配置的管理和调整。
  5. 分布式消息队列:微服务架构中,分布式消息队列(如Kafka、RabbitMQ等)可以用于实现微服务之间的异步通信和解耦。通过将消息发布到消息队列,不同的微服务可以根据自己的需要消费消息,并进行相应的处理。这种消息驱动的方式带来了更好的伸缩性和灵活性。

以上是微服务架构中常见的几个重要组件。它们共同协作,构成了一个高度可伸缩且可靠的微服务系统,提供了分布式系统中所需的关键功能。这些组件的合理选择和使用能够显著提高微服务系统的性能、可用性和可维护性。

[外链图片转存中…(img-Ljd3XJId-1686886241365)]

场景题:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 男女之间的爱情,源自朋友之间的善良,升华为恋人相互的珍重,最终演变成夫妻之间真挚的爱情。无论是从朋友到恋人,还是从恋人到夫妻,它们都有着共同的特点:坚定的信任、坚实的依赖、深沉的爱恋。 朋友之间,可以是一种无言的了解,也可以是熟悉的笑容,虽然不像恋人之间那样深情,但也有着温暖的爱意。只要朋友之间的感情坚固,就可以把这份友谊深化,发展成恋爱关系。恋人之间,开始可能只是一种牵动心弦的吸引,但随着相处越来越长,一种熟悉和依赖也会逐渐形成,双方也会越来越了解彼此,对对方的爱更加贴心。夫妻之间,是一种共同承担责任的爱,也是一种坚定不移的承诺。夫妻相互扶持,在困难帮助彼此,在幸福分享快乐,相互扶持,造就了一个完美的家庭。男女之间的爱情,从朋友到恋人,再到夫妻,走过的路都是那么的美好,愿每一份爱情都能够承受间的考验,抵达最后的彼岸。 ### 回答2: 朋友,是一种伴我笑,伴我闹的亲密关系。我们分享喜悦,诉说心情,无拘无束地相互扶持。然而,在这个纷繁复杂的世界里,友情是那样的单纯而珍贵,它并不常常转变成恋情。然而,有些幸运的人却从朋友角色中逐渐渗透进了爱的领域。 我们的关系像水般悄无声息地发生着改变,从朋友到恋人。那是一份互相吸引的力量,仿佛有一股无形的纽带将我们紧密联系在一起。我们不再只是谈论琐事,而开始分享更深层次的感情。每一次相遇都像是缠绵的一吻,每一句问候都蓄满了柔情蜜意。 我们的爱情在细水长流中承载着我们的梦想和希望,以成为一对有情有义的夫妻。我们相信彼此,彼此信任、关心和支持。我们渴望通过婚姻的誓约,将这份爱 perpetuated 到永远。 但婚姻更像是一个新的起点,一个完全陌生的领域。我们将面临更多的责任和挑战,我们需要学会更多的忍耐、理解和包容。然而,我相信我们的坚定信念将会支撑我们的爱。在每个挫折面前,我们会紧握对方的手,共同面对人生的风雨。我们的夫妻关系将成为两颗心紧密相连的羁绊,永远无法割舍。 朋友,恋人,夫妻,这是一个演替的剧本。我们从一个角色转变为另一个,经历着不同的人生阶段。然而,我们的情感始终如一,不断升华。我们的爱不仅仅是悦目的表象,而是一种深入灵魂的契约。无论走到何处,我们将始终携手共进,共同创造美好的回忆。 ### 回答3: 朋友,是一个人生中最美好的礼物。当我们与心灵相投的人成为朋友,情感的种子悄然埋下,温暖的友谊之花在我们心中绽放。然而,有候,友谊的边界并不止于此。当两个灵魂开始互相吸引,友情之上的情感便默默滋生,演变为一段美丽的恋情。 从朋友到恋人,这个过程接踵而来。我们开始对对方产生更多的非分之想,有了愈发深入的了解和真心的关心。我们乐于分享心事,乐于倾听对方的痛苦和喜悦。友谊之间的默契和信任,成为我们之间恋爱的基石。 在爱情的土壤里,我们的关系逐渐变得亲密而温暖。我们体验到彼此之间的亲密接触,感受到心弦上那动人的共鸣。我们能够在对方身上找到安慰和支持,一同度过人生的喜怒哀乐。 终于,我们迈入了夫妻的殿堂。这是一个承诺,表明我们决心一生一世守护对方、彼此相伴。婚姻不仅仅意味着爱情的牢牢束缚,更是两颗心彼此紧紧相连的象征。我们相信,这份爱将带给我们无尽的欢乐和充实,使我们的人生更加完整。 友谊可以演变成深厚的爱情,一对恋人可以成为携手走过一生的夫妻。这种发展,是源于我们共同的成长和理解,以及对彼此不断的包容和宽容。我们的情感经历了这样的转变,茁壮成长,让我们的心灵从相识到相知,从相知到相守。 友情、爱情和婚姻,是人生旅途上的三层境界。无论何何地,这些情感都会陪伴我们度过平凡和特殊的日子。愿我们的友谊转变为永恒的爱情,成为相伴一生的伴侣,共同书写一段幸福的篇章。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值