文章目录
一、数据库锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。
根据对数据操作的粒度,分为表锁、行锁、间隙锁。
1.表锁
更偏读。
偏向MyISAM存储引擎,开销小,加锁快;
无死锁,因为整个表都被锁了,别人不能访问;
锁定粒度大,发送锁冲突的概率最高,并发度低。
表锁举例:
创建表并插入数据:
create table mylock(
id int not null primary key auto_increment,
name varchar(20)
)engine myisam;
insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');
打印
Query OK, 0 rows affected (0.01 sec)
Query OK, 1 row affected (0.01 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
手动增加表锁:
语法:
lock table 表名字 read(write),表名字2 read(write);
lock table mylock read,book write;
打印
Query OK, 0 rows affected (0.01 sec)
查看所有数据库的所有表,同时可以查看创建的锁:
show open tables;
打印
+--------------------+------------------------------------------------------+--------+-------------+
| Database | Table | In_use | Name_locked |
+--------------------+------------------------------------------------------+--------+-------------+
| performance_schema | events_waits_summary_by_user_by_event_name | 0 | 0 |
| performance_schema | events_waits_summary_global_by_event_name | 0 | 0 |
| performance_schema | events_transactions_summary_global_by_event_name | 0 | 0 |
| performance_schema | replication_connection_status | 0 | 0 |
| mysql | time_zone_leap_second | 0 | 0 |
| mysql | columns_priv | 0 | 0 |
| performance_schema | metadata_locks | 0 | 0 |
| performance_schema | status_by_user | 0 | 0 |
| demo | mylock | 1 | 0 |
| mysql | time_zone_transition_type | 0 | 0 |
| performance_schema | events_statements_current | 0 | 0 |
| performance_schema | prepared_statements_instances | 0 | 0 |
| performance_schema | events_statements_history_long | 0 | 0 |
| mysql | db | 0 | 0 |
| performance_schema | file_instances | 0 | 0 |
| performance_schema | events_stages_summary_by_user_by_event_name | 0 | 0 |
| performance_schema | memory_summary_by_thread_by_event_name | 0 | 0 |
...
| performance_schema | events_stages_history | 0 | 0 |
| mysql | time_zone | 0 | 0 |
| mysql | slave_master_info | 0 | 0 |
| mysql | proxies_priv | 0 | 0 |
| performance_schema | events_statements_summary_by_host_by_event_name | 0 | 0 |
| demo | book | 1 | 0 |
| performance_schema | socket_instances | 0 | 0 |
| performance_schema | host_cache | 0 | 0 |
| performance_schema | status_by_account | 0 | 0 |
| performance_schema | events_statements_summary_by_account_by_event_name | 0 | 0 |
| performance_schema | memory_summary_by_account_by_event_name | 0 | 0 |
...
+--------------------+------------------------------------------------------+--------+-------------+
110 rows in set (0.00 sec)
显然,mylock表和book表的In_use值为1.
此时在同一命令行中读取
select * from mylock;
打印
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
此时再打开一个命令行,执行相同的命令:
select * from mylock;
打印
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
在命令行一中更新数据
update mylock set name = 'a2' where id = 1;
打印
ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
即读锁限制了对表的更新。
在命令行二中执行相同命令
update mylock set name = 'a2' where id = 1;
会发生堵塞,直到命令行一中解锁
unlock tables;
打印
Query OK, 0 rows affected (0.00 sec)
命令行二中打印
Query OK, 0 rows affected (57.79 sec)
Rows matched: 1 Changed: 0 Warnings: 0
即堵塞了57.79秒。
在命令行一中锁定mylock表之后,再在命令行二中查询其他表能正常查询,但是在命令行一中不能再查询其他表,否则会报错,要想读其他表,必须先对mylock解锁。
并且在命令行一关闭后,对mylock的锁自动关闭,其他命令行可对mylock表进行操作。
select * from book;
打印
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
在命令行一中加写锁
lock table mylock write;
打印
Query OK, 0 rows affected (0.00 sec)
查询数据
select * from mylock;
打印
+----+------+
| id | name |
+----+------+
| 1 | a2 |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
能正常查询数据。
在命令行二中执行相同命令:
select * from mylock;
发生堵塞。
在命令行二查询其他表能正常查询到。
在命令行一中更新数据
update mylock set name = 'a4' where id = 1;
打印
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
在命令行二中执行相同操作
update mylock set name = 'a4' where id = 1;
也会发生堵塞,直到命令行一释放写锁。
释放表锁:
语法:
unlock tables;
表锁总结:
MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁:
- 对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其他进行的写操作。即读锁会堵塞写,但是不会堵塞读。
- 对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作。即写锁会堵塞写和读。
show status like 'table%';
打印
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Table_locks_immediate | 112 |
| Table_locks_waited | 0 |
| Table_open_cache_hits | 0 |
| Table_open_cache_misses | 0 |
| Table_open_cache_overflows | 0 |
+----------------------------+-------+
5 rows in set (0.01 sec)
Table_locks_immediate表示产生表级锁的次数,Table_locks_waited表示表级锁发生冲突时等待的次数,这两个值较高,存在较高的表级锁竞争。
2.行锁
更偏写。
偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁;
锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
InnoDB与MyISAM的最大不同点,是支持事务,采用了行级锁。
行锁举例:
创建表并插入数据
create table test_innodb_lock(a int(11),b varchar(16))engine=innodb;
insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
create index idx_test_innodb_a on test_innodb_lock(a);
create index idx_test_innodb_b on test_innodb_lock(b);
打印
Query OK, 0 rows affected (0.02 sec)
Query OK, 1 row affected (0.01 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
两个命令行关闭自动提交:
set autocommit = 0;
打印
Query OK, 0 rows affected (0.00 sec)
在命令行一中更新数据:
update test_innodb_lock set b = '4001' where a = 4;
打印
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
在命令行一中查询,
select * from test_innodb_lock;
打印
+------+------+
| a | b |
+------+------+
| 1 | b2 |
| 3 | 1024 |
| 4 | 4001 |
| 5 | 1024 |
+------+------+
4 rows in set (0.00 sec)
数据已经改变
此时在命令行二中查询,
select * from test_innodb_lock;
打印
+------+------+
| a | b |
+------+------+
| 1 | b2 |
| 3 | 1024 |
| 4 | 1024 |
| 5 | 1024 |
+------+------+
4 rows in set (0.01 sec)
会发现未改变,需要在两个命令行中commit,然后再在命令行二中查询,
select * from test_innodb_lock;
打印
+------+------+
| a | b |
+------+------+
| 1 | b2 |
| 3 | 1024 |
| 4 | 4001 |
| 5 | 1024 |
+------+------+
4 rows in set (0.00 sec)
在命令行一中执行
update test_innodb_lock set b = '4002' where a = 4;
打印
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
此时在命令行二中执行
update test_innodb_lock set b = '4003' where a = 4;
会发生堵塞
在命令行一中提交后,命令行二打印
Query OK, 1 row affected (1 min 17.33 sec)
Rows matched: 1 Changed: 1 Warnings: 0
此时两个命令行分别查询,值分别为4002、4003,出现不一致的情况,再在两个命令行同时提交后,再次查询,数据一致,均为4003。
两个命令行中修改不同行的数据,不会发生冲突。
varchar不加引号:
- 索引失效,全表扫描;
- 由行锁变为表锁,可能阻塞。
在命令行一中执行
update test_innodb_lock set a = 5 where b = 4000;
打印
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
再在命令行二中执行
update test_innodb_lock set b = '4003' where a = 4;
会发生堵塞。
如何分析行锁定:
通过检查innodb_row_lock状态变量来分析系统上的行锁争夺情况。
show status like 'innodb_row_lock%';
打印
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 77325 |
| Innodb_row_lock_time_avg | 77325 |
| Innodb_row_lock_time_max | 77325 |
| Innodb_row_lock_waits | 1 |
+-------------------------------+-------+
5 rows in set (0.00 sec)
各个状态量的说明
状态量 | 说明 |
---|---|
Innodb_row_lock_current_waits | 当前正在等待锁定的数量 |
Innodb_row_lock_time | 从系统启动到现在锁定的总时间长度,值越大,锁定时间越长 |
Innodb_row_lock_time_avg | 每次等待所花费平均时间 |
Innodb_row_lock_time_max | 从系统启动到现在等待最长的一次所花费的时间 |
Innodb_row_lock_waits | 系统启动后到现在总共等待的次数 |
3.间隙锁
在命令行一中:
update test_innodb_lock set b = '1024' where a > 1 and a < 6;
打印
Query OK, 1 row affected (0.00 sec)
Rows matched: 3 Changed: 1 Warnings: 0
在命令行二中
insert into test_innodb_lock values(2,'2000');
发生堵塞,在命令行一中commit才能插入成功。
此即间隙锁。
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,innodb会给符合条件的已有数据记录的索引项加锁, 对于键值在条件范围内但并不存在的记录,叫做"间隙";
innodb也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁。
间隙锁的危害:
- 因为SQL执行过程中通过范围查找的话,他会锁定整个范围内所有的索引值,即使这个键值并不存在;
- 间隙锁有一个比较致命的弱点,就是当锁定以为范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。
如何锁定一行:
select * from test_innodb_lock where a = 8 for update;
二、MySQL分区表
分区表的特点:
在逻辑上为一个表,在物理上存储在多个文件中。
查看MySQL是否支持分区:
show plugins;
打印
+----------------------------+----------+--------------------+---------+---------+
| Name | Status | Type | Library | License |
+----------------------------+----------+--------------------+---------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| mysql_native_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| sha256_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
| INNODB_TRX | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_LOCKS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_LOCK_WAITS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
...
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| PERFORMANCE_SCHEMA | ACTIVE | STORAGE ENGINE | NULL | GPL |
| ARCHIVE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| FEDERATED | DISABLED | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| ngram | ACTIVE | FTPARSER | NULL | GPL |
+----------------------------+----------+--------------------+---------+---------+
44 rows in set (0.01 sec)
创建表
create table `login_log`(
login_id int(10) unsigned not null comment '登录用户id',
login_time timestamp not null default current_timestamp,
login_ip int(10) unsigned not null comment '登录类型'
)engine=innodb default charset=utf8 partition by hash(login_id) partitions 4;
打印
Query OK, 0 rows affected (0.06 sec)
分为4个区,此时在数据文件中有4个文件login_log#p#p0.ibd、login_log#p#p1.ibd、login_log#p#p2.ibd、login_log#p#p3.ibd。
分区键:
分区引入了分区键的概念,分区键用于根据某个区间值、特定值、或者HASH函数值执行数据的聚集,让数据根据规则分布在不同的分区中。
分区类型:
- RANGE分区
- LIST分区
- HASH分区
无论哪种分区类型,要么分区表上没有主键/唯一键,要么分区表的主键/唯一键都必须包括分区键,也就是说不能使用主键/唯一字段之外的其他字段分区。
RANGE分区:
RANGE分区特点:
- 根据分区键值的范围把数据行存储到表的不同分区中;
- 多个分区的范围要连续,但是不能重叠;
- 分区不包括上限,取不到上限值。
建立RANGE分区:
create table `login_log_range`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb
partition by range(login_id)(
partition p0 values less than(10000), #实际范围0-9999
partition p1 values less than(20000), #实际范围10000-19999
partition p2 values less than(30000), #实际范围20000-29999
partition p3 values less than maxvalue #存储大于30000的数据
);
打印
共 0 行受到影响
执行耗时 : 0.033 sec
传送时间 : 1.004 sec
总耗时 : 1.038 sec
此时插入一条login_id大于30000的数据:
insert into login_log_range values(30001,now(),1)
打印
Query OK, 1 row affected (0.01 sec)
分析SQL语句,查看分区
explain select * from login_log_range where login_id = 30001;
打印
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_range | p3 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
显示位于p3分区。
查看id为500的记录对应的分区:
explain select * from login_log_range where login_id = 500;
打印
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_range | p0 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+-----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
显示位于p0分区。
查看所有分区:
explain select * from login_log_range;
打印
+----+-------------+-----------------+-------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------------+-------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | login_log_range | p0,p1,p2,p3 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
+----+-------------+-----------------+-------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
RANGE分区使用场景:
- 分区键为日期或是时间类型;
- 经常运行包含分区键的查询,MySQL可以很快的确定只有某一个或某些分区需要扫描,例如检索商品login_id小于10000的记录数,MySQL只需要扫描p0分区即可;
- 定期按分区范围清理历史数据。
删除分区:
alter table login_log_range drop partition p0;
打印
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
HASH分区
HASH分区的特点:
- 根据MOD(分区键,分区值)的值把数据行存储到表的不同分区内;
- 数据可以平均地分布在各个分区中;
- HASH分区的键值必须是一个INT类型的值,或是通过函数可以转为INT类型。
建立HASH分区表:
create table `login_log_hash`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb default charset=utf8 partition by hash(login_id) partitions 4;
或者
create table `login_log_hash`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb default charset=utf8 partition by hash(UNIX_TIMESTAMP(login_time)) partitions 4;
打印
执行耗时 : 0.031 sec
传送时间 : 1.005 sec
总耗时 : 1.037 sec
插入数据
insert into login_log_hash values(1,now(),1);
insert into login_log_hash values(2,now(),2);
insert into login_log_hash values(3,now(),3);
insert into login_log_hash values(4,now(),4);
insert into login_log_hash values(5,now(),5);
打印
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
查看分区
explain select * from login_log_hash where login_id = 1;
explain select * from login_log_hash where login_id = 2;
explain select * from login_log_hash where login_id = 3;
explain select * from login_log_hash where login_id = 4;
explain select * from login_log_hash where login_id = 5;
打印
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_hash | p1 | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_hash | p2 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_hash | p3 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_hash | p0 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | login_log_hash | p1 | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
显然,每条数据位于对应的分区。
查看所有分区:
explain select * from login_log_hash;
打印
+----+-------------+----------------+-------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+-------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | login_log_hash | p0,p1,p2,p3 | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | NULL |
+----+-------------+----------------+-------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
hash分区为全表扫描。
LIST分区
LIST分区特点:
- 按分区键取值的列表进行分区;
- 同范围分区一样,各分区的列表值不能重复;
- 每一行数据必须能找到对应的分区列表,否则数据插入失败。
建立LIST分区
create table `login_log_list`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip',
login_type int(10) not null
)engine=innodb
partition by list(login_type)(
partition p0 values in(1,3,5,7,9),
partition p1 values in(2,4,6,8)
);
打印
Query OK, 0 rows affected (0.03 sec)
插入值
insert into login_log_list values(1,now(),2,1);
打印
Query OK, 1 row affected (0.00 sec)
可以正常插入。
插入一条login_type列表中没有的数据
insert into login_log_list values(1,now(),2,10);
打印
ERROR 1526 (HY000): Table has no partition for value 10
会报错,即没有10对应的分区。
三、练习–如何选择合适的分区方式
业务场景:
- 用户每次登录都会记录到日志表中;
- 用户登录日志保存一年,一年后可以删除。
create table login_lg_log(
login_id int(10) not null,
login_time datetime not null default current_timestamp,
login_ip int(10) not null
)engine = INNODB
partition by range(year(login_time))(
partition p0 values less than(2015),
partition p1 values less than(2016),
partition p2 values less than(2017),
partition p3 values less than(2018)
);
打印
Query OK, 0 rows affected (0.03 sec)
插入数据
insert into login_lg_log values
(1,'2015-01-01',1),
(2,'2015-01-02',1),
(3,'2016-01-01',1),
(4,'2016-01-01',1),
(5,'2017-01-01',1),
(6,'2017-01-01',1),
(7,'2017-01-01',1)
;
打印
Query OK, 7 rows affected (0.00 sec)
Records: 7 Duplicates: 0 Warnings: 0
查看分区
select table_name,partition_name,partition_description,table_rows from information_schema.partitions where table_name = 'login_lg_log';
打印
+--------------+----------------+-----------------------+------------+
| table_name | partition_name | partition_description | table_rows |
+--------------+----------------+-----------------------+------------+
| login_lg_log | p0 | 2015 | 0 |
| login_lg_log | p1 | 2016 | 2 |
| login_lg_log | p2 | 2017 | 2 |
| login_lg_log | p3 | 2018 | 3 |
+--------------+----------------+-----------------------+------------+
4 rows in set (0.00 sec)
添加分区
alter table login_lg_log add partition(partition p4 values less than(2019));
打印
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
删除分区
alter table login_lg_log drop partition p0;
打印
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
数据归档:
新建一个和要归档的表的结构一样的表:
create table login_lg_log_test(
login_id int(10) not null,
login_time datetime not null default current_timestamp,
login_ip int(10) not null
)engine = INNODB;
打印
Query OK, 0 rows affected (0.01 sec)
分区迁移:
alter table login_lg_log exchange partition p1 with table login_lg_log_test;
打印
Query OK, 0 rows affected (0.01 sec)
查看两个表:
select * from login_lg_log;
打印
+----------+---------------------+----------+
| login_id | login_time | login_ip |
+----------+---------------------+----------+
| 3 | 2016-01-01 00:00:00 | 1 |
| 4 | 2016-01-01 00:00:00 | 1 |
| 5 | 2017-01-01 00:00:00 | 1 |
| 6 | 2017-01-01 00:00:00 | 1 |
| 7 | 2017-01-01 00:00:00 | 1 |
+----------+---------------------+----------+
5 rows in set (0.00 sec)
即原表中p1分区的数据被移除。
select * from login_lg_log_test;
打印
+----------+---------------------+----------+
| login_id | login_time | login_ip |
+----------+---------------------+----------+
| 1 | 2015-01-01 00:00:00 | 1 |
| 2 | 2015-01-02 00:00:00 | 1 |
+----------+---------------------+----------+
2 rows in set (0.00 sec)
新表中即为原表p1分区的数据。
使用分区表的注意事项:
- 结合业务场景选择分区键,避免跨分区查询;
- 对分区表进行查询最好在where从句中包含分区键;
- 具有主键或唯一索引的表,主键或唯一索引必须是分区键的一部分。