1.1. 原子性(Atomicity)前端
1.2. 一致性(Consistency)java
1.3. 隔离性(Isolation)面试
一、脏读:事务A读取了事务B更新的数据,而后B回滚操做,那么A读取到的数据是脏数据sql
二、不可重复读:事务A屡次读取同一数据,事务B在事务A屡次读取的过程当中,对数据做了更新并提交,致使事务A屡次读取同一数据时,结果所以本事务前后两次读到的数据结果会不一致。数据库
三、幻读:幻读解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。编程
事务隔离级别缓存 |
脏读安全 |
不可重复读 |
幻读 |
读未提交 read-uncommitted |
是 |
是 |
是 |
不可重复读 read-committed |
否 |
是 |
是 |
可重复读 repeatable-read |
否 |
否 |
是 |
串行化 serializable |
否 |
否 |
否 |
MySQL默认的事务隔离级别为repeatable-read
MySQL支持4中事务隔离级别
Oracle支持的2种事务隔离级别:READ_COMMITED , SERIALIZABLE
1. SQL规范所规定的标准,不一样的数据库具体的实现可能会有些差别
2. MySQL中默认事务隔离级别是“可重复读”时并不会锁住读取到的行
MySQL 有多种存储引擎,每种存储引擎有各自的优缺点,能够择优选择使用:MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、FEDERATED、ARCHIVE、CSV、BLACKHOLE
。
虽然 MySQL 里的存储引擎不仅是 MyISAM 与 InnoDB 这两个,但经常使用的就是两个。
两种存储引擎的大体区别表如今:
select count(*) from table
时,InnoDB 须要扫描一遍整个表来计算有多少行,可是 MyISAM 只要简单的读出保存好的行数便可。注意的是,当 count(*)语句包含 where 条件时 MyISAM 也须要扫描整个表。DELETE FROM table
时,InnoDB 不会从新创建表,而是一行一行的 删除,效率很是慢。MyISAM 则会重建表。update table set a=1 where user like '%lee%'
。有人说 MyISAM 只能用于小型应用,其实这只是一种偏见。若是数据量比较大,这是须要经过升级架构来解决,好比分表分库,而不是单纯地依赖存储引擎。
如今通常都是选用 innodb 了,主要是 MyISAM 的全表锁,读写串行问题,并发效率锁表,效率低,MyISAM 对于读写密集型应用通常是不会去选用的。
事务处理上方面
锁级别
那为何你们不都用 Hash 索引而还要使用 B+树索引呢?
MySQL 中,只有 HEAP/MEMORY 引擎才显示支持 Hash 索引。
经常使用的 InnoDB 引擎中默认使用的是 B+树索引,它会实时监控表上索引的使用状况,若是认为创建哈希索引能够提升查询效率,则自动在内存中的“自适应哈希索引缓冲区”创建哈希索引(在 InnoDB 中默认开启自适应哈希索引),经过观察搜索模式,MySQL 会利用 index key 的前缀创建哈希索引,若是一个表几乎大部分都在缓冲池中,那么创建一个哈希索引可以加快等值查询。
若是是等值查询,那么哈希索引明显有绝对优点,由于只须要通过一次算法便可找到相应的键值;固然了,这个前提是,键值都是惟一的。若是键值不是惟一的,就须要先找到该键所在位置,而后再根据链表日后扫描,直到找到相应的数据;
若是是范围查询检索,这时候哈希索引就毫无用武之地了,由于原先是有序的键值,通过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
同理,哈希索引没办法利用索引完成排序,以及 like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
哈希索引也不支持多列联合索引的最左匹配规则;
B+树索引的关键字检索效率比较平均,不像 B 树那样波动幅度大,在有大量重复键值状况下,哈希索引的效率也是极低的,由于存在所谓的哈希碰撞问题。
在大多数场景下,都会有范围查询、排序、分组等查询特征,用 B+树索引就能够了。
<,<=,=,>,>=,between,in
悲观锁的特色是先获取锁,再进行业务操做,即“悲观”的认为获取锁是很是有可能失败的,所以要先确保获取锁成功再进行业务操做。一般所说的“一锁二查三更新”即指的是使用悲观锁。一般来说在数据库上的悲观锁须要数据库自己提供支持,即经过经常使用的 select … for update 操做来实现悲观锁。当数据库执行 select for update 时会获取被 select 中的数据行的行锁,所以其余并发执行的 select for update 若是试图选中同一行则会发生排斥(须要等待行锁被释放),所以达到锁的效果。select for update 获取的行锁会在当前事务结束时自动释放,所以必须在事务中使用。
这里须要注意的一点是不一样的数据库对 select for update 的实现和支持都是有所区别的,例如 oracle 支持 select for update no wait,表示若是拿不到锁马上报错,而不是等待,MySQL 就没有 no wait 这个选项。另外MySQL 还有个问题是 select for update 语句执行中全部扫描过的行都会被锁上,这一点很容易形成问题。所以若是在 MySQL 中用悲观锁务必要肯定走了索引,而不是全表扫描。
乐观锁,也叫乐观并发控制,它假设多用户并发的事务在处理时不会彼此互相影响,各事务可以在不产生锁的状况下处理各自影响的那部分数据。在提交数据更新以前,每一个事务会先检查在该事务读取数据后,有没有其余事务又修改了该数据。若是其余事务有更新的话,那么当前正在提交的事务会进行回滚。
乐观锁的特色先进行业务操做,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,所以在进行完业务操做须要实际更新数据的最后一步再去拿一下锁就好。
乐观锁在数据库上的实现彻底是逻辑的,不须要数据库提供特殊的支持。通常的作法是在须要锁的数据上增长一个版本号,或者时间戳,而后按照以下方式实现:
乐观锁(给表加一个版本号字段) 这个并非乐观锁的定义,给表加版本号,是数据库实现乐观锁的一种方式。
1\. SELECT data AS old_data, version AS old_version FROM …; 2\. 根据获取的数据进行业务操做,获得 new_data 和 new_version 3\. UPDATE SET data = new_data, version = new_version WHERE version = old_version if (updated row > 0) { // 乐观锁获取成功,操做完成 } else { // 乐观锁获取失败,回滚并重试 } 复制代码
乐观锁在不发生取锁失败的状况下开销比悲观锁小,可是一旦发生失败回滚开销则比较大,所以适合用在取锁失败几率比较小的场景,能够提高系统并发性能
乐观锁还适用于一些比较特殊的场景,例如在业务操做过程当中没法和数据库保持链接等悲观锁没法适用的地方。
悲观锁和乐观锁是数据库用来保证数据并发安全防止更新丢失的两种方法,例子在select ... for update
前加个事务就能够防止更新丢失。悲观锁和乐观锁大部分场景下差别不大,一些独特场景下有一些差异,通常咱们能够从以下几个方面来判断。
响应速度:若是须要很是高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不须要等待其余并发去释放锁。
冲突频率:若是冲突频率很是高,建议采用悲观锁,保证成功率,若是冲突频率大,乐观锁会须要屡次重试才能成功,代价比较大。
重试代价:若是重试代价大,建议采用悲观锁。
同步复制
异步复制
半同步复制
问题 1:master 的写操做,slaves 被动的进行同样的操做,保持数据一致性,那么 slave 是否能够主动的进行写操做?
假设 slave 能够主动的进行写操做,slave 又没法通知 master,这样就致使了 master 和 slave 数据不一致了。所以slave 不该该进行写操做,至少是 slave 上涉及到复制的数据库不能够写。实际上,这里已经揭示了读写分离的概念。
问题 2:主从复制中,能够有 N 个 slave,但是这些 slave 又不能进行写操做,要他们干吗?
以实现数据备份。
相似于高可用的功能,一旦 master 挂了,可让 slave 顶上去,同时 slave 提高为 master。
异地容灾,好比 master 在北京,地震挂了,那么在上海的 slave 还能够继续。
主要用于实现 scale out,分担负载,能够将读的任务分散到 slaves 上。
【极可能的状况是,一个系统的读操做远远多于写操做,所以写操做发向 master,读操做发向 slaves 进行操做】
问题 3:主从复制中有 master,slave1,slave2,...等等这么多 MySQL 数据库,那好比一个 JAVA WEB 应用到底应该链接哪一个数据库?
当 然,咱们在应用程序中能够这样,insert/delete/update
这些更新数据库的操做,用connection(for master)
进行操做,select 用 connection(for slaves)
进行操做。那咱们的应用程序还要完成怎么从 slaves 选择一个来执行 select,例如使用简单的轮循算法。
这样的话,至关于应用程序完成了 SQL 语句的路由,并且与 MySQL 的主从复制架构很是关联,一旦 master 挂了,某些 slave 挂了,那么应用程序就要修改了。能不能让应用程序与 MySQL 的主从复制架构没有什么太多关系呢?
找一个组件,application program 只须要与它打交道,用它来完成 MySQL 的代理,实现 SQL 语句的路由。
MySQL proxy 并不负责,怎么从众多的 slaves 挑一个?能够交给另外一个组件(好比 haproxy)来完成。
这就是所谓的MySQL READ WRITE SPLITE,MySQL
的读写分离。
问题 4:若是 MySQL proxy , direct , master 他们中的某些挂了怎么办?
总统通常都会弄个副总统,以防不测。一样的,能够给这些关键的节点来个备份。
问题 5:当 master 的二进制日志每产生一个事件,都须要发往 slave,若是咱们有 N 个 slave,那是发 N 次,仍是只发一次?
若是只发一次,发给了 slave-1,那 slave-2,slave-3,...它们怎么办?
显 然,应该发 N 次。实际上,在 MySQL master 内部,维护 N 个线程,每个线程负责将二进制日志文件发往对应的 slave。master 既要负责写操做,还的维护 N 个线程,负担会很重。能够这样,slave-1 是 master 的从,slave-1 又是 slave-2,slave-3,...的主,同时 slave-1 再也不负责 select。 slave-1 将 master 的复制线程的负担,转移到本身的身上。这就是所谓的多级复制的概念。
问题 6:当一个 select 发往 MySQL proxy,可能此次由 slave-2 响应,下次由 slave-3 响应,这样的话,就没法利用查询缓存了。
应该找一个共享式的缓存,好比 memcache 来解决。将 slave-2,slave-3,...这些查询的结果都缓存至 mamcache 中。
问题 7:随着应用的日益增加,读操做不少,咱们能够扩展 slave,可是若是 master 知足不了写操做了,怎么办呢?
scale on ?更好的服务器? 没有最好的,只有更好的,太贵了。。。
scale out ? 主从复制架构已经知足不了。
能够分库【垂直拆分】,分表【水平拆分】。
MySQL 有三种锁的级别:页级、表级、行级。
死锁的关键在于:两个(或以上)的 Session 加锁的顺序不一致。
那么对应的解决死锁问题的关键就是:让不一样的 session加锁有次序。
SELECT trx_MySQL_thread_id FROM information_schema.INNODB_TRX;
复制代码
Innodb 行锁的等待时间,单位秒。可在会话级别设置,RDS 实例该参数的默认值为 50(秒)。
生产环境不推荐使用过大的 innodb_lock_wait_timeout
参数值
该参数支持在会话级别修改,方便应用在会话级别单独设置某些特殊操做的行锁等待超时时间,以下:
set innodb_lock_wait_timeout=1000; —设置当前会话 Innodb 行锁等待超时时间,单位秒。复制代码
MySQL 高并发环境解决方案 分库 分表 分布式 增长二级缓存。。。。。
需求分析:互联网单位 天天大量数据读取,写入,并发性高。
1.1.定义
1.1.1.IoC
1.1.2.AOP
Aspect Oriented Programming,面向切面编程。经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。其中,最经常使用的使用场景通常有日志模块、权限模块、事物模块。
1.2.原理
1.2.1.IoC
还有其余的类不一一列举出来,都在 java.lang.reflect 包下。说到这个模块的时候,那么面试官可能会考察相关的知识,主要是考察你是否真的有去了解过反射的使用。举两个例子:
这里其实就是里面的重要考察点就是反射对私有属性的处理。
/** * 经过反射获取私有的成员变量. */ private Object getPrivateValue(Person person, String fieldName) { try { Field field = person.getClass().getDeclaredField(fieldName); // 主要就是这里,须要将属性的 accessible 设置为 true field.setAccessible(true); return field.get(person); } catch(Exception e) { e.printStackTrace(); } return null; }复制代码
使用默认构造函数(无参)建立的话:
Class.newInstance() Constroctor constroctor = clazz.getConstructor(String.class,Integer.class); Object obj = constroctor.newInstance("name", 18); 复制代码
AOP 的内部原理其实就是动态代理和反射了。主要涉及到的反射类:
JDK 动态代理
使用 CGLIB 动态代理,被代理类不须要强制实现接口。CGLIB 不能对声明为 final的方法进行代理,由于 CGLIB 原理是动态生成被代理类的子类。
循环依赖就是 N 个类中循环嵌套引用,这样会致使内存溢出。循环依赖主要分两种:
Spring 初始化单例对象大致是分为以下三个步骤的:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); // isSingletonCurrentlyInCreation:判断当前单例 bean 是否正在建立中 if(singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized(this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); // allowEarlyReference:是否容许从 singletonFactories 中经过 getObject 拿到 对象 if(singletonObject == null && allowEarlyReference) { ObjectFactory <? > singletonFactory = this.singletonFactories.get(beanName); if(singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return(singletonObject != NULL_OBJECT ? singletonObject : null); } 复制代码
protected void addSingletonFactory(String beanName, ObjectFactory <? > singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized(this.singletonObjects) { if(!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }复制代码
可能的缘由:
<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>复制代码
下面是几种比较重要的注解类型:
ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。在同步机制中,经过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析何时对变量进行读写,何时须要锁定某个对象,何时释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而 ThreadLocal 则从另外一个角度来解决多线程的并发访问。 ThreadLocal 会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。由于每个线程都拥有本身的变量副本,从而也就没有必要对该变量进行同步了。 ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,能够把不安全的变量封装进 ThreadLocal。
因为 ThreadLocal 中能够持有任何类型的对象,低版本 JDK 所提供的 get()返回的是 Object 对象,须要强制类型转换。但 JDK 5.0 经过泛型很好的解决了这个问题,在必定程度地简化 ThreadLocal 的使用。归纳起来讲,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而 ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不一样的线程排队访问,然后者为每个线程都提供了一份变量,所以能够同时访问而互不影响。
欢迎你们关注个人公种浩【以Java架构赢天下】,整理了960道2019年多家公司java面试题400多页pdf文档,还有一份本身平时学习整理的Java学习笔记,共500多页,文章都会在里面更新,整理的资料也会放在里面。喜欢文章记得关注我点个赞哟,感谢支持!