informatyka mysql mechanizmy wewnetrzne bazy danych sasha pachev ebook

background image

Wydawnictwo Helion
ul. Koœciuszki 1c
44-100 Gliwice
tel. 032 230 98 63

e-mail: helion@helion.pl

MySQL. Mechanizmy
wewnêtrzne bazy danych

Autor: Sasha Pachev
T³umaczenie: Grzegorz Werner
ISBN: 978-83-246-1232-1
Tytu³ orygina³u:

Understanding MySQL Internals

Format: B5, stron: 240

Poznaj sekrety jednej z najpopularniejszych baz danych

•

Jak przechowywane s¹ dane?

•

Jak dodawaæ w³asne zmienne konfiguracyjne?

•

Jak przebiega proces replikacji?

MySQL to obecnie jedna z najpopularniejszych baz danych. Jedn¹ z jej najwiêkszych
zalet jest nieodp³atny dostêp zarówno do samego systemu, jak i do jego kodu
Ÿród³owego. Mo¿liwoœæ przegl¹dania kodu i – w razie potrzeby – samodzielnego
modyfikowania go mo¿e okazaæ siê przydatna programistom tworz¹cym aplikacje, które
korzystaj¹ z MySQL jako zaplecza bazodanowego. Jednak samodzielne

„

przegryzanie

siê

”

przez setki tysiêcy linii kodu i rozpracowywanie mechanizmów dzia³ania bazy

danych mo¿e zaj¹æ mnóstwo czasu.

Dziêki tej ksi¹¿ce poznasz kod Ÿród³owy i sposób dzia³ania tego narzêdzia. Autor, przez
wiele lat pracuj¹cy w zespole tworz¹cym MySQL, przedstawia w niej tajniki systemu.
Podczas czytania poznasz architekturê i wzajemne powi¹zania pomiêdzy komponentami
MySQL, strukturê kodu Ÿród³owego oraz metody modyfikowania go przez kompilacj¹.
Dowiesz siê tak¿e, jak przebiega komunikacja pomiêdzy klientem i serwerem bazy
danych, jak realizowane s¹ zapytania, w jaki sposób sk³adowane s¹ dane i jak
implementowane s¹ mechanizmy replikacji.

•

Architektura MySQL

•

Struktura kodu Ÿród³owego

•

Komunikacja pomiêdzy klientem i serwerem

•

Zmienne konfiguracyjne

•

Obs³uga ¿¹dañ

•

Parser i optymalizator zapytañ

•

Mechanizmy sk³adowania danych

•

Replikacja danych

Dziêki tej ksi¹¿ce zrozumiesz budowê bazy danych MySQL i bêdziesz w stanie

samodzielnie dostosowaæ j¹ do ka¿dego zadania

background image

Spis tre

ļci

_

5

Spis tre

ļci

Przedmowa ...............................................................................................................................9

1. Historia i architektura MySQL ..................................................................................... 15

Historia MySQL

15

Architektura MySQL

17

2. Praca z kodem

Śródĥowym MySQL ............................................................................. 31

Powäoka Uniksa

31

BitKeeper

31

Przygotowywanie systemu do budowania MySQL z drzewa BitKeepera

34

Budowanie MySQL z drzewa BitKeepera

35

Budowanie z dystrybucji Ēródäowej

37

Instalowanie MySQL w katalogu systemowym

38

Ukäad katalogów z kodem Ēródäowym

38

Przygotowywanie systemu do uruchomienia MySQL w debugerze

40

Wycieczka po kodzie Ēródäowym w towarzystwie debugera

40

Podstawy pracy z gdb

41

Wyszukiwanie definicji w kodzie Ēródäowym

44

Interesujñce punkty wstrzymania i zmienne

45

Modyfikowanie kodu Ēródäowego

45

Wskazówki dla koderów

47

Aktualizowanie repozytorium BitKeepera

50

Zgäaszanie poprawki

51

3. Podstawowe klasy, struktury, zmienne i interfejsy API ............................................53

THD

53

NET

58

TABLE

58

Field

58

background image

6

_

Spis tre

ļci

Narzödziowe wywoäania API

65

Makra preprocesora

68

Zmienne globalne

70

4. Komunikacja mi

ýdzy klientem a serwerem ...............................................................73

Przeglñd protokoäu

73

Format pakietu

73

Relacje miödzy protokoäem MySQL a warstwñ systemu operacyjnego

74

Uzgadnianie poäñczenia

75

Pakiet polecenia

80

Odpowiedzi serwera

83

5. Zmienne konfiguracyjne .............................................................................................89

Zmienne konfiguracyjne: samouczek

89

Interesujñce aspekty konkretnych zmiennych konfiguracyjnych

96

6. W

étkowa obsĥuga żédaħ ...........................................................................................115

Wñtki kontra procesy

115

Implementacja obsäugi Ĕñdaþ

117

Problemy programowania wñtkowego

121

7. Interfejs mechanizmów sk

ĥadowania .......................................................................127

Klasa handler

127

Dodawanie wäasnego mechanizmu skäadowania do MySQL

142

8. Dost

ýp wspóĥbieżny i blokowanie ............................................................................ 163

MenedĔer blokad tabel

164

9. Parser i optymalizator ............................................................................................... 169

Parser

169

Optymalizator

172

10. Mechanizmy sk

ĥadowania ........................................................................................ 195

