MySQL 如何设计统计数据表


Posted in MySQL onJune 15, 2021

缓存型数据表通常在统计数据时会经常用到,因此也会叫统计性数据。举个例子来说,对于员工、部门数据表而言,我们可能会需要查询一个部门下有多少员工。这时候有三种方式实现:

  • 在部门下增加一个员工数量的字段,每次对员工进行增、改、删操作时都需要同步更新员工数量(如果员工换部门,则需要更新多个部门的员工数量)。这种方式能够保证实时性,但是却很低效。对于如果是操作不频繁时是没问题的,假设相当频繁,就意味着每次都需要操作两张表,而且业务代码都需要做埋点处理,将统计业务和普通业务深度耦合在一起了。
  • 每次查询的时候,从员工表中执行 SUM 函数,获取该部门的员工数。这种方式避免了埋点,但是每次都需要去员工数据表求和,如果员工数据量大的话会很低效。
  • 新建一张统计表,每隔一定时间从员工表中汇总每个部门的人员数量。这种定时抽取数据的方式会牺牲一定的实时性,但降低了代码的耦合,由于部门不会太多,这张表的大小是可预测的,也提高了数据访问的效率。这种方式即缓存型数据表。

以掘金的手机端个人中心为例,为展示每个用户的关注人数、关注者和掘力值,不可能每次查询都去做一次 SUM,这意味着需要做多张表的 SUM 操作,效率会很低,而且掘力值的计算还涉及到更为复杂的计算方法(与文章的浏览量和点赞数有关)。因此,可以猜测一下大致的表设计,这样在查询用户个人主页信息的时候只需要从这一张表就可以读取到所有数据了。

CREATE t_user_summay (
  id INT PRIMARY KEY,
  user_id BIGINT(20),
  focused_user_cnt INT,
  followed_user_cnt INT,
  user_value INT,
  user_level ENUM('Lv1', 'Lv2', ..., 'Lv8'),
  created_time DATETIME,
  updated_time DATETIME,
);

 

是否需要实时更新

在实际应用过程中,统计表有两种方式,一种是实时更新,一种是周期性的重建数据。两种方式有利有弊,实时更新保证了查询数据的即时性,但是会牺牲性能,并且要求代码埋点,而且由于数据更新是没有规律的,可能产生碎片。周期性的重建数据牺牲了实时性,如果说大部分数据都不变的话会带来不必要的统计计算,但如果数据经常变动,那周期性地重建数据显然会更高效而且避免了埋点的情况。当然,避免应用程序的埋点也可以通过触发器来完成。

物化视图工具(Flexviews)

在 MySQL 中,有一个 Flexviews 的开源工具用于从数据库的binlog 中提取数据完成数据统计。有点类似与视图,但与视图所不同的是,Flexviews 产生的数据表是物理表,这也是为什么称之为物化视图的原因。而且,Flexviews 还支持增量更新和全量更新。推荐使用增量更新,以避免所有行的统计数据都需要重建的情况。增量更新会检查哪些数据行数据发生了改变,再执行更新操作,相比全量更新而言性能会更高。但为了检测数据改变,需要引入一个视图记录数据行的变化日志。

计数表

在实际开发中,我们经常会需要对一些操作进行计数,比如文章的阅读数、点赞数。如果将计数值放入同一张表很可能在更新的时候出现并发问题。使用独立的计数表可以避免查询缓存失效问题并使用一些更高级的技巧。例如统计文章的阅读数、点赞数的数据表:

CREATE TABLE t_article_counter (
  article_id INT PRIMARY KEY,
  read_cnt INT UNSIGNED NOT NULL,
  praise_cnt INT UNSIGNED NOT NULL
);

在更新阅读数的时候,可以使用 MySQL 的内置加1操作:

UPDATE t_article_counter 
SET read_cnt = read_cnt + 1
WHERE article_id = 1;

这种方式可以使得操作是单行的,对事物而言是互斥的,因此会将事务序列化处理避免并发问题。但是却会影响并发请求量。可以对文章增加多个插槽来提高并发量。

CREATE TABLE t_article_counter (
  id INT NOT NULL PRIMARY KEY,
  slot TINYINT UNSIGNED,
  article_id INT,
  read_cnt INT UNSIGNED NOT NULL,
  praise_cnt INT UNSIGNED NOT NULL,
  INDEX(article_id)
);

这时可以创建100个插槽初始化数据,在更新的时候可以这样操作:

UPDATE t_article_counter
SET read_cnt = read_cnt + 1 
WHERE slot = RAND() * 100 AND article_id = 1;

获取某篇文章的总阅读数时,需要使用一个 SUM 操作:

SELECT SUM(read_cnt) FROM t_article_counter
WHERE article_id = 1;

这种方式实际上是空间换时间,提高了并发量。

总结

本篇介绍了如何设计统计数据表,关键的核心在于业务类型。对于更新频率低、数据量小的表使用实时同步或者直接 SUM 求和问题都不大。而对于大数据表,高频率的更新的情况,则可以使用独立的统计表。同时,若存在高并发的情况,统计表中可以考虑每项主体增加多个插槽的方式提高并发量。如果是周期性地同步数据,也可以使用 Flexviews 物化视图插件实现。

以上就是MySQL 如何设计统计数据表的详细内容,更多关于MySQL 设计统计数据表的资料请关注三水点靠木其它相关文章!

MySQL 相关文章推荐
mysql的MVCC多版本并发控制的实现
Apr 14 MySQL
MySQL5.7并行复制原理及实现
Jun 03 MySQL
新手入门Mysql--sql执行过程
Jun 20 MySQL
MySQL8.0.18配置多主一从
Jun 21 MySQL
MySQL系列之四 SQL语法
Jul 02 MySQL
mysql中int(3)和int(10)的数值范围是否相同
Oct 16 MySQL
SpringBoot连接MySQL获取数据写后端接口的操作方法
Nov 02 MySQL
MySQL中rank() over、dense_rank() over、row_number() over用法介绍
Mar 23 MySQL
一文了解MYSQL三大范式和表约束
Apr 03 MySQL
MySql重置root密码 --skip-grant-tables
Apr 11 MySQL
MySQL数据库如何使用Shell进行连接
Apr 12 MySQL
mysql通过group by分组取最大时间对应数据的两种有效方法
Sep 23 MySQL
浅谈MySQL 亿级数据分页的优化
解析MySQL binlog
详细谈谈MYSQL中的COLLATE是什么
Jun 11 #MySQL
探究Mysql模糊查询是否区分大小写
安装配置mysql及Navicat prenium的详细流程
mysql 如何获取两个集合的交集/差集/并集
Jun 08 #MySQL
Mysql 如何查询时间段交集
Jun 08 #MySQL
You might like
php 什么是PEAR?(第三篇)
2009/03/19 PHP
PHP date()函数警告: It is not safe to rely on the system解决方法
2014/08/20 PHP
thinkphp框架下404页面设置 仅三步
2016/05/14 PHP
php把时间戳转换成多少时间之前函数的实例
2016/11/16 PHP
laravel-admin 实现给grid的列添加行数序号的方法
2019/10/08 PHP
PHP Swoole异步MySQL客户端实现方法示例
2019/10/24 PHP
jQuery中filter()和find()的区别深入了解
2013/09/25 Javascript
jquery插件开发之实现jquery手风琴功能分享
2014/03/10 Javascript
javascript实现简单的页面右下角提示信息框
2015/07/31 Javascript
js实现简易的单数字随机抽奖(0-9)
2020/03/19 Javascript
JS实现的自定义右键菜单实例二则
2015/09/01 Javascript
JS实现自动变换的菜单效果代码
2015/09/09 Javascript
Jquery基础之事件操作详解
2016/06/14 Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单
2016/11/25 Javascript
浅谈Angular的$q, defer, promise
2016/12/20 Javascript
Vue.js -- 过滤器使用总结
2017/02/18 Javascript
jQuery 禁止表单用户名、密码自动填充功能
2017/10/30 jQuery
基于Vue实现拖拽效果
2018/04/27 Javascript
JS求解两数之和算法详解
2020/04/28 Javascript
如何手动实现一个 JavaScript 模块执行器
2020/10/16 Javascript
vue组件是如何解析及渲染的?
2021/01/13 Vue.js
[03:03]DOTA2校园争霸赛 济南城市决赛欢乐发奖活动
2013/10/21 DOTA
Python os模块学习笔记
2015/06/21 Python
win10下python3.5.2和tensorflow安装环境搭建教程
2018/09/19 Python
python使用zip将list转为json的方法
2018/12/31 Python
Python中的类与类型示例详解
2019/07/10 Python
python tkinter canvas使用实例
2019/11/04 Python
关于多元线性回归分析——Python&SPSS
2020/02/24 Python
HTML5 层的叠加的实现
2020/07/07 HTML / CSS
为什么要用EJB
2014/04/17 面试题
捐款倡议书范文
2014/02/02 职场文书
执行总经理岗位职责
2014/02/03 职场文书
支教个人总结
2015/03/04 职场文书
小学教学工作总结2015
2015/05/13 职场文书
致我们终将逝去的青春观后感
2015/06/10 职场文书
python实现学生信息管理系统(面向对象)
2022/06/05 Python