Spring事务异常一定会回滚吗?什么情况事务会失效?
1、事务失效的7种情况
1.1、未启用spring事务管理功能
1.2、方法不是public类型的
@Transaction 可以用在类上、接口上、public方法上,如果将@Trasaction用在了非public方法上,事务将无效。
1.3、数据源未配置事务管理器
spring是通过事务管理器了来管理事务的,一定不要忘记配置事务管理器了,要注意为每个数据源配置一个事务管理器:
1.4、自身调用问题
spring是通过aop的方式,对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。
1.5、异常类型错误
spring事务回滚的机制:对业务方法进行try catch,当捕获到有指定的异常时,spring自动对事务进行回滚,并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。
1.6、异常被吞了(重点,面试官给你上了一课)
当业务方法抛出异常,spring感知到异常的时候,才会做事务回滚的操作,若方法内部将异常给吞了,那么事务无法感知到异常了,事务就不会回滚了。
事务操作2发生了异常,但是被捕获了,此时事务并不会被回滚
@Transactional
public void m1(){
事务操作1
try{
事务操作2,内部抛出了异常
}catch(Exception e){
}
}
1.7、业务和spring事务代码必须在一个线程中
spring事务实现中使用了ThreadLocal,ThreadLocal大家应该知道吧,可以实现同一个线程中数据共享,必须是同一个线程的时候,数据才可以共享,这就要求业务代码必须和spring事务的源码执行过程必须在一个线程中,才会受spring事务的控制,比如下面代码,方法内部的子线程内部执行的事务操作将不受m1方法上spring事务的控制,这个大家一定要注意
@Transactional
public void m1() {
new Thread() {
一系列事务操作
}.start();
}
建立索引但是没有走索引情况
- 在索引字段上运算
- 多个索引字段进行运算
- 使用LIKE关键字,如果值是’%XXX’或者’%XXX%’,则无法使用索引。
- GROUP BY子句,查询数据库表,WHERE条件不包含索引列,但是GROUP BY子句的条件中包含索引列。这个时候即使explain会显示它是走group by字句的索引,但是扫描的rows也是接近于全表扫描。
- ORDER BY子句和GROUP BY子句类似
- 联合索引
什么情况引起fullGC
- System.gc()方法的调用
- 老年代空间不足
- 永生区空间不足
- 堆中分配很大的对象
HashMap为什么是不安全 不安全的实例
HashMap会进行resize操作,在resize操作的时候会造成线程不安全。下面将举两个可能出现线程不安全的地方。
1、put的时候导致的多线程数据不一致。
这个问题比较好想象,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。
2、另外一个比较明显的线程不安全的问题是HashMap的get操作可能因为resize而引起死循环(cpu100%)
我们假设有两个线程同时需要执行resize操作,我们原来的桶数量为2,记录数为3,需要resize桶到4,原来的记录分别为:[3,A],[7,B],[5,C],在原来的map里面,我们发现这三个entry都落到了第二个桶里面。
假设线程thread1执行到了transfer方法的Entry next = e.next这一句,然后时间片用完了,此时的e = [3,A], next = [7,B]。线程thread2被调度执行并且顺利完成了resize操作,需要注意的是,此时的[7,B]的next为[3,A]。此时线程thread1重新被调度运行,此时的thread1持有的引用是已经被thread2 resize之后的结果。线程thread1首先将[3,A]迁移到新的数组上,然后再处理[7,B],而[7,B]被链接到了[3,A]的后面,处理完[7,B]之后,就需要处理[7,B]的next了啊,而通过thread2的resize之后,[7,B]的next变为了[3,A],此时,[3,A]和[7,B]形成了环形链表,在get的时候,如果get的key的桶索引和[3,A]和[7,B]一样,那么就会陷入死循环。
sql如何具体优化
基础Sql优化:
- 查询SQL尽量不要使用select *,而是具体字段
- 避免在where子句中使用or来连接条件
- 使用varchar代替char
- 尽量使用数值替代字符串类型
- 查询尽量避免返回大量数据
- 使用explain分析你SQL执行计划
- 是否使用了索引及其扫描类型
- 创建name字段的索引
- 优化like语句:
- 字符串怪现象
- 索引不宜太多,一般5个以内
- 索引不适合建在有大量重复数据的字段上
- where限定查询的数据
- 避免在索引列上使用内置函数
- 避免在where中对字段进行表达式操作
- 避免在where子句中使用!=或<>操作符
- 去重distinct过滤字段要少
- where中使用默认值代替null
为什么使用B+树
- B TREE能解决的问题,B+TREE也能够解决(降低树的高度,增大节点存储数据量)
- B+Tree扫库和扫表能力更强
- B+TREE磁盘读写能力更强。B+树的高度低降低里IO次数
- B+Tree排序能力更强。B+树支持顺序和随机遍历,而B树只支持随机
- B+Tree查询性能稳定。B+Tree数据只保存在叶子节点,每次查询数据,查询IO次数一定是稳定的。
参考链接
最左原则 in和=
在 InnoDB 中联合索引只有先确定了前一个(左侧的值)后,才能确定下一个值。如果有范围查询的话,那么联合索引中使用范围查询的字段后的索引在该条 SQL 中都不会起作用。值得注意的是,in 和 = 都可以乱序,比如有索引(a,b,c),语句 select * from t where c =1 and a=1 and b=1,这样的语句也可以用到最左匹配,因为 MySQL 中有一个优化器,他会分析 SQL 语句,将其优化成索引可以匹配的形式,即 select * from t where a =1 and b=1 and c=1
数组get0和get500万一样吗
一样的
数组的随机访问速度快,这主要依赖于连续的物理内存。因为内存是连续的,且每个元素占用的空间是等长的,所以不管数组多长,不管访问哪个元素,时间复杂度都是O(1),整个访问过程都只需要两次寻址。
由于数组的内存是连续的,当需要创建长度很大的集合时,JVM很可能由于找不到一块很大的连续内存而导致内存溢出,这时可以考虑使用链表。
参考链接
单例模式的优点
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
主要优点:
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
主要缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
原文链接:https://blog.csdn.net/iblade/article/details/51107308
CMS和G1谁更需要内存
G1更占内存
G1 GC是基于Region的GC,适用于大内存机器。即使内存很大,Region扫描,性能还是很高的。
线程安全的集合
(1)以Concurrent开头的集合类: 如
ConcurrentHashMap、
ConcurrentLinkedQueue、
ConcurrentLinkedDeque、
ConcurrentSkipListMap
ConcurrentSkipListSet .
(2)以CopyOnWrite开头的集合类:如
CopyOnWriteArrayList、
CopyOnWriteArraySet .
redis如何保持数据一致性
将不一致分为三种情况:
1.数据库有数据,缓存没有数据;
2.数据库有数据,缓存也有数据,数据不相等;
3.数据库没有数据,缓存有数据。
- 首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据会写到缓存,并返回。
- 需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。
创建线程需要注意什么 线程数量和线程安全 最多可以创建多少个线程
线程安全,可能会造成线程死锁问题
线程数量:
一个Java进程可以启动的线程数可以通过如下公式计算:
(系统剩余内存 - 最大堆容量Xmx - 最大方法区容量MaxPermSize)/ 最大栈空间Xss
这样,4G的服务器单个进程可以开多少线程,可以粗略计算出来,大概是5000个线程。