Jak zaprojektować rozproszone bazy danych?

Jak zaprojektować rozproszone bazy danych?
G1
  • Rejestracja:około 4 lata
  • Ostatnio:22 dni
  • Postów:504
0

Witam serdecznie. Będę miał do zaprojektowania aplikację internetową typu multitenancy z tą różnicą, że każda organizacja będzie miała swoją własną instancje bazy danych na dane oraz będzie jedna wspólna baza danych hosta. Wszystkie bazy danych organizacji będą w ramach tej samej podsieci. Wszystkie bazy organizacji będą posiadały ten sam schemat. Potrzebuję też móc sprawować kontrolę nad "subskrybentami". Z mojej strony problemem będzie chociażby paginacja z EfCore. Nie widzę teraz innej możliwości jak pobrać wszystkie dane ze wszystkich baz i dopiero wykonywać Skip() i Take() na serwerze. Zdaję sobie sprawę, że przy ilościach rekordów (a będzie ich sporo) to nie przejdzie. Moglibyście mnie naprowadzić?

edytowany 2x, ostatnio: Riddle
SL
  • Rejestracja:około 7 lat
  • Ostatnio:około godziny
  • Postów:862
0

każda organizacja będzie miała swoją własną instancje

Instancję fizyczną czy logiczną? Powiedz też jaka baza

Potrzebuję też móc sprawować kontrolę nad "subskrybentami".

Read czy też write?

edytowany 2x, ostatnio: slsy
RJ
  • Rejestracja:ponad 2 lata
  • Ostatnio:około 7 godzin
  • Postów:427
0

Pisałem sobie coś takiego - miałem jedna scheme i w zależności od tenanta na konfiguracji DBContext podawałem ConnectionStringa.

W hedaderze leciało id tenanta i tak wybierało bazę.
Mam gdzieś na swoim GH chyba.

A co do agregowania to ciekawy temat jest z baz wielu i nie wiem jak to ugryźć. Postgres pozwala chyba na pisanie zapytań do wielu baz ze swojej instancji.

edytowany 1x, ostatnio: rjakubowski
KE
Pozwala, nazywa się to fdw.
ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:7 dni
1

SQL Server ma linked servers. Na podlinkowanych serwerach możesz wykonywać zapytania, włącznie z insert i update, tylko z tego co widzę składnia jest nieco inna. Nie wiem, czy EF sobie z tym poradzi - jeśli nie, to możesz spróbować użyć view lub procedur składowanych (oczywiście jako ostateczność).

Nigdy nie materializuj całych tabel po stronie klienta, to bardzo krótka droga do poważnych problemów z wydajnością. Takie problemy czasami są trywialne, a czasami mogą wymagać przeprojektowania całej infrastruktury, zatem mogą być ekstremalnie kosztowne. Pamiętaj, że początkowe złe decyzje mogą doprowadzić właśnie do tego drugiego, zatem warto zainwestować każdą rozsądną ilość czasu na solidny research.


G1
Wiem, bo już się na tym przejechałem :)
G1
  • Rejestracja:około 4 lata
  • Ostatnio:22 dni
  • Postów:504
0

Mój plan jest mniej więcej taki:
Baza Hosta:

  1. Subskrybenci
  2. Użytkownicy
  3. Słowniki
  4. Inne wspólne
    Baza Subskrybenta:
  5. np. Budynki
  6. np. Pojazdy

Największym wyzwaniem dla mnie będzie przede wszystkim relacja między np. HostDb->Users->Id a TenantDb->Buildings->CreatedUserId. Póki co siedze w google i ludzie piszą, że to niewykonalne ;D

Kolejnym wyzwaniem będzie właśnie pobranie wszystkich np. budynków od wszystkich subskrybentów i paginacja z sortowaniem. Aby posortować budynki najpierw muszę pobrać je wszystkie od wszystkich subskrybentów.

edytowany 1x, ostatnio: gswidwa1
SL
  • Rejestracja:około 7 lat
  • Ostatnio:około godziny
  • Postów:862
0

Czy nie możesz zaimportować wszystkich baz TenantDB co jakiś czas do jednej bazy i na niej wykonywać te query?

M0
  • Rejestracja:ponad 11 lat
  • Ostatnio:około 5 godzin
  • Postów:362
1

A dlaczego potrzebujesz mieć możliwość pobierania danych ze wszystkich baz i wyświetlenia tego w twoim "superadminie"? Czy jest to jakiś konkretny wymóg biznesowy?
Mam parę apek na multitenance i nie potrzebowałem agregować danych ze wszystkich tenantów, aby wyświetlić je na jednej tabelce. Po to mamy to wszystko odseparowane od siebie, aby w kontekście użytkownika były to osobne byty.

edytowany 1x, ostatnio: Michalk001
G1
  • Rejestracja:około 4 lata
  • Ostatnio:22 dni
  • Postów:504
0
Michalk001 napisał(a):

A dlaczego potrzebujesz mieć możliwość pobierania danych ze wszystkich baz i wyświetlenia tego w twoim "superadminie"? Czy jest to jakiś konkretny wymóg biznesowy?
Mam parę apek na multitenance i nie potrzebowałem agregować danych ze wszystkich tenantów, aby wyświetlić je na jednej tabelce.

Na przykład mam listę produktów, które tenant chce aby były widzialne w wyszukiwarce globalnej. wtedy taki landing page na oficjalnej stronie musi zebrać wszystkie produkty od wszystkich subskrybentów, posortować je i wyświetlić n pierwszych rekordów. Normalnie trzymałbym to w jednej bazie, ale chodzi o samą odpowiedzialność za dane

edytowany 1x, ostatnio: gswidwa1
ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:7 dni
2
Michalk001 napisał(a):

A dlaczego potrzebujesz mieć możliwość pobierania danych ze wszystkich baz i wyświetlenia tego w twoim "superadminie"?

W sumie to racja. Problem wydaje się być w innym miejscu - architektura bazy danych jest nieprawidłowa. Jeśli chodzi o odpowiedzialność za dane, to powinieneś mieć logi oraz - w bazie danych - autora danego rekordu + datę i autora ostatniej zmiany + datę (bo jak rozumiem dostęp do danych jest tylko przez API, prawda? Prawda???). Umożliwienie edycji/dodawania tylko konkretnego rodzaju rekordów w zależności od oddziału/klienta firmy jest dość proste do implementacji, natomiast oszczędzi wielu późniejszych problemów.
"Rozpraszanie" bazy danych oznacza:

  1. Duplikację struktury bazy danych. Jeśli będziesz musiał coś zmienić we współdzielonej strukturze, to będziesz to musiał rozpropagować po wszystkich bazach.
  2. Niższe SLA. Jeśli nawet jedna baza danych umarła, to staje cały system.
  3. Najwolniejsza baza danych może stać się wąskim gardłem całego systemu. Namierzenie, która to baza, może być nietrywialne i czasochłonne.
  4. Problemy, które już masz - ogarnięcie tego na poziomie EF może być niewykonalne lub może wymagać stworzenia wielu view i/lub sp, co spowoduje rozproszenie logiki biznesowej, a to wróży tylko kłopoty.
  5. Wyższe koszty utrzymania.

Z powyższych powodów tylko bardzo specyficzne wymagania biznesowe mogą usprawiedliwić użycie takiej "rozproszonej" bazy. Wierz mi, nie chcesz w to wchodzić.


M0
  • Rejestracja:ponad 11 lat
  • Ostatnio:około 5 godzin
  • Postów:362
3
gswidwa1 napisał(a):

Na przykład mam listę produktów, które tenant chce aby były widzialne w wyszukiwarce globalnej

Czyli rozumiem, że to jest jednak jedna organizacja, a nie niezależni klienci jeżeli ma być globalna wyszukiwarka. Od biedy można zrobić to tworząc bazę, gdzie będzie kopia tych produktów, a chcą zobaczyć szczegóły zaciągasz już z konkretnej bazy. W Projektach mam dwa podejścia. Pierwszy to schema per klient(tenant), drugie to wspólna baza i logiczne rozdzielenie na tenanty(kolumna tenantId). W podejściu drugim można bez problemu czytać ze wszystkich tenantów, w końcu dane wszystkich klientów siedzą w jednej tabeli.

gswidwa1 napisał(a):

ale chodzi o samą odpowiedzialność za dane

Co masz na myśli dokładnie? Dodajesz do tego logi, kto co i kiedy zmodyfikował. U siebie trzymam to w tabeli auditLog.

ŁF napisał(a):
  1. Duplikację struktury bazy danych. Jeśli będziesz musiał coś zmienić we współdzielonej strukturze, to będziesz to musiał rozpropagować po wszystkich bazach.

