詳細部分のMySQLテーブル

多くの場合、人々は言う、「xxxxは、テーブル分割する」最後に、その後、発言をなぜあなたは見なかった「データテーブルが大きすぎる、あなたがテーブルを分割する必要がある」を聞きますか?

データの量は、テーブルを分割する必要がありますか?

 

MySQLはインデックスにデータの量に影響を与えます

MySQLバージョン5.7

新しいテスト・データ

インデックスMySQLのクエリとのデータの量が、私は次のテストの準備をしたかどうかをテストするには:

表4新しい1条、article2、article3、article4、article5各テーブル20万500,000、1,000,000、2,000,000、15,000,000データが挿入され、データがランダムに生成されます

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

おすすめ

転載: www.cnblogs.com/liliuguang/p/10935080.html