mysql鎖機制分爲表級鎖和行級鎖,此外根據分頁查詢,還可以擴展出頁級鎖。
顧名思義,表級鎖可以理解爲鎖住整個表,可以同時讀,但是不能同時寫,也就是說,直接鎖定整張表,在你鎖定期間,其它進程無法對該表進行寫操作。如果你是寫鎖,則其它進程則讀也不允許。如:
select * from emp for update;
行級鎖是僅對指定的記錄進行加鎖,這樣其它進程還是可以對同一個表中的其它記錄進行操作。如:
select * from emp where id=1 for update ;
頁級鎖是一次鎖定相鄰的一組記錄,比表級鎖速度快,但衝突多,比行級衝突少,但速度慢。如:
select * from emp limit 0,5 for update ;
在行級鎖中有共享鎖與排他鎖。
共享鎖(Share Locks),簡稱S鎖,又稱爲讀鎖,它是多個事務對於同一數據可以共享的一把鎖,多個事務都能訪問到數據,但是隻能讀不能修改。普通查詢沒有任何鎖機制。
加共享鎖可以使用select ... lock in share mode語句
排他鎖(Exclusive Locks),簡稱X鎖,又稱爲寫鎖,該鎖能與其他鎖並存,如果一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對數據就行讀取和修改的。
加排他鎖可以使用select ...for update語句
無論採用共享鎖,還是排他鎖,都要開啓事務。
共享鎖和排他鎖均隸屬於悲觀鎖,一個線程在使用的時候,其他線程只能排隊等待,因而速度極慢。
在實際使用中,樂觀鎖一般是我們用得比較多的鎖,這是因爲樂觀鎖的效率較高。例如,版本管理工具SVN和git就採用了樂觀鎖機制,通常是對版本version 進行管理。
採用樂觀鎖機制管理版本是這樣判斷的:
我們拿到原始版本後,立即在自己手頭的版本是自增1,將來提交的時候,如果手頭這個版本比原始版本大1,就是可以正常提交的,否則就有問題。
接下來,我通過hibernate框架演示樂觀鎖操作。
創建一個工程案例,將hibernate依賴的jar包導入工程:
在工程的src目錄下創建hibernate.cfg.xml配置文件:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!--hibernate配置-->
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/b0307</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">2018</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<mapping resource="com/itszt/domain/Emp.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>
表emp的結構如下:
測試用的實體類文件Emp.java:
package com.itszt.domain;
/**
* 實體類
*/
public class Emp {
private Integer uid,version;
private String name;
public Emp() {
}
@Override
public String toString() {
return "Emp{" +
"uid=" + uid +
", version=" + version +
", name='" + name + '\'' +
'}';
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Emp(Integer uid, Integer version, String name) {
this.uid = uid;
this.version = version;
this.name = name;
}
}
類Emp與表emp之間的映射配置文件Emp.hbm.xml爲:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itszt.domain">
<class name="Emp" table="emp">
<id name="uid" column="uid">
<generator class="native"></generator>
</id>
<version name="version" column="version"></version><!--此處加version是配置樂觀鎖的關鍵,與Emp中的屬性version對應-->
<property name="name" column="name"></property>
</class>
</hibernate-mapping>
hibernate工具類文件UtilHibernate.java,該工具類運行時加載hibernate配置文件,產生操作數據庫的session連接對象:
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* hibernate工具類
*/
public class UtilHibernate {
private static Configuration configuration;
private static SessionFactory sessionFactory;
//session是要重複調用的
//配置文件是要初始加載一次的
static {
try {
configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
//JVM的關閉鉤子,在JVM退出時close我們的sessionFactory
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
super.run();
System.out.println("Execute Hook.....");
if (sessionFactory != null) {
sessionFactory.close();
}
}
});
} catch (HibernateException e) {
throw new ExceptionInInitializerError("hibernate加載失敗!!");
}
}
//開啓一個新的session
public static Session openNew() {
return sessionFactory.openSession();
}
//獲取當前的session對象
public static Session getCurrent() {
return sessionFactory.getCurrentSession();
}
//釋放session資源
public static void release(Session session) {
if (session != null) {
if (session.isOpen()) {
session.close();
}
}
}
}
測試類文件TestLock.java內容:
import com.itszt.domain.Emp;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
/**
* 測試樂觀鎖
*/
public class TestLock {
public static void main(String[] args) {
Session session = UtilHibernate.getCurrent();
Transaction transaction = session.beginTransaction();
//1.查詢到一個已有數據
Query query = session.createQuery("FROM Emp WHERE uid=?");
query.setParameter(0, 6);
Emp emp = (Emp) query.uniqueResult();
System.out.println("emp1 = " + emp);
//2.對已有的數據進行修改,version會自增1
emp.setName(emp.getName() + "_A");
System.out.println("emp2 = " + emp);
transaction.commit();
UtilHibernate.release(session);
System.exit(0);
}
}
當我們執行該測試類文件時,會發現每執行一次,表emp中的version字段就會自增1,從而實現了樂觀鎖機制。