Od biedy można napisać mechanizm migracji, w jednym projekcie mamy to i działa. W innych projektach działam jednak na bazie dokumentowej (postgresql i kolumna data jako json w tabeli), więc problem migracji mi odpada. Apka przy starcie robi synchronizację i jak wykryje, brakujące tabele na podstawie modeli na bazie to tworzy je tabele.

KE
  • Rejestracja:około 6 lat
  • Ostatnio:około 9 godzin
  • Postów:661
2

Normalnie trzymałbym to w jednej bazie, ale chodzi o samą odpowiedzialność za dane

Podejrzewam, że tutaj leży problem. Nie wiem czy robisz w mikroserwisy, czy w monolit, ale tak czy inaczej nie ma żadnego problemu, żeby mieć
a) dane tenantów w osobnych bazach danych z ograniczonym dostępem
b) dane np. do wyszukiwania w jeszcze innej, osobnej bazie danych, należącej do osobnego serwisu albo modułu.

Dane do bazy danych z punktu B wpycha sobie moduł odpowiedzialny za dane z B, np. zaczytując je z systemu kolejkowego, albo zaczytując co godzinę z wszystkich baz danych punktu A, albo na początek nawet przy każdym zapisie, whatever. Masz zachowaną izolację, ale też rozdzieloną odpowiedzialność za dane. Aczkolwiek to też rodzi problemy w stylu "jak usunąć jednego klienta" - drop database nie wystarczy.

edytowany 1x, ostatnio: kelog
markone_dev
  • Rejestracja:około 3 lata
  • Ostatnio:8 dni
  • Postów:809
3

Na przykład mam listę produktów, które tenant chce aby były widzialne w wyszukiwarce globalnej. wtedy taki landing page na oficjalnej stronie musi zebrać wszystkie produkty od wszystkich subskrybentów, posortować je i wyświetlić n pierwszych rekordów.

Nie wiem jaka skala tych wyszukań będzie, ale zwykle robi się usługę typu Wyszukiwarka opartą o jakiś elastic search czy coś podobnego i GUI wyszukiwarki uderza do tej bazy, która zawiera kopię danych z baz tenantów. Oczywiście będziesz mierzył się z duplikacją danych, synchronizacją, świeżością i innymi problemami wynikającymi z trzymania tych samych danych w kilku miejscach, ale tak to już jest. Nie da się zjeść ciastka i mieć ciastka. Dlatego czasem w dużych serwisach e-commerce, sprzedażowych czy magazynowych, może się zdarzyć, że ktoś kupi/zamówi towar, którego już nie mamy na stanie, ale figuruje w bazie jako dostępny.

W przypadku dużego ruchu, robienie zapytań na rozproszonych bazach to strzał w kolano. Ale jeżeli masz 5 wyszukań na minutę to może się nawet sprawdzić.


Programujący korpo architekt chmurowy.
Udzielam konsultacji i szkoleń w obszarze szeroko pojętego cloud computingu (Azure, AWS) i architektury systemów IT. Dla firm i prywatnie.
DevOps to proces nie stanowisko.
edytowany 2x, ostatnio: markone_dev
G1
Posmutniałem jak przeczytałem "Nie da się zjeść ciastka i mieć ciastka" haha
G1
  • Rejestracja:około 4 lata
  • Ostatnio:22 dni
  • Postów:504
0
Michalk001 napisał(a):
gswidwa1 napisał(a):

Na przykład mam listę produktów, które tenant chce aby były widzialne w wyszukiwarce globalnej

Czyli rozumiem, że to jest jednak jedna organizacja, a nie niezależni klienci jeżeli ma być globalna wyszukiwarka. Od biedy można zrobić to tworząc bazę, gdzie będzie kopia tych produktów, a chcą zobaczyć szczegóły zaciągasz już z konkretnej bazy. W Projektach mam dwa podejścia. Pierwszy to schema per klient(tenant), drugie to wspólna baza i logiczne rozdzielenie na tenanty(kolumna tenantId). W podejściu drugim można bez problemu czytać ze wszystkich tenantów, w końcu dane wszystkich klientów siedzą w jednej tabeli.

gswidwa1 napisał(a):

ale chodzi o samą odpowiedzialność za dane

