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安装后默认自带数据库的作用详解
Apr 27 MySQL
MySQL kill不掉线程的原因
May 07 MySQL
MySQL 百万级数据的4种查询优化方式
Jun 07 MySQL
MySQL 如何设计统计数据表
Jun 15 MySQL
低版本Druid连接池+MySQL驱动8.0导致线程阻塞、性能受限
Jul 01 MySQL
MySQL系列之二 多实例配置
Jul 02 MySQL
MySQL配置主从服务器(一主多从)
Aug 07 MySQL
MySQL库表太大怎么办? 数据库分库分表项目实践
Apr 11 MySQL
解决Mysql报错 Table 'mysql.user' doesn't exist
May 06 MySQL
MySQL数据库配置信息查看与修改方法详解
Jun 25 MySQL
MySQL实现字段分割一行转多行的示例代码
Jul 07 MySQL
MySQL 原理优化之Group By的优化技巧
Aug 14 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获取当前网址及域名的实现代码
2013/06/23 PHP
请离开include_once和require_once
2013/07/18 PHP
php不写闭合标签的好处
2014/03/04 PHP
php5.4以上版本GBK编码下htmlspecialchars输出为空问题解决方法汇总
2015/04/03 PHP
php无限级分类实现方法分析
2016/10/19 PHP
PHP利用超级全局变量$_POST来接收表单数据的实例
2016/11/05 PHP
Zend Framework入门教程之Zend_Config组件用法详解
2016/12/09 PHP
thinkphp3.2同时连接两个数据库的简单方法
2019/08/13 PHP
PHP实现通过二维数组键值获取一维键名操作示例
2019/10/11 PHP
解决windows上php xdebug 无法调试的问题
2020/02/19 PHP
理解Javascript_11_constructor实现原理
2010/10/18 Javascript
jquery插件开发方法(初学者)
2012/02/03 Javascript
解析dom中的children对象数组元素firstChild,lastChild的使用
2013/07/10 Javascript
js判断ie版本号的简单实现代码
2014/03/05 Javascript
js验证真实姓名与身份证号是否匹配
2015/10/13 Javascript
js实现人民币大写金额形式转换
2016/04/27 Javascript
Node.js实现兼容IE789的文件上传进度条
2016/09/02 Javascript
js 将input框中的输入自动转化成半角大写(税号输入框)
2017/02/16 Javascript
Angular.JS中的this指向详解
2017/05/17 Javascript
JavaScript条件判断_动力节点Java学院整理
2017/06/26 Javascript
nodejs 生成和导出 word的实例代码
2018/07/31 NodeJs
vue-cli3.0 脚手架搭建项目的过程详解
2018/10/19 Javascript
[40:29]2018DOTA2亚洲邀请赛 4.7总决赛 LGD vs Mineski 第一场
2018/04/10 DOTA
Django中对通过测试的用户进行限制访问的方法
2015/07/23 Python
python删除特定文件的方法
2015/07/30 Python
python 实时遍历日志文件
2016/04/12 Python
深入理解Python异常处理的哲学
2019/02/01 Python
python中从for循环延申到推导式的具体使用
2019/11/29 Python
Python使用re模块验证危险字符
2020/05/21 Python
pycharm不以pytest方式运行,想要切换回普通模式运行的操作
2020/09/01 Python
详解pandas中利用DataFrame对象的.loc[]、.iloc[]方法抽取数据
2020/12/13 Python
JBL加拿大官方商店:扬声器、耳机等
2020/10/23 全球购物
2014年文学毕业生自我鉴定
2014/04/23 职场文书
小学生保护环境倡议书
2014/05/15 职场文书
酒店人事主管岗位职责
2015/04/11 职场文书
SQL实现LeetCode(175.联合两表)
2021/08/04 MySQL