Archiwum kategorii ‘DB’

MSDE w Pudełku

piątek, 12 Grudzień 2008

Przyszło mi przenosić aplikację Pudełko napisaną przez firmę SOLVEO Software Solutions na zlecenie AMUR Sp. z o. o. dla OPEK Sp. z o. o. Większego problemu z aplikacją nie ma, pojawił się natomiast z MS SQL (MSDE).

Otóż konfiguracja mówi, że baza uruchamia się nasłuchując na named pipe i TCP, z tym, że po uruchomieniu nie widać żeby słuchało cokolwiek (vide netstat -a). Połączenia lokalne działają, więc z bazą wszystko w porządku. I oto co się okazuje po przejrzeniu dziennika zdarzeń:

Masz uruchomioną wersję systemu Microsoft SQL Server 2000 lub Microsoft SQL Server 2000 Desktop Engine (zwaną także MSDE), w której występują znane usterki zabezpieczeń podczas używania razem z tą wersją systemu Windows. W celu zmniejszenia podatności tego komputera na ataki niektórych wirusów zostały wyłączone porty sieciowe TCP/IP i UDP systemu Microsoft SQL Server 2000, MSDE lub obydwu. Aby włączyć te porty, musisz zainstalować poprawkę lub najnowszy dodatek Service Pack dla systemu Microsoft SQL Server 2000 lub MSDE ze strony http://www.microsoft.com/sql/downloads/default.asp

Aby znaleźć więcej informacji, zobacz http://go.microsoft.com/fwlink/events.asp w Centrum pomocy i obsługi technicznej.

No cóż. Szkoda, że podczas uruchamiania nie ma na ekranie żadnego komunikatu. Zaskakujące jest, że w logach jest informacja o znanym problemie… Zakładam, że firma Microsoft nie wypuściła aplikacji, która sama raportuje o błędzie i wyłącza porty wobec czego nie jest w pełni funkcjonalna, a komunikat pochodzi od jakiegoś zarządcy usług (mimo, że dziennik zdarzeń twierdzi, iż od MSDE), który może wpływać na samo MSDE i wyłączać funkcjonalność.

Problem udało się rozwiązać. Poprawka wraz z opisem dostępna jest na stronie Microsoft.

17148 :
SQL Server is terminating due to ‘stop’ request from Service Control Manager.

17052 :
Microsoft SQL Server  2000 – 8.00.194 (Intel X86)
Aug  6 2000 00:57:48
Copyright (c) 1988-2000 Microsoft Corporation
Desktop Engine on Windows NT 5.1 (Build 2600: Dodatek Service Pack 3)

17104 :
Server Process ID is 3752.

17162 :
SQL Server is starting at priority class ‘normal’(1 CPU detected).

17124 :
SQL Server configured for thread mode processing.

17125 :
Using dynamic lock allocation. [500] Lock Blocks, [1000] Lock Owner Blocks.

17834 :
Using ‘SSNETLIB.DLL’ version ’8.0.311′.

17052 :
Recovery complete.

19013 :
SQL server listening on 192.168.1.105: 1433.

19013 :
SQL server listening on 127.0.0.1: 1433.

19012 :
SuperSocket Info: Bind failed on TCP port 1433.

19012 :
SuperSocket Info: Bind failed on TCP port 1433.

19013 :
SQL server listening on TCP, Shared Memory, Named Pipes.

17126 :
SQL Server is ready for client connections

Product: MSDE 2000/SQL Server 2000 Critical Update — Installation completed successfully.

SQL Relay

czwartek, 4 Wrzesień 2008

Zajmował się ktoś SQL Relay? Wygląda obiecująco, i te wsparcie dostawców – Oracle, MySQL, mSQL, PostgreSQL, Sybase, MS SQL Server (oba przez FreeTDS), IBM DB2, Interbase, SQLite, ODBC, MS Access (MDB Tools)…