Wspólne cechy architektury

196

MyISAM

196

InnoDB

202

Memory (Heap)

204

MyISAM Merge

205

NDB

205

Archive

206

Federated

207

background image

Spis tre

ļci

_

7

11. Transakcje ..................................................................................................................209

Implementowanie transakcyjnego mechanizmu skäadowania

209

Implementowanie podklasy handler

210

Definiowanie handlertona

212

Praca z pamiöciñ podröcznñ zapytaþ

214

Praca z binarnym dziennikiem replikacji

214

Unikanie zakleszczeþ

215

12. Replikacja ....................................................................................................................217

Przeglñd

217

Replikacja oparta na instrukcjach i na wierszach

218

Dwuwñtkowy serwer podrzödny

219

Konfiguracja z wieloma serwerami nadrzödnymi

219

Polecenia SQL uäatwiajñce zrozumienie replikacji

220

Format dziennika binarnego

223

Tworzenie wäasnego narzödzia do replikacji

227

Skorowidz .............................................................................................................................229

background image

115

ROZDZIA

Ĥ 6.

W

étkowa obsĥuga żédaħ

Podczas pisania kodu serwera programista staje przed dylematem: czy obsäugiwaè Ĕñdania
za pomocñ wñtków, czy procesów? Oba podejĈcia majñ swoje zalety i wady. Od samego poczñtku
MySQL korzystaä z wñtków. W tym rozdziale uzasadnimy wñtkowñ obsäugö Ĕñdaþ w serwerze
MySQL, a takĔe omówimy jej wady i zalety oraz implementacjö.

W

étki kontra procesy

Byè moĔe najwaĔniejszñ róĔnicñ miödzy procesem a wñtkiem jest to, Ĕe wñtek potomny wspóä-
dzieli stertö (globalne dane programu) z wñtkiem macierzystym, a proces potomny — nie.
Ma to pewne konsekwencje, które trzeba uwzglödniè podczas wybierania jednego albo drugiego
modelu.

Zalety w

étków

Wñtki sñ implementowane w bibliotekach programistycznych i systemach operacyjnych z nastö-
pujñcych powodów:

x

Zmniejszone wykorzystanie pamiöci. Koszty pamiöciowe zwiñzane z tworzeniem nowego
wñtku sñ ograniczone do stosu oraz do pamiöci ewidencyjnej uĔywanej przez menedĔer
wñtków.

x

Dostöp do globalnych danych serwera bez uĔycia zaawansowanych technik. JeĈli dane mogñ
zostaè zmodyfikowane przez inny dziaäajñcy równolegle wñtek, wystarczy chroniè odpo-
wiedniñ sekcjö za pomocñ blokady ze wzajemnym wykluczaniem, zwanej muteksem (opi-
sywanej w dalszej czöĈci rozdziaäu). JeĈli nie ma takiej moĔliwoĈci, dostöp do globalnych
danych moĔna uzyskiwaè w taki sposób, jakby nie byäo Ĕadnych wñtków.

x

Tworzenie wñtku zajmuje znacznie mniej czasu niĔ tworzenie procesu, poniewaĔ nie trzeba
kopiowaè segmentu sterty, który moĔe byè bardzo duĔy.

x

Program szeregujñcy w jñdrze szybciej przeäñcza konteksty miödzy wñtkami niĔ miödzy
procesami. Dziöki temu w mocno obciñĔonym serwerze procesor ma wiöcej czasu na wyko-
nywanie rzeczywistej pracy.

background image

116

_

Rozdzia

ĥ 6. Wétkowa obsĥuga żédaħ

Wady w

étków

Wñtki odgrywajñ waĔnñ rolö we wspóäczesnych systemach komputerowych, ale majñ równieĔ
pewne wady:

x

Pomyäki programistyczne sñ bardzo kosztowne. Awaria jednego wñtku powoduje zaäama-
nie caäego serwera. Jeden wyrodny wñtek moĔe uszkodziè globalne i zakäóciè dziaäanie
innych wñtków.

x

ãatwo popeäniè pomyäkö. Programista musi stale myĈleè o problemach, jakie moĔe spowo-
dowaè jakiĈ inny wñtek, oraz o tym, jak ich uniknñè. Niezbödna jest bardzo defensywna
postawa.

x

Wielowñtkowe serwery sñ znane z usterek synchronizacyjnych, które sñ trudne do odtwo-
rzenia podczas testów, ale ujawniajñ siö w bardzo zäym momencie w Ĉrodowiskach produk-
cyjnych. Wysokie prawdopodobieþstwo wystöpowania takich usterek jest nastöpstwem
wspóädzielenia przestrzeni adresowej, co znacznie zwiöksza stopieþ interakcji miödzy
wñtkami.

x

W pewnych okolicznoĈciach rywalizacja o blokady moĔe wymknñè siö spod kontroli. JeĈli
zbyt wiele wñtków próbuje jednoczeĈnie pozyskaè ten sam muteks, moĔe to doprowadziè
do nadmiernego przeäñczania kontekstów: procesor przez wiökszoĈè czasu zamiast uĔytecz-
nej pracy wykonuje program szeregujñcy.

x

W systemach 32-bitowych przestrzeþ adresowa procesu jest ograniczona do 4 GB. Ponie-
waĔ wszystkie wñtki wspóädzielñ tö samñ przestrzeþ adresowñ, teoretycznie caäy serwer
ma do dyspozycji 4 GB pamiöci RAM, nawet jeĈli w komputerze zainstalowano znacznie
wiöcej fizycznej pamiöci. W praktyce przestrzeþ adresowa robi siö bardzo zatäoczona przy
znacznie mniejszym limicie, gdzieĈ okoäo 1,5 GB w Linuksie x86.

x

Zatäoczona 32-bitowa przestrzeþ adresowa stwarza jeszcze jeden problem: kaĔdy wñtek
potrzebuje trochö miejsca na stos. Kiedy stos zostaje przydzielony, to nawet jeĈli wñtek ko-
rzysta z niego w minimalnym stopniu, konieczne jest zarezerwowanie czöĈci przestrzeni
adresowej serwera. KaĔdy nowy wñtek ogranicza miejsce, które moĔna przeznaczyè na
stertö. JeĈli wiöc nawet w komputerze jest duĔo fizycznej pamiöci, moĔe siö okazaè, Ĕe nie da
siö jednoczeĈnie zaalokowaè duĔych buforów, uruchomiè wiele wspóäbieĔnych wñtków
oraz zapewniè kaĔdemu z nich duĔo miejsca na stos.

Zalety rozwidlonych procesów

Wady wñtków odpowiadajñ zaletom korzystania z oddzielnych procesów:

x

Pomyäki programistyczne nie sñ tak katastrofalne. Choè niekontrolowany proces moĔe
zakäóciè dziaäanie caäego serwera, jest to znacznie mniej prawdopodobne.

x

Trudniej popeäniè pomyäkö. Przez wiökszoĈè czasu programista moĔe myĈleè tylko o jed-
nym wñtku wykonania, nie martwiñc siö o potencjalnych intruzów.

x

Pojawia siö znacznie mniej fantomowych usterek. Kiedy wystñpi jakaĈ usterka, zwykle
moĔna äatwo jñ odtworzyè. KaĔdy rozwidlony proces ma wäasnñ przestrzeþ adresowñ, wiöc
stopieþ ich wzajemnej interakcji jest znacznie mniejszy.

x

W systemach 32-bitowych ryzyko wyczerpania przestrzeni adresowej jest duĔo mniejsze.

background image

Implementacja obs

ĥugi żédaħ

_

117

Wady rozwidlonych procesów

Aby podsumowaè nasz przeglñd, wymieniö wady rozwidlonych procesów, które sñ lustrzanym
odbiciem zalet wñtków:

x

Wykorzystanie pamiöci jest nieoptymalne. Podczas rozwidlania procesu potomnego kopio-
wane sñ duĔe segmenty pamiöci.

x

Wspóädzielenie danych miödzy procesami wymaga uĔycia specjalnych technik. Utrudnia
to dostöp do globalnych danych serwera.

x

Tworzenie procesu wiñĔe siö z wiökszymi kosztami na poziomie jñdra. KoniecznoĈè kopio-
wania segmentu danych procesu macierzystego znacznie obniĔa wydajnoĈè. Linux jednak
odrobinö tu oszukuje, stosujñc technikö zwanñ kopiowaniem przy zapisie. Rzeczywiste
kopiowanie strony procesu macierzystego zachodzi dopiero wtedy, gdy zostanie ona zmo-
dyfikowana przez proces macierzysty lub potomny. Do tego momentu oba procesy uĔywajñ
jednej strony.

x

Przeäñczanie kontekstów miödzy procesami jest bardziej czasochäonne, poniewaĔ jñdro musi
przeäñczyè strony, tabele deskryptorów plików oraz inne dodatkowe informacje kontekstowe.
Serwer ma mniej czasu na wykonywanie rzeczywistej pracy.

Ogólnie rzecz biorñc, serwer wñtkowy jest idealny wtedy, gdy programy obsäugi poäñczeþ muszñ
wspóädzieliè wiele danych, a programiĈcie nie brakuje umiejötnoĈci. Kiedy trzeba byäo wybraè
model odpowiedni dla MySQL, wybór byä prosty. Serwer baz danych musi mieè wiele wspóä-
uĔytkowanych buforów oraz innych wspóädzielonych danych.

JeĈli chodzi o umiejötnoĈci programistyczne, tych równieĔ nie brakowaäo. Podobnie jak dobry
jeĒdziec staje siö jednoĈciñ ze swoim koniem, tak Monty staä siö jednoĈciñ z komputerem.
Bolaäo go, kiedy widziaä marnotrawienie zasobów systemowych. Byä pewien, Ĕe potrafi napisaè
kod praktycznie pozbawiony usterek, poradziè sobie z problemami wspóäbieĔnoĈci powodo-
wanymi przez wñtki, a nawet pracowaè z maäym stosem. Co za ekscytujñce wyzwanie! Oczy-
wiĈcie, wybraä wñtki.

Implementacja obs

ĥugi żédaħ

Serwer oczekuje na poäñczenia w swoim gäównym wñtku. Po odebraniu poäñczenia przydziela
wñtek do jego obsäugi. W zaleĔnoĈci od konfiguracji i bieĔñcego stanu serwera, wñtek moĔe zostaè
utworzony od zera albo przydzielony z pamiöci podröcznej (puli) wñtków. Klient przesyäa
Ĕñdania, a serwer je realizuje, dopóki klient nie wyda polecenia koþczñcego sesjö (

COM_QUIT

)

