MySQL锁机制
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,从而实现了乐观锁机制。