解释一下什么叫AOP(面向切面编程)?
AOP是Aspect-Oriented Programming的缩写,是一种面向切面的编程方法。
在AOP中,一个切面是一组可以独立于其他代码执行的功能,如日志记录、安全性检查、事务处理等。这些功能通常被称为"通知",并且在AOP中被称为"切入点"。
AOP通过将这些切面"织入"到应用程序的正常流程中,使得可以将这些功能模块化,并在不修改主要业务逻辑的情况下将其应用到应用程序中。
这种方法可以让开发人员更专注于业务逻辑,而不必担心其他功能的实现细节。同时,AOP还可以帮助减少代码冗余,提高代码的可维护性和可扩展性。
什么是IoC和DI?DI是如何实现的?
IoC是Inversion of Control的缩写,是一种编程范式,它指的是控制流反转的概念。IoC的核心思想是对象之间的依赖关系是在运行时动态决定的,而不是在编译时静态确定的。
DI是Dependency Injection的缩写,是一种实现IoC的方法之一。DI通过将对象之间的依赖关系从使用者中解耦,使得对象可以独立于其依赖关系而存在。
DI通常用于将对象之间的依赖关系在运行时注入到对象中。这样就可以在运行时动态地更改对象之间的依赖关系,而不必在编译时就确定。
DI有多种实现方式,其中常见的有构造函数注入、Setter注入和接口注入。
构造函数注入是通过在构造函数中传入依赖对象的实例来实现DI的一种方式。
Setter注入是通过调用对象的Setter方法来注入依赖对象的实例来实现DI的一种方式。
接口注入是通过定义一个注入接口,然后在对象中实现这个接口来实现DI的一种方式。
通过使用DI,可以将对象之间的依赖关系从使用者中解耦,使得对象可以独立于其依赖关系而存在。这有助于提高代码的可维护性和可扩展性。此外,通过使用DI,可以在运行时动态地更改对象之间的依赖关系,使得系统更加灵活。
通常,DI是通过使用IoC容器来实现的。IoC容器是一个用于管理对象之间依赖关系的组件,它可以根据配置信息动态地创建对象并注入所需的依赖关系。
在Java中,Spring框架提供了一个功能强大的IoC容器,可以帮助开发人员快速地构建基于DI的应用程序。
SQL优化有哪些着手点?组合索引的最左前缀原则的含义?
首先讲第一个问题:
SQL优化,既然是优化,那么首先得要定位问题才能对症下药,开启慢查询日志监控,找出系统中比较慢的SQL。这就减少了筛查范围,然后逐条进行执行计划分析。没建索引的建索引,建了索引的看看索引是不是失效了,然后排查为什么索引失效?这些问题排查完了之后,可能因为表数据量过大,那就要考虑是不是要拆表,进行分表。
常用优化建议:
- 优化查询的选择、连接和排序操作。
- 优化查询中使用的索引,包括创建新索引、删除无用索引、调整索引的顺序等。
- 优化查询中使用的表连接方式,包括内连接、外连接、自连接等。
- 优化查询中使用的子查询,包括对子查询进行优化、使用连接代替子查询等。
- 优化查询中使用的聚合函数,包括使用索引进行优化、使用分组连接代替聚合函数等。
第二个问题:
组合索引的最左前缀原则指的是,在创建组合索引时,应该将最常用于筛选数据的列放在索引的最左侧,这样可以使索引更有效地帮助查询优化。
例如,如果有一张表中包含三个字段:A、B和C,并且频繁使用A和B这两个字段进行筛选数据,则应该将A和B作为组合索引的最左前缀,而不是C。
这样,在使用组合索引进行查询时,数据库系统就可以使用索引进行快速筛选,而不必扫描整张表。这有助于提高查询的效率。
最左前缀原则对于组合索引的创建非常重要,因为它可以帮助数据库系统更有效地使用索引。如果不遵循最左前缀原则,则组合索引可能会变得无用,甚至阻碍查询的优化。
数据库的隔离级别、MVCC
事务的四大特性(ACID)
- 原子性(atomicity): 事务的最小工作单元,要么全成功,要么全失败。
- 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏。
- 隔离性(isolation): 不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。
- 持久性(durability): 事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。
数据概念(脏读,不可重复读,幻读)
- 脏读 脏数据所指的就是未提交的数据。也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并做进一步的处理,就会产生未提交的数据依赖关系,这种现象被称为脏读。
- 不可重复读 一个事务先后读取同一条记录,而事务在两次读取之间该数据被其它事务所修改,则两次读取的数据不同,我们称之为不可重复读。
- 幻读 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好像发生了幻觉一样。
脏读 | 脏读 | 不可重复读 | 幻读 |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
事务的隔离级别
读未提交(Read UnCommitted/RU)
又称为脏读,一个事务可以读取到另一个事务未提交的数据。这种隔离级别是最不安全的一种,因为未提交的事务是存在回滚的情况。
举个例子:我的工资是3000,这个月发工资,财务不小心按错了数字,还没有来得及提交事务,就在这个时候,我发现的工资变为了3500元,我以为涨工资了高兴的飞起 ,财务发现不对,回滚了事务,将工资改回了3000元再提交。
实际我这个月的工资还是3000,但是我看到的是3500。我看到的是财务还没提交事务时的数据。这就是脏读。
读已提交(Read Committed/RC)
又称为不可重复读,一个事务因为读取到另一个事务已提交的修改数据,导致在当前事务的不同时间读取同一条数据获取的结果不一致。
举个例子:某已婚男子欲购买一套新房准备迎接新生命的到来,购房款 330万 ,付款的时候查询自己账号余额 360万(第一次查询),就在这个时候,该已婚男子的爱人,购买了理财产品,价值50万,并提交事务;该已婚男使用POS机付房款的时候发现钱不够了(第二次查询需要等待爱人支出事务提交完成)
如果有事务对数据进行更新(UPDATE)操作时,读取数据事务要等待这个更新操作事务提交后才能读取数据,有效的避免脏读,但是一个事务进行了两次相同的查询而返回了不同的数据,这就是不可重复读。
可重复读(Repeatable Read/RR)
又称为幻读,一个事物读可以读取到其他事务提交的数据,但是在RR隔离级别下,当前读取此条数据只可读取一次,在当前事务中,不论读取多少次,数据仍然是第一次读取的值,不会因为在第一次读取之后,其他事务再修改提交此数据而产生改变。因此也成为幻读,因为读出来的数据并不一定就是最新的数据。
举个例子:某已婚男欲购买一套新房准备迎接新生命的到来,购房款 330万 ,付款的时候查询自己账号余额 360万(第一次查询),在这个时候,该已婚男子的爱人无法从该已婚男账户转出余额,接下来就可以正常付款了。
重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
串行化(Serializable)
所有的数据库的读或者写操作都为串行执行,当前隔离级别下只支持单个请求同时执行,所有的操作都需要队列执行。所以种隔离级别下所有的数据是最稳定的,但是性能也是最差的。数据库的锁实现就是这种隔离级别的更小粒度版本。
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
事务和MVCC原理
不同事务同时操作同一条数据产生的问题
LBCC 解决数据丢失
LBCC,基于锁的并发控制,Lock Based Concurrency Control。
使用锁的机制,在当前事务需要对数据修改时,将当前事务加上锁,同一个时间只允许一条事务修改当前数据,其他事务必须等待锁释放之后才可以操作。
MVCC 解决数据丢失
MVCC,多版本的并发控制,Multi-Version Concurrency Control。
使用版本来控制并发情况下的数据问题,在B事务开始修改账户且事务未提交时,当A事务需要读取账户余额时,此时会读取到B事务修改操作之前的账户余额的副本数据,但是如果A事务需要修改账户余额数据就必须要等待B事务提交事务。
MVCC使得数据库读不会对数据加锁,普通的SELECT请求不会加锁,提高了数据库的并发处理能力。 借助MVCC,数据库可以实现READ COMMITTED,REPEATABLE READ等隔离级别,用户可以查看当前数据的前一个或者前几个历史版本,保证了ACID中的I特性(隔离性)。