Postanowiłem przyjrzeć się temu w połączeniu z PostgreSQL i MySQL z C/C++ i Perl API. Wersja 0.39.4.
Jest w portach dla FreeBSD, nie ma w paczkach dla Gentoo. Potrzebuje dodatkowej biblioteki Rudiments (devel/rudiments we FreeBSD, dev-cpp/rudiments w Gentoo) i jeśli chcemy jakiegoś API, to odpowiednio: Perl, PHP, Python, Ruby, TCL, Java lub Zope. GTK+ i glib da nam GUI.

Konfiguracja nietrudna – w pliku XML. W tagu instance definiujemy proces nasłuchu. Tag user to definicje użytkowników wraz z hasłami. Na koniec tag connection, który wskazuje konfigurację do konkretnej bazy.
W bardziej złożonej konfiguracji mamy jeszcze router i filter. Pierwszy tag odpowiada za kierowanie zapytań do odpowiedniej bazy, drugi – za filtrowanie zapytań (zdefiniowane nie będą w ogóle przesyłane do serwera, a klient dostanie błąd). W obu przypadkach jest możliwość dopasowania zapytań za pomocą wyrażeń regularnych.

Jeżeli uruchamiamy SQL Relay za pomocą sqlr-start, musimy pamiętać, że wszystkie programy, które wywołuje, np. sqlr-listener muszą być w ścieżce ($PATH). Baza również musi działać. Jeśli nie, pozostaje walka z czyszczeniem shared memory segment i semaphore (ipcs, ipcrm), bo SQL Relay nie sprząta po sobie.

Na tym skończyłem walkę. To znaczy próbowałem uruchomić SQL Relay zarówno z MySQL jak i PostgreSQL, ale niestety zakończyło się to niepowodzeniem (SQL Relay nasłuchiwał, przy połączeniu cisza). Mam nadzieję, że kiedyś wrócę do tego tematu.

Przepraszam, jeśli ktoś był pełen nadzieji, że czegoś się dowie z tego artykułu. Zmieniłem pracę i już nie robię tego co robiłem, więc wiele rzeczy pozostało niedokończonych…

Bezpieczne połączenia z MySQL (C API)

niedziela, 10 Sierpień 2008

Ostatnio coraz więcej kodu w C piszę z użyciem MySQL API, a że dane przy jednym z projektów są poufne i serwer nie jest lokalny, przyjrzałem się połączeniom SSL. W zasadzie po stronie kodu to nic wielkiego – wystarczy wywołać mysql_ssl_set() przed mysql_real_connect() (mysql_connect() jest deprecated). Oczywiście pod warunkiem, że klient jest skompilowany ze wsparciem OpenSSL (lub yaSSL, ale nigdy nie testowałem).

Uruchomienie samego serwera (również przy założeniu, że skompilowany ze wsparciem SSL) sprowadza się do podania dodatkowych parametrów w linii komend. Przykładowo może to wyglądać tak:

mysqld --defaults-file=/etc/mysql/my.cnf --ssl --ssl-ca=</path/to/CA.crt> --ssl-cert=</path/to/file.crt> --ssl-key=</path/to/file.key>

Ewentualnie odpowiednie parametry podać w pliku konfiguracyjnym (my.cnf):

ssl-ca = </path/to/CA.crt>
ssl-cert =</path/to/file.crt>
ssl-key =</path/to/file.key>

Pamiętać należy, że w przypadku, gdy certyfikat jest zabezpieczony hasłem, MySQL może być jedynie uruchomiony ręcznie. Korzystając ze skryptów startowych (na pewno w Gentoo Linux) czy nawet wrzucając proces w tło (&) serwer nie skorzysta z certyfikatu i nie nawiążemy szyfrowanej transmisji. Próba takiego uruchomienia zakończy się podobnym wpisem w logach:

[Warning] Failed to setup SSL

Testowe połączenie do serwera z konsoli mysql możemy wykonać podając jedynie ścieżkę do certyfikatu CA (format PEM), np.

