Есть чувства, есть галантерейные товары, поиск в WeChat【Третий принц Ао Бин] Обратите внимание на этого другого программиста.
эта статьяGitHub github.com/JavaFamilyВключено, и есть полные тестовые площадки, материалы и мой цикл статей для интервью с производителями первой линии.
предисловие
Есть несколько систем, за которые я отвечаю.С ростом объемов бизнеса данные, хранящиеся в MySQL, быстро увеличиваются.Я думал, что нынешняя сторона бизнеса не будет говорить о боевых искусствах, а будет заниматься скрытными атаками.Быстро, очень быстро, он достиг уровня 100 миллионов. Я был неосторожен, и не было вспышки. Это привело к тому, что SQL таблицы соединений стал очень медленным, а время отклика правильного интерфейса приложения стало больше, что повлияло на пользователя опыт.
После того, как я нашел деловую сторону, я раскритиковал их за то, что они сказали им говорить о боевых искусствах, и быстро извинился передо мной, и это дело было закончено.Когда я ушел, я сказал им, чтобы они не делали этого в следующий раз, сок из мышиного хвоста и отразить это.
После ругани и ругани, дела еще предстоит решить.Когда я проанализировал причины, я обнаружил, что объем данных некоторых таблиц быстро увеличивался, и соответствующий SQL сканировал много недействительных данных, из-за чего SQL тормозил. подтверждение, все эти большие таблицы - проточная вода. , записи и данные типа журнала должны храниться только в течение 1-3 месяцев. В это время необходимо очистить данные таблицы, чтобы добиться потери веса. Как правило, метод вставки + удаления используется для его очистки.
В этой статье я объясню, почему не рекомендуется удалять данные с точки зрения распределения пространства хранения InnoDB, влияние удаления на производительность и предложения по оптимизации.
Архитектура хранения InnoDB
Как видно из этого рисунка, структура хранилища InnoDB в основном состоит из двух частей: логической структуры хранения и физической структуры хранения.
Логически он состоит из табличного пространства табличное пространство —> сегмент сегмент или индексный дескриптор —> область Экстент —> страница данных Страница.Логической единицей управления Innodb является сегмент, а минимальной единицей выделения пространства является экстент.Каждый сегмент будет выделен из табличного пространства FREE_PAGE 32 страниц, когда 32 страниц недостаточно, он будет расширен по следующим принципам: если текущий экстент меньше 1 экстента, он будет расширен до 1 экстента, когда табличное пространство меньше 32 МБ, будет расширен один экстент. расширяться за раз; табличное пространство больше 32 МБ, 4 экстента каждый раз.
Физически он в основном состоит из файлов системных пользовательских данных и файлов журналов.Файлы данных в основном хранят данные словаря MySQL и пользовательские данные.Файлы журналов записывают записи об изменениях страницы данных и используются для восстановления MySQL Crash.
Табличное пространство Innodb
Хранилище InnoDB включает три типа табличных пространств: системное табличное пространство, пользовательское табличное пространство и табличное пространство отмены.
**Системное табличное пространство:** В основном оно хранит данные словаря данных внутри MySQL, например данные в information_schema.
**Пользовательское табличное пространство:** Если включен параметр innodb_file_per_table=1, таблица данных сохраняется независимо от системного табличного пространства в файле данных с помощью команды table_name.ibd, а информация о структуре сохраняется в файле table_name.frm.
**Табличное пространство отмены: **Хранение информации об отмене, такой как согласованное чтение моментального снимка и флэшбэк, оба используют информацию об отмене.
Начиная с MySQL 8.0 разрешены пользовательские табличные пространства.Конкретный синтаксис выглядит следующим образом:
CREATE TABLESPACE tablespace_name
ADD DATAFILE 'file_name' #数据文件名
USE LOGFILE GROUP logfile_group #自定义日志文件组,一般每组2个logfile。
[EXTENT_SIZE [=] extent_size] #区大小
[INITIAL_SIZE [=] initial_size] #初始化大小
[AUTOEXTEND_SIZE [=] autoextend_size] #自动扩宽尺寸
[MAX_SIZE [=] max_size] #单个文件最大size,最大是32G。
[NODEGROUP [=] nodegroup_id] #节点组
[WAIT]
[COMMENT [=] comment_text]
ENGINE [=] engine_name
Преимущество этого заключается в том, что данные можно отделить от горячих и холодных, а для хранения используются жесткие диски и твердотельные накопители, что позволяет не только обеспечить эффективный доступ к данным, но и сократить расходы.Например, можно добавить два жестких диска 500G и Можно создать группу томов vg для разделения логического тома lv, создать каталог данных и смонтировать соответствующий lv, предполагая, что два разделенных каталога — это /hot_data и /cold_data.
Таким образом, основные бизнес-таблицы, такие как таблица пользователей и таблица заказов, могут храниться на высокопроизводительном SSD-диске, а некоторые журналы и расходомеры могут храниться на обычном жестком диске.Основные этапы работы следующие:
#创建热数据表空间
create tablespace tbs_data_hot add datafile '/hot_data/tbs_data_hot01.dbf' max_size 20G;
#创建核心业务表存储在热数据表空间
create table booking(id bigint not null primary key auto_increment, …… ) tablespace tbs_data_hot;
#创建冷数据表空间
create tablespace tbs_data_cold add datafile '/hot_data/tbs_data_cold01.dbf' max_size 20G;
#创建日志,流水,备份类的表存储在冷数据表空间
create table payment_log(id bigint not null primary key auto_increment, …… ) tablespace tbs_data_cold;
#可以移动表到另一个表空间
alter table payment_log tablespace tbs_data_hot;
Распределение хранилища Inndob
Создайте пустую таблицу для просмотра изменений пространства
mysql> create table user(id bigint not null primary key auto_increment,
-> name varchar(20) not null default '' comment '姓名',
-> age tinyint not null default 0 comment 'age',
-> gender char(1) not null default 'M' comment '性别',
-> phone varchar(16) not null default '' comment '手机号',
-> create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-> update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
-> ) engine = InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户信息表';
Query OK, 0 rows affected (0.26 sec)
# ls -lh user1.ibd
-rw-r----- 1 mysql mysql 96K Nov 6 12:48 user.ibd
При установке параметра innodb_file_per_table=1 сегмент будет автоматически создан при создании таблицы, и одновременно будет выделен экстент, который содержит 32 страницы данных для хранения данных Размер по умолчанию пустой таблицы, созданной в таким образом, 96 КБ, и 64 соединения будут применены после того, как экстент будет израсходован.Страницы, так что для некоторых небольших таблиц или сегментов отмены в начале может быть запрошено меньше места, что экономит накладные расходы на дисковую емкость.
# python2 py_innodb_page_info.py -v /data2/mysql/test/user.ibd
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0000>
page offset 00000000, page type <Freshly Allocated Page>
page offset 00000000, page type <Freshly Allocated Page>
Total number of page: 6: #总共分配的页数
Freshly Allocated Page: 2 #可用的数据页
Insert Buffer Bitmap: 1 #插入缓冲页
File Space Header: 1 #文件空间头
B-tree Node: 1 #数据页
File Segment inode: 1 #文件端inonde,如果是在ibdata1.ibd上会有多个inode。
Пространственное изменение после вставки данных
mysql> DELIMITER $$
mysql> CREATE PROCEDURE insert_user_data(num INTEGER)
-> BEGIN
-> DECLARE v_i int unsigned DEFAULT 0;
-> set autocommit= 0;
-> WHILE v_i < num DO
-> insert into user(`name`, age, gender, phone) values (CONCAT('lyn',v_i), mod(v_i,120), 'M', CONCAT('152',ROUND(RAND(1)*100000000)));
-> SET v_i = v_i+1;
-> END WHILE;
-> commit;
-> END $$
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
#插入10w数据
mysql> call insert_user_data(100000);
Query OK, 0 rows affected (6.69 sec)
# ls -lh user.ibd
-rw-r----- 1 mysql mysql 14M Nov 6 10:58 /data2/mysql/test/user.ibd
# python2 py_innodb_page_info.py -v /data2/mysql/test/user.ibd
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0001> #增加了一个非叶子节点,树的高度从1变为2.
........................................................
page offset 00000000, page type <Freshly Allocated Page>
Total number of page: 896:
Freshly Allocated Page: 493
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 400
File Segment inode: 1
Пробел меняется после удаления данных
mysql> select min(id),max(id),count(*) from user;
+---------+---------+----------+
| min(id) | max(id) | count(*) |
+---------+---------+----------+
| 1 | 100000 | 100000 |
+---------+---------+----------+
1 row in set (0.05 sec)
#删除50000条数据,理论上空间应该从14MB变长7MB左右。
mysql> delete from user limit 50000;
Query OK, 50000 rows affected (0.25 sec)
#数据文件大小依然是14MB,没有缩小。
# ls -lh /data2/mysql/test/user1.ibd
-rw-r----- 1 mysql mysql 14M Nov 6 13:22 /data2/mysql/test/user.ibd
#数据页没有被回收。
# python2 py_innodb_page_info.py -v /data2/mysql/test/user.ibd
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0001>
........................................................
page offset 00000000, page type <Freshly Allocated Page>
Total number of page: 896:
Freshly Allocated Page: 493
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 400
File Segment inode: 1
#在MySQL内部是标记删除,
mysql> use information_schema;
Database changed
mysql> SELECT A.SPACE AS TBL_SPACEID, A.TABLE_ID, A.NAME AS TABLE_NAME, FILE_FORMAT, ROW_FORMAT, SPACE_TYPE, B.INDEX_ID , B.NAME AS INDEX_NAME, PAGE_NO, B.TYPE AS INDEX_TYPE FROM INNODB_SYS_TABLES A LEFT JOIN INNODB_SYS_INDEXES B ON A.TABLE_ID =B.TABLE_ID WHERE A.NAME = 'test/user1';
+-------------+----------+------------+-------------+------------+------------+----------+------------+---------+------------+
| TBL_SPACEID | TABLE_ID | TABLE_NAME | FILE_FORMAT | ROW_FORMAT | SPACE_TYPE | INDEX_ID | INDEX_NAME | PAGE_NO | INDEX_TYPE |
+-------------+----------+------------+-------------+------------+------------+----------+------------+---------+------------+
| 1283 | 1207 | test/user | Barracuda | Dynamic | Single | 2236 | PRIMARY | 3 | 3 |
+-------------+----------+------------+-------------+------------+------------+----------+------------+---------+------------+
1 row in set (0.01 sec)
PAGE_NO = 3 标识B-tree的root page是3号页,INDEX_TYPE = 3是聚集索引。 INDEX_TYPE取值如下:
0 = nonunique secondary index;
1 = automatically generated clustered index (GEN_CLUST_INDEX);
2 = unique nonclustered index;
3 = clustered index;
32 = full-text index;
#收缩空间再后进行观察
MySQL на самом деле не будет удалять пространство внутри, а удалит метку, то есть изменит delflag:N на delflag:Y, и он будет очищен в списке удаления после фиксации.Если в следующий раз будет вставлена более крупная запись, пробел после delete не будет повторно использоваться, если вставленная запись меньше или равна удаляемой записи, она будет использована повторно.Это содержимое можно проанализировать с помощью инструмента innblock от Zhishutang.
Фрагментация в Иннодбе
образование мусора
Мы знаем, что данные, хранящиеся в файловой системе, не всегда могут использовать 100% выделенного для них физического пространства, удаление данных оставит некоторые «дыры» на странице, или случайная запись (нелинейное увеличение кластеризованного индекса) вызовет разделение страниц, разделение страницы приводит к менее чем 50% пространства, используемого страницей. Кроме того, добавление, удаление и изменение таблицы вызовет случайные добавления, удаления и изменения соответствующих значений вторичного индекса, а также вызовет некоторые «дыры» в страницах данных в структуре индекса. , хотя эти дыры могут быть использованы повторно, это в конечном итоге приведет к тому, что часть физического пространства не будет использоваться, то есть мусор.
В то же время, даже если коэффициент заполнения установлен на 100 %, Innodb будет активно оставлять 1/16 пространства страницы для зарезервированного использования (значение innodb_fill_factor, равное 100, оставляет 1/16 пространства на страницах кластеризованного индекса свободным для использования в будущем). рост индекса), чтобы предотвратить переполнение строки, вызванное обновлением.
mysql> select table_schema,
-> table_name,ENGINE,
-> round(DATA_LENGTH/1024/1024+ INDEX_LENGTH/1024/1024) total_mb,TABLE_ROWS,
-> round(DATA_LENGTH/1024/1024) data_mb, round(INDEX_LENGTH/1024/1024) index_mb, round(DATA_FREE/1024/1024) free_mb, round(DATA_FREE/DATA_LENGTH*100,2) free_ratio
-> from information_schema.TABLES where TABLE_SCHEMA= 'test'
-> and TABLE_NAME= 'user';
+--------------+------------+--------+----------+------------+---------+----------+---------+------------+
| table_schema | table_name | ENGINE | total_mb | TABLE_ROWS | data_mb | index_mb | free_mb | free_ratio |
+--------------+------------+--------+----------+------------+---------+----------+---------+------------+
| test | user | InnoDB | 4 | 50000 | 4 | 0 | 6 | 149.42 |
+--------------+------------+--------+----------+------------+---------+----------+---------+------------+
1 row in set (0.00 sec)
Среди них data_free — это количество выделенных неиспользуемых байтов, что не означает, что это полностью фрагментированное пространство.
Переработка мусора
Для таблиц InnoDB можно использовать следующие команды для восстановления фрагментов и освобождения места.Это операция ввода-вывода со случайным чтением, которая отнимает много времени и блокирует обычную операцию DML в таблице.Также требуется больше места на диске.Для RDS Для например, это может привести к мгновенному заполнению дискового пространства, мгновенной блокировке экземпляра, и приложение не может выполнять операции DML, поэтому его выполнение в онлайн-среде запрещено.
#执行InnoDB的碎片回收
mysql> alter table user engine=InnoDB;
Query OK, 0 rows affected (9.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
##执行完之后,数据文件大小从14MB降低到10M。
# ls -lh /data2/mysql/test/user1.ibd
-rw-r----- 1 mysql mysql 10M Nov 6 16:18 /data2/mysql/test/user.ibd
mysql> select table_schema, table_name,ENGINE, round(DATA_LENGTH/1024/1024+ INDEX_LENGTH/1024/1024) total_mb,TABLE_ROWS, round(DATA_LENGTH/1024/1024) data_mb, round(INDEX_LENGTH/1024/1024) index_mb, round(DATA_FREE/1024/1024) free_mb, round(DATA_FREE/DATA_LENGTH*100,2) free_ratio from information_schema.TABLES where TABLE_SCHEMA= 'test' and TABLE_NAME= 'user';
+--------------+------------+--------+----------+------------+---------+----------+---------+------------+
| table_schema | table_name | ENGINE | total_mb | TABLE_ROWS | data_mb | index_mb | free_mb | free_ratio |
+--------------+------------+--------+----------+------------+---------+----------+---------+------------+
| test | user | InnoDB | 5 | 50000 | 5 | 0 | 2 | 44.29 |
+--------------+------------+--------+----------+------------+---------+----------+---------+------------+
1 row in set (0.00 sec)
Влияние удаления на SQL
Выполнение SQL перед удалением
#插入100W数据
mysql> call insert_user_data(1000000);
Query OK, 0 rows affected (35.99 sec)
#添加相关索引
mysql> alter table user add index idx_name(name), add index idx_phone(phone);
Query OK, 0 rows affected (6.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
#表上索引统计信息
mysql> show index from user;
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user | 0 | PRIMARY | 1 | id | A | 996757 | NULL | NULL | | BTREE | | |
| user | 1 | idx_name | 1 | name | A | 996757 | NULL | NULL | | BTREE | | |
| user | 1 | idx_phone | 1 | phone | A | 2 | NULL | NULL | | BTREE | | |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
#重置状态变量计数
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
#执行SQL语句
mysql> select id, age ,phone from user where name like 'lyn12%';
+--------+-----+-------------+
| id | age | phone |
+--------+-----+-------------+
| 124 | 3 | 15240540354 |
| 1231 | 30 | 15240540354 |
| 12301 | 60 | 15240540354 |
.............................
| 129998 | 37 | 15240540354 |
| 129999 | 38 | 15240540354 |
| 130000 | 39 | 15240540354 |
+--------+-----+-------------+
11111 rows in set (0.03 sec)
mysql> explain select id, age ,phone from user where name like 'lyn12%';
+----+-------------+-------+-------+---------------+----------+---------+------+-------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+-------+-----------------------+
| 1 | SIMPLE | user | range | idx_name | idx_name | 82 | NULL | 22226 | Using index condition |
+----+-------------+-------+-------+---------------+----------+---------+------+-------+-----------------------+
1 row in set (0.00 sec)
#查看相关状态呢变量
mysql> select * from information_schema.session_status where variable_name in('Last_query_cost','Handler_read_next','Innodb_pages_read','Innodb_data_reads','Innodb_pages_read');
+-------------------+----------------+
| VARIABLE_NAME | VARIABLE_VALUE |
+-------------------+----------------+
| HANDLER_READ_NEXT | 11111 | #请求读的行数
| INNODB_DATA_READS | 7868409 | #数据物理读的总数
| INNODB_PAGES_READ | 7855239 | #逻辑读的总数
| LAST_QUERY_COST | 10.499000 | #SQL语句的成本COST,主要包括IO_COST和CPU_COST。
+-------------------+----------------+
4 rows in set (0.00 sec)
Выполнение SQL после удаления
#删除50w数据
mysql> delete from user limit 500000;
Query OK, 500000 rows affected (3.70 sec)
#分析表统计信息
mysql> analyze table user;
+-----------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+-----------+---------+----------+----------+
| test.user | analyze | status | OK |
+-----------+---------+----------+----------+
1 row in set (0.01 sec)
#重置状态变量计数
mysql> flush status;
Query OK, 0 rows affected (0.01 sec)
mysql> select id, age ,phone from user where name like 'lyn12%';
Empty set (0.05 sec)
mysql> explain select id, age ,phone from user where name like 'lyn12%';
+----+-------------+-------+-------+---------------+----------+---------+------+-------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+-------+-----------------------+
| 1 | SIMPLE | user | range | idx_name | idx_name | 82 | NULL | 22226 | Using index condition |
+----+-------------+-------+-------+---------------+----------+---------+------+-------+-----------------------+
1 row in set (0.00 sec)
mysql> select * from information_schema.session_status where variable_name in('Last_query_cost','Handler_read_next','Innodb_pages_read','Innodb_data_reads','Innodb_pages_read');
+-------------------+----------------+
| VARIABLE_NAME | VARIABLE_VALUE |
+-------------------+----------------+
| HANDLER_READ_NEXT | 0 |
| INNODB_DATA_READS | 7868409 |
| INNODB_PAGES_READ | 7855239 |
| LAST_QUERY_COST | 10.499000 |
+-------------------+----------------+
4 rows in set (0.00 sec)
Статистический анализ результатов
действовать | COST | физические показатели | логические чтения | Строки сканирования | вернуть количество строк | время исполнения |
---|---|---|---|---|---|---|
Инициализировать вставку 100 Вт | 10.499000 | 7868409 | 7855239 | 22226 | 11111 | 30ms |
100 Вт случайным образом удалить 50 Вт | 10.499000 | 7868409 | 7855239 | 22226 | 0 | 50ms |
Это также показывает, что для обычных больших таблиц нереально использовать удаление данных для уменьшения размера таблицы, поэтому не используйте удаление для удаления данных в любое время, для удаления следует использовать элегантную отметку.
удалить предложения по оптимизации
Управление разрешениями бизнес-аккаунта
Для большой системы необходимо разделить подсистемы в соответствии с бизнес-характеристиками.Каждая подсистема может рассматриваться как служба, такая как приложение Meituan, которое имеет множество служб.Основные службы включают пользовательскую службу пользовательскую службу и поисковый сервис поисковый сервис, товарный продукт-сервис, локационный сервис локационный сервис, прайс-сервис прайс-сервис и др. Каждая служба соответствует базе данных, создайте отдельную учетную запись для базы данных и предоставьте только разрешения DML и не разрешайте удаление, а также запретите доступ между базами данных.
#创建用户数据库并授权
create database mt_user charset utf8mb4;
grant USAGE, SELECT, INSERT, UPDATE ON mt_user.* to 'w_user'@'%' identified by 't$W*g@gaHTGi123456';
flush privileges;
удалить, чтобы отметить удаление
В спецификации моделирования базы данных MySQL есть 4 общедоступных поля, которые в основном необходимы для каждой таблицы.В то же время создание индекса в столбце create_time имеет два преимущества:
- Некоторые бизнес-сценарии запросов будут иметь период времени по умолчанию, например 7 дней или один месяц, который фильтруется по create_time, а сканирование индекса выполняется быстрее.
- Некоторые основные бизнес-таблицы необходимо извлекать из хранилища данных способом T+1. Например, данные за предыдущий день извлекаются в 00:30 каждую ночь, что фильтруется по create_time.
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否逻辑删除:0:未删除,1:已删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
#有了删除标记,业务接口的delete操作就可以转换为update
update user set is_deleted = 1 where user_id = 1213;
#查询的时候需要带上is_deleted过滤
select id, age ,phone from user where is_deleted = 0 and name like 'lyn12%';
Способ подачи данных
Общие методы архивирования данных
#1. 创建归档表,一般在原表名后面添加_bak。
CREATE TABLE `ota_order_bak` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`order_id` varchar(255) DEFAULT NULL COMMENT '订单id',
`ota_id` varchar(255) DEFAULT NULL COMMENT 'ota',
`check_in_date` varchar(255) DEFAULT NULL COMMENT '入住日期',
`check_out_date` varchar(255) DEFAULT NULL COMMENT '离店日期',
`hotel_id` varchar(255) DEFAULT NULL COMMENT '酒店ID',
`guest_name` varchar(255) DEFAULT NULL COMMENT '顾客',
`purcharse_time` timestamp NULL DEFAULT NULL COMMENT '购买时间',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`create_user` varchar(255) DEFAULT NULL,
`update_user` varchar(255) DEFAULT NULL,
`status` int(4) DEFAULT '1' COMMENT '状态 : 1 正常 , 0 删除',
`hotel_name` varchar(255) DEFAULT NULL,
`price` decimal(10,0) DEFAULT NULL,
`remark` longtext,
PRIMARY KEY (`id`),
KEY `IDX_order_id` (`order_id`) USING BTREE,
KEY `hotel_name` (`hotel_name`) USING BTREE,
KEY `ota_id` (`ota_id`) USING BTREE,
KEY `IDX_purcharse_time` (`purcharse_time`) USING BTREE,
KEY `IDX_create_time` (`create_time`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE (to_days(create_time)) (
PARTITION p201808 VALUES LESS THAN (to_days('2018-09-01')),
PARTITION p201809 VALUES LESS THAN (to_days('2018-10-01')),
PARTITION p201810 VALUES LESS THAN (to_days('2018-11-01')),
PARTITION p201811 VALUES LESS THAN (to_days('2018-12-01')),
PARTITION p201812 VALUES LESS THAN (to_days('2019-01-01')),
PARTITION p201901 VALUES LESS THAN (to_days('2019-02-01')),
PARTITION p201902 VALUES LESS THAN (to_days('2019-03-01')),
PARTITION p201903 VALUES LESS THAN (to_days('2019-04-01')),
PARTITION p201904 VALUES LESS THAN (to_days('2019-05-01')),
PARTITION p201905 VALUES LESS THAN (to_days('2019-06-01')),
PARTITION p201906 VALUES LESS THAN (to_days('2019-07-01')),
PARTITION p201907 VALUES LESS THAN (to_days('2019-08-01')),
PARTITION p201908 VALUES LESS THAN (to_days('2019-09-01')),
PARTITION p201909 VALUES LESS THAN (to_days('2019-10-01')),
PARTITION p201910 VALUES LESS THAN (to_days('2019-11-01')),
PARTITION p201911 VALUES LESS THAN (to_days('2019-12-01')),
PARTITION p201912 VALUES LESS THAN (to_days('2020-01-01')));
#2. 插入原表中无效的数据(需要跟开发同学确认数据保留范围)
create table tbl_p201808 as select * from ota_order where create_time between '2018-08-01 00:00:00' and '2018-08-31 23:59:59';
#3. 跟归档表分区做分区交换
alter table ota_order_bak exchange partition p201808 with table tbl_p201808;
#4. 删除原表中已经规范的数据
delete from ota_order where create_time between '2018-08-01 00:00:00' and '2018-08-31 23:59:59' limit 3000;
Оптимизированный способ подачи
#1. 创建中间表
CREATE TABLE `ota_order_2020` (........) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE (to_days(create_time)) (
PARTITION p201808 VALUES LESS THAN (to_days('2018-09-01')),
PARTITION p201809 VALUES LESS THAN (to_days('2018-10-01')),
PARTITION p201810 VALUES LESS THAN (to_days('2018-11-01')),
PARTITION p201811 VALUES LESS THAN (to_days('2018-12-01')),
PARTITION p201812 VALUES LESS THAN (to_days('2019-01-01')),
PARTITION p201901 VALUES LESS THAN (to_days('2019-02-01')),
PARTITION p201902 VALUES LESS THAN (to_days('2019-03-01')),
PARTITION p201903 VALUES LESS THAN (to_days('2019-04-01')),
PARTITION p201904 VALUES LESS THAN (to_days('2019-05-01')),
PARTITION p201905 VALUES LESS THAN (to_days('2019-06-01')),
PARTITION p201906 VALUES LESS THAN (to_days('2019-07-01')),
PARTITION p201907 VALUES LESS THAN (to_days('2019-08-01')),
PARTITION p201908 VALUES LESS THAN (to_days('2019-09-01')),
PARTITION p201909 VALUES LESS THAN (to_days('2019-10-01')),
PARTITION p201910 VALUES LESS THAN (to_days('2019-11-01')),
PARTITION p201911 VALUES LESS THAN (to_days('2019-12-01')),
PARTITION p201912 VALUES LESS THAN (to_days('2020-01-01')));
#2. 插入原表中有效的数据,如果数据量在100W左右可以在业务低峰期直接插入,如果比较大,建议采用dataX来做,可以控制频率和大小,之前我这边用Go封装了dataX可以实现自动生成json文件,自定义大小去执行。
insert into ota_order_2020 select * from ota_order where create_time between '2020-08-01 00:00:00' and '2020-08-31 23:59:59';
#3. 表重命名
alter table ota_order rename to ota_order_bak;
alter table ota_order_2020 rename to ota_order;
#4. 插入差异数据
insert into ota_order select * from ota_order_bak a where not exists (select 1 from ota_order b where a.id = b.id);
#5. ota_order_bak改造成分区表,如果表比较大不建议直接改造,可以先创建好分区表,通过dataX把导入进去即可。
#6. 后续的归档方法
#创建中间普遍表
create table ota_order_mid like ota_order;
#交换原表无效数据分区到普通表
alter table ota_order exchange partition p201808 with table ota_order_mid;
##交换普通表数据到归档表的相应分区
alter table ota_order_bak exchange partition p201808 with table ota_order_mid;
Таким образом, исходная таблица и заархивированная таблица представляют собой секционированные таблицы на ежемесячной основе.Необходимо только создать промежуточную обычную таблицу и выполнить два обмена секциями в период низкой пиковой нагрузки, что может не только удалять недействительные данные. , но и восстановить пустое пространство, и нет фрагментации пространства, что повлияет на индексы в таблице и план выполнения SQL.
Суммировать
Из распределения пространства хранения InnoDB и влияния удаления на производительность мы видим, что физическое удаление удаления не может освободить место на диске, но также генерирует большое количество фрагментов, что приводит к частым разбиениям индекса и влияет на стабильность планов выполнения SQL. ;
В то же время, когда фрагмент перерабатывается, он будет потреблять много ресурсов ЦП и дискового пространства, влияя на нормальную работу DML над таблицей.
На уровне бизнес-кода удаление логической метки должно быть выполнено, чтобы избежать физического удаления; для выполнения требований к архивированию данных это может быть достигнуто с помощью функции таблицы разделов MySQL, которая представляет собой все операции DDL без фрагментации.
Еще одним лучшим решением является Clickhouse, Clickhouse можно использовать для хранения таблиц данных с жизненным циклом и использовать функцию TTL для автоматической очистки недействительных данных.
Я Ао Бин,Чем больше вы знаете, тем больше вы не знаете, спасибо за ваши таланты:как,собиратьиКомментарий, увидимся в следующий раз!
Статья постоянно обновляется, вы можете искать в WeChat "Третий принц Ао Бин"Прочтите это в первый раз, ответьте [материал] Подготовленные мной материалы интервью и шаблоны резюме крупных заводов первой линии, эта статьяGitHub github.com/JavaFamilyОн был включен, и есть полные тестовые сайты для интервью с крупными заводами.Добро пожаловать в Star.