forked from le0pard/postgresql_book
-
Notifications
You must be signed in to change notification settings - Fork 0
/
postgresql_cache.tex
378 lines (297 loc) · 18.9 KB
/
postgresql_cache.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
\chapter{Кэширование в PostgreSQL}
\begin{epigraphs}
\qitem{Чтобы что-то узнать, нужно уже что-то знать.}{Станислав Лем}
\end{epigraphs}
\section{Введение}
Кэш или кеш~--- промежуточный буфер с быстрым доступом, содержащий информацию, которая может быть запрошена с наибольшей вероятностью.
Кэширование SELECT запросов позволяет повысить производительность приложений и снизить нагрузку на PostgreSQL.
Преимущества кэширования особенно заметны в случае с относительно маленькими таблицами, имеющими статические данные,
например, справочными таблицами.
Многие СУБД могут кэшировать SQL запросы, и данная возможность идет у них, в основном, <<из коробки>>.
PostgreSQL не обладает подобным функционалом. Почему?
Во-первых, мы теряем транзакционную чистоту происходящего в базе. Что это значит?
Управление конкурентным доступом с помощью многоверсионности (MVCC~--- MultiVersion Concurrency Control)~---
один из механизмов обеспечения одновременного конкурентного доступа к БД, заключающийся в предоставлении каждому
пользователю <<снимка>> БД, обладающего тем свойством, что вносимые данным пользователем изменения в БД невидимы
другим пользователям до момента фиксации транзакции. Этот способ управления позволяет добиться того, что пишущие
транзакции не блокируют читающих, и читающие транзакции не блокируют пишущих.
При использовании кэширования, которому нет дела к транзакциям СУБД, <<снимки>> БД могут быть с неверними данними.
Во-вторых, кеширование результатов запросов, в основном, должно происходить на стороне приложения, а не СУБД.
В таком случае управление кэшированием может работать более гибко (включать и отключать его где потребуется для приложения), а
СУБД будет заниматся своей непосредственной целью~--- хранение и предоставление целосности данных.
Но, несмотря на все эти минуси, многим разработчикам требуется кэширование на уровне базы данных.
Для организации кэширования существует два инструмента для PostgreSQL:
\begin{itemize}
\item Pgmemcache (с memcached)
\item Pgpool-II (query cache)
\end{itemize}
\section{Pgmemcache}
\label{sec:pgmemcache}
Memcached\footnote{http://memcached.org/}~---
компьютерная программа, реализующая сервис кэширования данных в оперативной памяти на основе
парадигмы распределенной хеш-таблицы. С помощью клиентской библиотеки позволяет кэшировать данные в оперативной
памяти одного или нескольких из множества доступных серверов. Распределение реализуется путем сегментирования
данных по значению хэша ключа по аналогии с сокетами хэш-таблицы. Клиентская библиотека используя ключ данных
вычисляет хэш и использует его для выбора соответствующего сервера. Ситуация сбоя сервера трактуется как промах
кэша, что позволяет повышать отказоустойчивость комплекса за счет наращивания количества memcached серверов и возможности
производить их горячую замену.
Pgmemcache\footnote{http://pgfoundry.org/projects/pgmemcache/}~--- это
PostgreSQL API библиотека на основе libmemcached для взаимодействия с memcached. С помощью данной библиотеки
PostgreSQL может записывать, считывать, искать и удалять данные из memcached. Попробуем, что из себя представляет данный тип кэширования.
\subsection{Установка}
Во время написания этой главы была доступна 2.0.4 версия
pgmemcache\footnote{http://pgfoundry.org/frs/download.php/2672/pgmemcache\_2.0.4.tar.bz2}.
Pgmemcache будет устанавливатся и настраиватся на PostgreSQL версии 8.4 (для версии 9.0 все аналогично),
операционная система~--- Ubuntu Server 10.10. Поскольку Pgmemcache идет как модуль,
то потребуется PostgreSQL с PGXS (если уже не установлен, поскольку в сборках для Linux присутствует PGXS).
Также потребуется memcached и libmemcached библиотека версии не ниже 0.38.
После скачивания и распаковки исходников, существует два варианта установки Pgmemcache:
\begin{itemize}
\item \textbf{Установка из исходников}
Для этого достаточно выполнить в консоли:
\begin{lstlisting}[label=lst:pgcache1,caption=Установка из исходников]
$ make
$ sudo make install
\end{lstlisting}
\item \textbf{Создание и установка deb пакета (для Debian, Ubuntu)}
Иногда, если у Вас на серверах Debian или Ubuntu, удобнее создать deb пакет нужной программы и
распостранать его через собственный репозиторий на все сервера с PostgreSQL:
\begin{lstlisting}[label=lst:pgcache2,caption=Создание и установка deb пакета]
$ sudo apt-get install libmemcached-dev postgresql-server-dev-8.4 libpq-dev devscripts yada flex bison
$ make deb84
# устанавливаем deb пакет
$ sudo dpkg -i ../postgresql-pgmemcache-8.4*.deb
\end{lstlisting}
Для версии 2.0.4 утилита yada выдавала ошибку при создании deb пакета со следующим текстом:
\begin{lstlisting}[label=lst:pgcache3,caption=Создание и установка deb пакета]
Cannot recognize source name in 'debian/changelog' at /usr/bin/yada line 145, <CHANGELOG> line 1.
make: *** [deb84] Ошибка 9
\end{lstlisting}
Для устранения этой ошибки потребуется удалить первую строчку текста в <<debian/changelog>> в каталоге, котором происходит сборка:
\begin{lstlisting}[label=lst:pgcache4,caption=Создание и установка deb пакета]
$PostgreSQL: pgmemcache/debian/changelog,v 1.2 2010/05/05 19:56:50 ormod Exp $ <---- удалить
pgmemcache (2.0.4) unstable; urgency=low
* v2.0.4
\end{lstlisting}
Устранив эту проблему, сборка deb пакета не должна составить никаких проблем.
\end{itemize}
\subsection{Настройка}
После успешной установки Pgmemcache потребуется добавит во все базы данных (на которых вы хотите использовать Pgmemcache)
функции для работы с этой библиотекой:
\begin{lstlisting}[label=lst:pgcache5,caption=Настройка]
% psql [mydbname] [pguser]
[mydbname]=# BEGIN;
[mydbname]=# \i /usr/local/postgresql/share/contrib/pgmemcache.sql
# для Debian: \i /usr/share/postgresql/8.4/contrib/pgmemcache.sql
[mydbname]=# COMMIT;
\end{lstlisting}
Теперь можно добавлять сервера memcached через memcache\_server\_add и работать с кэшем. Но есть одно но. Все сервера memcached придется
задавать при каждом новом подключении к PostgreSQL.
Это ограничение можно обойти, если настроить параметры в postgresql.conf файле:
\begin{itemize}
\item Добавить <<pgmemcache>> в shared\_preload\_libraries (автозагрузка библиотеки pgmemcache во время старта PostgreSQL)
\item Добавить <<pgmemcache>> в custom\_variable\_classes (устанавливаем переменную для pgmemcache)
\item Создаем <<pgmemcache.default\_servers>>, указав в формате <<host:port>> (port - опционально) через запятую. Например:
\begin{lstlisting}[label=lst:pgcache6,caption=Настройка default\_servers]
pgmemcache.default_servers = '127.0.0.1, 192.168.0.20:11211' # подключили два сервера memcached
\end{lstlisting}
\item Также можем настроить работу самой библиотеки pgmemcache через <<pgmemcache.default\_behavior>>.
Настройки соответствуют настрокам libmemcached. Например:
\begin{lstlisting}[label=lst:pgcache7,caption=Настройка pgmemcache]
pgmemcache.default_behavior='BINARY_PROTOCOL:1'
\end{lstlisting}
\end{itemize}
Теперь не требуется при подключении к PostgreSQL указывать сервера memcached.
\subsection{Проверка}
После успешной установки и настройки pgmemcache, становится доступен список команд для работы с memcached серверами:
\begin{table}[h]
\caption{Список команд pgmemcache}
\label{tabular:pgmemcache1}
\begin{tabular}{| >{\raggedright\scriptsize}p{7cm}| >{\scriptsize}p{7cm} |}
\hline
Команда & Описание\\
\hline
memcache\_server\_add('hostname:port'::TEXT)
memcache\_server\_add('hostname'::TEXT)
&
Добавляет memcached сервер в список доступных серверов. Если порт не указан, по умолчанию используется 11211.\\
\hline
memcache\_add(key::TEXT, value::TEXT, expire::TIMESTAMPTZ)
memcache\_add(key::TEXT, value::TEXT, expire::INTERVAL)
memcache\_add(key::TEXT, value::TEXT)
&
Добавляет ключ в кэш, если ключ не существует.\\
\hline
newval = memcache\_decr(key::TEXT, decrement::INT4)
newval = memcache\_decr(key::TEXT)
&
Если ключ существует и является целым числом, происходит уменьшение
его значения на указаное число (по умолчанию на единицу).
Возвращает целое число после уменьшения.\\
\hline
memcache\_delete(key::TEXT, hold\_timer::INTERVAL)
memcache\_delete(key::TEXT)
&
Удаляет указанный ключ. Если указать таймер, то ключ с таким же названием может
быть добавлен только после окончания таймера.\\
\hline
memcache\_flush\_all()
&
Очищает все данные на всех memcached серверах.\\
\hline
value = memcache\_get(key::TEXT)
&
Выбирает ключ из кэша. Возвращает NULL, если ключ не существует, иначе~--- текстовую строку.\\
\hline
memcache\_get\_multi(keys::TEXT[])
memcache\_get\_multi(keys::BYTEA[])
&
Получает массив ключей из кэша.
Возвращает список найденных записей в виде <<ключ=значение>>.\\
\hline
newval = memcache\_incr(key::TEXT, increment::INT4)
newval = memcache\_incr(key::TEXT)
&
Если ключ существует и является целым числом, происходит увеличение
его значения на указаное число (по умолчанию на единицу).
Возвращает целое число после увеличения.\\
\hline
memcache\_replace(key::TEXT, value::TEXT, expire::TIMESTAMPTZ)
memcache\_replace(key::TEXT, value::TEXT, expire::INTERVAL)
memcache\_replace(key::TEXT, value::TEXT)
&
Заменяет значение для существующего ключа.\\
\hline
memcache\_set(key::TEXT, value::TEXT, expire::TIMESTAMPTZ)
memcache\_set(key::TEXT, value::TEXT, expire::INTERVAL)
memcache\_set(key::TEXT, value::TEXT)
&
Создаем ключ со значение. Если такой ключ существует~--- заменяем в нем значение на указаное.\\
\hline
stats = memcache\_stats()
&
Возвращает статистику по всем серверам memcached.\\
\hline
\end{tabular}
\end{table}
Посмотрим работу в СУБД данных функций. Для начала узнаем информацию по memcached серверах:
\begin{lstlisting}[label=lst:pgcache7,caption=Проверка memcache\_stats]
pgmemcache=# SELECT memcache_stats();
memcache_stats
---------------------------
Server: 127.0.0.1 (11211)
pid: 1116
uptime: 70
time: 1289598098
version: 1.4.5
pointer_size: 32
rusage_user: 0.0
rusage_system: 0.24001
curr_items: 0
total_items: 0
bytes: 0
curr_connections: 5
total_connections: 7
connection_structures: 6
cmd_get: 0
cmd_set: 0
get_hits: 0
get_misses: 0
evictions: 0
bytes_read: 20
bytes_written: 782
limit_maxbytes: 67108864
threads: 4
(1 row)
\end{lstlisting}
Теперь сохраним данные в memcached и попробуем их забрать:
\begin{lstlisting}[label=lst:pgcache8,caption=Проверка]
pgmemcache=# SELECT memcache_add('some_key', 'test_value');
memcache_add
--------------
t
(1 row)
pgmemcache=# SELECT memcache_get('some_key');
memcache_get
--------------
test_value
(1 row)
\end{lstlisting}
Можно также проверить работу счетчиков в memcached (данный функционал может пригодится для создания последовательностей):
\begin{lstlisting}[label=lst:pgcache9,caption=Проверка]
pgmemcache=# SELECT memcache_add('some_seq', '10');
memcache_add
--------------
t
(1 row)
pgmemcache=# SELECT memcache_incr('some_seq');
memcache_incr
---------------
11
(1 row)
pgmemcache=# SELECT memcache_incr('some_seq');
memcache_incr
---------------
12
(1 row)
pgmemcache=# SELECT memcache_incr('some_seq', 10);
memcache_incr
---------------
22
(1 row)
pgmemcache=# SELECT memcache_decr('some_seq');
memcache_decr
---------------
21
(1 row)
pgmemcache=# SELECT memcache_decr('some_seq');
memcache_decr
---------------
20
(1 row)
pgmemcache=# SELECT memcache_decr('some_seq', 6);
memcache_decr
---------------
14
(1 row)
\end{lstlisting}
Для работы с pgmemcache лучше создать функции и, если требуется, активировать эти функции через триггеры.
Например, наше приложение кэширует зашифрованые пароли пользователей в memcached
(для более быстрого доступа),
и нам требуется обновлять информацию в кэше, если она изменяется в СУБД. Создаем функцию:
\begin{lstlisting}[label=lst:pgcache10,caption=Функция для обновления данных в кэше]
CREATE OR REPLACE FUNCTION auth_passwd_upd() RETURNS TRIGGER AS $$
BEGIN
IF OLD.passwd != NEW.passwd THEN
PERFORM memcache_set('user_id_' || NEW.user_id || '_password', NEW.passwd);
END IF;
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';
\end{lstlisting}
Активируем триггер для обновления таблицы пользователей:
\begin{lstlisting}[label=lst:pgcache11,caption=Триггер]
CREATE TRIGGER auth_passwd_upd_trg AFTER UPDATE ON passwd FOR EACH ROW EXECUTE PROCEDURE auth_passwd_upd();
\end{lstlisting}
Но(!!!) данный пример транзакционно не безопасен~--- при отмене транзации кэш не вернется на старое значение.
Поэтому лучше очищать старые данные:
\begin{lstlisting}[label=lst:pgcache12,caption=Очистка ключа в кэше]
CREATE OR REPLACE FUNCTION auth_passwd_upd() RETURNS TRIGGER AS $$
BEGIN
IF OLD.passwd != NEW.passwd THEN
PERFORM memcache_delete('user_id_' || NEW.user_id || '_password');
END IF;
RETURN NEW;
END;$$ LANGUAGE 'plpgsql';
\end{lstlisting}
Также нужен триггер на чистку кэша при удалении записи из СУБД:
\begin{lstlisting}[label=lst:pgcache11,caption=Триггер]
CREATE TRIGGER auth_passwd_del_trg AFTER DELETE ON passwd FOR EACH ROW EXECUTE PROCEDURE auth_passwd_upd();
\end{lstlisting}
Замечу от себя, что создавать кэш в memcached на кешированый пароль нового пользователя (или обновленного) лучше через приложение.
\subsection{Заключение}
PostgreSQL с помощью Pgmemcache библиотеки позволяет работать с memcached серверами,
что может потребоватся в определенных случаях для кэширования данных напрямую с СУБД.
Удобство данной библиотеки заключается в полном доступе к функциям memcached,
но вот готовой реализации кэширование SQL запросов тут нет,
и её придется дорабатывать вручную через функции и триггеры PostgreSQL.