albo sesja nie zostanie nieoczekiwanie przerwana. Po zakoþczeniu sesji, w zaleĔnoĈci od konfi-
guracji i bieĔñcego stanu serwera, wñtek moĔe zostaè zakoþczony albo zwrócony do puli wñt-
ków w oczekiwaniu na nastöpne poäñczenie.

Struktury, zmienne, klasy i interfejsy API

JeĈli chodzi o obsäugö wñtków, prawdopodobnie najwaĔniejsza jest klasa

THD

, która reprezen-

tuje deskryptor wñtku. Niemal kaĔda z funkcji parsera i optymalizatora przyjmuje obiekt

THD

jako argument, najczöĈciej pierwszy na liĈcie argumentów. Klasö

THD

opisano szczegóäowo

w rozdziale 3.

background image

118

_

Rozdzia

ĥ 6. Wétkowa obsĥuga żédaħ

Podczas tworzenia wñtku deskryptor jest umieszczany na globalnej liĈcie wñtków

I_List<THD>

threads

(

I_List<>

to szablonowa klasa poäñczonej listy; zob. sql/sql_list.h oraz sql/sql_list.c).

Listy tej uĔywa siö do trzech podstawowych celów:

x

dostarczanie danych na uĔytek polecenia

SHOW PROCESSLIST

;

x

lokalizowanie docelowego wñtku podczas wykonywania polecenia

KILL

;

x

sygnalizowanie wszystkim wñtkom, aby przerwaäy pracö, kiedy serwer jest zamykany.

WaĔnñ rolö odgrywa inna lista

I_List<THD>

:

thread_cache

. Jest ona uĔywana w doĈè nieocze-

kiwany sposób: jako metoda na przekazywanie obiektu

THD

utworzonego przez wñtek gäówny

do wñtku oczekujñcego w puli, który zostaä wyznaczony do obsäugi bieĔñcego Ĕñdania. Wiöcej
informacji moĔna znaleĒè w funkcjach

create_new_thread()

,

start_cached_thread()

oraz

end_thread()

w pliku sql/mysqld.cc.

Wszystkie operacje zwiñzane z tworzeniem, koþczeniem i Ĉledzeniem wñtków sñ chronione
przez muteks

LOCK_thread_count

. Do obsäugi wñtków uĔywa siö trzech zmiennych warunku

POSIX.

CONT_thread_count

pomaga w synchronizacji podczas zamykania serwera, gwarantujñc,

Ĕe wszystkie wñtki dokoþczñ swojñ pracö przed zatrzymaniem wñtku gäównego. Warunek

COND_thread_cache

jest rozgäaszany, kiedy wñtek gäówny postanawia obudziè buforowany

wñtek i skierowaè go do obsäugi bieĔñcej sesji z klientem. Warunek

COND_flush_thread_cache

jest uĔywany przez buforowane wñtki do sygnalizowania, Ĕe zaraz zakoþczñ pracö (podczas
zamykania serwera albo przetwarzania sygnaäu

SIGHUP

).

Ponadto do obsäugi wñtków uĔywa siö kilku globalnych zmiennych stanu. Sñ one opisane
w tabeli 6.1.

Wykonywanie

żédaħ krok po kroku

Pötla realizacji standardowych Ĕñdaþ

select()/accept()

znajduje siö w funkcji

handle_con-

nections_sockets()

w pliku sql/mysqld.cc. Po doĈè skomplikowanej serii testów, które spraw-

dzajñ ewentualne bäödy wywoäania

accept()

na róĔnych platformach, docieramy do poniĔszego

fragmentu kodu:

if (!(thd= new THD))
{
(void) shutdown(new_sock,2);
VOID(closesocket(new_sock));
continue;
}

Tworzy on instancjö

THD

. Po pewnych dodatkowych operacjach na obiekcie

THD

wykonanie

przenosi siö do funkcji

create_new_thread()

w tym samym pliku sql/mysqld.cc. Po przepro-

wadzeniu kilku kolejnych testów oraz inicjalizacji dochodzimy to instrukcji warunkowej, która
ustala, jak zostanie uzyskany wñtek obsäugi Ĕñdania. Istniejñ dwie moĔliwoĈci: uĔyè buforowa-
nego wñtku albo utworzyè nowy.

Kiedy buforowanie wñtków jest wäñczone, stary wñtek po obsäuĔeniu Ĕñdania klienta nie koþ-
czy dziaäania, lecz usypia. Gdy nowy klient nawiñzuje poäñczenie, serwer nie tworzy od razu
nowego wñtku, lecz sprawdza, czy ma jakieĈ uĈpione wñtki w pamiöci podröcznej. JeĈli tak, to
budzi jeden z nich, przekazujñc mu instancjö

THD

jako argument.

background image

Implementacja obs

ĥugi żédaħ

_

119

Tabela 6.1. Zmienne globalne zwi

ñzane z wñtkami

Definicja zmiennej

Opis

int abort_loop

Znacznik, który sygnalizuje w

étkom, że czas po sobie posprzétaë i zakoħczyë pracý. Serwer

nigdy nie wymusza przerwania w

étku, ponieważ mogĥoby to doprowadzië do poważnego

uszkodzenia danych. Ka

żdy wétek jest napisany w taki sposób, aby monitorowaĥ swoje

ļrodowisko i koħczyĥ dziaĥanie, kiedy serwer tego zażéda.

int cached_thread_count

Zmienna stanu

ļledzéca liczbý wétków, które zakoħczyĥy dziaĥanie i oczekujé na przydzielenie

do obs

ĥugi innego żédania. Można jé obejrzeë w wynikach polecenia

SHOW STATUS

pod nag

ĥówkiem

Threads_connected

.

int kill_cached_thread

Znacznik, który sygnalizuje wszystkim buforowanym w

étkom, że powinny zakoħczyë dziaĥanie.

Buforowane w

étki czekajé na warunek

COND_thread_cache

w funkcji

end_thread()

.

Przerywaj

é pracý, kiedy wykrywajé, że ten znacznik jest ustawiony.

int max_connections

Zmienna konfiguracyjna serwera okre

ļlajéca maksymalné liczbý poĥéczeħ nieadministracyjnych,

które serwer mo

że przyjéë. Po osiégniýciu tego limitu administrator bazy danych może nawiézaë

jedno dodatkowe po

ĥéczenie administracyjne, aby jakoļ rozwiézaë kryzys.

Dzi

ýki temu limitowi serwer może „wyhamowaë”, zanim sparaliżuje system przez nadmierne

wykorzystanie zasobów.

Limit ten jest kontrolowany przez zmienn

é konfiguracyjné

max_connections

o domy

ļlnej

warto

ļci 100.

int max_used_connections

Zmienna stanu

ļledzéca maksymalné liczbý jednoczesnych poĥéczeħ odnotowané od czasu

uruchomienia serwera. Jej warto

ļë można obejrzeë w wynikach polecenia

SHOW STATUS

pod nag

ĥówkiem

Max_used_connections

.

int query_id

Zmienna u

żywana do generowania unikatowych identyfikatorów zapytaħ. Każdemu zapytaniu

przes

ĥanemu do serwera przypisuje siý bieżécé wartoļë tej zmiennej, która nastýpnie

jest zwi

ýkszana o 1.

int thread_cache_size

Zmienna konfiguracyjna serwera okre

ļlajéca maksymalné liczbý wétków w pamiýci podrýcznej

w

étków.

int thread_count

Zmienna stanu

ļledzéca bieżécé liczbý wétków. Jej wartoļë można obejrzeë w wynikach

polecenia

SHOW STATUS

pod nag

ĥówkiem

Threads_cached

.

int thread_created

Zmienna stanu

ļledzéca liczbý wétków utworzonych od momentu uruchomienia serwera.

Jej warto

ļë można obejrzeë w wynikach polecenia

SHOW STATUS

pod nag

ĥówkiem

Threads_created

.

int thread_id

Zmienna u

żywana do generowania unikatowych identyfikatorów wétków. Każdemu nowo

utworzonemu w

étkowi przypisuje siý bieżécé wartoļë tej zmiennej, która nastýpnie

jest zwi

ýkszana o 1. Można jé obejrzeë w wynikach polecenia

SHOW STATUS

pod nag

ĥówkiem

Connections

.

int thread_running

Zmienna stanu

ļledzéca liczbý wétków, które obecnie odpowiadajé na zapytanie. Zwiýkszana

o 1 na pocz

étku funkcji

dispatch_command()

w pliku sql/sql_parse.cc i zmniejszana o jeden

na ko

ħcu tej funkcji. Można jé obejrzeë w wynikach polecenia

SHOW STATUS

pod nag

ĥówkiem

Threads_running

.

Choè buforowanie wñtków moĔe znacznie zwiökszyè wydajnoĈè mocno obciñĔonego systemu,
funkcjö tö pierwotnie dodano w celu rozwiñzania pewnych problemów synchronizacji w Linuksie
na platformach Alpha.

JeĈli buforowanie wñtków jest wyäñczone albo Ĕaden buforowany wñtek nie jest dostöpny, w celu
obsäuĔenia Ĕñdania trzeba utworzyè nowy wñtek.

background image

120

_

Rozdzia

ĥ 6. Wétkowa obsĥuga żédaħ

Decyzja jest podejmowana w nastöpujñcym bloku:

if (cached_thread_count > wake_thread)
{
start_cached_thread(thd);
}

Funkcja

start_cached_thread()

z pliku sql/mysqld.cc budzi wñtek, który obecnie nie obsäuguje

Ĕñdania, jeĈli taki wñtek istnieje. Warunek

cached_thread_count > wake_thread

gwarantuje

istnienie uĈpionego wñtku, wiöc funkcja nigdy nie jest wywoäywana, jeĈli nie ma Ĕadnych bufo-
rowanych wñtków. Dotyczy to równieĔ sytuacji, w której pamiöè podröczna wñtków jest
wyäñczona.

JeĈli test dostöpnoĈci buforowanych wñtków zakoþczy siö niepowodzeniem, kod przechodzi
do bloku

else

, gdzie zadanie utworzenia nowego wñtku przypada poniĔszemu wierszowi:

if ((error=pthread_create(&thd->real_id, &connection_attrib,
handle_one_connection,
(void*) thd)))

Nowy wñtek zaczyna siö od funkcji

handle_one_connection()

w pliku sql/sql_parse.cc.

Funkcja

handle_one_connection()

po kilku testach i inicjalizacjach bierze siö do roboty:

while (!net->error && net->vio != 0 && !thd->killed)
{
if (do_command(thd))
break;
}

