You are on page 1of 7

Przestrzenne bazy danych

Sprawozdanie

Projekt:
Porównanie wydajności modułów przestrzennych w
najnowszych wersjach systemów baz danych MySQL i
PostgreSQL

Wykonali:
Julia Karwat 300557
Marcin Kotulski 300570
1. Cel pracy
Celem projektu jest porównanie wydajności modułów przestrzennych różnych baz
danych. Polega ono na zaimportowaniu identycznych danych do baz oraz
wykonywaniu różnych rodzajów zapytań na danych. Wykorzystując metryki takie jak
czas wykonywania operacji, użycie CPU ... podczas wykonywania zapytań
wywnioskujemy, który moduł lepiej radzi sobie z opracowywaniem danych
przestrzennych.

2. Schemat danych
W każdej bazie danych znalazły się dwie tabele zawierające dane na temat
budynków znajdujących się w województwie.
Każda tabela zawierała kolumny:

• gid(int) – indeksacja nieprzestrzenna domyślnie tworzona w postGIS(tylko w tej


bazie)
• osm_id(int) – do nadawania unikalnych indeksów wszystkim obiektom w projekcie
OpenStreetMap
• fclass(string) - "feature class". W kontekście OpenStreetMap (OSM), feature class
odnosi się do różnych rodzajów obiektów geograficznych, takich jak drogi,
budynki, obszary wodne itp.
W naszym przypadku fclass zawsze ma wartość building, ponieważ takie dane
zaimportowaliśmy.
• name(string) – nazwa obiektu/budynku
• geom(geometry) – ten typ danych służy do przechowywania obiektów
geometrycznych, takich jak punkty, linie, poligony, wielokąty i inne. Na tej
kolumnie zostały założone indeksy danych przestrzennych w obu bazach danych.
3. Zastosowane indeksy
3.1. Indeksy przestrzenne:
• PostGIS – indeks GIST to bardziej ogólny indeks przestrzenny w porównaniu do
specjalistycznych indeksów takich jak R-drzewa czy B-drzewa. Jest to często
wykorzystywane rozwiązanie w kontekście danych przestrzennych w
PostgreSQL ze względu na swoją uniwersalność i zdolność do obsługi
różnorodnych struktur danych przestrzennych.
• MySQL – SPATIAL INDEX jest to indeks o strukturze R-drzewo. R-tree jest
powszechnie używanym typem indeksu przestrzennego, ponieważ pozwala na
hierarchiczną organizację danych przestrzennych, co z kolei umożliwia
skuteczne przeszukiwanie obszarów i identyfikację obiektów znajdujących się
w określonych obszarach.
3.2. Indeksy nieprzestrzenne:

• gid (Global Identifier) – PostGIS automatycznie generuje kolumnę gid jako


identyfikator globalny dla każdego wiersza w tabeli przestrzennej. Jest to
używane jako unikalny identyfikator dla każdego obiektu przestrzennego w
bazie danych.
• osmid (OpenStreetMap Identifier)- W przypadku danych związanych z
OpenStreetMap, osmid jest używane do jednoznacznego identyfikowania
obiektów na mapie. Na przykład, jeśli pobrane zostaną dane z OSM, każdy
segment drogi, budynek czy inny obiekt będzie miał swój unikalny osmid.

4. Wykorzystane zapytania

4.1. Zawieranie punktu przez obiekt:

SELECT * FROM lubuskie WHERE ST_Contains(geom,


ST_GeomFromText('Point(15.001020149877485 52.67181144981775)',
4326));

Tutaj ST_Contains sprawdza, czy geometria geom w każdym wierszu tabeli zawiera
punkt określony przez współrzędne lon i lat.
4.2. Przecięcie z odcinkiem lub innym obiektem:

SELECT * FROM dolnoslaskie WHERE ST_Intersects(geom,


ST_GeomFromText('LINESTRING(16.860772 51.026913, 17.054226
51.182742)', 4326));

W tym przypadku ST_Intersects zwraca wszystkie geometrie, które mają część


wspólną z podaną linią (lub innym obiektem).

4.3. Zawieranie w obszarze:

SELECT * FROM lubuskie WHERE


ST_Within(ST_GeomFromText('Point(15.001020149877485
52.67181144981775)', 4326), geom);

ST_Within zwraca te geometrie, które całkowicie mieszczą się w określonym


prostokącie (lub innym obszarze).

4.4. Zapytania o sąsiadów:


K najbliższych sąsiadów:
select * from dolnoslaskie order by geom <->
ST_GeomFromText('POINT(51.09470061779064 17.032347931267115)',
4326) limit 100;

Operator <-> zwraca odległość między punktami w sposób przybliżony, ale jest
szybki i użyteczny dla zapytań KNN (k najbliższych sąsiadów).
Obiekty w zadanej odległości:
select * from dolnoslaskie where ST_Distance(geom,
ST_GeomFromText('POINT(16.860772 51.026913)', 4326))> 0.60;

4.5. Obliczanie pola obiektów:


SELECT ST_Area(geom) from lubuskie where name='Abc';

ST_Area oblicza pole powierzchni geometrii.

4.6Obliczanie odległości:
SELECT ST_Distance(geom, ST_GeomFromText('POINT(16.860772
51.026913)', 4326)) from lubuskie;

ST_Distance oblicza odległość między dwoma geometriami.

` 4.7. Zapytania skorelowane


SELECT d.gid, d.name, ST_AsText(d.geom), ST_Area(d.geom)
FROM dolnoslaskie d
WHERE d.name like ‘Z%’ and ST_Area(d.geom) < (
SELECT AVG(ST_Area(l.geom))
FROM lubuskie l
WHERE l.name = d.name);

Zapytanie zwraca informacje o budynkach w województwie dolnośląskim, które mają


mniejszą powierzchnię od średniej powierzchni budynków w województwie lubuskim
o takiej samej nazwie. Zapytanie zostało ograniczone do nazw budynków, które
zaczynają się na literę Z, ponieważ w innym wypadku jego wykonanie trwał bardzo
długo.

5. Środowisko testowe
Bazy danych zostały uruchomione na kontenerach Dockerowych. Parametry obu
kontenerów były takie same aby nie wpływały one na wyniki projektu.
Komenda uruchamiająca kontener postgis
$ docker run --name spdb-postgis1 -e POSTGRES_PASSWORD=mysecretpassword -d
postgis/postgis -p 5432:5432

Komenda uruchamiająca kontener mysql


$ docker run --name spdb-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:latest
–p 1234:5432
6. Metryki
W celu porównania modułów przestrzennych wybraliśmy metryki, które bierzemy pod
uwagę przy ich ocenie. Będą one wskaźnikiem, jak szybko i przy zużyciu jakich zasobów
takie same zapytania zostały wykonane na takich samych danych.
Wykorzystanymi metrykami są:

• Czas wykonywania zapytania[s]


• Użycie pamięci RAM[%]
• Zużycie CPU[%]

7. Wyniki i wnioski
Do uzyskania wyników wykorzystano skrypt napisany w języku Python:
import psycopg2
import time
import psutil

# Konfiguracja dla PostgreSQL


pg_conn = psycopg2.connect(
host="localhost",
database="gis_db",
user="postgres",
password="mysecretpassword"
)
def measure_query_performance(connection, query):
start_time = time.time()

try:
cursor = connection.cursor()
cursor.execute(query)
result = cursor.fetchall()
connection.commit()
except Exception as e:
connection.rollback()
print(f"Error: {str(e)}")
finally:
cursor.close()

end_time = time.time()

cpu_usage = psutil.cpu_percent()
ram_usage = psutil.virtual_memory().percent
execution_time = end_time - start_time
disk_io_counters = psutil.disk_io_counters()
print(f"Disk I/O Counters: {disk_io_counters}")
print(f"CPU Usage: {cpu_usage}%")
print(f"RAM Usage: {ram_usage}%")
print(f"Execution Time: {execution_time} seconds")

return result
# Lista zapytań do pomiaru
queries = [
"SELECT * FROM lubuskie WHERE ST_Contains(geom, ST_GeomFromText('Point(15.001020149877485
52.67181144981775)', 4326))",
"SELECT * FROM dolnoslaskie WHERE ST_Intersects(geom, ST_GeomFromText('LINESTRING(16.860772
51.026913, 17.054226 51.182742)', 4326)) ",
"SELECT * FROM lubuskie WHERE ST_Within(ST_GeomFromText('Point(15.001020149877485
52.67181144981775)', 4326), geom)",
"SELECT * FROM dolnoslaskie WHERE ST_Within(geom, ST_MakeEnvelope(16.860772, 51.026913,
17.054226, 51.182742 ,4326))",
"SELECT * FROM dolnoslaskie order by geom <-> ST_GeomFromText('POINT(51.09470061779064
17.032347931267115)', 4326) limit 100",
"SELECT * FROM dolnoslaskie where ST_Distance(geom, ST_GeomFromText('POINT(16.860772
51.026913)', 4326))> 0.6",
"SELECT ST_Area(geom) FROM dolnoslaskie",
"SELECT ST_Area(geom) FROM lubuskie where name='Abc'",
"SELECT ST_Distance(geom, ST_GeomFromText('POINT(16.860772 51.026913)', 4326)) from
lubuskie",
" SELECT d.gid, d.name, ST_AsText(d.geom), ST_Area(d.geom) FROM dolnoslaskie d WHERE d.name
like ‘Z%’ and ST_Area(d.geom) < (SELECT AVG(ST_Area(l.geom)) FROM lubuskie l WHERE l.name =
d.name);"]

for query in queries:


print(f"Measuring query: {query}")
result_pg = measure_query_performance(pg_conn, query)
print("\n" + "=" * 50 + "\n")

pg_conn.close()

Numer Zapytania PostGIS [s] MySQL[s]


4.1 0,5879 0,7201
4.2 1,3763 1,5029
4.3 0,4850 0,5416
4.4.1 0,0234 0,1015
4.4.2 5,6526 6,3208
4.5 0,3000 0,4105
4.6 1,7202 1,8636
4.7 25,5466 37,2948
Tabela 1. Czas wykonywania zapytań [s]

Numer Zapytania PostGIS MySQL


4.1 94.3% 82.2%
4.2 52.4% 63.8%
4.3 93.9% 94.8%
4.4.1 50.0% 55.3%
4.4.2 80.9% 86.2%
4.5 69.6% 74.2%
4.6 86.3% 88.2%
4.7 51,4% 54,7%
Tabela 2. Zużycie CPU
Numer Zapytania PostGIS MySQL
4.1 63.8% 69.7%
4.2 63.9% 70.0%
4.3 63.8% 68.4%
4.4.1 67.3% 69.3%
4.4.2 75.8% 77.9%
4.5 71.4% 75.2%
4.6 69.7% 72.9%
4.7 58.6% 61,1%
Tabela 3. Zużycie RAM
Wnioski:
PostGIS okazał się przyjaźniejszy w użyciu niż MySQL. Użytkownicy prawdopodobnie zauważą
łatwość w obsłudze PostGIS w porównaniu do MySQL, co może przyczynić się do bardziej
intuicyjnego korzystania z funkcji przestrzennych.
Tworzenie indeksów przestrzennych zajmowało więcej czasu w MySQL niż w PostGIS. To
może być kluczowym aspektem, zwłaszcza w przypadku dużych zbiorów danych, gdzie
efektywność tworzenia indeksów przestrzennych jest ważnym kryterium.
Czas wykonania zapytań był dłuższy w MySQL niż w PostGIS. PostGIS, jako dedykowana
rozszerzenie przestrzenne, może oferować zoptymalizowane operacje przestrzenne, co
przekłada się na szybsze wykonywanie zapytań w porównaniu do MySQL.
Zużycie CPU i RAM było niższe w przypadku PostGIS niż w MySQL. To może być kluczowym
czynnikiem dla efektywności systemu, szczególnie w sytuacjach, gdzie zasoby sprzętowe są
ograniczone. PostGIS może oferować lepszą optymalizację w wykorzystaniu zasobów
systemowych.
Podsumowując, w kontekście funkcji przestrzennych, PostGIS okazał się bardziej wydajny i
przyjazny w użyciu niż MySQL. Dłuższy czas tworzenia indeksów i wolniejsze wykonanie
zapytań w MySQL mogą sugerować, że PostGIS jest lepszym wyborem dla projektów z dużym
naciskiem na operacje przestrzenne.

You might also like