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多頁,文章都會在裏面更新,整理的資料也會放在裏面。喜歡文章記得關注我點個贊喲,感謝支持!