วิธีติดตั้งระบบแท็ก


90

ฉันสงสัยว่าวิธีที่ดีที่สุดคือการติดตั้งระบบแท็กเช่นเดียวกับที่ใช้กับ SO ฉันกำลังคิดถึงสิ่งนี้ แต่ไม่สามารถหาโซลูชันที่ปรับขนาดได้ดีได้

ฉันกำลังคิดว่าจะมีวิธีแก้ปัญหาพื้นฐาน 3 ตารางคือมีtagsโต๊ะarticlesโต๊ะและtag_to_articlesโต๊ะ

นี่เป็นทางออกที่ดีที่สุดสำหรับปัญหานี้หรือมีทางเลือกอื่นหรือไม่? เมื่อใช้วิธีนี้ตารางจะมีขนาดใหญ่มากและสำหรับการค้นหาสิ่งนี้ไม่ได้มีประสิทธิภาพมากเกินไปฉันคิดว่า ในทางกลับกันไม่สำคัญที่การสืบค้นจะดำเนินการอย่างรวดเร็ว


คำตอบ:


119

ฉันเชื่อว่าคุณจะพบว่าโพสต์บล็อกนี้น่าสนใจ: แท็ก: สคีมาฐานข้อมูล

ปัญหา: คุณต้องการมีสคีมาฐานข้อมูลที่คุณสามารถแท็กบุ๊กมาร์ก (หรือโพสต์บล็อกหรืออะไรก็ได้) ด้วยแท็กได้มากเท่าที่คุณต้องการ หลังจากนั้นคุณต้องการเรียกใช้การสืบค้นเพื่อ จำกัด บุ๊กมาร์กให้รวมกันหรือตัดกันของแท็ก คุณยังต้องการยกเว้น (พูดว่า: ลบ) บางแท็กจากผลการค้นหา

โซลูชัน“ MySQLicious”

ในโซลูชันนี้สคีมามีเพียงตารางเดียวมันถูกทำให้เป็นปกติ ประเภทนี้เรียกว่า“ MySQLicious solution” เนื่องจาก MySQLicious นำเข้าข้อมูล del.icio.us ลงในตารางที่มีโครงสร้างนี้

ป้อนคำอธิบายภาพที่นี่ป้อนคำอธิบายภาพที่นี่

จุดตัด (AND) คำค้นหาสำหรับ“ search + webservice + semweb”:

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags LIKE "%semweb%"

ยูเนี่ยน (OR) เคียวรีสำหรับ“ ค้นหา | บริการเว็บ | semweb”:

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
OR tags LIKE "%webservice%"
OR tags LIKE "%semweb%"

Minus Query สำหรับ "search + webservice-semweb"

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags NOT LIKE "%semweb%"

โซลูชัน "Scuttle"

Scuttleจัดระเบียบข้อมูลเป็นสองตาราง ตารางนั้น“ scCategories” คือ“ แท็ก” - ตารางและมีคีย์นอกสำหรับ“ บุ๊กมาร์ก” - ตาราง

ป้อนคำอธิบายภาพที่นี่

จุดตัด (AND) คำค้นหาสำหรับ“ bookmark + webservice + semweb”:

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId
HAVING COUNT( b.bId )=3

ขั้นแรกให้ค้นหาชุดค่าผสมบุ๊กมาร์ก - แท็กทั้งหมดโดยที่แท็กคือ "บุ๊กมาร์ก", "บริการเว็บ" หรือ "เซมเว็บ" (c.category IN ('bookmark', 'webservice', 'semweb')) จากนั้นก็แค่บุ๊กมาร์กที่ ได้รับทั้งสามแท็กที่ค้นหาถูกนำมาพิจารณา (HAVING COUNT (b.bId) = 3)

Union (OR) Query สำหรับ "bookmark | webservice | semweb": เพียงแค่ละเว้น HAVING clause และคุณมี union

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId

เครื่องหมายลบ (การยกเว้น) ข้อความค้นหาสำหรับ "bookmark + webservice-semweb" นั่นคือ: บุ๊กมาร์กและบริการเว็บและไม่ใช่ semweb

SELECT b. *
FROM scBookmarks b, scCategories c
WHERE b.bId = c.bId
AND (c.category IN ('bookmark', 'webservice'))
AND b.bId NOT
IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = 'semweb')
GROUP BY b.bId
HAVING COUNT( b.bId ) =2

การออกจาก HAVING COUNT จะนำไปสู่การค้นหา "bookmark | webservice-semweb"


วิธีแก้ปัญหา“ Toxi”

