Эта статья предназначена для технических специалистов, работающих с Oracle: администраторов баз данных, инженеров производительности, разработчиков инструментов мониторинга и исследователей внутренних механизмов СУБД. Это готовый пример низкоуровневого исследования, который можно адаптировать под собственные задачи. Мы покажем, как связать привычные SQL‑данные из v$sesstat с их реальным расположением в памяти процесса, и на конкретном примере проиллюстрируем методику, применимую и к другим внутренним структурам Oracle. Это первая статья из серии, она посвящена Oracle 11g, довольно древней версии Oracle, далее мы увидим как изменялся подход корпорации Oracle к хранению внутренних структур статистики.
Итак, начинаем: устанавливаем клиентскую сессию в Oracle. Фиксируем её ключевые идентификаторы: sid равен 69, ospid — 2945.
Теперь нужно определить адрес этой сессии в памяти. Для этого выполняем запрос к системному представлению v$session
SELECT saddr FROM v$session WHERE sid = 69;
В результате получаем адрес структуры сессии: 0000000188936D38. Именно по этому адресу в памяти процесса хранятся внутренние данные о сессии — и дальше мы будем использовать его, чтобы заглянуть «под капот» и увидеть статистику напрямую.
Сначала посмотрим на статистику моей сессии "традиционным" способом, с помощью запроса: SELECT value FROM v$sesstat WHERE sid=69 ORDER BY statistic#;
0 |
1 |
1 |
333 |
3 |
0 |
0 |
37 |
5560 |
15 |
Давайте отформатируем результат в 2 колонки (зачем - будет понятно несколькими секундами позже):
0 |
1 |
1 |
333 |
3 |
0 |
0 |
37 |
5560 |
15 |
Далее "гвоздь программы"! Переходим от работы с SQL‑представлениями к низкоуровневой диагностике — и заглядываем прямо в память процесса Oracle.
Мы подключаемся к процессу через GDB.
На сервере базы данных пользователем root, выполняем командуgdb attach 2945
Команда gdb attach 2945 означает, что отладчик GDB присоединяется к операционной системе процессу с PID 2945 — это и есть OS‑процесс, который обслуживает сессию Oracle с sid = 69. Так у исследователя появляется доступ к адресному пространству этого процесса.
2. Чтение памяти по адресу со смещением.
В GDB выполняется команда:
(gdb) x/100g 0x0000000188936D38 + 0x140
-
Eё части означают:
x(examine) — команда GDB для просмотра содержимого памяти;/100g— прочитать 100 элементов, где каждый элемент — 8 байт (giant, то есть 64‑битное значение);0x0000000188936D38— адрес структуры сессии (saddr), полученный ранее изv$session;+0x140— смещение в байтах от этого адреса: автор предполагает (и затем подтверждает), что именно по этому смещению в SGA лежат статистики сессии.
Результат выполнения данной команды:
0x188936e78: 0 10x188936e88: 1 3330x188936e98: 3 00x188936ea8: 0 370x188936eb8: 5560 15
Вы видите те же самые числа, что и в 2х колоночной табличке выше?
Это означает, что статистика сессии располагается в SGA неподалёку от адреса сессии — со смещением 0x140. Структура данных, судя по всему, представляет собой простой массив 8‑байтных значений, в котором статистики упорядочены по номеру “statistic#”.
Практическое применение.
Для чего это может быть нужно и каким образом это знание можно использовать?
Здесь на помощь приходит следующая низко-уровневая техника.
Предположим, мы желаем понять, какая внутренняя функция Oracle ответственна за изменение статистики “session cursor cache hits”. Данная статистика имеет номер 498 (statistic#).
Как известно, в gdb есть возможность установить наблюдение за областью памяти:
awatch *(0x00000001889EDAC8+0x140+8*498)
awatch— тип точки наблюдения: срабатывает не только при записи, но и при чтении или записи в адрес (access watchpoint). Это полезно, если нужно поймать любой доступ к ячейке (например, обновление счётчика или его чтение для отдачи в представление).*— разыменование: мы следим не за самим адресом, а за значением, которое лежит по этому адресу.0x00000001889EDAC8— базовый адрес (адрес структуры сессии).+0x140— знакомое смещение: именно там, как выяснили ранее, начинается блок статистики сессии в SGA.8*498— номер статистики 498 помноженный на размер отдельного значения (8 byte).
После 3-х кратного выполнения одного и того же запроса в моей SQL-сессии сработала поставленная ранее точка наблюдения (watchpoint) — процессор дошёл до определённого места в коде Oracle, и GDB остановил выполнение.
Останов произошёл в функции kkspsc0+372, т.е. в функции kkspsc0 по смещению 372
==> 0x00000000083f7e4a : mov QWORD PTR [rdi+r8*8+0x140],rsi
Что именно произошло
Сработал watchpoint. Тот самый
awatch, который вы поставили на ячейку памяти для статистики № 498, "поймалось" обращение к этой ячейке (чтение или запись).Указан адрес в коде:
kkspsc0+372.kkspsc0— это имя внутренней функции ядра Oracle (префиксkksобычно относится к компоненту Kernel SQL Compile/Execute).+372— смещение внутри этой функции (в байтах от начала), то есть конкретная инструкция, которая обратилась к отслеживаемой ячейке.Связь с вашими действиями. Три запуска SQL‑запроса привели к тому, что ядро Oracle в какой‑то момент обновило (или прочитало) нужную статистику — и именно в этот момент GDB прервал процесс.
Что это даёт для диагностики
Прямая привязка действия к изменению метрики. Вы видите: «три раза выполнил запрос → в функции
kkspsc0на смещении+372изменилась конкретная статистика». Это однозначное доказательство механизма учёта.Понимание, какая часть ядра отвечает за статистику. Теперь можно исследовать функцию
kkspsc0: что она делает, в каких случаях вызывается, какие операции считаются.Возможность собрать стек вызовов. В GDB после остановки можно выполнить
bt(backtrace) и увидеть полный стек: от SQL‑уровня до низкоуровневых функций. Это помогает понять цепочку событий, которая приводит к обновлению счётчика.
Ключевые выводы
Статистика сессии Oracle 11g физически располагается в SGA, со смещением
0x140от базового адреса сессии (saddr), и организована как массив 8‑байтных значений, упорядоченных поSTATISTIC#.Стандартные представления вроде
v$sesstat— это интерфейс над этой областью памяти: их данные можно не только читать через SQL, но и напрямую сопоставлять с содержимым памяти процесса.С помощью GDB можно ставить watchpoint на отдельные счётчики и ловить моменты их изменения, получая точную привязку к коду ядра (например, к функции
kkspsc0).
drema201 Автор
Поправил PGA -> SGA (спасибо коллегам из RUOUG за замечание)
Верифицировать можно через :
кусочек из скрипта Tanel Poder.