Co masz na myśli dokładnie? Dodajesz do tego logi, kto co i kiedy zmodyfikował. U siebie trzymam to w tabeli auditLog.

ŁF napisał(a):
  1. Duplikację struktury bazy danych. Jeśli będziesz musiał coś zmienić we współdzielonej strukturze, to będziesz to musiał rozpropagować po wszystkich bazach.

Od biedy można napisać mechanizm migracji, w jednym projekcie mamy to i działa. W innych projektach działam jednak na bazie dokumentowej (postgresql i kolumna data jako json w tabeli), więc problem migracji mi odpada. Apka przy starcie robi synchronizację i jak wykryje, brakujące tabele na podstawie modeli na bazie to tworzy je tabele.

Tak. Jest to jedna organizacja w sieci WAN. Już mam jedną apkę postawioną w jednym "Oddziale" z której korzysta wiele "Oddziałów" właśnie na zasadzie TenantId i jednej bazy danych i działa to znakomicie. Jednak Oddział A jest oburzony, że wszystkie "oddziały" korzystają z miejsca na serwerze oddziału A i jest potrzeba aby rozdzielić dane per oddział ;/ Myślałem też o webhookach, oddzielnym API do synchronizacji danych i powielaniu danych subskrybentów, użytkowników itp. na każdy Oddział, ale to nadal nie rozwiązuje problemu takiej prostej paginacji danych ze wszystkich Oddziałów naraz. Ktoś wcześniej wspomniał o widokach w SQL, ale na razie staram się pozostać w EF CORE, ponieważ SQL jest dla mnie czarną magią

markone_dev napisał(a):

Na przykład mam listę produktów, które tenant chce aby były widzialne w wyszukiwarce globalnej. wtedy taki landing page na oficjalnej stronie musi zebrać wszystkie produkty od wszystkich subskrybentów, posortować je i wyświetlić n pierwszych rekordów.

Nie wiem jaka skala tych wyszukań będzie, ale zwykle robi się usługę typu Wyszukiwarka opartą o jakiś elastic search czy coś podobnego i GUI wyszukiwarki uderza do tej bazy, która zawiera kopię danych z baz tenantów. Oczywiście będziesz mierzył się z duplikacją danych, synchronizacją, świeżością i innymi problemami wynikającymi z trzymania tych samych danych w kilku miejscach, ale tak to już jest. Nie da się zjeść ciastka i mieć ciastka. Dlatego czasem w dużych serwisach e-commerce, sprzedażowych czy magazynowych, może się zdarzyć, że ktoś kupi/zamówi towar, którego już nie mamy na stanie, ale figuruje w bazie jako dostępny.

W przypadku dużego ruchu, robienie zapytań na rozproszonych bazach to strzał w kolano. Ale jeżeli masz 5 wyszukań na minutę to może się nawet sprawdzić.

Czyli tak czy siak aby wyszukiwać globalnie muszę mieć "kopię bazy", która gdzieś będzie sobie leżeć na serwerze. Może jak tak to przedstawię to odwiodę ich od pomysłu wielu baz danych

edytowany 1x, ostatnio: cerrato
Qbelek
  • Rejestracja:prawie 6 lat
  • Ostatnio:około 4 godziny
  • Postów:102
0

Czemu nie zrobisz schema per user na jednej instancji bazy? Wtedy możesz sobie pisać zapytania zbierające dane że wszystkich i masz zachowaną izolacje danych klientów.

markone_dev
  • Rejestracja:około 3 lata
  • Ostatnio:8 dni
  • Postów:809
0
Qbelek napisał(a):

Czemu nie zrobisz schema per user na jednej instancji bazy? Wtedy możesz sobie pisać zapytania zbierające dane że wszystkich i masz zachowaną izolacje danych klientów.

Niektórzy klienci wymagają fizycznej izolacji danych.

gswidwa1 napisał(a):

Czyli tak czy siak aby wyszukiwać globalnie muszę mieć "kopię bazy", która gdzieś będzie sobie leżeć na serwerze. Może jak tak to przedstawię to odwiodę ich od pomysłu wielu baz danych

Nie do końca. Owszem możesz mieć osobną bazę i dedykowany serwis do wyszukiwania, ale możesz też to robić na rozproszonych bazach danych. Pamiętaj tylko, że w przypadku dużego ruchu takie zapytania mogą okazać się wąskim gardłem twojego systemu. To już musisz Ty zadecydować na podstawie przewidywanego ruchu i ilości danych.