Toxiมาพร้อมกับโครงสร้างสามโต๊ะ ผ่านตาราง "tagmap" บุ๊กมาร์กและแท็กเกี่ยวข้องกันแบบ n-to-m แต่ละแท็กสามารถใช้ร่วมกับบุ๊กมาร์กที่แตกต่างกันและในทางกลับกัน DB-schema นี้ยังใช้โดย wordpress ข้อความค้นหาค่อนข้างเหมือนกับในโซลูชัน "scuttle"

ป้อนคำอธิบายภาพที่นี่

จุดตัด (AND) คำค้นหาสำหรับ“ bookmark + webservice + semweb”

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3

ยูเนี่ยน (OR) การค้นหา "บุ๊กมาร์ก | เว็บบริการ | semweb"

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id

เครื่องหมายลบ (การยกเว้น) ข้อความค้นหาสำหรับ "bookmark + webservice-semweb" นั่นคือ: บุ๊กมาร์กและบริการเว็บและไม่ใช่ semweb

SELECT b. *
FROM bookmark b, tagmap bt, tag t
WHERE b.id = bt.bookmark_id
AND bt.tag_id = t.tag_id
AND (t.name IN ('Programming', 'Algorithms'))
AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = 'Python')
GROUP BY b.id
HAVING COUNT( b.id ) =2

การออกจาก HAVING COUNT จะนำไปสู่การค้นหา "bookmark | webservice-semweb"


3
ผู้เขียนบล็อกโพสต์ที่นี่ Chrome ไม่ได้บล็อกบล็อกอีกต่อไป (ช่องโหว่ wordpress โง่ ๆ ย้ายไปที่ tumblr แล้ว) ความรุ่งโรจน์ในการแปลงร่างเป็น markdown
hansaplast

สวัสดี @Philipp ตกลงแก้ไขคำตอบของฉัน BTW ขอบคุณสำหรับโพสต์ที่ยอดเยี่ยมเกี่ยวกับระบบแท็กฐานข้อมูล
Nick Dandoulakis

1
หมายเหตุ: หากคุณต้องการให้ Intersection Query สำหรับโซลูชัน Toxi แสดงบุ๊กมาร์กด้วยหากคุณค้นหา "bookmark" AND "webservice" คุณจะต้องเปลี่ยน "HAVING COUNT (b.id) = 3" จาก 3 ถึง "sizeof (array ('bookmark', 'webservice'))" เป็นเพียงรายละเอียดเล็กน้อยหากคุณวางแผนที่จะใช้สิ่งนี้เป็นฟังก์ชันการสืบค้นแท็กแบบไดนามิก
toxicate 20

3
ลิงก์ใด ๆ สำหรับการเปรียบเทียบประสิทธิภาพสำหรับโซลูชันต่างๆที่กล่าวถึงในโพสต์
kampta

@kampta ครับผมไม่มีลิงค์
Nick Dandoulakis

8

ไม่มีอะไรผิดปกติกับโซลูชันสามตารางของคุณ

อีกทางเลือกหนึ่งคือ จำกัด จำนวนแท็กที่สามารถใช้กับบทความได้ (เช่น 5 ใน SO) และเพิ่มแท็กเหล่านั้นลงในตารางบทความของคุณโดยตรง

การปรับฐานข้อมูลให้เป็นมาตรฐานมีข้อดีและข้อเสียเช่นเดียวกับการเดินสายไฟเข้าในตารางเดียวก็มีประโยชน์และข้อเสีย

ไม่มีอะไรบอกว่าคุณทำทั้งสองอย่างไม่ได้ มันขัดกับกระบวนทัศน์ DB เชิงสัมพันธ์ในการทำซ้ำข้อมูล แต่ถ้าเป้าหมายคือประสิทธิภาพคุณอาจต้องทำลายกระบวนทัศน์


ใช่การใส่แท็กลงในตารางบทความโดยตรงจะเป็นตัวเลือกแม้ว่าวิธีนี้จะมีข้อเสียอยู่บ้างก็ตาม หากคุณเก็บแท็ก 5 แท็กไว้ในช่องที่คั่นด้วยเครื่องหมายจุลภาคเช่น (tag1,2,3,4) นี่จะเป็นวิธีที่ง่าย คำถามคือหากการค้นหาจะดำเนินไปเร็วกว่านี้ ตัวอย่างเช่นมีคนต้องการดูทุกอย่างด้วย tag1 คุณต้องดูตารางบทความทั้งหมด ซึ่งจะน้อยกว่าจากนั้นจะไปตามตาราง tag_to_article แต่แล้วอีกครั้งตาราง tags_to_article นั้นบางกว่า อีกอย่างคือคุณต้องระเบิดทุกครั้งใน php ฉันไม่รู้ว่าต้องใช้เวลาหรือเปล่า
Saif Bechan

หากคุณทำทั้งสองอย่าง (แท็กที่มีบทความและในตารางแยกกัน) สิ่งนี้จะช่วยให้คุณมีประสิทธิภาพทั้งสำหรับการค้นหาหลังจุดศูนย์กลางและการค้นหาที่เน้นแท็ก การแลกเปลี่ยนเป็นภาระในการรักษาข้อมูลที่ทำซ้ำ นอกจากนี้โดยการ จำกัด จำนวนแท็กคุณสามารถใส่แต่ละแท็กลงในคอลัมน์ของตัวเองได้ เพียงแค่เลือก * จากบทความ Where XXXXX และไป; ไม่จำเป็นต้องระเบิด
John

6

การติดตั้งตารางสามตารางที่คุณเสนอจะใช้ได้ผลกับการติดแท็ก

Stack overflow ใช้อย่างไรก็ตามการใช้งานที่แตกต่างกัน พวกเขาเก็บแท็กไว้ในคอลัมน์ varchar ในตารางโพสต์ในรูปแบบข้อความธรรมดาและใช้การจัดทำดัชนีข้อความแบบเต็มเพื่อดึงโพสต์ที่ตรงกับแท็ก ตัวอย่างเช่นposts.tags = "algorithm system tagging best-practices". ฉันแน่ใจว่าเจฟฟ์พูดถึงเรื่องนี้ที่ไหนสักแห่ง แต่ฉันลืมไปแล้วว่าอยู่ที่ไหน


4
ดูเหมือนว่าไม่มีประสิทธิภาพมาก สิ่งที่เกี่ยวกับการสั่งซื้อแท็ก? หรือแท็กที่เกี่ยวข้อง? (เช่น "กระบวนการ" คล้ายกับ "อัลกอริทึม" หรือสิ่งที่คล้ายกัน)
Richard Duerr

3

วิธีแก้ปัญหาที่เสนอเป็นวิธีที่ดีที่สุดหากไม่ใช่วิธีเดียวที่ทำได้เท่านั้นที่ฉันคิดได้เพื่อจัดการกับความสัมพันธ์แบบกลุ่มต่อกลุ่มระหว่างแท็กและบทความ ดังนั้นการโหวตของฉันคือ 'ใช่มันยังดีที่สุด' ฉันสนใจทางเลือกอื่น ๆ


ฉันเห็นด้วย. แท็กและตารางแท็กแมปเหล่านี้มีขนาดบันทึกที่เล็กและเมื่อจัดทำดัชนีอย่างถูกต้องไม่ควรลดประสิทธิภาพลงอย่างมาก การ จำกัด จำนวนแท็ก od ต่อรายการอาจเป็นความคิดที่ดี
PanJanek

2

หากฐานข้อมูลของคุณรองรับอาร์เรย์ที่จัดทำดัชนีได้ (เช่น PostgreSQL เป็นต้น) ฉันขอแนะนำโซลูชันที่ถูกทำให้เป็นมาตรฐานโดยสิ้นเชิง - จัดเก็บแท็กเป็นอาร์เรย์ของสตริงในตารางเดียวกัน มิฉะนั้นการแมปตารางรองออบเจ็กต์กับแท็กเป็นทางออกที่ดีที่สุด หากคุณต้องการจัดเก็บข้อมูลเพิ่มเติมกับแท็กคุณสามารถใช้ตารางแท็กแยกกันได้ แต่ไม่มีประเด็นใดที่จะแนะนำการรวมครั้งที่สองสำหรับการค้นหาแท็กทุกครั้ง


POstgreSQL รองรับเฉพาะดัชนีในอาร์เรย์จำนวนเต็ม: postgresql.org/docs/current/static/intarray.html
Mike Chamberlain

1
Nowadys รองรับข้อความด้วย: postgresql.org/docs/9.6/static/arrays.html
luckydonald

2

ฉันอยากจะแนะนำให้ปรับ MySQLicious เพื่อประสิทธิภาพที่ดีขึ้น ก่อนหน้านั้นข้อเสียของการแก้ปัญหา Toxi (3 table) คือ

หากคุณมีคำถามหลายล้านข้อและมี 5 แท็กในแต่ละแท็กจะมี 5 ล้านรายการในตารางแท็กแมป ก่อนอื่นเราต้องกรองรายการแท็กแมป 10,000 รายการตามการค้นหาแท็กจากนั้นกรองคำถามที่ตรงกันของ 10,000 รายการเหล่านั้นอีกครั้ง ดังนั้นในขณะที่กรองออกว่า artical id เป็นตัวเลขธรรมดาก็ใช้ได้ แต่ถ้าเป็น UUID (32 varchar) การกรองออกจำเป็นต้องมีการเปรียบเทียบที่มากขึ้นแม้ว่าจะมีการจัดทำดัชนีก็ตาม

วิธีแก้ปัญหาของฉัน:

เมื่อใดก็ตามที่สร้างแท็กใหม่ให้มีตัวนับ ++ (ฐาน 10) และแปลงตัวนับนั้นเป็นฐาน 64 ตอนนี้ชื่อแท็กแต่ละชื่อจะมี id base64 และส่งรหัสนี้ไปยัง UI พร้อมกับชื่อ ด้วยวิธีนี้คุณจะมีรหัสถ่านสูงสุดสองรหัสจนกว่าเราจะสร้างแท็ก 4095 ในระบบของเรา ตอนนี้เชื่อมหลายแท็กเหล่านี้เข้าด้วยกันในคอลัมน์แท็กตารางคำถามแต่ละคอลัมน์ เพิ่มตัวคั่นด้วยและจัดเรียง

ตารางจะเป็นแบบนี้

ป้อนคำอธิบายภาพที่นี่

ในขณะที่ค้นหาให้ค้นหา ID แทนชื่อแท็กจริง เพราะมันเป็นเรียง , andเงื่อนไขการแท็กจะมีประสิทธิภาพมากขึ้น ( LIKE '%|a|%|c|%|f|%)

โปรดทราบว่าตัวคั่นช่องว่างเดียวไม่เพียงพอและเราต้องใช้ตัวคั่นสองครั้งเพื่อแยกความแตกต่างของแท็กเช่นsqlและmysqlเนื่องจากLIKE "%sql%"จะส่งคืนmysqlผลลัพธ์เช่นกัน ควรจะเป็นLIKE "%|sql|%"

ฉันรู้ว่าการค้นหาไม่ได้รับการจัดทำดัชนี แต่คุณยังอาจจัดทำดัชนีในคอลัมน์อื่น ๆ ที่เกี่ยวข้องกับบทความเช่นผู้เขียน / dateTime else จะนำไปสู่การสแกนแบบเต็มตาราง

ในที่สุดด้วยโซลูชันนี้ไม่จำเป็นต้องมีการรวมภายในซึ่งจะต้องมีการเปรียบเทียบล้านระเบียนกับ 5 ล้านระเบียนในเงื่อนไขการเข้าร่วม


ทีมงานโปรดให้ข้อมูลของคุณเกี่ยวกับข้อเสียของโซลูชันนี้ในความคิดเห็น
Kanagavelu Sugumar

@Nick Dandoulakis โปรดช่วยฉันด้วยการแสดงความคิดเห็นของคุณเกี่ยวกับโซลูชันข้างต้นจะได้ผลหรือไม่?
Kanagavelu Sugumar

@Juha Syrjäläวิธีแก้ปัญหาข้างต้นดีไหม
Kanagavelu Sugumar

0
CREATE TABLE Tags (
    tag VARHAR(...) NOT NULL,
    bid INT ... NOT NULL,
    PRIMARY KEY(tag, bid),
    INDEX(bid, tag)
)

หมายเหตุ:

  • สิ่งนี้ดีกว่า TOXI ตรงที่ไม่ต้องผ่านหลาย ๆ ตารางเพิ่มเติมซึ่งทำให้การเพิ่มประสิทธิภาพเป็นเรื่องยาก
  • แน่นอนว่าแนวทางของฉันอาจจะใหญ่กว่าเล็กน้อย (กว่า TOXI) เนื่องจากแท็กซ้ำซ้อน แต่นั่นเป็นเปอร์เซ็นต์เล็กน้อยของฐานข้อมูลทั้งหมดและการปรับปรุงประสิทธิภาพอาจมีนัยสำคัญ
  • สามารถปรับขนาดได้สูง
  • ไม่มี (เพราะไม่จำเป็นต้องมี) AUTO_INCREMENTPK ตัวแทน ดังนั้นจึงดีกว่า Scuttle
  • MySQLicious ดูดเนื่องจากไม่สามารถใช้ดัชนีได้ ( LIKEด้วยไวด์การ์ดชั้นนำการเข้าชมที่ผิดในสตริงย่อย)
  • สำหรับ MySQL อย่าลืมใช้ ENGINE = InnoDB เพื่อรับเอฟเฟกต์ 'การจัดกลุ่ม'

การสนทนาที่เกี่ยวข้อง (สำหรับ MySQL):
หลายรายการสั่งซื้อการเพิ่มประสิทธิภาพตารางการแมปหลาย
รายการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.