经常听到有人说“数据表太大了,需要分表”,“xxxx了,要分表”的言论,那么,到底为什么要分表?

难道数据量大就要分表?

mysql数据量对索引的影响

本人mysql版本为5.7

新增数据测试

为了测试mysql索引查询是否和数据量有关,本人做了以下的测试准备:

新建4个表article1,article2,article3,article4,article5 每个表分别插入20万,50万,100万,200万,1500万的数据,数据都是随机生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    create table test.article1(
  id          int auto_increment comment 'id'
    primary key,
  user_id     int          not null comment '用户id',
  title       varchar(64)  not null comment '标题',
  add_time    datetime     null comment '新增时间',
  update_time int          null comment '更新时间',
  description varchar(255) null comment '简介',
  status      tinyint(1)   null comment '状态 1正常 0隐藏'
)
  charset = utf8;
 
create index article_title_index
  on test.article1 (title);

生成数据脚本,使用easyswoole,多协程插入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
include "./vendor/autoload.php";
\EasySwoole\EasySwoole\Core::getInstance()->initialize();
for ($i = 0; $i <= 2000; $i++) {//协程最多3000,创建1000个协程
    go(function () use ($i) {
        \App\Utility\Pool\MysqlPool::invoke(function (\App\Utility\Pool\MysqlPoolObject $mysqlObject) use ($i) {
            for ($y = 0; $y <= 1000; $y++) {//每个协程插入100条数据
                $data = [
                    'user_id'     => mt_rand(1, 2500),
                    'title'       => \EasySwoole\Utility\Random::character(32),//随机生成32位字母的标题
                    'add_time'    => date('Y-m-d H:i:s', mt_rand(strtotime('2018-01-01'), strtotime('2019-01-01'))),//随机生成日期
                    'update_time' => mt_rand(strtotime('2018-01-01'), strtotime('2019-01-01')),//随机生成日期
                    'description' => getChar(mt_rand(8, 64)),//随机生成8-64位汉字,
                    'status'      => mt_rand(0, 1),
                ];
                $mysqlObject->insert('article2', $data);
            }
            echo "协程$i 插入完成\n";
        }, -1);
    });
}
 
function getChar($num)  // $num为生成汉字的数量
{
    $b = '';
    for ($i = 0; $i < $num; $i++) {
        // 使用chr()函数拼接双字节汉字,前一个chr()为高位字节,后一个为低位字节
        $a = chr(mt_rand(0xB0, 0xD0)) . chr(mt_rand(0xA1, 0xF0));
        // 转码
        $b .= iconv('GB2312', 'UTF-8', $a);
    }
    return $b;
}

生成的数据如图:

数据库总条数预览:

1
select (select count(1) from article1) as "1" , (select count(1) from article2) as "2", (select count(1) from article3) as "3", (select count(1) from article4) as "4", (select count(1) from article5) as "5";

查询时间测试

查询脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 19-5-11
 * Time: 下午7:20
 */
include "./vendor/autoload.php";
\EasySwoole\EasySwoole\Core::getInstance()->initialize();
go(function () {
    /**
     * @var $db \App\Utility\Pool\MysqlPoolObject
     */
    $db = \App\Utility\Pool\MysqlPool::defer();
    $startTime = microtimeFloat();
    //查询1000次
    for ($i = 0; $i < 10000; $i++) {
        $str =\EasySwoole\Utility\Random::character(32);//随机生成字符串,用于查询
        $data = $db->where('title',$str)->getOne('article1');
    }
 
    echo "1耗时" . (microtimeFloat() - $startTime) . '秒'.PHP_EOL;
    $startTime = microtimeFloat();
    //查询1000次
    for ($i = 0; $i < 10000; $i++) {
        $str =\EasySwoole\Utility\Random::character(32);//随机生成字符串,用于查询
        $data = $db->where('title',$str)->getOne('article2');
    }
 
    echo "2耗时" . (microtimeFloat() - $startTime) . '秒'.PHP_EOL;
    $startTime = microtimeFloat();
    //查询1000次
    for ($i = 0; $i < 10000; $i++) {
        $str =\EasySwoole\Utility\Random::character(32);//随机生成字符串,用于查询
        $data = $db->where('title',$str)->getOne('article3');
    }
 
    echo "3耗时" . (microtimeFloat() - $startTime) . '秒'.PHP_EOL;
    $startTime = microtimeFloat();
    //查询1000次
    for ($i = 0; $i < 10000; $i++) {
        $str =\EasySwoole\Utility\Random::character(32);//随机生成字符串,用于查询
        $data = $db->where('title',$str)->getOne('article4');
    }
 
    echo "4耗时" . (microtimeFloat() - $startTime) . '秒'.PHP_EOL;
    $startTime = microtimeFloat();
    //查询1000次
    for ($i = 0; $i < 10000; $i++) {
        $str =\EasySwoole\Utility\Random::character(32);//随机生成字符串,用于查询
        $data = $db->where('title',$str)->getOne('article5');
    }
 
    echo "5耗时" . (microtimeFloat() - $startTime) . '秒'.PHP_EOL;
});
function microtimeFloat()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