Czasem searche na jednej bazie danych są problematyczne, bo ktoś skopał schemat i trzeba robić pokraczne zapytania by to ogarnąć, albo brakuje indeksów, albo serwer nie wyrabia. Teraz to pomnóż razy 5 albo dziesięć, w zależności ilu będziesz miał tenantów.


Programujący korpo architekt chmurowy.
Udzielam konsultacji i szkoleń w obszarze szeroko pojętego cloud computingu (Azure, AWS) i architektury systemów IT. Dla firm i prywatnie.
DevOps to proces nie stanowisko.
G1
Tenantów będzie stała ilość - 3 ;D
G1
  • Rejestracja:około 4 lata
  • Ostatnio:22 dni
  • Postów:504
0
markone_dev napisał(a):
Qbelek napisał(a):

Czemu nie zrobisz schema per user na jednej instancji bazy? Wtedy możesz sobie pisać zapytania zbierające dane że wszystkich i masz zachowaną izolacje danych klientów.

Niektórzy klienci wymagają fizycznej izolacji danych.

gswidwa1 napisał(a):

Czyli tak czy siak aby wyszukiwać globalnie muszę mieć "kopię bazy", która gdzieś będzie sobie leżeć na serwerze. Może jak tak to przedstawię to odwiodę ich od pomysłu wielu baz danych

Nie do końca. Owszem możesz mieć osobną bazę i dedykowany serwis do wyszukiwania, ale możesz też to robić na rozproszonych bazach danych. Pamiętaj tylko, że w przypadku dużego ruchu takie zapytania mogą okazać się wąskim gardłem twojego systemu. To już musisz Ty zadecydować na podstawie przewidywanego ruchu i ilości danych.

Czasem searche na jednej bazie danych są problematyczne, bo ktoś skopał schemat i trzeba robić pokraczne zapytania by to ogarnąć, albo brakuje indeksów, albo serwer nie wyrabia. Teraz to pomnóż razy 5 albo dziesięć, w zależności ilu będziesz miał tenantów.

Co mi sie udało wykminić to faktycznie 3 zapytania do trzech tenantów i otrzymanie x wyników razy 3, potem na nowo przefiltrowanie, posortowanie, paginacja i mamy 10 rekordów z 3 baz. I to nawet sprawnie działa (co prawda 3 razy wolniej niż mogłoby i też ostatnia operacja zużywa zasobu serwera ale tylko na 30 rekordach). Co sądzicie?:

Kopiuj
var filteredDb1 = await _dbContext1.Filter(input).Page(0, 10);
var filteredDb2 = await _dbContext2.Filter(input).Page(0, 10);
var filteredDb3 = await _dbContext3.Filter(input).Page(0, 10);
var filtered = await filterAndPage(0, 10, filteredDb1, filteredDb2, filteredDb3)
markone_dev
  • Rejestracja:około 3 lata
  • Ostatnio:8 dni
  • Postów:809
2

Ja sądzę, że przy małej skali zapytań i danych takie podejście się sprawdzi.

Co będzie za rok czy dwa to tego nie wiem, ale zakładam, że Ty lub Twój biznes to wiecie bo macie jakąś strategię biznesową i możecie podjąć decyzję, czy zaczynacie od najprostszego rozwiązania, np. takiego jakie podałeś, a jak się okaże w pewnym momencie że jest niewydajne to go odpowiednio przerobicie. Albo od razu idziecie z osobną usługą do wyszukiwania, żeby później już nie musieć tego rozgrzebywać.


Programujący korpo architekt chmurowy.
Udzielam konsultacji i szkoleń w obszarze szeroko pojętego cloud computingu (Azure, AWS) i architektury systemów IT. Dla firm i prywatnie.
DevOps to proces nie stanowisko.
ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:7 dni
3
gswidwa1 napisał(a):
Kopiuj
var filteredDb1 = await _dbContext1.Filter(input).Page(0, 10);
var filteredDb2 = await _dbContext2.Filter(input).Page(0, 10);
var filteredDb3 = await _dbContext3.Filter(input).Page(0, 10);
var filtered = await filterAndPage(0, 10, filteredDb1, filteredDb2, filteredDb3)

