MySQL 表数据分区,每10000条数据自动分区

MySQL 表数据分区,每10000条数据自动分区,第1张

ql代码

#这里使用HASH表分区,mysql会根据HASH字段来自动分配数据到不同的表分区,这种情况适用于没有表分区规则但是有需要分表来进行查询优化的情况。这里根据id字段hash规则创建2个表分区

CREATE TABLE `creater_bak` (

`id` int(11) NOT NULL,

`name` varchar(100) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

PARTITION BY HASH(id) PARTITIONS 2

创建完成后开始导入原表数据:

Sql代码

insert into creater_bak select * from creater

导入以后的新表数据就是分布在不同的2个表分区中了。

如果数据量非常大,觉得预设的表分区数量太少,那么可以新增表分区,mysql会自动重新分配:

Sql代码

#这里新增8个表分区,加上新建表时候的2个,一共10个表分区了

ALTER TABLE `creater_bak` ADD PARTITION PA

在大型的企业应用或企业级的数据库应用中 要处理的数据量通常可以达到几十到几百GB 有的甚至可以到TB级 虽然存储介质和数据处理技术的发展也很快 但是仍然不能满足用户的需求 为了使用户的大量的数据在读写 *** 作和查询中速度更快 Oracle提供了对表和索引进行分区的技术 以改善大型应用系统的性能

使用分区的优点

·增强可用性 如果表的某个分区出现故障 表在其他分区的数据仍然可用

·维护方便 如果表的某个分区出现故障 需要修复数据 只修复该分区即可

·均衡I/O 可以把不同的分区映射到磁盘以平衡I/O 改善整个系统性能

·改善查询性能 对分区对象的查询可以仅搜索自己关心的分区 提高检索速度

Oracle数据库提供对表或索引的分区方法有三种

·范围分区

·Hash分区(散列分区)

·复合分区

下面将以实例的方式分别对这三种分区方法来说明分区表的使用 为了测试方便 我们先建三个表空间

以下为引用的内容

create tablespace dinya_space

datafile /test/demo/oracle/demodata/dinya dnf size M

create tablespace dinya_space

datafile /test/demo/oracle/demodata/dinya dnf size M

create tablespace dinya_space

datafile /test/demo/oracle/demodata/dinya dnf size M

分区表的创建

范围分区

范围分区就是对数据表中的某个值的范围进行分区 根据某个值的范围 决定将该数据存储在哪个分区上 如根据序号分区 根据业务记录的创建日期进行分区等

需求描述 有一个物料交易表 表名 material_transactions 该表将来可能有千万级的数据记录数 要求在建该表的时候使用分区表 这时候我们可以使用序号分区三个区 每个区中预计存储三千万的数据 也可以使用日期分区 如每五年的数据存储在一个分区上

根据交易记录的序号分区建表 以下为引用的内容

SQL>create table dinya_test

(

transaction_id number primary key

item_id number( ) not null

item_description varchar ( )

transaction_date date not null

)

partition by range (transaction_id)

(

partition part_ values less than( ) tablespace dinya_space

partition part_ values less than( ) tablespace dinya_space

partition part_ values less than(maxvalue) tablespace dinya_space

)

Table created

建表成功 根据交易的序号 交易ID在三千万以下的记录将存储在第一个表空间dinya_space 中 分区名为:par_ 在三千万到六千万之间的记录存储在第二个表空间

dinya_space 中 分区名为 par_ 而交易ID在六千万以上的记录存储在第三个表空间dinya_space 中 分区名为par_

根据交易日期分区建表

以下为引用的内容

SQL>create table dinya_test

(

transaction_id number primary key

item_id number( ) not null

item_description varchar ( )

transaction_date date not null

)

partition by range (transaction_date)

(

partition part_ values less than(to_date( yyyy mm dd ))

tablespace dinya_space

partition part_ values less than(to_date( yyyy mm dd ))

tablespace dinya_space

partition part_ values less than(maxvalue) tablespace dinya_space

)

Table created

这样我们就分别建了以交易序号和交易日期来分区的分区表 每次插入数据的时候 系统将根据指定的字段的值来自动将记录存储到制定的分区(表空间)中

当然 我们还可以根据需求 使用两个字段的范围分布来分区 如partition

by range ( transaction_id transaction_date)

分区条件中的值也做相应的改变 请读者自行测试

Hash分区(散列分区)

散列分区为通过指定分区编号来均匀分布数据的一种分区类型 因为通过在I/O设备上进行散列分区 使得这些分区大小一致 如将物料交易表的数据根据交易ID散列地存放在指定的三个表空间中

以下为引用的内容

SQL>create table dinya_test

(

transaction_id number primary key

item_id number( ) not null

item_description varchar ( )

transaction_date date

)

partition by hash(transaction_id)

(

partition part_ tablespace dinya_space

partition part_ tablespace dinya_space

partition part_ tablespace dinya_space

)

Table created

建表成功 此时插入数据 系统将按transaction_id将记录散列地插入三个分区中 这里也就是三个不同的表空间中

复合分区

有时候我们需要根据范围分区后 每个分区内的数据再散列地分布在几个表空间中 这样我们就要使用复合分区 复合分区是先使用范围分区 然后在每个分区内再使用散列分区的一种分区方法 如将物料交易的记录按时间分区 然后每个分区中的数据分三个子分区 将数据散列地存储在三个指定的表空间中

以下为引用的内容

SQL>create table dinya_test

(

transaction_id number primary key

item_id number( ) not null

item_description varchar ( )

transaction_date date

)

partition by range(transaction_date)subpartition by hash(transaction_id)

subpartitions store in (dinya_space dinya_space dinya_space )

(

partition part_ values less than(to_date( yyyy mm dd ))

partition part_ values less than(to_date( yyyy mm dd ))

partition part_ values less than(maxvalue)

)

Table created

该例中 先是根据交易日期进行范围分区 然后根据交易的ID将记录散列地存储在三个表空间中

分区表 *** 作

以上了解了三种分区表的建表方法 下面将使用实际的数据并针对按日期的范围分区来测试分区表的数据记录的 *** 作

插入记录

以下为引用的内容

SQL>insert into dinya_test values( BOOKS sysdate)

row created

SQL>insert into dinya_test values( BOOKS sysdate+ )

row created

SQL>insert into dinya_test values( BOOKS to_date( yyyy mm dd ))

row created

SQL>insert into dinya_test values( BOOKS to_date( yyyy mm dd ))

row created

SQL>insert into dinya_test values( BOOKS to_date( yyyy mm dd ))

row created

SQL>insert into dinya_test values( BOOKS to_date( yyyy mm dd ))

row created

SQL>mit

Commit plete

SQL>

按上面的建表结果 年前的数据将存储在第一个分区part_ 上 而 年到 年的交易数据将存储在第二个分区part_ 上 年以后的记录存储在第三个分区part_ 上

查询分区表记录 以下为引用的内容

SQL>select * from dinya_test partition(part_ )

TRANSACTION_ID ITEM_ID ITEM_DESCRIPTION TRANSACTION_DATE

BOOKS : :

BOOKS : :

SQL>

SQL>select * from dinya_test partition(part_ )

TRANSACTION_ID ITEM_ID ITEM_DESCRIPTION TRANSACTION_DATE

BOOKS

BOOKS

SQL>

SQL>select * from dinya_test partition(part_ )

TRANSACTION_ID ITEM_ID ITEM_DESCRIPTION TRANSACTION_DATE

BOOKS

BOOKS

SQL>

从查询的结果可以看出 插入的数据已经根据交易时间范围存储在不同的分区中 这里是指定了分区的查询 当然也可以不指定分区 直接执行select * from dinya_test查询全部记录

在也检索的数据量很大的时候 指定分区会大大提高检索速度

更新分区表的记录

以下为引用的内容

SQL>update dinya_test partition(part_ ) t set em_description= DESK where

t transaction_id=

row updated

SQL>mit

Commit plete

SQL>

这里将第一个分区中的交易ID= 的记录中的item_description字段更新为 DESK 可以看到已经成功更新了一条记录 但是当更新的时候指定了分区 而根据查询的记录不在该分区中时 将不会更新数据 请看下面的例子 以下为引用的内容

SQL>update dinya_test partition(part_ ) t set em_description= DESK where

t transaction_id=

rows updated

SQL>mit

Commit plete

SQL>

指定了在第一个分区中更新记录 但是条件中限制交易ID为 而查询全表 交易ID为 的记录在第三个分区中 这样该条语句将不会更新记录

删除分区表记录

以下为引用的内容

SQL>delete from dinya_test partition(part_ ) t where t transaction_id=

row deleted

SQL>mit

Commit plete

SQL>

上面例子删除了第二个分区part_ 中的交易记录ID为 的一条记录 和更新数据相同 如果指定了分区 而条件中的数据又不在该分区中时 将不会删除任何数据

分区表索引的使用

分区表和一般表一样可以建立索引 分区表可以创建局部索引和全局索引 当分区中出现许多事务并且要保证所有分区中的数据记录的唯一性时采用全局索引

局部索引分区的建立

以下为引用的内容

SQL>create index dinya_idx_t on dinya_test(item_id)

local

(

partition idx_ tablespace dinya_space

partition idx_ tablespace dinya_space

partition idx_ tablespace dinya_space

)

Index created

SQL>

看查询的执行计划 从下面的执行计划可以看出 系统已经使用了索引

以下为引用的内容

SQL>select * from dinya_test partition(part_ ) t where em_id=

Execution Plan

SELECT STATEMENT Optimizer=CHOOSE (Cost= Card= Bytes= )

TABLE ACCESS (BY LOCAL INDEX ROWID) OF DINYA_TEST (Cost=

Card= Bytes= )

INDEX (RANGE SCAN) OF DINYA_IDX_T (NON UNIQUE) (Cost=

Card= )

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

SQL>

全局索引分区的建立

全局索引建立时global 子句允许指定索引的范围值 这个范围值为索引字段的范围值

以下为引用的内容

SQL>create index dinya_idx_t on dinya_test(item_id)

global partition by range(item_id)

(

partition idx_ values less than ( ) tablespace dinya_space

partition idx_ values less than ( ) tablespace dinya_space

partition idx_ values less than (maxvalue) tablespace dinya_space

)

Index created

SQL>

本例中对表的item_id字段建立索引分区 当然也可以不指定索引分区名直接对整个表建立索引 如

以下为引用的内容

SQL>create index dinya_idx_t on dinya_test(item_id)

Index created

SQL>

同样的 对全局索引根据执行计划可以看出索引已经可以使用

以下为引用的内容

SQL>select * from dinya_test t where em_id=

Execution Plan

SELECT STATEMENT Optimizer=CHOOSE (Cost= Card= Bytes= )

TABLE ACCESS (BY GLOBAL INDEX ROWID) OF DINYA_TEST (Cost

= Card= Bytes= )

INDEX (RANGE SCAN) OF DINYA_IDX_T (NON UNIQUE) (Cost=

Card= )

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

SQL>

分区表的维护

了解了分区表的建立 索引的建立 表和索引的使用后 在应用的还要经常对分区进行维护和管理 日常维护和管理的内容包括 增加一个分区 合并一个分区及删除分区等等 下面以范围分区为例说明增加 合并 删除分区的一般 *** 作

增加一个分区:

以下为引用的内容

SQL>alter table dinya_test

add partition part_ values less than(to_date( yyyy mm dd ))

tablespace dinya_spa

ce

Table altered

SQL>

增加一个分区的时候 增加的分区的条件必须大于现有分区的最大值 否则系统将提示ORA partition bound must collate higher than that of the last partition 错误

合并一个分区

以下为引用的内容

SQL>alter table dinya_test merge partitions part_ part_ into partition part_

Table altered

SQL>

在本例中将原有的表的part_ 分区和part_ 分区进行了合并 合并后的分区为part_ 如果在合并的时候把合并后的分区定为part_ 的时候 系统将提示ORA cannot reuse lower bound partition as resulting partition 错误

删除分区

以下为引用的内容

SQL>alter table dinya_test drop partition part_

Table altered

SQL>

删除分区表的一个分区后 查询该表的数据时显示 该分区中的数据已全部丢失 所以执行删除分区动作时要慎重 确保先备份数据后再执行 或将分区合并

总结

lishixinzhi/Article/program/Oracle/201311/17329

分表是分散数据库压力的好方法。

分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库。

当然,首先要知道什么情况下,才需要分表。个人觉得单表记录条数达到百万到千万级别时就要使用分表了。

分表的分类

**1、纵向分表**

将本来可以在同一个表的内容,人为划分为多个表。(所谓的本来,是指按照关系型数据库的第三范式要求,是应该在同一个表的。)

分表理由:根据数据的活跃度进行分离,(因为不同活跃的数据,处理方式是不同的)

案例:

对于一个博客系统,文章标题,作者,分类,创建时间等,是变化频率慢,查询次数多,而且最好有很好的实时性的数据,我们把它叫做冷数据。而博客的浏览量,回复数等,类似的统计信息,或者别的变化频率比较高的数据,我们把它叫做活跃数据。所以,在进行数据库结构设计的时候,就应该考虑分表,首先是纵向分表的处理。

这样纵向分表后:

首先存储引擎的使用不同,冷数据使用MyIsam 可以有更好的查询数据。活跃数据,可以使用Innodb ,可以有更好的更新速度。

其次,对冷数据进行更多的从库配置,因为更多的 *** 作时查询,这样来加快查询速度。对热数据,可以相对有更多的主库的横向分表处理。

其实,对于一些特殊的活跃数据,也可以考虑使用memcache ,redis之类的缓存,等累计到一定量再去更新数据库。或者mongodb 一类的nosql 数据库,这里只是举例,就先不说这个。

**2、横向分表**

字面意思,就可以看出来,是把大的表结构,横向切割为同样结构的不同表,如,用户信息表,user_1,user_2等。表结构是完全一样,但是,根据某些特定的规则来划分的表,如根据用户ID来取模划分。

分表理由:根据数据量的规模来划分,保证单表的容量不会太大,从而来保证单表的查询等处理能力。

案例:同上面的例子,博客系统。当博客的量达到很大时候,就应该采取横向分割来降低每个单表的压力,来提升性能。例如博客的冷数据表,假如分为100个表,当同时有100万个用户在浏览时,如果是单表的话,会进行100万次请求,而现在分表后,就可能是每个表进行1万个数据的请求(因为,不可能绝对的平均,只是假设),这样压力就降低了很多很多。

延伸:为什么要分表和分区?

日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。

什么是分表?

分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表,我们可以称为子表,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些子表可以分布在同一块磁盘上,也可以在不同的机器上。app读写的时候根据事先定义好的规则得到对应的子表名,然后去 *** 作它。

什么是分区?

分区和分表相似,都是按照规则分解表。不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。分区后,表面上还是一张表,但数据散列到多个位置了。app读写的时候 *** 作的还是大表名字,db自动去组织分区的数据。

**MySQL分表和分区有什么联系呢?**

1、都能提高mysql的性高,在高并发状态下都有一个良好的表现。

2、分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式(如果merge这种分表方式,不能和分区配合的话,可以用其他的分表试),访问量不大,但是表数据很多的表,我们可以采取分区的方式等。

3、分表技术是比较麻烦的,需要手动去创建子表,app服务端读写时候需要计算子表名。采用merge好一些,但也要创建子表和配置子表间的union关系。

4、表分区相对于分表, *** 作方便,不需要创建子表。

我们知道对于大型的互联网应用,数据库单表的数据量可能达到千万甚至上亿级别,同时面临这高并发的压力。Master-Slave结构只能对数据库的读能力进行扩展,写 *** 作还是集中在Master中,Master并不能无限制的挂接Slave库,如果需要对数据库的吞吐能力进行进一步的扩展,可以考虑采用分库分表的策略。

**1、分表**

在分表之前,首先要选中合适的分表策略(以哪个字典为分表字段,需要将数据分为多少张表),使数据能够均衡的分布在多张表中,并且不影响正常的查询。在企业级应用中,往往使用org_id(组织主键)做为分表字段,在互联网应用中往往是userid。在确定分表策略后,当数据进行存储及查询时,需要确定到哪张表里去查找数据,

数据存放的数据表 = 分表字段的内容 % 分表数量

**2、分库**

分表能够解决单表数据量过大带来的查询效率下降的问题,但是不能给数据库的并发访问带来质的提升,面对高并发的写访问,当Master无法承担高并发的写入请求时,不管如何扩展Slave服务器,都没有意义了。我们通过对数据库进行拆分,来提高数据库的写入能力,即所谓的分库。分库采用对关键字取模的方式,对数据库进行路由。

数据存放的数据库=分库字段的内容%数据库的数量

**3、即分表又分库**

数据库分表可以解决单表海量数据的查询性能问题,分库可以解决单台数据库的并发访问压力问题。

当数据库同时面临海量数据存储和高并发访问的时候,需要同时采取分表和分库策略。一般分表分库策略如下:

中间变量 = 关键字%(数据库数量*单库数据表数量)

库 = 取整(中间变量/单库数据表数量)

表 = (中间变量%单库数据表数量)

实例:

1、分库分表

很明显,一个主表(也就是很重要的表,例如用户表)无限制的增长势必严重影响性能,分库与分表是一个很不错的解决途径,也就是性能优化途径,现在的案例是我们有一个1000多万条记录的用户表members,查询起来非常之慢,同事的做法是将其散列到100个表中,分别从members0到members99,然后根据mid分发记录到这些表中,牛逼的代码大概是这样子:

复制代码 代码如下:

<?php

for($i=0$i<100$i++ ){

//echo "CREATE TABLE db2.members{$i} LIKE db1.members

"

echo "INSERT INTO members{$i} SELECT * FROM members WHERE mid%100={$i}

"

}

?>

2、不停机修改mysql表结构

同样还是members表,前期设计的表结构不尽合理,随着数据库不断运行,其冗余数据也是增长巨大,同事使用了下面的方法来处理:

先创建一个临时表:

/*创建临时表*/

CREATE TABLE members_tmp LIKE members

然后修改members_tmp的表结构为新结构,接着使用上面那个for循环来导出数据,因为1000万的数据一次性导出是不对的,mid是主键,一个区间一个区间的导,基本是一次导出5万条吧,这里略去了

接着重命名将新表替换上去:

/*这是个颇为经典的语句哈*/

RENAME TABLE members TO members_bak,members_tmp TO members

就是这样,基本可以做到无损失,无需停机更新表结构,但实际上RENAME期间表是被锁死的,所以选择在线少的时候 *** 作是一个技巧。经过这个 *** 作,使得原先8G多的表,一下子变成了2G多。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/6669149.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-03-26
下一篇 2023-03-26

发表评论

登录后才能评论

评论列表(0条)

保存