该脚本是一个实例脚本,在后面的其他测试中依旧使用该脚本,修改下字段和逻辑

title全索引查询一条时间情况:(为了准确,本人运行了多次)

可以看出,数据量在200万以下时,查询时间几乎没有差别,只是在数据量1400万时,查询1万次的时间增加了1秒

注:本人在之前测试,和之后测试时,查询article5时时间大概是2.1-2.5秒左右,可能mysql有其他知识点本人未掌握,所以没法详细解释

title全索引查询不限制条数时间情况:(为了准确,本人运行了多次)

可以看出,在200万数据之前 查询时间并没有太大的差距,1400万有一点点的差距

title like 左前缀 索引查询不限制条数时间情况:(为了准确,本人运行了多次)

根据这次测试,我们可以发现

1:mysql的查询和数据量的大小关系并不大(微乎其微)

2:mysql只要是命中索引,不管数据量有多大,都会非常快(快的一批,由于本人比较懒,并且本人之前也测试过单表1.5亿速度一样很快,就懒得继续新增2亿测试数据了,太累)

什么情况需要分表

从上面的章节可以发现,数据量的多少和查询速度其实关系不是很大,那么为什么要分表呢?原因有以下几种:

1:   单表 不涉及索引的操作太多,无法直接命中索引的

2:模糊查找范围过大,无法直接命中索引的,例如日志表查时间区间

3:单表数据量过大,操作繁忙的

4:数据量过大,有大部分数据很少访问的(冷热数据)
5:装逼,需要用分表装逼的

分表优缺点

在上面,我们已经知道了为什么要分表,分表该怎么分呢?

首先,我们需要先搞懂分表的意义

数据分表有着以下好处:

1:分散表压力,使其响应速度提高

2:数据降维,提升查询速度

3:分冷热数据,更好管理,备份

4:支持分布式部署数据库,将压力分担到其他服务器中

同时,缺点如下:

1:分表之后较难管理多表

2:join表时可能需要join多个

3:查询模糊数据时需要全部的表一起查

所以,数据量不大时候,不建议分表。

水平分表

根据数据的不同规则作为一个分表条件,区分数据以数据之间的分表叫做水平分表

水平分表是比较常见的分表方法,也是解决数据量大时候的分表方法,在水平分表中,也根据场景的不同而分表方法不同

取模分表

假设有个用户表(1000w用户)需要分表,那么我们可以根据该用户表的唯一标识(id ,用户账号)进行取模分表

重新新建n个表。例如5个, user1,user2,user3....uesr5

取出所有用户,根据 用户账号进行取模,例如:

1
2
3
4
5
6
<?php
$userAccount ='tioncico';
$num =  (crc32($userAccount)%5);
$tableName = 'user'.($num+1);
echo "{$userAccount}应该存储到{$tableName}表";
//tioncico应该存储到user3表

不建议使用id分表,因为一般情况下,我们是使用账号,或者其他唯一标识 来进行区分某个人的,如果你表设计像qq号一样,那完全可以将id命名为其他的字段,用于区分,自增id同样需要

取模分表法会使数据尽量的均衡分布,压力均衡,非常适合于需要通过特定标识字段查找数据的表(会员表)

冷热数据分表

冷热数据大多数体现在跟时间有关的 日志表,订单表上面

在冷热数据分表时,我们应该遵循以下几种分表规则

1:数据冷热分表,需要注意冷热数据的界限

例如,商城订单表,每天增加100万的订单,一年就会增加到3.6亿的订单数,而大多数情况下,用户只会查询近1-3个月的数据,我们可以

通过订单时间进行分表,只需要按照月份进行分表即可

2:通过取模分表,需要注意取模字段,

垂直分表

区分一条数据的不同字段,叫做垂直分表

垂直分表其实我们在设计数据库时,可能已经是用到了的,比如会员金额表,关联会员表的userId,这个时候,其实就可以叫做是垂直分表
把会员金额的字段分到了其他的表中(会员金额表)

垂直分表较为简单,有以下几种分法:

1:字段意义和表其他字段意义不同,可以尝试分表

2:字段占用空间太大,不常用或只在特定情况使用,可以尝试分表

3:字段与其他字段更新时间不同,可以尝试分表

mysql分表详解的更多相关文章

  1. mysql.user表详解

    GRANT语法:     GRANT 权限 ON 数据库.* TO 用户名@'登录主机' IDENTIFIED BY '密码'  权限:  ALL,ALTER,CREATE,DROP,SELECT,U ...

  2. oracle的 分表 详解 -----表分区

    此文从以下几个方面来整理关于分区表的概念及操作:         1.表空间及分区表的概念         2.表分区的具体作用         3.表分区的优缺点         4.表分区的几种类 ...

  3. Mysql—用户表详解(mysql.user)

    MySQL是一个多用户管理的数据库,可以为不同用户分配不同的权限,分为root用户和普通用户,root用户为超级管理员,拥有所有权限,而普通用户拥有指定的权限. MySQL是通过权限表来控制用户对数据 ...

  4. 【转】oracle的 分表 详解 -----表分区

    转载:https://www.cnblogs.com/congcidaishangjiamianju/p/8045804.html 一 表空间及分区表的概念 表空间: 是一个或多个数据文件的集合,所有 ...

  5. mysql分表和表分区详解

    为什么要分表和分区? 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能 ...

  6. 【mysql】mysql分表和表分区详解

    为什么要分表和分区? 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能 ...

  7. MySQL简单查询详解-单表查询

    MySQL简单查询详解-单表查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查询的执行路径 一条SQL查询语句的执行过程大致如下图所示: 1>.客户端和服务端通过my ...

  8. MySQL分表

    一.概念 1.为什么要分表和分区?日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询 ...

  9. Mysql加锁过程详解(8)-理解innodb的锁(record,gap,Next-Key lock)

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

随机推荐

  1. 四、局域网连接SqlServer

    一.局域网连接SqlServer 一台服务器上装有四个数据库的时候,我们可以通过IP\实例名的方式进行访问. navicat 连接sqlserver数据库

  2. javascript 回到顶部效果的实现

    demo.js window.onload=function() { var timer=null; var obtn=document.getElementById('btn'); var isTo ...

  3. javascript中数组元素删除方法splice,用在for循环中巨坑

    一.demo splice: 该方法会改变自动原始数组长度 实例: var array = ["aa","dd","cc","aa ...

  4. 三栏布局只知道圣杯、双飞翼,最终评级是……F

    三栏布局,面试与工作中的常见布局.分左中右三部分,其中左右宽度已知,中间宽度自适应.根据不同的DOM顺序与CSS处理,这里写下了五类布局方式. 一.浮动布局 1 圣杯布局 L:"我问你,你就 ...

  5. [CSP-S模拟测试]:蔬菜(二维莫队)

    题目描述 小$C$在家中开垦了一块菜地,可以抽象成一个$r\times c$大小的矩形区域,菜地的每个位置都种着一种蔬菜.秋天到了,小$C$家的菜地丰收了. 小$C$拟定了$q$种采摘蔬菜的计划,计划 ...

  6. 2018-2019-2 20175120 实验五《Java网络编程》实验报告

    实验报告封面 课程:Java程序设计 班级:1751班 姓名:彭宇辰 学号:20175120 指导教师:娄嘉鹏 实验日期:2019年5月26日 实验时间:13:10 - 15:25 实验序号:20 实 ...

  7. python slot

    每个实例包含一个字典,slot 让实例变成tup 或list,减少内存,但不能再增加属性 For classes that primarily serve as simple data structu ...

  8. AGC024B Backfront

    题目大意 给你一个1~n的排列 你有两个操作:将一个数移到最后或将一个数移到最前 问将排列排序最少要几次操作 分析 年纪大了,脑子不行了.. 实际我们只需求出对与一段连续的数它在排列中已经有序的最长长 ...

  9. xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

    运行xcode命令报错: sh-3.2# xcodebuild xcode-select: error: tool 'xcodebuild' requires Xcode, but active de ...

  10. Oralce常用系统函数

    dual:Oracle系统内部提供的一个用于实现临时数据计算的特殊表,它只有一个列DUMMY (VARCHAR2(1)) 字符类函数: concat--连接字符串 initcap--每个单词首字母大写 ...