使用Windbg分析dump文件排查C++软件异常的一般步骤与要点分享
目录
1、概述
2、打开dump文件,查看发生异常的异常类型码
3、查看发生异常的那条汇编指令
3.1、汇编代码能最直接、最本真的反映出崩溃的原因
3.2、汇编指令中访问64KB小地址内存区,可能是访问了空指针
3.3、汇编指令中访问了很大的内核态的内存地址
3.4、汇编指令中访问了一个正常的内存地址,可能是野指针
4、查看发生崩溃时的函数调用堆栈
5、可以尝试在Windbg中查看相关变量的值,可能是排查问题的关键线索
6、有时可能需要尝试找到复现问题的方法和步骤,确定代码的排查范围和方向
7、有时可能需要查看二进制文件的汇编代码上下文去辅助分析问题
8、最后
C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战专栏(专栏文章已更新460多篇,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/140824370C++ 软件开发从入门到精通(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html 当程序发生异常崩溃时,程序中安装的异常捕获模块感知到了异常,自动生成了dump文件,事后用Windbg静态分析dump文件去排查异常,这是分析程序异常的常用方法。但技术群中很多朋友反映不知道该如何分析,表示无从下手,普遍缺乏分析问题的思路与方法。所以今天在这里总结一下使用Windbg分析dump文件去排查C++软件异常的一般步骤与方法,供大家借鉴或参考。
1、概述
使用Windbg静态分析dump文件,是排查C++软件异常与崩溃的常见手段与方法。使用Windbg分析dump文件的思路和流程大概是这样子的:
1)用Windbg打开dump文件后,先看一下发生的异常类型,可能能找到直接的线索。
2)紧接着查看发生异常的那条汇编指令,汇编指令中可能访问了不该访问的内存,可以初步地估计出引发该内存访问的可能原因,作为排查问题的方向和线索。
3)接下来就是查看发生异常的线程的函数调用堆栈,对照着C++源码去分析。
4)必要时,可能需要在Windbg中查看函数中相关变量的值,这些变量的值可能是关键线索。
5)此外,在分析不出问题时,可能要使用IDA反汇编工具查看二进制文件的汇编代码上下文去辅助分析。
6)有时可能需要尝试找到复现问题的方法和步骤,确定代码的排查范围和方向。
2、打开dump文件,查看发生异常的异常类型码
取来dump文件,用Windbg打开,就能看到程序中发生的异常类型码,如下所示:
通过这个异常类型码可以初步判断出异常的种类,比如Access violation(内存访问违例)、Integer divided by zero(除0异常)、Stack overflow(线程栈溢出)、Stack buffer overrun(栈内存越界)等,通过这个异常类型码可能就能找到最直接的线索。
比如Integer divided by zero(除0异常)异常,就是代码中出现除数为0的情况,查看异常发生时的函数调用堆栈就能快速定位。
再比如Stack overflow(线程栈溢出)异常,就是发生异常的线程的函数调用堆栈中函数占用的栈空间达到了系统给线程分配的栈内存的上限,直接去查看异常发生时的函数调用堆栈,就能快速定位问题。之前在文章中总结过引发线程栈溢出异常的常见原因:
1)函数递归调用的深度过深
因为一直在递归调用,在到达最底下的那层调用之前,递归函数一直没返回,栈空间一直没有释放,导致当前线程占用的栈空间越来越多,达到上限。
2)消息上触发函数的死循环调用
消息触发的函数死循环调用,因为死循环调用了,函数的栈空间一直没释放,导致当前线程占用的栈空间越来越多。这个问题我们在实际项目中遇到过两次。
3)定义了一个占用内存很大的局部变量
比如定义了一个很庞大的结构体,在一个函数中用该结构体定义了一个局部变量,假设该结构体接近或者大于1MB,则会直接导致线程栈溢出。
4)函数中使用switch...case语句,包含了大量的case分支
每个case分支中都定义了局部变量,导致当前函数占用了大量的栈空间。case分支中的局部变量的生命周期是在case分支中的,即代码运行到对应的case分支中时该分支中的局部变量才有“生命”,但其实这个局部变量的栈空间已经在函数入口处分配好栈空间了,并不是代码执行到case子句中才分配栈空间的。这点可以通过编写测试代码,查看函数入口处给当前函数分配栈空间的汇编代码就能看出来了,可以先顶一个变量查看汇编代码看看分配了多少栈空间,然后再增加一个变量,看看分配的栈空间是否变大。
5)多个if-else分支,每个分支中都有定义局部变量
引发问题的原因与多个case语句的场景是一样的,此处就不再赘述了。本问题案例的场景,就是与if-else分支有关系的。
对于Access violation内存访问违例的异常,就是程序中访问了不该访问的内存,原因比较多,比如访问空指针或野指针、访问内核态内存地址、访问已经释放的内存等都会引发内存访问违例,内存访问违例就没有明确的问题指向性了,就要查看函数调用堆栈与源码进行详细分析了。
3、查看发生异常的那条汇编指令
打开dump文件,输入.ecxr命令,切换到发生异常的线程上下文,可以查看到发生异常崩溃的那条汇编指令以及各个寄存器的值,如下所示:
查看发生异常的那条汇编指令,可能能找到初步的线索。比如汇编指令中访问了一个很小的内存地址,可能是访问了空指针导致的。再比如汇编指令中访问了一个很大的内核态的内存地址,可能是访问了未初始化的指针变量导致的,用户态的代码是不允许访问内核态内存地址,会触发内存访问违例。围绕着这些初步的估计线索,对照着函数调用堆栈去查看C++源码,这样排查的方向可能更明确、更有针对性,效率更高。
3.1、汇编代码能最直接、最本真的反映出崩溃的原因
程序运行时执行的是二进制文件中的汇编代码(或者称为二进制机器码,二进制机器码和汇编代码是等价的),程序最终崩溃在某条汇编指令上,汇编指令能直观的反映出崩溃的原因。
比如我们都知道访问空指针可能会引发崩溃,但为啥会崩溃呢?其实是汇编指令中访问了很小的内存地址,而系统禁止访问这个小地址内存区域,一旦访问就会触发内存访问违例,引发崩溃。
再比如,访问野指针,对应的汇编指令中可能访问了之前已经释放的内存或者一个随机的未知内存,一定会触发内存访问违例吗?答案是不一定,系统允许你访问,你就能访问;系统不允许你访问,则会触发内存访问违例。
3.2、汇编指令中访问64KB小地址内存区,可能是访问了空指针
在Windows系统中,0到64KB以内的小内存地址区域是禁止访问的,该区域被称为空指针内存区,专门用来协助程序员去排查访问空指针问题的。如果程序访问这个范围内的内存,则会触发内存访问违例,系统会强行将进程终止掉。
导致代码访问小地址内存区域,可能有以下几个原因:
1)访问了空指针
程序中访问了空指针,通过空指针去访问类的成员变量,类的成员变量的地址就是相对所在类对象首地址的偏移,如果类对象地址是空指针,则访问成员变量的地址就很小,这样就产生了访问小内存地址的场景。
2)访问了未初始化的指针
还可能是访问了一个未初始化的指针引起的,指针未初始化,则指针值是给指针变量分配的内存中残留的随机值,这个随机值可能很小,所以也会触发访问小地址内存区域。
3)程序中发生内存越界,指针变量的值被篡改了
程序中发生了内存越界,指针变量的值被篡改了,可能被篡改成一个很小的值,该值对应着一个很小的内存地址。
在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)
专栏1:(该精品技术专栏的订阅量已达到520多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)
C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!
专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!
专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达160多个,专栏文章已经更新到400多篇,持续更新中...)
C/C++实战进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。
专栏3:
C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!
专栏4:
VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。
专栏5:
C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。
3.3、汇编指令中访问了很大的内核态的内存地址
对于32位程序,系统会给进程分配4GB的虚拟内存,一般情况下用户态和内核态会各占一半,即各占2GB,我们编写的代码基本都是运行在用户态的,用户态的代码时不能访问内核态内存地址的(内核态地址是供系统内核模块使用的)。
如果汇编指令中访问了一个很大的内存地址,超过用户态的地址范围0-2GB,内存地址大于0x8000000,即访问了一个内核态的内存地址,则会触发内存违例,因为用户态的代码是禁止访问内核态内存地址的。
为什么代码中会访问内核态内存地址呢?可能有两个原因:
1)访问了一个未初始化的指针
Release下,指针未初始化,则指针值是给指针变量分配内存时内存中残留的随机值,这个随机值可能很大,可能是内存态的内存地址。
2)程序中发生内存越界,指针变量的值被篡改了
程序中发生了内存越界,指针变量的值被篡改了,可能被篡改成一个很大的值,该值对应着内核态的内存地址。
3.4、汇编指令中访问了一个正常的内存地址,可能是野指针
当前发生崩溃的这条汇编指令中访问了一个正常的内存地址,既不是很小的内存地址,也不是很大的内核态内存地址。很可能是访问的野指针,野指针主要包含以下两种情况:
1)该野指针可能是未初始化的指针变量。指针值是给指针变量分配内存时内存中残留的随机值。
2)可能是指针指向的内存已经被释放了(指针的值就是其指向的内存地址),而指针变量没有置为NULL,代码中又访问了该指针变量。
之前在项目中就遇到这个场景,发生崩溃的汇编指令中访问了一个正常的内存地址,产生了内存访问违例,查看崩溃的函数调用堆栈,一直找不到引发问题的原因。后来又回到这个汇编指令,大概地思考了一下,从这个访问的内存地址入手,既然访问的是一个正常的内存地址(内存地址不小,也不大,在正常范围),有没有可能该内存地址空间被释放了呢?代码中在释放内存时,没有将指针变量置为NULL,所以出现代码中访问已释放内存的野指针的场景。后来围绕这个可能的线索,根据函数调用堆栈去查看C++源码,然后就快速地定位了问题。相关的问题案例,之前已经写成了文章,可以查看:
根据发生异常的汇编指令以及函数调用堆栈,估计出问题的原因,确定排查方向,快速定位问题https://blog.csdn.net/chenlycly/article/details/141869579
4、查看发生崩溃时的函数调用堆栈
查看异常崩溃时的函数调用堆栈,是我们分析问题的最重要的线索。根据函数调用堆栈去查看C++源码,引发问题的点可能就在当前的调用堆栈中的函数中,当然也可能在其他处的代码中(代码崩溃有时具有延后性,执行到引发问题的代码点时不会立即崩溃,在执行到后续代码时发生崩溃)。
用Windbg打开dump文件,输入.ecxr命令切换到发生异常的那个线程,输入kn命令查看发生异常时的函数调用堆栈:
要查看到详细的函数调用堆栈信息,比如具体的函数名以及代码的行号,需要根据模块的时间戳找到对应的pdb文件,然后将pdb的路径设置到Windbg中,然后重新输入.ecxr命令和kn命令,就可以查看到详细的函数调用堆栈了。
这里需要注意一下,函数调用堆栈可能会显示代码崩溃在Windows系统库或者开源库(比如duilib)模块中,一般问题都不在系统库或开源库中,问题应该出在上层的模块中,要到上层模块中找问题,很多时候要从内存的角度去分析。比如上层操作的类对象地址有问题,可能对象内存已经释放了,但上层还在使用,再次访问就会出现内存访问违例,导致程序崩溃。
5、可以尝试在Windbg中查看相关变量的值,可能是排查问题的关键线索
我们看到了发生异常时的函数调用堆栈,然后根据堆栈去详细查看源码,有时分析问题原因可能需要查看相关变量的值,可以直接尝试在Windbg中查看,变量的值可能是关键的线索。在Windbg中查看变量值的一个示例截图如下所示:
之前写过相关的实战问题分析案例,可以查看我的文章:
通过查看Windbg中变量值去定位C++软件异常问题https://blog.csdn.net/chenlycly/article/details/125731044通过查看Windbg中变量值去定位C++软件异常的又一典型案例分享
https://blog.csdn.net/chenlycly/article/details/125793532
6、有时可能需要尝试找到复现问题的方法和步骤,确定代码的排查范围和方向
因为执行了引发问题的代码,其导致后续的崩溃会有延后性,通过崩溃时的代码没法找到引发问题的代码,可能需要尝试找到复现问题的方法和步骤,确定代码的排查范围和方向。
复现问题,找到复现问题的方法,也是排查问题的一种有效的辅助手段与方法。
有时我们需要让测试人员去复现问题,找到复现问题的方法与步骤,通过复现的步骤确定问题与哪些操作有关,这样就能大概确定问题与哪块代码有关,这样就确定了代码的排查范围和方向,然后在查阅对应代码的过程中结合用户执行的操作(复现问题的步骤),查找代码中存在的逻辑控制错误或业务异常。
对于软件UI异常或者业务异常,我们可能会尝试去找问题的复现办法,或者是我们在使用一些方法和手段去排查但很难定位问题时,也希望测试人员去尝试复现问题。 再比如,有些问题是在客户环境中出现的,在公司测试环境中没出现过,问题很难排查定位,我们可能需要尝试在公司环境中复现,如果能复现,在公司环境中排查起来也比较方便。
7、有时可能需要查看二进制文件的汇编代码上下文去辅助分析问题
有时我们查看C++源代码很难搞清楚问题到底出在那句代码上,或者无法排查出问题时,我们可能就需要查看汇编代码上下文去分析了。软件最终是崩溃在某一条汇编指令上,通过查看汇编代码才能最直观的看出为什么会发生异常崩溃。
在Windows平台下我们主要使用IDA Pro反汇编工具打开二进制文件,去查看二进制文件中的汇编代码。我们一般对汇编代码不是很精通,我们需要将C++源代码和汇编代码对照看,找到汇编代码与C++源码的对应关系,同时依靠汇编代码中的注释,才能大概地看懂汇编代码的上下文。
有一点需要注意,release下编译器在编译时会对代码进行优化,这样可能会导致汇编代码有时较难和C++源代码完全对应起来,比如有变量被优化掉、有函数调用被优化掉。
要阅读汇编代码上下文,需要掌握一些基础的汇编知识,这些基础汇编知识可以查看我之前写的文章:
分析C++软件异常需要掌握的汇编知识汇总https://blog.csdn.net/chenlycly/article/details/124758670 至于如何使用IDA Pro反汇编工具以及实战问题分析案例,可以参看我的文章:
反汇编工具IDA使用详解(使用IDA查看二进制文件的汇编代码以及使用IDA分析崩溃问题实例分享)https://blog.csdn.net/chenlycly/article/details/120635120使用IDA查看汇编代码,结合安卓系统生成的Tombstone文件,分析安卓app程序崩溃问题
https://blog.csdn.net/chenlycly/article/details/132283582 此外,C++开发人员要了解汇编,了解汇编后有诸多的好处。不仅可以辅助排查C++软件异常问题,还可以理解很多高级语言不好理解的代码执行细节(从汇编代码的角度可以看到代码的完整执行细节),特别是多线程编程中的问题。关于为什么要学习汇编以及学习汇编有哪些好处,可以查看我的文章:
C/C++程序员为什么要了解汇编?了解汇编有哪些好处?如何学习汇编?https://blog.csdn.net/chenlycly/article/details/142795872
8、最后
本文详细讲述了使用Windbg分析dump文件去排查程序异常的一般方法与思路,并给出了一些实战问题分析实例(以文章链接的方式提供),有一定的实战参考价值,希望能帮到大家。本文讲到了Windbg分析dump文件的要点,至于使用Windbg分析dump文件的详细操作步骤及相关问题分析实例,可以参考我的文章:
使用Windbg分析dump文件的一般步骤详解https://blog.csdn.net/chenlycly/article/details/135484682
相关文章:

使用Windbg分析dump文件排查C++软件异常的一般步骤与要点分享
目录 1、概述 2、打开dump文件,查看发生异常的异常类型码 3、查看发生异常的那条汇编指令 3.1、汇编代码能最直接、最本真的反映出崩溃的原因 3.2、汇编指令中访问64KB小地址内存区,可能是访问了空指针 3.3、汇编指令中访问了很大的内核态的内存地…...
30 天 Python 3 学习计划
30 天 Python 3 学习计划 https://www.runoob.com/python3/python3-tutorial.html 1. Python3 基础语法 2. Python3 基本数据类型 3. Python3 数据类型转换 4. Python3 解释器 5. Python3 注释 6. Python3 运算符 7. Python3 数字(Number) 8. Python3 字符串 …...

【MATLAB实例】批量提取.csv数据并根据变量名筛选
【MATLAB实例】批量提取.csv数据并根据变量名筛选 准备:数据说明MATLAB批量提取参考 准备:数据说明 .csv数据如下: 打开某表格数据,如下:(需要说明的是此数据含表头) 需求说明:需…...

【软件】Ubuntu下QT的安装和使用
【软件】Ubuntu下QT的安装和使用 零、前言 QT是应用得比较广泛的程序框架,是因为其跨平台特性比较好,且用C/C作为开发语言,性能也比较好,故本文介绍如何安装和使用QT,用的版本是QT 6.2.4,由于QT在Windows…...

在Spring Boot中具有多个实现的接口正确注入的六种方式
博客主页: 南来_北往 系列专栏:Spring Boot实战 在Spring Boot中,当一个接口具有多个实现时,正确地将这些实现注入到需要使用它们的地方是一个常见的需求。以下是在Spring Boot中实现这一目标的六种方式: 1. 使用Autowir…...

登陆微软账户太慢了,如何解决
软账号登录慢解决办法: 打开“网络和Internet”选择“以太网”选择“更改适配器选项”选择现用网络,右键->属性选择“IPV4”右键属性更改DNS地址为以下两者4.2.2.14.2.2.2...
Vue3动态组件component不生效问题解决方法
问题: vue3循环渲染动态组件component不生效,页面空白 在vue3使用component动态组件展示组件时,组件就是不展示显示空白。在vue2中使用动态变量component展示组件都是没问题。试了很多方法 踩了很多坑,所以记录下: 登录…...

算力基础篇:从零开始了解算力
什么是算力 算力即计算能力(Computing Power),狭义上指对数字问题的运算能力,而广义上指对输入信息处理后实现结果输出的一种能力。虽然处理的内容不同,但处理过程的能力都可抽象为算力。比如人类大脑、手机以及各类服…...

Redis 万字入门教程
0. 前言 文章已经收录到 GitHub 个人博客项目,欢迎 Star: https://github.com/chenyl8848/chenyl8848.github.io或者访问网站,进行在线浏览: https://chenyl8848.github.io/1. NoSQL 1.1 NoSQL 介绍 NoSQL(Not Only SQL )&…...

LeetCode :LCR 173. 点名
🔥个人主页:guoguoqiang. 🔥专栏:leetcode刷题 LeetCode :LCR 173. 点名 这个题就是缺失的数字,我们可以通过三种方式来解决这个问题。 1.可以通过位异或的方式来找到这个数(相同的数异或为…...

Gin框架操作指南06:POST绑定(下)
官方文档地址(中文):https://gin-gonic.com/zh-cn/docs/ 注:没用过Gin的读者强烈建议先阅读第一节:Gin操作指南:开山篇。 本节继续演示POST绑定,包括将request-body绑定到不同的结构体中&#x…...
LLaMA、llama.cpp和Ollama区别
LLaMA:LLaMA是由Meta(Facebook的母公司)开源的大型语言模型,它提供了不同规模的模型,包括1B、3B、11B和90B等参数规模的版本。LLaMA模型支持多语言对话,并在多个基准数据集上进行了评估,展现出与…...
NDK开发
NDK介绍 app为什么会把代码放到so中 a) C语言历史悠久,有很多现成的代码可用 b) C代码执行效率比Java高 c) Java代码很容易被反编译,而且反编译以后的逻辑很清晰 为什么要学习NDK开发 在安卓的so开发中,其他基本与C/C开发一致ÿ…...
docker overlay 占用空间太大,迁移到 /data/
将 Docker 的 overlay 存储驱动迁移到 /data/ 目录下,可以通过以下步骤完成: 1. 停止 Docker 服务 首先,停止 Docker 服务以确保没有容器在运行,并且数据不会被写入到当前的存储位置。 sudo systemctl stop docker2. 备份现有数…...
Windows性能监控与调优:让电脑运行如飞
一、性能监控 1. 使用任务管理器深入监控 打开任务管理器 我们可以通过按下Ctrl Shift Esc快捷键来打开任务管理器。 或者右键点击任务栏空白处,选择“任务管理器”。 查看性能 在任务管理器中,点击“性能”标签页。 我们可以看到“概览”标签&#x…...
前端响应式布局
1.什么是响应式布局? 响应式布局是一种使网页在不同设备(如手机、平板和桌面)上均能良好显示的设计理念。 2.响应式布局的原理? 通过灵活的网格布局、CSS 媒体查询和弹性单位等技术,实现内容自适应屏幕尺寸变化。 3.响…...

