MySQL日志
MySQL日志两大日志事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
事务的隔离性由 锁机制 实现。
而事务的原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。
REDO LOG 称为 重做日志 ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。
UNDO LOG称为 回滚日志 ,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
有的DBA或许会认为 UNDO 是 REDO 的逆过程,其实不然。REDO和UNDO都可以视为是一种恢复操作,但是:
redo log:是存储引擎层(innodb)生成的日志,记录的是”物理级别”上的页修改操作,比如页号xx、偏移量yyy写入了’zzz’数据。主要为了保证数据的可靠性;
undo log:是存储引擎层(innodb)生成的日志,记录的是逻辑操作日志,比如对某一行数据进行了INSERT语句操作,那么undo log就记录一条与之相反的DELETE操作。主要用于事务的回滚(undo log记录的是每个修改操作的逆操作)和一致性非锁定读(undo lo ...
垃圾收集器
垃圾收集器串行回收默认被应用在客户端的Client模式下的JVM
评价GC的性能指标
吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)
垃圾收集开销:吞吐量的补数,垃圾收集所用时间与总运行时间的比例
暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间
收集频率:相对于应用程序的执行,收集操作发生的频率
内存占用:Java堆区所占用的内存大小
快速:一个对象从诞生到被回收所经历的时间
重点关注:
吞吐量
暂停时间
吞吐量
吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
比如:虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%
这种情况下,应用程序能容忍较高的暂停时间(暂停次数少),因此,高吞吐量的应用程序有更长的时间基准,快速响应是不必考虑的
暂停时间
是指一个时间段内应用程序线程暂停,让GC线程执行的状态(STW)
例如,GC期间100毫秒的暂停时间意味着在这个100毫秒期间内没有应用程序线程是活动的
总结 ...
MySQL索引
索引索引的定义就是帮助存储引擎快速获取数据的一种数据结构,形象的说就是索引是数据的目录。
索引分类按数据结构从数据结构的角度来看,MySQL 常见索引有 B+Tree 索引、HASH 索引、Full-Text 索引。
每一种存储引擎支持的索引类型不一定相同,我在表中总结了 MySQL 常见的存储引擎 InnoDB、MyISAM 和 Memory 分别支持的索引类型。
索引类型
InnoDB
MyISAM
Memory
B+
YES
YES
YES
Hash
NO
NO
YES
Full-Text
YES
YES
YES
B+Tree 是一种多叉树,叶子节点才存放数据,非叶子节点只存放索引,而且每个节点里的数据是按主键顺序存放的。每一层父节点的索引值都会出现在下层子节点的索引值中,因此在叶子节点中,包括了所有的索引值信息,并且每一个叶子节点都有两个指针,分别指向下一个叶子节点和上一个叶子节点,形成一个双向链表。
B+Tree 存储千万级的数据只需要 3-4 层高度就可以满足,这意味着从千万级的表查询目标数据最多需要 3-4 次磁盘 I/O,所以B+Tre ...
CAS
CAS概述compareAndSet,也有(Compare And Swap的说法),它必须时原子操作
是由硬件指令集支持的原子操作,原子行是由CPU保证的,JVM只是封装了汇编调用,这个操作需要输入两个数值,一个旧值(期望操作执行的值)和一个性质,在操作期间先比较旧值有没有改变,如果没有发生变化,才交换成新值
JDK正式利用这些CAS指令,可以实现并发的数据结构,比如AtomicInteger等原子类
volatile:
获取共享变量时,为了保证该变量的可见性,需要volatile修饰
它可以用来修饰成员变量和静态变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存的。即一个线程对volatile变量的修改,对另一个线程可见
**注意:**volatile仅仅保证了共享变量的可见性,让其他线程能够看到最新值,但不能解决指令交错问题(不能保证原子性)
CAS必须借助volatile才能读取到共享变量的最新值来实现“比较并交换”的效果
为什么无锁效率高
无锁情况下,即使重试失败,线程始终再高速运行,没有停歇,而syn ...
Java内存模型
概述目前CPU的处理速度与内存的读写速度不在一个数量级,所以需要在CPU和内存之间加上缓存,来进行提速,这样就呈现出了一种CPU-寄存器-缓存-主存的访问结
这种结构在单CPU时期运行得很好,但是当一台计算机中引入了多个CPU,就出现缓存之间数据一致性的问题
针对这个问题,出现了缓存一致性协议,主要就是为了解决多个CPU缓存之间的同步问题,CPU缓存一致性协议有很多。
Java内存模型
屏蔽了各种硬件和操作系统的内存访问差异,实现了让Java程序能够在各种硬件平台下都能够按照预期的方法来运行
每个工作线程都有自己独占的本地内存,本地内存中存储的是私有变量以及共享变量的副本,使用一定机制来控制本地内存和主存之间读写数据的同步问题。
更加具体一点,我们将工作线程和本地内存具象为thread stack,将主存具象为heap。‘
在Thread stack中有两种类型的变量
原始类型变量,比如(int,char等)存储在线程上
对象类型变量,引用(指针)本身存储在线程栈上,引用指向对象存储在堆上。
堆中存储对象本身,持有该对象引用的线程就能够访问该对象了
Java线程模型中Thread ...
自动配置原理
自动配置原理Auto-Configuration
它是基于你引入的依赖jar包,对SpringBoot应用进行自动配置
他为SpringBoot框架的“开箱即用”提供了基础支撑
术语“配置类”,英文Configuration Class:
广义的“配置类”:被注解@Component直接或间接修饰的某个类,即我们常说的Spring组件,其中包括了@Configuration类
狭义的“配置类”:特指被注解@Configuration所修饰的某个类,又称为@Configuration类
相关注解@ComponentScan
@ComponentScan,是来自Spring框架的一个注解:
它的作用对指定的package进行扫描,找到其中复合条件的类,默认是搜索被注解@Component修饰的配置类
通过属性basePackages或basePackageClasses,来指定要进行扫描的package
如果未指定package,则默认扫描当前@ComponentScan所修饰的类所在的package
@Import
是来自Spring框架的一个注解:
它的作用是提供了一种显 ...
垃圾清除算法
当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配
目前在JVM中比较常见的三种垃圾收集算法是标记-清除算法(Mark-Swap)、复制算法(Copying)、标记-压缩算法(Mark-Compact)
标记-清除算法标记-清除算法是一种非常基础和常见的垃圾收集算法
执行过程
当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除
标记:Collector从引用根节点开始遍历,标记所有被引用的对象。一般是在对象的Header中记录可达对象
清除:Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收
缺点
效率不算高
在进行GC的时候,需要停止整个应用程序,导致用户体验差
这种方式清理出来的空闲内存是不连续的,产生内存碎片。需要维护一个空闲列表
何为清除?
这里所谓的清除并不是真的置空,而是把需要清除的 ...
垃圾回收
基本概述什么是垃圾
垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾
如果不及时对内存中的垃圾进行处理,那么,这些垃圾对象所占的内存空间会一直保留到应用程序结束,被保留的空间无法被其他对象使用。甚至可能导致内存溢出
为什么需要GC
对于高级语言来说,一个基本认知是如果不进行垃圾回收,内存迟早都会被消耗完,因为不断地分配内存空间而不进行回收,就好像不停地生产生活垃圾而从来不打扫一样
除了释放没用的对象,垃圾回收也可以清楚内存里的记录碎片。碎片整理将所占用的堆内存移动到堆的一端,以便JVM将整理出来的内存分配给新的对象
随着应用程序所应付的业务越来越庞大、复杂,用户越来越多,没有GC就不能保证应用程序的正常运行。而经常造成的STW的GC又跟不上实际需求,所以才会不断尝试对GC进行优化
早期的垃圾回收
在早期的C/C++时代,垃圾回收基本上是手工进行的。开发人员可以使用new关键字进行内存申请,并使用delete关键字进行内存释放
这种方法可以灵活控制内存释放的时间,但是会给开发人员带来频繁申请和内存的管理负担。倘若有一处内存区间由程序员编码的问题 ...
Redis缓存问题
缓存穿透当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。
缓存穿透的发生一般有这两种情况:
业务误操作,缓存中的数据和数据库中的数据都被误删除了,所以导致缓存和数据库中都没有数据;
黑客恶意攻击,故意大量访问某些读取不存在数据的业务;
常见解决方案应对缓存穿透的方案,常见的方案有三种。
第一种方案,非法请求的限制;
第二种方案,缓存空值或者默认值;
第三种方案,使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在;
非法请求限制当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
缓存空值或者默认值当我们线上业务发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置一个空值或者默认值,这样后续请求就可以从缓 ...








