MySQL单表插入&查询性能测试

测试SQL(52个字段)

CREATE DEFINER=`root`@`%` PROCEDURE `generateData`()
BEGIN
    DROP TABLE
    IF
        EXISTS USER;
    CREATE TABLE `user` (
        `id` INT ( 11 ) AUTO_INCREMENT,
        `username` VARCHAR ( 255 ),
        `addr1` VARCHAR ( 255 ),
        `addr2` VARCHAR ( 255 ),
        `addr3` VARCHAR ( 255 ),
        `addr4` VARCHAR ( 255 ),
        `addr5` VARCHAR ( 255 ),
        `addr6` VARCHAR ( 255 ),
        `addr7` VARCHAR ( 255 ),
        `addr8` VARCHAR ( 255 ),
        `addr9` VARCHAR ( 255 ),
        `addr10` VARCHAR ( 255 ),
        `addr11` VARCHAR ( 255 ),
        `addr12` VARCHAR ( 255 ),
        `addr13` VARCHAR ( 255 ),
        `addr14` VARCHAR ( 255 ),
        `addr15` VARCHAR ( 255 ),
        `addr16` VARCHAR ( 255 ),
        `addr17` VARCHAR ( 255 ),
        `addr18` VARCHAR ( 255 ),
        `addr19` VARCHAR ( 255 ),
        `addr20` VARCHAR ( 255 ),
        `addr21` VARCHAR ( 255 ),
        `addr22` VARCHAR ( 255 ),
        `addr23` VARCHAR ( 255 ),
        `addr24` VARCHAR ( 255 ),
        `addr25` VARCHAR ( 255 ),
        `addr26` VARCHAR ( 255 ),
        `addr27` VARCHAR ( 255 ),
        `addr28` VARCHAR ( 255 ),
        `addr29` VARCHAR ( 255 ),
        `addr30` VARCHAR ( 255 ),
        `addr31` VARCHAR ( 255 ),
        `addr32` VARCHAR ( 255 ),
        `addr33` VARCHAR ( 255 ),
        `addr34` VARCHAR ( 255 ),
        `addr35` VARCHAR ( 255 ),
        `addr36` VARCHAR ( 255 ),
        `addr37` VARCHAR ( 255 ),
        `addr38` VARCHAR ( 255 ),
        `addr39` VARCHAR ( 255 ),
        `addr40` VARCHAR ( 255 ),
        `addr41` VARCHAR ( 255 ),
        `addr42` VARCHAR ( 255 ),
        `addr43` VARCHAR ( 255 ),
        `addr44` VARCHAR ( 255 ),
        `addr45` VARCHAR ( 255 ),
        `addr46` VARCHAR ( 255 ),
        `addr47` VARCHAR ( 255 ),
        `addr48` VARCHAR ( 255 ),
        `addr49` VARCHAR ( 255 ),
        `addr50` VARCHAR ( 255 ),
    PRIMARY KEY ( `id` )) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
    
    SET @n = 1;
    WHILE
            @n <= 2000000 DO
            INSERT INTO `user`
        VALUES
            (    NULL,
                CONCAT( 'user', @n ),
                CONCAT( 'user', @n, '_', 'addr', 1 ),
                CONCAT( 'user', @n, '_', 'addr', 2 ),
                CONCAT( 'user', @n, '_', 'addr', 3 ),
                CONCAT( 'user', @n, '_', 'addr', 4 ),
                CONCAT( 'user', @n, '_', 'addr', 5 ),
                CONCAT( 'user', @n, '_', 'addr', 6 ),
                CONCAT( 'user', @n, '_', 'addr', 7 ),
                CONCAT( 'user', @n, '_', 'addr', 8 ),
                CONCAT( 'user', @n, '_', 'addr', 9 ),
                CONCAT( 'user', @n, '_', 'addr', 10 ),
                CONCAT( 'user', @n, '_', 'addr', 11 ),
                CONCAT( 'user', @n, '_', 'addr', 12 ),
                CONCAT( 'user', @n, '_', 'addr', 13 ),
                CONCAT( 'user', @n, '_', 'addr', 14 ),
                CONCAT( 'user', @n, '_', 'addr', 15 ),
                CONCAT( 'user', @n, '_', 'addr', 16 ),
                CONCAT( 'user', @n, '_', 'addr', 17 ),
                CONCAT( 'user', @n, '_', 'addr', 18 ),
                CONCAT( 'user', @n, '_', 'addr', 19 ),
                CONCAT( 'user', @n, '_', 'addr', 20 ),
                CONCAT( 'user', @n, '_', 'addr', 21 ),
                CONCAT( 'user', @n, '_', 'addr', 22 ),
                CONCAT( 'user', @n, '_', 'addr', 23 ),
                CONCAT( 'user', @n, '_', 'addr', 24 ),
                CONCAT( 'user', @n, '_', 'addr', 25 ),
                CONCAT( 'user', @n, '_', 'addr', 26 ),
                CONCAT( 'user', @n, '_', 'addr', 27 ),
                CONCAT( 'user', @n, '_', 'addr', 28 ),
                CONCAT( 'user', @n, '_', 'addr', 29 ),
                CONCAT( 'user', @n, '_', 'addr', 30 ),
                CONCAT( 'user', @n, '_', 'addr', 31 ),
                CONCAT( 'user', @n, '_', 'addr', 32 ),
                CONCAT( 'user', @n, '_', 'addr', 33 ),
                CONCAT( 'user', @n, '_', 'addr', 34 ),
                CONCAT( 'user', @n, '_', 'addr', 35 ),
                CONCAT( 'user', @n, '_', 'addr', 36 ),
                CONCAT( 'user', @n, '_', 'addr', 37 ),
                CONCAT( 'user', @n, '_', 'addr', 38 ),
                CONCAT( 'user', @n, '_', 'addr', 39 ),
                CONCAT( 'user', @n, '_', 'addr', 40 ),
                CONCAT( 'user', @n, '_', 'addr', 41 ),
                CONCAT( 'user', @n, '_', 'addr', 42 ),
                CONCAT( 'user', @n, '_', 'addr', 43 ),
                CONCAT( 'user', @n, '_', 'addr', 44 ),
                CONCAT( 'user', @n, '_', 'addr', 45 ),
                CONCAT( 'user', @n, '_', 'addr', 46 ),
                CONCAT( 'user', @n, '_', 'addr', 47 ),
                CONCAT( 'user', @n, '_', 'addr', 48 ),
                CONCAT( 'user', @n, '_', 'addr', 49 ),
                CONCAT( 'user', @n, '_', 'addr', 50 )
            );
            SET @n = @n + 1;
    END WHILE;
