Mysql:如何处理数百万行的表?

时间:2017-04-20 16:24:20

标签: mysql database database-design database-performance

我正在寻找能够正确存档非常大的表格的解决方案(每天大约10,000行)。

我目前有这种情况:

订单表:

CREATE TABLE `tbl_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `idproduct` int(11) NOT NULL DEFAULT '0',
  `iduser` int(11) NOT NULL DEFAULT '0',
  `state` int(11) NOT NULL DEFAULT '0',
  `progressive` int(11) NOT NULL DEFAULT '0',
  `show-voucher` int(11) NOT NULL DEFAULT '0',
  `voucher-custom` int(11) NOT NULL DEFAULT '0', 
  `check-validate` int(11) NOT NULL DEFAULT '0',
  `code-order` varchar(8) NOT NULL DEFAULT '',
  `code-product` char(15) NOT NULL DEFAULT '',
  `product-year` int(11) NOT NULL DEFAULT '2017',
  `product-area` char(3) NOT NULL DEFAULT '',
  `payment-type` char(3) NOT NULL DEFAULT '',
  `usr-qnt` int(11) NOT NULL DEFAULT '0',
  `usr-id` char(11) NOT NULL DEFAULT '',
  `usr-cid` char(8) NOT NULL DEFAULT '',
  `usr-ct` char(3) NOT NULL DEFAULT '000',
  `price` decimal(10,2) NOT NULL DEFAULT '0.00',
  `price-penale` decimal(10,2) NOT NULL DEFAULT '0.00',
  `price-rate` decimal(10,2) NOT NULL DEFAULT '0.00',
  `price-contanti` decimal(10,2) NOT NULL DEFAULT '0.00',
  `price-bonusmalus-rate` decimal(10,2) NOT NULL DEFAULT '0.00',
  `price-bonusmalus-contanti` decimal(10,2) NOT NULL DEFAULT '0.00',
  `price-incasso-contanti` decimal(10,2) NOT NULL DEFAULT '0.00',
  `rate-qnt` int(11) NOT NULL DEFAULT '0',
  `card-qnt` int(11) NOT NULL DEFAULT '0',

  `grp-user` longtext NOT NULL,
  `grp-price` longtext NOT NULL,
  `grp-item` longtext NOT NULL,
  `grp-element` longtext NOT NULL,

  `bonusmalus-description` varchar(500) NOT NULL,

  `note-s` text NOT NULL ,
  `note-c` text NOT NULL,
  `note-incasso` text NOT NULL,
  `note-interne` text NOT NULL,

  `d-start` date NOT NULL DEFAULT '0000-00-00',
  `d-end` date NOT NULL DEFAULT '0000-00-00',
  `d-create` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `d-incasso` date NOT NULL DEFAULT '0000-00-00',
  `d-sconf` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `d-cconf` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `d-export` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `d-expire` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `d-notify-vote` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `d-lastupdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  PRIMARY KEY (`id`),
  KEY `iduser` (`iduser`),
  KEY `code-order` (`code-order`),
  KEY `code-product` (`code-product`),
  KEY `idproduct` (`idproduct`),
  KEY `state` (`state`),
  KEY `price` (`price`),
  KEY `usr-qnt` (`usr-qnt`),
  KEY `d-expire` (`d-expire`),
  KEY `d-export` (`d-export`),
  KEY `price-bonusmalus-contanti` (`price-bonusmalus-contanti`),
  KEY `price-penale` (`price-penale`),
  KEY `price-bonusmalus-contanti_2` (`price-bonusmalus-contanti`),
  KEY `price-rate` (`price-rate`),
  KEY `price-contanti` (`price-contanti`),
  KEY `show-voucher` (`show-voucher`),
  KEY `voucher-custom` (`voucher-custom`),
  KEY `check-validate` (`check-validate`),
  KEY `progressive` (`progressive`),
  KEY `d-incasso` (`d-incasso`),
  KEY `price-incasso-contanti` (`price-incasso-contanti`),
  KEY `d-notify-vote` (`d-notify-vote`),
  KEY `product-year` (`product-year`),
  KEY `product-area` (`product-area`),
  KEY `d-lastupdate` (`d-lastupdate`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

订单是用户( iduser )为旅游套餐提出的请求,例如 Booking.com

此表每天生成 8,000 15,000 行。 我担心这张桌子会变得太大而导致问题。

我的核心领域是:

  • ID 代码订单
  • Idproduct 产品代码(我每年约有5000种产品)
  • ID用户所
  • 产品年度(我每年都有不同的产品)
  • 产品领域(我共有20个区域:000,001,002,003 ... 019)

我已经在网上阅读了几个解决方案,但我无法弄清楚哪一个可能是最好的解决方案:

1)将主表划分为许多其他子表或许多其他数据库?

