數據庫連接池、事務


連接池簡介

1、連接池是創建和管理一個連接的緩沖池的技術,這些連接准備好被任何需要它們的線程使用。
  作用:避免頻繁地創建與消毀,給服務器減壓力。
2、數據庫的弊端:
  1.當用戶群體少服務器可以支撐,如果群體比較大萬級別服務器直接死機。數據庫默認的並發訪問50.
  2.每一個用完數據庫之后直接關閉,不能重復利用太浪費資源。
3、設計連接池:
  1.在池子中創建出多個連接供使用。
  2.當用戶需要操作數據庫時直接從池子中獲取連接即可。
  3.當用戶使用完畢之后把連接歸還給連接池,可以達到重復使用。
  4.可以設定池子的最大容器。比如50個連接,當第51個人訪問的時候,需要等待。
  5.其它用戶釋放資源的時候,可以使用。

4、手動實現連接池

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class JDBCTool {
    private static Connection conn;
    private static String driverName;
    private static String username;
    private static String url;
    private static String password;
    static {
        InputStream is = JDBCTool.class.getClassLoader().getResourceAsStream("qq.properties");
        Properties prop = new Properties();
        try {
            prop.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        driverName = (String)prop.get("driverClassName");
        username = (String)prop.get("username");
        url = (String)prop.get("url");
        password = (String)prop.get("password");
        
        try {
            Class.forName(driverName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    public static Connection getConn() {
        try {
            conn = DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    
    public static void close(Connection conn,PreparedStatement ps,ResultSet rs) {
        if(conn!=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(ps!=null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
    }
}
手動實現連接池第一步
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

public class MyJDBCPool {
    /* 1.定義一個容器  集合
     2.初始化連接,並放在池子中。
     3.創建一個從容器中獲取連接的方法
     4.創建一個歸還連接的方法。*/
    private static List<Connection> pool = new ArrayList<>();
    private static int List_size = 3;
    static {
        for(int i=0;i<List_size;i++) {
            Connection conn=null;
            conn = JDBCTool.getConn();
            pool.add(conn);
            System.out.println("當前存放連接為:"+conn+",當前池子中剩余連接數為:"+pool.size());
            
        }
        System.out.println("=====================");
    }
    
    //取出一個連接 要判斷是否為空
    public static Connection getConn() throws NullPointerException{
        if(pool.isEmpty()) {
            System.out.println("請等待");
            throw new NullPointerException("已經空了,別拿了");
        }
        Connection conn = pool.remove(0);
        System.out.println("當前取出的連接為:"+conn+",當前剩余連接數為:"+pool.size());
        return conn;
    }
    
    //歸還連接 要判斷是否為真正的連接
    public static void returnConn(Connection conn) {
        if(conn==null) {
            System.out.println("你玩我?");
            return;
        }
        pool.add(conn);
        System.out.println("當前存入的連接為:"+conn+",當前剩余連接數為:"+pool.size());
    }
}
手動實現連接池第二步
import java.sql.Connection;

public class Test1 {

    public static void main(String[] args) {
        Connection conn = MyJDBCPool.getConn();
        Connection conn1 = MyJDBCPool.getConn();
        Connection conn2 = MyJDBCPool.getConn();
        
        MyJDBCPool.returnConn(conn2);
        MyJDBCPool.returnConn(conn1);
        MyJDBCPool.returnConn(conn);
        
    }

}
手動連接池的測試

  注:這里涉及到一個properties文件,文件內容是key =value形式存在的,先使用類加載器(或輸入流)將文件加載進來,然后使用properties對象處理文件,使用get()方法獲取內容。

常用連接池

1、導入連接池的步驟

  導包 buildpath

  配置文件 properties

  加載文件流,使用properties處理文件

  使用連接池的API讀取prop對象,創建連接池

  getConnection獲取連接

  返回連接的引用 

2、dbcp連接池 開源連接池,效率高,但安全性不強

  工具包下載地址:http://commons.apache.org/proper/commons-pool/download_pool.cgi

    http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi

  導包 commons-dbcp  和commons-pool  mysql-connection

  配置文件導入*.properties  建議放在src根目錄

import java.sql.Connection;

import org.apache.commons.dbcp.BasicDataSource;

public class Test1{
    
    public static void main(String[] args) throws Exception{
        
        BasicDataSource bds = new BasicDataSource();
        
        // 4個必須設置的屬性
        bds.setDriverClassName("com.mysql.jdbc.Driver");
        bds.setUrl("jdbc:mysql:///db0619");
        bds.setUsername("root");
        bds.setPassword("root");
        
        
        //其他屬性,都是可選屬性
        bds.setInitialSize(100);  // 初始創建100個連接
        bds.setMaxActive(50);  // 最大活動數
        bds.setMaxIdle(20);    // 最大空閑數
        bds.setMinIdle(10);    // 最小空閑數
        bds.setMaxWait(-1);  // 最大等待時間
        
        
        Connection conn = bds.getConnection();
        System.out.println(conn);
        Connection conn2 = bds.getConnection();
        System.out.println(conn2);
        
        
    }

}
手動創建DBCP連接池
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtil {
    
    private static DataSource ds;
    
    static{
        
        try {
             Properties p = new Properties();
             InputStream is = new FileInputStream("src/db.properties");
             p.load(is);
             ds = BasicDataSourceFactory.createDataSource(p);
        } catch (Exception e) {
            
            e.printStackTrace();
        }
        
    }
    
    
    public static DataSource getDataSource(){
        
        return ds;
    }
    
    
    public static Connection getConnection(){
        Connection conn = null;
        try {
            conn = ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    

}
使用配置文件自動創建DBCP連接池,工具類

3、c3p0連接池

  工具包:https://sourceforge.net/projects/c3p0/

  導包 c3p0-0.9.5.2.jar  mchange-commons-java...  mysql-connection...

  配置文件名稱:c3p0-config.xml   c3p0.properties

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 默認配置,如果沒有指定則使用這個配置 -->
    <default-config>
        <!-- 四個基本配置 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///db0619</property>
        <property name="user">root</property>
        <property name="password">root</property>
        
        
        <!-- 當連接池用完時客戶端調用getConnection()后等待獲取新連接的時間,超時后將拋出
              SQLException,如設為0則無限期等待。單位毫秒。Default: 0 -->
        <property name="checkoutTimeout">30000</property>
        
        <!--隔多少秒檢查連接池的空閑連接,0表示不檢查-->
        <property name="idleConnectionTestPeriod">30</property>
        
        <!-- 初始化連接數 -->
        <property name="initialPoolSize">10</property>
        
        <!-- 連接的最大空閑時間,默認為0秒、不會關閉任何連接。設置30秒,30秒到期后,
            連接若未使用就會被關閉 -->
        <property name="maxIdleTime">30</property>
        
        <!-- 池中最多的連接存放數目 -->
        <property name="maxPoolSize">100</property>
        
        <!-- 池中最少的連接存放數目 -->
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </default-config>
    
    
    <named-config name="offcn">
        <!-- 四個基本配置 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///test</property>
        <property name="user">root</property>
        <property name="password">root</property>
        
        
        <!-- 當連接池用完時客戶端調用getConnection()后等待獲取新連接的時間,超時后將拋出
              SQLException,如設為0則無限期等待。單位毫秒。Default: 0 -->
        <property name="checkoutTimeout">30000</property>
        
        <!--隔多少秒檢查連接池的空閑連接,0表示不檢查-->
        <property name="idleConnectionTestPeriod">30</property>
        
        <!-- 初始化連接數 -->
        <property name="initialPoolSize">10</property>
        
        <!-- 連接的最大空閑時間,默認為0秒、不會關閉任何連接。設置30秒,30秒到期后,
            連接若未使用就會被關閉 -->
        <property name="maxIdleTime">30</property>
        
        <!-- 池中最多的連接存放數目 -->
        <property name="maxPoolSize">100</property>
        
        <!-- 池中最少的連接存放數目 -->
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </named-config>


</c3p0-config>
c3p0 xml配置文件
import java.sql.Connection;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class Test1{
    
    public static void main(String[] args) throws Exception{
        
        ComboPooledDataSource cds = new ComboPooledDataSource();
        
        //4 個基本設置
        cds.setDriverClass("com.mysql.jdbc.Driver");
        cds.setJdbcUrl("jdbc:mysql:///db0619");
        cds.setUser("root");
        cds.setPassword("root");
        
        
        Connection conn = cds.getConnection();
        System.out.println(conn);
    }

}
手動設置c3p0連接池
import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
    
    private static DataSource ds;
    
    static{
        //ds = new ComboPooledDataSource();//加載src/c3p0-config.xml,並使用配置文件中默認的配置配置<default-config>
        ds = new ComboPooledDataSource("offcn"); //加載src/c3p0-config.xml,並使用名字為offcn的配置配置
    }
    
    public static DataSource getDataSource(){
        
        return ds;
    }
    
    
    public static Connection getConnection(){
        
        Connection conn = null;
        
        try {
            conn= ds.getConnection();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return conn;
    }

}
使用xml文件自動創建c3p0,工具類

4、druid連接池

  工具包:

  導包:druid-1....jar   mysql-connection...

  配置文件名稱 druid.properties

import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

public class DruidUtils {
    private static DataSource ds;
    private static Connection conn;
    static{
        try {
            InputStream is = MyJDBC.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties prop = new Properties();
            prop.load(is);
            ds = DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() {
        try {
            conn = ds.getConnection();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return conn;
    }
    public static DataSource getDataSource() {
        return ds;
    }
}
使用配置文件自動創建Druid連接池,工具類

DBUTils工具類

1、DBUtils是java編程中的數據庫操作實用工具,小巧簡單實用。

2、核心類QueryRunner

3、數據更新操作使用update(String sql,Object[] params...); 若無指定連接,也可以在參數一前傳Connection

  參數一:sql語句 ,參數二 Object數組,對sql語句中的占位符賦值  返回值為int  代表更新了幾行數據

4、數據查詢操作  query(String sql,handler,Object[] params...); 若無指定連接,也可以在參數一前傳Connection

  參數一:sql語句 ,

  參數二 :如何處理返回的二維表格Object[],List<Object[]>,map, list<map>,javaBean,List<JavaBean>,Object…

  參數三:對sql語句中的占位符賦值 

5、ResultSetHandler中常用的處理方式

BeanHandler

將結果集中第一條記錄封裝到一個指定的javaBean中。

BeanListHandler

將結果集中每一條記錄封裝到指定的javaBean中,將這些javaBean在封裝到List集合中

MapHandler

將結果集中第一條記錄封裝到了Map<String,Object>集合中,key就是字段名稱,value就是字段值

 

MapListHandler

將結果集中每一條記錄封裝到了Map<String,Object>集合中,key就是字段名稱,value就是字段值,在將這些Map封裝到List集合中。

ScalarHandler

它是用於單數據。例如select count(*) from 表操作。

 

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import com.bean.Users;
import com.utils.MyJDBC;

public class UsersdaoImpl implements Usersdao {
    
    @Override
    public int insertUser(Users user) {
        QueryRunner qr = new QueryRunner(MyJDBC.getDataSource());
        String sql="insert into users values(?,?,?)";
        int result=0;
        try {
            result = qr.update(sql,new Object[] {user.getId(),user.getUsername(),user.getPwd()});        
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public int deleteUserById(Integer id) {
        QueryRunner qr = new QueryRunner();
        Connection conn = MyJDBC.getConnection();
        String sql = "delete from users where id=?";
        int result = 0;
        try {
            result = qr.update(conn, sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    @Override
    public int updateUser(Users user) {
        QueryRunner qr = new QueryRunner();
        Connection conn = MyJDBC.getConnection();
        String sql = "update users set username=?,pwd=? where id=?";
        int result=0;
        try {
            result = qr.update(conn, sql,new Object[] {user.getUsername(),user.getPwd(),user.getId()});
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    @Override
    public List<Users> findAllStudent() {
        QueryRunner qr = new QueryRunner();
        Connection conn = MyJDBC.getConnection();
        String sql = "select * from users";
        List<Users> list=null;
        try {
            list = qr.query(conn,sql, new BeanListHandler<>(Users.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }
    
    @Override
    public Users findUserById(Integer id) {
        QueryRunner qr = new QueryRunner();
        Connection conn = MyJDBC.getConnection();
        String sql = "select * from users where id=?";
        Users user=null;
        try {
            user = qr.query(conn,sql,new BeanHandler<>(Users.class),id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return user;
    }
    
    @Override
    public List<Users> findUserByName(String name) {
        QueryRunner qr = new QueryRunner(MyJDBC.getDataSource());
        String sql = "select * from users where username like ?";
        List<Users> list = null;
        try {
            list = qr.query(sql,new BeanListHandler<Users>(Users.class), "%"+name+"%");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }
    
    @Override
    public Map<String, Object> findFirstRow() {
        QueryRunner qr = new QueryRunner(MyJDBC.getDataSource());
        String sql = "select * from users";
        Map<String,Object> map = null;
        try {
            map=qr.query(sql, new MapHandler());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return map;
    }
    
    @Override
    public List<Map<String, Object>> findAllStudent2() {
        QueryRunner qr = new QueryRunner(MyJDBC.getDataSource());
        String sql = "select * from users";
        List<Map<String, Object>> lm=null;
        try {
            lm = qr.query(sql, new MapListHandler());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return lm;
    }
    
    @Override
    public long findUserCount() {
        QueryRunner qr = new QueryRunner(MyJDBC.getDataSource());
        String sql = "select count(username) from users where username='HH'";
        long l=0;
        try {
            l =(long)qr.query(sql,new ScalarHandler());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return l;
    }
    
    @Override
    public Object[] findStudent() {
        QueryRunner qr = new QueryRunner(MyJDBC.getDataSource());
        String sql = "select * from users";
        Object[] s=null;
        try {
            s = qr.query(sql, new ArrayHandler());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return s;
    }
}
UsersdaoImpl
import java.util.List;
import java.util.Map;

import org.junit.Test;

import com.bean.Users;
import com.dao.Usersdao;
import com.dao.UsersdaoImpl;

public class Test {
    Usersdao us = new UsersdaoImpl();
    @Test
    public void test1() {
        Users user1 = new Users();
        user1.setId(null);
        user1.setUsername("小紅");
        user1.setPwd("112233");
        int result = us.insertUser(user1);
        if(result>0) {
            System.out.println("添加成功");
        }else {
            System.out.println("添加失敗");
        }
    }
    
    @Test
    public void test2() {
        int result = us.deleteUserById(7);
        if(result>0) {
            System.out.println("刪除成功");
        }else {
            System.out.println("刪除失敗");
        }
    }
    
    @Test
    public void test3() {
        Users user = new Users();
        user.setId(10);
        user.setUsername("HH");
        user.setPwd("123456");
        int result = us.updateUser(user);
        if(result>0) {
            System.out.println("修改成功");
        }else {
            System.out.println("修改失敗");
        }
    }
    
    @Test
    public void test4() {
        List<Users> list = us.findAllStudent();
        for(Users user:list) {
            System.out.println(user.getId()+"\t"+user.getUsername()+"\t"+user.getPwd());
        }
    }
    
    @Test
    public void test5() {
        Users user = us.findUserById(3);
        System.out.println(user.getId()+"\t"+user.getUsername()+"\t"+user.getPwd());
    }
    
    @Test
    public void test6() {
        List<Users> list = us.findUserByName("劉");
        for(Users user:list) {
            System.out.println(user.getId()+"\t"+user.getUsername()+"\t"+user.getPwd());
        }
    }
    
    @Test
    public void test7() {
        Map<String, Object> map = us.findFirstRow();//返回查詢的第一條數據  key為字段 value為字段值
        System.out.println(map);
    }
    
    @Test
    public void test8() {
        List<Map<String, Object>> lm = us.findAllStudent2();
        for(Map<String,Object> map:lm) {
            System.out.println(map);
        }
    }
    
    @Test
    public void test9() {
        long l = us.findUserCount();
        System.out.println(l);
    }
    
    @Test
    public void test10() {
        Object[] obj = us.findStudent();
        System.out.println(obj);
    }
}
Test
import java.util.List;
import java.util.Map;

import com.bean.Users;

public interface Usersdao {
    public int insertUser(Users user);
    public int deleteUserById(Integer id);
    public int updateUser(Users user);
    public List<Users> findAllStudent();
    public Users findUserById(Integer id);
    public List<Users> findUserByName(String name);
    public Map<String,Object> findFirstRow();
    public List<Map<String,Object>> findAllStudent2();
    public long findUserCount();
    public Object[] findStudent();
}
Usersdao

 

事務

1、什么是事務?

  事務: 一組SQL操作,要么同時成功,要么同時失敗。

2、在實際的業務開發中,有些業務操作要多次訪問數據庫。一個業務要發送多條SQL語句給數據庫執行。需要將多次   訪問數據庫的操作視為一個整體來執行,要么所有的SQL語句全部執行成功。如果其中有一條SQL語句失敗,就進行事務的回滾,所有的SQL語句全部執行失敗。 例如: jacktom轉賬,jack賬號減錢,tom賬號加錢。

3、MYSQL中可以有兩種方式進行事務的操作:

  自動提交事務:默認

  手動提交事務:Start transaction;      //開啟事務

      Sql1

      Sql2

      Sql3

      ...

      Sqln

      Commit;

4、回滾點 savepoint

  在某些成功的操作完成之后,后續的操作有可能成功有可能失敗,但是不管成功還是失敗,前面操作都已經成功,   可以在當前成功的位置設置一個回滾點。可以供后續失敗操作返回到該位置,而不是返回所有操作,這個點稱之為  回滾點。

  Start transaction

   Sql1

   Sql2;

   Savepoint ‘aaa’;

   Sql3

   ...

   Sqln

   Rollback to ‘aaa’

  Commit;

5、java中操作事務的方式

  try{

  conn.setAutoCommit(false);   //默認為true 自動提交事務  false設置為手動提交

  操作1,2,3,。。。    //注意,這些操作需是同一個conn連接去執行

  conn.commit()  //提交事務

  }catch(SQLException e){

    conn.rollback();    //回滾

    e.printStackTrace();

  }

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.utils.C3P0Util;

public class MyTransaction {
    
    
    public static void main(String[] args) {
        
        Connection conn = C3P0Util.getConnection();
        PreparedStatement ps = null;
        try {
            conn.setAutoCommit(false);  // 默認值時true --- 設置為手動提交事務
            
            String sql = "update account set money=money+1000 where name='Tom'";
            String sql2 = "update account set money=money+1000 where name='Tom'";
            
            ps = conn.prepareStatement(sql);
            ps.executeUpdate();
            
            ps = conn.prepareStatement(sql2);
            ps.executeUpdate();
            
            conn.commit(); //提交事務
            
            
        } catch (SQLException e) {
            
            try {
                conn.rollback();  // 回滾
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }  
        
        
    }

}
java中事務的操作方式

6、事務的四大特性

事務特性

含義

原子性

(Atomicity)

事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。

一致性

(Consistency)

事務前后數據的完整性必須保持一致

隔離性

(Isolation)

是指多個用戶並發訪問數據庫時,一個用戶的事務不能被其它用戶的事務所干擾,多個 並發事務之間數據要相互隔離,不能相互影響。

持久性

(Durability)

指一個事務一旦被提交(commit),它對數據庫中數據的改變就是永久性的,接下來即使數據庫發 生故障也不應該對其有任何影響

7、幾類事務的問題

  • 第1類丟失更新:事務A撤銷時,把已經提交的事務B的更新數據覆蓋了。 

  第2類丟失更新:事務A提交時覆蓋事務B已經提交的數據,造成事務B所做的操作丟失。

  解決方法:對行加鎖,只允許並發一個更新事務。

  • 臟讀(Dirty Read):A事務讀取B事務尚未提交的數據並在此基礎上操作,而B事務執行回滾,那么A讀取到的數據就是臟數據。

  解決方法:如果在第一個事務提交前,任何其他事務不可讀取其修改過的值,則可以避免該問題。

  • 不可重復讀(Non-repeatable Reads) 
    一個事務對同一行數據重復讀取兩次,但是卻得到了不同的結果。事務T1讀取某一數據后,事務T2對其做了修改,當事務T1再次讀該數據時得到與前一次不同的值。 

  解決辦法:如果只有在修改事務完全提交之后才可以讀取數據,則可以避免該問題。

  • 幻象讀:指兩次執行同一條 select 語句會出現不同的結果,第二次讀會增加一數據行,並沒有說這兩次執行是在同一個事務中。

  解決辦法:如果在操作事務完成數據處理之前,任何其他事務都不可以添加新數據,則可避免該問題。

8、數據庫事務的隔離級別有4個,由低到高依次為Read uncommitted(未授權讀取、讀未提交)、Read committed(授權讀取、讀提交)、Repeatable read(可重復讀取)、Serializable(序列化),這四個級別可以逐個解決臟讀、不可重復讀、幻象讀這幾類問題。

  • Read uncommitted(未授權讀取、讀未提交): 
    如果一個事務已經開始寫數據,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。這樣就避免了更新丟失,卻可能出現臟讀。也就是說事務B讀取到了事務A未提交的數據。
  • Read committed(授權讀取、讀提交): 
    讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。該隔離級別避免了臟讀,但是卻可能出現不可重復讀。事務A事先讀取了數據,事務B緊接了更新了數據,並提交了事務,而事務A再次讀取該數據時,數據已經發生了改變。
  • Repeatable read(可重復讀取): 
    可重復讀是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,即使第二個事務對數據進行修改,第一個事務兩次讀到的的數據是一樣的。這樣就發生了在一個事務內兩次讀到的數據是一樣的,因此稱為是可重復讀。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。這樣避免了不可重復讀取和臟讀,但是有時可能出現幻象讀。(讀取數據的事務)這可以通過“共享讀鎖”和“排他寫鎖”實現。
  • Serializable(序列化): 
    提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,但不能並發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。序列化是最高的事務隔離級別,同時代價也花費最高,性能很低,一般很少使用,在該級別下,事務順序執行,不僅可以避免臟讀、不可重復讀,還避免了幻像讀。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM