· 10 years ago · Sep 23, 2016, 07:50 PM
1\documentclass[12pt]{article}
2% Ðта Ñтрока — комментарий, она не будет показана в выходном файле
3\usepackage{ucs}
4\usepackage[utf8]{inputenc} % Включаем поддержку UTF8
5\usepackage[russian]{babel} % Включаем пакет Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ руÑÑкого Ñзыка
6\title{Отчёт по чиÑленным методам}
7\date{}
8\author{}
9
10\usepackage{geometry} % Ð4, примерно 28-31 Ñтрок(а) на Ñтранице
11 \geometry{paper=a4paper}
12 \geometry{includehead=false} % Ðет верх. колонтитула
13 \geometry{includefoot=true} % ЕÑть номер Ñтраницы
14 \geometry{bindingoffset=0mm} % Переплет : 0 мм
15 \geometry{top=20mm} % Поле верхнее: 20 мм
16 \geometry{bottom=25mm} % Поле нижнее : 25 мм
17 \geometry{left=25mm} % Поле левое : 25 мм
18 \geometry{right=25mm} % Поле правое : 25 мм
19 \geometry{headsep=10mm} % От ÐºÑ€Ð°Ñ Ð´Ð¾ верх. колонтитула: 10 мм
20 \geometry{footskip=20mm} % От ÐºÑ€Ð°Ñ Ð´Ð¾ нижн. колонтитула: 20 мм
21\usepackage{amsmath} % \bar (матрицы и проч. ...)
22\usepackage{amsfonts} % \mathbb (Ñимвол Ð´Ð»Ñ Ð¼Ð½Ð¾Ð¶ÐµÑтва дейÑтвительных чиÑел и проч. ...)
23\usepackage{mathtools} % \abs, \norm
24 \DeclarePairedDelimiter\abs{\lvert}{\rvert}
25 \DeclarePairedDelimiter\norm{\lVert}{\rVert}
26\usepackage{listings} %лиÑтинги
27
28\lstset{
29 basicstyle=\ttfamily,
30 columns=fullflexible,
31 keepspaces=true,
32 frame=top,frame=bottom,
33}
34
35\usepackage[table,xcdraw]{xcolor}
36
37 %Ð´Ð»Ñ Ð¿Ð¾Ð´Ñветки лиÑтинга javascript
38 \usepackage{color}
39\definecolor{lightgray}{rgb}{.9,.9,.9}
40\definecolor{darkgray}{rgb}{.4,.4,.4}
41\definecolor{purple}{rgb}{0.65, 0.12, 0.82}
42
43\lstdefinelanguage{JavaScript}{
44 keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
45 keywordstyle=\color{blue}\bfseries,
46 ndkeywords={class, export, boolean, throw, implements, import, this},
47 ndkeywordstyle=\color{darkgray}\bfseries,
48 identifierstyle=\color{black},
49 sensitive=false,
50 comment=[l]{//},
51 morecomment=[s]{/*}{*/},
52 commentstyle=\color{purple}\ttfamily,
53 stringstyle=\color{red}\ttfamily,
54 morestring=[b]',
55 morestring=[b]"
56}
57
58\lstset{
59 language=JavaScript,
60 %backgroundcolor=\color{lightgray},
61 extendedchars=true,
62 basicstyle=\footnotesize\ttfamily,
63 showstringspaces=false,
64 showspaces=false,
65 %numbers=left,
66 %numberstyle=\footnotesize,
67 %numbersep=9pt,
68 %tabsize=2,
69 breaklines=true,
70 showtabs=false,
71 captionpos=b
72}
73
74
75\begin{document}
76 \newpage
77 {
78 \thispagestyle{empty}
79 \centering
80
81 \textbf{
82 МОСКОВСКИЙ ГОСУДÐРСТВЕÐÐЫЙ ТЕХÐИЧЕСКИЙ УÐИВЕРСИТЕТ ИМЕÐИ Ð. Ð. БÐУМÐÐÐ \\
83 Факультет информатики и ÑиÑтем ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ \\
84 Кафедра теоретичеÑкой информатики и компьютерных технологий}
85 \bigskip
86 \bigskip
87 \bigskip
88 \bigskip
89 \bigskip
90 \bigskip
91 \bigskip
92
93 \vfill
94
95 {\large Ð›Ð°Ð±Ð¾Ñ€Ð°Ñ‚Ð¾Ñ€Ð½Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð° â„–3}\\
96 по курÑу <<ЧиÑленные методы>>\\
97 \LARGE{<<ПоÑтроение Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ‡Ð½Ð¾-заданной функции \\
98 кубичеÑкого Ñплайна,\\
99 Ñплайна Ðкимы, \\
100 Б-Ñплайна>>\\ }
101 \normalsize
102
103 \bigskip
104 \vfill
105 \hfill\parbox{5cm} {
106 Выполнил:\\
107
108 Проверила:\\
109
110 }
111 \vspace{\fill}
112
113
114 МоÑква \number\year
115 \clearpage
116 }
117 \newpage
118 {
119 \tableofcontents
120 \clearpage
121 }
122
123
124
125
126 {
127 \section{ВВЕДЕÐИЕ}
128 }
129
130 ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ñ†ÐµÐ»ÑŒ данной работы - конвертировать базу данных автоматизированной ÑиÑтемы теÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ T-BMSTU, иÑпользуемой на кафедре ИУ9 Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð»Ð°Ð±Ð¾Ñ€Ñ‚Ð°Ñ‚Ð¾Ñ€Ð½Ñ‹Ñ… работ по курÑам программированиÑ, в формат MySQL. Сравнить производительноÑть новой реализации базы и, при необходимоÑти, оптимизировать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº базе, либо оптимизировать имеющуюÑÑ Ð±Ð°Ð·Ñƒ данных в новом формате. \\
131
132 Ð’ ходе работы будет иÑÑледована имеющаÑÑÑ Ð¸ÑÑ…Ð¾Ð´Ð½Ð°Ñ Ñ€ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð±Ð°Ð·Ñ‹ в формате SQLite. Затем, данные из иÑходной базы будут извлечены и перенеÑены в новую, целевую базу данных Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ конвертера. Затем в новый формат будет конвертирован модельный SQL Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸ будет иÑÑледовано его выполнение на новой базе данных. Ðтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð±ÑƒÐ´ÐµÑ‚ оптимизирован под целевую реализацию базы данных. \\
133
134 Путем ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти реализаций ``новой'' и ``Ñтарой'' баз данных и запроÑов будет принÑто решение о целеÑообразноÑти перехода на новую реализацию.
135
136 \clearpage
137
138
139
140 {
141 \section{ÐЕОБХОДИМЫЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕÐИЯ}
142 }
143
144 Прежде вÑего Ñтоит раÑÑмтореть отличительные черты иÑходной и целевой СУБД. ПоÑле Ñтого мы Ñможем заключить, возможна ли в теории выгода от прехода от одной СУБД к другой.\\
145
146
147 {
148 \subsection{SQLite. ПреимущеÑтва и недоÑтатки}
149 }
150
151 SQLite ÑвлÑетÑÑ Ð¿Ð¾Ð¿ÑƒÐ»Ñрной вÑтраиваемой релÑционной базой данных. Релиз поÑледней на данный момент верÑии одноименной СУБД (SQLite 3.14.1) ÑоÑтоÑлÑÑ Ð² авгуÑте 2016 года. СУБД выпуÑкаетÑÑ Ð¿Ð¾Ð´ общеÑтвенной (public domain) лицензией, не накладывающей никаких ограничений на иÑпользование.
152
153 РаÑÑторим теперь оÑобенноÑти базы данных:\\
154
155
156 Главным отличием SQLite от других баз данных ÑвлÑетÑÑ Ð¿Ð°Ñ€Ð°Ð´Ð¸Ð³Ð¼Ð°, в которой Ñоздана база. Ð’ отличие от большинÑта оÑтальных баз данных, иÑпользующих клиент-Ñерверную архитектуру, Ñамо приложение SQLite по Ñути ÑвлÑетÑÑ Ñервером. SQLite не ÑвлÑетÑÑ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ð¼ процеÑÑом. ВмеÑто Ñтого она предоÑтавлÑет библиотеку Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ `подключений` к единÑтвенному файлу, в виде которого она находитÑÑ Ð² конечной ÑиÑтеме.
157
158 ОтноÑÐ¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾Ñтота реализации такого подхода ÑвлÑетÑÑ, пожалуй, главной отличительной чертой Ñтой СУБД. Ð’ÑÑ Ð±Ð°Ð·Ð° хранитÑÑ Ð² одном файле. Ðто позволÑет ÑущеÑтвенно Ñкономить реÑурÑÑ‹ ÑиÑтемы, Ñокращает Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ÐºÐ»Ð¸ÐºÐ° и ÑущеÑтвенно упрощает логику работы программ, иÑпользующих Ñту БД. База данных ÑвлÑетÑÑ ÐµÐ´Ð¸Ð½Ñтвенным файлом в кроÑплатформенном формате, что обеÑпечивает бОльшую мобильноÑть по Ñравнению Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ СУБД, так как Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ð‘Ð” в новой ÑиÑтеме, развертывание базы не треубетÑÑ. \\
159
160
161 Однако, у такой реализации еÑть и Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð°Ñ Ñторона. ПроÑтота реализации доÑтигаетÑÑ Ð·Ð° Ñчет того, что во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð¿Ð¸Ñи веÑÑŒ файл блокируетÑÑ Ð¾Ð´Ð½Ð¸Ð¼ процеÑÑом. Рзначит, неÑколько процеÑÑов, одновременно подключенных к базе могут лишь Ñчитывать данные, в то времÑ, как только один из них может изменÑть Ñти данные. Ðтот принцип, ``читают многие - пишет один'', ÑвлÑетÑÑ Ð¾Ð´Ð½Ð¸Ð¼ из недоÑтатков Ñтой СУБД. \\
162
163 Еще одним недоÑтатком ÑвлÑетÑÑ Ð¾Ñ‚ÑутÑтвие полной поддержки SQL-92:
164 \begin{itemize}
165 \item[] Ðе поддерживаетÑÑ, например, удаление или изменение Ñтолбца в таблице:
166 \begin{itemize}
167 \item[] \verb| ALTER TABLE DROP COLUMN ... |
168 \item[] \verb| ALTER TABLE ALTER COLUMN ... | отÑутÑтвуют в SQLite
169 \end{itemize}
170 \item[] Опущены \verb| RIGHT OUTER JOIN | и \verb| FOR EACH STATEMENT |
171 \item[] По умолчанию отключена поддержка foreign key
172 \item[] ÐедоÑтупны хранимые процедуры
173 \item[] Триггеры SQLite намного менее функциональны, нежели триггеры других СУБД
174 \end{itemize}
175
176 Ð”Ð»Ñ Ð²Ð·Ð°Ð¸Ð¼Ð¾Ð´Ð¹ÑÑ‚Ð²Ð¸Ñ Ñ SQLite из приложений отÑутÑтвуют официальные драйвера. Таковых нет ни под JDBC, ни под ADO.Net, ни под ODBC. ОтÑтутÑтвие Ñтого ``из коробки'' ÑлÑетÑÑ ÑущеÑтвенным минуÑом SQLite.\\
177
178
179 Еще одной оÑобенноÑтью SQLite ÑвлÑетÑÑ ``ÑÐ»Ð°Ð±Ð°Ñ Ñ‚Ð¸Ð¿Ð¸Ð·Ð°Ñ†Ð¸Ñ'', или ÐºÐ¾Ð½Ñ†ÐµÐ¿Ñ†Ð¸Ñ ``близоÑти типов'' (type affinity). Так, тип Ñтолбца не определÑет тип хранимого в Ñтом Ñтолбце значениÑ. Ð’ любой Ñтолбец может быть запиÑано любое значение, а Ñам тип Ñтолбца иÑпользуетÑÑ Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ к одному типу при Ñравнении значений. Ð’Ñе Ñто позволÑет Ñоздавать таблицу ``проÑтым'' \verb| CREATE TABLE sampleTable| \verb|(col1, col2, col3) | без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ‡ÐµÐ³Ð¾ либо еще, что ÑвлÑетÑÑ Ð½ÐµÐ´Ð¾Ð¿ÑƒÑтимым и недоÑтупным в других СУБД.
180
181 Однако, в SQLite доÑтупны лишь 5 типов данных:
182 \begin{itemize}
183 \item[] NULL
184 \item[] INTEGER (знаковое целое чиÑло до 8 байт)
185 \item[] REAL (чиÑло Ñ Ð¿Ð»Ð°Ð²Ð°ÑŽÑ‰ÐµÐ¹ точкой, 8 байт в формате IEEE)
186 \item[] TEXT (Ñтрока в кодировке UTF-8 или UTF-16)
187 \item[] BLOB (входное значение, `как еÑть`)
188 \end{itemize}
189
190 СоглаÑно руководÑтву, Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° Boolean рекомендуетÑÑ Ð¸Ñпользовать INTEGER 0 или 1, а Date и Time типы хранить в виде Ñтрок. ВышеÑказанное ÑвлÑетÑÑ Ð¾Ð´Ð½Ð¾Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾ как недоÑтатком, так и доÑтоинÑтвом и не может быть однозначно интерпретировано в рамках `общих` задачах. \\
191
192
193 Ð’ SQLite также отÑутÑтвуют какие-либо механизмы репликации.
194
195 ОтÑутÑтвует ÑиÑтема пользователей.
196
197 ОтÑутÑтвует возможноÑть ÑƒÐ²ÐµÐ»Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти.\\
198
199 ÐеÑÐ¼Ñ‚Ð¾Ñ€Ñ Ð½Ð° вÑе Ñто, SQLite ÑвлÑетÑÑ Ð¾Ñ‚Ð»Ð¸Ñ‡Ð½Ñ‹Ð¼ кандидатом на иÑпользование в качеÑтве вÑтраиваемой ÑиÑтемы и пользуетÑÑ Ð±Ð¾Ð»ÑŒÑˆÐ¾Ð¹ популÑрноÑтью в Ñтой Ñфере.
200
201
202 {
203 \subsection{MySQL. ПреимущеÑтва и недоÑтатки}
204 }
205
206 Теперь взглÑнем на целевую базу данных, MySQL.
207
208 MySQL ÑвлÑетÑÑ Ñамой раÑпроÑтраненной СУБД. РазрабатываетÑÑ ÐºÐ¾Ñ€Ð¿Ð¾Ñ€Ð°Ñ†Ð¸ÐµÐ¹ Oracle, как доÑÑ‚ÑƒÐ¿Ð½Ð°Ñ Ð¿Ð¾Ð´ универÑальной общеÑтвенной лицензией GNU (GNU General Public License) замена промышленной БД Oracle.
209
210 РаÑÑмотрим оÑобенноÑти MySQL:\\
211
212
213 MySQL разработана в ÑоответÑтвии Ñ ``клаÑÑичеÑкой'' клиент-Ñерверной архитектурой и поддерживает вÑе оÑновные ОС. Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ð±Ð°Ð·Ð¾Ð¹ из приложений имеютÑÑ Ð¾Ñ„Ð¸Ñ†Ð¸Ð°Ð»ÑŒÐ½Ñ‹Ðµ драйвера ADO.Net, JDBC и ODBC. Ð¥Ð¾Ñ‚Ñ ÐºÐ¾Ð»Ð¸Ñ‡ÐµÑтво Ñзыков, поддерживаемых API MySQL и меньше, чем у SQLite, недоÑтатком Ñто не ÑвлÑетÑÑ, так как упущены не Ñамые популÑрные в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ñзыки, такие, как Basic, Forth и Fortran.\\
214
215
216 MySQL поддерживает большое количеÑтво типов данных:
217 \begin{itemize}
218 \item[] TINYINT (BOOL), SMALLINT, MEDIUMINT, INTEGER, BIGINT Ð´Ð»Ñ Ñ†ÐµÐ»Ð¾Ñ‡Ð¸Ñленных значений
219 \item[] FLOAT, DOUBLE, NUMERIC, REAL Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ Ñ Ð¿Ð»Ð°Ð²Ð°ÑŽÑ‰ÐµÐ¹ точкой
220 \item[] DATE, TIME, DATETIME, YEAR, TIMESTAMP Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ даты и времени
221 \item[] CHAR, VARCHAR Ð´Ð»Ñ Ñтроковых значений фикÑированной / переменной длины
222 \item[] TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT Ð´Ð»Ñ Ñ‚ÐµÐºÑ‚Ð¾Ð²Ñ‹Ñ… значений длины $2^{8}-1$ / $2^{16}-1$ / $2^{24}-1$ / $2^{32}-1$ ÑоответÑтвенно
223 \item[] TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ `как еÑть`
224 \item[] ENUM, SET Ð´Ð»Ñ Ð·Ð°Ð½Ñ‡ÐµÐ½Ð¸Ð¹ типа перечиÑление / множеÑтво
225 \end{itemize}
226
227 Ðто ÑвлÑетÑÑ ÑущеÑтвенным преимущеÑтвом MySQL перед SQLite, так как таблицы могут быть поÑтроены более оптимально в ÑоответÑтвии Ñ Ð±Ð¸Ð·Ð½ÐµÑ Ð»Ð¾Ð³Ð¸ÐºÐ¾Ð¹.\\
228
229
230 ПоддерживаетÑÑ Ð¿Ð¾Ñ‡Ñ‚Ð¸ полный Ñтандарт SQL-92 (DML, DDL, DCL), Ñ…Ð¾Ñ‚Ñ Ð¸ приÑутÑтвует проприетарное раÑширение ÑинтакÑиÑа. Ð’ чаÑтноÑти, в Ñтой БД иÑпользуетÑÑ Ñвой ÑинтакÑÐ¸Ñ Ð´Ð»Ñ Ð½Ð°Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ð¾Ð², а также хранимых процедур, что не поддерживаютÑÑ Ð² SQLite. Однако, Ñтоит отметить, что в MySQL упущены, в чаÑтноÑти, INSTEAD OF триггеры, Ñ…Ð¾Ñ‚Ñ Ð¾Ð½Ð¸ и могут быть ÑамоÑтоÑтельно реализованы отдельно.\\
231
232
233 Ð’ Mysql приÑутÑтвуют механизмы репликации, так как разработчики Ñоздавали функциональноÑть по заказу лицензионных пользователей и Ñто (репликациÑ) ÑвлÑетÑÑ Ð²Ð°Ð¶Ð½Ñ‹Ð¼ фактором при выборе БД Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼ÐµÑ€Ñ‡ÐµÑкого иÑпользованиÑ. Так, поддерживаетÑÑ
234 \begin{itemize}
235 \item[] МногомаÑÑ‚ÐµÑ€Ð½Ð°Ñ (Multi-master replication) репликациÑ. Данные в данном Ñлучае хранÑÑ‚ÑÑ Ð³Ñ€ÑƒÐ¿Ð¿Ð¾Ð¹ уÑтройÑтв и могут быть изменены любым уÑтройÑтвом из Ñтой группы `маÑтеров`, так и
236 \item[] СиÑтема Ñ Ð²ÐµÐ´ÑƒÑ‰Ð¸Ð¼Ð¸ и ведомыми уÑтройÑтвами (Master-slave), где Ð²ÐµÐ´ÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных раÑÑматриваетÑÑ, как `авторитетный` иÑточник данных, а подчиненные ÑинхронизируютÑÑ Ñ Ð½ÐµÐ¹.
237 \end{itemize}
238
239 Ð’Ñе Ñто ÑущеÑтвенно повышает надежноÑть работы Ñтой БД и отличает ее от SQLite, где механизмы репликации впринципе отÑутÑтвуют.\\
240
241
242 Mysql поддерживает многопоточную работу Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ механизма блокировки таблиц или Ñтрок, что ÑвлÑетÑÑ Ð±Ð¾Ð»ÐµÐµ вариативной функциональноÑтью, нежели захват вÑего файла базой SQLite.
243
244 ПриÑутÑтвуют мехнизмы ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÑ€Ð¾Ð²Ð½ÐµÐ¼ доÑтупа пользователей, Ñ…Ð¾Ñ‚Ñ Ð¾Ñ‚ÑутÑтвуют механизмы Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹ на роли и группы.
245
246 СиÑтема Mysql маÑштабируема.
247
248 Также, Ñреди преимущеÑтв можно отметить наличие множеÑтва ``движков'' (database engine), каждый из которых обладает Ñвоими преимущеÑтвами и может быть выбран ÑоглаÑно решаемой задаче. Ð’ ходе работы некоторые из движков будут раÑÑмторены чуть подробнее.
249
250 Ð‘Ð»Ð°Ð³Ð¾Ð´Ð°Ñ€Ñ Ñвободному доÑтупу к иÑходному коду, Ñта СУБД может быть подÑтроена под индивидуальное решение. РвывÑÐ¾ÐºÐ°Ñ Ð¿Ð¾Ð¿ÑƒÐ»ÑрноÑть ÑиÑтемы обеÑпечивает наличие поддержки по многим возможным проблемам.\\
251
252
253 ИÑÑ…Ð¾Ð´Ñ Ð¸Ð· вÑего вышеперечиÑленного, переход Ñ SQLite на MySQL видитÑÑ Ñ€Ð°Ð·ÑƒÐ¼Ð½Ñ‹Ð¼ в рамках большей чаÑти задач общего назначениÑ. MySQL объективно обладает большим чиÑлом преимущеÑтв перед SQLite.
254
255 Однако, в рамках курÑовой работы раÑÑматриваетÑÑ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных автоматизированной ÑиÑтемы теÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ T-BMSTU. И Ð´Ð»Ñ Ð¾Ñ†ÐµÐ½ÐºÐ¸ реальной выгоды от перехода Ñ Ð¾Ð´Ð½Ð¾Ð¹ базы на другую необходимо прежде вÑего оценить умеÑтноÑть преимущеÑтв MySQL перед SQLite в рамках конкретной задачи.
256
257 Ð”Ð»Ñ Ñтого раÑÑмторим теперь иÑходную базу данных T-BMSTU.
258
259 \bigskip
260
261
262 {
263 \section{ИЗУЧЕÐИЕ ВХОДÐЫХ ДÐÐÐЫХ}
264 }
265
266 ИÑходными данными курÑовой работы ÑвлÑÑŽÑ‚ÑÑ SQL Ñкрипт ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных, непоÑредÑтвенно база данных в виде файла {\it tbmstu.db} и запроÑ, формирующий выборку данных из базы Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° Web-Ñервере теÑтированиÑ.\\
267
268 {
269 \subsection{ИÑÑ…Ð¾Ð´Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных T-BMSTU}
270 }
271 ВзглÑнем на Ñхему базы данных
272
273 <здеÑÑŒ будет Ñхема базы>
274 %тут картинка Ñхемы Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ Ð±ÑƒÐ´ÐµÑ‚ и Ð²ÐµÑ€Ñ‚Ð¸ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð¼Ð± даже
275
276 ИÑÑ…Ð¾Ð´Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных Ñодержит в Ñебе 22 таблицы:
277 \begin{verbatim}
278 Institutions
279 Subjects
280 Modules
281 Groups
282 RelGroupsModules
283 Persons
284 Sessions
285 CurrentAdmins
286 CurrentTaskAuthors
287 Students
288 ModuleAuthors
289 Approvers
290 Languages
291 Tasks
292 RelTasksLanguages
293 RelLanguagesModules
294 Submissions
295 FailedTests
296 Comments
297 Approvements
298 RelTasksModules
299 \end{verbatim}
300
301 %отÑортировать в порÑдке ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ°Ðº по Ñкрипту
302
303 Также имеютÑÑ 6 предÑтавлений:
304 \begin{verbatim}
305 TimeDesc
306 RelModulesInstitutions
307 RelSubmissionsApprovers
308 FinalApprovements
309 PersonRoles
310 RelStudentsTasks
311 \end{verbatim}
312
313 ПриÑутÑтвуют 7 триггеров, выводÑщих Ñообщение об ошибке, в Ñлучае нарушений работы Ñ Ð²Ð½ÐµÑˆÐ½Ð¸ÐºÐ¸ ключами (вÑтавки и обновлениÑ):
314 \begin{verbatim}
315 fki_RelGroupsModules
316 fku_RelGroupsModules
317 fki_Tasks_PersonId
318 fku_Tasks_PersonId
319 fki_Approvements_PersonId
320 fku_Approvements_PersonId
321 fku_Submissions_PassedTests
322 \end{verbatim}
323
324 Ðазначение таблиц, предÑтавлений и триггеров понÑтно из их названий.
325
326 Ð’ большинÑтве таблиц в базе хранитÑÑ Ð¿Ð¾ неÑколько деÑÑтков запиÑей - Ñто таблицы Ñо ÑпиÑками Ñтудентов групп, предметов и модулей, таблица-Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ ÑˆÐºÐ°Ð»Ð°, таблица авторов заданий. ЕÑть таблицы на одну или неÑколько Ñотен запиÑей: аккаунты в ÑиÑтеме, заданиÑ, проверÑющие и вÑпомогательные таблицы. ЕÑть таблица универÑитетов Ñ Ð¾Ð´Ð½Ð¾Ð¹ запиÑью, так как на данный момент ÑиÑтема применÑтеÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в МГТУ. И еÑть 4 таблицы Ñ Ð´ÐµÑÑтками тыÑÑч запиÑей. Ð’ порÑдке ÑƒÐ±Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ количеÑтву запиÑей (по данным на февраль 2016):
327
328 \begin{itemize}
329 \item[] Sessions, ~ 130 тыÑ. запиÑей
330 \item[] Submissions, ~ 80 тыÑ. запиÑей
331 \item[] Comments, ~ 70 тыÑ. запиÑей
332 \item[] Approvements, ~70 тыÑ. запиÑей.
333 \end{itemize}
334
335 Очевидно, что работа Ñ Ñтими таблицами предÑтавлÑет наибольшую ÑложноÑть не только по причине чаÑтой запиÑи в них, но и по причине того, что Ñти таблицы имеют дочерние запиÑи и ÑÑылаютÑÑ Ð½Ð° большое чиÑло других таблиц. РаÑÑмотрим, например таблицу (Ñкрипт ÑозданиÑ) решений, отправленных на Ñервер теÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ - таблицу {\it Submissions}:\\
336
337
338\begin{lstlisting}[caption={Скрипт ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Submissions}, label={lst:table_submissions}]
339CREATE TABLE Submissions (
340 SubmissionID INTEGER NOT NULL PRIMARY KEY,
341
342 PersonID INTEGER NOT NULL,
343 GroupID INTEGER NOT NULL,
344 TaskID INTEGER NOT NULL,
345 ModuleID INTEGER NOT NULL,
346 SubmissionTime TEXT NOT NULL CHECK (length(SubmissionTime) < 32),
347
348 LanguageID INTEGER NOT NULL,
349 SourceCode TEXT NOT NULL CHECK (length(SourceCode) < 50*1024),
350 Draft INTEGER CHECK (Draft = 0 OR Draft = 1),
351
352 SentToTestTime TEXT,
353 PassedTests INTEGER,
354 TestingServerId INTEGER,
355
356 UNIQUE (PersonID, GroupID, TaskID, ModuleID, SubmissionTime),
357 FOREIGN KEY (PersonID, GroupID) REFERENCES Students
358 ON DELETE CASCADE ON UPDATE CASCADE,
359 FOREIGN KEY (TaskID, ModuleID) REFERENCES RelTasksModules
360 ON DELETE RESTRICT ON UPDATE CASCADE,
361 FOREIGN KEY (GroupID, ModuleID) REFERENCES RelGroupsModules
362 ON DELETE RESTRICT ON UPDATE CASCADE,
363 FOREIGN KEY (TaskID, LanguageID) REFERENCES RelTasksLanguages
364 ON DELETE RESTRICT ON UPDATE CASCADE,
365 FOREIGN KEY (LanguageID, ModuleID) REFERENCES RelLanguagesModules
366 ON DELETE RESTRICT ON UPDATE CASCADE
367);
368 \end{lstlisting}
369
370
371 Как видно из лиÑтинга ~\ref{lst:table_submissions}, Ñта таблица ÑÑылаетÑÑ Ñразу на 5 других таблиц. Рзначит, при добавлении запиÑи в нее необходимо обратитьÑÑ Ñразу к 5 таблицам. Также Ñ Ñтой таблицей работает триггер {\it fku\_Submissions\_PassedTests}, проверÑющий наличие валидного Ñервера теÑтированиÑ, назначенного по решению.
372
373 Тут же ÑтановÑÑ‚ÑÑ Ð²Ð¸Ð´Ð½Ñ‹ ограничениÑ, озвученные ранее при анализе SQLite ``из коробки'', такие, как ограниченное чиÑло типов данных: {\it SubmissionTime} хранитÑÑ Ð² виде Ñтроки TEXT, {\it Dratf} хранитÑÑ Ð² виде INTEGER'а. Из-за Ñтого приходитÑÑ Ð¾ÑущеÑтвлÑть валидацию входных данных проверÑÑ Ð´Ð»Ð¸Ð½Ñƒ или значние.
374
375 Также необходимо Ñоблюдение уникальноÑти группы полей {\it PersonID, GroupID, TaskID, ModuleID, SubmissionTime} и PRIMARY KEY.
376
377 Работа Ñ Ñтой таблицей веÑьма труднозатратна отноÑительно работы Ñ Ð¾Ñтальными таблицами в рамках базы данных.
378
379 У Ñтой таблицы имеютÑÑ Ñразу 4 вручную Ñозданных индекÑа. Учтем Ñто в дальнейшем при возможной оптимизации Ñтой таблицы. \\
380
381 Таблиц, подобных Ñтой, в базе еще 3. Ðужно отметить, что таблица {\it Sessions} выделÑетÑÑ Ñреди ``крупных таблиц'' Ñвоими размерами, а также отноÑительной проÑтотой работы: в ней еÑть только Ð²Ð°Ð»Ð¸Ð´Ð°Ñ†Ð¸Ñ ip-адреÑа по длине и ÑÑылка на таблицу пользователей Ñервера Ð´Ð»Ñ Ð¿Ñ€Ð¸ÐºÑ€ÐµÐ¿Ð»ÐµÐ½Ð¸Ñ ÐµÐ³Ð¾ к ÑеанÑу. ПоиÑк по Ñтой таблице не нужен, поÑтому еÑть только Ñтандартный PRIMARY KEY
382
383 Ðти 4 таблицы поÑтоÑнно иÑпользуютÑÑ Ð² ходе работы Ñервера. Данные в них обновлÑÑŽÑ‚ÑÑ Ð¿Ð¾ÑтоÑнно.
384
385 БольшинÑтво данных оÑтальных таблиц редактируетÑÑ Ð»Ð¸Ð±Ð¾ Ñ Ð½Ð°Ñ‡Ð°Ð»Ð¾Ð¼ Ð¼Ð¾Ð´ÑƒÐ»Ñ (например, таблицы {\it Modules, Tasks}), либо ÑемеÑтра (такие, как {\it Subjects, Groups, TimeScale}), либо Ñ Ð½Ð°Ñ‡Ð°Ð»Ð¾Ð¼ учебного года ({\it Persons, Groups, TimeScale}). Таблицы {\it Languages, CurrentAdmins, CurrentTaskAuthors, Institutions} редактируютÑÑ ÐµÑ‰Ðµ реже. То еÑть, данные большей чаÑти таблиц редактируютÑÑ ÑовÑем не чаÑто. Ðо, Ð±ÐžÐ»ÑŒÑˆÐ°Ñ Ñ‡Ð°Ñть данных вÑей базы редактируетÑÑ Ð¿Ð¾ÑтоÑнно. Учтем Ñто в дальнейшем.\\
386
387 {
388 \subsection{ИÑходный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº базе}
389 }
390
391 Во входных данных также приÑутÑтвует Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº опиÑанной выше базе. Ðтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ñ„Ð¾Ñ€Ð¼Ð¸Ñ€ÑƒÐµÑ‚ выборку данных Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° Ñтранце Web-Ñервера.
392 ВзглÑнем на Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð² его первоначальном варианте:\\
393
394 %шрифт лиÑтинга поменьше бы чтоб Ñ…Ð¾Ñ‚Ñ Ñ‹ на 2 Ñтраницы
395
396 \begin{lstlisting}[caption={Скрипт выборки из базы}, label={lst:bigq}]
397SELECT DISTINCT
398 Institutions.InstitutionId, Institutions.InstitutionName,
399 /*****/
400 Groups.GroupId, Groups.TimeId, TimeDesc.TimeName, Groups.GroupName,
401 /*****/
402 Subjects.SubjectId, Subjects.SubjectName,
403 /*****/
404 Modules.ModuleId, Modules.ModuleNo, Modules.ModuleName, Modules.MinRating,
405 RelGroupsModules.ExpireTime, IsStudent, IsApprover,
406 /*****/
407 StTaskId, StTaskNo, StTaskName, StTaskRating, Status,
408 /*****/
409 SubmissionId, SubmPersonId, SubmLogin, SubmFirstName,
410 SubmLastName, SubmTaskId, SubmTaskName, SubmissionTime
411FROM (
412 SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent,
413 max(IsApprover) AS IsApprover
414 FROM (
415 SELECT Students.GroupId, ModuleId, 1 AS IsStudent, 0 AS IsApprover
416 FROM Students
417 JOIN RelGroupsModules USING(GroupId)
418 WHERE PersonId = 1
419 UNION SELECT GroupId, ModuleId, 0 AS IsStudent, 1 AS IsApprover
420 FROM Approvers
421 WHERE PersonId = 1
422 )
423 GROUP BY GroupId, ModuleId
424 )
425JOIN RelGroupsModules USING(GroupId, ModuleId)
426JOIN Groups USING(GroupId)
427JOIN Institutions USING(InstitutionId)
428JOIN TimeDesc USING(TimeId)
429JOIN Modules USING(ModuleId)
430JOIN Subjects USING(SubjectId)
431LEFT OUTER JOIN (
432 SELECT * FROM (
433 SELECT DISTINCT GroupId, ModuleId,
434 TaskId AS StTaskId, TaskNo AS StTaskNo, TaskName AS StTaskName,
435 TaskRating AS StTaskRating,
436 (CASE
437 WHEN count(SubmissionId) = 0 THEN 0
438 WHEN max(AcceptedInTime) = 1 THEN 3
439 WHEN max(AcceptedOutdated) = 1 THEN 4
440 WHEN count(SubmissionId) > count(AcceptedInTime)+
441 count(AcceptedOutdated) THEN 1
442 ELSE 2
443 END) AS Status,
444 NULL AS SubmissionId, NULL AS SubmPersonId,
445 NULL AS SubmLogin, NULL AS SubmFirstName,
446 NULL AS SubmLastName, NULL AS SubmTaskId,
447 NULL AS SubmTaskName, NULL AS SubmissionTime
448 FROM (
449 SELECT DISTINCT Students.PersonId,
450 RelGroupsModules.GroupId, RelGroupsModules.ModuleId,
451 RelTasksModules.TaskId, RelTasksModules.TaskNo, Tasks.TaskName,
452 RelTasksModules.TaskRating, Submissions.SubmissionId,
453 (SELECT Accepted
454 FROM Approvements
455 WHERE Approvements.SubmissionId = Submissions.SubmissionId
456 AND (RelGroupsModules.ExpireTime IS NULL
457 OR Submissions.SubmissionTime < RelGroupsModules.ExpireTime)
458 ORDER BY ApprovementTime DESC
459 LIMIT 1
460 ) AS AcceptedInTime,
461 (SELECT Accepted
462 FROM Approvements
463 WHERE Approvements.SubmissionId = Submissions.SubmissionId
464 AND RelGroupsModules.ExpireTime IS NOT NULL
465 AND Submissions.SubmissionTime >= RelGroupsModules.ExpireTime
466 ORDER BY ApprovementTime DESC
467 LIMIT 1
468 ) AS AcceptedOutdated
469 FROM Students, RelGroupsModules
470 LEFT OUTER JOIN RelTasksModules USING(ModuleId)
471 LEFT OUTER JOIN Tasks USING(TaskId)
472 LEFT OUTER JOIN Submissions
473 ON Submissions.PersonId = 1
474 AND Submissions.GroupId = RelGroupsModules.GroupId
475 AND Submissions.TaskId = RelTasksModules.TaskId
476 AND Submissions.ModuleId = RelGroupsModules.ModuleId
477 AND (Submissions.Draft IS NULL OR Submissions.Draft = 0)
478 WHERE Students.PersonId = 1 AND
479 Students.GroupId = RelGroupsModules.GroupId
480 )
481 GROUP BY GroupId, ModuleId, TaskId
482 )
483 UNION SELECT DISTINCT Approvers.GroupId, Approvers.ModuleId,
484 NULL AS StTaskId, NULL AS StTaskNo, NULL AS StTaskName,
485 NULL AS StTaskRating, 0 AS Status,
486 Submissions.SubmissionId, Submissions.PersonId AS SubmPersonId,
487 Persons.Login AS SubmLogin, Persons.FirstName AS SubmFirstName,
488 Persons.LastName AS SubmLastName, Tasks.TaskId AS SubmTaskId,
489 Tasks.TaskName AS SubmTaskName, Submissions.SubmissionTime
490 FROM Approvers
491 LEFT OUTER JOIN Submissions
492 ON Submissions.GroupId = Approvers.GroupId
493 AND Submissions.ModuleId = Approvers.ModuleId
494 AND Draft = 0
495 AND NOT EXISTS (
496 SELECT * FROM Approvements
497 WHERE Approvements.SubmissionId = Submissions.SubmissionId
498 )
499 LEFT OUTER JOIN Tasks
500 ON Tasks.TaskId = Submissions.TaskId
501 LEFT OUTER JOIN Persons
502 ON Persons.PersonId = Submissions.PersonId
503 WHERE Approvers.PersonId = 1
504 ) AS Cont
505 ON Cont.GroupId = Groups.GroupId
506 AND Cont.ModuleId = Modules.ModuleId
507ORDER BY
508 Institutions.InstitutionId ASC,
509 Groups.TimeId DESC,
510 Groups.GroupId ASC,
511 SubjectId ASC,
512 ModuleNo ASC,
513 StTaskNo ASC,
514 SubmissionTime ASC;
515 \end{lstlisting}
516
517 Ð’ запроÑе из ЛиÑтинга ~\ref{lst:bigq} приÑутÑтвуют 11 выборок Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SELECT из большей чаÑти таблиц базы. Ð’ том чиÑле еÑть Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ðº ``большим'' таблицам - {\it Submissions} и {\it Approvements}. ПриÑутÑтвует множеÑтво объединений таблиц, неÑколько группировок по учебным модулÑм и группам а также Ñ Ð¸Ñпользованием агрегирующих функций, неÑколько DISTINCT выборок и Ñортировок.
518
519 ВыполнÑть оптимизации ÑоглаÑно заданию, будем ``Ñ Ð¾Ð³Ð»Ñдкой'' на Ñтот запроÑ.\\
520
521
522 \clearpage
523
524 {
525 \section{ПОРТИРОВÐÐИЕ БÐЗЫ ДÐÐÐЫХ}
526 }
527
528 Теперь, когда мы получили предÑтавление об уÑтройÑве иÑходной базы данных SQLite, потрируем ее на целевую платформу MySQL.
529
530 Стандартные реверÑ-инжиниринговые утилиты Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ некорректно взаимодейÑтвовать Ñо Ñхемой базы данных и в результате некоторые таблицы могут не ÑоздатьÑÑ, а внутреннее уÑтройÑтво других будет отличатьÑÑ. К тому же нам необходимо произвеÑти некоторые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ‚Ð¸Ð¿Ð¾Ð² ÑоглаÑно best practices, опиÑанных в руководÑтве Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ MySQL. %здеÑÑŒ ÑÑыль
531
532 Ð’ Ñлучае данной курÑовой работы, у Ð½Ð°Ñ Ð¸Ð¼ÐµÐµÑ‚ÑÑ Ð¿Ñ€ÐµÐ¸Ð¼ÑƒÑ‰ÐµÑтво перед такими утилитами в виде еще одного входного Ñкрипта - Ñкрипта ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð±Ð°Ð·Ñ‹. Ð’Ñе что необходимо Ñделать в данном Ñлучае - перенеÑти непоÑредÑтвенно данные в подготовленную на новом меÑте Ñхему.\\
533
534
535 Так как обе СУБД не полноÑтью поддерживают формат SQL-92, проÑтым запуÑком Ñкрипта ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð±Ð°Ð·Ñ‹ обойтиÑÑŒ не удаÑÑ‚ÑÑ. Преобразуем иÑходный Ñкрипт Ñ ÑƒÑ‡ÐµÑ‚Ð¾Ð¼ отличий диалекта SQL, иÑпользуемого в SQLite от диалекта, иÑпользуемого в MySQL. Также выполним Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ‚Ð¸Ð¿Ð¾Ð² данных, заменив Ñтроки, Ñодержащие даты и Ð²Ñ€ÐµÐ¼Ñ Ð½Ð° тип DATETIME. Другие Ñтроковые конÑтанты заменим на тип VARCHAR, более предпочтительный в рамках MySQL. Своеобразный Boolean в SQLite, реализованый через INTEGER и проверку значений Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ CHECK() заменим на TINYINT, ÑвлÑющийÑÑ, аналогом BOOLEAN'а в MySQL. Проведем еще некоторые Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ портируем триггеры, Ð¿ÐµÑ€ÐµÐ²ÐµÐ´Ñ Ð¸Ñ… на проприетарный ÑинткаÑÐ¸Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ð¾Ð² и хранимых прцедур MySQL.\\
536
537
538 Считаем, что Ñхема базы данных уже подготовлена. Ðапишем небольшую утилиту на $C\#$, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ технологии ADO.Net доÑтупа приложений на платформе .Net к данным выберет вÑе данные из базы SQLite и вÑтавит в подготовленную Ñхему базы MySQL. ЕдинÑтвенной возможной проблемой ÑвлÑетÑÑ Ð¾Ñ‚ÑутÑтвие официальных драйверов SQLite Ð´Ð»Ñ ADO.Net, однако Ñто решаетÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¾Ð¹ аналога через вÑтроенный менеджер пакетов NuGet. ЗапуÑкаем утилиту и получаем на выходе наполненную базу данных MySQL Ñ Ð¾Ð¿Ð¸Ñанными выше изменениÑми. Триггеры необходимо добавить вручную.
539
540 ПоÑкольку никаких других ÑущеÑтвенных изменений Ñделано не было, Ñхема базы данных и внутреннее уÑтройÑтво таблиц Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾Ñтью до типов некоторых Ñтолбцов оÑталиÑÑŒ без изменений и повторно приводить их не имеет ÑмыÑла. Портирование на Ñтом завершено. Считаем, что имеетÑÑ Ð¿Ð¾Ð»Ð½Ñ‹Ð¹ MySQL аналог иÑходной SQLite базы.\\
541
542
543 {
544 \section{MYSQL ОПТИМИЗÐЦИИ}
545 }
546
547 Ðа данный момент имеетÑÑ Ð±Ð°Ð·Ð° данных MySQL. Можно Ñчитать, что вÑе дальнейшие операции, еÑли не оговорено иное, проиÑходÑÑ‚ над ней.
548
549 ЗапроÑ, раÑÑмотренный ранее в ЛиÑтинге ~\ref{lst:bigq} не Ñработает в Ñвоем иÑходном виде на новой базе из-за Ð¾Ñ‚Ð»Ð¸Ñ‡Ð¸Ñ Ð¸Ñпользуемых диалектов. Ð’ MySQL у каждой выборки должен быть Ñвой Ð°Ð»Ð¸Ð°Ñ (alias, пÑевдоним). Проименуем вÑе таблицы в запроÑе, добавив к ним ÑоответÑтвующие техничеÑкие наименованиÑ.
550
551 Теперь мы можем выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° новой базе. ВыполнÑем и фикÑируем результат запроÑа, чтобы при дальнейшем его изменении их можно было Ñравнить и проверить, выдают они одинаковую выборку или нет.\\
552
553 Перейдем теперь к главной чаÑти Ñтой работы - иÑÑледованию и оптимизации запроÑа и/или базы.\\
554
555
556 {
557 \subsection{ИÑÑледование Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на базе MySQL}
558 }
559
560 Как уже отмечалоÑÑŒ ранее, у MySQL имеютÑÑ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ðµ преимущеÑтва перед SQLite. Среди них - наличие команды EXPLAIN. При выполненеии любого запроÑа, оптимизатор запроÑов MySQL Ñоздает наиболее оптимальный план его выполнениÑ. Ðтот план можно поÑмтореть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ команды EXPLAIN. Ðта команда ÑвлÑетÑÑ Ð¾Ð´Ð½Ð¸Ð¼ из Ñамых мощных инÑтрументов разработчика, доÑтупных в MySQL. \\
561
562 Выполним Ñту команду вмеÑте Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñом из ЛиÑтинга ~\ref{lst:bigq}:
563 %здеÑÑŒ результат
564
565 <здеÑÑŒ результат первого EXPLAINа>
566 %ÑÑылка на рез-Ñ‚ ÑкÑплÑина
567
568 ВзглÑнем на результат Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа в таблице <ÑÑылка на рез-Ñ‚ explain'а>.
569 Результат Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑоÑтоит из 11 Ñтолбцов:
570 \begin{itemize}
571 \item[] Id - порÑдковый номер SELECT'а внутри запроÑа
572 \item[] Select\_type - тип запроÑа SELECT. Среди возможных значений:
573 \begin{itemize}
574 \item[] PRIMARY - Ñамый внешний Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð² JOIN'е
575 \item[] DERIVED - данный Ð·Ð°Ð¿Ñ€Ð¾Ñ ÑвлÑетÑÑ Ð¿Ð¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñом
576 \item[] SUBQUERY - первый SELECT в подзапроÑе
577 \item[] UNION - второй или поÑледующий SELECT в UNION'е
578 \item[] UNION RESULT - результат UNION'а
579 \end{itemize}
580 \item[] Table - таблица, к которой отноÑитÑÑ Ñтрока результата
581 \item[] Type - тип ÑвÑÐ·Ñ‹Ð°Ð½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†. Среди возможных значений:
582 \begin{itemize}
583 \item[] Const - таблица имеет только одну ÑоответÑтвующую Ñтроку, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ñ€Ð¾Ð¸Ð½Ð´ÐµÐºÑирована. Таблица в данном Ñлучае читаетÑÑ Ð»Ð¸ÑˆÑŒ раз и в дальнейшем значение Ñтроки воÑпринимаетÑÑ, как конÑтанта. Ðто наиболее быÑтрый тип ÑвзÑываниÑ
584 \item[] Eq\_ref - вÑе чаÑти PRIMARY KEY или UNIQUE NOT NULL индекÑа иÑпользуютÑÑ Ð´Ð»Ñ ÑвÑзываниÑ. Еще один наилучший тип ÑвÑзываниÑ
585 \item[] Ref - прÑÐ¼Ð°Ñ ÑÑылка. Ð’Ñе Ñтроки индекÑного Ñтолбца противопоÑтавлÑÑŽÑ‚ÑÑ Ñтрокам предыдущей таблицы. Ðеплохой вариант
586 \item[] Index - Ñканирование вÑего индекÑного дерева Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка Ñтрок
587 \item[] All - худший тип ÑвÑзи. Ð”Ð»Ñ Ð½Ð°Ñ…Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ñтрок иÑпользуетÑÑ Ð¿Ð¾Ð»Ð½Ð¾Ñ‚ÐµÐºÑтовое Ñканирование таблицы. Указывает на отÑутÑтвие подходÑщих индекÑов в таблице
588 \end{itemize}
589 \item[] Possible\_keys - возможные индекÑÑ‹. Возможно, они не иÑпользуютÑÑ. Значение NULL указывает на отÑутÑтвие подходÑщих индекÑов в таблице.
590 \item[] Key - фактичеÑки иÑпользованный ключ. Может отличатьÑÑ Ð¾Ñ‚ указанных в {\it Possible\_keys} значений
591 \item[] Key\_len - длина иÑпользуемого ключа
592 \item[] Ref - Ñтолбцы или конÑтанты, которые ÑравниваютÑÑ Ñ Ð¸Ð½Ð´ÐµÐºÑом
593 \item[] Rows - чиÑло обработанных запиÑей
594 \item[] Filtered - процент отфильтрованных запиÑей
595 \item[] Extra - Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ð± обработке запроÑа. Среди возможных значений:
596 \begin{itemize}
597 \item[] Using index - Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð° Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸ÐµÐ¼ индекÑного дерева без доп. поиÑка Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñтроки. Возможно при вÑех проиндекÑированных Ñтолбцах.
598 \item[] Using temporary - Ñоздане временной таблицы. Ðапример, при ORDER BY на наборе Ñтобцов, отличном от набора в GROUP BY
599 \item[] Using filesort - Дополнительный проход Ñ Ñохранением ключей Ñтрок, которые попали под уÑловие WHERE и поÑледующей Ñортировкой Ñамих ключей.
600 \item[] Using join buffer (Block Nested Loop) - иÑпользование буфера Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ† Ñ Ð¿Ð¾Ñледующим их объединением путем выборки подходÑщих Ñтрок из буфера
601 \end{itemize}
602 \end{itemize}
603
604 Выше предÑтавлена Ð¸Ð½Ñ‚ÐµÑ€Ð¿Ñ€ÐµÑ‚Ð°Ñ†Ð¸Ñ Ð·Ð°Ñ‡ÐµÐ½Ð¸Ð¹ резульатат EXPLAIN'а. Оценим результат нашего запроÑа:
605 \begin{itemize}
606 \item[] Ð’ процеÑÑе Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа полнотекÑтово ÑканируютÑÑ Ñразу 6 таблиц, которые вполедÑтвие хранÑÑ‚ÑÑ Ð² виде временных таблиц ({\it type: all})
607 \item[] Ð’ первой выборке приÑутÑтвует полное Ñканирование индекÑного дерева первой проÑматриваемой таблицы ({\it type: index}), неÑÐ¼Ð¾Ñ‚Ñ€Ñ Ð½Ð° то, что в таблице ÑодержитÑÑ Ð¾Ð´Ð½Ð° запиÑÑŒ
608 \item[] Ð‘Ð¾Ð»ÑŒÑˆÐ°Ñ Ð´Ð»Ð¸Ð½Ð° иÑпользуемого ключа таблицы первой выборки
609 \item[] ИÑпользование индекÑного дерева ({\it extra: using index}) Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ таблицы ({\it extra: using temporary}) и дополнительный проход по временной таблице Ð´Ð»Ñ Ñортировки ({\it extra: using filesort}) при том, что в таблице 1 запиÑÑŒ
610 \item[] Большое количеÑтво вложенных ({\it Select\_type: DERIVED}) запроÑов, 10 подзапроÑов
611 \item[] ПриÑутÑтвуют 2 таблицы ({\it id: 5,6}) Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ð¼ количеÑтвом проÑмотренных запиÑей (отноÑительно результата запроÑа) ({\it rows: 2247})
612 \item[] ОтÑутÑтвуют данные о ключах в шеÑти запроÑах ({\it id: 1, 4, 5, null, 2, null})
613 \item[] ОтÑутÑтвуют данные о количеÑтве/проценте обработанных Ñтрок ({\it rows/filtered: null}) в процеÑÑе Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ ({\it select\_type: union reslut}) выборок {\it id:5 + id:10} и {\it id:3 + id:4}
614 \end{itemize}
615
616
617
618 {
619 \subsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа}
620 }
621
622 ``Узкие меÑта'' запроÑа на данный момент ÑÑны. ПриÑупим к оптимизации запроÑа. Ð”Ð»Ñ Ñтого вновь обратимÑÑ Ðº best practices по оптимизации запроÑов из руководÑтва к MySQL и выполним некоторые преобразованиÑ.\\
623
624 {
625 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ WHERE}
626 }
627
628 Оптимизтор MySQL умеет удалÑть ненужные Ñкобки, Ñворачивать конÑтанты, удалÑть ненужные уÑловиÑ. Однако, Ñти дейÑÑ‚Ð²Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð°ÐºÑ‚Ð¸Ñ‡ÐµÑки без затрат и выполнение Ñтих преобразований вручную можно опуÑтить, чтобы оÑтавить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð² более понÑтном и удобном Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð²Ð¸Ð´Ðµ.
629
630 Ð’ некотрых ÑлучаÑÑ…, еÑли вÑе Ñтолбцы в индекÑе чиÑловые, MySQL может читать Ñтроки из индекÑа, ÑовÑем не обращаÑÑÑŒ непоÑредÑтвенно к данным.\\
631
632 Среди возможных доÑтупных, но еще не примененных оптимизаций выделÑетÑÑ ``углубление'' уÑÐ»Ð¾Ð²Ð¸Ñ WHERE в запроÑе. Так, при JOIN'е выборка Ñтрок, подходÑщих под уÑловие, будет оÑущеÑтвлÑтьÑÑ Ð´Ð¾ объединениÑ, а значит при выполнении запроÑа не придетÑÑ Ð¿Ñ€Ð¾Ñматривать конечную ``большую'' выборку.
633
634 ПротеÑтируем Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° возможноÑть подобной оптимизации:\\
635
636\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации WHERE}, label={lst:test1where_before}]
637SELECT SQL_NO_CACHE p.personID, m.moduleID, s.taskID
638 FROM submissions s
639 JOIN persons p ON s.PersonID = p.PersonID
640 JOIN modules m ON s.ModuleID = m.ModuleID
641 WHERE p.PersonID BETWEEN 92 AND 109 AND m.ModuleName LIKE `%C%';
642\end{lstlisting}
643
644
645\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации WHERE}, label={lst:test1where_after}]
646SELECT SQL_NO_CACHE p.personID, m.moduleID, s.taskID
647 FROM submissions s
648 JOIN (SELECT personID FROM persons
649 WHERE PersonID BETWEEN 92 AND 109) p
650 ON p.PersonID = s.PersonID
651 JOIN (SELECT moduleID FROM modules WHERE ModuleName LIKE '%C%') m
652 ON m.ModuleID = s.ModuleID;
653\end{lstlisting}
654
655\begin{table}[h]
656 \caption {Сравнение таймингов запроÑов}
657 \label {test1where}
658 \begin{tabular}{|l|c|c|}
659 \hline
660 Ð—Ð°Ð¿Ñ€Ð¾Ñ & Тайминг клиентÑкой Ñтороны & Тайминг Ñерверной Ñтророны \\ \hline
661 До оптимизации & 0.0471 & 0.0463 \\ \hline
662 ПоÑле оптимизации & 0.0313 & 0.0317 \\ \hline
663 \end{tabular}
664\end{table}
665
666%\begin{table}[h]
667% \caption {test1where}
668% \begin{tabular}{|l|}
669% \hline
670% Test query before \\ \hline
671% \verb|SELECT SQL_NO_CACHE p.personID, m.moduleID, s.taskID|\\ \verb| FROM submissions s|\\ \verb| JOIN persons p ON s.PersonID = p.PersonID|\\ \verb| JOIN modules m ON s.ModuleID = m.ModuleID|\\ \verb| WHERE p.PersonID BETWEEN 92 AND 109|\\ \verb| AND m.ModuleName LIKE '%Программирование%';| \\ \hline
672% Client side timing: 0.0471\\Server side timing: 0.0463 \\ \hline
673% Test query after \\ \hline
674% \verb|SELECT SQL_NO_CACHE p.personID, m.moduleID, s.taskID|\\ \verb| FROM submissions s|\\ \verb| JOIN (SELECT personID FROM persons|\\ \verb| WHERE PersonID BETWEEN 92 AND 109) p|\\ \verb| ON p.PersonID = s.PersonID|\\ \verb| JOIN (SELECT moduleID FROM modules|\\ \verb| WHERE ModuleName LIKE `%Программирование%') m|\\ \verb| ON m.ModuleID = s.ModuleID;|\\ \hline
675% Client side timing: 0.0313\\Server side timing: 0.0317 \\ \hline
676% \end{tabular}
677%\end{table}
678
679 Как видно, Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ переноÑе уÑÐ»Ð¾Ð²Ð¸Ñ Ð²Ð³Ð»ÑƒÐ±ÑŒ запроÑа имеет меÑто быть. Теперь применим Ñту оптимизацию к оÑновному запроÑу:\\
680
681\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ I оптимизации WHERE}, label={lst:bigq_where1_before}]
682FROM Students as a3, RelGroupsModules as a4
683LEFT OUTER JOIN RelTasksModules USING(ModuleId)
684LEFT OUTER JOIN Tasks USING(TaskId)
685LEFT OUTER JOIN Submissions
686 ON Submissions.PersonId = 1 AND ...
687 AND (Submissions.Draft IS NULL OR Submissions.Draft = 0)
688WHERE a3.PersonId = 1
689 AND a3.GroupId = a4.GroupId
690\end{lstlisting}
691
692
693\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле I оптимизации WHERE}, label={lst:bigq_where1_after}]
694FROM (
695 SELECT PersonID, a3.GroupID, ModuleID, ExpireTime
696 FROM Students as a3,
697 relgroupsmodules as a4
698 WHERE a3.PersonId = 1
699 AND a3.GroupID = a4.GroupId
700) as pg
701\end{lstlisting}
702
703%\begin{table}[h]
704% \caption {main1where1}
705% \begin{tabular}{|l|}
706% \hline
707% Main query before \\ \hline
708% FROM Students as a3, RelGroupsModules as a4\\ LEFT OUTER JOIN RelTasksModules USING(ModuleId) \\ LEFT OUTER JOIN Tasks USING(TaskId) \\ LEFT OUTER JOIN Submissions \\ ON Submissions.PersonId = 1 \\ AND Submissions.GroupId = a4.GroupId \\ AND Submissions.TaskId = RelTasksModules.TaskId \\ AND Submissions.ModuleId = a4.ModuleId \\ AND (Submissions.Draft IS NULL OR Submissions.Draft = 0) \\ WHERE a3.PersonId = 1 AND \\ a3.GroupId = a4.GroupId \\ \hline
709% Main query after \\ \hline
710% FROM (\\ select PersonID, a3.GroupID, ModuleID, ExpireTime \#5\\ from Students as a3,\\ relgroupsmodules as a4\\ WHERE a3.PersonId = 1\\ AND a3.GroupID = a4.GroupId\\ ) as pg \\ \hline
711% \end{tabular}
712%\end{table}
713
714 Ðа лиÑтингах ~\ref{lst:bigq_where1_before} и ~\ref{lst:bigq_where1_after} предÑтавлены Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ и поÑле оптимизации, озвученной выше. СущеÑтвует еще одна возможноÑть применить оптимизацию к оÑновному запроÑу:\\
715
716\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ II оптимизации WHERE}, label={lst:bigq_where2_before}]
717SELECT * FROM ( ... )
718UNION SELECT ... FROM ...
719LEFT OUTER JOIN Persons
720 ON Persons.PersonId = Submissions.PersonId
721WHERE a1.PersonId = 1
722\end{lstlisting}
723
724\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле II оптимизации WHERE}, label={lst:bigq_where2_after}]
725UNION SELECT ... FROM (
726 SELECT * FROM Approvers
727 WHERE PersonID = 1
728) AS a1
729\end{lstlisting}
730%~\ref{lst: }
731
732 Ðа лиÑтингах ~\ref{lst:bigq_where2_before} и ~\ref{lst:bigq_where2_after} применена еще одна Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñ‡Ð½Ð°Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ WHERE.
733%
734%\begin{table}[h]
735% \caption {main1where2}
736% \begin{tabular}{|l|}
737% \hline
738% Main query before \\ \hline
739% SELECT * FROM ( ... )\\UNION SELECT ...\\LEFT OUTER JOIN Submissions \\LEFT OUTER JOIN Tasks \\\\LEFT OUTER JOIN Persons \\ ON Persons.PersonId = Submissions.PersonId \\ WHERE a1.PersonId = 1 \\ \hline
740% Main query after \\ \hline
741% \\\\ \\ \\ \hline
742% \end{tabular}
743%\end{table}
744
745 {
746 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ ORDER BY / GROUP BY}
747 }
748
749 При иÑпользовании GROUP BY в общем Ñлучае при выполнении запроÑа будет проÑканирована вÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°, затем будет Ñоздана Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° Ð´Ð»Ñ Ñ€Ð°ÑÐ¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñей по группам и Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ðº ним агрегирующих функций. Ðо, в некоторых ÑлучаÑÑ… MySQL может поÑтупить иначе, еÑли имеет дело Ñ Ð¸Ð½Ð´ÐµÐºÑами.
750
751 Самым важным предуÑловием в данном Ñлучае ÑвлÑетÑÑ Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ðµ индекÑа по вÑем Ñтолбцам, фигурирующим в GROUP BY. Ð’ таком Ñлучае ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ таблицы может не потребоватьÑÑ Ð¸ она будет заменена работой Ñ Ð¸Ð½Ð´ÐµÐºÑным деревом.\\
752
753
754 Ð’ MySQL заложены 2 ÑпоÑоба Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ GROUP BY запроÑа Ñ Ð¸Ñпользованием индекÑов:
755 \begin{itemize}
756 \item[] Loose Index Scan - ``Ñвободное'' Ñканирование индеÑа. Самый Ñффективный ÑпоÑоб обработки GROUP BY - когда Ð¸Ð½Ð´ÐµÐºÑ Ð¸ÑпользуетÑÑ Ñ‡Ñ‚Ð¾Ð±Ñ‹ выбрать Ñтолбцы группировки. MySQL может в данном Ñлучае выгодно иÑпользовать ÑвойÑтво Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÐºÐ»ÑŽÑ‡ÐµÐ¹, подразумевающее их Ñортировку. Ðто ÑвойÑтво позволÑет выбирать группы из индекÑа без необходимоÑти раÑÑматривать вÑе ключи, удовлетворÑющие уÑловию WHERE. Сканирование в таком Ñлучае принÑто называть ``Ñвободным''. Столбцы, фигурирующие в GROUP BY при Ñтом обÑзательно должны ÑоÑтавлÑть Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ Ð² каком-либо из индекÑов. ЕÑть и еще одно уÑловие - допуÑтимы только агрегирующие функции MIN() и MAX() и ÑÑылаютÑÑ Ð¾Ð½Ð¸ при Ñтом на один и тот же Ñтолбец, приÑутÑтвующий в индекÑе и Ñледующий непоÑредÑтвенно за Ñтолбцами GROUP BY. Ð”Ð»Ñ Ñтоблцов должны быть Ñозданы полноценные индекÑÑ‹, индекÑирующие Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑоответÑтвующих Ñтолбцов полноÑтью. Ð’ Ñлучае, еÑли вÑе Ñто будет выполнено, в Ñтолбце {\it Extra} ÑоответÑтвующей выборки будет значение {\it Using index for group-by}. Однако, добитьÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð²Ñех Ñтих уÑловий довольно Ñложно. Ð’ таком Ñлучае применÑетÑÑ Tight Index Scan.
757 \item[] Tight Index Scan - ``плотное'' Ñканирование индекÑа. Ð’ Ñлучае, еÑли уÑÐ»Ð¾Ð²Ð¸Ñ Ð´Ð»Ñ Ñвободного ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½Ðµ могут быть выполнены, вÑе еще можно добитьÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð° без ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ таблицы. ЕÑли в уÑловии WHERE приÑутÑтвует проверка диапазона значений, данный метод читает лишь индекÑÑ‹, удовлетворÑющие уÑловиÑм. Иначе он запуÑкает Ñканирование индекÑа. Лишь поÑле Ñтих операций начинаетÑÑ Ð³Ñ€ÑƒÐ¿Ð¿Ð¸Ñ€Ð¾Ð²ÐºÐ° значений.
758 \end{itemize}
759
760 Проверим оптимизацию на примере, в котором применÑетÑÑ Ñ„Ð¾Ñ€Ñированное иÑпользование индекÑа:\\
761
762\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации ORDER BY}, label={lst:test_order_before}]
763SELECT SQL_NO_CACHE * FROM persons
764 HAVING FirstName LIKE '%a%'
765 ORDER BY FirstName;
766\end{lstlisting}
767%~\ref{lst:}
768
769\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации ORDER BY}, label={lst:test_order_after}]
770CREATE INDEX PersonsFirstNameIndex ON Persons(FirstName);
771SELECT SQL_NO_CACHE * FROM persons FORCE INDEX (PersonsFirstNameIndex)
772 HAVING FirstName LIKE '%a%'
773 ORDER BY FirstName;
774\end{lstlisting}
775%~\ref{lst:}
776
777 Выполним оба запроÑа. Сравним результаты выполнениÑ:
778
779\begin{table}[h]
780 \caption {Сравнение таймингов запроÑов}
781 \label {test1where}
782 \begin{tabular}{|l|c|c|}
783 \hline
784 Ð—Ð°Ð¿Ñ€Ð¾Ñ & Тайминг клиентÑкой Ñтороны & Тайминг Ñерверной Ñтророны \\ \hline
785 До оптимизации & 0.0160 & 0.0159 \\ \hline
786 ПоÑле оптимизации & 0.0 & 0.0018 \\ \hline
787 \end{tabular}
788\end{table}
789
790%
791%\begin{table}[h]
792% \caption {test2order}
793% \begin{tabular}{|l|}
794% \hline
795% Test query before \\ \hline
796% SELECT SQL\_NO\_CACHE * FROM persons\\ HAVING FirstName LIKE '\%а\%'\\ ORDER BY FirstName; \\ \hline
797% Client side timing: 0.0160\\Server side timing: 0.0159 \\ \hline
798% Test query after \\ \hline
799% CREATE INDEX PersonsFirstNameIndex ON Persons(FirstName); \\\\SELECT SQL\_NO\_CACHE * FROM persons FORCE INDEX (PersonsFirstNameIndex)\\ HAVING FirstName LIKE '\%а\%'\\ ORDER BY FirstName; \\ \hline
800% Client side timing: 0.0\\Server side timing: 0.0018 \\ \hline
801% \end{tabular}
802%\end{table}
803
804 Как Ñледует из результатов выше, Ð´Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°. Ð”Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ð¸ ``боевого'' запроÑа мы можем добавить индеÑÑ‹ на Ñорируемые /группируемые полÑ, либо добитьÑÑ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð»ÐµÐ²Ñ‹Ñ… префикÑов уже имеющихÑÑ Ð¸Ð½Ð´ÐµÐºÑов. Ð’ данном Ñлучае поÑтараемÑÑ Ð¸Ð·Ð±Ð°Ð²Ð¸Ñ‚ÑŒ от повторной выборки (таблицы {\it a7} и {\it a8}):\\
805
806\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ I оптимизации GROUP BY}, label={lst:bigq_group1_before}]
807SELECT ...
808 FROM (
809 SELECT DISTINCT ... FROM ...
810 GROUP BY GroupId, ModuleId, TaskId
811) as a8
812\end{lstlisting}
813%~\ref{lst:}
814
815\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле I оптимизации GROUP BY}, label={lst:bigq_group1_after}]
816SELECT ...
817 FROM ( ... ) AS a7
818GROUP BY GroupId, ModuleId, TaskId
819\end{lstlisting}
820%~\ref{lst:}
821
822
823%\begin{table}
824% \caption {main2groupby1}
825% \begin{tabular}{|l|}
826% \hline
827% Main query before \\ \hline
828% SELECT DISTINCT ...\\FROM ...\\GROUP BY GroupId, ModuleId, TaskId \\ \\ \hline
829% Main query after \\ \hline
830% SELECT DISTINCT ...\\FROM ...\\... ) as a7\\GROUP BY GroupId, ModuleId, TaskId \\ \hline
831% \end{tabular}
832%\end{table}
833
834 Итак, мы избавлиÑÑŒ от дублирующей выборки, как Ñледует из ЛиÑтингов ~\ref{lst:bigq_group1_before} и ~\ref{lst:bigq_group1_after}. Применим Ñту же оптимизацию еще раз:\\
835
836
837\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ II оптимизации GROUP BY}, label={lst:bigq_group2_before}]
838(SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent, max(IsApprover) AS IsApprover
839 FROM (
840 ...
841 ) as a10
842 GROUP BY GroupId, ModuleId
843) as a12
844\end{lstlisting}
845%~\ref{lst:}
846
847\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле II оптимизации GROUP BY}, label={lst:bigq_group2_after}]
848(SELECT GroupId, ModuleId, IsStudent, IsApprover
849 FROM WorkRole
850 ...
851 GROUP BY GroupId, ModuleId
852) as a12
853\end{lstlisting}
854%~\ref{lst:}
855
856%\begin{table}
857% \caption {main2groupby2}
858% \begin{tabular}{|l|}
859% \hline
860% Main query before \\ \hline
861% SELECT ...\\ FROM ( SELECT ... FROM ...\\ JOIN ...\\ WHERE ...\\ UNION SELECT ... FROM...\\ WHERE ...\\ ) as a10\\ GROUP BY GroupId, ModuleId \\ ) as a12 \\ \hline
862% Main query after \\ \hline
863% CREATE INDEX ... ON WorkRole(GroupID, ModuleID)\\ \\SELECT ... FROM WorkRole \\WHERE ...\\ GROUP BY GroupId, ModuleId \\ ) as a12 \\ \hline
864% \end{tabular}
865%\end{table}
866
867 СоглаÑно ЛиÑтингам ~\ref{lst:bigq_group2_before} и ~\ref{lst:bigq_group2_after} мы Ñократили количеÑтво иÑпользуемых таблиц, Ð²Ð²ÐµÐ´Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½ÑƒÑŽ таблицу {\it WorkRole}, речь о которой подробнее будет немного позднее. Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ñтоит лишь отметить, что в запроÑе задейÑтвован алгоритм группировки по индекÑам.
868
869 Оптимизируем теперь и ORDER BY Ñ Ð¾Ð´Ð½Ð¸Ð¼ допущением. ПоÑкольку ÑиÑтема T-BMSTU на данный момент иÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в МГТУ, обращение к таблице {\it Institutions} можно заменить выборкой из Ñтой таблицы конÑтанты:\\
870
871
872\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации ORDER BY}, label={lst:bigq_order_before}]
873SELECT ... FROM ...
874JOIN ... ON ...
875...
876ORDER BY
877 Institutions.InstitutionId ASC, ...
878\end{lstlisting}
879%~\ref{lst:}
880
881\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации ORDER BY}, label={lst:bigq_order_after}]
882SELECT ... FROM ...
883JOIN ... ON ...
884JOIN (SELECT institutions.InstitutionID, institutions.InstitutionName FROM ...
885 WHERE InstitutionID = 1) AS inst
886ORDER BY ...
887\end{lstlisting}
888%~\ref{lst:}
889
890%\begin{table}
891% \caption {main2orderby1}
892% \begin{tabular}{|l|}
893% \hline
894% Main query before \\ \hline
895% SELECT ... FROM ...\\\\...\\JOIN ... ON ...\\\\ Institutions.InstitutionId ASC, \\ ... \\ \hline
896% Main query after \\ \hline
897% SELECT ... FROM ...\\JOIN ... ON ...\\JOIN (SELECT institutions.InstitutionID, institutions.InstitutionName FROM ...\\ WHERE InstitutionID = 1) AS inst\\JOIN ... ON ...\\ORDER BY \\... \\ \hline
898% \end{tabular}
899%\end{table}
900
901 Итак, мы Ñократили количеÑтво полей (Ñм ЛиÑтинги ~\ref{lst:bigq_order_before} и ~\ref{lst:bigq_order_after}), по которым проиÑходит Ñортировка и теперь в одной из таблиц первичной выборки приÑутÑтвует конÑÑ‚Ð°Ð½Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ из таблицы, что немного уÑкорÑет выполнение запроÑа.
902
903 {
904 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ LIMIT X}
905 }
906
907 Ð’ некоторых ÑлучаÑÑ… оптимизатор MySQL оптимизирует запроÑ, который имеет в Ñвоем ÑоÑтаве LIMIT и не имеет при Ñтом HAVING. ЕÑли в качеÑтве лимита указано необльшое значение, MySQL вероÑтно предпочтет выолнить проход по индекÑам, в то времÑ, как в обычном Ñлучае началоÑÑŒ бы Ñканирование таблицы.
908
909 ЕÑли LIMIT иÑпользуетÑÑ ÑовмеÑтно Ñ ORDER BY, MySQL закончит Ñортировку, как только наберетÑÑ Ð´Ð¾Ñтаточное Ð´Ð»Ñ LIMIT'а количеÑтво Ñтрок. Ð’ Ñлучае Ñ DISTINCT MySQL поÑтупит аналогичным образом.\\
910
911 Однако, ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ ORDER BY вмеÑте Ñ LIMIT 1 может быть оптимизирована. Так, Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ð¸Ð´Ð° \verb|SELECT col FROM table ORDER BY col LIMIT 1| может быть заменен на \verb|SELECT MIN(col) FROM table|. Ð’ Ñлучае, еÑли Ñтолбец проиндекÑирован, MySQL проÑто вернет минимальное значение Ñтолбца из индекÑа, в то времÑ, как LIMIT+ORDER BY должны упорÑдоченно обойти индекÑ.
912
913 Ð”Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° проверим возможноÑть оптимизации на таком теÑтовом запроÑе:\\
914
915\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации LIMIT}, label={lst:test_limit_before}]
916SELECT SQL_NO_CACHE LoginTime FROM Sessions
917 WHERE PersonID BETWEEN 92 AND 109
918 ORDER BY LoginTime DESC
919 LIMIT 1;
920\end{lstlisting}
921%~\ref{lst:}
922
923\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации LIMIT}, label={lst:test_limit_after}]
924SELECT SQL_NO_CACHE MAX(LoginTime) FROM Sessions
925 WHERE PersonID BETWEEN 92 AND 109;
926\end{lstlisting}
927%~\ref{lst:}
928
929 Выполним оба теÑтовых запроÑа. Сравним результаты выполнениÑ:
930
931\begin{table}[h]
932 \caption {Сравнение таймингов запроÑов}
933 \label {test1where}
934 \begin{tabular}{|l|c|c|}
935 \hline
936 Ð—Ð°Ð¿Ñ€Ð¾Ñ & Тайминг клиентÑкой Ñтороны & Тайминг Ñерверной Ñтророны \\ \hline
937 До оптимизации & 0.0780 & 0.0913 \\ \hline
938 ПоÑле оптимизации & 0.0470 & 0.0501 \\ \hline
939 \end{tabular}
940\end{table}
941
942%
943%\begin{table}
944% \caption {test3limit}
945% \begin{tabular}{|l|}
946% \hline
947% Test query before \\ \hline
948% SELECT SQL\_NO\_CACHE LoginTime FROM Sessions\\ WHERE PersonID BETWEEN 92 AND 109\\ ORDER BY LoginTime DESC\\ LIMIT 1; \\ \hline
949% Client side timing: 0.0780\\Server side timing: 0.0913 \\ \hline
950% Test query after \\ \hline
951% SELECT SQL\_NO\_CACHE MAX(LoginTime) FROM Sessions\\ WHERE PersonID BETWEEN 92 AND 109; \\ \hline
952% Client side timing: 0.0470\\Server side timing: 0.0501 \\ \hline
953% \end{tabular}
954%\end{table}
955
956 Ðта Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð´Ð°ÐµÑ‚ незначительные преимущеÑтва в Ñлучае индекÑированных Ñтолбцов и небольшой выигрыш в Ñлучае отÑутÑÑ‚Ð²Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑов. ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ÑутÑтвует на ЛиÑтингах ~\ref{lst:test_limit_before} и ~\ref{lst:test_limit_after}.
957
958 Применим Ñту оптимизацию:\\
959
960
961\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации LIMIT}, label={lst:bigq_limit_before}]
962SELECT Accepted FROM approvements a6
963 JOIN submissions
964 JOIN relgroupsmodules a4
965 WHERE a6.SubmissionId = Submissions.SubmissionId
966 AND (a4.ExpireTime IS NULL
967 OR Submissions.SubmissionTime < a4.ExpireTime)
968 ORDER BY ApprovementTime DESC
969 LIMIT 1;
970\end{lstlisting}
971%~\ref{lst:}
972
973\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации LIMIT}, label={lst:bigq_limit_after}]
974SELECT MAX(Accepted) FROM approvements a6
975 JOIN submissions
976 JOIN relgroupsmodules a4
977 WHERE a6.SubmissionId = Submissions.SubmissionId
978 AND (a4.ExpireTime IS NULL
979 OR Submissions.SubmissionTime < a4.ExpireTime)
980 AND ApprovementTime = (SELECT MAX(ApprovementTime)
981 FROM approvements);
982\end{lstlisting}
983%~\ref{lst:}
984
985
986%\begin{table}
987% \caption {main3limi1}
988% \begin{tabular}{|l|}
989% \hline
990% Main query before \\ \hline
991% SELECT Accepted \\ FROM approvements a6\\ JOIN submissions\\ JOIN relgroupsmodules a4\\ WHERE a6.SubmissionId = Submissions.SubmissionId \\ AND (a4.ExpireTime IS NULL \\ OR Submissions.SubmissionTime < a4.ExpireTime) \\ ORDER BY ApprovementTime DESC \\ LIMIT 1; \\ \hline
992% Main query after \\ \hline
993% SELECT MAX(Accepted)\\ FROM approvements a6\\ JOIN submissions\\ JOIN relgroupsmodules a4\\ WHERE a6.SubmissionId = Submissions.SubmissionId \\ AND (a4.ExpireTime IS NULL \\ OR Submissions.SubmissionTime < a4.ExpireTime) \\ AND ApprovementTime = (SELECT MAX(ApprovementTime) \\ FROM approvements); \\ \hline
994% \end{tabular}
995%\end{table}
996
997 ДоÑтупна еще одна Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð² запроÑе, Ð¿Ð¾Ð´Ð¾Ð±Ð½Ð°Ñ Ð¸Ð·Ð»Ð¾Ð¶ÐµÐ½Ð½Ð¾Ð¹ на ЛиÑтингах ~\ref{lst:bigq_limit_before} и ~\ref{lst:bigq_limit_after} . ОпуÑтим ее, так как их механики в запроÑе идентичны Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾Ñтью до знаков.
998
999 {
1000 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ UNION и DISTINCT}
1001 }
1002
1003 Преобразование UNION в UNION ALL в запроÑе дает большую выгоду. Первым предположением ÑвлÑетÑÑ Ñ‚Ð¾, что Ñто доÑтигаетÑÑ Ð·Ð° Ñчет того, что UNION ALL'у не нужна Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð°, однако Ñто не ÑовÑем верно. Обе формы Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¸Ñпользуют временную таблицу Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ результата.
1004
1005 ИнтереÑен тот факт, что Ñоздание Ñтой временной таблицы можно поÑмотреть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SHOW STATUS. Ð’ обычном EXPLAIN'е Ñтой дейÑтиве по-умолчанию Ñкрыто.
1006
1007 Отличием же в выполнении Ñтих запроÑов ÑвлÑетÑÑ Ñ‚Ð¾, что обычный UNION Ñоздает промежуточную таблицу, накладывает на нее Ð¸Ð½Ð´ÐµÐºÑ Ð¸ лишь поÑле Ñтого приÑтупает к выборке, удалÑÑ Ð´ÑƒÐ±Ð»Ð¸ÐºÐ°Ñ‚Ñ‹. Ð’ то же Ð²Ñ€ÐµÐ¼Ñ UNION ALL пропуÑкает Ñти дейÑтвиÑ, за Ñчет чего и доÑтигаетÑÑ Ð¿Ð¾Ð²Ñ‹ÑˆÐµÐ½Ð¸Ðµ производительноÑти.\\
1008
1009 Проверим Ñтот факт, выполнив Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¾Ð´Ð¸Ð½ раз Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸ÐµÐ¼ UNION, другой - Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸ÐµÐ¼ UNION ALL, как показано на ЛиÑтингах ~\ref{lst:test_union_before} и ~\ref{lst:test_union_after}:\\
1010
1011
1012\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации UNION}, label={lst:test_union_before}]
1013SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime
1014 FROM Submissions WHERE PassedTests IS NOT NULL
1015UNION
1016SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime
1017 FROM Submissions WHERE PassedTests IS NULL;
1018\end{lstlisting}
1019%~\ref{lst:}
1020
1021\begin{lstlisting}[caption={теÑтовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации UNION}, label={lst:test_union_after}]
1022SELECT DISTINCT personID, groupID, moduleID, taskID
1023 FROM Submissions WHERE PassedTests IS NOT NULL
1024UNION ALL
1025SELECT DISTINCT personID, groupID, moduleID, taskID
1026 FROM Submissions WHERE PassedTests IS NOT NULL;
1027\end{lstlisting}
1028%~\ref{lst:}
1029
1030 Выполним оба теÑтовых запроÑа. Сравним результаты выполнениÑ:
1031
1032\begin{table}[h]
1033 \caption {Сравнение таймингов запроÑов}
1034 \label {test1where}
1035 \begin{tabular}{|l|c|c|}
1036 \hline
1037 Ð—Ð°Ð¿Ñ€Ð¾Ñ & Тайминг клиентÑкой Ñтороны & Тайминг Ñерверной Ñтророны \\ \hline
1038 До оптимизации & 0.1400 & 0.1337 \\ \hline
1039 ПоÑле оптимизации & 0.0780 & 0.0657 \\ \hline
1040 \end{tabular}
1041\end{table}
1042
1043%\begin{table}
1044% \caption {test4union}
1045% \begin{tabular}{|l|}
1046% \hline
1047% Test query before \\ \hline
1048% SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime\\ FROM Submissions WHERE PassedTests IS NOT NULL \\UNION \\SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime\\ FROM Submissions WHERE PassedTests IS NULL; \\ \hline
1049% Client side timing: 0.1400\\Server side timing: 0.1337 \\ \hline
1050% Test query after \\ \hline
1051% SELECT DISTINCT personID, groupID, moduleID, taskID \\ FROM Submissions WHERE PassedTests IS NOT NULL \\UNION ALL\\SELECT DISTINCT personID, groupID, moduleID, taskID \\ FROM Submissions WHERE PassedTests IS NOT NULL; \\ \hline
1052% Client side timing: 0.0780\\Server side timing: 0.0657 \\ \hline
1053% \end{tabular}
1054%\end{table}
1055
1056 ОÑновным моментом здеÑÑŒ ÑвлÑетÑÑ Ñ‚Ð¾, что в обоих запроÑах приÑутÑтвуют ключевые Ñлова DISTINCT. Рзначит, по Ñхеме Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ MySQL Ñначала Ñделает выборку из одной таблицы, отфильтровав дубликаты, затем поÑтупит по аналогии Ñо второй таблицей. Затем в запроÑе Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼ UNION, поÑле Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð±Ð¾Ñ€Ð¾Ðº во временную таблицу, MySQL добавит индекÑÑ‹ и вновь отфильтрует результаты, Ð¿Ñ€Ð¾Ð¹Ð´Ñ Ð¿Ð¾ таблице. Однако в таблице к тому моменту уже не будет дубликатов и Ñтот проход будет лишним.
1057
1058 ИзбавившиÑÑŒ от него Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ UNION ALL, мы получим выигрыш в производительноÑти.
1059
1060 Стоит также заметить, что как и в Ñлучае Ñ GROUP BY / ORDER BY, MySQL может иÑпользовть лишь левые префикÑÑ‹ индекÑов Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ ÐºÐ»ÑŽÑ‡Ð°Ð¼Ð¸, и в целом DISTINCT иногда раÑÑматриваетÑÑ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð¾Ð¼, как чаÑтный Ñлучай GROUP BY.
1061
1062 Выполним опиÑанные выше преобразованиÑ, так как аналогичные уÑÐ»Ð¾Ð²Ð¸Ñ Ð¸Ð¼ÐµÑŽÑ‚ÑÑ Ð² раÑÑматриваемом нами запроÑе и запишем новые запроÑе в ЛиÑтинги ~\ref{lst:bigq_union_before} и ~\ref{lst:bigq_union_after}:\\
1063
1064
1065\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации UNION}, label={lst:bigq_union_before}]
1066SELECT DISTINCT ... FROM ... AS a8
1067UNION
1068SELECT DISTINCT ... FROM ... AS a1
1069\end{lstlisting}
1070%~\ref{lst:}
1071
1072\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации UNION}, label={lst:bigq_union_after}]
1073SELECT DISTINCT ... FROM ... AS a7
1074UNION ALL
1075SELECT DISTINCT ... FROM ... AS a1
1076\end{lstlisting}
1077%~\ref{lst:}
1078
1079
1080%\begin{table}
1081% \caption {main4union1}
1082% \begin{tabular}{|l|}
1083% \hline
1084% Main query before \\ \hline
1085% SELECT DISTINCT ... FROM ... AS a8 \\UNION \\SELECT DISTINCT ... FROM ... AS a1 \\ \hline
1086% Main query after \\ \hline
1087% SELECT DISTINCT ... FROM ... AS a7\\UNION ALL\\SELECT DISTINCT ... FROM ... AS a1 \\ \hline
1088% \end{tabular}
1089%\end{table}
1090
1091
1092 {
1093 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ LEFT и RIGHT JOIN}
1094 }
1095
1096 MySQL выполнÑет объединение таблиц, например \verb|A LEFT JOIN B| подобным образом:
1097 \begin{itemize}
1098 \item[] Таблица B уÑтанавливаетÑÑ Ð·Ð°Ð²Ð¸Ñимой от A и от вÑех таблиц, от которых завиÑит A. Таблица A в Ñвою очередь уÑтанваливаетÑÑ Ð·Ð°Ð²Ð¸Ñимой от вÑех таблиц, кроме B, которые иÑпользуютÑÑ Ð² уÑловии LEFT JOIN
1099 \item[] ИÑпользуетÑÑ ÑƒÑловие LEFT JOIN Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы решить, как выбрать запиÑи из таблицы B
1100 \item[] ВыполнÑÑŽÑ‚ÑÑ Ð²Ñе Ñтандартные оптимизации входÑщих внутрь запроÑа уÑловий
1101 \item[] Ð’ Ñлучае, еÑли в таблице B запиÑÑŒ, ÑоответÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ ÑƒÑловию ON, и Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ в таблице A имеетÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ, отÑутÑтвует, то в таблицу B допиÑываетÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ Ñо вÑеми Ñтолбцами, равными NULL
1102 \item[] ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ в таблице Ñо вÑеми параметрами, равными NULL, добавлÑетÑÑ Ð² результирующей выборке в ÑоответÑтвие непуÑтой запиÑи из таблицы A
1103 \end{itemize}
1104
1105 Ð ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ RIGHT JOIN аналогична Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾Ñтью до порÑдка таблиц. Оптимизацией JOIN запроÑов ÑвлÑетÑÑ Ð¿ÐµÑ€ÐµÑтановка таблиц в запроÑах. Однако LEFT JOIN и STRAIGHT JOIN практичеÑки не оптимизируютÑÑ.
1106
1107 Еще одна форма Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ - STRAIGHT JOIN. Ðта команда не дает оптимизатору выбора, кроме как принÑть порÑдок, заданный пользователем, вÑледÑтвие чего никакие дополнительные Ð¿Ñ€ÐµÐ¾Ð±Ð°Ñ€Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½Ðµ производÑÑ‚ÑÑ Ð¸ Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð·Ð° Ñчет Ñтого уÑкорÑетÑÑ. Как и в оÑтальных ÑлучаÑÑ… упор делаетÑÑ Ð½Ð° наличие индекÑов. ДоÑтигаетÑÑ Ñто улучшение только в том, Ñлучае, еÑли извеÑтно, что ``навÑзанный'' оптимзатору порÑдок ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ð¾ лучше чем тот, который может выбрать он Ñам. Ð’ большинÑтве Ñлучаев ``навÑзывание'' подобных уÑловий оптимизатору может привеÑти к обратному результату.\\
1108
1109 ÐавÑзвание иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑов ÑовмеÑтно Ñ ``непереÑтавлÑемым'' LEFT JOIN'ом уже раÑÑматривалоÑÑŒ ранее. Чтобы избежать дублированиÑ, опуÑтим Ñтот момент здеÑÑŒ.
1110
1111 {
1112 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ IS (NOT) NULL}
1113 }
1114
1115 MySQL оптимизатор умеет удалÑть ненужны проверки на наличие /отÑутÑтивие NULL значений. Так, еÑли уÑловие WHERE Ñодержит проверку Ñтолбца {\it a} IS NULL при том, что Ñам Ñтолбец объÑвлен как NOT NULL, проверка будет удалена.
1116
1117 Также, ÑоглаÑно best practices, не рекомендуетÑÑ Ð¸Ñпользовать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° NULL на Ñтолбцах типов DATE, TIME и DATETIME. Убрав поддержку NULL значений и заменив ее Ñравнением Ñ ``новым отÑутÑтвующим'' значением, например Ñтандартной NULL-датой 0000-00-00 00:00:00, обеÑпечим выолнение best practices в рамках таблицы. ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ñ€ÐµÐ´Ñтавлена на ЛиÑтингах ~\ref{lst:bigq_null_before} и ~\ref{lst:bigq_null_after}:\\
1118
1119\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ оптимизации IS (NOT) NULL}, label={lst:bigq_null_before}]
1120(SELECT Accepted
1121 FROM Approvements as a6
1122 WHERE a6.SubmissionId = Submissions.SubmissionId
1123 AND (a4.ExpireTime IS NULL
1124 OR Submissions.SubmissionTime < a4.ExpireTime)
1125\end{lstlisting}
1126%~\ref{lst:}
1127
1128\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле оптимизации IS (NOT) NULL}, label={lst:bigq_null_after}]
1129(SELECT Accepted
1130 FROM Approvements as a6
1131 WHERE a6.SubmissionId = Submissions.SubmissionId
1132 AND (a4.ExpireTime > '0000-00-00 00:00:00'
1133 OR Submissions.SubmissionTime < a4.ExpireTime)
1134\end{lstlisting}
1135%~\ref{lst:}
1136
1137%\begin{table}
1138% \caption {main6null1}
1139% \begin{tabular}{|l|}
1140% \hline
1141% Main query before \\ \hline
1142% SELECT Accepted \\FROM Approvements as a5\\WHERE a5.SubmissionId = Submissions.SubmissionId \\AND a4.ExpireTime IS NOT NULL AND Submissions.SubmissionTime >= a4.ExpireTime \\ORDER BY ApprovementTime DESC \\LIMIT 1 \\ \hline
1143% Main query after \\ \hline
1144% SELECT Accepted \#7\\ FROM Approvements as a5\\ WHERE a5.SubmissionId = Submissions.SubmissionId \\ AND a4.ExpireTime > '0000-00-00 00:00:00' \\AND Submissions.SubmissionTime >= pg.ExpireTime \\ ORDER BY ApprovementTime DESC \\ LIMIT 1 \\ \hline
1145% \end{tabular}
1146%\end{table}
1147
1148 {
1149 \subsection{MySQL и индекÑÑ‹}
1150 }
1151
1152 Лучший ÑпоÑоб улучшить производительноÑть операции SELECT Ñто Ñоздать индекÑÑ‹ на одном или неÑкольких Ñтолбцах, иÑпользуемых в запроÑе. ИндекÑÑ‹ выÑтупают в качетÑве указателей на запиÑи, позволÑÑ Ð±Ñ‹Ñтро определить какие запиÑи подходÑÑ‚ под уÑловие WHERE и выбрать оÑтальные Ð¿Ð¾Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи.
1153
1154 Ð’Ñе типы данных в MySQL могут быть проиндекÑированы. Ð’Ñе индекÑÑ‹ в MySQL в рамках движка InnoDB, будь то PRIMARY, UNIQUE и INDEX, Ñохранены в B-беревьÑÑ…. ИндекÑные Ñтраницы при Ñтом хранÑÑ‚ÑÑ Ð²Ð¼ÐµÑте Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸. MyISAM же иÑпользует Ð´Ð»Ñ Ñтих целей хеш-таблицы.
1155
1156 И, Ñ…Ð¾Ñ‚Ñ Ð¼Ð¾Ð¶ÐµÑ‚ возникнуть желание Ñоздать индекÑÑ‹ по вÑем возможным Ñтолбцам, неиÑпользуемые и ненужные ндекÑÑ‹ занимают меÑто и отнимают у оптимизатора MySQL Ð²Ñ€ÐµÐ¼Ñ Ð½Ð° поиÑк необходимиого ему оптимального индекÑа.
1157
1158 ИндекÑÑ‹ также увеличивают ``ÑтоимоÑть'' операций вÑтавки, ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸ обновлениÑ, так как каждый Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть обновлен.
1159
1160 MySQL поддерживает до 16 ключей на одной таблице.
1161
1162 Стобцы типов BLOB и TEXT поддерживают неполное индекÑирование. Ðа Ñтолбцах типов CHAR и VARCHAR разрешено Ñоздавать чаÑтичные индекÑÑ‹, которые при Ñтом могут ÑоÑтавлÑть чаÑть многоÑтолбцового индекÑа.
1163
1164 Как уже оговаривалоÑÑŒ ранее, только крайние левые префикÑÑ‹ индекÑа могут быть иÑпользованы большинÑтвом операций. Однако, иногда выборка может быть произведена ÑовÑем без Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ðº данным. Ðто проиÑходит в том Ñлучае, еÑли выбираетÑÑ Ñ‡Ð°Ñть индекÑа по уÑловию другой чаÑти индекÑа. Так, трехÑтолбцовый Ð¸Ð½Ð´ÐµÐºÑ Ð½Ð° Ñтолбцах {\it (a, b, c, d)} дает поиÑковые преимущеÑтва в таких ÑочетаниÑÑ…: {\it(a), (a, b), (a, b, c)}. При выборке же можно иÑпользовать Ñтолбец {\it c} в то времÑ, как уÑловие наложено на Ñтолбец {\it a} или {\it b}.\\
1165
1166 Ранее было отмечено, что данные большинÑтва таблиц в иÑходной базе данных редактируютÑÑ Ð½ÐµÑ‡Ð°Ñто. ПоÑтому, на них можно без ограничений накладыватьь индекÑÑ‹. Ð’ базе приÑутÑтвуют лишь 4 поÑтоÑнно иÑпользуемых таблицы, которые были отмечены ранее. Ðа Ñтих таблицах уже приÑутÑтвуют индекÑÑ‹ помимо PRIMARY и UNIQUE. Ðти индекÑÑ‹, дейÑтвительно, иÑпользуютÑÑ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð¾Ð¼ и ``утÑжелÑть'' таблицы дополнительными индекÑами мы не будем.
1167
1168 ВмеÑто Ñтого добавим неÑколько индекÑов, возможное отÑутÑтвие некоторых из которых приводило к полму Ñканированию таблиц ÑоглаÑно первому результату EXPLAIN'а:\\
1169
1170 \begin{verbatim}
1171CREATE INDEX InstitutionIDInstitutionNameIndex
1172 ON Institutions(InstitutionID, InstitutionName);
1173CREATE INDEX GroupInstitutionTimeNameIndex
1174 ON Groups (GroupID, InstitutionID, TimeID, GroupName);
1175CREATE INDEX GroupIndex ON Approvers (GroupID);
1176CREATE UNIQUE INDEX RelGroupsModulesExpireTimeGroupIDModuleIDUniqueIndex
1177 ON RelGroupsModules(ExpireTime, GroupID, ModuleID);
1178 \end{verbatim}
1179
1180
1181 {
1182 \subsection{Другие оптимизации}
1183 }
1184
1185 {
1186 \subsubsection{Выбор движка базы данных}
1187 }
1188
1189 Одной из оÑобенноÑтей MySQL ÑвлÑетÑÑ Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ðµ большого чиÑла движков, практичеÑки каждый из которых Ñпециализирован под конкретную задачу. ПредполагаетÑÑ, что выбор движка проиÑходит на Ñтапе проектированиÑ. ПеречиÑлим оÑновные движки и озвучим их оÑобенноÑти:
1190
1191 \begin{itemize}
1192 \item[] MyISAM - не поддерживает транцзакции, но поддерживает полнотекÑтовый поиÑк. Внешние ключи недоÑтупны. Данные и индекÑÑ‹ хранÑÑ‚ÑÑ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ð¾. Сравнительно невыÑÐ¾ÐºÐ°Ñ Ð½Ð°Ð´ÐµÐ¶Ð½Ð¾Ñть Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…
1193 \item[] Memory (ранее, HEAP) - отличаетÑÑ Ð½ÐµÑравнимо быÑтрой работой Ñ Ð½ÐµÐ±Ð¾Ð»ÑŒÑˆÐ¸Ð¼Ð¸ таблицами, так как хранит вÑе временные таблицы в оперативной памÑти. ПрактичеÑки не имеет конкурентов по ÑкороÑти работы
1194 \item[] Federated - Ñ„ÐµÐ´ÐµÑ€Ð°Ñ†Ð¸Ñ Ñерверов, обеÑÐ¿ÐµÑ‡Ð¸Ð²Ð°ÑŽÑ‰Ð°Ñ Ð²Ñ‹Ñокую маÑштабируемоÑть и выÑокую отказоуÑтойчивоÑть
1195 \item[] CSV - хранит таблицы в CSV формате и позволÑет редактировать их внешними приложениÑми. ОтличаетÑÑ Ñ‚Ð°ÐºÐ¶Ðµ невыÑокой ÑтаблильнотÑью работы
1196 \item[] InnoDB - движок Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ† ``общего'' назначениÑ, тем не менее поддерживающий большие таблицы. ÐŸÐ¾Ð»Ð½Ð°Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ° транзакций (ACID), внешних ключей. МакÑимальный объем - 64ТБ. По заÑвлениÑм разработчиков InnoDB - Ñамый быÑтрый оÑнованный ``на диÑке'' движок. Однако, Ñильно завиÑит от надлежащей индекÑации данных
1197 \item[] Blackhole - движок, Ñозданный Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡ репликации.Ðе умеет ÑамоÑтоÑтельно хранить данные. Может выÑтпуть ``маÑтером'' в Ñхеме репликации master-slave
1198 \item[] Example - ÑкÑпериментальный движок Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð². Таблицы, оÑнованные на нем не могут хранить данные и нужны в первую очередь Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ñ‹Ñ… типов таблиц.
1199 \end{itemize}
1200
1201 Как было замечено ранее, в работе был выбран движок InnoDB по причине поддержки внешних ключей, транзакций и других функций ``из коробки''.
1202
1203
1204 {
1205 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… типов данных}
1206 }
1207
1208 Одним из ÑпоÑобов Ð¸Ð·Ð¼ÐµÑ€ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти запроÑов MySQL называет измерение количеÑтва диÑковых операций. Ð”Ð»Ñ Ð½ÐµÐ±Ð¾Ð»ÑŒÑˆÐ¸Ñ… таблиц обычно можно найти Ñтроку одним обращением. Ð”Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ñ… таблиц, иÑпользующих дерево индекÑов, количеÑтво диÑковых операции можно оценить формулой
1209
1210 $$C=\frac{\ln row\_count}{\ln {\frac{index\_block\_length \times 2}{3 \times (index\_length + data\_pointer\_length)}}}$$
1211
1212 ИндекÑный блок обычно ÑоÑтавлÑет 1024 байта, указатель - 4 байта.
1213
1214 Можем оценить количеÑтво диÑковых операций Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ {\it Submissions}:
1215
1216 $$C=\frac{\ln 130'000}{\ln {\frac{1'024 \times 2}{3 \times (16 + 4)}}} = \frac{11.775}{3.530} \approx 3$$
1217
1218 Итого, потребуетÑÑ Ð² Ñреднем 3 диÑковых операции Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы найти конкретную Ñтроку в таблице {\it Submissions}, вмещающей 130'000 запиÑей при наличии на ней индекÑа длины 16.
1219
1220 ЛогарифмичеÑÐºÐ°Ñ Ð·Ð°Ð²Ð¸ÑимоÑть объема таблицы позволÑет оценить незначительноÑть количеÑтва диÑковых операций Ð´Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ð½Ñтва таблиц базы. ВзÑв абÑтрактную таблицу, Ñодержащую, например, 300 запиÑей и имеющую Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð»Ð¸Ð½Ñ‹ 4 на Ñтолбце типа INTEGER выÑÑним, что запиÑи из подобных таблиц выбираютÑÑ Ð·Ð° одно обращение. Ðто еще раз подтверждает возможноÑть Ð½Ð°Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ñ… индекÑов на подобные таблицы:
1221
1222 $$C=\frac{\ln 300}{\ln {\frac{1'024 \times 2}{3 \times (4 + 4)}}} \approx 1$$
1223
1224 И Ñ…Ð¾Ñ‚Ñ Ð² наше Ð²Ñ€ÐµÐ¼Ñ ÑкороÑть работы важнее объема затраченной памÑти, в MySQL рекомендуетÑÑ Ñодержать данные компактно. Рзначит, Ñледуют и небольшие оптимизации некоторых таблиц базы данных:\\
1225
1226 \begin{itemize}
1227 \item[] TINYINT или MEDIUMINT препочтительнее ``обчыного'' типа INT, еÑли Ñто не проиворечит логике работы
1228 \item[] NULL требует дополнительного меÑта, а значит, Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð² виде NOT NULL значений немного уменьшат меÑто. Ðто преимущеÑтво в раÑÑамтриваемой базе незначительно ввиду небольшого общего объема данных
1229 \item[] Ð’Ñ‹Ð½Ð¾Ñ Ð»Ð¾Ð³Ð¸ÐºÐ¸ работы Ñ Ð²ÐµÐ»Ð¸Ñ‡Ð¸Ð½Ð°Ð¼Ð¸ времени и даты во вне. Ð’ базе при Ñтом можно оÑтавить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° TIMESTAMP или INT. Ðта Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð·Ð½Ð°Ñ‡Ð¸Ñ‚ÐµÐ»ÑŒÐ½ÐµÐµ предыдущих и может дать преимущеÑтво в неÑколько раз.
1230 \end{itemize}
1231
1232 {
1233 \subsubsection{ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных}
1234 }
1235
1236 Ð’ ходе раÑÑÐ¼Ñ‚Ð¾Ñ€ÐµÐ½Ð¸Ñ Ð¿Ð»Ð°Ð½Ð° Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа было выÑвлено большое количеÑтво подзапроÑов, в том чиÑле Ñ Ð¿Ð¾Ð»Ð½Ñ‹Ð¼ Ñканированием таблиц. Один из таких запроÑов, Ñ {\it id=1} отмечен как {\it derived 2}, ÑÑылающийÑÑ Ð½Ð° Ð¿Ð¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ Ñ {\it id=2}. Тот в Ñвою очередь отмечен, как {\it derived 3}. ÐŸÐ¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ Ñ {\it id=3} предÑтавлÑет из ÑÐµÐ±Ñ 2 подзапроÑа - {\it a11} и {\it RelGroupsModules}. ПоÑле Ñтого выполнÑетÑÑ Ð²Ñ‹Ð±Ð¾Ñ€ÐºÐ° из таблицы {\it a9}. И лишь поÑле Ñтого проиÑходит объединение Ñтих подзапроÑов в результирующую выборку. Ð’Ñе Ñто ÑочетаетÑÑ Ñ Ð½ÐµÐ¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ð¾Ñтью ключей Ð´Ð»Ñ Ð²Ñех Ñтих выборок а также поÑтоÑнным Ñканированием таблиц.
1237
1238 Ðто можно иÑправить путем ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ таблицы. Ðазовем ее WorkRole. Ð’ нее перенеÑем функциональноÑть под разделению ролей пользователей на Ñтудентов и принимающую их Ñторону:\\
1239
1240\begin{lstlisting}[caption={Скрипт ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ WorkRole}, label={lst:table_workrole}]
1241CREATE TABLE WorkRole (
1242 PersonID INTEGER NOT NULL REFERENCES Persons
1243 ON DELETE CASCADE ON UPDATE CASCADE,
1244 GroupID INTEGER NOT NULL REFERENCES Groups
1245 ON DELETE CASCADE ON UPDATE CASCADE,
1246 ModuleID INTEGER NOT NULL REFERENCES Modules
1247 ON DELETE CASCADE ON UPDATE CASCADE,
1248
1249 IsStudent TINYINT NOT NULL,
1250 IsApprover TINYINT NOT NULL,
1251
1252 PRIMARY KEY (PersonID, GroupID, ModuleID, IsStudent)
1253);
1254\end{lstlisting}
1255
1256 Ðаличие ключа, ÑоÑтоÑщего из 4 Ñтолбцов объÑÑнÑетÑÑ Ñпецификой выборки данных в запроÑе. Таблица ноÑит техничеÑкий характер и Ñоздана Ñ Ñ†ÐµÐ»ÑŒÑŽ Ñбора данных Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа. При Ñтом необходимо наличие PRIMARY индекÑа в подобном порÑдке, чтобы впоÑледÑтвие иÑпользовать его также как и в изначальной верÑии запроÑа.
1257
1258Создадим дополнительные индекÑÑ‹ Ð´Ð»Ñ Ð²Ñ‹Ð±Ð¾Ñ€ÐºÐ¸ непоÑредÑтвенно ролей (так как в PRIMARY индекÑе при отÑутÑтвии выборки по префикÑу {\it PersonID, GroupID, ModuleID}, иÑполнитель не Ñможет оптимально работать Ñо Ñтолбцами {\it IsStudent} и {\it IsApprover}:\\
1259
1260\begin{verbatim}
1261CREATE INDEX WorkRoleIsStudentIndex ON WorkRole(PersonID, IsStudent);
1262CREATE INDEX WorkRoleIsApproverIndex ON WorkRole(PersonID, IsApprover);
1263\end{verbatim}
1264
1265 Чтобы не повлиÑть на текущую функциональноÑть базы, вÑе изначально имеющиеÑÑ Ð² ней таблице редактировать не будем. Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ же Ñ Ñтой вновь Ñозданной таблицей Ñоздадим необходимые триггеры, которые изменÑÑŽÑ‚ данные внутри {\it WorkRole} оÑновываÑÑÑŒ на изменении данных в ``базовых'' Ð´Ð»Ñ Ð½ÐµÐµ таблицах {\it Students} и {\it Approvers}.
1266
1267 Перепишем ту чаÑть запроÑа, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½ÐµÐºÐ¾Ð³Ð´Ð° выполнÑла опиÑанные выше дейÑтвиÑ, изменив порÑдок Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð½Ð° работу Ñ Ð½Ð¾Ð²Ð¾Ð¹ таблицей WorkRole, ÑоглаÑно ЛиÑтингам ~\ref{lst:bigq_workrole_before} и ~\ref{lst:bigq_workrole_after}:\\
1268
1269\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ Ð²Ð½ÐµÐ´Ñ€ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ WorkRole}, label={lst:bigq_workrole_before}]
1270SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent, max(IsApprover) AS IsApprover
1271 FROM (
1272 SELECT a11.GroupId, ModuleId, 1 AS IsStudent, 0 AS IsApprover
1273 FROM Students as a11
1274 JOIN RelGroupsModules USING(GroupId)
1275 WHERE PersonId = 1
1276 UNION
1277 SELECT GroupId, ModuleId, 0 AS IsStudent, 1 AS IsApprover
1278 FROM Approvers as a9
1279 WHERE PersonId = 1
1280 ) as a10
1281 GROUP BY GroupId, ModuleId
1282\end{lstlisting}
1283%~\ref{lst:}
1284
1285\begin{lstlisting}[caption={оÑновной Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ñле Ð²Ð½ÐµÐ´Ñ€ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ WorkRole}, label={lst:bigq_workrole_after}]
1286SELECT GroupId, ModuleId, IsStudent, IsApprover
1287 FROM WorkRole
1288 WHERE PersonID = 1
1289 GROUP BY GroupId, ModuleId
1290\end{lstlisting}
1291%~\ref{lst:}
1292
1293
1294%\begin{table}
1295% \caption {WorkRole}
1296% \begin{tabular}{|l|}
1297% \hline
1298% Main query before \\ \hline
1299% SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent, max(IsApprover) AS IsApprover \\ FROM ( \\ SELECT a11.GroupId, ModuleId, 1 AS IsStudent, 0 AS IsApprover \\ FROM Students as a11\\ JOIN RelGroupsModules USING(GroupId) \\ WHERE PersonId = 1 \\ UNION SELECT GroupId, ModuleId, 0 AS IsStudent, 1 AS IsApprover \\ FROM Approvers as a9\\ WHERE PersonId = 1 \\ ) as a10\\ GROUP BY GroupId, ModuleId \\ \hline
1300% Main query after \\ \hline
1301% SELECT GroupId, ModuleId, IsStudent, IsApprover\\ FROM WorkRole \\ WHERE PersonID = 1\\ GROUP BY GroupId, ModuleId \\ \hline
1302% \end{tabular}
1303%\end{table}
1304
1305 \bigskip
1306
1307 {
1308 \section{ТЕСТИРОВÐÐИЕ}
1309 }
1310
1311 {
1312 \subsection{Сравнение производительноÑти}
1313 }
1314
1315 Ðа данный момент имеетÑÑ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ð¹ по опиÑанным выше пунктам запроÑ. Ðекоторые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ходе работы коÑнулиÑÑŒ и Ñамой базы данных. Она также была оптимизирована.
1316
1317 Ð’Ñе проводимые оптимизации не затрагивали результат выборки оÑновоного запроÑа. Так что иÑходный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸ получившийÑÑ Ð² итоге, оптимизированный, можно Ñчитать идентичными по Ñвоей функциональноÑти.
1318
1319 ВзглÑнем на план Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ запроÑа
1320
1321 %вÑтавить новый ÑкÑплÑин
1322 <здеÑÑŒ новый EXPLAIN>
1323
1324 Вот что изменилоÑÑŒ по Ñравнению Ñ Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð¸Ð¼ выполнением EXPLAIN'а:
1325 \begin{itemize}
1326 \item[] ОÑталиÑÑŒ 2 полнотекÑтовых ÑканированиÑ, вмеÑто 6 первоначальных
1327 \item[] Убрана неиÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð²Ñ‹Ð±Ð¾Ñ€ÐºÐ°, Ð´ÑƒÐ±Ð»Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ð¿Ð¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ ({\it id = 4})
1328 \item[] ОÑталиÑÑŒ лишь 7 подзапроÑов вмеÑто 10 первоначальных
1329 \item[] Перва выборка имеет макÑимально быÑтрый (поÑле ({\it type=system}) тип объединениÑ, а длина иÑпользуемого ключа Ñокращена
1330 \item[] Уменьшено общее количеÑтво учаÑтвующих в запроÑе Ñтрок
1331 \end{itemize}
1332
1333 СоглаÑно результатам EXPLAIN'ов, запроÑ, дейÑтвительно, Ñтал работать оптимальнее. Ð’ качеÑтве результата предÑтавим таблицу Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов инÑтрументом MySQL Profiler, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½Ðµ учитывает Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных, Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ð¸ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†, Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… процеÑÑов и Ñ‚.д., а показывает идеализированное ``чиÑтое'' Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ:
1334
1335\begin{table}[h]
1336 \caption {test1profiling}
1337 \begin{tabular}{|c|c|c|}
1338 \hline
1339 Тип дейÑÑ‚Ð²Ð¸Ñ & Тайминг иÑходного запроÑа & Тайминг полученного запроÑа\\ \hline
1340 executing & 0.000001 & 0.000001 \\
1341 Sending data & 0.000005 & 0.000005 \\
1342 executing & 0.000001 & 0.000001 \\
1343 Sending data & 0.000005 & {\bf 0.000016 } \\
1344 executing & 0.000001 & 0.000001 \\
1345 Sending data & 0.000005 & 0.000006 \\ \hline
1346 … & … & … \\ \hline
1347 Sending data & 0.000006 & {\bf 0.000035 } \\
1348 executing & 0.000001 & 0.000002 \\
1349 Sending data & {\bf 0.005742} & 0.000046 \\
1350 executing & 0.000003 & 0.000001 \\
1351 Sending data & {\bf 0.031268} & 0.023095 \\
1352 Creating sort index & {\bf 0.014082} & 0.013395 \\
1353 end & 0.000011 & 0.000012 \\ \hline
1354 … & … & … \\ \hline
1355 query end & 0.000013 & 0.000013 \\
1356 removing tmp table & 0.000175 & {\bf 0.000201} \\
1357 closing tables & 0.000002 & 0.000004 \\
1358 removing tmp table & 0.000004 & 0.000003 \\
1359 closing tables & 0.000001 & 0.000005 \\
1360 freeing items & 0.000127 & {\bf 0.000139} \\
1361 cleaning up & 0.000017 & 0.000018 \\ \hline
1362 \end{tabular}
1363\end{table}
1364
1365 ОпуÑтим бОльшую чаÑть таблицы резульата, оÑтавив только начало и наиболее отличительные моменты. Общее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ запроÑа ÑоÑтавлÑет около 0.06 Ñекунды ÑоглаÑно профайлеру. Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð²Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾, оптимизированного запроÑа ÑоÑтавлÑет порÑдка 0.04 Ñекунды. Как видно из результата, наибольший выигрыш проиÑходит при отправке меньшего количеÑтва данных на Ñортировку - отправка отрабатывает почти на треть быÑтрее. Одна из введенных нами оптимизаций позволила не отправлÑть также значительную чаÑть данных, в отличие от первого запроÑа, Ð±Ð»Ð°Ð³Ð¾Ð´Ð°Ñ€Ñ Ñ‡ÐµÐ¼Ñƒ мы также получили выигрыш в производительноÑти. Однако, Ñреднее Ð²Ñ€ÐµÐ¼Ñ ``уборки'' поÑле запроÑа незначительно выроÑло, как Ñледует из нижней чаÑти результата - дейÑтвий поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ€Ð¼Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ñ‹Ð±Ð¾Ñ€ÐºÐ¸.
1366 При Ñравнении результатов запроÑов в SQLite и MySQL ÑÐ¸Ñ‚ÑƒÐ°Ñ†Ð¸Ñ Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñ‡Ð½Ð°Ñ Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾Ñтью до затрат по подключению непоÑредÑтвенно к базам данных (из утилиты оÑнованной на ADO.Net). Ðто объÑÑнÑетÑÑ Ð½Ð¸Ð²ÐµÐ»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸ÐµÐ¼ преимущеÑтв одной СУБД над другой за Ñчет оптимизации общей их чаÑти - механики Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾ большей чаÑти идентична. Результат закономерен.
1367
1368 Выполним ``проверку ÑтатуÑов'' SELECT запроÑом \verb|SESSION STATUS LIKE `Select\%'|:
1369
1370\begin{table}[h]
1371 \caption {test2selects}
1372 \begin{tabular}{|c|c|c|}
1373 \hline
1374 Тип призведенного дейÑÑ‚Ð²Ð¸Ñ & ИÑходный Ð·Ð°Ð¿Ñ€Ð¾Ñ & Ðовый Ð·Ð°Ð¿Ñ€Ð¾Ñ \\ \hline
1375 Select\_full\_join & 1 & 0 \\
1376 Select\_range\_check & 0 & 0 \\
1377 Select\_range & 0 & 0 \\
1378 Select\_scan & 6 & 2 \\ \hline
1379 \end{tabular}
1380\end{table}
1381
1382 Ðти результаты мы могли наблюдать через EXPLAIN. Общее количеÑтво Ñканирований ÑократилоÑÑŒ.
1383
1384 Выполним проверку ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ñ‹Ñ… таблиц \verb|SESSION STATUS LIKE `Created_tmp\%'|:
1385
1386\begin{table}[h]
1387 \caption {test3temporaries}
1388 \begin{tabular}{|c|c|c|}
1389 \hline
1390 Тип произведенного дейÑÑ‚Ð²Ð¸Ñ & ИÑходный Ð·Ð°Ð¿Ñ€Ð¾Ñ & Ðовый Ð·Ð°Ð¿Ñ€Ð¾Ñ \\ \hline
1391 Created\_tmp\_files & 4 & 4 \\
1392 Created\_tmp\_tables & 12 & 8 \\ \hline
1393 \end{tabular}
1394\end{table}
1395
1396 Из результата Ñтой выборки также видно уменьшение количеÑтва временно Ñозданных таблиц Ñ 12 до 8.
1397
1398 {
1399 \subsection{Оценка оптимизации}
1400 }
1401
1402 ОптимизациÑ, Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð½Ð°Ñ Ð½Ð°Ð´ базой не включала в ÑÐµÐ±Ñ Ð¼Ð°Ñштабные изменениÑ. Структура базы практичеÑки не затронута. Ð’Ñе изначально ÑущеÑтвовавшие таблицы Ñохранены, предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñохранены. Триггеры адаптированы и Ñохранены. Рефакторинг Ñтруктуры базы не был произведен ввиду необходимоÑти Ñохранить ее функционирующее ÑоÑтоÑние.
1403
1404 Изначально ÑÐ¿Ñ€Ð¾ÐµÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных доÑтаточно нормализована, не Ñодержит избыточных данных, многозначных и многоцелевых Ñтолбцов. Ðа данный момент в базе данных приÑутÑтвует Ñравнительно небольшой объем данных. ОÑновываÑÑÑŒ на вÑем Ñтом можно Ñделать вывод, что имеющаÑÑÑ Ð±Ð°Ð·Ð° данных в рефакторинге не нуждаетÑÑ, по крайней мере на данный момент.
1405
1406 Переход Ñ SQLite на MySQL ÑвлÑетÑÑ Ñпорным моментом, так как одним из плюÑов СУБД SQLite ÑвлÑетÑÑ ÐµÐµ проÑтота. БольшинÑтво преимущеÑтв MySQL над SQLite в ходе данной работы задейÑтвованы не были и в целом ÑвлÑÑŽÑ‚ÑÑ Ð¿Ñ€ÐµÐ¸Ð¼ÑƒÑ‰ÐµÑтвами в ÑпецифичеÑких уÑловиÑÑ….
1407
1408
1409 \clearpage
1410
1411 {
1412 \section{ЗÐКЛЮЧЕÐИЕ}
1413 }
1414
1415 Ð’ ходе работы над курÑовым проектом было изучено поведение MySQL при обработке SQL запроÑов. ПротеÑтирована механика многих оптимизаций, которые могут быть применены ко входной базе данных. БольшинÑтво Ñтих оптимизаций было оÑущеÑтвлено. Были оценены улучшившиеÑÑ Ð¿Ð¾ÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ð¸ работы, были произведены теÑты производительноÑти полученной ÑиÑтемы.\\
1416
1417 {
1418 \subsection{Возможные дальнейшие улучшениÑ}
1419 }
1420
1421 Ð”Ð°Ð½Ð½Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ не может быть названа полноÑтью законченной, так как абÑолютно любую ÑиÑтему можно так или иначе улучшить. Ð’Ð¾Ð¿Ñ€Ð¾Ñ ÑоÑтоит лишь в том, наÑколько необходимо ``улучшение'', и не приведет ли оно к противоположному результату в другом меÑте. Так, Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð¾Ð¹ базы данных в рамках работы шла ``Ñ Ð¾Ð³Ð»Ñдкой'' на выполнение конкретного моделируемого запроÑа. \\
1422
1423 При возникновении подобной необходимоÑти в будущем, работа может быть продолжена и доведена до необходимиого ÑоÑтоÑÐ½Ð¸Ñ Ñ ÑƒÑ‡ÐµÑ‚Ð¾Ð¼ будущих потрбноÑтей и возможноÑтей.
1424
1425
1426 \newpage
1427 <bibtexÑпиÑок литературы>
1428
1429 \end{document}