按月分区方法的比较
1、事出有因
在生产系统中发现在高并发时,分区表常常被锁住,而记录的锁非常奇怪,是最早分区中的一个数据,而实际删除的时候都限制了分区字段的值,这个值是一个between A and B ,A B之间并不包括最早分区。innodb的行级锁是按索引来的,无论如何都不应该会将他的这行记录锁住。
说明存在问题:我们的分区可能有问题或者mysql本身的bug
以下主要针对分区方式的分区解析和行锁进行的测试。
2、表结构 -按月分区的两种方式,第一种是按照 日期/100 得到的结果进行分区
- CREATE TABLE `test_stock_history_1` (
- `rtime` bigint(20) unsigned NOT NULL,
- `oid` int(10) unsigned DEFAULT NULL,
- `fund_key` bigint(20) unsigned NOT NULL,
- `trans_date` date NOT NULL DEFAULT '1700-01-01',
- ......
- ......
- `createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `broker` int(10) DEFAULT NULL,
- `terminal` char(3) DEFAULT NULL,
- `dealway` char(3) DEFAULT NULL,
- `type1` char(10) DEFAULT NULL,
- `type2` char(20) DEFAULT NULL,
- UNIQUE KEY `idx_sh_fto` (`fund_key`,`trans_date`,`oid`) USING BTREE,
- KEY `idx_sh_fts` (`fund_key`,`trans_date`,`stock_code`) USING BTREE
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8
- |
复制代码第二种分区是直接按照日期的方式取值范围进行分区: - | test_stock_history_2 | CREATE TABLE `test_stock_history_2` (
- `rtime` bigint(20) unsigned NOT NULL,
- `oid` int(10) unsigned DEFAULT NULL,
- `fund_key` bigint(20) unsigned NOT NULL,
- `trans_date` date NOT NULL DEFAULT '1700-01-01',
- ......
- ......
- `createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `broker` int(10) DEFAULT NULL,
- `terminal` char(3) DEFAULT NULL,
- `dealway` char(3) DEFAULT NULL,
- `type1` char(10) DEFAULT NULL,
- `type2` char(20) DEFAULT NULL,
- UNIQUE KEY `idx_sh_fto` (`fund_key`,`trans_date`,`oid`) USING BTREE,
- KEY `idx_sh_fts` (`fund_key`,`trans_date`,`stock_code`) USING BTREE
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8
- |
复制代码
以上两种表,除了分区方式不同,其他字段、索引均相同,每个分区中存放的数据也相同
3、按照索引的执行计划解释
- explain partitions (select * from test_stock_history_1 where fund_key=3100120 and trans_date >= '2016-01-01' and trans_date<= '2016-01-03' );
- +----+-------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+-----------------------+------------+---------+------+------+-------------+
- | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+-----------------------+------------+---------+------+------+-------------+
- | 1 | SIMPLE | test_stock_history_1 | p_201407,p_201408,p_201409,p_201410,p_201411,p_201412,p_201501,p_201502,p_201503,p_201504,p_201505,p_201506,p_201507,p_201508,p_201509,p_201510,p_201511,p_201512,p_201601,p_201602,p_201603,p_201604,p_201605,p_201606,p_201607,p_201608,p_201609,p_201610,p_201611,p_201612,p_catchall | range | idx_sh_fto,idx_sh_fts | idx_sh_fto | 11 | NULL | 10 | Using where |
- +----+-------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+-----------------------+------------+---------+------+------+-------------+
- 发现是所有分区。
- mysql> explain partitions (select * from test_stock_history_2 where fund_key=3100120 and trans_date >= '2016-01-01' and trans_date<= '2016-01-03' );
- +----+-------------+----------------------+------------+-------+-----------------------+------------+---------+------+------+-------------+
- | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+----------------------+------------+-------+-----------------------+------------+---------+------+------+-------------+
- | 1 | SIMPLE | test_stock_history_2 | p_201601 | range | idx_sh_fto,idx_sh_fts | idx_sh_fto | 11 | NULL | 1 | Using where |
- +----+-------------+----------------------+------------+-------+-----------------------+------------+---------+------+------+-------------+
复制代码 2、查询解释
尽管上面的分区表解释不同,但是他们的执行计划解释是完全相同的
这种分区方式看起来能够定位具体的分区,看起来好像比较好,需要进行测试。
两个表同时导入3千万数据(loaddata) ,花费时间几乎一致。
删除
3、事务锁的情况
3.1、表一方式的锁
从监控的结果看,是锁住了一条毫无关系的一行记录。并且在实验环境下这种情况是完全可重现的
- 执行事务1
- start transaction;
- delete from test_stock_history_1 where fund_key = 3100190 and trans_date between '2016-05-01' and '2016-05-20';
- 执行事务2
- start transaction;
- delete from test_stock_history_1 where fund_key = 3100190 and trans_date between '2016-04-01' and '2016-04-20';
复制代码
原来个人认为这两条记录毫无关系,按照innodb的行锁规则,1和2无关,2是不会被锁的,但是实际执行中被锁住了。
- 检查事务和锁表的情况:
- select * from innodb_trx \G
- *************************** 1. row ***************************
- trx_id: 2798703
- trx_state: LOCK WAIT
- trx_started: 2016-06-27 18:36:17
- trx_requested_lock_id: 2798703:328:17:459
- trx_wait_started: 2016-06-27 18:36:17
- trx_weight: 2
- trx_mysql_thread_id: 2
- trx_query: delete from test_stock_history_1 where fund_key = 3100190 and trans_date between '2016-04-01' and '2016-04-20'
- trx_operation_state: starting index read
- trx_tables_in_use: 31
- trx_tables_locked: 31
- trx_lock_structs: 2
- trx_lock_memory_bytes: 360
- trx_rows_locked: 1
- trx_rows_modified: 0
- trx_concurrency_tickets: 0
- trx_isolation_level: REPEATABLE READ
- trx_unique_checks: 1
- trx_foreign_key_checks: 1
- trx_last_foreign_key_error: NULL
- trx_adaptive_hash_latched: 0
- trx_adaptive_hash_timeout: 10000
- trx_is_read_only: 0
- trx_autocommit_non_locking: 0
- *************************** 2. row ***************************
- trx_id: 2798701
- trx_state: RUNNING
- trx_started: 2016-06-27 18:35:01
- trx_requested_lock_id: NULL
- trx_wait_started: NULL
- trx_weight: 107
- trx_mysql_thread_id: 3
- trx_query: NULL
- trx_operation_state: NULL
- trx_tables_in_use: 0
- trx_tables_locked: 0
- trx_lock_structs: 92
- trx_lock_memory_bytes: 13864
- trx_rows_locked: 84
- trx_rows_modified: 15
- trx_concurrency_tickets: 0
- trx_isolation_level: REPEATABLE READ
- trx_unique_checks: 1
- trx_foreign_key_checks: 1
- trx_last_foreign_key_error: NULL
- trx_adaptive_hash_latched: 0
- trx_adaptive_hash_timeout: 10000
- trx_is_read_only: 0
- trx_autocommit_non_locking: 0
- 2 rows in set (0.00 sec)
复制代码
- mysql> select * from innodb_locks \G
- *************************** 1. row ***************************
- lock_id: 2798703:328:17:459
- lock_trx_id: 2798703
- lock_mode: X
- lock_type: RECORD
- lock_table: `test`.`test_stock_history_1`
- lock_index: idx_sh_fto
- lock_space: 328
- lock_page: 17
- lock_rec: 459
- lock_data: 3100310, 1031394, 0
- *************************** 2. row ***************************
- lock_id: 2798701:328:17:459
- lock_trx_id: 2798701
- lock_mode: X
- lock_type: RECORD
- lock_table: `test`.`test_stock_history_1`
- lock_index: idx_sh_fto
- lock_space: 328
- lock_page: 17
- lock_rec: 459
- lock_data: 3100310, 1031394, 0
- 2 rows in set (0.00 sec)
复制代码
文章来源:沧海大声啸的博客作者:Kervin(博为峰网校讲师)
|