这个就不多说了,主要是根据自己的简历上面的项目进行一个简短的概括使用的技术栈和什么背景解决了什么问题等等。
在Java中线程安全的类比如在Collection接口下的vector,queue底下的Bolockingqueue,还有map接口下的Hashtable,ConcurrentHashMap都是线程安全的,还有StringBuffer也是线程安全的,还有在Java的JUC包下的类都是线程安全的。
平时用过的是StringBuffer使用过,StringBuffer主要是用于在对可变字符串的频繁添加和修改过程过中使用。比如在家政项目的门户模块中,有一个添加地址的使用,就是用到了StringBuffer,顾名思义线程安全的类主要是为了保障线程安全的,避免多线程对共享变量的操作引起数据的一致性问题。
mysql主要有有三种日志文件分别为:binlog,undolog,redolog三类
binlog又称为归档日志(二进制日志),主要是对数据库的中的数据进行数据备份,崩溃恢复,数据复制等操作,binlog主要是记载了有关DDL,DML语句对数据库的修改和添加操作,例如:insert,update,delete等操作都会记录在binlog日志文件中。在mysql的主从复制方面,会用到binlog,主节点主要是对数据的写操作记录到binlog中,而从节点主要是读操作,从而减少了数据库的压力。
undolog又称为回滚日志,主要是为了事务的回滚操作,当一个数据要进行修改时,首先undo log会记录修改前的数据状态,当该事务出现故障或者崩溃时,会通过Undolog进行回滚操作,并且在保证事务的隔离性方面使用了MVCC机制(采用的就是undolog链+redeView)
redolog又称为重做日志,主要是为了事务的崩溃恢复和数据持久操作,redolog会记录该数据的操作状态,当一个事务出现崩溃时,会通过redolog进行崩溃恢复,并且会通过一定的刷盘机制,将修改后的数据刷新到磁盘中。
项目中主要使用redis做缓存操作,比如在家政项目的门户界面使用了redis做缓存,可以提高门户界面的效率
redis的快主要是因为redis是基于纯内存的操作,相比于磁盘的操作来说速度会比较快,并且redis的主线程是单线程嘛,单线程避免了多线程带来的上下文切换所带来的性能开销和多线程安全型的保证,因此也可以提高运行效率,再者redis还采用了IO多路复用技术技术,在面对多个网络请求时,可以显著提高响应速度,并且redis还有这高效的数据结构,例如散列表,集合等。
redis在保证高性能方面,redis是基于key-value键值对的数据机构嘛,因此在查找数据方面得时间复杂度为O(1),并且redis也提供了数据的持久化机制,RDB,AOF和两者得混合使用都保证了redis得高性能方面
redis在保证高并发方面,redis可以通过集群方案,哨兵方案,主从复制等方面来理解。
Redis 中的字典(dict)是一种用于存储键值对的数据结构。它是一个散列表(Hash Table),也被称为哈希表。字典结构由三部分组成:哈希表(dictEntry **ht [2])、rehash 索引(int rehashidx)和一些统计信息(如字典中键值对的数量等)。
哈希表主要由哈希表节点+哈希表实际大小组成,哈希表节点是由三个域组成的(键,值,指向下一个哈希表节点的指针),而哈希表实际大小一般是2的幂次方
rehash索引是指;当哈希表中的键值对数量(dictEntry 的数量)与哈希表大小(size)的比例达到一定阈值(如 Redis 中负载因子大于等于 1 时),为了保持哈希表的性能,需要对哈希表进行扩展(或者收缩,如果键值对数量过少)。这一过程就是 rehash。
rehash的过程:在 rehash 过程中,Redis 会创建一个新的、更大(或更小)的哈希表(ht [1])。新哈希表的大小通常是原哈希表大小的 2 倍(扩展时)或者原哈希表大小的 1/2(收缩时)。 rehashidx 初始化为 0,表示开始对原哈希表(ht [0])中的第 0 个桶进行 rehash 操作。在 rehash 过程中,会逐个将原哈希表中的键值对重新计算哈希值,然后插入到新的哈希表中。 当 rehash 操作完成时,rehashidx 会被设置为 - 1,表示 rehash 过程结束,此时新的哈希表(ht [1])会替换原哈希表(ht [0])成为正式的哈希表
负载因子为 1 的原因 空间与时间的平衡 减少内存浪费:如果负载因子设置得过低,例如小于 1,意味着哈希表中有较多的空闲空间。虽然这可能会减少哈希冲突的概率(因为每个桶对应较少的键值对),但会造成内存的浪费。Redis 是一个内存数据库,内存资源是非常宝贵的,过多的空闲空间不利于内存的高效利用。 控制哈希冲突在可接受范围内:当负载因子为 1 时,表明哈希表的使用程度相对较高,但又不至于过度拥挤。在这个负载因子下,哈希冲突的数量在可接受的范围内。哈希冲突是指不同的键经过哈希计算后得到相同的桶索引,从而在桶中形成链表(用于存储这些冲突的键值对)。如果负载因子过高,哈希冲突会过多,导致查询操作在链表中查找的时间复杂度增加,影响性能;而负载因子为 1 时,在保证内存利用率的同时,哈希冲突对性能的影响相对较小。 性能优化与调整开销 触发 rehash 操作的时机:当负载因子达到 1 时,Redis 会触发 rehash 操作。rehash 操作是为了扩展哈希表的大小,以适应更多的键值对存储需求。选择负载因子为 1 作为触发 rehash 的阈值,可以在哈希表即将变得拥挤之前进行调整,避免性能急剧下降。如果负载因子的阈值设置得过高,哈希表可能会过度拥挤,导致查找、插入和删除操作的性能显著降低;而如果阈值设置得过低,会导致频繁的 rehash 操作,rehash 操作本身也需要消耗一定的系统资源(如 CPU 时间和内存),影响整体性能。
JVM(Java Virtual Machine)有众多的参数,可分为标准参数、非标准参数(-X 参数)和不稳定参数(-XX 参数):
一、标准参数 帮助相关参数 -help或-?:用于显示所有的标准参数信息。这有助于开发人员快速了解 JVM 的基本参数使用方法。 版本相关参数 -version:打印 JVM 版本信息并退出。例如,在命令行中执行java -version,会显示 Java 的版本号、Java 运行时环境(JRE)的构建版本以及 Java HotSpot ™ 64 - Bit Server VM 等相关信息。 -showversion:在启动 Java 程序时先显示版本信息,然后正常启动程序。 类路径相关参数 **-cp或-classpath:**用于指定类路径。例如java -cp /path/to/classes:/path/to/jars MyClass,其中/path/to/classes和/path/to/jars是包含类文件和 JAR 文件的目录,MyClass是要运行的主类。这对于确保 JVM 能够找到所需的类文件非常重要。
二、非标准参数(-X 参数) 堆内存相关参数 -Xms:设置 JVM 初始堆内存大小。例如-Xms512m表示初始堆内存为 512MB。合适的初始堆内存大小可以避免 JVM 在运行初期频繁调整堆内存大小。 -Xmx:设置 JVM 最大堆内存大小。如-Xmx1024m,限制了堆内存的最大容量为 1024MB。这有助于防止 JVM 因内存不足而导致程序崩溃或出现性能问题。
栈内存相关参数 -Xss:设置每个线程的栈内存大小。例如-Xss1m表示每个线程的栈内存为 1MB。栈内存大小会影响线程的创建数量和每个线程的执行情况,过小的栈内存可能导致栈溢出错误。
垃圾回收相关参数(部分) -Xloggc::指定垃圾回收日志文件的路径。例如-Xloggc:/var/log/gc.log,这样可以方便地记录和分析垃圾回收的情况,有助于排查内存泄漏等问题。
三、不稳定参数(-XX 参数) 垃圾回收器相关参数 -XX:+UseSerialGC:启用串行垃圾回收器。在单 CPU 环境或者对暂停时间要求不高的小型应用中,串行垃圾回收器可能是一种简单有效的选择。 -XX:+UseParallelGC:启用并行垃圾回收器。适用于多 CPU 环境下,注重吞吐量的应用程序。并行垃圾回收器可以利用多个 CPU 同时进行垃圾回收操作,提高垃圾回收效率。 -XX:+UseConcMarkSweepGC(简称 CMS):启用并发标记 - 清除垃圾回收器。这种垃圾回收器旨在减少垃圾回收时的停顿时间,适用于对响应时间要求较高的应用程序。 -XX:+UseG1GC:启用 G1 垃圾回收器。G1 垃圾回收器是一种面向服务器端应用的垃圾回收器,它具有可预测的停顿时间,并且在处理大内存应用时表现出色。
堆内存结构相关参数 -XX:NewRatio=:设置年轻代(Young Generation)和年老代(Old Generation)的比例。例如-XX:NewRatio = 2表示年老代与年轻代的比例为 2:1。合理调整这个比例可以根据应用程序的对象分配和回收特性来优化内存使用。 -XX:SurvivorRatio=:设置 Eden 区和 Survivor 区的比例。例如-XX:SurvivorRatio = 8表示 Eden 区与每个 Survivor 区的比例为 8:1。这有助于优化年轻代内存的分配和回收效率。
其他性能相关参数 -XX:MaxTenuringThreshold=:设置对象晋升到老年代的最大年龄阈值。例如-XX:MaxTenuringThreshold = 15,对象在年轻代中经过的垃圾回收次数达到这个阈值后就会晋升到老年代。这个参数可以根据对象的生命周期特点进行调整,以优化内存管理。 -XX:+PrintGCDetails:打印详细的垃圾回收细节。这对于分析垃圾回收过程、排查内存相关问题非常有用。
关于保证缓存和数据库中的数据一致性问题,主要的策略就是延迟双删+消息队列或者使用阿里巴巴开源的组件Canal 监听mysql的binlog日志,在同步写入到MQ中,redis客户端去拉取MQ中的数据即可。
关于第一种方案:延迟双删+消息队列,双删的目的是最大程度的保证缓存和数据库中数据的一致性问题,延迟的作用在于第二次删除的时候要等待一会再删除是为了避免因为并发问题导致保存旧值的情况发生,所以会延迟一段时间之后在删除,这样即使有并发问题,也可最大限度解决保存旧值问题,因为是延迟后删除的,可以最大程度保证缓存和数据库的最终一致性,而为什么需要到消息队列呢?是因为消息队列有消息确认应答机制,它可以保证执行完第一次删除缓存之后,即使此时服务器宕机了,也可以保证执行后续的流程,可以保证业务的完整性。
关于第二种方案:Canal 监听mysql的binlog日志,在同步写入到MQ中,redis客户端去拉取MQ中的数据即可。整体流程如下:
关于Canal的原理:
容器化技术主要解决以下几个问题:环境一致性问题,资源利用率问题,应用程序隔离问题,运维和部署等问题。
环境一致性问题:
资源利用率问题:
应用程序隔离问题:
运维和部署等问题:
容器化技术的原理:
算法思路:所有的状态在转移的时候的可能性都是唯一的。也就是说,我们可以从每一种边界情况开始「扩展」,也可以得出所有的状态对应的答案。
边界情况即为子串长度为 1 或 2 的情况。我们枚举每一种边界情况,并从对应的子串开始不断地向两边扩展。如果两边的字母相同,我们就可以继续扩展,例如从 P(i+1,j−1) 扩展到 P(i,j);如果两边的字母不同,我们就可以停止扩展,因为在这之后的子串都不能是回文串了。
可以发现,「边界情况」对应的子串实际上就是我们「扩展」出的回文串的「回文中心」。本质即为:我们枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。我们对所有的长度求出最大值,即可得到最终的答案。
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
if(n<2){
return s;
}
int start = 0,end = 0;
for(int i=0;i<n;i++){
//检查奇数字符串
int len1 = CheckString(s,i,i);
//检查偶数字符串
int len2 = CheckString(s,i,i+1);
int MaxString = Math.max(len1,len2);
if(MaxString > end - start){
start = i - (MaxString - 1) / 2;
end = i + MaxString / 2;
}
}
return s.substring(start,end+1);
}
private int CheckString(String s,int left,int right){
while(left>=0 && right<s.length() && s.charAt(left) == s.charAt(right)){
--left;
++right;
}
return right - left -1;
}
}