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

目录
  • 一、触发器解决方案
  • 二、分区表解决方案
  • 三、通用表空间解决方案

关于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 限制一张表的记录数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MYSQL速度慢的问题 记录数据库语句

    需要开启一个慢查询输出的一个机关:log_slow_queries.可以在MySQL配置文件中(my.ini/my.cnf)中设置,也可以通过MySQL客户端临时设置.第二种方法的好处是,可以不用重启MySQL服务,而使设置生效.那就来试试这个: 首先通过客户端连接到Mysql服务器,然后输入下面的语句: SET GLOBAL log_slow_queries = ON; SET GLOBAL long_query_time = 3; 这样MySQL就会把耗时>=3秒的SQL语句给记录下来,并输

  • mysql实现查询最接近的记录数据示例

    本文实例讲述了mysql实现查询最接近的记录数据.分享给大家供大家参考,具体如下: 查询场景:现在的需求是查询年龄最接近20岁的用户,获取前5个 我现在的数据库记录用户年龄的字段记录格式是"1995-05-20",字段名称birthday 解决思路: 1.首先查询时转换成用户年龄 日期格式转年龄的方法: (1)当前年份 - 日期格式中的年份 date_format(now(), '%Y') - from_unixtime(unix_timestamp(birthday), '%Y')

  • MySQL分区表的局限和限制详解

    禁止构建 分区表达式不支持以下几种构建: 存储过程,存储函数,UDFS或者插件 声明变量或者用户变量 可以参考分区不支持的SQL函数 算术和逻辑运算符 分区表达式支持+,-,*算术运算,但是不支持DIV和/运算(还存在,可以查看Bug #30188, Bug #33182).但是,结果必须是整形或者NULL(线性分区键除外,想了解更多信息,可以查看分区类型). 分区表达式不支持位运算:|,&,^,<<,>>,~ . HANDLER语句 在MySQL 5.7.1之前的分区表不

  • MySQL通过触发器解决数据库中表的行数限制详解及实例

    MySQL通过触发器解决数据库中表的行数限制详解及实例 最近项目一个需求是对操作日志的数量限制为10万条,超过十万条便删除最旧的那一条,保存数据库中日志数量不超过10万. 当时我的第一想法是通过触发器来做,便在数据库中执行了如下的SQL: delimiter $ create trigger limitLog before insert on OperationLog for each row begin if (select count(*) from OperationLog) > 1000

  • MySQL数据库查看数据表占用空间大小和记录数的方法

    如果想知道MySQL数据库中每个表占用的空间.表记录的行数的话,可以打开MySQL的 information_schema 数据库.在该库中有一个 TABLES 表,这个表主要字段分别是: TABLE_SCHEMA : 数据库名 TABLE_NAME:表名 ENGINE:所使用的存储引擎 TABLES_ROWS:记录数 DATA_LENGTH:数据大小 INDEX_LENGTH:索引大小 其他字段请参考MySQL的手册,这几个字段对我们来说最有用. 一个表占用空间的大小,相当于是 数据大小 +

  • PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法

    本文实例讲述了PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法.分享给大家供大家参考,具体如下: 这是一段简单的代码,可实现统计该数据库中每个表的记录数,并按递减顺序排列的功能 $host = '127.0.0.1'; $port = 3306; $dbname = 'test'; $username = 'root'; $password = ''; function ee($p) { if(PHP_SAPI == 'cli') { echo "\n"; }else{

  • 详解MySQL恢复psc文件记录数为0的解决方案

    psc文件是用Navicat工具生成的备份文件,只能用Navicat工具进行恢复. (一)错误出现步骤 1 用Navicat连接MySQL 2 新建一个数据库,名字随便取,比如mydb1,字符集选utf-8(因为备份文件用的就是utf-8) 3 选择mydb1数据库-->备份-->还原备份-->选择psc文件,开始还原 4 还原结束后,发现673个对象只恢复了551个,47366条记录恢复了0条.这说明这个备份文件是有问题的. (二)解决方案 1 重新还原,在"高级"

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

    目录 一.触发器解决方案 二.分区表解决方案 三.通用表空间解决方案 关于MySQL 如何限制一张表的记录数,这没有一个简化的答案,比如执行一条命令或者说简单设置一个参数都不能完美解决.接下来我给出一些可选解决方案. 对数据库来讲,一般问题的解决方案无非有两种,一种是在应用端:另外一种是在数据库端. 首先是在数据库端(假设表硬性限制为1W条记录): 一.触发器解决方案 触发器的思路很简单,每次插入新记录前,检查表记录数是否到达限定数量,数量未到,继续插入:数量达到,先插入一条新记录,再删除最老的

  • Mysql获取id最大值、表的记录总数等相关问题的方法汇总

    一.mysql 获取当前字段最大id SQL语句: select max(id) from yourtable; 二.获取mysql表自增(Auto_increment)值 Auto_increment是表中的一个属性,只要把表的状态获取到,也就可以获取到那个自增值 SQL语句: show table status like "表名"; php代码实现 $get_table_status_sql = "SHOW TABLE STATUS LIKE '表名'"; $r

  • SQL小技巧 又快又简单的得到你的数据库每个表的记录数

    但是如何得到某个数据库所有的表的记录数,你要是用上面的方法估计得累死了.呵呵 下面提供如何借用sysindexes和sysobjects表来得到某个数据库每个表记录数的方法: 先给出SQL Server 2000版本的: 复制代码 代码如下: SELECT o.NAME, i.rowcnt FROM sysindexes AS i INNER JOIN sysobjects AS o ON i.id = o.id WHERE i.indid < 2 AND OBJECTPROPERTY(o.id

  • MySQL如何实现两张表取差集

    目录 MySQL两张表取差集 求差集 SQL 脚本 MySQL查两个表之间的数据差集 SQL LEFT JOIN 关键字 总结 MySQL两张表取差集 业务场景如下: 人员表中有证件号.手机号字段,这两个字段因为涉及到个人隐私问题,因此加密存储,有另外一张解密表可以和人员表进行关联,查出解密后的证件号和手机号,现在需要统计出人员表中有多少没有加密的数据,进行再次加密处理,这个时候,考虑到的就是求两张表的差集,来找出未加密的人员. 求差集 SQL 脚本 select p.* from person

  • 关于关系数据库如何快速查询表的记录数详解

    前言 在数据库中,很多人员习惯使用SELECT COUNT(*) .SELECT COUNT(1) .SELECT COUNT(COL)来查询一个表有多少记录,对于小表,这种SQL的开销倒不是很大,但是对于大表,这种查询表记录数的做法就是一个非常消耗资源了,而且效率很差.下面介绍一下SQL Server. Oracle.MySQL中如何快速获取表的记录数. SQL SERVER 数据库 在SQL Server数据库中, 对象目录视图sys.partitions中有一个字段rows会记录表的记录数

  • MySQL多表数据记录查询详解

    在实际应用中,经常需要实现在一个查询语句中显示多张表的数据,这就是所谓的多表数据记录连接查询,简称来年将诶查询. 在具体实现连接查询操作时,首先将两个或两个以上的表按照某个条件连接起来,然后再查询到所要求的数据记录.连接查询分为内连接查询和外连接查询. 在具体应用中,如果需要实现多表数据记录查询,一般不使用连接查询,因为该操作效率比较低.于是MySQL又提供 了连接查询的替代操作,子查询操作. 1.关系数据操作:  在连接查询中,首先需要对两张或两张以上的表进行连接操作.连接操作是关系数据操作中

  • 解析如何查看Oracle数据库中某张表的字段个数

    Oracle中查询某个表的总字段数,要用SQL语句,或者在PL/SQL里面 复制代码 代码如下: select count(column_name) from user_tab_columns where table_name='T_B_AUDITOR' 能够查出来指定的那张表的字段数.下面是通过大致查看:select   tname,count(*)   from   col   group   by   tname; 复制代码 代码如下: 64 T_A_BOOKSTAGEINFO 465 T

  • 一个Shell小脚本精准统计Mysql每张表的行数实现

    前言 对于开发或者运维人员来说,Mysql数据库每张表的数量肯定是要了解下,有助于我们清理无用数据或者了解哪张表比较占用空间. 另外多次统计表的行数,还能发现Mysql表的增量情况,能够预测表未来会有多大的量. 废话不多说,直接带大家写一个简单的Shell小脚本 循环获取数据库名 直接上Shell代码,show databases获取所有的库名.结果有一个我们不想要的,就是Database,这个grep -v掉,轻松获取所有数据库 [root@shijiangeit ~]# mysql -h 1

  • MySQL数据库查询之多表查询总结

    目录 多表关系 表与表之间的联系: 一对多(多对一) 多对多 一对一 多表查询 多表查询的分类 1.连接查询: 2.子查询 内连接 外连接 自连接 联合查询 子查询 标量子查询 列子查询 行子查询 表子查询 多表查询案例 总结 多表关系 在进行数据库表结构的设计时,会根据业务的需求和业务模块之间的关系,分析设计表结构,由于业务之间相互关联,所以各个表结构之间也存在各种联系 表与表之间的联系: 1.一对多(多对一) 2.多对多 3.一对一 一对多(多对一) 例如,一个员工对应一个部门,一个部门可以

随机推荐