不要再问Spring是如何解决循环依赖了

news2025/6/8 1:04:15

1、什么是循环依赖?

循环依赖主要来次三个方面,第一种A相互依赖,第二种是 A依赖B,B依赖A,第三种是A依赖B,B依赖C,C依赖A。

总结一句话就是对象之间形成环形依赖。

代码如下:

第一种:

public class A {
    private A a;
}

第二种:

public class A {
    private B b;
}
class B {
    private A a;
}

第三种:

public class A {
    private B b;
}
class B {
    private C c;
}
class C {
    private A a;
}

2、Spring 三级缓存是什么?

  • 第一级缓存:singletonObjects,用于缓存成品的bean,保证 Spring 的单例属性。
  • 第二级缓存:earlySingletonObjects,用于保存实例化完成的半成品 bean 实例,包括AOP代理对象。
  • 第三级缓存:singletonFactories,Map<String, ObjectFactory<?>>,Map 的 Value 是一个对象的代理工厂,用来存放对象的代理工厂用于保存 bean 创建工厂,以便后面有机会创建代理对象。为“打破循环”而生,存放的是生成半成品单例 Bean 的工厂方法。

要想说清楚三级缓存,我们先来看Spring bean 的生命周期,看bean的创建过程。

核心几个类我们从AnnotationConfigApplicationContext上下文为基础来讲解bean的生命周期,AnnotationConfigApplicationContext是基于注解的上下文,看下这核心构造器。

	/**
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given component classes and automatically refreshing the context.
	 * @param componentClasses one or more component classes &mdash; for example,
	 * {@link Configuration @Configuration} classes
	 */
	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();
		register(componentClasses);
		refresh();
	}

再看refresh()

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

核心主要是finishBeanFactoryInitialization(),其中beanFactory.preInstantiateSingletons()该方法中会生成所有非懒加载的单例bean。

	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no BeanFactoryPostProcessor
		// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}
public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
    logger.trace("Pre-instantiating singletons in " + this);
}

// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
                FactoryBean<?> factory = (FactoryBean<?>) bean;
                boolean isEagerInit;
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
                }
                else {
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                                   ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    getBean(beanName);
                }
            }
        }
        else {
            getBean(beanName);
        }
    }
}

// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
    Object singletonInstance = getSingleton(beanName);
    if (singletonInstance instanceof SmartInitializingSingleton) {
        StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
            .tag("beanName", beanName);
        SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                smartSingleton.afterSingletonsInstantiated();
                return null;
            }, getAccessControlContext());
        }
        else {
            smartSingleton.afterSingletonsInstantiated();
        }
        smartInitialize.end();
    }
}
}

doGetBean方法中有getSingletongetObjectForBeanInstancecreateBean 比较核心,下面继续看下这代码。代码篇幅过长、简单理解下,在doGetBean方法中首先调用getSingleton方法检查单例缓存中是否有该bean,这也是我们分析三级缓存的关键,如果没有则判断当前bean的作用域是单例(singleton)还是原型(prototype),如果是单例的,再次调用getSingleton方法,不过这次的该方法是重载的一个;如果是原型的则调用createBean方法生成bean,上面几个步骤生成的beanInstance均要调用getObjectForBeanInstance方法获得bean对象。

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object beanInstance;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
					.tag("beanName", name);
			try {
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}
				}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
				beanCreation.end();
			}
		}

		return adaptBeanInstance(name, beanInstance, requiredType);
	}

我们知道了DefaultSingletonBeanRegistry类下的getSingleton是Spring bean 缓存的关键,那么看下代码,

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
        //一级缓存
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //二级缓存
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
                            // 三级缓存
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

以上就是从源码上了解三级缓存,执行流程:

1.先从“第一级缓存”找对象,有就返回,没有就找“二级缓存”;

2.找“二级缓存”,有就返回,没有就找“三级缓存”;

3.找“三级缓存”,找到了,就获取对象,放到“二级缓存”,从“三级缓存”移除。

3、如何解决缓存依赖问题?

以上都是Spring Bean缓存的介绍,是如何解决循环依赖的问题呢?

我们再看一段代码

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        // 判断一级缓存中不存在此对象
        if (!this.singletonObjects.containsKey(beanName)) { 
            // 直接从工厂中获取 Bean
            object o = singletonFactory.getObject(); 
            // 添加至二级缓存中
            this.earlySingletonObjects.put(beanName, o); 
            this.registeredSingletons.add(beanName);
        }
    }
}

