MySQL 如何限制一张表的记录数


Posted in MySQL onSeptember 14, 2021

关于MySQL 如何限制一张表的记录数,这没有一个简化的答案,比如执行一条命令或者说简单设置一个参数都不能完美解决。接下来我给出一些可选解决方案。

对数据库来讲,一般问题的解决方案无非有两种,一种是在应用端另外一种是在数据库端

首先是在数据库端(假设表硬性限制为1W条记录):

一、触发器解决方案

触发器的思路很简单,每次插入新记录前,检查表记录数是否到达限定数量,数量未到,继续插入;数量达到,先插入一条新记录,再删除最老的记录,或者反着来也行。为了避免每次检测表总记录数全表扫,规划另外一张表,用来做当前表的计数器,插入前,只需查计数器表即可。要实现这个需求,需要两个触发器和一张计数器表。
t1为需要限制记录数的表,t1_count 为计数器表:

mysql:ytt_new>create table t1(id int auto_increment primary key, r1 int);
Query OK, 0 rows affected (0.06 sec)
   
mysql:ytt_new>create table t1_count(cnt smallint unsigned);
Query OK, 0 rows affected (0.04 sec)
   
mysql:ytt_new>insert t1_count set cnt=0;
Query OK, 1 row affected (0.11 sec)

得写两个触发器,一个是插入动作触发:

DELIMITER $$

USE `ytt_new`$$

DROP TRIGGER /*!50032 IF EXISTS */ `tr_t1_insert`$$

CREATE
    /*!50017 DEFINER = 'ytt'@'%' */
    TRIGGER `tr_t1_insert` AFTER INSERT ON `t1` 
    FOR EACH ROW BEGIN
       UPDATE t1_count SET cnt= cnt+1;
    END;
$$

DELIMITER ;

另外一个是删除动作触发:

DELIMITER $$

USE `ytt_new`$$

DROP TRIGGER /*!50032 IF EXISTS */ `tr_t1_delete`$$

CREATE
    /*!50017 DEFINER = 'ytt'@'%' */
    TRIGGER `tr_t1_delete` AFTER DELETE ON `t1` 
    FOR EACH ROW BEGIN
       UPDATE t1_count SET cnt= cnt-1;
    END;
$$

DELIMITER ;

给表t1造1W条数据,达到上限:

mysql:ytt_new>insert t1 (r1) with recursive tmp(a,b) as (select 1,1 union all select a+1,ceil(rand()*20) from tmp where a<10000 ) select b from tmp;
Query OK, 10000 rows affected (0.68 sec)
Records: 10000  Duplicates: 0  Warnings: 0

计数器表 t1_count 记录为1W。

mysql:ytt_new>select cnt from t1_count;
+-------+
| cnt   |
+-------+
| 10000 |
+-------+
1 row in set (0.00 sec)

插入前需要判断计数器表是否到达限制,如果到了这个限制则删除老旧记录先。我写一个存储过程简单理下逻辑:

DELIMITER $$

USE `ytt_new`$$

DROP PROCEDURE IF EXISTS `sp_insert_t1`$$

CREATE DEFINER=`ytt`@`%` PROCEDURE `sp_insert_t1`(
    IN f_r1 INT
    )
BEGIN
      DECLARE v_cnt INT DEFAULT 0;
      SELECT cnt INTO v_cnt FROM t1_count;
      IF v_cnt >=10000 THEN
        DELETE FROM t1 ORDER BY id ASC LIMIT 1;
      END IF;
      INSERT INTO t1(r1) VALUES (f_r1);          
    END$$

DELIMITER ;

此时,调用存储过程即可实现:

mysql:ytt_new>call sp_insert_t1(9999);
Query OK, 1 row affected (0.02 sec)

mysql:ytt_new>select count(*) from t1;
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
1 row in set (0.01 sec)

这个存储过程的处理逻辑也可以继续优化为一次批量处理。 比如每次多缓存一倍的表记录数,判断逻辑变为在2W条以前,只插入新记录,并不删除老记录当到达2W条后,一次性删除旧的1W条记录

