В этой статье много знаний и много места, пожалуйста, наберитесь терпения, чтобы изучить
В настоящее время MySQL стала основой продуктов реляционных баз данных, и ее предпочитают крупные интернет-компании. предложение будет сильно сокращено.
Зачем оптимизировать
- Узким местом пропускной способности системы часто является скорость доступа к базе данных.
- По мере работы приложения в базе данных будет все больше и больше данных, и время обработки будет соответственно медленнее
- Данные хранятся на диске, а скорость чтения и записи не идет ни в какое сравнение с памятью
Как оптимизировать
- При проектировании базы данных: таблица базы данных, дизайн поля, механизм хранения
- Эффективно используйте функции, предоставляемые самой MySQL, такие как индексы и т. д.
- Горизонтальное масштабирование: кластер MySQL, балансировка нагрузки, разделение чтения и записи.
- Оптимизация операторов SQL (с небольшим успехом)
полевой дизайн
Выбор типов полей, проектные спецификации, парадигмы, общие случаи проектирования
Принцип: попробуйте использовать целые числа для представления строк
Хранить IP
INET_ATON(str)
, адрес по номеру
INET_NTOA(number)
, номер для адреса
Тип перечисления (один выбор) и тип коллекции (множественный выбор) внутри MySQL
Однако он не часто используется из-за высокой стоимости обслуживания.Таблица ассоциацийспособ заменитьenum
Принцип: выбор типов данных фиксированной и нефиксированной длины.
Decimal не потеряет точности, а объем памяти будет увеличиваться с увеличением данных. double занимает фиксированное пространство, и хранение больших чисел потеряет точность. Также есть varchar, текст нефиксированной длины
количество
Точность данных высока, и есть проблемы с точностью при работе и хранении десятичных дробей (все десятичные дроби не могут быть преобразованы в двоичные).
десятичный с фиксированной запятой
price decimal(8,2)
Числа с фиксированной точкой с двумя десятичными знаками, числа с фиксированной точкой поддерживают очень большие числа (даже больше, чемint,bigint
количество диапазонов хранения)
Небольшие единицы и большие суммы, чтобы избежать десятичных знаков
юань -> центы
Хранение строк
Фиксированная длинаchar
, неопределенная длинаvarchar、text
(до 65535, гдеvarchar
также потребляет 1-3 байта длины записи, в то время какtext
Используйте дополнительное пространство для записи длины)
Принцип: выбирайте небольшие типы данных и указывайте как можно меньше длины.
Принцип: по возможности используйте not null
Нетnull
Поля обрабатываются болееnull
Обработка полей более эффективна! и не нужно судить о том,null
.
null
В MySQL это непросто, для хранения требуется дополнительное пространство, а для операций также требуются специальные операторы. какselect null = null
иselect null <> null
(<>
является знаком неравенства) имеет тот же результат, толькоis null
иis not null
определить, является ли полеnull
.
Как хранить? Каждая запись в MySQL требует дополнительного места для хранения, указывающего, является ли каждое полеnull
. Поэтому для заполнителей обычно используются специальные данные, такие какint not null default 0
,string not null default ‘’
Принцип: Аннотации полей должны быть полными, см. название
Принцип: в одной таблице не должно быть слишком много полей.
Двадцать или тридцать это предел
Принцип: Поля могут быть зарезервированы
Сначала удовлетворите потребности бизнеса, прежде чем использовать вышеуказанные принципы.
Дизайн таблицы ассоциаций
иностранный ключ
foreign key
Возможно только сопоставление «один к одному» или «один ко многим».
один ко многим
Использовать внешние ключи
многие ко многим
Создайте отдельную таблицу, чтобы разделить «многие ко многим» на две «один ко многим».
один на один
Например, основная информация о продукте (item
) и сведения о продукте (item_intro
), обычно используя тот же первичный ключ или добавляя поле внешнего ключа (item_id
)
Обычный формат
Спецификация дизайна таблицы данных, набор все более и более строгой системы спецификаций (если вам нужно соответствовать нормальной форме N, вы должны сначала выполнить нормальную форму N-1). Н
Первая нормальная форма 1NF: атомарность поля
Поля атомарны, и поля не могут быть далее разделены.
Реляционная база данных, первая нормальная форма по умолчанию
Обратите внимание на подверженный ошибкам момент. В схеме «один ко многим» запятые используются для разделения нескольких внешних ключей. Хотя этот метод удобен для хранения, он не способствует обслуживанию и индексации (например, поиску тегов с этикетки).java
статья)
Вторая нормальная форма: устранение частичных зависимостей от первичных ключей
То есть добавить в таблицу в качестве первичного ключа поле, не связанное с бизнес-логикой.
Первичный ключ: Поле или набор полей, которые однозначно идентифицируют запись.
course_name | course_class | рабочий день (день недели) | course_teacher |
---|---|---|---|
MySQL | Учебный корпус 1525 | в понедельник | Чжан Сан |
Java | Учебный корпус 1521 | среда | Ли Си |
MySQL | Учебный корпус 1521 | Пятница | Чжан Сан |
Зависимость: поле может определять поле B, тогда поле B зависит от поля A. Например, если вы знаете, что следующий урок будет уроком математики, вы можете определить, кто будет учителем. тогдакоторый деньиследующий урокИ может сформировать составной первичный ключ, который может определить, в какой класс идти, кто учитель и т. д. Но мы часто добавляемid
Как первичный ключ, так и устранить частичную зависимость от первичного ключа.
Частичная зависимость от первичного ключа: поле зависит от части составного первичного ключа.
Решение: добавьте отдельное поле в качестве первичного ключа.
Третья нормальная форма: устранение транзитивных зависимостей от первичных ключей
Транзитивная зависимость: поле B зависит от A, а поле C зависит от B. Например, в приведенном выше примере, кто является учителем, зависит от того, какой класс, а какой класс зависит от первичного ключа.id
. Следовательно, эту таблицу необходимо разделить на две таблицы: расписание и учебный план (независимые данные и независимые таблицы):
id | weekday | course_class | course_id |
---|---|---|---|
1001 | в понедельник | Учебный корпус 1521 | 3546 |
course_id | course_name | course_teacher |
---|---|---|
3546 | Java | Чжан Сан |
Это снижает избыточность данных (даже если занятия Java проводятся каждый день с понедельника по воскресенье, это толькоcourse_id:3546
появилось 7 раз)
выбор механизма хранения
Ранний вопрос: как выбрать между MyISAM и Innodb?
Этой проблемы больше нет, Innodb постоянно совершенствуется, догоняя MyISAM по всем параметрам, а также по умолчанию используется MySQL.
Механизм хранения Механизм хранения: то, как данные, индексы и другие объекты хранятся в MySQL, является реализацией набора файловых систем.
функциональные различия
show engines
Engine | Support | Comment |
---|---|---|
InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys |
MyISAM | YES | MyISAM storage engine |
разница хранения
MyISAM | Innodb | |
---|---|---|
формат файла | Данные и индексы хранятся отдельно, данные.MYD ,показатель.MYI
|
Данные и индексы хранятся централизованно,.ibd
|
Можно ли перенести файл | Да, таблица соответствует.frm ,MYD ,MYI 3 файла |
Нет, потому что есть такжеdata другие файлы под |
порядок хранения записей | Сохранить в порядке размещения записи | Вставить по размеру первичного ключа |
фрагментация пространства (удаление записей иflush table 表名 После этого размер файла таблицы не меняется) |
производить. Время: Используйте командуoptimize table 表名 выполнить |
не производит |
дела | не поддерживается | служба поддержки |
иностранный ключ | не поддерживается | служба поддержки |
Поддержка блокировки (блокировки — это механизм, позволяющий избежать конкуренции за ресурсы, блокировки MySQL почти прозрачны для пользователей) | блокировка на уровне таблицы | Блокировка на уровне строки, блокировка на уровне таблицы, низкое усилие блокировки, высокая возможность параллелизма |
расширение блокировки
блокировки на уровне таблицы (
table-level lock
):lock tables <table_name1>,<table_name2>... read/write
,unlock tables <table_name1>,<table_name2>...
. вread
Это общая блокировка, и однажды заблокированная, она не может быть прочитана ни одним клиентом;write
Это исключительная блокировка/запись, только заблокированный клиент может читать и писать, а другие клиенты не могут ни читать, ни писать. Блокируется одна таблица или несколько таблиц.блокировка на уровне строки (
row-level lock
): блокирует одну или несколько строк записей. Общий замок:select * from <table_name> where <条件> LOCK IN SHARE MODE;
, добавить общую блокировку в запись запроса;select * from <table_name> where <条件> FOR UPDATE;
, добавьте монопольную блокировку к записи запроса. здесьпримечательныйявляется:innodb
Блокировка строки на самом деле является блокировкой поддиапазона, которая блокирует часть диапазона в соответствии с условием, а не сопоставляет ее с определенной строкой, поэтому у нее также есть научное название: блокировка пробела. Напримерselect * from stu where id < 20 LOCK IN SHARE MODE
заблокируетid
существует20
вокруг следующего диапазона, вы не сможете вставитьid
за18
или22
новый рекорд.
основа выбора
Если нет особых требований, используйте значение по умолчанию.Innodb
Вот и все.
MyISAM: Приложения, основанные на вставке для чтения и записи, такие как системы блогов, новостные порталы.
Innodb: Частота операций обновления (удаления) также высока, иначе должна быть гарантирована целостность данных; параллелизм высок, а транзакции и внешние ключи поддерживаются для обеспечения целостности данных. Например, автоматизированная офисная система OA.
показатель
Отношение отображения между ключевыми словами и данными называется индексом (
Содержит ключевое слово и соответствующий адрес, записанный на диск). Ключевые слова — это определенный контент, извлеченный из данных для идентификации и извлечения данных.
Почему поиск индекса выполняется быстро?
- Ключевое слово относится к самим данным,небольшой объем данных
- ключевое словоаккуратныйДа, бинарный поиск может быстро определить местоположение
Библиотеки добавляют порядковые номера к каждой книге (категория-этаж-полка), словари составляют списки в алфавитном порядке для пояснений слов и так далее.
Типы индексов в MySQL
нормальный индекс(
key
),уникальный индекс(unique key
),индекс первичного ключа(primary key
),полный текстовый указатель(fulltext key
)
Методы индексации трех индексов одинаковы, но существуют разные ограничения на ключевые слова индекса:
- Обычный индекс: без ограничений по ключевым словам
- Уникальный индекс: ключ, предоставленный записью, не может быть повторен
- Индекс первичного ключа: требуется, чтобы ключ был уникальным, а не нулевым.
синтаксис управления индексом
индекс просмотра
show create table 表名
:
desc 表名
создать индекс
Создайте индекс после создания таблицы
create TABLE user_index(
id int auto_increment primary key,
first_name varchar(16),
last_name VARCHAR(16),
id_card VARCHAR(18),
information text
);
-- 更改表结构
alter table user_index
-- 创建一个first_name和last_name的复合索引,并命名为name
add key name (first_name,last_name),
-- 创建一个id_card的唯一索引,默认以字段名作为索引名
add UNIQUE KEY (id_card),
-- 鸡肋,全文索引不支持中文
add FULLTEXT KEY (information);
show create table user_index
:
Укажите индекс при создании таблицы
CREATE TABLE user_index2 (
id INT auto_increment PRIMARY KEY,
first_name VARCHAR (16),
last_name VARCHAR (16),
id_card VARCHAR (18),
information text,
KEY name (first_name, last_name),
FULLTEXT KEY (information),
UNIQUE KEY (id_card)
);
падение индекса
Удалите обычные индексы, уникальные индексы и полнотекстовые индексы в соответствии с именем индекса:alter table 表名 drop KEY 索引名
alter table user_index drop KEY name;
alter table user_index drop KEY id_card;
alter table user_index drop KEY information;
Удалите индекс первичного ключа:alter table 表名 drop primary key
(потому что есть только один первичный ключ). Здесь стоит отметить, что вы не можете сделать это напрямую, если первичный ключ автоматически увеличивается (автоинкремент зависит от индекса первичного ключа):
Нужно отменить саморазвитие и потом удалить:
alter table user_index
-- 重新定义字段
MODIFY id int,
drop PRIMARY KEY
Однако первичный ключ обычно не удаляется, поскольку структура первичного ключа не должна иметь отношения к бизнес-логике.
план выполненияобъяснить
CREATE TABLE innodb1 (
id INT auto_increment PRIMARY KEY,
first_name VARCHAR (16),
last_name VARCHAR (16),
id_card VARCHAR (18),
information text,
KEY name (first_name, last_name),
FULLTEXT KEY (information),
UNIQUE KEY (id_card)
);
insert into innodb1 (first_name,last_name,id_card,information) values ('张','三','1001','华山派');
мы можем пройтиexplain selelct
Чтобы проанализировать план выполнения перед выполнением оператора SQL:
Из приведенного выше рисунка видно, что этот оператор SQL извлекается в соответствии с индексом первичного ключа.
План выполнения таков: при выполнении оператора SQL он сначала проанализирует и оптимизирует, чтобы сформировать план выполнения, а затем выполнит его в соответствии с планом выполнения.
Сценарии использования индекса (выделено)
where
На рисунке выше, согласноid
записи запросов, потому чтоid
Для поля устанавливается только индекс первичного ключа, поэтому необязательным индексом для этого выполнения SQL является только индекс первичного ключа.Если их несколько, в качестве основы для извлечения будет выбран лучший.
-- 增加一个没有建立索引的字段
alter table innodb1 add sex char(1);
-- 按sex检索时可选的索引为null
EXPLAIN SELECT * from innodb1 where sex='男';
Когда поле не проиндексировано, вы можете попытаться проиндексировать поле в соответствии с эффективностью запроса поля (
alter table 表名 add index(字段名)
), та же эффективность выполнения SQL, вы обнаружите, что эффективность запросов будет значительно повышена (чем больше объем данных, тем более очевидно).
order by
когда мы используемorder by
При сортировке результатов запроса по полю, если поле не проиндексировано, план выполнения будет использовать внешнюю сортировку для всех запрошенных данных (считывать данные с жесткого диска в память пакетами и использовать внутреннюю сортировку, и, наконец, объединить результаты сортировки), эта операция сильно влияет на производительность, поскольку все данные, участвующие в запросе, необходимо считать с диска в память (если один фрагмент данных слишком велик или объем данных слишком велик, эффективность снизится), не говоря уже о сортировке после чтения памяти.
Но если мы проиндексируем полеalter table 表名 add index(字段名)
, то, поскольку сам индекс упорядочен, данные можно извлекать один за другим непосредственно в соответствии с порядком индекса и отношением отображения. И если он разбит на страницы, то просто используйтеПолучить данные, соответствующие индексу в определенном диапазоне индексной таблицы, вместо вышеуказанногоудалить все данныеСортировка и возврат диапазона данных. (извлечение данных с диска больше всего влияет на производительность)
join
правильно
join
Отношение сопоставления утверждений (on
) индексирование задействованных полей может повысить эффективность
покрытие индекса
Если все запрашиваемые поля были проиндексированы, механизм будет напрямую запрашивать индексную таблицу, не обращаясь к исходным данным (в противном случае, пока одно поле не проиндексировано, будет выполнено полное сканирование таблицы), что называется индексным покрытием. . Поэтому нам нужноselect
Задний
Здесь стоит отметить, что вы не думаете об индексации каждого поля, потому что преимущество использования индекса в первую очередь заключается в его небольшом размере.
Детали синтаксиса (точки)
В сценарии, соответствующем использованию индекса (
where/order by/join on
или охват индекса), индекс не обязательно используется
Поля должны отображаться независимо
Например, следующие две инструкции SQL семантически идентичны, но первая будет использовать индекс первичного ключа, а вторая — нет.
select * from user where id = 20-1;
select * from user where id+1 = 20;
как запрос, не может начинаться с подстановочных знаков
Например, заголовок поиска содержитmysql
статья:
select * from article where title like '%mysql%';
Этот план выполнения SQL не использует индексы (like
Выражение сопоставления операторов начинается с подстановочного знака), поэтому оно может выполнять только полное сканирование таблицы, что крайне неэффективно и почти не используется в практических проектах. Как правило, используется полнотекстовый индекс, поддерживающий китайский язык, предоставленный третьей стороной.
нозапрос ключевого словаФункция напоминания о горячем поиске все еще может быть выполнена, например, набравmysql
напомнить позжеmysql 教程
,mysql 下载
,mysql 安装步骤
Ждать. Используются следующие предложения:
select * from article where title like 'mysql%';
этоlike
Можно использовать индекс (конечно, при условии, чтоtitle
поле индексируется).
Составные индексы действительны только для первого поля.
Создайте составной индекс:
alter table person add index(first_name,last_name);
Принцип заключается в том, чтобы сначала установить индекс в соответствии сfirst_name
Отсортируйте ключевые слова, извлеченные изlast_name
Извлеченные ключевые слова сортируются, то есть индексная таблица основана только на записях.first_name
Значения полей упорядочены.
следовательноselect * from person where first_name = ?
индекс доступен иselect * from person where last_name = ?
Невозможно использовать index.
Итак, каков сценарий применения этого составного индекса?
Комбинированный запрос
например, дляselect * person from first_name = ? and last_name = ?
, составной индекс сравниваетсяfirst_name
иlast_name
Само по себе индексирование более эффективно. Понятно, первый двоичный поиск по составному индексу сfirst_name = ?
сопоставление записей, а затем бинарный поиск в этих записях с помощьюlast_name
Соответствующие записи включают только одну индексную таблицу. Отдельное индексирование выполняется вfirst_name
Разделите индексную таблицу, чтобы найти иfirst_name = ?
совпадающие записи, затем вlast_name
Разделите индексную таблицу, чтобы найти иlast_name = ?
записи, пересечение двух.
или оба условия имеют доступные индексы
Если на одной стороне нет доступного индекса, это приведет к полному сканированию таблицы всего оператора SQL.
Значение статуса, не простой в использовании индекс
Поля значений статуса, такие как пол и платежный статус, часто имеют только несколько возможных значений.Даже если такие поля проиндексированы, они часто недоступны. Это связано с тем, что значение состояния может соответствовать большому количеству записей, и в этом случае MySQL сочтет, что использование индекса менее эффективно, чем полное сканирование таблицы, и, таким образом, отбросит индекс. Индекс - это произвольный доступ к диску, а полное сканирование таблицы - это последовательное обращение к диску.Это как 20-ти этажное офисное здание.Указательная доска под этажом говорит о том, что некая компания соответствует нескольким этажам, которые не являются соседними Идите в компанию, чтобы найти людей, лучше подняться по одному наверх с 1-го этажа и найти верхний этаж, вместо того, чтобы по инструкциям каталожной карты пойти на один из этажей и не найти его, тогда приходите вниз, чтобы прочитать учетную карточку, а затем подняться наверх.
Как создать индекс
- Построить базовый индекс: at
where、order by、join
Индекс на поле. - Оптимизация, составной индекс: на основе бизнес-логики
- Если условия часто появляются вместе, рассмотрите возможность обновления многостолбцового индекса досоставной индекс
- Если за счет увеличения индекса отдельных полей может появитьсяпокрытие индекса, то вы можете рассмотреть возможность индексации этого поля
- При запросе индексы, которые обычно не используются, должны быть удалены.
- Если условия часто появляются вместе, рассмотрите возможность обновления многостолбцового индекса до
индекс префикса
грамматика:index(field(10))
, используйте первые 10 символов значения поля для построения индекса, по умолчанию для построения индекса используется все содержимое поля.
Предпосылка: Префикс имеет высокую степень идентификации. Например, пароли подходят для построения префиксного индекса, потому что пароли практически разные.
мы можем использоватьselect count(*)/count(distinct left(password,prefixLen));
, регулируя отprefixLen
Значение (самоувеличивающееся от 1) для просмотра средней степени совпадения различных длин префикса, хорошо, когда оно близко к 1 (представляющее префикс пароля)prefixLen
символы могут почти определить уникальную запись)
BTree
btree (многостороннее сбалансированное дерево поиска) является широко используемым
отadd index(first_name,last_name)
Например:
Узел BTree может хранить несколько ключевых слов, а размер узла зависит от файловой системы компьютера, поэтому мы можем заставить узел хранить больше ключевых слов, уменьшив длину поля индекса. Если ключевые слова в узле заполнены, таблица индексов может быть расширена с помощью указателей дочерних узлов между каждым ключевым словом, но порядок структуры не может быть разрушен, например, согласноfirst_name
Первый заказ,last_name
Второе упорядоченное правило, недавно добавленное韩香
может быть подключен к韩康
после.白起 < 韩飞 < 韩康 < 李世民 < 赵奢 < 李寻欢 < 王语嫣 < 杨不悔
. Это та же идея, что и у бинарного дерева поиска, за исключением того, что эффективность поиска бинарного дерева поиска равнаlog(2,N)
(логарифм N по основанию 2), а эффективность поиска BTree равнаlog(x,N)
(где x — количество ключевых слов узла, которое может достигать более 1000).
отlog(1000+,N)
Можно видеть, что небольшое количество операций чтения с диска может пройти через большой объем данных, что также является целью разработки btree.
Структура кластера B+Tree
В структуре кластера (также обновленной на BTree) ключевые слова и записи хранятся вместе.
Только в MySQLInnodb
из
Innodb
Индексы непервичных ключей являются типичными структурами BTree.
хэш-индекс
Когда индекс загружается в память, для его хранения используется хэш-структура.
кэш запросов
тайник
select
результат запроса оператора
на окнах даmy.ini
, в линуксе естьmy.cnf
существует[mysqld]
конфигурация в сегментеquery_cache_type
:
- 0: не включено
- 1: включено, все кеши кэшируются по умолчанию, и их необходимо добавить в операторе SQL.
select sql-no-cache
предложить сбросить кеш - 2: On, по умолчанию не кешируется, его нужно добавить в оператор SQL
select sql-cache
активно кэшировать (Обычно используется)
После изменения конфигурации необходимо выполнить перезагрузку, чтобы конфигурация вступила в силу.После перезагрузки можно пройтиshow variables like ‘query_cache_type’;
видеть:
show variables like 'query_cache_type';
query_cache_type DEMAND
через элементы конфигурацииquery_cache_size
устанавливать:
show variables like 'query_cache_size';
query_cache_size 0
set global query_cache_size=64*1024*1024;
show variables like 'query_cache_size';
query_cache_size 67108864
select sql_cache * from user;
reset query cache;
При изменении таблицы данных все кэши, основанные на этой таблице данных, удаляются. (Управление на поверхностном уровне не является управлением на уровне записи, поэтому частота отказов выше)
- приложение, не должно заботиться
query cache
использование. Вы можете попробовать использовать его, но неquery cache
Определитесь с бизнес-логикой, потому чтоquery cache
Управляется администратором баз данных. - Кэш хранится с оператором SQL в качестве ключа, поэтому, даже если оператор SQL имеет ту же функцию, если есть дополнительное пространство или другой регистр, кеш не будет сопоставлен.
раздел
В общем, таблица, которую мы создаем, соответствует набору файлов хранилища, используяMyISAM
механизм хранения.MYI
и.MYD
файл, использоватьInnodb
механизм хранения.ibd
и.frm
(структура таблицы).
Когда объем данных большой (как правило, более 10 миллионов записей), производительность MySQL начнет снижаться.В это время нам необходимо распределить данные по нескольким наборам файлов хранилища.
Наиболее распространенная схема разделенияid
раздел следующим образомid
Хэш-значение по модулю 10 для равномерного распределения данных по 10..ibd
В файле хранилища:
create table article(
id int auto_increment PRIMARY KEY,
title varchar(64),
content text
)PARTITION by HASH(id) PARTITIONS 10
Проверятьdata
содержание:
Разбиение таблиц на стороне сервера прозрачно для клиентов, клиент по-прежнему вставляет данные как обычно, но сервер будет хранить данные децентрализованно в соответствии с алгоритмом разделения.
Поле для разделения должно быть частью первичного ключа, раздел предназначен для быстрого нахождения данных, поэтому частота поиска по этому полю высока и его следует использовать как сильное поле поиска, иначе нет смысла разбивать по этому полю
hash(field)
Тот же вход дает тот же результат. Результаты вывода не имеют ничего общего с тем, является ли ввод регулярным или нет.
key(field)
иhash(field)
того же характера, за исключением того, чтоkey
да
hash()
Еще один шаг — вычислить целое число из строки, чтобы выполнить операцию по модулю.
create table article_key(
id int auto_increment,
title varchar(64),
content text,
PRIMARY KEY (id,title) -- 要求分区依据字段必须是主键的一部分
)PARTITION by KEY(title) PARTITIONS 10
алгоритм диапазона
это
Таким образом, согласно времени публикации статьи, данные хранятся в разделах в августе, сентябре и октябре 2018 года:
create table article_range(
id int auto_increment,
title varchar(64),
content text,
created_time int, -- 发布时间到1970-1-1的毫秒数
PRIMARY KEY (id,created_time) -- 要求分区依据字段必须是主键的一部分
)charset=utf8
PARTITION BY RANGE(created_time)(
PARTITION p201808 VALUES less than (1535731199), -- select UNIX_TIMESTAMP('2018-8-31 23:59:59')
PARTITION p201809 VALUES less than (1538323199), -- 2018-9-30 23:59:59
PARTITION p201810 VALUES less than (1541001599) -- 2018-10-31 23:59:59
);
Примечание. Условные операторы можно использовать только с
p201808,p201819,p201810
Порядок, в котором определяются разделы,created_time
Значения варьируются от малых до больших и не могут быть обращены вспять.
insert into article_range values(null,'MySQL优化','内容示例',1535731180);
flush tables; -- 使操作立即刷新到磁盘文件
В связи со временем публикации вставленной статьи1535731180
меньше, чем1535731199
(2018-8-31 23:59:59
), поэтому хранится вp201808
В каком разделе хранится этот алгоритм, зависит от состояния данных.
алгоритм списка
Это также условное разбиение, разбиение по значению списка (in (值列表)
).
create table article_list(
id int auto_increment,
title varchar(64),
content text,
status TINYINT(1), -- 文章状态:0-草稿,1-完成但未发布,2-已发布
PRIMARY KEY (id,status) -- 要求分区依据字段必须是主键的一部分
)charset=utf8
PARTITION BY list(status)(
PARTITION writing values in(0,1), -- 未发布的放在一个分区
PARTITION published values in (2) -- 已发布的放在一个分区
);
insert into article_list values(null,'mysql优化','内容示例',0);
flush tables;
range/list
добавить раздел
В предыдущей статье мы пытались использоватьrange
Статьи архивируются по месяцам, по мере увеличения времени нужно добавлять месяц:
alter table article_range add partition(
partition p201811 values less than (1543593599) -- select UNIX_TIMESTAMP('2018-11-30 23:59:59')
-- more
);
удалить раздел
alter table article_range drop PARTITION p201808
Уведомление:
key/hash
Добавить раздел
alter table article_key add partition partitions 4
уничтожить раздел
alter table article_key coalesce partition 6
key/hash
Управление разделами не удаляет данные, но каждая корректировка (добавление или удаление раздела) перезаписывает все данные в новый раздел.
Когда объем данных в таблице данных велик, повышение эффективности, вызванное секционированием, будет только проявляться.
Повышение эффективности, вызванное секционированием, будет более очевидным только в том случае, если поле поиска является секционированным полем. следовательно,
Горизонтальное разделение и вертикальное разделение
Горизонтальное разделение: храните данные отдельно, создавая несколько таблиц с одинаковой структурой.
Вертикальное разбиение: Поместите поля, которые часто используются вместе, в отдельную таблицу, и между записями разделенной таблицы будет взаимно однозначное соответствие.
- Распаковать базу данных
- Ограничения алгоритма разделения
- Поддержка базы данных не полная (
5.1
послеmysql
Поддерживает только работу с разделами)
- Заимствуйте сторонние приложения, такие как
memcache、redis
изid
автоматическое приращение - Создайте отдельный, содержащий только
id
Таблица полей, каждый раз, когда поле увеличивается как запись данныхid
кластер
Горизонтальное расширение: Принципиально (производительность аппаратной обработки одной машины ограничена) для повышения производительности базы данных. Связанные технологии из этого:
Разделение чтения-записи, балансировка нагрузки
окрестности
-
Red Hat Enterprise Linux Server release 7.0 (Maipo)
(виртуальная машина) -
mysql5.7
(ссылка для скачивания)
Установить и настроить
Разархивируйте в каталог службы, предоставляемой извне (я создал ее сам/export/server
хранить)
tar xzvf mysql-5.7.23-linux-glibc2.12-x86_64.tar.gz -C /export/server
cd /export/server
mv mysql-5.7.23-linux-glibc2.12-x86_64 mysql
Добавить кmysql
Группа и владелец каталога:
groupadd mysql
useradd -r -g mysql mysql
cd /export/server
chown -R mysql:mysql mysql/
chmod -R 755 mysql/
Создайтеmysql
каталог хранения данных (где/export/data
это каталог, который я создал для хранения данных для различных сервисов)
mkdir /export/data/mysql
инициализацияmysql
Служить
cd /export/server/mysql
./bin/mysqld --basedir=/export/server/mysql --datadir=/export/data/mysql --user=mysql --pid-file=/export/data/mysql/mysql.pid --initialize
В случае успеха он покажет
mysql
изroot
Начальный пароль учетной записи, запишите его для последующих входов. Если в ошибке отсутствуют зависимости, используйтеyum instally
Установить последовательно
настроитьmy.cnf
vim /etc/my.cnf
[mysqld]
basedir=/export/server/mysql
datadir=/export/data/mysql
socket=/tmp/mysql.sock
user=mysql
server-id=10 # 服务id,在集群时必须唯一,建议设置为IP的第四段
port=3306
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mariadb according to the
# instructions in http://fedoraproject.org/wiki/Systemd
[mysqld_safe]
log-error=/export/data/mysql/error.log
pid-file=/export/data/mysql/mysql.pid
#
# include all files from the config directory
#
!includedir /etc/my.cnf.d
Добавить сервис в автозапуск при загрузке
cp /export/server/mysql/support-files/mysql.server /etc/init.d/mysqld
запустить службу
service mysqld start
Настройте переменные среды в/etc/profile
добавить следующее в
# mysql env
MYSQL_HOME=/export/server/mysql
MYSQL_PATH=$MYSQL_HOME/bin
PATH=$PATH:$MYSQL_PATH
export PATH
Сделайте так, чтобы конфигурация вступила в силу
source /etc/profile
использоватьroot
Авторизоваться
mysql -uroot -p
# 这里填写之前初始化服务时提供的密码
После авторизации изменитеroot
Пароль учетной записи (пароль для удобства поменял на root), иначе при работе с БД будет сообщение об ошибке
set password=password('root');
flush privileges;
Сделать сервис доступным для всех удаленных клиентов
use mysql;
update user set host='%' where user='root';
flush privileges;
Это можно использовать на хосте
navicat
Удаленно подключиться к mysql на виртуальной машине linux
Настройте главные и подчиненные узлы
настроить мастер
отlinux
(192.168.10.10
)Вверхmysql
заmaster
, гостья (192.168.10.1
)Вверхmysql
заslave
Настройте репликацию master-slave.
Исправлятьmaster
изmy.cnf
следующее
[mysqld]
basedir=/export/server/mysql
datadir=/export/data/mysql
socket=/tmp/mysql.sock
user=mysql
server-id=10
port=3306
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mariadb according to the
# instructions in http://fedoraproject.org/wiki/Systemd
log-bin=mysql-bin # 开启二进制日志
expire-logs-days=7 # 设置日志过期时间,避免占满磁盘
binlog-ignore-db=mysql # 不使用主从复制的数据库
binlog-ignore-db=information_schema
binlog-ignore-db=performation_schema
binlog-ignore-db=sys
binlog-do-db=test #使用主从复制的数据库
[mysqld_safe]
log-error=/export/data/mysql/error.log
pid-file=/export/data/mysql/mysql.pid
#
# include all files from the config directory
#
!includedir /etc/my.cnf.d
перезагружатьmaster
service mysqld restart
Авторизоватьсяmaster
Проверьте, вступила ли конфигурация в силу (ON
включен, по умолчаниюOFF
):
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
существуетmaster
Создайте резервную учетную запись в базе данных:backup
это имя пользователя,%
представляет собой любой удаленный адрес, пользователяback
можно использовать пароль1234
Подключение через любой удаленный клиентmaster
grant replication slave on *.* to 'backup'@'%' identified by '1234'
Проверятьuser
В таблице виден только что созданный нами пользователь:
mysql> use mysql
mysql> select user,authentication_string,host from user;
+---------------+-------------------------------------------+-----------+
| user | authentication_string | host |
+---------------+-------------------------------------------+-----------+
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B | % |
| mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | localhost |
| mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | localhost |
| backup | *A4B6157319038724E3560894F7F932C8886EBFCF | % |
+---------------+-------------------------------------------+-----------+
новыйtest
базу данных, создатьarticle
форму для последующего тестирования
CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(64) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`)
) CHARSET=utf8;
Перезапустите службу и сбросьте состояние базы данных в файл хранилища (with read lock
Указывает, что во время этого процесса клиент может только считывать данные для получения согласованного моментального снимка)
[root@zhenganwen ~]# service mysqld restart
Shutting down MySQL.... SUCCESS!
Starting MySQL. SUCCESS!
[root@zhenganwen mysql]# mysql -uroot -proot
mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)
Проверятьmaster
в текущем двоичном журнале и смещении (обратите внимание наFile
иPosition
)
mysql> show master status \G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 154
Binlog_Do_DB: test
Binlog_Ignore_DB: mysql,information_schema,performation_schema,sys
Executed_Gtid_Set:
1 row in set (0.00 sec)
File
Указывает лог, реализующий функцию репликации, то есть приведенный выше рисунокBinary log
;Position
означаетBinary log
После смещения файл журнала будет синхронизирован сslave
, то те, что перед смещением, необходимо импортировать вручную.
Любая модификация на главном сервере будет сохранена в двоичном журнале, запустить поток ввода-вывода с сервера (фактически клиентский процесс основного сервера), подключиться к главному серверу, чтобы запросить чтение двоичного журнала, а затем поместить Прочитанный двоичный журнал записывается в локальный журнал Realy. Запустите поток SQL с сервера, чтобы регулярно проверять журнал Realy, и, если будут обнаружены какие-либо изменения, немедленно выполнить измененное содержимое на локальном компьютере.
Если есть один ведущий и несколько подчиненных, то основная библиотека отвечает как за запись, так и за предоставление двоичных журналов для нескольких подчиненных библиотек. В это время вы можете сделать небольшую настройку и передать двоичный журнал только одному подчиненному устройству, тогда это подчиненное устройство откроет двоичный журнал и отправит свой собственный двоичный журнал другим подчиненным устройствам. Или просто это никогда не записывает, отвечает только за пересылку бинарных логов другим подчиненным, поэтому производительность может быть намного лучше в архитектуре, и задержка между данными должна быть немного лучше.
Импорт вручную изmaster
экспортировать данные в
mysqldump -uroot -proot -hlocalhost test > /export/data/test.sql
будетtest.sql
Содержание вslave
Выполните его снова.
Настроить ведомое устройство
Исправлятьslave
изmy.ini
в файле[mysqld]
часть
log-bin=mysql
server-id=1 #192.168.10.1
Перезапустите после сохранения измененийslave
,WIN+R
->services.msc
->MySQL5.7
-> перезагрузить
Авторизоватьсяslave
экзаменlog_bin
Следует ли включить:
show VARIABLES like 'log_bin';
настроить сmaster
Синхронная репликация:
stop slave;
change master to
master_host='192.168.10.10', -- master的IP
master_user='backup', -- 之前在master上创建的用户
master_password='1234',
master_log_file='mysql-bin.000002', -- master上 show master status \G 提供的信息
master_log_pos=154;
включитьslave
узел и просмотр состояния
mysql> start slave;
mysql> show slave status \G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.10.10
Master_User: backup
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 154
Relay_Log_File: DESKTOP-KUBSPE0-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000002
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 154
Relay_Log_Space: 537
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 10
Master_UUID: f68774b7-0b28-11e9-a925-000c290abe05
Master_Info_File: C:\ProgramData\MySQL\MySQL Server 5.7\Data\master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
Обратите внимание на строки 4, 14 и 15. Если они совпадают со мной, значит
slave
Настроено успешно
контрольная работа
закрытиеmaster
блокировка чтения
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
В направленииmaster
вставить часть данных
mysql> use test
mysql> insert into article (title,content) values ('mysql master and slave','record the cluster building succeed!:)');
Query OK, 1 row affected (0.00 sec)
Проверятьslave
Синхронизируются ли данные автоматически
mysql> insert into article (title,content) values ('mysql master and slave','record the cluster building succeed!:)');
Query OK, 1 row affected (0.00 sec)
На данный момент настройка репликации master-slave прошла успешно! :)
Используйте команду mysqlreplicate для быстрой сборки репликации master-slave Mysql.
Разделение чтения-записи основано на репликации ведущий-ведомый, которая, в свою очередь, обеспечивает разделение чтения-записи. Поскольку для репликации master-slave требуетсяslave
Не могу писать, могу только читать (если правильноslave
выполнить операцию записи, затемshow slave status
представитSlave_SQL_Running=NO
, на этом этапе вам нужно вручную синхронизировать, как упоминалось ранееslave
).
Схема 1. Определить два подключения
Как мы определили, когда изучали JDBCDataBase
Точно так же мы можем извлечьReadDataBase,WriteDataBase implements DataBase
, но таким образом нельзя использовать превосходную технологию пула потоков, такую какDruidDataSource
Помогите нам управлять соединениями, также не можете воспользоватьсяSpring AOP
сделать пару соединенийDAO
слой прозрачный.
Вариант 2, используя Spring AOP
если вы можете использоватьSpring AOP
Чтобы решить проблему переключения источника данных, вы можетеMybatis
,Druid
интегрированы вместе.
мы интегрируемSpring1
иMybatis
, нам нужно только написать интерфейс DAO и соответствующийSQL
утверждение, то кто создал экземпляр DAO? фактическиSpring
Созданный для нас, он использует источник данных, который мы внедрили, чтобы помочь нам получить от него соединение с базой данных и использовать это соединение для выполнения.SQL
Процесс оператора и, наконец, процесс возврата соединения с источником данных.
Если мы можем вызвать интерфейс DAO в соответствии с соглашением об именах методов интерфейса (увеличениеaddXXX/createXXX
,УдалитьdeleteXX/removeXXX
,изменятьupdateXXXX
,чекselectXX/findXXX/getXX/queryXXX
) динамически выбирать источник данных (чтение источника данных, соответствующее соединениюmaster
Источник данных записи соответствует соединениюslave
), тогда может быть достигнуто разделение чтения и записи.
Структура проекта
импортировать зависимости
Среди них, чтобы облегчить доступ к базе данных,mybatis
иdruid
, реализация динамического переключения источников данных в основном зависит отspring-aop
иspring-aspects
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
класс данных
package top.zhenganwen.mysqloptimize.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {
private int id;
private String title;
private String content;
}
файл конфигурации весны
вRoutingDataSourceImpl
Это основной класс, реализующий функцию динамического переключения, которая будет представлена позже.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="db.properties"></context:property-placeholder>
<context:component-scan base-package="top.zhenganwen.mysqloptimize"/>
<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driverClass}"/>
<property name="url" value="${master.db.url}"></property>
<property name="username" value="${master.db.username}"></property>
<property name="password" value="${master.db.password}"></property>
</bean>
<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driverClass}"/>
<property name="url" value="${slave.db.url}"></property>
<property name="username" value="${slave.db.username}"></property>
<property name="password" value="${slave.db.password}"></property>
</bean>
<bean id="dataSourceRouting" class="top.zhenganwen.mysqloptimize.dataSource.RoutingDataSourceImpl">
<property name="defaultTargetDataSource" ref="masterDataSource"></property>
<property name="targetDataSources">
<map key-type="java.lang.String" value-type="javax.sql.DataSource">
<entry key="read" value-ref="slaveDataSource"/>
<entry key="write" value-ref="masterDataSource"/>
</map>
</property>
<property name="methodType">
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="read" value="query,find,select,get,load,"></entry>
<entry key="write" value="update,add,create,delete,remove,modify"/>
</map>
</property>
</bean>
<!-- Mybatis文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="dataSourceRouting" />
<property name="mapperLocations" value="mapper/*.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="top.zhenganwen.mysqloptimize.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
</beans>
dp.properties
master.db.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
master.db.username=root
master.db.password=root
slave.db.url=jdbc:mysql://192.168.10.10:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
slave.db.username=root
slave.db.password=root
db.driverClass=com.mysql.jdbc.Driver
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="top.zhenganwen.mysqloptimize.entity.Article" alias="Article"/>
</typeAliases>
</configuration>
интерфейс маппера и конфигурационный файл
ArticleMapper.java
package top.zhenganwen.mysqloptimize.mapper;
import org.springframework.stereotype.Repository;
import top.zhenganwen.mysqloptimize.entity.Article;
import java.util.List;
@Repository
public interface ArticleMapper {
List<Article> findAll();
void add(Article article);
void delete(int id);
}
ArticleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="top.zhenganwen.mysqloptimize.mapper.ArticleMapper">
<select id="findAll" resultType="Article">
select * from article
</select>
<insert id="add" parameterType="Article">
insert into article (title,content) values (#{title},#{content})
</insert>
<delete id="delete" parameterType="int">
delete from article where id=#{id}
</delete>
</mapper>
основной класс
RoutingDataSourceImpl
package top.zhenganwen.mysqloptimize.dataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.*;
/**
* RoutingDataSourceImpl class
* 数据源路由
*
* @author zhenganwen, blog:zhenganwen.top
* @date 2018/12/29
*/
public class RoutingDataSourceImpl extends AbstractRoutingDataSource {
/**
* key为read或write
* value为DAO方法的前缀
* 什么前缀开头的方法使用读数据员,什么开头的方法使用写数据源
*/
public static final Map<String, List<String>> METHOD_TYPE_MAP = new HashMap<String, List<String>>();
/**
* 由我们指定数据源的id,由Spring切换数据源
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
System.out.println("数据源为:"+DataSourceHandler.getDataSource());
return DataSourceHandler.getDataSource();
}
public void setMethodType(Map<String, String> map) {
for (String type : map.keySet()) {
String methodPrefixList = map.get(type);
if (methodPrefixList != null) {
METHOD_TYPE_MAP.put(type, Arrays.asList(methodPrefixList.split(",")));
}
}
}
}
Его основная функция заключается в том, что изначально мы настроили только один источник данных, поэтомуSpring
Этот источник данных используется непосредственно при динамическом проксировании интерфейса DAO.Теперь, когда у нас есть два источника данных для чтения и записи, нам нужно добавить некоторую собственную логику, чтобы указать, какой интерфейс использовать, какой источник данных вызывать (интерфейс для чтения данные используетslave
, интерфейс для записи данных используетmaster
. это говоритSpring
Класс источника данных для использованияAbstractRoutingDataSource
, метод, который должен быть переопределенdetermineCurrentLookupKey
Возвращает идентификатор источника данных в сочетании сspring
Файл конфигурации (строки 5 и 6 следующего кода)
<bean id="dataSourceRouting" class="top.zhenganwen.mysqloptimize.dataSource.RoutingDataSourceImpl">
<property name="defaultTargetDataSource" ref="masterDataSource"></property>
<property name="targetDataSources">
<map key-type="java.lang.String" value-type="javax.sql.DataSource">
<entry key="read" value-ref="slaveDataSource"/>
<entry key="write" value-ref="masterDataSource"/>
</map>
</property>
<property name="methodType">
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="read" value="query,find,select,get,load,"></entry>
<entry key="write" value="update,add,create,delete,remove,modify"/>
</map>
</property>
</bean>
еслиdetermineCurrentLookupKey
возвращениеread
затем используйтеslaveDataSource
, если вернутьсяwrite
просто используйтеmasterDataSource
.
DataSourceHandler
package top.zhenganwen.mysqloptimize.dataSource;
/**
* DataSourceHandler class
* <p>
* 将数据源与线程绑定,需要时根据线程获取
*
* @author zhenganwen, blog:zhenganwen.top
* @date 2018/12/29
*/
public class DataSourceHandler {
/**
* 绑定的是read或write,表示使用读或写数据源
*/
private static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void setDataSource(String dataSource) {
System.out.println(Thread.currentThread().getName()+"设置了数据源类型");
holder.set(dataSource);
}
public static String getDataSource() {
System.out.println(Thread.currentThread().getName()+"获取了数据源类型");
return holder.get();
}
}
DataSourceAspect
package top.zhenganwen.mysqloptimize.dataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import static top.zhenganwen.mysqloptimize.dataSource.RoutingDataSourceImpl.METHOD_TYPE_MAP;
/**
* DataSourceAspect class
*
* 配置切面,根据方法前缀设置读、写数据源
* 项目启动时会加载该bean,并按照配置的切面(哪些切入点、如何增强)确定动态代理逻辑
* @author zhenganwen,blog:zhenganwen.top
* @date 2018/12/29
*/
@Component
//声明这是一个切面,这样Spring才会做相应的配置,否则只会当做简单的bean注入
@Aspect
@EnableAspectJAutoProxy
public class DataSourceAspect {
/**
* 配置切入点:DAO包下的所有类的所有方法
*/
@Pointcut("execution(* top.zhenganwen.mysqloptimize.mapper.*.*(..))")
public void aspect() {
}
/**
* 配置前置增强,对象是aspect()方法上配置的切入点
*/
@Before("aspect()")
public void before(JoinPoint point) {
String className = point.getTarget().getClass().getName();
String invokedMethod = point.getSignature().getName();
System.out.println("对 "+className+"$"+invokedMethod+" 做了前置增强,确定了要使用的数据源类型");
Set<String> dataSourceType = METHOD_TYPE_MAP.keySet();
for (String type : dataSourceType) {
List<String> prefixList = METHOD_TYPE_MAP.get(type);
for (String prefix : prefixList) {
if (invokedMethod.startsWith(prefix)) {
DataSourceHandler.setDataSource(type);
System.out.println("数据源为:"+type);
return;
}
}
}
}
}
Проверка разделения чтения и записи
Как проверить чтение из
slave
Что насчет средней школы? Копирование после записи может бытьslave
Данные в данных изменяются, а затем считывают данные, чтобы знать, чтоslave
читать в.Уведомление, но правильноslave
После завершения операции записи необходимо вручную сброситьslave
иmaster
Синхронизируйте, иначе репликация master-slave не удастся.
package top.zhenganwen.mysqloptimize.dataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import top.zhenganwen.mysqloptimize.entity.Article;
import top.zhenganwen.mysqloptimize.mapper.ArticleMapper;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-mybatis.xml")
public class RoutingDataSourceTest {
@Autowired
ArticleMapper articleMapper;
@Test
public void testRead() {
System.out.println(articleMapper.findAll());
}
@Test
public void testAdd() {
Article article = new Article(0, "我是新插入的文章", "测试是否能够写到master并且复制到slave中");
articleMapper.add(article);
}
@Test
public void testDelete() {
articleMapper.delete(2);
}
}
балансировки нагрузки
алгоритм балансировки нагрузки
- голосование
- Взвешенный циклический перебор: взвешивание по вычислительной мощности
- Распределение нагрузки: в соответствии с текущим состоянием простоя (но проверьте использование памяти, загрузку ЦП и т. д. каждого узла, а затем сравните и выберите самый простой, эффективность слишком низкая)
В серверной архитектуре, чтобы гарантировать, что сервер не будет отключен и подключен к сети 7x24, необходимо предоставить резервные машины для каждого одноточечного сервера (сервер, предоставляемый одним сервером, например, сервер записи, промежуточное программное обеспечение базы данных).
Для сервера записи должен быть предоставлен тот же сервер с резервированием записи.Когда сервер записи исправен (избыточность записи определяется пульсом), резервирование записи действует как ведомое устройство для репликации содержимого сервера записи и выполнения Синхронизация: когда сервер записи выходит из строя, появляется резервный сервер записи, который продолжает служить сервером записи. Этот процесс прозрачен для внешнего мира, то есть внешний мир получает доступ к сервису только через один IP.
Типичный SQL
DDL (язык определения базы данных) относится к определению структуры таблицы базы данных (create table
)И обслуживание(alter table
)язык. Выполните DDL онлайн, нижеMySQL5.6
Управление версиями приведет к тому, что вся таблица будет монопольно заблокирована. В настоящее время таблица находится в состоянии обслуживания и не работает, что приведет к тому, что любой доступ к таблице в течение этого периода не будет отвечать. Но когдаMySQL5.6
После этого поддержитеOnline DDL
, что значительно сокращает время блокировки.
Техника оптимизации заключается в использовании DDL для поддержки структуры таблицы (например, добавление столбца или добавление индекса).
Но с обновлением MySQL проблема практически исчезла.
При восстановлении данных может быть импортирован большой объем данных. На данный момент, чтобы импортировать быстро, вам нужно освоить некоторые навыки:
- при импортеСначала отключите индексы и ограничения:
alter table table-name disable keys
После завершения импорта данных откройте индекс и ограничения и одновременно создайте индекс.
alter table table-name enable keys
- база данных, если используемый движок
Innodb
, Затем этоПо умолчанию транзакция добавляется к каждой инструкции записи.(Это также займет определенное время), поэтому рекомендуется вручную запустить транзакцию, затем выполнить определенный объем пакетного импорта и, наконец, отправить транзакцию вручную. - Если формат команды SQL для пакетного импорта тот же, но данные разные, то сначала следует
prepare
предварительно скомпилированоЭто может сэкономить много времени на повторную компиляцию.
Старайтесь, чтобы не было большихoffset
,Напримерlimit 10000,10
Эквивалентно отбрасыванию количества запрошенных строк10000
взять за линию10
Line, вы можете добавить некоторые условия для фильтрации (заполнить фильтр), и не следует использоватьlimit
Пропустить уже запрошенные данные. Это
offset
делать бесполезную работуТо есть попробуй выбрать нужные тебе поляselect
, но это влияние не очень большое, потому что передача по сети составляет более десятков сотен байт без особых задержек, а сейчас используются популярные ORM-фреймворкиselect *
, но при оформлении таблицы мы уделяем внимание разделению полей с большим объемом данных, например, сведения о товаре можно отделить от таблицы сведений о товаре, чтобы это не повлияло на скорость загрузки при просмотре краткой страницы товара.
Его логика — случайная сортировка (генерирует случайное число для каждого фрагмента данных, а затем сортирует по размеру случайного числа). какselect * from student order by rand() limit 5
Эффективность выполнения очень низкая, потому что он генерирует случайные числа и сортирует для каждого фрагмента данных в таблице, а нам нужны только первые 5 фрагментов.
Решение. В приложении сгенерируйте случайный первичный ключ и используйте первичный ключ для его извлечения из базы данных.
Многотабличный запрос:join
, подзапросы — это запросы, включающие несколько таблиц. если вы используетеexplain
Проанализировав план выполнения, вы обнаружите, что многотабличный запрос также представляет собой потабличный процесс, и, наконец, результаты объединяются. Следовательно, можно сказать, что запрос к одной таблице оказывает вычислительную нагрузку на приложение, в то время как запрос с несколькими таблицами оказывает вычислительную нагрузку на базу данных.
Теперь есть структура ORM, которая поможет нам решить проблему сопоставления объектов, вызванную запросом одной таблицы (при запросе одной таблицы, если обнаружен внешний ключ для автоматического запроса связанной таблицы, это запрос таблицы за таблицей) .
существуетMyISAM
В механизме хранения количество строк в таблице записывается автоматически, поэтому используйтеcount(*)
возможность быстро вернуться. иInnodb
Внутри такого счетчика нет, нам нужно вручную считать количество записей, решение — использовать отдельную таблицу:
id | table | count |
---|---|---|
1 | student | 100 |
Если можно определить, что получен только один элемент, рекомендуется добавитьlimit 1
, на самом деле в этом нам помогает фреймворк ORM (автоматически добавится операция запроса одной записиlimit 1
).
журнал медленных запросов
Он используется для записи журналов SQL, время выполнения которых превышает определенный порог, который используется для быстрого обнаружения медленных запросов и служит ориентиром для нашей оптимизации.
Элемент конфигурации:slow_query_log
можно использоватьshow variables like ‘slov_query_log’
Проверьте, включен ли он, если значение состоянияOFF
,можно использоватьset GLOBAL slow_query_log = on
чтобы включить его, он будетdatadir
произвести следующийxxx-slow.log
документ.
Элемент конфигурации:long_query_time
Проверять:show VARIABLES like 'long_query_time'
, в секундах
настраивать:set long_query_time=0.5
На практике следует ставить с длинного на короткое время, то есть оптимизировать самый медленный SQL
Как только SQL превысит установленное нами критическое время, он будет зарегистрирован вxxx-slow.log
середина
информация профиля
Элемент конфигурации:profiling
set profiling=on
Если этот параметр включен, все детали выполнения SQL будут автоматически записываться.
mysql> show variables like 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | OFF |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> set profiling=on;
Query OK, 0 rows affected, 1 warning (0.00 sec)
show profiles
mysql> show variables like 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | ON |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> insert into article values (null,'test profile',':)');
Query OK, 1 row affected (0.15 sec)
mysql> show profiles;
+----------+------------+-------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------------------------------+
| 1 | 0.00086150 | show variables like 'profiling' |
| 2 | 0.15027550 | insert into article values (null,'test profile',':)') |
+----------+------------+-------------------------------------------------------+
show profile for query Query_ID
вышеshow profiles
Результаты каждого SQL имеютQuery_ID
, вы можете использовать его, чтобы увидеть, какие шаги были предприняты для выполнения SQL, каждый из которых потребляет несколько раз
Типичная конфигурация сервера
Все следующие конфигурации зависят от фактической операционной среды.
-
max_connections
, максимальное количество клиентских подключенийmysql> show variables like 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 151 | +-----------------+-------+
-
table_open_cache
, кеш дескриптора файла таблицы (данные таблицы хранятся на диске, дескриптор файла кеша диска удобен для открытия файла для чтения данных)mysql> show variables like 'table_open_cache'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | table_open_cache | 2000 | +------------------+-------+
-
key_buffer_size
, размер кэша индекса (индекс, прочитанный с диска, кэшируется в памяти, которую можно установить больше, что способствует быстрому поиску)mysql> show variables like 'key_buffer_size'; +-----------------+---------+ | Variable_name | Value | +-----------------+---------+ | key_buffer_size | 8388608 | +-----------------+---------+
-
innodb_buffer_pool_size
,Innodb
Размер пула кэша механизма хранения (дляInnodb
Для наиболее важной конфигурации, если все таблицы используютInnodb
, то даже рекомендуется ставить значение 80% физической памяти,Innodb
Многие улучшения производительности, такие как индексация, зависят от этого)mysql> show variables like 'innodb_buffer_pool_size'; +-------------------------+---------+ | Variable_name | Value | +-------------------------+---------+ | innodb_buffer_pool_size | 8388608 | +-------------------------+---------+
innodb_file_per_table
(innodb
, данные таблицы хранятся в.ibd
файл, если элемент конфигурации установлен вON
, то таблица соответствуетibd
файл, иначе всеinnodb
общее табличное пространство)
Инструмент измерения давления mysqlslap
Инструмент стресс-тестирования поставляется с установкой MySQL.mysqlslap
(родыbin
Под содержанием)
C:\Users\zaw>mysqlslap --auto-generate-sql -uroot -proot
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
Average number of seconds to run all queries: 1.219 seconds
Minimum number of seconds to run all queries: 1.219 seconds
Maximum number of seconds to run all queries: 1.219 seconds
Number of clients running queries: 1
Average number of queries per client: 0
C:\Users\zaw>mysqlslap --auto-generate-sql --concurrency=100 -uroot -proot
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
Average number of seconds to run all queries: 3.578 seconds
Minimum number of seconds to run all queries: 3.578 seconds
Maximum number of seconds to run all queries: 3.578 seconds
Number of clients running queries: 100
Average number of queries per client: 0
C:\Users\zaw>mysqlslap --auto-generate-sql --concurrency=150 -uroot -proot
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
Average number of seconds to run all queries: 5.718 seconds
Minimum number of seconds to run all queries: 5.718 seconds
Maximum number of seconds to run all queries: 5.718 seconds
Number of clients running queries: 150
Average number of queries per client: 0
C:\Users\zaw>mysqlslap --auto-generate-sql --concurrency=150 --iterations=10 -uroot -proot
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
Average number of seconds to run all queries: 5.398 seconds
Minimum number of seconds to run all queries: 4.313 seconds
Maximum number of seconds to run all queries: 6.265 seconds
Number of clients running queries: 150
Average number of queries per client: 0
C:\Users\zaw>mysqlslap --auto-generate-sql --concurrency=150 --iterations=3 --engine=innodb -uroot -proot
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
Running for engine innodb
Average number of seconds to run all queries: 5.911 seconds
Minimum number of seconds to run all queries: 5.485 seconds
Maximum number of seconds to run all queries: 6.703 seconds
Number of clients running queries: 150
Average number of queries per client: 0
C:\Users\zaw>mysqlslap --auto-generate-sql --concurrency=150 --iterations=3 --engine=myisam -uroot -proot
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
Running for engine myisam
Average number of seconds to run all queries: 53.104 seconds
Minimum number of seconds to run all queries: 46.843 seconds
Maximum number of seconds to run all queries: 60.781 seconds
Number of clients running queries: 150
Average number of queries per client: 0
Ну напишу это первым.Это не просто организовать.Люблю ставить лайки.
оригинал:Краткое изложение из 30 000 слов, суть оптимизации Mysql - Требуется программист