测试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,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制。