事務對數據庫的性能有什麼影響?有無索引查找對數據庫性能影響多大?不跟你多bb,反手就是一波實例連招---MySql insert 2000萬條數據性能測試和select有無索引查找測試

1、插入測試

在此次測試中數據庫表有三個字段,id(自增主鍵)、username、password。
在這裏插入圖片描述
圖1

1.1 插入耗時實測

實測:

  • 插入30多萬條數據,採用一次事務提交一條sql語句,耗時2小時以上,具體沒統計,因爲計劃是這樣插入2000萬條數據的,但由於太慢提前終止了,所以插入30多萬條數據。
  • 插入2000萬條數據,採用一次事務提交10萬條sql語句,耗時50多分鐘,將近1小時(若是老鐵們做這樣的實驗,可以加上一個時間對象,這樣可以看的更加清晰)。

1.2 TestDemo

在這裏插入圖片描述
圖2

1.3 測試代碼:

1.3.1 MybatisTest
import mapper.TestMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import po.TestBean;

import java.io.IOException;
import java.io.InputStream;

public class MybatisTest {


    public static void main(String[] args) throws IOException {
        //1.讀取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis.xml");
        //2.創建SqlSessionFactory工廠
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工廠生產SqlSession對象
        SqlSession session = factory.openSession();
        //4.使用SqlSession創建Dao接口的代理對象

        TestMapper testMapper = session.getMapper(TestMapper.class);
        //5.使用代理對象執行方法
        TestBean testBean =new TestBean();

        for (int i = 10; i<20000000 ;i++){
            testBean.setName("lbw"+i);
            testBean.setPassword("nb"+i);
                testMapper.saveTest(testBean);
//            //1次操作 提交1次事務
//            session.commit();
            // 100000次操作提交1次事務
            if (i%100000 == 0){
                session.commit();
            }

        }
        //6.釋放資源
        session.close();
        in.close();
    }

}

1.3.2 TestBean
package po;

import java.io.Serializable;

public class TestBean implements Serializable {


    String id;
    String name;
    String password;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "po.TestBean{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

1.3.3 TestMapper
package mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import po.TestBean;

public interface TestMapper {

    @Insert("insert into test200411(name,password) values(#{name},#{password})")
    void saveTest(TestBean t);

    @Select("select id,name,password from test200411 where id = #{s}")
    TestBean selectById(String s);
}

1.3.4 mybatis.xml(mybatis配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
    <!-- 配置環境 -->
    <environments default="mysql">
        <!-- 配置mysql的環境-->
        <environment id="mysql">
            <!-- 配置事務的類型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置數據源(連接池) -->
            <dataSource type="POOLED">
                <!-- 配置連接數據庫的4個基本信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每個dao獨立的配置文件
        如果是用註解來配置的話,此處應該使用class屬性指定被註解的dao全限定類名
    -->
    <mappers>
        <mapper class="mapper.TestMapper"/>
    </mappers>
</configuration>

1.4 過程體驗

程序運行後在幾個小時時間裏,電腦處於烤機(烤雞誘惑,烤雞pg是塊寶)狀態!!!很遺憾的是,當時沒有截圖程序運行了的時間,不過1.1結論是數據是保真的,實在不想再搞幾千萬條數據在數據庫了。
在這裏插入圖片描述
圖3
在這裏插入圖片描述
圖4

1.5 數據量

1.5.1 計數用時

使用 select count(0) from test200411 進行計數,統計查詢用時:13.7s,得到統計數: 20379620,之所以是20379620,是因爲我測試中先插入30多萬,之後再插入2000萬。數據是不會騙人的哦,沒毛病吧,鐵子們。
在這裏插入圖片描述
圖5

1.5.2 佔用硬盤空間
  • 硬盤空間佔用爲:1.47GB(1585455597 B)。
  • 分析一下,我的表字段只有3個,且每個字段裏面實質存儲的數據長度都不長。
    151585455597 B / 20379620 = 77 B
    這樣算下每一行數據平均佔用77B,這個數據一看就知道,存儲過程中應該是有數據壓縮的,這一結論不太嚴謹,有大佬知道mysql底層存儲的話,望指導一下。
    在這裏插入圖片描述
    圖6
    在這裏插入圖片描述
    圖7

2、索引查詢測試

2.1 索引檢測

使用 show index from test200411可以查到當前表擁有的索引,當前只有我自己自增id的默認索引,和我自己建立的id字段索引。
在這裏插入圖片描述
圖8

2.2 對有無索引的字段where查詢比較

2.2.1 無索引字段

使用 SELECT * from test200411 where password = "nb1234"
查詢到兩條數據,耗時:57.9s,明顯這個查詢時間是不可取的

  • 小結論:所以在大表查詢我們爲什麼需要及其注重性能了,平常若是做個小的測試系統,數據量撐死幾千,查詢時,根本就感覺不出時間上的差異。這也是爲什麼我們需要那麼多數據庫優化的策略,索引、分庫分表、主從複製、讀寫分離、緩存設計等等。
  • 爲什麼會這麼慢呢,因爲該字段沒有索引的話,它就需要一行一行的去做比對,這樣做,不僅比對長,磁盤io也多。
    在這裏插入圖片描述
    圖9
2.2.1 有索引字段

使用:SELECT * from test200411 where id = 2566345
查詢到唯一數據,耗時:0.133s

  • 這個根據索引查詢數據的時間和無索引字段查詢時間比較,由實質數據得到它們之間性能差了500多倍。
  • 爲什麼索引這麼快?因爲索引底層是b+樹,所以索引是按照一定的大小順序來排序的,根據索引查詢,能減少磁盤io,也能減少比對次數。
    在這裏插入圖片描述
    圖10

2.3 建立索引

2.3.1 索引創建

使用:CREATE INDEX paw_index ON test200411(password)
耗時:156.0s
在這裏插入圖片描述
圖11

2.3.2 索引查詢

再次查看已經索引,可以看到一個paw_index 的新索引,這就是我們剛剛創建的索引。
在這裏插入圖片描述
圖12

2.3.3 查詢新建立索引字段

使用 SELECT * from test200411 where password = "nb12345"
耗時:0.110s
可以將此條sql和 2.2.1 比較 ,除了匹配的字符串不一樣(之所以用了不同的字符串,是因爲實驗是連着做的,同一條字符串查詢,會有緩存,影響結果),其他都是一樣的。可以看到它們之前驚人的查詢速度差異,速度相差大概是500倍。
在這裏插入圖片描述
圖13

3、總結

3.1 事務對數據庫性能影響

在一個事務內只提交一條sql和在一個事務內提交多個sql,對數據庫數據的批量處理影響是很大的。
一個事務內提交多個sql可以提高批量數據的處理速度。

3.2 數據庫索引對數據庫查詢性能分析

通過上面的實例,我們可以看到,有無索引對數據庫查詢速度的影響是很大的,實驗中它們速度差異可以到500倍,當然這是取決的實驗過程中的數據量,而我們的實驗數據量有2000多萬行,也正是這樣才能放大對 有無索引對數據庫查詢的影響 效果。

總之,通過這個實例不會有人還沒搞懂索引這個東西的重要性和其作用吧!不會吧?不會吧?
當然我這沒將索引的三板斧,“是什麼,爲什麼需要,怎麼做”,全列出來,我覺得寫也的還行,帶有實例出發,通俗易懂吧!!

這個實驗從上到下都是可以復現的,代碼和過程都有,有興趣的小夥伴可以嘗試復現一下,讓自己對索引的理解加深。

最後,若是小夥伴有什麼問題,可以一起討論哦。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章