这种方案有以下几个缺陷:

  1. 计数器表的记录更新是由insert/delete触发,如果对表进行truncate则计数器表不触发更新从而数据不一致。
  2. 对表进行drop 操作则触发器也跟着删除,需要重建触发器,重置计数器表。
  3. 对表写入只能是类似存储过程这样的单一入口,不能是其他入口。

二、分区表解决方案

建立一个 range 分区,第一个分区有1W条记录,第二个分区为默认分区,等表记录数达到限制后,删除第一个分区,重新调整分区定义即可。

分区表初始定义:

mysql:ytt_new>create table t1(id int auto_increment primary key, r1 int) partition by range(id) (partition p1 values less than(10001), partition p_max values less than(maxvalue));
Query OK, 0 rows affected (0.45 sec)

查找第一个分区是否已满:

mysql:ytt_new>select count(*) from t1 partition(p1);
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
1 row in set (0.00 sec)

删除第一个分区,并且重新调整分区表:

mysql:ytt_new>alter table t1 drop partition p1;
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql:ytt_new>alter table t1 reorganize partition p_max into (partition p1 values less than (20001), partition p_max values less than (maxvalue));
Query OK, 0 rows affected (0.60 sec)
Records: 0  Duplicates: 0  Warnings: 0

这种方法的优势很明显:

  1. 表插入入口可以很随机,INSERT语句、存储过程、导文件都行。
  2. 删除第一个分区是一个DROP操作,非常快。

但也有缺点:表记录不能有空隙,如果有空隙,就得改变分区表定义。比如把分区p1的最大值改为20001,那即使在这个分区里有一半的记录不连续,也不影响检索分区里的总记录数。

三、通用表空间解决方案

提前计算好这张表1W条记录需要多少磁盘空间,之后在磁盘上划分一个区专门来存放这张表的数据。
挂载划好的分区,添加为 InnoDB 表空间的备选目录(/tmp/mysql/)。

mysql:ytt_new>create tablespace ts1 add datafile '/tmp/mysql/ts1.ibd' engine innodb;
Query OK, 0 rows affected (0.11 sec)
mysql:ytt_new>alter table t1 tablespace ts1;
Query OK, 0 rows affected (0.12 sec)
Records: 0  Duplicates: 0  Warnings: 0

我大致算了下,不是很准确,所以记录上可能有点误差,不过意思已经很明确:等表报 “TABLE IS FULL” 后即可。

mysql:ytt_new>insert t1 (r1) values (200);
ERROR 1114 (HY000): The table 't1' is full

mysql:ytt_new>select count(*) from t1;
+----------+
| count(*) |
+----------+
|    10384 |
+----------+
1 row in set (0.20 sec)

表满后移除表空间,清空表,再插入新记录

