目录
你觉得有什么优势。
最近关注的热点:
最有成就感的事,
遇到的挫折
沟通中最重要的是什么
最难合作交往的人,
对共公司的理解与选择的理由.
碰到的最难的事情:
对当前的就业前景怎么看
任务繁重的情况下怎么应对
描述自己的关键词
介绍一下爱好特长
说一说你最崇拜的人:
描述大学期间班干部遇到的最棘手的问题
拜访私企高管,提前做什么准备
遇到脾气很差的领导,怎么处理
1.int integer区别
2.说一下堆和栈
3.守护线程与本地线程区别
4.a=a+b,a+=b区别
5.++操作线程安全吗?为什么?
6.Windows Linux 如何查看进程占用cpu(资源)多少(好像是这个)linux ps top命令
7. ==和equals的区别
8. Integer a=200和Integer b=200用equals比较和用==比较分别是true还是false等等一系列(接连拷打)
9. 迭代器模式,为啥不用for循环
10. 工厂模式和抽象工厂模式
11. redis的缓存击穿是什么,咋解决?怎么确定设置锁的时间是多少?
12. 敏感词过滤是怎么做的
16.mysql有几种引擎
17. mysql和hadoop的区别
18.死锁的四个条件
3,聊一聊JVM?
方法区(Method Area)
Java堆(JVM堆、Java heap)
虚拟机栈(JVM栈、VM Stack)
本地方法栈(Native Method Stack)
程序计数器
怎么判断对象可以被回收了?
分代回收机制:
新生代被分成三个空间:
5,ArrayList和LinkedList区别?
6,说一下泛型擦除?
7,说一下io流,用到了哪些设计模式?
9,说一下hashcode()和equals()?
11,如何判断走索引?索引的底层数据结构?
12,分库分表中update语句会带来什么问题?如何解决?(懵逼)
分库分表之后的问题
13,如何定位sql问题和优化?
16,缓存穿透、缓存雪崩、缓存击穿。
Redis 故障宕机
缓存击穿:
缓存穿透
MySQL有几种锁
行级锁
2 表级锁
3页级锁
悲观锁
乐观锁
自增锁(AUTO-INC锁)
利用Mysql怎么设置分布式锁?
.springboot的常用注解:
autowird和resource的区别
.MyBatis的工作原理
Spring中的循环依赖
mybatis的循环依赖,
索引的分类
索引优化:
#前缀索引优化
索引失效:
MySQL层优化
为什么学习mysql:
mysql的未来发展
spring和springboot的区别:
springboot的优点:
mybatis怎么实现:
redis缓存读写策略
transactional注解失效
threadlocal内存泄漏
多线程的锁:
线程池中的参数
delete,drop,truncate的区别
编辑事务的四个特性
隔离级别
TCP和UDP
三次握手和四次挥手
数据库索引
mysql索引的数据结构
b+树和红黑树区别
介绍io多路复用
进程间通信方式
hashmap底层实现
HashMap为什么要引入红黑树?
hash值冲突的话怎么处理
MySQL什么情况下查询会慢,怎么优化(没答好)
什么适合做索引(没怎么答上来,举了个例子问)
七层网络协议介绍,IP协议是哪层
有哪些排序算法
主从复制 Redis集群
垃圾回收 怎么判断是不是垃圾
一段时间MySQL查询变慢怎么回事
hashmap和currenthashmap区别,后者如何解决线程安全问题的
jvm内存结构
ioc控制反转,aop
编辑mysql索引失效
mvcc
mysql默认级别,幻读怎么解决
Springboot和SpringMVC的区别、
自动装配的原理
编辑Bean生命周期
动态代理的实现方式
MySQL底层?为什么B+树不红黑树?
MySQL主从复制
springboot自动装配注解
spring事务什么时候失效
hashtable怎么实现线程安全的。
springcloud组件
面试不是淘汰,而是选拔。因此,每一个回答都应该具备“我很适合贵公司,很适合这个岗位,选我!”的潜台词。
综合面(ai面):
你想来重庆吗,为啥,家离得挺远的?对公司了解吗
自我介绍,
最近主要在关注杭州亚运会,像双人赛艇的首枚金牌,赛艇女子轻量级双人双桨决赛,中国组合邹佳琪/邱秀萍夺得金牌,这是本届亚运会产生的首枚金牌,也是中国代表团获得的首枚金牌,央视报导的时候,还有就是新星全红婵赢得了“双冠”战绩
莫过于高考
入党答辩
倾听是沟通的关键部分。倾听不仅包括听到对方说的话,还包括理解和关注对方的观点、情感和需求。倾听可以表现为积极的非言语反应,如眼神接触和头部的微小动作,以及口头回应,如提问题或发表评论,以表明你在意对方的话语。
清晰、简洁和明确地表达自己的想法和意见非常重要。使用恰当的语言,避免歧义,确保对方理解你的意图。
尊重对方是建立积极关系的基础。尊重包括尊重对方的观点、文化、信仰和感情,以及避免冒犯性言论或行为。
积极的反馈可以鼓励对话和建立信任。积极的反馈可以是赞扬、支持或鼓励。它有助于建立积极的沟通氛围。
语气和语调可以对沟通产生深远的影响。使用友好、尊重和合作的语气,避免过于强硬或傲慢的语气。
在进行沟通之前,明确你的沟通目的是什么。你是要提供信息、解决问题、表达情感还是寻求建议?明确你的目标可以帮助你选择合适的沟通方式。
你对难相处的同事的定义?你是如何与那位同事沟通的?你保持冷静了么?你是否能顺畅地与各种各样的人一起工作?
在社团部门工作的时候,有个同学会对事情不在乎,完全不负责任,也没有时间观念,到完成任务的时间的时候,就会各种找理由。我的话,刚开始只能尽量去和他沟通,了解真实原因,尽量去帮助他完成,后来发现一直如此,也就给他划分一些锦上添花的任务,可以完成,也可以不完成这种。
中邮消费金融有限公司(以下简称“中邮消费金融”)成立于2015年11月,总部设在广州,经中国银行保险监督管理委员会批准成立 [1] ,由中国邮政储蓄银行等7家企业发起成立, [2] 是一家为居民个人提供消费金融服务的全国性金融机构。中邮消费金融在全国16个省(市)设立省级营销中心 [3] ,2018年,中邮消费金融注册资本增至30亿元。
公司能够提供一个很大的平台,对我个人职业发展很有帮助,而且认同该公司的核心价值观:以客户为中心 全力守护金融消费者合法权益 。在网上了解到公司的文化和工作环境都很好,很多员工的满意度也很高,同时公司的名声声誉都很好,地理位置也处于北京。
做毕设的时候
个人看法:计算机科学和信息技术领域在许多国家都处于高需求状态。云计算、大数据、人工智能、物联网、网络安全等领域的需求不断增加。这些技术的发展推动了各种行业对计算机专业人才的需求。而且提供了各种职业机会,包括软件开发、数据分析、网络管理、信息安全、人工智能研究、机器学习工程师等。这意味着毕业生和专业人士有机会选择适合他们兴趣和技能的工作。
计算机领域的技术不断发展和演进,这要求从业者保持终身学习的态度,不断更新和发展技能。
但是目前计算机领域是竞争激烈的,因为很多人都在学计算机。而且技术快速演变,可能导致一些职位的变革或淘汰。因此,专业人员需要保持灵活性,以适应新技术和趋势。
任务之间的冲突是什么、如何安排任务完成顺序以及你计划怎么完成等。如何安排任务优先级的、你是如何判断任务优先级的以及你是如何按照优先级安排任务完成顺序的。如何安排任务优先级的、你是如何判断任务优先级的以及你是如何按照优先级安排任务完成顺序的。
首先我要列一个任务清单,按照紧急程度以及重要程度、完成所需时间、任务的难度以及任务是否需要小组合作等问题来安排任务的优先级,再按照优先级对任务进行一个排序,同时要思考 是不是加班到半夜也无法完成,根据排序,进行任务的执行,即使加班也要尽量完成。
当你遇到问题时,你会及时跟上司沟通,如果不能按时完成,也会及时反馈,让上司有时间采取补救措施,避免给公司带来大麻烦。
善良,坚韧,乐观,自信,专注,公平,诚信,负责任。
介绍一下家庭
前苹果公司的联合创始人和前首席执行官,史蒂夫·乔布斯,
乔布斯以其独特的创新和远见而闻名。他的愿景推动了苹果公司的发展,带来了革命性的产品,如iPhone、iPad和iPod。他相信技术应该为人们提供更美好、更简单的生活,这种信念激发了他不断寻求创新的决心。
乔布斯非常注重产品设计和用户体验。他相信产品不仅应该在功能上卓越,还应该在外观和用户界面上令人愉悦。这种设计哲学使苹果的产品在市场上脱颖而出。乔布斯以其对细节的苛刻要求而闻名。他相信产品必须完美无缺,这种追求卓越推动了苹果的产品质量。
乔布斯在苹果的创业历程中经历了许多挫折和失败,但他始终坚持不懈。他的决心和毅力帮助苹果克服了困难,最终取得了成功。
乔布斯激发了团队成员的创造力,鼓励他们勇于突破传统和挑战传统思维。他的领导风格鼓励团队成员实现卓越。
为什么报考这个岗位和这个行业
主要就是协调同学或者小团体之间的冲突分歧,以及自身如组织活动、开会、回应学生的问题在与学术任务和考试等学业任务的平衡上。首先,了解每个团队成员的个性、兴趣、强项和弱点,以便更好地适应他们。这种了解可以通过个人会谈、问卷调查或团队建设活动来获得。建立一个开放的沟通氛围,鼓励团队成员分享他们的观点、想法和顾虑。确保每个人都有机会发表意见,感受到尊重。强调团队合作的重要性,让团队成员明白他们的工作如何与整个团队的目标相关联。鼓励他们共享资源、知识和经验。尊重每个团队成员的观点和决策,即使他们与你的看法不同。倾听是建立信任和良好关系的关键。当冲突出现时,要积极解决问题,而不是回避。采用有效的冲突解决方法,如协商、调解或妥协,以平衡不同的利益。有时候,不同个性的团队成员可能会有不同的需求,例如更多的支持或更多的自主权。要尽量满足个体的需求,同时保持公平性。
研究公司和高管:在会议之前,详细了解该私企的业务、历史、文化、目标和重要项目。同时,了解你将拜访的高管的背景、职责和个人或专业兴趣。这将有助于你更好地与他们建立联系。
明确会议目的:确保你明确了拜访的目的。你是否是为了提供某种产品或服务,请求支持,建立合作关系,或是寻求建议?清晰的目标将有助于你有效地传达信息。
准备资料和提纲:根据会议目的,准备相应的资料、提纲或演示文稿。确保这些资料简洁明了,能够突出你的主要信息。
制定提问计划:准备一些问题,以便与高管进行有针对性的讨论。这些问题可以帮助你更好地了解他们的需求和关注点,同时展示你的兴趣和专业知识。
计划会议日程:安排会议的日期、时间和地点,并确保你提前到达。如果需要旅行,预订交通工具和住宿。
考虑沟通和谈判策略:思考如何进行有效的沟通,包括语言和口头表达方式。同时,考虑可能需要的谈判策略,以达成共识。
着装得体:根据会议的性质和地点选择合适的着装。通常情况下,商务正装是一个安全的选择。
测试设备和技术:如果你需要使用电子设备或技术(例如笔记本电脑、投影仪等),确保在会议前测试它们,以避免技术故障。
跟进计划:在会议结束后,考虑如何跟进会议。这可能包括发送会议纪要、感谢信或执行下一步的行动计划。
维护礼貌和专业态度:在会议期间,保持礼貌、尊重和专业的态度。尊重高管的时间和观点,积极倾听,遵循商业礼仪。
保持冷静:当你遇到脾气很差的领导时,首要任务是保持冷静。不要与他们产生情绪冲突,保持冷静和专业。
尊重和礼貌:无论领导的态度如何,都要尊重和保持礼貌。对他们的指示和决策要表示尊重,即使你不同意。
倾听:倾听领导的意见和担忧,即使他们的表达方式可能不是很友好。尝试理解他们的立场和需求。
避免争论:尽量避免与脾气暴躁的领导争吵或争辩。这可能会加剧紧张局势。如果必须表达自己的看法,要选择合适的时机和方式。
提供解决方案:如果你能够提供解决问题的建议或方法,那么这可能有助于缓解领导的不满情绪。然而,要以合适的方式提供建议,不要显得傲慢或指责。
采取行动:如果领导的脾气会导致不合理的要求或负面影响,你可以采取适当的行动,如与人力资源部门交流或与上级领导讨论问题。
可能需要寻求公司内部的中立第三方协助,如人力资源部门或其他上级领导。
技术问题:
为了能够将这些基本数据类型当成对象操作,Java为每 一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer。
分为内存上的和数据结构上的
栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。地址由高到底
堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表。堆的内存地址生长方向与栈相反,由低到高
堆与栈实际上是操作系统对进程占用的内存空间的两种管理方式,
主要有如下几种区别:
(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同。每个进程拥有的栈大小要远远小于堆大小。理论上,进程可申请的堆大小为虚拟内存大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;
(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有 2 种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca()函数分配,但是栈的动态分配和堆是不同的,它的动态分配是由操作系统进行释放,无需我们手工实现。
(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。
(6)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。
堆和栈相比,由于大量malloc()/free()或new/delete的使用,容易造成大量的内存碎片,
线程特性:守护线程会随着 JVM 关闭而自动结束,而本地线程则会一直执行直到程序运行结束或线程手动停止。
线程优先级:在 Java 中,线程分为优先级较高的线程(如用户线程)和优先级较低的线程(如守护线程)。如果同时存在守护线程和用户线程,JVM 在所有用户线程执行完成后才会关闭 JVM 进程,并且任何正在运行的守护线程都将被强制终止。当没有其他用户线程执行时,守护线程可以在后台执行,例如垃圾收集、日志记录等任务。
特殊用途: 守护线程的主要作用是为其他线程提供某种服务支持,比如 Java 虚拟机的垃圾回收线程就是一个守护进程,用于回收已经死去对象占用的内存空间。而普通的本地线程则是为程序的正常执行贡献自己的计算资源,执行各种业务逻辑。因此,我们往往使用本地线程来实现工作线程(Worker Thread)功能,用于处理应用程序的核心业务逻辑,而使用守护线程来支持应用程序中重要但是并不紧急的后台服务。
线程启动方式:创建一个本地线程和创建一个守护线程的过程是相同的。不过在 Java 中,可以通过调用 Thread 类中的 setDaemon() 方法将一个用户线程转化为守护线程。这个方法必须在 start() 方法之前被调用,否则将会出现 IllegalThreadStateException 异常。
其他注意事项:在 Java 语言中,当所有的非守护线程运行完成后,JVM 就退出了。因此如果希望非守护型线程执行完毕后有一些其它操作需进行,就需要看 You need to verify that all non-daemon threads are complete before terminating the JVM.
总之,在 Java 中,守护线程与普通线程是有区别的。守护线程随着 JVM 的关闭而结束,主要用于提供后台服务,如垃圾回收、日志记录等;而普通线程则是用于执行应用程序的各种任务。根据具体需求和场景,我们需要选择合适的线程类型以达到最佳效果。
a=a+b是加法运算 需要两次寻找地址而a+=b是增量运算有寄存器优先时 只有一次地址查找。、a+=b是改变了a原始的值,而a=a+b是计算出a+b后,a在指向那个值。当a和b的数据类型相同时,a+=b的效率是高于a=a+b的。
i++是不安全的
可分为三个阶段:
读值,从内存到寄存器
+1,寄存器自增
写值,写回内存
如果是方法里定义的(局部变量),一定是线程安全的,因为每个方法栈是线程私有的;如果是类的静态成员变量(全局变量),i++则不是线程安全的,因为 线程共享栈区,不共享堆区和全局区
equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,所以说所有类中的equals()方法都继承自Object类,在没有重写equals()方法的类中,调用equals()方法其实和使用==的效果一样,也是比较的是引用类型的变量所指向的对象的地址,不过,Java提供的类中,有些类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值,比如String类。
-128~127 用 == 都是相等的,超过的都是 false
判断int和integer是否相同
int和int之间,用==比较,肯定为true,基本数据类型没有equals方法
int和integer比较,Integer会自动拆箱,==和equals结果都为true
int和new Integer之间比较,Integer会自动拆箱,调用intValue方法,所以==和equals结果都为true
Integer和Integer之间
1)直接赋值:
会进行自动装箱,所以当值在[-128,127]时,在这个区间内赋值不会创建新的对象,而是直接从缓存中获取已经创建好的Integer对象. 而当大于这个区间的时候,就会创建新的对象;
所以在进行==比较的时候,在[-127,128]区间内的值比较结果为true;大于该区间的值比较结果为false;
2)当Integer与Integer进行equals比较时,由于Integer重写了equals方法,比较的是内容,所以结果为true;
3)integer和new integer:
new integer会创建新的对象,存储在堆中,而integer在[-127,128]中是从缓存中取;所以integer和new integer进行 == 比较,结果为false,若进行equals比较,结果为true
4)new integer 和 new integer之间比较:
进行 == 比较的时候结果为false,进行equals比较的时候结果为true
从数据结构角度分析,for循环适合访问顺序结构,可以根据下标快速获取指定元素.而Iterator 适合访问链式结构,因为迭代器是通过next()和Pre()来定位的.可以访问没有顺序的集合.
不使用迭代器:若不采用迭代器去访问数据,那么必须要知道存储数据的数据结构是什么。这样子使得访问数据的逻辑代码与数据结构耦合得很严重。采用ArrayList访问数据一般用get(index)方法,而用LinkedList访问数据则一般不用get0方法。如此一来,只要数据结构的实现改变了,那么客户端的逻辑代码也要改变或者不能重用代码
使用迭代器:客户端通过控制迭代器来访问数据这样将耦合度降低很多,而且我无需知道数据结构到底采用了什么。
简单工厂说白了就是一个超级工厂,他可以生产各种各样的产品,产品之间无关联,
可以把简单工厂模式理解为工厂方法模式的一种特例,将他的那个超级大工厂拆分成多个工厂就是工厂方法模式了。工厂方法模式,需要区分不同的工厂,这里我们创建格力工厂、海尔工厂和海信工厂。
抽象工厂其实就是帮助减少系统的工厂数量的,但前提条件就是这些工厂要具备两个及以上的共性。
互斥锁
当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时其他请求也一直拿不到锁,整个系统就会出现无响应的现象。
3. 后台更新缓存
业务线程不再负责更新缓存,缓存也不设置有效期,而是让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新。
事实上,缓存数据不设置有效期,并不是意味着数据一直能在内存里,因为当系统内存紧张的时候,有些缓存数据会被“淘汰”,而在缓存被“淘汰”到下一次后台定时更新缓存的这段时间内,业务线程读取缓存失败就返回空值,业务的视角就以为是数据丢失了。
解决上面的问题的方式有两种。
第一种方式,后台线程不仅负责定时更新缓存,而且也负责频繁地检测缓存是否有效,检测到缓存失效了,原因可能是系统紧张而被淘汰的,于是就要马上从数据库读取数据,并更新到缓存。
这种方式的检测时间间隔不能太长,太长也导致用户获取的数据是一个空值而不是真正的数据,所以检测的间隔最好是毫秒级的,但是总归是有个间隔时间,用户体验一般。
第二种方式,在业务线程发现缓存数据失效后(缓存数据被淘汰),通过消息队列发送一条消息通知后台线程更新缓存,后台线程收到消息后,在更新缓存前可以判断缓存是否存在,存在就不执行更新缓存操作;不存在就读取数据库数据,并将数据加载到缓存。这种方式相比第一种方式缓存的更新会更及时,用户体验也比较好。
在业务刚上线的时候,我们最好提前把数据缓起来,而不是等待用户访问才来触发缓存构建,这就是所谓的缓存预热,后台更新缓存的机制刚好也适合干这个事
敏感词过滤用的使用比较多的 Trie 树算法 和 DFA 算法
13. 登录注册,密码怎么弄的怎么判断用户可以登录
14. 用MyBatis的时候有2个参数怎么处理?
15. 间接调用方法注解不能用是为什么?
MyISAM(早期) InnoDB Memory(只存在于内存)
事务。默认的事务隔离级别为可重复度,通过MVCC(并发版本控制)来实现的。
使用的锁粒度为行级锁,可以支持更高的并发;
支持外键约束;
mysql就是一个麻袋,里面装的是数据。而hadoop则是一种很强大的工具,它的作用就是去处理包括这些麻袋在内的大数据。
hadoop是一种分布式计算框架,用于处理大量的数据。而mysql是数据库用来存放数据的。
配合hadoop的数据库不是mysql这类传统的关系型数据库,因为当数据量非常大的时候,这些数据库的处理速度会非常慢(就算做了集群也一样慢),取而代之的则是hbase这类非关系型数据库,在大量数据处理过程中,处理速度会比较稳定。
19.线程安全
方法区用于存储JVM加载完成的类型信息、常量、静态变量、即时编译器编译后的代码缓存,方法区和 Java 堆区一样,都是线程共享的内存区域。
在JDK8以前,使用永久代的方式来实现方法区,JDK8以后,永久代的概念被废弃了,方法区改用和 JRockit、J9一样的在本地内存中实现的元空间(Meta Space)来代替,好处是元空间会在运行时根据需要动态调整,只要没有超过当前进程可用的内存上限(32位和64位系统各不相同),就不会出现溢出的问题。
堆区负责存放对象实例,当Java创建一个类的实例对象或者数组时,都会在堆中为新的对象分配内存。
虚拟机中只有一个堆,程序中所有的线程都共享它。
通常情况下,堆占用的内存空间是最多的。
堆的存取方式为管道类型,先进先出。
在程序运行中,可以动态的分配堆的内存大小。
在Java栈中只保存基础数据类型(参考:Java 基本数据类型 - 四类八种 - 知乎专栏)和对象的引用,注意只是对象的引用而不是对象本身哦,对象是保存在堆区中的。
拓展知识:像String、Integer、Byte、Short、Long、Boolean等等包装类型,它们是存放于堆中的。
栈的存取类型为类似于水杯,先进后出。
栈内创建的基本类型数据在超出其作用域后,会被自动释放掉,它不由JVM GC管理。而在栈内创建的引用类型实例,则还是由JVM GC管理。
当一个线程创建运行的时候,与之对应的栈就创建了,每个栈中的数据都是私有的,其他线程不能访问。
每个线程都会建立一个栈,每个栈又包含了若干个栈帧,每个栈帧对应着每个方法的每次调用,栈帧包含了三个部分:
局部变量区(方法内基本类型变量、对象实例的引用)
操作数栈区(存放方法执行过程中产生的中间结果)
运行环境区(动态连接、正确的方法返回相关信息、异常捕捉)
本地方法栈的功能和JVM栈非常类似,区别在于虚拟机栈执行的是Java方法,本地方法栈执行的是本地(Native)方法服务,存储的也是本地方法的局部变量表,本地方法的操作数栈等信息。
栈的存取类型为类似于水杯,先进后出。
栈内的数据在超出其作用域后,会被自动释放掉,它不由JVM GC管理。
每一个线程都包含一个栈区,每个栈中的数据都是私有的,其他栈不能访问。
本地方法栈是在 程序调用 或 JVM调用 本地方法接口(Native)时候启用。
本地方法都不是使用Java语言编写的,它们可能由C或其他语言编写,本地方法也不由JVM去运行,所以本地方法的运行不受JVM管理。
HotSpot VM将本地方法栈和JVM栈合并了。
在JVM的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,为了各条线程之间的切换后计数器能恢复到正确的执行位置,所以每条线程都会有一个独立的程序计数器。
程序计数器仅占很小的一块内存空间。
当线程正在执行一个Java方法,程序计数器记录的是正在执行的JVM字节码指令的地址。如果正在执行的是一个Natvie(本地方法),那么这个计数器的值则为空(Underfined)。
程序计数器不会抛出 OutOfMemoryError(内存不足错误)。
GC 是垃圾回收器的简称:
JVM GC只回收堆区和方法区内的基本类型数据和对象。栈区的数据(仅指基本类型数据),在超出作用域后会自动出栈释放掉,所以其不在JVM GC的管理范围内。
简单来说就是:对象没有引用了或者对象不可达,常见的有两种算法,分别是 引用计数法 和 可达性分析法(根搜索算法)。
新生代(Young generation)、老年代(Old generation)所占空间比例为 1 : 2
· 1个伊甸园空间(Eden)
· 2个幸存者空间(Fron Survivor、To Survivor)
默认情况下,新生代空间的分配:Eden : Fron : To = 8 : 1 : 1
标记 - 清除算法
标记 - 复制算法
标记 - 整理算法
4,说一些项目中实际用到的集合类?
是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
底层数据结构: ArrayList 底层使⽤的是 Object 数组; LinkedList 底层使⽤的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区 别,下⾯有介绍到!)
插⼊和删除是否受元素位置的影响: ArrayList 采⽤数组存储,所以插⼊和删除元素的时间复杂度受元素位置的影响。 LinkedList 采⽤链表存储,所以,如果是在头尾插⼊或者删除元素不受元素位置的影响 因为需要先移动到指定位置再插 ⼊。
是否⽀持快速随机访问: LinkedList 不⽀持⾼效的随机元素访问,⽽ ArrayList ⽀持。
内存空间占⽤: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留⼀定的容量空间, ⽽ LinkedList 的空间花费则体现在它的每⼀个元素都需要消耗⽐ ArrayList 更多的空间(因为要 存放直接后继和直接前驱以及数据)。
编译时,JVM编译器会将所有的泛型信息都擦除掉,变成原始类型,一般是将泛型的类型参数替换成具体类型的上限或下限 (如果没有指定上界,则默认为Object)。
Java的IO流库提供了一种链接(Chaining)机制,可以将一个流处理器跟另一个流处理器首位相连,以其中之一的输出作为另一个的输入而形成一个流管道链接。
对于Java IO流还涉及一种对称性的设计策略,其表现为输入输出对称性和字节字符的对称性
装饰者模式:给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例(各种字符流键装饰,各种字节流间装饰)。
适配器模式:有时候我们需要实现一个接口,但那个接口可能有很多抽象方法,我们只需要其中一个或多个方法。这时候我们可以创建一个类(适配器)实现接口,并重写接口中所有方法(空方法,有方法体,但方法体中无内容)。当我们需要接口中的某个方法时,只需继承类似于适配器的类 ,然后重写对应的方法即可。 将某个类的接口转换成我们期望的另一个接口表示,目的是消除由于接口不匹配所造成类的不兼容问题(字符流与字节流间互相适配)。
8,int 100和integer100一样嘛?为什么?
equals() 定义在Object类中,意味着所有的java类中都实现了这个方法。其底层其实就是通过==来进行比较,也就是说通过比较两个对象的内存地址是否相同判断是否是同一个对象。
hashcode()同样也定义在Object类中,意味着java中任何类都有这个函数。
重写equals()方法必须重写hashcode()方法。通过上面的讲解应该能大致明白为什么了吧。因为当你需要将该对象存入到底层为散列表结构的集合中时,是先判断hashcode值,碰到相同值时再通过equals进一步判断。所以两个方法都需要重写,保证如果两个对象是相等的,它们的 equals() 方法应该要返回 true,它们的 hashCode() 需要返回相同的结果。但这其实是针对当该类会在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结
查看慢查询日志和EXPLAIN命令
分库分表是非常常见针对单个数据表数据量过大的优化方式,它的核心思想是把一个大的数据表拆分成多个小的数据表,这个过程也叫(数据分片),它的本质其实有点类似于传统数据库中的分区表,比如mysql和oracle都支持分区表机制。
愿意:
单库太大:数据库里面的表太多,所在服务器磁盘空间装不下,IO次数多CPU忙不过来。
(2)单表太大:一张表的字段太多,数据太多。查询起来困难。
1、联合查询困难
联合查询不仅困难,而且可以说是不可能,因为两个相关联的表可能会分布在不同的数据库,不同的服务器中。
2、需要支持事务
分库分表后,就需要支持分布式事务了。数据库本身为我们提供了事务管理功能,但是分库分表之后就不适用了。如果我们自己编程协调事务,代码方面就又开始了麻烦。
3、跨库join困难
分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表, 结果原本一次查询能够完成的业务,可能需要多次查询才能完成。 我们可以使用全局表,所有库都拷贝一份。
4、结果合并麻烦
比如我们购买了商品,订单表可能进行了拆分等等,此时结果合并就比较困难。
对于低性能的SQL语句的定位,最重要也是最有效的方法就是使用执行计划explain 命令
主要就是优化索引
缓存雪崩:
当大量缓存数据在同一时间过期(失效)或者 Redis 故障宕机时,如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩的问题。
发生缓存雪崩有两个原因:
针对大量数据同时过期而引发的缓存雪崩问题,常见的应对方法有下面这几种:
针对 Redis 故障宕机而引发的缓存雪崩问题,常见的应对方法有下面这几种:
如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。
可以发现缓存击穿跟缓存雪崩很相似,你可以认为缓存击穿是缓存雪崩的一个子集。
应对缓存击穿可以采取前面说到两种方案:
当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题
缓存穿透的发生一般有这两种情况:
应对缓存穿透的方案,常见的方案有三种。
17,十万级的用户量如何保证数据持久化?(懵逼)
按照对数据操作的锁粒度来分:行级锁、表级锁、页级锁、间隙锁
按照锁的共享策略来分:共享锁、排他锁、意向共享锁、意向排他锁
从加锁策略上分:乐观锁和悲观锁
(1) 描述
行级锁是mysql中锁定粒度最细的一种锁。表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁
(2) 特点
开销大,加锁慢,会出现死锁。发生锁冲突的概率最低,并发度也最高
(1) 描述
表级锁是mysql中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分mysql引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)
(2)特点
开销小,加锁快,不会出现死锁。发生锁冲突的概率最高,并发度也最低。
(1) 描述
页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折中的页级锁,一次锁定相邻的一组记录。BDB 支持页级锁。
(2) 特点
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
读锁(共享锁):Shared Locks(S锁),针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁(排它锁):Exclusive Locks(X锁),当前写操作没有完成前,它会阻断其他写锁和读锁
IS锁:意向共享锁、Intention Shared Lock。当事务准备在某条记录上加读锁时,需要先在表级别加一个意向共享锁锁。
IX锁:意向排他锁、Intention Exclusive Lock。当事务准备在某条记录上加写锁时,需要先在表级别加一个意向排他锁锁。
认为对于同一个数据的并发操作,一定是会发生修改的(或者增删改多,查少),哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
则认为对于同一个数据的并发操作,是不会发生修改的(或者增删改少,查多)。在更新数据的时候,会采用不断尝试更新的方式来修改数据。也就是先不管资源有没有被别的线程占用,直接取申请操作,如果没有产生冲突,那就操作成功,如果产生冲突,有其他线程已经在使用了,那么就不断地轮询。乐观的认为,不加锁的并发操作是没有事情的。就是通过记录一个数据历史记录的多个版本,如果修改完之后发现有冲突再将版本返回到没修改的样子,乐观锁就是不加锁。好处就是减少上下文切换,坏处是浪费CPU时间。
自增锁是一种特殊的表级锁,主要用于事务中插入自增字段,也就是我们最常用的自增主键id。通过innodb_autoinc_lock_mode参数可以设置自增主键的生成策略。防止并发插入数据的时候自增id出现异常。
分布式锁可以用来解决在分布式环境下,多个用户在同一时间读取/更新相同的资源带来的问题。
目标:
实现:
唯一键约束
我们可以使用MySQL的唯一性约束来实现分布式锁,整体的思路如下:
@SpringBootApplication @EnableAutoConfiguration @Configuration @ComponentScan @Repository @Service @RestController @ResponseBody @Component @Bean @AutoWired @request body @path value.
1、@SpringBootApplication
这个注解是Spring Boot最核心的注解,用在 Spring Boot的主类上,标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力。实际上这个注解是@Configuration,@EnableAutoConfiguration,@ComponentScan三个注解的组合。由于这些注解一般都是一起使用,所以Spring Boot提供了一个统一的注解@SpringBootApplication。
2、@EnableAutoConfiguration
允许 Spring Boot 自动配置注解,开启这个注解之后,Spring Boot 就能根据当前类路径下的包或者类来配置 Spring Bean。
3、@Configuration
用于定义配置类,指出该类是 Bean 配置的信息源,相当于传统的xml配置文件,一般加在主类上。如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
4、@ComponentScan
组件扫描。让spring Boot扫描到Configuration类并把它加入到程序上下文。 @ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,
5、@Repository
用于标注数据访问组件,即DAO组件。
使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
6、@Service
一般用于修饰service层的组件
7、@RestController
用于标注控制层组件(如struts中的action),表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器;它是@Controller和@ResponseBody的合集。
8、@ResponseBody
表示该方法的返回结果直接写入HTTP response body中
一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。
9、@Component
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
10、@Bean
相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
11、@AutoWired
byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
当加上(required=false)时,就算找不到bean也不报错。
@Autowired 和 @Resource 都是 Spring/Spring Boot 项目中,用来进行依赖注入的注解。它们都提供了将依赖对象注入到当前对象的功能,
1、读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
2、加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在
MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3、构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4、创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5、Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
6、MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数, 该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
7、输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
8、输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程
循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环。比如“A对象依赖B对象,而B对象也依赖A对象”,或者“A对象依赖B对象,B对象依赖C对象,C对象依赖A对象”;
即是mapper.xml里面的A查询的resultMap包含了B属性(B属性是通过子查询得到的),而B属性中又包含了A(B查询的resultMap中又包含了A的查询),就会造成A-B-A的情况。
Mybatis中利用的是一级缓存、空占位符,和延迟加载来解决循环依赖问题的。
Mybatis 框架是一个轻量级的 ORM 框架,通常不会出现循环依赖问题。但是,如果在实际应用场景中出现了循环依赖的情况,Mybatis 也提供了一些解决方案,以下是其中两种:
使用延迟加载(Lazy Loading)特性
Mybatis 支持延迟加载,可以将对象的加载推迟到真正需要使用它时再进行加载,这样就可以避免一些不必要的依赖。具体实现方式是通过在 Mapper 文件中配置使用延迟加载的关联属性或集合属性。
你知道索引有哪些吗?大家肯定都能霹雳啪啦地说出聚簇索引、主键索引、二级索引、普通索引、唯一索引、hash索引、B+树索引等等。
然后再问你,你能将这些索引分一下类吗?可能大家就有点模糊了。其实,要对这些索引进行分类,要清楚这些索引的使用和实现方式,然后再针对有相同特点的索引归为一类。
我们可以按照四个角度来分类索引。
索引优化规则:
1)如果MySQL估计使用索引比全表扫描还慢,则不会使用索引。
返回数据的比例是重要的指标,比例越低越容易命中索引。记住这个范围值——30%,后面所讲的内容都是建立在返回数据的比例在30%以内的基础上。
2)前导模糊查询不能命中索引。
选择区分度高的字段建立索引
在允许的情况下,尽量选择占用存储空间少的字段建立索引
where字句中经常被使用到的字段应该建立索引,分组字段或排序字段应创建索引,多表连接字段应创建索引
更新频繁的字段不适合建立索引
建立索引遵循最左前缀原则
尽量使用前缀索引
尽量设计多列索引,避免创建重复的索引以及删除未使用的索引
尽可能设计三星索引
我一般遵从五个原则:
学习MySQL是有很多好处的,特别是如果你有兴趣或需要与数据库相关的工作、应用开发或数据管理。以下是学习MySQL的一些重要理由:
数据存储和管理:MySQL是一个强大的关系型数据库管理系统,用于存储和管理数据。它可以帮助你组织、检索、更新和删除数据,以便更好地管理和维护信息。
数据分析:MySQL可以用于存储大量数据,这对于数据分析和报告生成非常有用。学习如何查询和分析数据可以帮助你做出更好的商业决策。
软件开发:MySQL是许多应用程序的后端数据库系统,特别是Web应用程序。学习MySQL可以帮助你开发具有强大数据存储需求的应用程序。
职业机会:具备MySQL技能可以增加你在数据库管理、数据分析、应用开发和相关领域的职业机会。数据库管理人员、数据分析师和开发人员通常需要熟练掌握MySQL。
开源性质:MySQL是开源的,免费使用,有一个庞大的社区支持。这意味着你可以轻松获取学习资源、文档和支持。
安全性:学习如何设置MySQL数据库以确保数据的安全性对于组织和维护敏感信息的应用程序至关重要。
可扩展性:MySQL支持可扩展性,这意味着它可以处理大型数据集并随着需求的增长而扩展。
跨平台:MySQL可以在多种操作系统上运行,包括Windows、Linux和macOS,因此它具有跨平台的灵活性。
总之,学习MySQL可以为你提供广泛的数据库管理和数据处理技能,有助于你在多个领域中取得成功,从职业生涯到数据分析和应用开发。
云数据库服务: 云计算和云数据库服务的发展将继续推动数据库管理的演变。像Amazon RDS、Google Cloud SQL和Azure Database这样的云平台提供了MySQL的托管服务,使数据库的管理更加简化和灵活。
大数据集成: 数据分析和大数据处理的需求将继续增长。因此,MySQL可能会更好地整合大数据技术,以便更有效地处理和分析大型数据集。
性能和可扩展性: MySQL将继续提高性能和可扩展性,以适应不断增长的数据和用户负载。这可能包括更快的查询处理、支持更大的数据集以及更好的集群和复制功能。
安全性: 鉴于数据泄漏和安全威胁的风险,MySQL将不断加强安全性特性,以确保数据的安全性和隐私。
开源社区和生态系统: MySQL的开源本质将继续吸引开发者和社区的支持。MySQL社区将继续提供插件、工具和改进,以增强MySQL的功能。
新技术集成: MySQL可能会集成新的技术,如人工智能、机器学习和自动化,以提供更高级的数据库管理和分析功能。
多模型数据库: 未来,数据库系统可能会更多地支持多模型数据,包括关系型、文档型和图形型数据。这样可以更灵活地满足不同应用的需求。
请注意,数据库技术领域在不断演进,未来的发展趋势可能会受到技术创新、市场需求和其他因素的影响。因此,了解最新趋势和持续学习数据库技术非常重要,以保持竞争力。如果你有特定的MySQL版本或用例的问题,建议查阅MySQL官方网站或参考最新的数据库技术新闻。
Spring和Spring Boot都是Java应用程序开发中常用的框架,但它们在设计和使用上有一些区别:
1. **Spring框架**:
- **全栈框架**:Spring是一个广泛的全栈框架,提供了众多模块和组件,包括Spring Core(Spring IoC容器)、Spring AOP(面向切面编程)、Spring MVC(用于构建Web应用程序的MVC框架)、Spring Data(用于数据访问的模块)等。你可以根据需要选择和配置这些模块来构建应用程序。
- **配置复杂性**:Spring框架需要显式的配置,通常使用XML配置文件或Java注解来定义Bean、依赖注入等。这提供了更大的灵活性,但也可能导致较多的配置工作。
- **相对较轻量**:Spring框架本身相对较轻量,不包括内置的Web服务器或其他功能。你需要手动集成其他框架或工具,以构建完整的应用程序。
2. **Spring Boot**:
- **微服务框架**:Spring Boot是一个用于构建微服务应用程序的快速开发框架。它旨在简化Spring应用程序的开发,减少开发者的配置工作,提供一站式解决方案。
- **自动配置**:Spring Boot引入了自动配置,通过默认设置和约定促使开发者进行更少的配置。这意味着你可以更快地启动项目,因为Spring Boot会自动配置许多常见的组件,如数据库连接、Web服务器等。
- **嵌入式Web服务器**:Spring Boot包括嵌入式的Web服务器,如Tomcat、Jetty和Undertow,这使得构建独立的Web应用程序变得更加容易。
- **生态系统**:Spring Boot配备了一系列开发工具、监控工具和可插拔的模块,使得构建微服务和云原生应用变得更容易。
总的来说,Spring Boot是建立在Spring框架之上的,旨在提供更简化的开发体验,特别适用于微服务架构和快速原型开发。如果你需要更多的控制和自定义选项,或者正在构建复杂的大型应用程序,Spring框架可能更适合你。选择哪个框架取决于你的项目需求和偏好。
pring Boot是一种用于构建Java应用程序的框架,具有许多优点,这些优点使它成为广泛采用的选择:
简化的配置: Spring Boot采用约定大于配置的原则,减少了应用程序的配置工作。它提供了许多默认配置,因此你可以快速启动项目而无需复杂的配置文件。
快速开发: Spring Boot具有自动配置功能,可自动配置许多常见组件,如数据库连接、Web服务器、安全性等。这加速了开发过程,让你能够快速开发原型或新功能。
内嵌式Web服务器: Spring Boot包括内嵌的Web服务器(如Tomcat、Jetty或Undertow),这使得构建独立的Web应用程序变得更加容易。你不必手动配置外部Web服务器。
生产就绪: Spring Boot提供了许多特性,以帮助你构建生产就绪的应用程序。它包括健康检查、指标、集成监控和管理工具,使应用程序容易管理和监视。
微服务支持: Spring Boot是构建微服务架构应用的理想选择。它支持开发微服务应用,并具有与Spring Cloud等库集成的能力,以简化微服务的构建和管理。
强大的开发工具: Spring Boot配备了一系列开发工具,如Spring Boot CLI、Spring Initializer和可嵌套的Spring应用程序容器,使得构建和测试应用程序变得更加容易。
自动依赖管理: Spring Boot使用Apache Maven或Gradle进行依赖管理,这使得管理项目依赖变得更加简单。Spring Boot的Starter POMs可以快速引入常见依赖。
广泛的社区支持: Spring Boot拥有一个庞大的开发者社区,提供了大量的文档、教程和支持。这使得学习和使用Spring Boot更容易。
安全性: Spring Boot提供了强大的安全性特性,包括身份验证和授权机制。它还集成了常见的安全框架,如Spring Security。
可扩展性: 虽然Spring Boot提供了很多默认配置,但你仍然可以根据需要进行自定义配置。这意味着你可以根据项目的要求扩展和定制应用程序。
总之,Spring Boot的主要优点是快速开发、简化配置、内嵌式Web服务器、微服务支持和生产就绪特性。这使它成为构建现代Java应用程序的强大工具。
MyBatis是一个流行的Java持久性框架,用于将Java应用程序中的对象映射到关系数据库。它的核心思想是通过XML或注解将SQL语句与Java对象进行映射,从而使数据库操作更加方便。以下是使用MyBatis的一般步骤:
导入MyBatis依赖: 首先,在你的Java项目中,你需要导入MyBatis框架的依赖。你可以使用构建工具(如Maven或Gradle)来管理这些依赖。
创建数据库表和模型类: 在数据库中创建表,然后为这些表创建相应的Java模型类,以便将数据库记录映射到Java对象。
配置MyBatis: 配置MyBatis的数据源、SQL映射文件、以及其他设置。MyBatis配置文件通常是mybatis-config.xml
,其中包含了数据库连接信息和其他配置。
创建SQL映射文件: 创建XML文件,其中包含SQL查询、插入、更新和删除等语句,以及这些语句与Java对象的映射规则。这些SQL映射文件通常存储在mapper
目录下。
创建DAO接口: 创建一个DAO(Data Access Object)接口,该接口定义了在数据库中执行的操作,如查询、插入、更新等。
编写Java代码: 在Java代码中,创建MyBatis的SqlSessionFactory,它是MyBatis的核心对象,用于创建SqlSession。然后,使用SqlSession执行SQL语句。
关闭会话: 在完成数据库操作后,不要忘记关闭SqlSession以释放资源。
运行应用程序: 编写完代码后,运行你的Java应用程序,它将使用MyBatis执行数据库操作。
这些是使用MyBatis的一般步骤,当然,根据项目的需求和复杂性,你可能需要更多的配置和定制。MyBatis提供了丰富的功能,包括参数映射、结果集映射、动态SQL等,以帮助你处理各种数据库操作。你可以参考MyBatis的官方文档和示例来更深入地了解如何使用它。
1. 旁路缓存模式
我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景,服务端需要同时维系 db 和 cache,并且是以 db 的结果为准。
写:先更新 db,然后直接删除 cache 。
读 : 从 cache 中读取数据,读取到就直接返回;cache 中读取不到的话,就从 db 中读取数据返回,再把数据放到 cache 中。
“在写数据的过程中,可以先删除 cache ,后更新 db 么?” 那肯定是不行的!因为这样可能会造成 数据库(db)和缓存(Cache)数据不一致的问题。
2. 读写穿透
服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 db,从而减轻了应用程序的职责。
写:先查 cache,cache 中不存在,直接更新 db。cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(同步更新 cache 和 db)。
读:从 cache 中读取数据,读取到就直接返回 。读取不到的话,先从 db 加载,写入到 cache 后返回响应。
3.异步缓存写入
由 cache 服务来负责 cache 和 db 的读写。只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。
1. Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。 将方法改为public
2. 发生自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,在类内部调用调用类内部@Transactional标注的方法,这种情况下也会导致事务不开启。
3.异常被吃了,或者异常类型错误,事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
内存泄漏:不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄露
为什么ThreadLocal会发生内存泄漏呢?
因为 每一个Thread维护一个ThreadLocalMap,ThreadLocalMap中的 key是弱引用,而value是强引用。当ThreadLoca1 没有被强引用时,在进行垃圾回收时,key 会被清理掉,而 value 不会被清理掉,这时如果不做任何处理,value 将永远不会被回收,产生内存泄漏。
如何解决ThreadLocal的内存泄漏?
其实在ThreadLoca]在设计的时候已经考虑到了这种情况,在调用set 、get0、remove() 等方法时就会清理掉 key为 null的记录,所以在使用完 ThreadLoca1 后最好手动调用 remove( 方法。
强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。
如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。
弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。
由于Thread中包含变量ThreadLocalMap,因此ThreadLocalMap与Thread的生命周期是一样长,如果都没有手动删除对应key,都会导致内存泄漏。
但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set(),get(),remove()的时候会被清除。
因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
互斥锁、自旋锁、读写锁、乐观锁、悲观锁
线程A 加锁失败 就会释放cpu,进入睡眠,也可以说用户态陷入到内核态,锁释放后,又会睡眠」状态的线程会变为「就绪」状态
读写锁由「读锁」和「写锁」两部分构成,写锁是独占锁,因为任何时刻只能有一个线程持有写锁,类似互斥锁和自旋锁,而读锁是共享锁,因为读锁可以被多个线程同时持有。
读写锁在读多写少的场景,能发挥出优势。
根据实现的不同,读写锁可以分为「读优先锁」和「写优先锁」。
悲观锁做事比较悲观,它认为多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问共享资源前,先要上锁。
乐观锁做事比较乐观,它假定冲突的概率很低,它的工作方式是:先修改完共享资源,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。乐观锁全程并没有加锁,所以它也叫无锁编程。
三个线程抢占十个资源使用什么锁
互斥锁
线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
corePoolSize: 核心线程池的大小
maximumPoolSize: 线程池能创建线程的最大个数
keepAliveTime: 空闲线程存活时间
unit: 时间单位,为 keepAliveTime 指定时间单位
workQueue: 阻塞队列,用于保存任务的阻塞队列
threadFactory: 创建线程的工程类
handler: 饱和策略 (拒绝策略)
一、corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。
二、maximumPoolSize 线程池最大线程数量
当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列(后面会介绍)中。如果队列也已满,则会去创建一个新线程来出来这个处理。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
三、keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
四、unit 空闲线程存活时间单位
keepAliveTime的计量单位
五、workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
六、threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
七、handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:
1. CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
2. AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
3. DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
4. DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
执行速度:drop > truncate >> DELETE
其中三种现象:
三次握手:
CLOSE
状态。先是服务端主动监听某个端口,处于 LISTEN
状态client_isn
),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN
标志位置为 1
,表示 SYN
报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT
状态。SYN
报文后,首先服务端也随机初始化自己的序号(server_isn
),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1
, 接着把 SYN
和 ACK
标志位置为 1
。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD
状态。客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK
标志位置为 1
,其次「确认应答号」字段填入 server_isn + 1
,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED
状态。
服务端收到客户端的应答报文后,也进入 ESTABLISHED
状态。
四次挥手:
FIN
标志位被置为 1
的报文,也即 FIN
报文,之后客户端进入 FIN_WAIT_1
状态。ACK
应答报文,接着服务端进入 CLOSE_WAIT
状态。ACK
应答报文后,之后进入 FIN_WAIT_2
状态。FIN
报文,之后服务端进入 LAST_ACK
状态。FIN
报文后,回一个 ACK
应答报文,之后进入 TIME_WAIT
状态ACK
应答报文后,就进入了 CLOSE
状态,至此服务端已经完成连接的关闭。2MSL
一段时间后,自动进入 CLOSE
状态,至此客户端也完成连接的关闭。索引是数据的目录。索引的定义就是帮助存储引擎快速获取数据的一种数据结构
B+Tree 是一种多叉树,叶子节点才存放数据,非叶子节点只存放索引,而且每个节点里的数据是按主键顺序存放的。每一层父节点的索引值都会出现在下层子节点的索引值中,因此在叶子节点中,包括了所有的索引值信息,并且每一个叶子节点都有两个指针,分别指向下一个叶子节点和上一个叶子节点,形成一个双向链表。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。再二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
1. 节点是红色或黑色
2. 根节点是黑色。
3 每个叶节点(NIL节点,空节点)是黑色的。
4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
B+ 树是一种树数据结构,是一个n叉排序树,每个节点通常有多个孩子,一棵B+树包含根节点、内部节点和叶子节点。根节点可能是一个叶子节点,也可能是一个包含两个或两个以上孩子节点的节点。
B+树是对B树的一种变形树,它与B树的差异在于:
一个进程虽然任一时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用,
单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力
1.管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2.消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
3.共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
4.信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
5.信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6.套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
Jdk1.7:数组 + 链表 ( 当数组下标相同,则会在该下标下使用链表)
Jdk1.8:数组 + 链表 + 红黑树 (预值为8 如果链表长度<=8则会把链表变成红黑树 )
Jdk1.7中链表新元素添加到链表的头结点,先加到链表的头节点,再移到数组下标位置
Jdk1.8中链表新元素添加到链表的尾结点
其实主要就是为了解决jdk1.8以前hash冲突所导致的链化严重的问题,因为链表结构的查询效率是非常低的,他不像数组,能通过索引快速找到想要的值,链表只能挨个遍历,当hash冲突非常严重的时候,链表过长的情况下,就会严重影响查询性能,本身散列列表最理想的查询效率为O(1),当时链化后链化特别严重,他就会导致查询退化为O(n)为了解决这个问题所以jdk8中的HashMap添加了红黑树来解决这个问题,当链表长度>=8的时候链表就会变成红黑树,红黑树其实就是一颗特殊的二叉排序树嘛,这个时间复杂…反正就是要比列表强很多
2.1开放定址法
该方法也叫做再散列法,其基本原理是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi 。
2.2再Hash法
这种方法就是同时构造多个不同的哈希函数: Hi=RH1(key) i=1,2,…,k。当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
2.3链地址法(Java就是采用这种方法)
其基本思想: 将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
2.4建立公共溢出区
这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
1. SQL 索引不生效
2. 优化器选错了索引
修改方案:
使用force index
强行选择某个索引
修改你的SQl,引导它使用我们期望的索引
优化你的业务逻辑
优化你的索引,新建一个更合适的索引,或者删除误用的索引。
3. limit深分页问题
修改方案:
通过减少回表次数来优化。一般有标签记录法和延迟关联法。
把条件转移到主键索引树,然后减少回表
4. 单表数据量太大
考虑分库分表了。
分库分表可能导致的问题:
事务问题
跨库问题
排序问题
分页问题
分布式ID
5. join 或者子查询过多
6. in元素过多
每一层负责的职能都不同,如下:
由于 OSI 模型实在太复杂,提出的也只是概念理论上的分层,并没有提供具体的实现方案。
事实上,我们比较常见,也比较实用的是四层模型,即 TCP/IP 网络模型,Linux 系统正是按照这套网络模型来实现网络协议栈的。
TCP/IP 网络模型共有 4 层,分别是应用层、传输层、网络层和网络接口层,每一层负责的职能如下:
冒泡排序、选择排序、插入排序、归并排序、快速排序、希尔排序、堆排序、计数排序、基数排序
第一次同步:
主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接。就可以传输命令了
主从复制共有三种模式:全量复制、基于长连接的命令传播、增量复制。
主从服务器第一次同步的时候,就是采用全量复制,此时主服务器会两个耗时的地方,分别是生成 RDB 文件和传输 RDB 文件。为了避免过多的从服务器和主服务器进行全量复制,可以把一部分从服务器升级为「经理角色」,让它也有自己的从服务器,通过这样可以分摊主服务器的压力。
第一次同步完成后,主从服务器都会维护着一个长连接,主服务器在接收到写操作命令后,就会通过这个连接将写命令传播给从服务器,来保证主从服务器的数据一致性。
如果遇到网络断开,增量复制就可以上场了,不过这个还跟 repl_backlog_size 这个大小有关系。
如果它配置的过小,主从服务器网络恢复时,可能发生「从服务器」想读的数据已经被覆盖了,那么这时就会导致主服务器采用全量复制的方式。所以为了避免这种情况的频繁发生,要调大这个参数的值,以降低主从服务器断开后全量同步的概率。
简单来说就是:对象没有引用了或者对象不可达
常见的有两种算法,分别是 引用计数法 和 可达性分析法
通过一系列可被作为 GC Roots 的根对象来作为起始节点,从这些节点开始,根据引用关系向下搜索,搜索过程的就是一条引用链(Reference Chain),没有在这个链条上面的对象,也就是根节点通过引用链不可达到这个对象时,就认为这个对象是可以被回收的。
表结构问题:表的设计可能不合理,可能需要优化,例如使用适当的数据类型,避免使用大型文本字段等。
区别:
HashMap不支持并发操作,没有同步方法,ConcurrentHashMap支持并发操作,通过继承 ReentrantLock(JDK1.7重入锁)/CAS和synchronized(JDK1.8内置锁)来进行加锁(分段锁),每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
JDK1.8之前HashMap的结构为数组+链表,JDK1.8之后HashMap的结构为数组+链表+红黑树;JDK1.8之前ConcurrentHashMap的结构为segment数组+数组+链表,JDK1.8之后ConcurrentHashMap的结构为数组+链表+红黑树。
ConcurrentHashMap 采用了分段锁技术,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。
==和equals,需不需要重写hashcode方法
需要
方法区(Method Area)
方法区用于存储JVM加载完成的类型信息、常量、静态变量、即时编译器编译后的代码缓存,方法区和 Java 堆区一样,都是线程共享的内存区域。
在JDK8以前,使用永久代的方式来实现方法区,JDK8以后,永久代的概念被废弃了,方法区改用和 JRockit、J9一样的在本地内存中实现的元空间(Meta Space)来代替,好处是元空间会在运行时根据需要动态调整,只要没有超过当前进程可用的内存上限(32位和64位系统各不相同),就不会出现溢出的问题。
Java堆(JVM堆、Java heap)
堆区负责存放对象实例,当Java创建一个类的实例对象或者数组时,都会在堆中为新的对象分配内存。
虚拟机中只有一个堆,程序中所有的线程都共享它。
通常情况下,堆占用的内存空间是最多的。
堆的存取方式为管道类型,先进先出。
在程序运行中,可以动态的分配堆的内存大小。
虚拟机栈(JVM栈、VM Stack)
在Java栈中只保存基础数据类型(参考:Java 基本数据类型 - 四类八种 - 知乎专栏)和对象的引用,注意只是对象的引用而不是对象本身哦,对象是保存在堆区中的。
拓展知识:像String、Integer、Byte、Short、Long、Boolean等等包装类型,它们是存放于堆中的。
栈的存取类型为类似于水杯,先进后出。
栈内创建的基本类型数据在超出其作用域后,会被自动释放掉,它不由JVM GC管理。而在栈内创建的引用类型实例,则还是由JVM GC管理。
当一个线程创建运行的时候,与之对应的栈就创建了,每个栈中的数据都是私有的,其他线程不能访问。
每个线程都会建立一个栈,每个栈又包含了若干个栈帧,每个栈帧对应着每个方法的每次调用,栈帧包含了三个部分:
局部变量区(方法内基本类型变量、对象实例的引用)
操作数栈区(存放方法执行过程中产生的中间结果)
运行环境区(动态连接、正确的方法返回相关信息、异常捕捉)
虚拟机栈在深度溢出或扩展失败的时候,会分别抛出StackOverflowError 和 OutOfMemoryError 异常。
本地方法栈(Native Method Stack)
本地方法栈的功能和JVM栈非常类似,区别在于虚拟机栈执行的是Java方法,本地方法栈执行的是本地(Native)方法服务,存储的也是本地方法的局部变量表,本地方法的操作数栈等信息。
栈的存取类型为类似于水杯,先进后出。
栈内的数据在超出其作用域后,会被自动释放掉,它不由JVM GC管理。
每一个线程都包含一个栈区,每个栈中的数据都是私有的,其他栈不能访问。
本地方法栈是在 程序调用 或 JVM调用 本地方法接口(Native)时候启用。
本地方法都不是使用Java语言编写的,它们可能由C或其他语言编写,本地方法也不由JVM去运行,所以本地方法的运行不受JVM管理。
HotSpot VM将本地方法栈和JVM栈合并了。
本地方法栈也会在深度溢出或扩展失败的时候,分别抛出StackOverflowError 和 OutOfMemoryError 异常。
程序计数器
在JVM的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,为了各条线程之间的切换后计数器能恢复到正确的执行位置,所以每条线程都会有一个独立的程序计数器。
程序计数器仅占很小的一块内存空间。
当线程正在执行一个Java方法,程序计数器记录的是正在执行的JVM字节码指令的地址。如果正在执行的是一个Natvie(本地方法),那么这个计数器的值则为空(Underfined)。
程序计数器不会抛出 OutOfMemoryError(内存不足错误)
1. 对索引使用左或者左右模糊匹配
当我们使用左或者左右模糊匹配的时候,也就是 like %xx
或者 like %xx%
这两种方式都会造成索引失效。
2. 对索引使用函数、对索引进行表达式计算、对索引进行表达式计算
3. 联合索引非最左匹配 ,,多个普通字段组合在一起创建的索引就叫做联合索引,也叫组合索引。
创建联合索引时,我们需要注意创建时的顺序问题,因为联合索引 (a, b, c) 和 (c, b, a) 在使用的时候会存在差别。
联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配。
4. WHERE 子句中的 OR ,,在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。
MVCC: Multiversion Concurrency Control,翻译为多版本并发控制,其目标就是为了提高数据库在高并发场景下的性能。
MVCC最大的优势:读不加锁,读写不冲突。在读多写少的场景下极大的增加了系统的并发性能
看到MVCC的作用就是在不加锁的情况下,解决数据库读写冲突问题,并且解决脏读、幻读、不可重复读等问题,但是不能解决丢失修改问题。
解决幻读:
MVCC加上间隙锁的方式
(1)在快照读读情况下,mysql通过mvcc来避免幻读。开始事务后(执行 begin 语句后),在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好了避免幻读问题。
(2)在当前读读情况下,mysql通过next-key来避免幻读。锁住某个条件下的数据不能更改。
Spring
Spring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就是控制反转(IOC),和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。
2、SpringMVC
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。SpringMVC是一种web层mvc框架,用于替代servlet(处理|响应请求,获取表单参数,表单校验等。将其封装,简化其开发难度,让开发人员无需处理整个HttpRequest,也无需处理IO流,只需关心业务处理。同时它也进行了切面封装,可以定义全局异常处理器。基于Servlet开发时,IO返回的即是页面显示的,而spring mvc却可以返回一个渲染后页面。
spring mvc发展到现在,已经有@GetMapping, @PostMapping, @RestController等注解,进一步简化了开发。
3、SpringBoot
Springboot是一个微服务框架,延续了spring框架的核心思想IOC和AOP,简化了应用的开发和部署。Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。提供了一堆依赖打包,并已经按照使用习惯解决了依赖问题—>习惯大于约定。
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。
实例化 -> 属性赋值 -> 初始化 -> 销毁
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
基于接口的动态代理: 用到的类是Proxy的newProxyInstance静态方法创建,要求被代理对象至少实现一个接口,如果没有,则不能创建代理对象
基于cglib的动态代理
要导入cglib第三方库,使用的类是Enhancer的create静态方法创建,要求被代理类不能是最终类,即不能用final修饰,如String类。
(1)系统从磁盘中读取数据是以磁盘块(block)为单位的,位于同一磁盘的数据都会被读取出来而不是需要什么就读取什么。
数据库系统通常将索引的一个节点设置为一个页的大小,这样每个节点只需一次I/O就可以完全载入。如果数据不在同一个磁盘上,那么通常需要移动磁臂来寻找柱面,效率低下。
(2)因为B+树非页节点都用来存储键值,数据都存储在叶节点中,所以当数据库记录很多时,B+树的深度也不会很大,所以I/O查询次数也不会很多最多h-1次(根节点常驻内存)。B+ 树相对于红黑树有更低的树高,进行寻道的次数与树高成正比,在同一个磁盘块上进行访问只需要很短的磁盘旋转时间,所以 B+ 树更适合磁盘数据的读取。
MySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。
MySQL 集群的主从复制过程梳理成 3 个阶段:
具体详细过程如下:
在完成主从复制之后,你就可以在写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行。
在add方法上添加synchronized关键字,修饰整个数组,将该方法体设为线程同步块,方法体每次只能有一个线程执行,这样就有效的解决了线程安全的问题。
特点:
- Spirng Cloud 天然支持 Spring Boot,更加便于业务落地。
- 实现了:高内聚,低耦合.
- eureka组件实现了:服务的注册与发现 的功能;
- Feign + Ribbon组件实现了:服务之间的调用和负载均衡 的功能;
- Hystrix组件实现了:服务的降级,服务的熔断(即,断路器) 的功能;
- config组件实现了:分布式的配置管理(通过git管理) 的功能;
- Zuul组件实现了:路由/网关 的功能;
Sleuth - 服务追踪
第一种回答:
redis的集群有两种。一种是redis的哨兵集群,一种是redis cluster。
从三个方面来回答:
一是redis的哨兵集群是基于主从复制来实现的,它可以实现读写分离,分担redis读操作的压力,而redis cluster里面的slaver节点,只是实现冷备的一个机制,它只有在master宕机之后才会工作。
二是redis哨兵机制无法在线扩容,所以它的并发压力受限于单个服务器的资源的配置,redis cluster提供了一个基于slot槽的一个数据分片的一个机制,它可以实现在线扩容去提升读写的性能。
三是从集群架构的层面来看。redis哨兵集群是一主多从,而redis cluster是一个多主多从的一个机制。
第二种回答:
哨兵机制是单节点的高可用方案,它主要是通过监控Redis主节点的状态,并在主节点出现故障时自动将从节点升级为主节点,从而实现高可用。哨兵机制具有简单、灵活、易于部署等特点,但是无法实现水平扩展。
而Redis集群则是分布式的高可用方案,它将数据分散到不同的节点上存储,通过集群内部的协调机制实现数据的负载均衡和故障转移。Redis集群具有水平扩展、高性能、高可用等特点,但是相对于哨兵机制,集群部署和维护较为复杂,需要考虑数据分片、节点间通信、数据迁移等问题。
因此,哨兵机制适用于单节点的高可用需求,而Redis集群适用于大规模、高并发的分布式应用场景。