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

posted @ 2018-04-17 19:46  奔跑在梦想的道路上  阅读(138)  评论(0编辑  收藏  举报