尽量使用联合索引,没有必要的索引不要创建,会影响联合索引命中。
如果时间字段为条件中的关键业务字段。一定要确保时间索引占支配作用。如果命中了其他索引效率反而会变慢。但是时间BETWEEN查询范围如果过大,是不走索引的,具体原因。数据量过大时,建议对表进行分区。业务允许的话尽量限制时间查询范围。
将表中的数据进行分区,以月份为分区单位。分区字段必须为主键。
ALTER TABLE case_info_converge
PARTITION BY RANGE( YEAR(occurrence_date) * 100 + MONTH(occurrence_date) ) (
PARTITION p201901 VALUES LESS THAN (201902),
PARTITION p201902 VALUES LESS THAN (201903),
PARTITION p201903 VALUES LESS THAN (201904),
...
PARTITION p202112 VALUES LESS THAN (202201)
);
以时间为分区单位有个缺点:会造成分区内数据分布不均匀
Mysql不能自动创建分区,需要使用mysql event事件的方式自动创建分区
创建分区的存储过程如下(每次执行先校验当前分区是否存在,如果存在则不处理;不存在则创建):
CREATE DEFINER=`root`@`localhost` PROCEDURE `create_partition_by_day`(IN `IN_SCHEMANAME` varchar(64),IN `IN_TABLENAME` varchar(64))
BEGIN
DECLARE ROWS_CNT INT UNSIGNED;-- 当前日期存在的分区的个数
DECLARE TARGET_DATE TIMESTAMP;-- 目前日期,为当前日期的后一天
DECLARE PARTITIONNAME VARCHAR(9);-- 分区的名称,格式为p20180620
DECLARE PARTITION_ADD_DAY VARCHAR(9);-- 当前分区名称的分区值上限,即为 PARTITIONNAME + 1
SET TARGET_DATE = NOW() + INTERVAL 1 DAY;
SET PARTITIONNAME = DATE_FORMAT( TARGET_DATE, 'p%Y%m%d' );
SET TARGET_DATE = TARGET_DATE + INTERVAL 1 DAY;
SET PARTITION_ADD_DAY = DATE_FORMAT( TARGET_DATE, '%Y%m%d' );
SELECT COUNT(*) INTO ROWS_CNT FROM information_schema.partitions WHERE table_schema = IN_SCHEMANAME AND table_name = IN_TABLENAME AND partition_name = PARTITIONNAME;
IF ROWS_CNT = 0 THEN
SET @SQL = CONCAT( 'ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
' ADD PARTITION (PARTITION ', PARTITIONNAME, " VALUES LESS THAN (",
PARTITION_ADD_DAY ,") ENGINE = InnoDB);" );
PREPARE STMT FROM @SQL;
EXECUTE STMT;
DEALLOCATE PREPARE STMT;
ELSE
SELECT CONCAT("partition `", PARTITIONNAME, "` for table `",IN_SCHEMANAME, ".", IN_TABLENAME, "` already exists") AS result;
END IF;
END
DELIMITER $$
#该表所在的数据库名称
USE `ry_vue_sjzz`$$
CREATE EVENT IF NOT EXISTS `daily_generate_partition`
ON SCHEDULE EVERY 1 hour #执行周期,还有天、月等等
STARTS '2023-12-31 00:00:00'
ON COMPLETION PRESERVE
ENABLE
COMMENT 'Creating partitions'
DO BEGIN
#调用刚才创建的存储过程,第一个参数是数据库名称,第二个参数是表名称
CALL ry_vue_sjzz.create_partition_by_day('ry_vue_sjzz','case_info');
END$$
DELIMITER;
查看定时任务是否创建成功
SHOW EVENTS;
字段case_name
, description
创建全文索引,必须用ngram分词器,不指定ngrm,分词效果很差。
分区表不支持全文检索。创建全文索引的字段必须是CHAR、VARCHAR或者TEXT类型,且表的存储引擎必须是InnoDB或者MyISAM 。
ALTER TABLE case_info_converge ADD FULLTEXT INDEX title_desc_fulltext ( `case_name`, `description` ) WITH PARSER ngram;
如果想对分区的表创建全文检索,可以将表进行copy。取消表的分区。然后对原表创建触发器同步数据。如果时间允许的情况,建议还是上ES。
取消case_info_converge表的分区
ALTER TABLE case_info_converge REMOVE PARTITIONING;
全文索引查询语句
SELECT * FROM case_info_converge WHERE MATCH(`case_name`, `description`) against('果蔬')
触发器
CREATE TRIGGER case_info_converge_insert AFTER INSERT ON case_info
FOR EACH ROW
INSERT INTO case_info_converge VALUES (NEW.case_code, NEW.source_row_id, NEW.channel_type);