Polecenia sñ akceptowane i przetwarzane dopóty, dopóki nie wystñpi warunek zakoþczenia
pötli. Oto moĔliwe warunki wyjĈcia:

x

Bäñd sieciowy.

x

Wñtek zostaje usuniöty poleceniem

KILL

przez administratora bazy danych albo przez zamy-

kany serwer.

x

Klient wysyäa Ĕñdanie

COM_QUIT

, informujñc serwer, Ĕe chce zakoþczyè sesjö. W takim przy-

padku funkcja

do_command()

z pliku sql/sql_parse.cc zwraca wartoĈè niezerowñ.

x

Funkcja

do_command()

zwraca wartoĈè niezerowñ z jakiejĈ innej przyczyny. Obecnie jedynñ

innñ moĔliwoĈciñ jest to, Ĕe nadrzödny serwer replikacji postanawia przerwaè przesyäanie
strumienia aktualizacji, którego serwer podrzödny (albo klient podszywajñcy siö pod ser-
wer podrzödny) zaĔñdaä poleceniem

COM_BINLOG_DUMP

.

Nastöpnie funkcja

handle_one_connection()

przechodzi do fazy koþczenia wñtku i porzñd-

kowania. Kluczowym elementem tego segmentu kodu jest wywoäanie funkcji

end_thread()

z pliku sql/mysqld.cc.

Funkcja

end_thread()

zaczyna od pewnych dodatkowych czynnoĈci porzñdkowych, a nastöpnie

dociera do interesujñcego punktu: moĔliwoĈci umieszczenia obecnie wykonywanego wñtku
w pamiöci podröcznej. Decyzja jest podejmowana przez nastöpujñcñ instrukcjö warunkowñ:

if (put_in_cache && cached_thread_count < thread_cache_size &&
! abort_loop && !kill_cached_threads)

JeĈli funkcja

end_thread()

postanowi zbuforowaè wñtek, wykonywana jest poniĔsza pötla:

while (!abort_loop && ! wake_thread && ! kill_cached_threads)
(void) pthread_cond_wait(&COND_thread_cache, &LOCK_thread_count);

background image

Problemy programowania w

étkowego

_

121

Pötla czeka, aĔ wñtek zostanie obudzony przez wywoäanie

start_cached_thread()

, procedurö

obsäugi sygnaäu

SIGHUP

albo procedurö zamykania serwera. JeĈli sygnaä budzenia pochodzi

od funkcji

start_cached_thread()

, parametr

wake_thread

ma wartoĈè niezerowñ. W takim

przypadku kod pobiera obiekt

THD

przekazany przez

start_cached_thread()

z listy

thread_

cache

, a nastöpnie wraca (zwróèmy uwagö na makro

DBUG_VOID_RETURN

) do funkcji

handle_one_

connection()

, aby zaczñè obsäugiwanie nowego klienta.

JeĈli wñtek nie zostanie przeniesiony do pamiöci podröcznej, ostatecznie koþczy dziaäanie przez
wywoäanie

pthread_exit()

.

Problemy programowania w

étkowego

W MySQL wystöpujñ podobne komplikacje co w innych programach, które uĔywajñ wñtków.

Wywo

ĥania standardowej biblioteki C

Podczas pisania kodu, który moĔe byè wykonywany przez kilka wñtków jednoczeĈnie, trzeba
zachowaè szczególnñ ostroĔnoĈè, jeĈli chodzi o wywoäywanie funkcji z zewnötrznych bibliotek.
Zawsze istnieje pewne prawdopodobieþstwo, Ĕe wywoäany kod uĔywa zmiennej globalnej,
pisze we wspóädzielonym deskryptorze pliku albo uĔywa jakiegoĈ innego wspólnego zasobu,
nie gwarantujñc wzajemnego wykluczania. W takim przypadku trzeba zabezpieczyè wywoäanie
za pomocñ muteksu.

Trzeba zachowaè ostroĔnoĈè, a jednoczeĈnie unikaè nadmiernej ochrony, która moĔe spowo-
dowaè spadek wydajnoĈci. Na przykäad moĔna oczekiwaè, Ĕe wywoäanie

malloc()

jest bezpieczne

dla wñtków. Inne funkcje, takie jak

gethostbyname()

, czösto majñ odpowiedniki bezpieczne dla

wñtków. Skrypty konfigurujñce kompilacjö MySQL sprawdzajñ, czy sñ one dostöpne i uĔywajñ
ich, kiedy tylko jest to moĔliwe. JeĈli odpowiednik bezpieczny dla wñtków nie zostanie wykryty,
w ostatecznoĈci wäñczany jest ochronny muteks.

Ogólnie rzecz biorñc, MySQL oszczödza sobie wielu zmartwieþ zwiñzanych z bezpieczeþstwem
wñtków, implementujñc odpowiedniki wywoäaþ standardowej biblioteki C w warstwie przeno-
ĈnoĈci w mysys oraz w bibliotece äaþcuchów w strings. Nawet jeĈli ostatecznie wywoäywana
jest biblioteka C, to w wiökszoĈci przypadków odbywa siö to za poĈrednictwem nakäadki. JeĈli
w jakimĈ systemie okazuje siö, Ĕe wywoäanie nie jest bezpieczne dla wñtków, moĔna äatwo roz-
wiñzaè problem przez dodanie muteksu do nakäadki.

Blokady z wzajemnym wykluczaniem (muteksy)

W serwerze wñtkowym kilka wñtków moĔe mieè dostöp do wspóädzielonych danych. W takim
przypadku kaĔdy wñtek musi zagwarantowaè, Ĕe bödzie miaä dostöp na wyäñcznoĈè. W tym
celu stosuje siö blokady z wzajemnym wykluczaniem, zwane teĔ muteksami.

W miarö jak zwiöksza siö zäoĔonoĈè aplikacji, trzeba zdecydowaè, ilu muteksów uĔyè i jakie
dane powinny byè chronione przez kaĔdy z nich. Jednñ skrajnoĈciñ jest utworzenie oddzielnego
muteksu dla kaĔdej zmiennej. Ma to tö zaletö, Ĕe rywalizacja o muteksy jest ograniczona do
minimum. Sñ równieĔ pewne wady: co siö stanie, jeĈli trzeba bödzie uzyskaè dostöp do grupy
zmiennych w sposób atomowy? Konieczne bödzie oddzielne pozyskanie kaĔdego muteksu.

background image

122

_

Rozdzia

ĥ 6. Wétkowa obsĥuga żédaħ

W takim przypadku trzeba zawsze pozyskiwaè je w tej samej kolejnoĈci, aby uniknñè zaklesz-
czeþ. Czöste wywoäania funkcji

pthread_mutex_lock()

i

pthread_mutex_unlock()

doprowa-

dzñ do spadku wydajnoĈci, a programista prödzej czy póĒniej pomyli kolejnoĈè wywoäaþ i spo-
woduje zakleszczenie.

Na drugim koþcu spektrum znajduje siö jeden muteks dla wszystkich zmiennych. Upraszcza
to pracö programisty — wystarczy zaäoĔyè blokadö podczas dostöpu do zmiennej globalnej,
a póĒniej jñ zwolniè. Niestety, ma to bardzo negatywny wpäyw na wydajnoĈè. Wiele wñtków
musi niepotrzebnie czekaè, kiedy jeden z nich uzyskuje dostöp do zmiennej, która nie musi
byè chroniona przed innymi.

Rozwiñzaniem jest odpowiednie pogrupowanie zmiennych globalnych i utworzenie muteksu
dla kaĔdej grupy. WäaĈnie w ten sposób postñpili twórcy MySQL.

W tabeli 6.2 znajduje siö lista globalnych muteksów MySQL wraz z opisami grup zmiennych,
które sñ przez nie chronione.

Tabela 6.2. Globalne muteksy

Nazwa muteksu

Opis muteksu

LOCK_Acl

Inicjalizowany, ale obecnie nieu

żywany w kodzie. W przyszĥoļci może zostaë usuniýty.

LOCK_active_mi

Chroni wska

Śnik

active_mi

, który wskazuje deskryptor aktywnego podrz

ýdnego serwera

replikacji. W tym momencie ochrona jest zb

ýdna, ponieważ wartoļë

active_mi

nigdy

nie jest zmieniana wspó

ĥbieżnie. Ochrona stanie siý jednak konieczna, kiedy do serwera

zostanie dodana obs

ĥuga wielu serwerów nadrzýdnych.

LOCK_bytes_received

Chroni zmienn

é stanu

bytes_received

, która

ļledzi liczbý bajtów odebranych

od wszystkich klientów od momentu uruchomienia serwera. Nieu

żywana w wersji 5.0

i nowszych.

LOCK_bytes_sent

Chroni zmienn

é stanu

bytes_sent

, która

ļledzi liczbý bajtów wysĥanych do wszystkich

klientów od momentu uruchomienia serwera. Nieu

żywana w wersji 5.0 i nowszych.

LOCK_crypt

Chroni wywo

ĥanie uniksowej biblioteki C

crypt()

, które nie jest bezpieczne dla w

étków.

LOCK_delayed_create

Chroni zmienne i struktury zaanga

żowane w tworzenie wétku do obsĥugi opóŚnionego

wstawiania. Opó

Śnione operacje wstawiania natychmiast wracajé do klienta, nawet

je

ļli tablica jest zablokowana — w takim przypadku sé przetwarzane w tle przez wétek

opó

Śnionego wstawiania.

LOCK_delayed_insert

Chroni list

ý wétków opóŚnionego wstawiania

I_List<delayed_insert>

delayed_threads

.

LOCK_delayed_status

Chroni zmienne stanu

ļledzéce operacje opóŚnionego wstawiania.

LOCK_error_log

Chroni zapisy w dzienniku b

ĥýdów.

LOCK_gethostbyname_r

Chroni wywo

ĥanie

gethostbyname()

w funkcji

my_gethostbyname_r()

w pliku

mysys/my_gethostbyname.c w systemach, w których biblioteka C nie oferuje wywo

ĥania

gethostbyname_r()

.

LOCK_global_system_variables

Chroni operacje modyfikuj

éce globalne zmienne konfiguracyjne z poziomu wétku

klienckiego.

LOCK_localtime_r

Chroni wywo

ĥanie

localtime()

w funkcji

my_localtime_r()

w pliku

mysys/my_pthread.c w systemach, w których biblioteka C nie oferuje wywo

ĥania

localtime_r()

.

LOCK_manager

Chroni struktury danych u

żywane przez wétek menedżera, który obecnie jest odpowiedzialny

za okresowe wymuszanie zapisu tabel na dysku (je

ļli ustawienie

flush_time

jest

niezerowe) oraz za porz

édkowanie dzienników Berkeley DB.

background image

Czytaj dalej...

Problemy programowania w

étkowego

_ 123

Tabela 6.2. Globalne muteksy — ci

ñg dalszy

Nazwa muteksu

Opis muteksu

LOCK_mapped_file

Chroni struktury danych i zmienne u

żywane do operacji na plikach odwzorowanych

w pami

ýci. Obecnie funkcja ta jest wewnýtrznie obsĥugiwana, ale nie jest używana

w

żadnej czýļci kodu.

LOCK_open

Chroni struktury danych i zmienne zwi

ézane z pamiýcié podrýczné tabel oraz z otwieraniem

i zamykaniem tabel.

LOCK_rpl_status

Chroni zmienn

é

rpl_status

, która mia

ĥa byë używana do bezpiecznej replikacji

z automatycznym przywracaniem danych. Obecnie jest to martwy kod.

LOCK_status

Chroni zmienne wy

ļwietlane w wynikach polecenia

SHOW STATUS

.

LOCK_thread_count

Chroni zmienne i struktury danych zaanga

żowane w tworzenie lub niszczenie wétków.

LOCK_uuid_generator

Chroni zmienne i struktury danych u

żywane przez funkcjý SQL

UUID()

.

THR_LOCK_charset

Chroni zmienne i struktury danych zwi

ézane z operacjami na zestawie znaków.

THR_LOCK_heap

Chroni zmienne i struktury danych zwi

ézane z pamiýciowym mechanizmem skĥadowania

(MEMORY).

THR_LOCK_isam

Chroni zmienne i struktury danych zwi

ézane z mechanizmem skĥadowania ISAM.

THR_LOCK_lock

Chroni zmienne i struktury danych zwi

ézane z menedżerem blokad tabel.

THR_LOCK_malloc

Chroni zmienne i struktury danych zwi

ézane z nakĥadkami na rodziný wywoĥaħ

malloc()

. U

żywany gĥównie w wersji

malloc()

przeznaczonej do debugowania

(zob. mysys/safemalloc.c).

THR_LOCK_myisam

Chroni zmienne i struktury danych zwi

ézane z mechanizmem skĥadowania MyISAM.

THR_LOCK_net

Obecnie u

żywany do ochrony wywoĥania

inet_ntoa()

w funkcji

my_inet_ntoa()

w pliku mysys/my_net.c

THR_LOCK_open

Chroni zmienne i struktury danych, które

ļledzé otwarte pliki.

Oprócz muteksów globalnych istnieje kilka muteksów osadzonych w strukturach lub klasach,
które säuĔñ do ochrony czöĈci danej struktury lub klasy. Istnieje wreszcie kilka muteksów glo-
balnych o zasiögu plikowym (

static

) w bibliotece mysys.

Blokady odczytu-zapisu

Blokada na wyäñcznoĈè nie zawsze jest najlepszym rozwiñzaniem ochrony operacji wspóäbieĔ-
nych. WyobraĒmy sobie sytuacjö, w której pewna zmienna rzadko jest modyfikowana tylko
przez jeden wñtek, natomiast czösto czytana przez wiele innych. GdybyĈmy uĔyli muteksu,
zwykle jeden wñtek czytajñcy musiaäby czekaè, aĔ inny zakoþczy czytanie, choè mogäyby one
wykonywaè siö wspóäbieĔnie.

W takich sytuacjach lepiej sprawdza siö inny typ blokady: blokada odczytu-zapisu. Blokady
odczytu mogñ byè wspóädzielone, a blokady zapisu wzajemnie siö wykluczajñ. Zatem wiele
wñtków czytajñcych moĔe dziaäaè wspóäbieĔnie, pod warunkiem Ĕe nie ma wñtku piszñcego.

Jak widaè, blokada odczytu-zapisu moĔe robiè to samo co muteks i wiöcej. Czemu wiöc nie uĔy-
waè tylko blokad odczytu-zapisu? Jak mówi przysäowie, nie ma nic za darmo. Dodatkowe
funkcje wymagajñ bardziej zäoĔonej implementacji. W rezultacie blokady odczytu-zapisu zaj-
mujñ wiöcej cykli procesora, nawet gdy blokada zostanie pozyskana natychmiast.


Wyszukiwarka

Podobne podstrony:
kolokwium zal1 2006 2, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 2, bd2
MySQL Mechanizmy wewnetrzne baz Nieznany
PHP MySQL SQL CGI bazy danych w internecie, Oracle, Oracle - materiały różne
Zadanie 3 PLSQL, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 2, bd2 - kopi
kolokwium zal2 2006 1, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 2, cwic
zadania 1, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 1
Zadanie2, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 2, bd2 - kopia mrako
zadania 2, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 1
sciaga na egzamin (1), Informatyka i Ekonometria SGGW, Semestr 4, Bazy danych SQL, EGZAMiN
Zadanie 4 PLSQL, wisisz, wydzial informatyki, studia zaoczne inzynierskie, bazy danych 2, bd2 - kopi
informatyka mysql leksykon kieszonkowy wydanie ii george reese ebook
Podstawy Informatyki Wykład XIX Bazy danych

więcej podobnych podstron