A teraz wyobraź sobie, co będzie na drugiej stronie. Albo ostatniej :D Będziesz musiał zmaterializować wszystkie poprzednie strony. Powodzenia.
Co się stanie, jeśli dojdzie czwarta baza?
Serio, nie tędy droga. Ktoś w firmie ubzdurał sobie, że skoro są trzy oddziały, to każdy ma mieć osobną infrastrukturę i każda wspólna aplikacja musi korzystać z tych trzech części. To bardzo komplikuje proste sprawy. W efekcie utrzymanie wszystkiego zacznie więcej kosztować, a sam koszt infrastruktury już poszedł w górę, skoro są trzy bazy, a może być jedna.

Jednak Oddział A jest oburzony, że wszystkie "oddziały" korzystają z miejsca na serwerze oddziału A i jest potrzeba aby rozdzielić dane per oddział

Skoro to dane wspólne, to powinieneś mieć na to osobny worek, jakiś global albo shared. Najwyraźniej infrastrukturę też macie źle zaprojektowaną, co teraz rodzi kolejne problemy, które później zrodzą kolejne problemy. Wspólne rzeczy do wspólnego worka, company branch specific - do company branch bags. Oddzielny tenant na każdy oddział też mi śmierdzi - u mnie w korpo jest grubo ponad 5k ludzi, kilka oddziałów rozrzuconych po całym świecie, kilka przejętych mniejszych firm razem z ich infrastrukturami, wszyscy są migrowani do jednego tenanta, jednego SSO.

Najczęściej takie głupie decyzje są podejmowane przez ludzi, którzy nie mają pełnej świadomości z jakimi konsekwencjami ich decyzje się wiążą. Jeśli są to w miarę rozsądni ludzie, to jeszcze nie ma problemu, trzeba taką decyzyjną osobę namierzyć i wszystko dokładnie wytłumaczyć (co oznacza, że najpierw implikacje trzeba zrozumieć samemu + zrobić research możliwych rozwiązań i ich plusów i minusów). Skoro jednak decyzja jest głupia, to jest szansa, że podjął ją głupi człowiek, któremu nie wytłumaczysz, że zrobił źle, bo się obrazi (vide błędy poznawcze). Wtedy problem trzeba eskalować, może przełożony tej osoby będzie rozsądniejszy. Jeśli wasze systemy informatyczne są istotną częścią działalności firmy, to może warto udać się na samą górę, do CTO, CEO albo zarządu, żeby zastanowili się nad inwestycją w sensowną architekturę infrastruktury - będzie taniej i na abonamencie, i na utrzymaniu, i na deweloperce.

Naprawdę, wierz mi, że nie chcesz iść w duplikowanie czegokolwiek (poza backupami, RAID albo klastrami ;-)). Mam wrażenie, że sam nie rozumiesz implikacji, patrząc na Twoje rozwiązanie problemu wyszukiwania na trzech bazach. Jeszcze raz, w skrócie: 1) koszty infrastruktury 2) niższe SLA 3) zduplikowana struktura = więcej roboty = wyższe koszty utrzymania 4) wąskie gardła 5) więcej błędów trudnych do naprawienia/obejścia = większe koszty, do tego słabszy UX, bo mniejsza wygoda pracy. Money, money, money.


flinst-one
Dokładnie. Taniej wyjdzie przenieść wszystko na dzielony serwer (niech każdy dział się zrzuca na utrzymanie).
ŁF
@flinst-one: prawda płynie z Twoich ust.
SL
  • Rejestracja:około 7 lat
  • Ostatnio:około godziny
  • Postów:862
0
gswidwa1 napisał(a):

(co prawda 3 razy wolniej niż mogłoby

Czemu nie wywołać tych query równolegle? Nie znam się na C#, ale to wygląda dobrze: https://stackoverflow.com/a/25010220/4638604

Co sądzicie?:

Paging będzie ciężki. Co zrobisz jak kolejność zwróconych itemóœ będzie taka dla konkretnego query: tenant1 x 1000 rows, tenant2 x 1 row, tenant3 x 1000rows. W takim wypadku Page(1, 10) na wszystkich bazach da błędne wyniki

Wszystko zależy ile będzie tych zapytań wykonywanych i jak będzie wyglądał rozwój tego ficzera w przyszłości. Przy większej skali trzymanie tych danych w jednym miejscu to jedyny sposóbn na utrzymanie zdrowia psychicznego

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)