END

插入执行耗时

插入1万条数据:6.5s
插入10万条数据:65s
插入50万条数据:334s
插入100万条数据:669s
插入200万条数据:1389s

查询执行耗时(200万条数据)

查数据总量:

查询SQL:SELECT COUNT(*) FROM user
耗时:2.7s

查所有字段,查不同偏移量,1000条数据:

查询SQL:SELECT * FROM user LIMIT 1000
耗时:0.03s

查询SQL:SELECT * FROM user LIMIT 500000,1000
耗时:1.6s

查询SQL:SELECT * FROM user LIMIT 1000000,1000
耗时:3s

查询SQL:SELECT * FROM user LIMIT 1500000,1000
耗时:4.5s

查两个字段,查不同偏移量,1000条数据:

查询SQL:SELECT addr1, addr2 FROM user LIMIT 500000,1000
耗时:0.8s

查询SQL:SELECT addr1, addr2 FROM user LIMIT 1000000,1000
耗时:1.5s

查询SQL:SELECT addr1, addr2 FROM user LIMIT 1500000,1000
耗时:2.1s

查所有字段,查同一偏移量,不同条数据:

查询SQL:SELECT * FROM user LIMIT 1000000,1000
耗时:3s

查询SQL:SELECT * FROM user LIMIT 1000000,2000
耗时:3s

查询SQL:SELECT * FROM user LIMIT 1000000,3000
耗时:3s

查询SQL:SELECT * FROM user LIMIT 1000000,10000
耗时:3s

查询SQL:SELECT * FROM user LIMIT 1000000,100000
耗时:3.8s

测试索引

查所有字段,对 username 列使用 Like 查第1000000到1000999共1000个用户的数据:

查询SQL:
SELECT * FROM user
WHERE username LIKE CONCAT(‘user1000’,‘%’) AND LENGTH(username) = 11
耗时:6.2s

查询所有字段,对 username 列使用 = 号查第1000000个用户的数据:

查询SQL:
SELECT * FROM user
WHERE username = ‘user1000000’
耗时:6s

对 username 列创建索引:

SQL:CREATE INDEX idx_username ON user(username)
耗时:8.5s

创建索引后,对 username 列使用 Like 查第1000000到1000999共1000个用户的数据:

查询SQL:
SELECT * FROM user
WHERE username LIKE CONCAT(‘user1000’,‘%’) AND LENGTH(username) = 11
耗时:0.031s

创建索引后,对 username 列使用 = 号查第1000000个用户的数据:

查询SQL:
SELECT * FROM user
WHERE username = ‘user1000000’
耗时:0.025s

数据存储占用

插入200万条数据后,数据长度:1.86 GB(Navicat中右侧可以看到) ,所占用磁盘空间:2G(user.ibd 文件的大小)

创建索引后,索引长度:48.6MB(Navicat中右侧可以看到)

查询时,mysqld 内存占用并不会增加,只有 Navicat 内存会增加

总结

在查询时,待查询的数据的字段数,偏移量,条数都会影响查询时间。
建立索引后,能够大大减少查询时间。

补充

MySQL本身并没有对单表最大记录数进行限制。

阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。

MySql数据库一张表中能存储的最大数据量和实际记录的条数无关,而与 MySQL 的配置以及机器的硬件有关。

MySQL 为了提高性能,会将表的索引装载到内存中。InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。

但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制。


版权声明:本文为sw_feng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。