文章目錄
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多萬行,也正是這樣才能放大對 有無索引對數據庫查詢的影響 效果。
總之,通過這個實例不會有人還沒搞懂索引這個東西的重要性和其作用吧!不會吧?不會吧?
當然我這沒將索引的三板斧,“是什麼,爲什麼需要,怎麼做”,全列出來,我覺得寫也的還行,帶有實例出發,通俗易懂吧!!
這個實驗從上到下都是可以復現的,代碼和過程都有,有興趣的小夥伴可以嘗試復現一下,讓自己對索引的理解加深。
最後,若是小夥伴有什麼問題,可以一起討論哦。。。。。