mysql -h <host> -u <user> -p --ssl-ca=</path/to/CA.crt> <database>

Wywołując STATUS możemy sprawdzić czy bieżące połączenie ma wsparcie SSL:

SSL:            Cipher in use is DHE-RSA-AES256-SHA

czy też nie:

SSL:            Not in use

Wymuszanie szyfrowanych połączeń możliwe tylko poprzez REQUIRE SSL przy nadawaniu praw użytkownikowi (GRANT). W takim przypadku połączenie bez SSL zakończy się komunikatem podobnym jak w przypadku np. podania błędnego hasła:

ERROR 1045 (28000): Access denied for user 'exg'@'192.168.1.101' (using p assword: YES)

Więcej o używaniu SSL w MySQL można poczytać tutaj:

Wracając do samej funkcji mysql_ssl_set(), to jako parametry przyjmuje: uchwyt połączenia zwrócony przez mysql_init(), ścieżkę do pliku klucza, certyfikatu i certyfikatu CA oraz ścieżkę do katalogu z certyfikatami CA w formacie PEM, a także listę wspieranych algorytmów.
Nieużywane parametry powinny przyjmować wartość NULL.
Funkcja zawsze zwraca 0, o błędach SSL informuje mysql_real_connect().

ERROR 2026 (HY000): SSL connection error

Na koniec łyżka dziegciu – libmysql (i klient mysql) dla Windows (ten z binarnej paczki) nie jest skompilowany z obsługą SSL. Aby korzystać z bezpiecznych połączeń należy pobrać źródła i samodzielnie dokonać kompilacji. Mi się nie udało… Może przez zbyt stare Visual C++.

Analiza log-slow-queries (z życia wzięte…)

sobota, 26 Lipiec 2008

Jedna z tabel klienta wygląda tak:

CREATE TABLE `mix_stats` (
`id` int(11) NOT NULL auto_increment,
`id_produktu` decimal(20,0) NOT NULL default ’0′,
`data` date default NULL,
`count` decimal(1,0) NOT NULL default ’0′,
`session` varchar(255) NOT NULL default ”,
`ip` varchar(20) NOT NULL default ”,
`referer` varchar(255) NOT NULL default ”,
PRIMARY KEY  (`id`)
) TYPE=MyISAM

MySQL 3.23, w logach następujące wpisy:

# Time: 080726 10:40:51
# User@Host: xxx[xxx] @ localhost []
# Query_time: 48  Lock_time: 0  Rows_sent: 1  Rows_examined: 639552
SELECT count(id) FROM mix_stats WHERE id_produktu = ’84092324382e93e’ AND session = ”;

Jeżeli ktoś jest na tyle cierpliwy, by czekać 48 sekund na wynik, to musi mu bardzo zależeć…

Analizę bazy wykonywałem na MySQL 5.0.60, więc mam większe pole do popisu, np.:

mysql> SELECT count(id) FROM mix_stats WHERE id_produktu = '84092324382e93e' AND session = '';
+-----------+
| count(id) |
+-----------+
|         0 |
+-----------+
1 row in set, 65535 warnings (0.99 sec)

mysql> SHOW WARNINGS LIMIT 3;
+---------+------+-----------------------------------------------------+
| Level   | Code | Message                                             |
+---------+------+-----------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '84092324382e93e' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '84092324382e93e' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '84092324382e93e' |
+---------+------+-----------------------------------------------------+
3 rows in set (0.00 sec)

Na początek polecam: DECIMAL. Choć pewnie skończy się przejściem do innego usługodawcy, gdzie mocniejsza maszyna bazodanowa przemieli to szybciej (ostrzeżenia i błędy i tak użytkowników nie interesują)…

MySQL vs. PostgreSQL (adresy IP)

poniedziałek, 21 Lipiec 2008

