Detailed sub-mysql table

Often hear people say "data table is too big, you need to divide the table", "xxxx, and to divide the table" remarks, then, in the end Why did you watch?

Is the amount of data necessary to divide the table?

 

Mysql affect the amount of data to index

Mysql version 5.7

New test data

To test whether the index mysql query and the amount of data about, I made the following test preparation:

Table 4 New article1, article2, article3, article4, article5 are inserted in each table 200,000, 500,000, 1,000,000, 2,000,000, 15,000,000 data, the data is randomly generated

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);

Data script generated using easyswoole, multiple coroutine inserted:

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 ;
}

FIG generated data:

仙士可博客

 

The total number of database Preview:

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" ;

仙士可博客

 

Query time test

Query script

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:字段与其他字段更新时间不同,可以尝试分表

Guess you like

Origin www.cnblogs.com/liliuguang/p/10935080.html