mysql:ytt_new>alter table t1 tablespace innodb_file_per_table;
Query OK, 0 rows affected (0.18 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql:ytt_new>drop tablespace ts1;
Query OK, 0 rows affected (0.13 sec)

mysql:ytt_new>truncate table t1;
Query OK, 0 rows affected (0.04 sec)

另外一个就是在应用端处理:

可以提前在应用端缓存表数据,达到限定的记录数后再批量写入数据库端,写入数据库前,先清空表即可。
举个例子: 表t1数据缓存到文件t1.csv,当t1.csv到达1W行时,数据库端清空表数据,导入t1.csv

结语:

之前 MySQL 在 MyISAM 时代,表属性 max_rows 来预估表的记录数,但也不是硬性规定,类似我上面写的使用通用表空间来达到限制表记录数的作用;到了 InnoDB 时代就没有一个直观的方法,更多是靠以上列出来的方法来解决这个问题,具体选哪个方案,还是得看需求。

到此这篇关于MySQL 如何限制一张表的记录数的文章就介绍到这了,更多相关MySQL 限制一张表的记录数内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
MySQL令人咋舌的隐式转换
Apr 05 MySQL
虚拟机linux端mysql数据库无法远程访问的解决办法
May 26 MySQL
mysql定时自动备份数据库的方法步骤
Jul 07 MySQL
MySql子查询IN的执行和优化的实现
Aug 02 MySQL
浅谈MySQL之select优化方案
Aug 07 MySQL
MySQL中的引号和反引号的区别与用法详解
Oct 24 MySQL
mysql主从复制的实现步骤
Oct 24 MySQL
如何避免mysql启动时错误及sock文件作用分析
Jan 22 MySQL
MySQL高级进阶sql语句总结大全
Mar 16 MySQL
如何创建一个创建MySQL数据库中的datetime类型
Mar 21 MySQL
MySQL transaction事务安全示例讲解
Jun 21 MySQL
Mysql如何查看是否使用到索引
Dec 24 MySQL
MySQL into_Mysql中replace与replace into用法案例详解
Sep 14 #MySQL
MYSQL 的10大经典优化案例场景实战
Sep 14 #MySQL
MySQL中连接查询和子查询的问题
mysql配置SSL证书登录的实现
MySQL约束超详解
Sep 04 #MySQL
MySQL中的隐藏列的具体查看
Sep 04 #MySQL
Mysql实现简易版搜索引擎的示例代码
Aug 30 #MySQL
You might like
在WIN98下以apache模块方式安装php
2006/10/09 PHP
深入理解require与require_once与include以及include_once的区别
2013/06/05 PHP
如何使用php判断服务器是否是HTTPS连接
2013/07/05 PHP
PHP7.0版本备注
2015/07/23 PHP
js中页面的重新加载(当前页面/上级页面)及frame或iframe元素引用介绍
2013/01/24 Javascript
javascript中的toFixed固定小数位数 简单实例分享
2013/07/12 Javascript
JavaScript学习笔记之JS函数
2015/01/22 Javascript
JS仿iGoogle自定义首页模块拖拽特效的方法
2015/02/13 Javascript
开启BootStrap学习之旅
2016/05/04 Javascript
jQuery+ajax实现实用的点赞插件代码
2016/07/06 Javascript
AngularJS 依赖注入详解及示例代码
2016/08/17 Javascript
jQuery简单实现中间浮窗效果
2016/09/04 Javascript
用JavaScript和jQuery实现瀑布流
2017/03/19 Javascript
动态加载JavaScript文件的3种方式
2018/05/05 Javascript
以v-model与promise两种方式实现vue弹窗组件
2018/05/21 Javascript
JS实现移动端点击按钮复制文本内容
2019/07/28 Javascript
layui 选择列表,打勾,点击确定返回数据的例子
2019/09/02 Javascript
修改layui的后台模板的左侧导航栏可以伸缩的方法
2019/09/10 Javascript
详解Python map函数及Python map()函数的用法
2017/11/16 Python
PyQt5 对图片进行缩放的实例
2019/06/18 Python
python使用openCV遍历文件夹里所有视频文件并保存成图片
2020/01/14 Python
总结html5自定义属性有哪些
2020/04/01 HTML / CSS
爱尔兰电子产品购物网站:Komplett.ie
2018/04/04 全球购物
FLIR美国官网:热成像, 夜视和红外摄像系统
2018/07/13 全球购物
WatchShop法国:英国排名第一的独立手表零售商
2020/02/17 全球购物
英国最大的在线照明商店:Litecraft
2020/08/31 全球购物
高二历史教学反思
2014/01/25 职场文书
运动会入场词60字
2014/02/15 职场文书
学校感恩教育活动总结
2014/07/07 职场文书
2014年基层党支部工作总结
2014/12/04 职场文书
仓管员岗位职责
2015/02/03 职场文书
大学军训口号大全
2015/12/24 职场文书
SQLServer权限之只开启创建表权限
2022/04/12 SQL Server
SQL Server使用T-SQL语句批处理
2022/05/20 SQL Server
浅谈MySql update会锁定哪些范围的数据
2022/06/25 MySQL
Win10多屏显示如何设置?Win10电脑多屏显示设置操作方法
2022/07/07 数码科技