traditional relational databases architecture
DESCRIPTION
Traditional relational databases architectureTRANSCRIPT
Group Technology and Operations Deutsche Bank
Traditional relational
databases architecture Dmitry Remizov
Dmitry Remizov
14 August 2014 Deutsche Bank
Я собираюсь рассказать некоторые базовые вещи об архитектуре
традиционных баз данных на примере 2х наиболее ярких
представителей систем этого класса, а именно - Oracle и Microsoft
SQL. Выбор обусловлен тем, что это, во-первых, лидирующие
системы в своих сегментах, во-вторых, они исповедуют весьма
различные базовые идеологии. В основном по историческим
причинам.
Начнем, однако, с того, зачем нужны базы данных вообще. Почему
бы не писать просто в плоские файлы. Действительно есть класс
задач, для которого это вполне релевантный подход (логи и т.п.).
Однако довольно часто нам надо писать что-то консистентно и из
многих потоков одновременно.
И вот этим-то и занимаются реляционные базы данных.
Agenda
Dmitry Remizov
14 August 2014 Deutsche Bank
Несмотря на все исторические различия, многие архитектурные
решения оказались весьма сходными. Хотя, конечно, есть и
существенные различия.
Начнем с того, что Oracle «версионник», а MSSQL «блокировочник».
Расшифруем немного этот жаргон:
•«Блокировочник» означает, что «писатель» может заблокировать
«читателей», 2 пишущие сессии должны логически лочить друг
друга, если имеют дело с одной и той же строкой (данными).
•«Версионник» вместо блокировки читателя возвращает клиенту
«предыдущую версию правды». На какой момент это отдельный
вопрос.
Два подхода
Dmitry Remizov
14 August 2014 Deutsche Bank
Из чего состоит реляционная база данных:
•Процессы OS
•Структуры в памяти
•Файлы на файловой системе
На следующем слайде мы увидим некоторую обзорную картинку
основных компонентов Oracle и MSQL
Архитектура
Dmitry Remizov
14 August 2014 Deutsche Bank
MSSQL
Buffer pool
TLogs
Memory structures and processes
Oracle
Buffer Cache
Online redo logs
MSSQL
Buffer Pool
TLogs
Dmitry Remizov
14 August 2014 Deutsche Bank
MSSQL взаимодействие базы данных с клиентом
Dmitry Remizov
14 August 2014 Deutsche Bank
Сравнение основных «фоновых» процессов
Dmitry Remizov
14 August 2014 Deutsche Bank
Oracle MSSQL
Buffer Cache Buffer Pool
Redo log buffer Log cache
Shared Pool Procedural cache
Сравнение основных областей памяти
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
ACID
В незапамятные времена была определена некая модель, которой
должны удовлетворять все реляционные базы данных. Это 4
базовых принципа и по начальным буквам эта модель называется
ACID. Вкратце:
Atomicity – транзакция не может быть завершена частично.
Consistency – база данных консистентна в начале и конце каждой
транзакции
Isolation – транзакция не видит изменения произведенные другой
незавершенной транзакцией
Durability – закоммиченная транзакция «переживает» крах системы
Все реляционные базы в меру своих возможностей пытаются
удовлетворить ACID принципам и соответственно для этого нужны
низкоуровневые механизмы.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Transaction logs
Рассмотрим один из важнейших механизмов обеспечения ACID (а
именно A & D частей) - подсистема transaction log’ов. В MSSQL это
одна подсистема – TLOG + LogWriter, которая обепечивает
возможность commit/rollback (Atomicity) и восстанавливаемость
системы (Durability).
В Oracle немного сложнее - 2 тесно связанных подсистемы REDO
and UNDO. REDO для Durability и UNDO для Atomicity (и еще для
обеспечения много-версионного чтения).
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
WAL
Несколько слов зачем вообще нужны логи транзакций.
Современные базы данных - это в основном большие (иногда
распределенные кэши) с конкурентным доступом, и все, что вы
делаете с данными в основном происходит в оперативной памяти
(есть исключения), однако нам надо обеспечить механизм
восстановления данных при крахе системы.
Для этого все изменения в системе пишутся в отдельный лог,
который при commit’е синхронно сохраняется на файловую систему.
И в Oracle и в MSSQL реализован механизм т.н. WAL ( Write ahead
logging), т.е. все данные, которые вы записываете, сначала пишутся
в лог (точнее в лог буфер, который постоянно скидывается на диск)
и только в момент commit’a мы окончательно синхронизируем лог
буфер и лог файлы (на момент коммита). И только после того, как
мы записали лог, мы реально изменяем данные (сначала в памяти,
а потом когда-нибудь на диске).
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
MSSQL – transaction log
SQL Server помечает каждую запись в лог файле используя Logical
Sequence Number (LSN). Это монотонно возрастающее число,
более свежие записи имеют выше LSN. Log records связанные с
данной транзакцией пролинкованы в LSN chain. Log records
связаны в обратную цепочку (backword chained) – каждый log record
сохраняет ссылку на предыдущий в той же транзакции. Это
позволяет SQLServer делать rollback применяя каждый log record в
обратном порядке.
Важная концепция «active log». Начало active log’a - это старейший
LSN из всех открытых транзакций. Конец active log’a - это
наивысший записанный LSN в системе.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Rollback and recovery в MSSQL
В случае краха системы MSSQL последовательно накатывает
transaction log и затем откатывает незакоммиченные транзакции.
В случае rollback - использует «Log records связаны в обратную
цепочку (backword chained)» и применяет все log records в обратном
порядке.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
В Oracle все немного сложнее
Любые изменения в блоке данных сразу же генерят инструкции по
их отмене, которые записываются в UNDO. И изменения в дата
блоках и в UNDO сегменте тут же записываются в REDO log.
На самом деле вначале записываются эти инструкции в REDO, а
затем изменения происходят в UNDO и дата блоке.
См. слайд.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Oracle: REDO and UNDO
1. Создаем «change vector» для UNDO record
2. Создаем «change vector» для Data block
3. Комбинируем эти 2 «change vector» в “redo record” и записываем в redo log (buffer).
4. Записываем «undo record» в UNDO block
5. Меняем data block.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Почему в Oracle 2 подсистемы, почему не только REDO
REDO нужен для обеспечения recoverability
Позволяет записывать только изменения в данных, избавляя от необходимости
синхронно записывать измененные дата блоки. Теоретически мы можем
прокрутить все изменения с момента создания «пустой» базы данных и получить
текущее состояние. Похожие подходы есть во многих (нереляционных в том
числе) базах данных, например KDB.
UNDO нужен для обеспечения согласованности по чтению и rollback’ов
Если бы мы пытались это сделать при помощи REDO, нам бы пришлось
иметь многосвязный REDO log. Сейчас это простой последовательный по времени
лог. Иначе возникла бы необходимость иметь связанность по:
• object_id – обьектам, чтобы обеспечить согласованное чтение;
• SCN – “номер транзакции” для обеспечения rollback.
В MSSQL, так как это изначально «блокировочник», согласованное чтение было не
очень актуально: читатели блокировались писателями, а rollback как раз
обеспечивается пролинковкой внутри TLOG’а (см. Log records связаны в обратную
цепочку (backward chained)).
А в новом режиме snapshot есть подобие UNDO, реализованное в tempDB для
обеспечения согласованного чтения.
16
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Типичный “REDO record”
REDO RECORD - Thread:1 RBA: 0x00036f.00000005.008c LEN: 0x00f8 VLD: 0x01
SCN: 0x0000.03ee485a SUBSCN: 1 03/13/2011 17:43:01
CHANGE #1 TYP:0 CLS:36 AFN:2 DBA:0x0080009a SCN:0x0000.03ee485a SEQ: 4 OP:5.1
…
CHANGE #2 TYP:0 CLS: 1 AFN:11 DBA:0x02c0018a SCN:0x0000.03ee485a SEQ: 2 OP:11.5
Видно что типичный REDO состоит из пар инструкций по
изменению UNDO и data block’a.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
UNDO record internal structure uba: 0x00c38375.04c4.2b ctl max scn: 0x0000.002b7cc5 prv tx scn: 0x0000.002b7cc6
txn start scn: scn: 0x0000.002b84d4 logon user: 47
prev brb: 12813167 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x03 ver: 0x01
compat bit: 4 (post-11) padding: 1
op: Z
KDO Op code: URP row dependencies Disabled
xtype: XA flags: 0x00000000 bdba: 0x01800bb3 hdba: 0x01800bb2
itli: 2 ispac: 0 maxfr: 4858
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12
ncol: 3 nnew: 1 size: 308
col 1: [311]
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31
+++++++++++ Next block not in extent map - rollback segment has been shrunk.
+ WARNING + Block dba (file#, block#): 0,0x00000000
Я про’update’ил одно поле
в таблице, видно что
записывается только
операция по изменению, а
не “before image”
update FFF set f1='ZZZZZ'
where rownum =1;
Первоначальное
содержимое поля f1:
11111…1111 (ASCII code
31).
Identifier Deutsche Bank Dmitry Remizov
14 August 2014 19
Некоторые коды операций (из презентации Julian Duke)
Row operations generate layer 11 redo
Opcodes include:
Opcode Mnemonic Description
11.2 IRP Insert Single Row
11.3 DRP Delete Single Row
11.4 LKR Lock Row
11.5 URP Update Row
11.6 ORP Chained Row
11.9 CKI Cluster key index
11.10 SKL Set cluster key pointers
11.11 QMI Insert Multiple Rows
11.12 QMD Delete Multiple Rows
Identifier Deutsche Bank Dmitry Remizov
14 August 2014 20
Multi Row Update
Redo Statements
COMMIT 5.4
REDO #3
UNDO #3
11.5
5.1
UNDO #2
REDO #2
5.1
11.5
REDO #1
UNDO #1
HEADER
11.5
5.1
5.2 -- T1 contains 3 rows
UPDATE t1 SET c2 = c2 + 1;
COMMIT;
Identifier Deutsche Bank Dmitry Remizov
14 August 2014 21
Global Temporary Tables
Redo Statements
COMMIT 5.4
REDO #3
UNDO #3
11.2
5.1
UNDO #2
REDO #2
5.1
11.2
UNDO #1
HEADER
REDO #1 11.2
5.1
5.2
COMMIT;
-- Statement #1
INSERT INTO t1 VALUES (1);
-- Statement #2
INSERT INTO t1 VALUES (2);
-- Statement #3
INSERT INTO t1 VALUES (3);
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Различия Oracle и SQL Server
Oracle:
2 подсистемы: REDO & UNDO
REDO – очень простая, служит для восстановления
после сбоев, i.e. durability.
UNDO – гораздо сложнее, обеспечивает rollback’и,
согласованное чтение, i.e. atomicity, isolation
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Как оптимизировать «recovery» или зачем нужен CheckPoint process
Как было сказано на предыдущем слайде, возможно получить текущее
состояние базы накатывая transaction логи (REDO логи) «с начала
времен», однако это не выглядит разумной стратегией.
И здесь вступает в действие другой процесс - checkpoint (называется
абсолютно одинаково и в Oracle и в MSSQL).
Однако осуществляет сheckpoint в основном DWR0-9 (database writer)
процесс, он записывает все грязные блоки в дата файлы.
Вспомогательный CKPT процесс обновляет заголовки дата файлов и
control file.
Задачей checkpoint процесса является «асинхронная» синхронизация
данных в памяти с данными в дата файлах на диске (в Oracle >10
разнообразных checkpoint процессов и каждый содержит свою цепочку
буферных блоков - checkpoint queue).
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Checkpoint
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Еще немного о checkpoint Изначально (до 8.0) существовали только так называемые «нормальные» контрольные
точки, задачей которых было записать «грязные блоки» при переключении online redo
log’ов, что ожидаемо приводило к всплеску активности DBWR в момент такого
переключения.
Поэтому было решено размазать эту активность по времени и ввести алгоритм
«инкрементальной контрольной» точки.
•По мере того, как блоки данных в буферном кэше претерпевают изменения и
становятся “грязными”, процесс DBWR формирует очередь (checkpoint queue) из
связанных в упорядоченный по возрастанию low RBA список этих блоков. Low RBA – это
redo-адрес первого изменения блока, сделавшего его “грязным”.
•В какой-то момент времени DBWR определяет, что один из параметров, управляющий
наступлением инкрементальной контрольной точки, превысил свое пороговое значение.
Начинается выполнение инкрементальной контрольной точки. DBWR
вычисляет target RBA – то значение, до которого он будет осуществлять запись блоков
из checkpoint queue. Этим значением не обязательно будет on disk RBA – то значение,
до которого DBWR сможет безопасно записывать “грязные” буфера из буферного кэша,
так как redo-информация, защищающая эти изменения, уже записана процессом LGWR
в журнальные файлы. DBWR выберет в качестве target RBAзначение, которое вернет
после выполнения инкрементальной контрольной точки значение параметра, ее
вызвавшее, в рамки допустимого. Названия(и суть) параметров довольно сильно
менялись от версии к версии.
Подробнее в замечательной статье Сергея Маркеленкова:
http://www.fors.ru/upload/magazine/03/http_texts/russia_chech_point.html
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
МИФ 1ый – Oracle, MSSQL, etc. “умный”
Поэтому может оптимизировать операцию типа:
update TSTLOG2 set …, f1=f1, f2=f2 WHERE rownum <100001;
Где «на всякий случай» перечислены все поля, меняли вы их или не
меняли. Простой экперимент покажет, что это неверно как с точки
зрения DBW, так и LGWR процессов.
С логической точки зрения это операция ровным счетом ничего не
делает (update поля само в себя):
SQL>
SQL> update TSTLOG2 set f1=f1 WHERE rownum
<100001;
100000 rows updated.
Elapsed: 00:00:28.22
SQL> commit;
Commit complete.
Elapsed: 00:00:00.55
SQL>
SQL> AlTER SYSTEM CHECKPOINT;
System altered.
Elapsed: 00:00:20.15
SQL>
Видно что UPDATE часть
выполняется довольно долго (и
генерит большое кол-во redo)
NAME VALUE
---------------------------------------------------------------- ----------
redo size 2894048836
CHECKPOINT тоже нашел большое
кол-во «грязных» блоков чтобы
сбросить их на диск.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Некоторые операции, которые также вызывают checkpoint или почему мой TRUNCATE длился полчаса. Некоторые операции для своего правильного выполнения должны
синхронизовать состояние buffer cache c datafiles на диске.
Это DROP/TRUNCATE/и direct path операции, проведем простой
экперимент
SQL> UPDATE tstlog set f1=f1;
100000 rows updated.
SQL> commit;
Commit complete.
SQL> select /*+ PARALLEL (t 4) */count(1) from TSTLOG t;
COUNT(1)
----------
100000
SQL> select event, time_waited/100 from v$session_event where sid =
(select sid from v$mystat where rownum=1)
2 order by time_waited desc;
EVENT TIME_WAITED/100
---------------------------------------------------------------- ---------------
SQL*Net message from client 159.6
enq: KO - fast object checkpoint 7.56
PX Deq: Execute Reply 2.85
resmgr:cpu quantum .02
PX Deq: Parse Reply 0
SQL*Net message to client 0
В 1ой сессии мы сгенерили
достаточно большое кол-во грязных
блоков.
Во 2ой провели параллельный
SELECT, к-рый приводит к direct path
read операции.
Видно что мы более 7 секунд ждали
выполнения контрольной точки (из 10
сек общего времени)
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Еще немного о truncate/drop
Воспроизведу рассуждение Таннела Подера.
• Предположим, у нас есть большой буфферный кэш, и мы drop’аем
таблицу A без инициации контрольной точки для «грязных»
буферов.
• Сразу же после этого мы создаем таблицу B, которая
переиспользует только что освобожденное место.
• Через несколько секунд просыпается DBWR, находит «грязные»
буфера и радостно перезаписывает часть содержимого таблицы B.
Конечно, можно было бы предложить чтобы DBRW проверял какие
блоки он скидывает, тот ли это обьект и т.п., но это не слишком
естественно для такого низкоуровнего кода, который работает с
блоками а не обьектами базы данных.
http://blog.tanelpoder.com/2011/07/06/what-is-the-purpose-of-segment-level-checkpoint-
before-droptruncate-of-a-table/
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Миф 2: база данных читает/обрабатывает данные построчно.
На самом деле Oracle и MSSQL обрабатывают данные по блокам:
В MSSQL - 8k блоки
В Oracle 2k,4k,8k.16k,32k блоки (стадартный тоже 8k)
Предположим, что мы решили ускорить наши SELECT’ы и прочитали в SQL
Bible или еще где-нибудь, что для этого надо сжать нашу таблицу. Звучит
разумно: SQL> CREATE TABLE FFF AS SELECT rownum+10000 id,
2 rpad(mod(rownum,3),311,mod(rownum,3)) f1,
3 rpad(mod(rownum,2),330,mod(rownum,2)) f2 from dual
4 connect by level<10001;
Table created.
SQL> CREATE TABLE CCC COMPRESS BASIC AS SELECT rownum+10000 id,
2 rpad(mod(rownum,3),311,mod(rownum,3)) f1,
3 rpad(mod(rownum,2),330,mod(rownum,2)) f2 from dual
4 connect by level<10001;
Table created.
SQL> begin
2 dbms_stats.gather_table_stats(USER,'CCC');
3 dbms_stats.gather_table_stats(USER,'FFF');
4 end;
5 /
SQL> select table_name, blocks from user_tab_statistics where table_name in ('FFF','CCC');
TABLE BLOCKS
----- ----------
CCC 23
FFF 934
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Результаты экперимента
Видим, что данные ужались в 40 раз, ожидаем «взлет» производительности.
Однако произведем замер и увидим, что разница довольно «скромная»
SQL> set arraysize 10
SQL> set autot traceonly
SQL> SELECT * FROM FFF;
10000 rows selected.
Statistics
----------------------------------------------------------
1823 consistent gets
0 physical reads
6698273 bytes sent via SQL*Net to client
11352 bytes received via SQL*Net from client
1001 SQL*Net roundtrips to/from client
10000 rows processed
SQL>
SQL> SELECT * FROM CCC;
10000 rows selected.
Statistics
----------------------------------------------------------
1019 consistent gets
0 physical reads
6638309 bytes sent via SQL*Net to client
11352 bytes received via SQL*Net from client
1001 SQL*Net roundtrips to/from client
10000 rows processed
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Анализ:
Вспоминаем, что база данных читает данные поблочно, и только клиент
запрашивает построчно. В JDBC интерфейсе это определяется параметром
defaultRowPrefetch по умолчанию 10, аналог в sqlplus – arraysize.
Попробуем увеличить arraysize/prefetch.
SQL> set arraysize 5000
SQL> set autot traceonly
SQL>
SQL> SELECT * FROM FFF;
10000 rows selected.
Statistics
----------------------------------------------------------
916 consistent gets
6610449 bytes sent via SQL*Net to client
374 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
10000 rows processed
SQL> SELECT * FROM CCC;
10000 rows selected.
Statistics
----------------------------------------------------------
23 consistent gets
6570445 bytes sent via SQL*Net to client
374 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
10000 rows processed
На этот раз разница в количестве
обработанных блоков
приблизительно соответствует
степени сжатия
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Внутренняя механика обеспечения row level locking and “versioning” в Oracle
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Детали read consistency/мульти-версионного чтения
Сессия, читая блок
обнаруживает, что
есть открытый ETL,
она проверяет статус
транзакции (читает
заголовок UNDO
сегмента и видит,
что транзакция
активна.
Это значит, сессия
должна откатить
изменения
сделанные активной
транзакцией до
момента первого
изменения. Она
создает копию блока
и применяет UNDO.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Детали блокировок.
Допустим сессия хочет
поменять строку
проапдейченную первой
транзакцией, эта сессия
видит open ITL, идет
проверяет статус
транзакции в Undo
segment, видит активную
транзакцию и встает в
ожидание на адресе:
usn#.slot#.wrap#
•номер сегмента
•номер слота
•порядковый номер
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Relation engine
MSSQL steps
• Query parsing
• Algebrizer – semantic analyses
• The query optimizer
Oracle steps
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Виды парсинга в Oracle
• hard parse – наиболее тяжелый, «дорогостоящий» вид парсинга,
нет никакого execution plan’a, и Oracle проходит через все стадии на
предыдущей картинке, включая оптимизацию/построение плана.
•soft parse – второй по стоимости, Oracle все еще должен провести
синтаксический и семантический разбор, провести поиск в Shared
Pool (Library Cache).
•soft-soft parse – наиболее загадочный, похоже работает только
внутри PLSQL (если есть несколько parse call внутри PLSQL то на
3ий раз, парсинг уже не происходит и инкрементится статистика
cursor authentications, зависит от session_cached_cursors)
•no parse – просто держит открытый курсор и выполняет его
несколько раз. (parse count (total) не увеличивается) Возможно в
JDBC либо явно, либо на уровне драйвера. Похоже не зависит от
“session_cached_cursors”.
См. http://docs.oracle.com/cd/E11882_01/java.112/e16548/stmtcach.htm
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
На сцену выходит Shared Pool
Это область памяти которая хранит
в том числе и execution plans
Nowadays, with realfree private memory
management, PGA, UGA and Callheaps are
completely separate top-level heaps (allocated
separately from OS using mmap) and UGA is
not physically inside PGA anymore.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Shared Pool management challenge
Управление shared pool существенно отличается от случая buffer
cache в сторону сложности.
Buffer cache - блоки стандартного размера.
Shared pool – блоки не могут быть одного размера (слишком
неээфективно), с другой стороны, находить блок нестандартного
размера нужно достаточно быстро, мы не можем долго искать блок
нужного размера, либо конструировать его на лету.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Shared pool – приблизительная схема
Free list организованы в цепочки блоков разного размера, начиная с 16
байт, затем 20, 24 и т.д.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Любите свой Shared pool, и он ответит вам взаимностью.
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Как можно помочь shared pool’у
PreparedStatement ps = con.prepareStatement("INSERT INTO FFF VALUES (?,?,?)");
ps.setInt(1, 1);
ps.setString(2, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
ps.setString(3, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
ps.execute();
ps.setInt(1, 1);
ps.setString(2, "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
ps.setString(3, "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
ps.execute();
ps.setInt(1, 1);
ps.setString(2, "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN");
ps.setString(3, "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN");
ps.execute();
ps.setInt(1, 1);
ps.setString(2, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
ps.setString(3, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
ps.execute();
ps.setInt(1, 1);
ps.setString(2, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
ps.setString(3, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
ps.execute();
Переиспользуйте открытый курсор, JDBC драйвер может это сделать за вас, но
процесс лучше держать под контролем
Dmitry Remizov
14 August 2014 Deutsche Bank
В начале
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 7
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
Dmitry Remizov
14 August 2014 Deutsche Bank
1st execution
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
Dmitry Remizov
14 August 2014 Deutsche Bank
2nd execution
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
5-ый execution
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
Dmitry Remizov
14 August 2014 Deutsche Bank
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 7
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
NAME VALUE
--------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
NAME VALUE
---------------------------------------------------------------- ----------
parse count (total) 8
parse count (hard) 0
parse count (failures) 0
parse count (describe) 0
Общий результат
Dmitry Remizov
14 August 2014 Deutsche Bank
Это довольно запутанный случай из нашей практики.
Однако вероятность столкнутся с ним довольно велика при
использовании Spring framework.
Два случая представленные ниже отличаются по времени
исполнения в 100 раз (обрабатывая точно тот же массив данных).
Так что эффект колоссальный.
Что бывает, когда не заботишься о Shared pool
Dmitry Remizov
14 August 2014 Deutsche Bank
List<Map<String, Object>> batchArgs = new LinkedList<Map<String, Object>>();
for (int i = 1; i < 1000; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("f1", getRandomNumber()); --- see we don’t define data type here explicitrely
map.put("f2", getRandomString());
……………………
map.put("f8", getRandomString());
batchArgs.add(map);
}
simpleJdbcTemplate.batchUpdate("INSERT /* BatchIssueSpring */ INTO YYYTEST
(f1,f2,f3,f4,f5,f6,f7,f8) VALUES (:f1,:f2,:f3,:f4,:f5,:f6,:f7,:f8)", batchArgs.toArray(new Map[0]));
System.out.println("time " + ((double) (System.currentTimeMillis() - startTime))/1000);
A tricky case – “untyped nulls”
time 37.36
Dmitry Remizov
14 August 2014 Deutsche Bank
List<MapSqlParameterSource> batchArgs = new LinkedList<MapSqlParameterSource>();
for (int i = 1; i < 1000; i++) {
MapSqlParameterSource source = new MapSqlParameterSource();
source.addValue("f1", getRandomNumber(), Types.NUMERIC);
source.addValue("f2", getRandomString(), Types.VARCHAR);
………………………………………….
source.addValue("f8", getRandomString(), Types.VARCHAR);
batchArgs.add(source);
}
simpleJdbcTemplate.batchUpdate("INSERT INTO YYYTEST
(f1,f2,f3,f4,f5,f6,f7,f8) VALUES (:f1,:f2,:f3,:f4,:f5,:f6,:f7,:f8);
batchArgs.toArray(new SqlParameterSource[0]));
System.out.println("time " + ((double) (System.currentTimeMillis() - startTime))/1000);
A tricky case 2 – let’s define “nulls” types
time 0.39
Moralite:
When something is provided for “free” try to find out the cost
Dmitry Remizov
14 August 2014 Deutsche Bank
private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, String typeName)
throws SQLException {
if (sqlType == SqlTypeValue.TYPE_UNKNOWN) {
boolean useSetObject = false;
sqlType = Types.NULL;
try {
DatabaseMetaData dbmd = ps.getConnection().getMetaData();
String databaseProductName = dbmd.getDatabaseProductName();
String jdbcDriverName = dbmd.getDriverName();
if (databaseProductName.startsWith("Informix") ||
jdbcDriverName.startsWith("Microsoft SQL Server")) {
useSetObject = true;
}
else if (databaseProductName.startsWith("DB2") ||
jdbcDriverName.startsWith("jConnect") ||
jdbcDriverName.startsWith("SQLServer")||
jdbcDriverName.startsWith("Apache Derby")) {
sqlType = Types.VARCHAR;
}
}
catch (Throwable ex) {
logger.debug("Could not check database or driver name", ex);
}
if (useSetObject) {
ps.setObject(paramIndex, null);
}
else {
ps.setNull(paramIndex, sqlType);
}
A root cause of the issue on Java(Spring) side:
Dmitry Remizov
14 August 2014 Deutsche Bank
select sql_id, child_number, bind_mismatch, c.BIND_LENGTH_UPGRADEABLE from
v$sql_shared_cursor c where sql_id ='0ajsk0mu1sakr'
And from the database side
SQL_ID CHILD_NUMBER BIND_MISMATCH BIND_LENGTH_UPGRADEABLE
0ajsk0mu1sakr 0 N N
0ajsk0mu1sakr 1 Y N
0ajsk0mu1sakr 2 N Y
0ajsk0mu1sakr 3 Y N
0ajsk0mu1sakr 4 Y N
0ajsk0mu1sakr 5 N Y
0ajsk0mu1sakr 6 Y Y
0ajsk0mu1sakr 7 Y N
0ajsk0mu1sakr 8 N Y
0ajsk0mu1sakr 9 Y Y
0ajsk0mu1sakr 10 N Y
0ajsk0mu1sakr 11 Y Y
0ajsk0mu1sakr 12 Y Y
0ajsk0mu1sakr 13 Y N
.......................................................................................................
Incredible amount of child cursors: >300 (after 3-4 executions)
It could lead to:
• extreme slowness
• the session death
Dmitry Remizov
14 August 2014 Deutsche Bank
Let’s compare ROWS/EXECUTIONS and other metrics:
SELECT y.sql_id,
y.rows_processed,
ROUND (y.rows_processed / NULLIF (y.elapsed_time, 0) * 1000000, 2) AS rows_per_sec,
ROUND (y.rows_processed / NULLIF (y.executions, 0), 2) AS rows_per_exec,
ROUND (y.elapsed_time / 1000000 / NULLIF (y.executions, 0), 2) AS sec_per_exec
FROM v$sql y
WHERE y.sql_id in ('0ajsk0mu1sakr','5twjz0zzm07z0')
ORDER BY y.sql_id desc;
Some SQL metrics for both cases
SQL_ID ROWS_PROCESSED ROWS_PER_SEC ROWS_PER_EXEC SEC_PER_EXEC
-- explicetely defined data type
5twjz0zzm07z0 999 16903.55 999 0.06
-- undefined data types
0ajsk0mu1sakr 54 1026.87 1.04 0
0ajsk0mu1sakr 14 1052.32 1.27 0
0ajsk0mu1sakr 14 1462.45 2 0
0ajsk0mu1sakr 12 859.05 1 0
0ajsk0mu1sakr 21 1513.08 1.91 0
0ajsk0mu1sakr 7 769.74 1.4 0
0ajsk0mu1sakr 19 1498.66 1.9 0
0ajsk0mu1sakr 515 3285.17 2.75 0
………………………………………………………………………………………………………………………………………………………
Dmitry Remizov
14 August 2014 Deutsche Bank
Jonathan Lewis: Oracle Core Essentials for DBAs and Developers.
Delaney Kalin: Inside Microsoft SQL Server 2005 The Storage Engine
http://blog.tanelpoder.com/
http://technet.microsoft.com/en-us/library/cc280362(v=sql.105).aspx
http://jonathanlewis.wordpress.com/
Oracle 9i OpSem materials
Литература
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Критика традиционных RDBMS
•Latching
•Locking
•Buffer pool
•Recovery
Сьедают до
95% времени
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Traditional Overheads
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Traditional Overheads 2
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Traditional Overheads 3
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Traditional Overheads 4
Identifier Deutsche Bank Dmitry Remizov
14 August 2014
Критика критики
На самом деле 95% преувеличение, но процентов 70-80% есть.
В отдельных случаях вы можете отказатся от части сервисов
RDBMS, например, NOLOGGING операции в data warehousing.
Использование субсекционирования, чтобы уменьшить
конкуренцию почти до 0, каждый поток льет в свой отдельный
сегмент и т.д.