实施例

  • DB-2016.tbl_order-JAN
  • DB-2016.tbl_order - 2月
  • .....
  • Db-2017.tbl_order-jan
  • DB-2017.tbl_order - 2月

  • Db.tbl_order-2016
  • Db.tbl_order-2015
  • Db.tbl_order-2014

  • Db.tbl_order主
  • Db.tbl_order产物面积

  • Db.tbl_order主
  • Db.tbl_order产物年

在这种情况下,是通过UNION进行SELECT吗?

2)对表格进行分区? 哪些字段可以执行? 产品年份?产品面积(共20个)? 订单创建日期(d-create)? 按id?

3)Sharding?但我不知道它是什么......

4)Innodb o MyISAM?

我仍然可以采用的一个解决方案是将LONGTEXT字段拆分为辅助表以减少tbl_order的权重:

  • Db.tbl_order
  • Db.tbl_order-grp-user(idorder,data)
  • Db.tbl_order-grp-price(idorder,data)
  • Db.tbl_order-grp-item(idorder,data)
  • Db.tbl_order-grp-element(idorder,data)

怀疑:如果我这样做,我减轻了tbl_order表的权重,但我没有减少记录数量。因此,必须对 db.tbl_order-grp-user,db.tbl_order-grp-price,db.tbl_order-grp-item,db.tbl_order-grp-elements 表进行分区?如果你使用范围idorder?

包含所有数据的SELECT将为:

Select *,
( SELECT `u`.`data` FROM `db`.`tbl_order-grp-user` as `u` where `u`.`idorder`=`order`.`id`) as `grp-user`, 
( SELECT `p`.`data` FROM `db`.`tbl_order-grp-price` as `p` where `p`.`idorder`=`order`.`id`) as `grp-price`, 
( SELECT `i`.`data` FROM `db`.`tbl_order-grp-item` as `i` where `i`.`idorder`=`order`.`id`) as `grp-item`
FROM  `db`.`tbl_order` as `order`
WHERE ............

感谢所有支持! : - )

1 个答案:

答案 0 :(得分:1)

新手警报......

INT(3个字节)就足够时,不要使用MEDIUMINT UNSIGNED(4个字节)。查看剩余的INT选项。

不要盲目索引每一列。

请查看您的SELECTs,了解哪些复合索引是有益的。查看我的Index Cookbook

不要太担心15K /天 - 小于1 /秒。 100 /秒是潜在问题的第一个转折点。

不要PARTITION。它不是不是的万能药,通常不会带来任何好处。用例很少。

不要拆分成多个“相同”的表格。永远。 (嗯,有用的用例非常少。)

不要害怕一百万行;确实关心了十亿行。

不要将CHAR用于可变长度字段;使用VARCHAR

请考虑使用utf8mb4而不是utf8。 utf8mb4匹配外部世界UTF-8的视图,包括表情符号和所有中文。

使用InnoDB。期。完全停止。 MyISAM正在消失; InnoDB在几乎所有方面都是好的或者更好。

考虑更改列名以避免-; _很常见,当您忘记背景时会避免错误。

不要碎片。 (这是跨多个服务器分割数据。)这是一个中等大小的表,中等流量;需要对具有大量流量的大型桌子进行分片。

在适当的地方说CHARACTER SET ascii。例如product-area。你现在拥有9个字节 - 3个字符*每个字符3个字节(utf8)的空间。

考虑TINYINT(3) UNSIGNED ZEROFILL product-area - 这将花费1个字节并为您重建前导零。

考虑您是否拥有“自然”PRIMARY KEY而不是AUTO_INCREMENT

请告诉我们grp列包含的内容。

请回到暂定的SELECT陈述。没有他们,我无法完成这次审查。

请考虑这是否应该是单个表格。真的是一个用户只订购一种产品吗?您可能需要一个用户表,一个产品表,一个订单表等等。