总结来说,Spring 通过三级缓存实现循环依赖,来存放对象的代理工厂用于保存 bean 创建工厂。在遇到循环依赖时候优先通过二级缓存看是否存在依赖对象的半成品、没有则去三级缓存获取Bean Factory创建对象,创建成功之后放入到二级缓存中。

为什么多出二级缓存呢,只要三级缓存能不能实现?

二级缓存”的目的是为了避免因为 AOP 创建多个对象,其中存储的是半成品的 AOP 的单例 bean。

能不能只要二级缓存,二级缓存也可以解决循环依赖问题啊?

主要解决代理对象问题,假如不存在代理对象,二级缓存也是足够的。

Spring 只用二级缓存来解决循环依赖,那么所有 Bean 都需要在实例化完成之后就立马为其创建代理,而Spring 的设计原则是会在完成属性填充、并且执行完初始化之后再为其创建代理。所以,Spring 选择了三级缓存,,但是因为循环依赖的出现,导致了 Spring 不得不提前去创建代理,因为如果不提前创建代理对象,那么注入的就是原始对象,这样就会产生错误。

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA时序约束与分析 (1)--- 约束工具及相关命令的使用

注意&#xff1a; 时序约束辅助工具或者相关的TCL命令&#xff0c;都必须在 open synthesis design / open implemention design 后才能有效运行。 1、时序约束辅助工具 2、查看相关时序信息 3、一般的时序约束顺序 1、 时序约束辅助工具&#xff08;1&#xff09;时序约束编辑…

蓝桥杯刷题练习Python(笔记)

# a1 A # a2 A B A # a3 ABA C ABA # a4 ABACABADABACABAnum int(input(N < 26:)) # 26 个字母 str "ABCDEFGHIJKLMNOPQRSTUVWXYZ"if num 1:a A # str[0] else:a Afor i in range(1, num, 1): # b str[i]c a b aa c# print(str[i])# print(a) …

【React】从 0 开始学 React —— 实现井字棋小游戏

目录1 React 简介2 实现井字棋小游戏2.1 初始化2.2 props2.3 setState2.4 状态提升2.5 副本2.6 简化组件2.7 key2.8 小结3 核心概念1 React 简介 React 是一个声明式&#xff0c;高效且灵活的用于构建用户界面的 JavaScript 库 官网提供了两种学习思路 如果你喜欢边做边学&am…

Vue3 组件之间的通信

组件之间的通信 经过前面几章的阅读&#xff0c;相信开发者已经可以搭建一个基础的 Vue 3 项目了&#xff01; 但实际业务开发过程中&#xff0c;还会遇到一些组件之间的通信问题&#xff0c;父子组件通信、兄弟组件通信、爷孙组件通信&#xff0c;还有一些全局通信的场景。 …

【GPLT 二阶题目集】L2-043 龙龙送外卖

参考地址&#xff1a;AcWing 4474. 龙龙送外卖&#xff08;杂题选讲&#xff09; 作者&#xff1a;yxc 感谢y总&#xff01; 龙龙是“饱了呀”外卖软件的注册骑手&#xff0c;负责送帕特小区的外卖。帕特小区的构造非常特别&#xff0c;都是双向道路且没有构成环 —— 你可以…

Spring Cloud Alibaba Sentinel 控制台

简介 Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口&#xff0c;它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上&#xff0c;我们可以配置规则并实时查看流量控制效果。 下载 有以下两种方式获取Sentinel控制台 下…

进阶技术:Linux Arm32是如何调用C Main的

前言&#xff1a; Linux x64通过qemu的rdinit方式调用的C Main&#xff0c;实际上是通过load_elf_binary(加载和解析elf)和start_thread(设置Ip和sp)&#xff0c;用缺页异常来调用。关于这点可以看这篇文章&#xff1a;点击查看。那么Arm32里面是如何调用C Main的呢&#xff1f…

Android 进阶——Framework 核心之Binder 相关预备理论(一)

文章大纲引言一、进程的内存空间和进程隔离二、Linux 系统内存的用户空间和内核空间1、用户空间&#xff08;User Space&#xff09;2、内核空间&#xff08;Kernel Space&#xff09;三、Linux IPC 原理1、内核态和用户态2、IPC 步骤四、内核模块和驱动五、Binder1、Binder IP…

优思学院|從《狂飙》高启强爱看的《孙子兵法》到六西格玛项目管理

近期最受人瞩目的&#xff0c;无疑是电视剧《狂飙》中出类拔萃的反派高启强。而在剧中&#xff0c;指引高启强走向顶峰的&#xff0c;正是那部著名的军事经典——《孙子兵法》。 在剧中&#xff0c;高启强在一次村庄改造项目上遇到了困难&#xff0c;但他仍保持冷静&#xff0…

QT开发安卓程序初识

Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤 测试结果如下&#xff1a;

AcWing 840. 模拟散列表

题目描述 餐前小菜&#xff1a; 在讨论本题目之前先看一个简单的问题&#xff1a;给出 NNN 个正整数 (a1,a2,...,an)(a_1,a_2,...,a_n)(a1​,a2​,...,an​)&#xff0c;再给出 MMM 个正整数 (x1,x2,...,xm)(x_1,x_2,...,x_m)(x1​,x2​,...,xm​)&#xff0c;问这 MMM 个数中…

冷知识|鹤顶红还能用来修长城?

大家好&#xff0c;我是建模助手。 在上篇浅浅地蹭了波热点之后&#xff0c;我灵机一动&#xff0c;倒不如也搞一搞建筑方面的冷知识&#xff1f;冷热搭配&#xff0c;事半功倍... 问问大家&#xff0c;如果谈起古建筑&#xff0c;关键词都有什么&#xff1f;是庄严、震撼、壮…

DHCP实验及配置

DHCP实验配置基于接口拓扑图配置基于全局拓扑图配置基于接口 拓扑图 配置 [Huawei]dhcp enable//在全局下使能DHCP服务 [Huawei]interface GigabitEthernet0/0/0//进入接口 [Huawei-GigabitEthernet0/0/0] ip address 192.168.1.1 255.255.255.0 //配置接口地址 [Huawei-Giga…

什么是QoS?QoS是如何工作的?QoS的实验配置如何进行?

QoS&#xff08;Quality of Service&#xff09;是服务质量的简称。对于网络业务来说&#xff0c;服务质量包括哪些方面呢&#xff1f; 从传统意义上来讲&#xff0c;无非就是传输的带宽、传送的时延、数据的丢包率等&#xff0c;而提高服务质量无非也就是保证传输的带宽&…

人工智能:分享五个目前最火的ChatGPT开源项目

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️荣誉&#xff1a; CSDN博客专家、数据库优质创作者&#x1f3c6;&…

Nacos简介(一)

目录 一、概览 二、注册中心基本概念 1) 什么是注册中心&#xff1f; 2) 如果没有注册中心&#xff1f;会怎样 3) 注册中心主要有三种角色&#xff1a; 4) 服务注册中心的作用 5&#xff09;CAP 理论 6&#xff09;CP和AP的选择 三、什么是 Nacos&#xff1f; 四、Nac…

C++——继承那些事儿你真的知道吗?

目录1.继承的概念及定义1.1继承的概念1.2 继承定义1.2.1定义格式1.2.2继承关系和访问限定符1.2.3继承基类成员访问方式的变化2.父类和子类对象赋值转换3.继承中的作用域4.派生类的默认成员函数5.继承与友元6. 继承与静态成员7.复杂的菱形继承及菱形虚拟继承如何解决数据冗余和二…

Spring 5(黑马)

文章目录传统JavaWeb开发的困惑IoC、DI和Aop思想提出Spring框架的诞生Spring 框架概述Spring 框架历史Spring Framework技术栈图示BeanFactory 快速入门DI 入门案例ApplicationContext快速入门BeanFactory 和 ApplicationContext的关系BeanFactory 的继承体系ApplicationContex…

全志H616——安装SQlite库并使用常用的数据库操作指令

在官网下载安装包&#xff1a;SQLite下载页面、https://www.sqlite.org/2022/sqlite-autoconf-3400100.tar.gz&#xff08;安装包&#xff09;下载到MobaXterm_Personal中解压&#xff1a;tar xvf sqlite-autoconf-3400100.tar.gz设置下载路径&#xff1a;./configure --help:.…

滴滴一面:order by 调优10倍,思路是啥?

背景说明&#xff1a; Mysql调优&#xff0c;是大家日常常见的调优工作。 所以&#xff0c;Mysql调优是一个非常、非常核心的面试知识点。 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;其相关面试题是一个非常、非常高频的交流话题。 近段时间&#xff0c;有小伙伴面…