Jeżeli kiedykolwiek zastanawialiśmy się jak przechowywać i łatwo przetwarzać adresy IP w bazie danych MySQL, odpowiedź brzmi – zmienić bazę na PostgreSQL (i nie tylko w tym przypadku).

MySQL oprócz funkcji INET_ATON() i INET_NTOA() nie oferuje nic więcej, ale to i tak lepsze niż przechowywanie adresów w CHAR(15) czy VARCHAR(15). Wskazany typ kolumny to UNSIGNED INT, a konwersja przy wstawianiu rekordów czy ich pobieraniu właśnie za pomocą wspomnianych funkcji.

PostgreSQL daje nam następujące typy danych: cidr, inet i dodatkowo macaddr (do przechowywania adresów MAC). Umożliwia także łatwe zarządzanie danymi za pomocą operatorów i funkcji.

Oczywiście sposób przechowywania adresu IP na pewno nie powinien być kryterium wyboru silnika baz danych, ale może być drobnym argumentem.

Do przeanalizowania kwestia szybkości przeszukiwania, indeksowania i wielkości danych.

Do poczytania:

Sprawdzanie aktywności użytkowników MySQL

piątek, 18 Lipiec 2008

Podłoże problemu: bazy zakładane na serwerze na żądanie użytkowników / klientów nie były usuwane. Po pewnym czasie ilość baz urosła, z czego spory odsetek była nieużywana. Nie obciąża to serwera, ale zabiera miejsce na dysku i ogólnie powoduje bałagan. Trzeba to jakoś posprzątać!

Prosty sposób – REVOKE i telefon. Mało elegancki! My szukamy czegoś na poziomie ;-)

Od pewnego czasu śledzę projekt mysql-proxy. Wprawdzie już jakiś czas projekt jest nieuaktualniany (At revision 511.), ale mam nadzieję, że Jan Kneschke cały czas pracuje…

Początkowo używałem mysql-proxy jako typowego pośrednika, co było pomocne przy przenoszeniu baz (sporo klientów łączyło się przez UNIX socket). Nie sprawdzał się za dobrze (ostatnia testowana wersja nie zawsze łączyła z PHP, wcześniejsze miały duże problemy ze stabilną pracą w ogóle). Na podobny projekt – pgpool-II (dla PostgreSQL) nie narzekałem nigdy. Niemniej z racji długości życia nie ma ich co porównywać.

Dla śledzenia użytkowników z tematu napisałem skrypt (LUA), który każde połączenie zapisuje do pliku dziennika (można przerobić na zapisywanie do bazy + jakiś licznik). Informacją wystarczającą jest IP klienta, nazwa użytkownika oraz baza, do której się podłączył. Do posprzątania nic więcej nas nie interesuje.
Sposób uruchomienia mysql-proxy opisany jest w dokumentacji, więc warto tam zajrzeć.
Skrypt wygląda tak:

local log_file = ‘/tmp/mysql.log’
local fh = io.open(log_file, „a+”)

function read_auth_result(auth)
if auth.packet:byte() == proxy.MYSQLD_PACKET_OK then
fh:write(
string.format(„%s %s\t%s\t%s\n”,
os.date(‘%Y-%m-%d %H:%M:%S’),
proxy.connection.client.address,
proxy.connection.client.username,
proxy.connection.client.default_db))
end
fh:flush()
end

Ze struktury proxy.connection.client przyda nam się address (adres IP klienta), username (nazwa uzytkownika) oraz default_db (baza, do której zostało wykonane połączenie). Jest jeszcze scrambled_password, ale logowanie tego podpada pod inwigilację. Logujemy tylko uwierzytelnionych użytkowników (MYSQLD_PACKET_OK – 0, ale jest jeszcze możliwość użycia MYSQLD_PACKET_ERR – 255). I w wyniku otrzymujemy:

2008-07-18 11:52:40 127.0.0.1:43228    root    test
2008-07-18 12:00:16 127.0.0.1:62133    exg    exg
2008-07-18 12:00:32 127.0.0.1:62901    root    mysql
2008-07-18 12:07:54 10.74.0.254:11243 guzik
2008-07-18 12:08:00 10.74.0.254:16369 guzik test

Log bez bazy, to połączenie z konsoli. \u czy USE nie loguje się w ogóle.
Po długoterminowym zbieraniu informacji (do miesiąca) mamy już listę wykorzystywanych baz. Pozostaje dump i DROP :>

Pamiętajmy też, że baza widzi połączenie z mysql-proxy, więc należy dać stosowne uprawnienia dla tego hosta.

Więcej do poczytania:

MySQL: multiple tablespaces

środa, 16 Lipiec 2008

Wiadomo nie od dziś, że plik przestrzeni tabel InnoDB rośnie, ale nie maleje. Szczególnie dużym problemem jest to w momencie gdy tworzymy i usuwamy duże tabele. Sposobów radzenia sobie z tym jest kilka (nie wszystkie eleganckie, ale takie znalazłem – ja nie korzystam z żadnego z nich):

  • zmiana silnika wszystkich tabel z InnoDB na MyISAM, zatrzymanie bazy, usunięcie pliku ibdata1 i stworzenie go od nowa, zmiana silnika tabel z MyISAM na InnoDB – życzę powodzenia przy większej ilości tabel,
  • zrzut danych (dump), usunięcie pliku ibdata1 i utworzenie go od nowa, import danych.

Pewnie inne wariacje powyższych też zadziałają podobnie. Niemniej uważam, że downtime jest dość istotny, więc nie sprawdzą się w mocno obciążonym środowisku produkcyjnym.

Więcej o zmaganiach innych można poczytać tutaj:

Ja wybrałem tworzenie osobnego pliku dla każdej tabeli InnoDB. Na pierwszy rzut oka wydaje się, że jest to lek na całe zło InnoDB – usunięcie bazy / tabeli skutkuje usunięciem pliku, szybsze OPTIMIZE dla tabel, łatwiejsze tworzenie ‘zimnych kopii zapasowych’, obejście problemu systemu plików związanego z maksymalną wielkością pliku. Nic tylko używać.

Jeśli chodzi o przygotowanie serwera do tego, to sprowadza się do dodania w sekcji [mysqld] pliku my.cnf następującej linii:

[mysqld]
innodb_file_per_table

Oczywiście wymaga to restartu serwera. Wszystkie nowe tabele będą już miały osobny plik .ibd w katalogu bazy (dane i indeksy w jednym pliku w odróżnieniu od MyISAM – .MYD i MYI, plik z definicją tabel .frm tworzony jest dla obu silników). Dostęp do istniejących tabel zapisanych w ibdata1 jest po zmianie możliwy. Podobnie w sytuacji, gdybyśmy chcieli powrócić do poprzeniej konfiguracji – osobne pliki będą obsługiwane nadal.

Gdybyśmy chcieli dotychczasowe tabele utrzymywać w osobnych plikach, należy wykonać:

ALTER TABLE test ENGINE=InnoDB;

pamiętając, że nadal nie zmniejszy to głównego pliku.

Poza tym wspólna przestrzeń tabel (tablespace) dla InnoDB (plik ibdata1) zawsze będzie istnieć, nawet jak nową bazę uruchomimy z innodb_file_per_table, a wynika to z tego, że trzymane są tam np. undo logs.

Dodatkowo, wg dokumentacji MySQL, plików .ibd nie można przenosić dowolnie pomiędzy katalogami baz tak jak w przypadku MyISAM (jeśli ktoś w ogóle robi coś takiego). Powodem jest przetrzymywanie we wspólnej przestrzeni nazw InnoDB nazwy bazy. Po szczegóły zainteresowanych odsyłam do dokumentacji.

Nie znalazłem jeszcze wad tego rozwiązania. Jak tylko się znajdą, uaktualnię artykuł.

Do poczytania: