Өнөөдөр SQL дээр нарийн төвөгтэй тохиолдол, нарийн алгоритм байхгүй болно. Бүх зүйл маш энгийн, ахмад Обвиусийн түвшинд байх болно - үүнийг хийцгээе үйл явдлын бүртгэлийг үзэх цаг хугацаагаар эрэмбэлсэн.
Өөрөөр хэлбэл, мэдээллийн санд тэмдэг байдаг events
, мөн түүнд талбай бий ts
- бид эдгээр бүртгэлийг эмх цэгцтэй харуулахыг хүсч буй яг цаг хугацаа:
CREATE TABLE events(
id
serial
PRIMARY KEY
, ts
timestamp
, data
json
);
CREATE INDEX ON events(ts DESC);
Бидэнд хэдэн арван бичлэг байхгүй нь тодорхой тул бидэнд ямар нэг хэлбэр хэрэгтэй болно хуудасны навигаци.
#0. "Би ээжийнхээ погромист"
cur.execute("SELECT * FROM events;")
rows = cur.fetchall();
rows.sort(key=lambda row: row.ts, reverse=True);
limit = 26
print(rows[offset:offset+limit]);
Энэ нь бараг хошигнол биш юм - энэ нь ховор, гэхдээ зэрлэг байгальд олддог. Заримдаа ORM-тэй ажилласны дараа SQL-тэй "шууд" ажил руу шилжихэд хэцүү байдаг.
Гэхдээ илүү нийтлэг, тодорхой бус асуудлууд руу шилжье.
#1. ОФСЕТ
SELECT
...
FROM
events
ORDER BY
ts DESC
LIMIT 26 OFFSET $1; -- 26 - записей на странице, $1 - начало страницы
26 гэдэг тоо хаанаас ирсэн бэ? Энэ нь нэг дэлгэцийг дүүргэх оролтын ойролцоогоор тоо юм. Нарийвчлан хэлэхэд, 25 бичлэг, дээр нь 1 нь түүвэрт дор хаяж өөр зүйл байгаа бөгөөд цааш явах нь утга учиртай болохыг харуулж байна.
Мэдээжийн хэрэг, энэ утгыг хүсэлтийн үндсэн хэсэгт "оёж" болохгүй, гэхдээ параметрээр дамжуулна. Гэхдээ энэ тохиолдолд PostgreSQL төлөвлөгч нь харьцангуй цөөн бүртгэл байх ёстой гэсэн мэдлэгт найдах боломжгүй бөгөөд үр дүнгүй төлөвлөгөөг амархан сонгох болно.
Програмын интерфэйс дээр бүртгэлийг үзэх нь харааны "хуудсууд" хооронд шилжих байдлаар хэрэгждэг боловч удаан хугацааны туршид хэн ч сэжигтэй зүйлийг анзаардаггүй. Тохиромжтой байдлын төлөөх тэмцэлд UI/UX интерфэйсийг "эцэс төгсгөлгүй гүйлгэх" болгон өөрчлөх шийдвэр гаргах хүртэл, өөрөөр хэлбэл бүртгэлийн бүх оруулгуудыг хэрэглэгч дээш доош гүйлгэж болох нэг жагсаалтад оруулсан болно.
Тиймээс дараагийн шалгалтын үеэр та баригдсан бүртгэлийн давхардал бүртгэлд. Яагаад гэвэл хүснэгт нь ердийн индекстэй байдаг (ts)
, таны асуулга юунд тулгуурладаг вэ?
Та үүнийг анхаарч үзээгүй учраас ts
өвөрмөц түлхүүр биш юм энэ хүснэгтэд. Үнэндээ, мөн түүний үнэ цэнэ нь өвөрмөц биш юм, бодит нөхцөл дэх аливаа "цаг" шиг - иймээс хоёр зэргэлдээх асуулга дахь ижил бичлэг нь ижил түлхүүр утгыг эрэмбэлэх хүрээнд өөр эцсийн дарааллаас болж хуудаснаас хуудас руу амархан "үсрэх" болно.
Үнэндээ энд бас хоёрдахь асуудал нуугдаж байгаа бөгөөд үүнийг анзаарахад илүү хэцүү байдаг - зарим оруулгууд харагдахгүй бүх! Эцсийн эцэст, "давхардсан" бүртгэлүүд өөр хэн нэгний байрыг эзэлсэн. Сайхан зургуудтай дэлгэрэнгүй тайлбарыг олж болно
Индексийг өргөжүүлж байна
Зальтай хөгжүүлэгч индексийн түлхүүрийг өвөрмөц болгох хэрэгтэй гэдгийг ойлгодог бөгөөд хамгийн хялбар арга бол үүнийг PK-д тохирсон өвөрмөц талбараар өргөжүүлэх явдал юм:
CREATE UNIQUE INDEX ON events(ts DESC, id DESC);
Мөн хүсэлт нь өөрчлөгддөг:
SELECT
...
ORDER BY
ts DESC, id DESC
LIMIT 26 OFFSET $1;
#2. "Курсор" руу шилжих
Хэсэг хугацааны дараа DBA тан дээр ирж, таны хүсэлтэд "сэтгэл хангалуун" байна
SELECT
...
WHERE
(ts, id) < ($1, $2) -- последние полученные на предыдущем шаге значения
ORDER BY
ts DESC, id DESC
LIMIT 26;
Ирэх хүртлээ та санаа амарсан...
#3. Цэвэрлэх индексүүд
Учир нь нэг өдөр таны DBA уншсан (ts DESC)
.
Гэхдээ хуудсуудын хооронд бичлэгүүдийг "үсрэх" анхны асуудлыг яах вэ? .. Бүх зүйл энгийн - та тодорхойгүй тооны бичлэг бүхий блокуудыг сонгох хэрэгтэй!
Ер нь “яг 26” биш “26-аас доошгүй” гэж уншихыг хэн хориглодог юм бэ? Жишээлбэл, дараагийн блокт байгаа тул илт өөр өөр утгатай бичлэгүүд ts
- тэгвэл блокуудын хооронд бичлэгүүдийг "үсрэх" асуудал гарахгүй!
Үүнд хэрхэн хүрэх вэ:
SELECT
...
WHERE
ts < $1 AND
ts >= coalesce((
SELECT
ts
FROM
events
WHERE
ts < $1
ORDER BY
ts DESC
LIMIT 1 OFFSET 25
), '-infinity')
ORDER BY
ts DESC;
Энд юу болоод байна?
- Бид 25 бичлэгийг "доошоо" алхаж, "хил" утгыг авна
ts
. - Хэрэв тэнд юу ч байхгүй бол NULL утгыг орлуулаарай
-infinity
. - Бид хүлээн авсан утгын хоорондох утгын сегментийг бүхэлд нь хасдаг
ts
болон интерфэйсээс дамжуулсан $1 параметр (өмнөх "сүүлийн" үзүүлсэн утга). - Хэрэв блок 26-аас бага бичлэгтэй буцаж ирвэл энэ нь сүүлчийнх юм.
Эсвэл ижил зураг:
Учир нь одоо бидэнд байгаа түүвэрт тодорхой "эхлэл" байхгүй байна, тэгвэл энэ хүсэлтийг эсрэг чиглэлд "өргөжүүлж", "лавлагаа цэг" -ээс доош болон дээш хоёр чиглэлд өгөгдлийн блокуудыг динамик ачааллахад юу ч саад болохгүй.
Тэмдэглэл
- Тийм ээ, энэ тохиолдолд бид индекс рүү хоёр удаа ханддаг, гэхдээ бүх зүйл "цэвэр индексээр" байна. Тиймээс, дэд асуулга нь зөвхөн үр дүнд хүрэх болно Зөвхөн нэг индексийн скан хийх.
- Энэ техникийг зөвхөн үнэт зүйлтэй үед л ашиглах нь ойлгомжтой
ts
зөвхөн тохиолдлоор гаталж болно, мөн тийм ч олон биш. Хэрэв таны ердийн тохиолдол "00:00:00.000 дахь сая бичлэг" бол та үүнийг хийх ёсгүй. Ийм хэрэг гарахыг зөвшөөрөх ёсгүй гэж хэлэх гэсэн юм. Гэхдээ ийм зүйл тохиолдвол өргөтгөсөн индекс бүхий сонголтыг ашиглана уу.
Эх сурвалж: www.habr.com