第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Hibernate 性能之隔離機(jī)制

1. 前言

事務(wù)有 4 大特性,其隔離性尤其重要,沒有良好的隔離性就相當(dāng)于你可以隨意出入鄰居家。不能保證數(shù)據(jù)的完整性。

每一種隔離機(jī)制都有自己使用的真實(shí)場(chǎng)景。

本節(jié)課探討一下 Hibernate 中是如何進(jìn)行隔離設(shè)置的。通過本節(jié)課程的學(xué)習(xí),你將了解到:

  • Hibernate 中如何設(shè)置隔離級(jí)別;
  • 悲觀鎖和樂觀鎖的比較。

2. Hibernate 中的隔離機(jī)制

如前面課程所述,隔離機(jī)制能保證事務(wù)之間的良好秩序,但是,太嚴(yán)格的隔離機(jī)制會(huì)讓事務(wù)之間產(chǎn)生時(shí)間上的等待或延遲,也就是說并發(fā)性弱。

太松散的隔離機(jī)制,雖然可以增加并發(fā)性,但可能會(huì)產(chǎn)生事務(wù)之間的數(shù)據(jù)臟讀等一系列不希望出現(xiàn)的事情。

有時(shí),純粹地依靠 JDBC 提供的 4 種隔離機(jī)制很難做到隔離的優(yōu)雅性,所以,一般采用 讀取已提交 或者 更低的事務(wù)隔離級(jí)別,再配合各種并發(fā)訪問控制策略來達(dá)到并發(fā)事務(wù)控制的目的。

Hibernate 中如何設(shè)置隔離機(jī)制?

這個(gè)問題很簡單,你要做的就是在 Hibernate 主配置文件中添加如下信息:

<property name="connection.isolation">2</property>

這里的 2 是什么意思?

是這樣的,Hibernate 使用 1 、2 、4 、8 這幾個(gè)數(shù)字分別代表 4 種隔離機(jī)制。

  • 8 - Serializable 串行化;
  • 4 - Repeatable Read 可重復(fù)讀;
  • 2 - Read Commited 可讀已提交;
  • 1 - Read Uncommited 可讀未提交。

使用數(shù)字有幾個(gè)好處,畢竟不用記那么一長串字符串,最主要的是,這幾個(gè)數(shù)字可以換算成 二進(jìn)制中的 0001、0010、0100、1000??梢灾苯油ㄟ^二進(jìn)制位運(yùn)算的方式進(jìn)行權(quán)限控制。

設(shè)置就是這么簡單,但是,這還不夠。

剛說過, 最好再配合并發(fā)控制策略。

那么, Hibernate 提供了怎樣的 策略,告訴你,有 2 種 “鎖” 機(jī)制:

  • 樂觀鎖;
  • 悲觀鎖。

你是喜歡先苦后甜還是先甜后苦了,我喜歡先苦后甜。好吧,先講解什么是悲觀鎖。

3. 悲觀鎖 Pessimistic Locking

悲觀地認(rèn)為并發(fā)的事務(wù)時(shí)時(shí)會(huì)發(fā)生,總是擔(dān)心隔離機(jī)制不能很好的保證事務(wù)之間的安全性。

  • 基本思想就是當(dāng)一個(gè)事務(wù)讀取某一條記錄后,就會(huì)把這條記錄鎖住,如果其它的事務(wù)要想更新,必須等以前的事務(wù)提交或者回滾解除鎖;
  • 悲觀鎖的實(shí)現(xiàn),一般依靠數(shù)據(jù)庫提供的鎖機(jī)制。

只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù)。

SQL 的 select 語句中有一個(gè) for 語法關(guān)鍵字:

SELECT * from  student where stuName='Hibernate' for UPDATE

其作用就是鎖定這條記錄,當(dāng)前事務(wù)沒有結(jié)束之前,其它的事務(wù)不能在這條記錄上進(jìn)行數(shù)據(jù)更新操作。

Hiberntae 實(shí)現(xiàn)悲觀鎖

前面我們使用過 Session 的 get()方法,大家還記得是怎么用的嗎?

stu = (Student) session.get(Student.class, new Integer(1));

其實(shí)這個(gè)方法還可以傳遞第三個(gè)參數(shù),好吧,先看一下方法的原型:

public Object get(Class clazz, Serializable id, LockOptions lockOptions);

LockOptions 類本質(zhì)是對(duì) LockMode 枚舉類型的高級(jí)封裝,提供了幾種鎖的使用:

  • 無鎖的機(jī)制,Transaction 結(jié)束時(shí),切換到此模式;

hibernate 內(nèi)部使用。

public static final LockOptions NONE = new LockOptions(LockMode.NONE);
  • 查詢的時(shí)候,Hibernate 自動(dòng)獲取鎖;

hibernate 內(nèi)部使用。

public static final LockOptions READ = new LockOptions(LockMode.READ);
  • 利用數(shù)據(jù)庫的 for update 子句加鎖(Select * from 表 for update),通過此選項(xiàng)實(shí)現(xiàn)悲觀鎖。
public static final LockOptions UPGRADE = new LockOptions(LockMode.UPGRADE);

悲觀鎖在實(shí)際生產(chǎn)環(huán)境中使用頻率并不高,限制了并發(fā)的發(fā)生率,降低了程序的響應(yīng)速度。

編寫一個(gè)簡單的測(cè)試實(shí)例:

  1. 第一個(gè)事務(wù),查詢加鎖,使用 Thread.sleep()模擬事務(wù)操作時(shí)長;

模擬時(shí)間不要太長,如果長時(shí)間不釋放鎖,其它等待事務(wù)會(huì)拋出等待超時(shí)異常。

stu = (Student) session.get(Student.class, new Integer(1), LockOptions.UPGRADE);
Thread.sleep(30000);
transaction.commit();
System.out.println("-----------第一個(gè)事務(wù)結(jié)束-----------");

執(zhí)行此實(shí)例,查看控制臺(tái)輸出信息,查詢語句上添加了 for update,在模擬時(shí)長內(nèi)事務(wù)沒有結(jié)束。

Hibernate: 
    select
        student0_.stuId as stuId1_1_0_,
        student0_.classRoomId as classRoo5_1_0_,
        student0_.stuName as stuName2_1_0_,
        student0_.stuPassword as stuPassw3_1_0_,
        student0_.stuSex as stuSex4_1_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=? for update
  1. 第二個(gè)事務(wù),進(jìn)行查詢、更新操作,此事務(wù)并不能馬上更新成功,只有等待第一個(gè)事務(wù)結(jié)束后才能成功。
 stu = (Student) session.get(Student.class, new Integer(1));
 System.out.println("-------------更新-------------");
 stu.setStuName("Hibernate 01");
 transaction.commit();
 System.out.println("--------------更新成功-----------");

悲觀鎖的實(shí)現(xiàn)很簡單,也很好理解,無非就是我用時(shí)你不能用的問題。

4. 樂觀鎖

樂觀是一種積極的解決問題的態(tài)度。

所謂樂觀鎖認(rèn)為系統(tǒng)中的事務(wù)并發(fā)更新不會(huì)很頻繁,即使沖突了也沒事,大不了重新再來一次。

  1. 基本思想:

每次提交一個(gè)事務(wù)更新時(shí),查看要修改的數(shù)據(jù)從上次讀取以后有沒有被其它事務(wù)修改過,如果修改過,那么更新就會(huì)失敗。

  1. 實(shí)現(xiàn)方案:

在實(shí)體中增加一個(gè)版本控制字段,每次事務(wù)更新后就將版本 (Version) 字段的值加 1。

Tips: 樂觀鎖本質(zhì)就是版本控制管理的實(shí)現(xiàn),記錄的每一次更新操作都會(huì)以版本遞增的方式進(jìn)行記錄。

一個(gè)事務(wù)在更新之前,先獲取記錄的當(dāng)前版本號(hào),更新時(shí),如果版本還是最新的則可以更新,否則說明有事務(wù)比你先更新,則需要放棄?;蛘咧匦虏樵兊阶钚掳姹拘畔⒑笤俑?。

所以,在樂觀鎖的實(shí)現(xiàn)中,沖突是常態(tài)。

  1. 實(shí)現(xiàn)過程:

在學(xué)生實(shí)體類中添加新屬性,用來記錄每次更新的版本號(hào)。

public class Student implements Serializable {
//省略…… 
@Version
private Long version;
//省略…… 
stu = (Student) session.get(Student.class, new Integer(1));
System.out.println("當(dāng)前版本號(hào):"+stu.getVersion);
//模擬延遲,如果在這個(gè)時(shí)間內(nèi)有其它事務(wù)進(jìn)行了更新操作,此事務(wù)的更新不會(huì)成功
Thread.sleep(30000);
stu.setStuName("Hibernate");
transaction.commit();

好了,悲觀也好,樂觀也好,只是一種解決問題的態(tài)度。對(duì)于這兩種態(tài)度,咱們要總結(jié)一下。

樂觀鎖:

優(yōu)勢(shì):性能好,并發(fā)性高。

缺點(diǎn):用戶體驗(yàn)不好,可能會(huì)出現(xiàn)高高興興去更新,卻告知已經(jīng)有人捷足先登了。

悲觀鎖:

優(yōu)勢(shì):鎖住記錄為我所用,沒修改完成之前,其他事務(wù)只能瞪眼瞧著,時(shí)間雖然延遲,至少心里有底。

缺點(diǎn):并發(fā)性不好,性能不高。

Hibernate 的其它性能優(yōu)化:

  1. 隨時(shí)使用 Session.clear()及時(shí)清除 Session 緩存區(qū)的內(nèi)容;

  2. 1+N 問題 ( 一條 SQL 語句能解決的問題用了很多條 SQL 語句來實(shí)現(xiàn)) ;

    • 使用 Criteria 查詢可以解決這個(gè)問題;

    • Lazy 加載:需要時(shí),使用 get() 方法發(fā)出 SQL 語句。

    • 使用類似于 from Student s left join s.classRoom c 的關(guān)聯(lián)查詢語句。

  3. 緩存使用:在對(duì)象更新、刪除、添加相對(duì)于查詢要少得多時(shí), 二級(jí)緩存的應(yīng)用將不怕 n+1 問題,因?yàn)榧词沟谝淮尾樵兒苈?,之后直接緩存命中也是很快的,剛好又利用?n+1。

5. 小結(jié)

性能優(yōu)化顯然是一個(gè)不輕松、但又絕對(duì)不能忽視的話題。本節(jié)課程和大家講解了在 Hibernate 是如何處理事務(wù)隔離的,在隔離機(jī)制的基礎(chǔ)上,結(jié)合樂觀鎖或悲觀鎖更好地解決這個(gè)問題。

希望我們以樂觀的態(tài)度看待我們的生活、學(xué)習(xí)以及未來!