事务对数据库的性能有什么影响?有无索引查找对数据库性能影响多大?不跟你多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多万行,也正是这样才能放大对 有无索引对数据库查询的影响 效果。

总之,通过这个实例不会有人还没搞懂索引这个东西的重要性和其作用吧!不会吧?不会吧?
当然我这没将索引的三板斧,“是什么,为什么需要,怎么做”,全列出来,我觉得写也的还行,带有实例出发,通俗易懂吧!!

这个实验从上到下都是可以复现的,代码和过程都有,有兴趣的小伙伴可以尝试复现一下,让自己对索引的理解加深。

最后,若是小伙伴有什么问题,可以一起讨论哦。。。。。

原创文章 59 获赞 21 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44185736/article/details/105454820