力扣MySQL 1581
先把两张表连接,amount为null 的正是我们需要的,再按customer_id聚合 select Visits.visit_id,customer_id ,Transactions.visit_id ,transaction_id ,amount from Visits left join Transactions on Visits.visit_idTransactions.visit_id 正确代码&…...

就是这个样的粗爆,手搓一个计算器:科学计算器
作为程序员,没有合适的工具,就得手搓一个,PC端,移动端均可适用。废话不多说,直接上代码。 HTML: <div class"calculator"><div class"display-wrapper"><div class"display…...

wordpress使用popup弹窗插件的对比
您在寻找最好的 WordPress 弹出插件吗?大多数网站利用某种形状或形式的弹出窗口来将访问者指向他们希望他们去的地方。例如,这可能用于结帐、电子邮件订阅或用于生成潜在客户。 表现 弹出插件会减慢您的网站速度。当插件使用 WordPress 跟踪弹出窗口的…...

开源OpenStack
1.查询HCS基于OpenStack哪个版本开发 2.九大核心组件 OpenStack可以对接FC也可以对接KVM主机;(OpenStack 对接华为FusionCompute,一个集群对应 openstack 一台计算主机)-引申出nova compute 2.1nova nova两个核心组件nova contro…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
JavaScript 标签加载
目录 JavaScript 标签加载script 标签的 async 和 defer 属性,分别代表什么,有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...

java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟
众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了,延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp ,边缘服务器拉流推送到云服务器 …...