หน้าแรก / บทความ / พื้นฐานเว็บ

ทำไมกดบัตรแล้วเว็บล่ม? ลองดูคอขวด ล็อกที่นั่ง และห้องรอ ด้วย PostgreSQL ในเครื่อง

เข้าใจว่าทำไมเพิ่มเซิร์ฟเวอร์แล้วเว็บขายบัตรยังล่ม ลองดูขีดจำกัด connection ของ PostgreSQL ใน Docker ด้วยตาตัวเอง ลองขายที่นั่งซ้ำแล้วกันด้วย SELECT ... FOR UPDATE และรู้จัก connection pool คิวงาน กับห้องรอ

วันเปิดขายบัตรคอนเสิร์ตดัง คนเป็นล้านกดเข้าเว็บพร้อมกัน แล้วหน้าเว็บก็หมุนค้าง ขึ้น error หรือแย่กว่านั้นคือจ่ายเงินแล้วแต่ที่นั่งหายไป คำถามที่ตามมาทุกครั้งคือ "ทำไมไม่เพิ่มเซิร์ฟเวอร์ให้มากกว่านี้" คำตอบสั้นๆ คือเพิ่มได้ แต่คอขวดจริงมักไม่ได้อยู่ที่จำนวนเซิร์ฟเวอร์

บทความนี้อธิบายว่าคอขวดอยู่ตรงไหน แล้วให้ลองเห็นกับตาในเครื่องตัวเองด้วย PostgreSQL ใน Docker ทั้งขีดจำกัด connection ของฐานข้อมูล ปัญหาขายที่นั่งซ้ำ และการล็อกที่นั่งให้ทีละคน ปิดท้ายด้วย connection pool คิวงาน และห้องรอ ที่ระบบรับคนจำนวนมากใช้กัน

จบบทความนี้คุณจะ:

  • อธิบายได้ว่าทำไมเพิ่มเซิร์ฟเวอร์แล้วเว็บยังล่ม
  • เปิด PostgreSQL ใน Docker แล้วดูขีดจำกัด connection ด้วยตาตัวเอง
  • เห็น race condition ที่ทำให้ขายที่นั่งซ้ำ และกันได้ด้วย SELECT ... FOR UPDATE
  • รู้ว่า connection pool, คิวงาน และห้องรอ แก้ปัญหาคนละชั้นยังไง
  • แก้ปัญหาที่เจอบ่อยในแล็บ และเก็บกวาดเครื่องให้เรียบร้อย

เรื่องจริงที่เป็นข่าว

เดือนพฤศจิกายน 2022 Ticketmaster เปิดขายบัตรรอบพรีเซลทัวร์ของ Taylor Swift แล้วระบบรับไม่ไหวจนเป็นข่าวไปทั่วโลก บริษัทออกคำแถลงพร้อมตัวเลขนี้

ตัวเลขที่มา
คำขอ 3,500 ล้านครั้ง สูงกว่าสถิติเดิม 4 เท่าคำแถลงของ Ticketmaster (รวมบอต)
ผู้เข้ามาราว 14 ล้านรายคำแถลงเดียวกัน
ขายบัตรได้กว่า 2 ล้านใบ ในวันนั้นคำแถลงเดียวกัน

อ่านตัวเลขนี้ให้ถูก ตัวเลขเป็นของบริษัทเอง ไม่ใช่ผลตรวจสอบอิสระ และบทความนี้ไม่ได้บอกว่าระบบของเขาล่มเพราะอะไร เราไม่รู้สถาปัตยกรรมจริงของระบบขายตั๋วรายไหน ใช้ตัวเลขนี้แค่ให้เห็นว่าคนมาพร้อมกันได้มากแค่ไหน ส่วนหลักการที่เหลือในบทความเป็นหลักการทั่วไปที่ใช้กับเว็บทุกแบบ

ศัพท์ที่ต้องรู้

คำความหมายแบบมือใหม่เทียบกับ
requestคำขอหนึ่งครั้งที่เบราว์เซอร์ส่งไปหาเซิร์ฟเวอร์กดปุ่มหนึ่งครั้ง
serverเครื่องที่ตอบ requestหน้าร้าน
scale outเพิ่มจำนวนเซิร์ฟเวอร์ให้ช่วยกันรับเปิดหน้าร้านเพิ่ม
database (DB)ที่เก็บข้อมูลกลาง เช่น ที่นั่งไหนขายแล้วห้องเก็บของ
connectionสายที่เปิดคุยกับ DB ค้างไว้ช่องบริการ
bottleneckจุดที่แคบที่สุด ทั้งระบบเร็วได้ไม่เกินจุดนี้คอขวด
race conditionสองคนทำพร้อมกันแล้วผลผิด เช่น ขายที่นั่งซ้ำมือแย่งที่นั่ง
lockจองแถวข้อมูลให้ทีละคน คนอื่นต้องรอกุญแจบนที่นั่ง
transactionชุดคำสั่งที่สำเร็จทั้งหมดหรือไม่สำเร็จเลยใบสั่งซื้อหนึ่งใบ
connection poolสายที่เปิดไว้ล่วงหน้าแล้วแบ่งกันใช้ช่องบริการจำนวนคงที่
queueให้รอตามลำดับแทนการแย่งกันห้องรอ

คอขวดอยู่ตรงไหน

ลองไล่ตามทางที่การกดจองหนึ่งครั้งเดินทางครับ

  1. หน้าเว็บ scale out ได้ เพิ่มเครื่องได้เรื่อยๆ เพราะแต่ละเครื่องแค่ส่งหน้าเว็บและรับ request
  2. ทุกเครื่องต้องถาม DB ตัวเดียวกัน ที่นั่งมีชุดเดียว ข้อมูลจริงต้องอยู่ที่เดียว ไม่งั้นสองเครื่องอาจขายที่นั่งเดียวกันให้คนละคน
  3. DB รับ connection ได้จำกัด ใน PostgreSQL ค่า max_connections โดยทั่วไปคือ 100 (ตามเอกสาร PostgreSQL หัวข้อ Connections and Authentication)
  4. ที่นั่งเดียวกันต้องล็อก ยิ่งคนแย่งที่เดียวกันมาก ยิ่งต้องรอกันนาน

นึกภาพหน้าร้านที่เปิดเพิ่มได้เป็นพันร้าน แต่ทุกร้านต้องเดินไปต่อแถวที่ห้องเก็บของห้องเดียว เพิ่มหน้าร้านมากเท่าไร แถวหน้าห้องเก็บของก็ยิ่งยาว ทางแก้จึงต้องคุมจำนวนคนที่เข้าไปถึง DB ไม่ใช่แค่เพิ่มเครื่อง

คนจำนวนมากผ่านห้องรอ เข้าเว็บเซิร์ฟเวอร์หลายเครื่อง แล้วทุกเครื่องต้องผ่าน connection pool ไปหาฐานข้อมูลตัวเดียวที่รับได้ราว 100 สาย
เพิ่มเว็บเซิร์ฟเวอร์ได้ แต่ทุกเครื่องต้องไปหาฐานข้อมูลตัวเดียวกัน

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

เตรียมแล็บ PostgreSQL ด้วย Docker

แล็บนี้ใช้ Docker จะได้ไม่ต้องติดตั้งฐานข้อมูลลงเครื่อง ใช้เวลาราว 30–45 นาทีทั้งบท เปิด Docker Desktop ให้ทำงานก่อน แล้วพิมพ์

Terminal / PowerShell
docker run --name pg-lab -e POSTGRES_PASSWORD=lab -p 5432:5432 -d postgres:18
ผลลัพธ์ประมาณนี้
(ตัวเลขยาวๆ คือรหัสของ container)

ครั้งแรก Docker จะดาวน์โหลด image ของ PostgreSQL ก่อน รอสักครู่ แล้วเช็กว่า container ทำงานอยู่

Terminal / PowerShell
docker ps
ผลลัพธ์ประมาณนี้
... postgres:18 ... Up ... pg-lab

เปิดหน้าต่างคุยกับฐานข้อมูลด้วย psql ซึ่งมีมาใน container อยู่แล้ว

Terminal / PowerShell
docker exec -it pg-lab psql -U postgres
ผลลัพธ์ประมาณนี้
psql (18.x)
Type "help" for help.
 
postgres=#

เห็น postgres=# แปลว่าพร้อมแล้ว คำสั่งในหัวข้อต่อจากนี้พิมพ์ในหน้าต่างนี้ ทุกคำสั่ง SQL ต้องปิดท้ายด้วย ; ส่วนออกจาก psql พิมพ์ \q

รหัส lab ใช้แค่ในแล็บนี้ อย่าใช้รหัสง่ายๆ แบบนี้กับฐานข้อมูลจริง และอย่าเปิดพอร์ตฐานข้อมูลให้คนนอกเข้าถึงได้

ดูขีดจำกัด connection

เริ่มจากถามฐานข้อมูลว่ารับได้กี่สาย

psql
SHOW max_connections;
ผลลัพธ์ประมาณนี้
 max_connections 
-----------------
 100
(1 row)

แล้วดูว่าตอนนี้ใช้ไปกี่สาย

psql
SELECT count(*) FROM pg_stat_activity;
ผลลัพธ์ประมาณนี้
 count 
-------
     9
(1 row)

ตัวเลขนี้รวมโปรเซสเบื้องหลังของ PostgreSQL ด้วย จึงอาจต่างไปตามรุ่น (ตอนทดสอบกับ PostgreSQL 18.6 ได้ 9) อยากรู้ว่าแต่ละสายคืออะไร ใช้คำสั่งนี้

psql
SELECT backend_type, count(*) FROM pg_stat_activity GROUP BY 1;
ผลลัพธ์ประมาณนี้
         backend_type         | count 
------------------------------+-------
 autovacuum launcher          |     1
 background writer            |     1
 checkpointer                 |     1
 client backend               |     1
 io worker                    |     3
 logical replication launcher |     1
 walwriter                    |     1
(7 rows)

client backend คือ psql ของเราเอง ลองเปิด Terminal อีกหน้าต่าง รัน docker exec -it pg-lab psql -U postgres อีกตัว แล้วนับใหม่ในหน้าต่างแรก จะเพิ่มขึ้น 1 ทุกคนที่ต่อเข้ามากินหนึ่งช่องจาก 100 ช่องนี้

อยากเห็น error ตอนเต็มกับตา

เปิด container อีกตัวที่ตั้งให้รับได้แค่ 5 สาย (ไม่ต้องเปิดพอร์ต เพราะต่อผ่าน docker exec)

Terminal / PowerShell
docker run --name pg-tiny -e POSTGRES_PASSWORD=lab -d postgres:18 -c max_connections=5

แล้วเปิด Terminal ทีละหน้าต่าง รัน docker exec -it pg-tiny psql -U postgres ในทุกหน้าต่าง ห้าหน้าต่างแรกเข้าได้ปกติ พอถึงหน้าต่างที่หก จะเจอ

ผลลัพธ์ประมาณนี้
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  sorry, too many clients already

นี่คือภาพเดียวกับหน้าเว็บที่หมุนค้างแล้วพังตอนเปิดขาย แอปขอสายใหม่ไม่ได้ ก็ตอบผู้ใช้ไม่ได้ ดูเสร็จแล้วปิด psql ทุกหน้าต่างด้วย \q และลบ container ตัวนี้

Terminal / PowerShell
docker rm -f pg-tiny

อย่าแก้ max_connections ให้สูงมากๆ เพื่อหนีปัญหา แต่ละสายกินหน่วยความจำของเซิร์ฟเวอร์ ตั้งไว้สูงเกินไป ฐานข้อมูลอาจช้าลงทั้งตัวจนทุกคนได้รับผลกระทบ ทางที่ใช้กันคือ connection pool และคิว ซึ่งอยู่ในหัวข้อถัดๆ ไป

ลองขายที่นั่งซ้ำ

กลับมาที่ pg-lab สร้างตารางที่นั่ง 24 ที่ ยังไม่มีใครซื้อ

psql
CREATE TABLE seats (
  id int PRIMARY KEY,
  buyer text
);
INSERT INTO seats (id) SELECT generate_series(1, 24);
ผลลัพธ์ประมาณนี้
CREATE TABLE
INSERT 0 24

เปิด psql สองหน้าต่าง เรียกว่า A และ B แทนคนสองคนที่กดจองพร้อมกัน ให้ทั้งคู่ดูที่นั่งหมายเลข 10

psql A และ psql B
SELECT buyer FROM seats WHERE id = 10;
ผลลัพธ์ประมาณนี้
 buyer 
-------
 
(1 row)

ทั้งสองคนเห็นว่าว่าง ต่างคนต่างจอง พิมพ์บรรทัดแรกใน A แล้วพิมพ์บรรทัดที่สองใน B

psql
UPDATE seats SET buyer = 'A' WHERE id = 10; -- ใน A
UPDATE seats SET buyer = 'B' WHERE id = 10; -- ใน B
ผลลัพธ์ประมาณนี้ (ทั้งสองหน้าต่าง)
UPDATE 1

ทั้งสองคำสั่งสำเร็จ แต่คนหลังเขียนทับคนแรก ดูที่นั่ง 10 อีกครั้งจะเห็นชื่อ B ถ้าแอปบอก A ไปแล้วว่าจองสำเร็จ ก็เท่ากับขายที่นั่งเดียวให้สองคน นี่คือ race condition

ทำไมในเว็บจริงถึงเกิดบ่อยกว่าในแล็บ ในแล็บเราพิมพ์ห่างกันหลายวินาที แต่ในวันเปิดขาย request นับพันมาถึงห่างกันไม่กี่มิลลิวินาที ช่วงระหว่าง "ดูว่าว่าง" กับ "เขียนว่าจองแล้ว" แคบแค่ไหนก็มีคนแทรกเข้ามาได้

ล็อกที่นั่งให้ทีละคน

PostgreSQL มีคำสั่ง SELECT ... FOR UPDATE ที่ล็อกแถวที่อ่านไว้จนกว่า transaction จะจบ ใครจะแก้หรือล็อกแถวเดียวกันต้องรอ ลองดูทีละขั้น

เส้นเวลาของ A และ B A ล็อกที่นั่ง 10 ก่อน B ล็อกตามแล้วต้องรอ พอ A COMMIT แล้ว B ถึงได้คำตอบและเห็นว่ามีคนจองแล้ว
B ต้องรอจน A จบ ที่นั่งจึงไม่ถูกเขียนทับ

ล้างที่นั่งก่อน แล้วให้ A เริ่ม transaction และล็อกที่นั่ง 10

psql A (ล้างที่นั่งก่อน)
UPDATE seats SET buyer = NULL WHERE id = 10;
psql A
BEGIN;
SELECT buyer FROM seats WHERE id = 10 FOR UPDATE;

ตอนนี้ให้ B ทำแบบเดียวกัน

psql B
BEGIN;
SELECT buyer FROM seats WHERE id = 10 FOR UPDATE;
ผลลัพธ์ประมาณนี้
(B ค้างอยู่ ไม่ตอบ กำลังรอ A)

กลับไปที่ A จองแล้วจบ transaction

psql A
UPDATE seats SET buyer = 'A' WHERE id = 10;
COMMIT;

ทันทีที่ A พิมพ์ COMMIT หน้าต่าง B จะได้คำตอบ

ผลลัพธ์ใน psql B
 buyer 
-------
 A
(1 row)

B เห็นว่ามีคนจองแล้ว จึงยกเลิก

psql B
ROLLBACK;

ผ่านเมื่อ B ต้องรอจน A จบ แล้วเห็นชื่อ A โดยไม่มีการเขียนทับ ตามเอกสาร PostgreSQL หัวข้อ Explicit Locking แถวที่ล็อกด้วย FOR UPDATE จะกันคนอื่นแก้หรือล็อกซ้ำจนกว่า transaction จะจบ ในแอปจริง หลังได้ล็อกแล้วโค้ดต้องเช็กว่า buyer ยังว่างไหม ถ้าไม่ว่างก็บอกผู้ใช้ว่าที่นั่งนี้มีคนจองแล้ว

อีกวิธีที่สั้นกว่า

ถ้าแค่จองที่นั่งเดียว เขียนเงื่อนไขไว้ในคำสั่ง UPDATE เลยก็ได้

psql
UPDATE seats SET buyer = 'B' WHERE id = 10 AND buyer IS NULL;
ผลลัพธ์ประมาณนี้ (เมื่อมีคนจองไปแล้ว)
UPDATE 0

UPDATE 1 แปลว่าจองสำเร็จ ส่วน UPDATE 0 แปลว่ามีคนได้ไปก่อน ถ้าสองคนสั่งพร้อมกัน PostgreSQL ให้คนหลังรอคนแรกจบ แล้วเช็กเงื่อนไขใหม่ คนหลังจึงได้ UPDATE 0 ไม่ใช่เขียนทับ

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

connection pool และคิวงาน

แทนที่ทุก request จะเปิดสายใหม่ไปหา DB แอปเปิดสายไว้จำนวนหนึ่ง (เช่น 20 สาย) แล้วแบ่งกันใช้ request ที่เกินต้องรอสายว่างในแอป แทนที่จะไปทำให้ DB เต็ม

เรื่องไม่มี poolมี pool 20 สาย
request หมื่นตัวพยายามเปิดหมื่นสายใช้สายเดิม 20 สายผลัดกัน
ฝั่ง DBเกิน 100 สาย DB ปฏิเสธDB เห็นแค่ 20 สาย ทำงานต่อได้
ผลลัพธ์พังทั้งระบบช้าลง แต่ไม่พัง

ไลบรารีฐานข้อมูลส่วนใหญ่มี pool ในตัว เช่น ไลบรารี pg ของ Node.js

db.js (Node.js กับไลบรารี pg)
import pg from "pg";
 
// ทั้งแอปใช้สายร่วมกันไม่เกิน 20 สาย
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 20 });
 
export const query = (text, params) => pool.query(text, params);

ระวังว่าถ้ารันแอป 10 เครื่อง แต่ละเครื่องมี pool 20 สาย รวมกันก็เป็น 200 สาย เกิน 100 อยู่ดี ระบบที่มีหลายเครื่องจึงนิยมวางตัวกลางอย่าง PgBouncer ไว้หน้า PostgreSQL ให้ทุกเครื่องมาต่อที่ PgBouncer แล้วมันแบ่งสายจริงจำนวนน้อยๆ ให้

อีกชั้นหนึ่งคือ คิวงาน รับคำสั่งจองไว้ในคิวก่อน แล้วให้ตัวทำงาน (worker) หยิบไปทำทีละรายการ คนกดได้คำตอบว่า "รับคำขอแล้ว" ทันที ส่วน DB ทำงานตามจังหวะที่รับไหว ไม่โดนทุกคนกระแทกพร้อมกัน

จำง่ายๆ pool คุมจำนวนสายที่ไปหา DB ส่วนคิวและห้องรอคุมจำนวนคนที่เข้ามาถึงระบบ ใช้ร่วมกันได้และควรใช้ร่วมกัน

ห้องรอ (Waiting Room)

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

ตัวอย่างบริการสำเร็จรูปคือ Cloudflare Waiting Room ตามหน้าเอกสาร ณ ต.ค. 2026

ที่เอกสาร Cloudflare บอกทำไมช่วย
ส่งผู้ใช้ส่วนเกินไปห้องรอ ที่แต่งหน้าตาให้เข้ากับเว็บได้เซิร์ฟเวอร์ต้นทางไม่โดนคำขอท่วม
แสดงเวลารอโดยประมาณ ที่อัปเดตตลอดคนรู้ว่าต้องรอนานแค่ไหน ไม่กดรีเฟรชซ้ำ
ตั้งช่วงเวลาพิเศษได้ (Scheduled Events)ปรับการปล่อยคนเฉพาะช่วงเปิดขาย
กำหนดกฎยกเว้นได้ (Waiting Room Rules)ไม่กันทราฟฟิกหรือหน้าที่ไม่จำเป็น

เป็นบริการแบบเสียเงิน หน้าเอกสารระบุว่า Waiting Room ใช้ได้กับแพ็กเกจ Business หรือ Enterprise (และองค์กรที่ผ่านโครงการ Project Fair Shot) เราไม่ได้ลองใช้จริง จึงไม่ขอบอกว่าคิวเรียงลำดับแบบไหน ถ้าจะใช้ให้อ่านเอกสารฉบับเต็มก่อน

แก้ปัญหาที่เจอบ่อย

อาการน่าจะเป็นเพราะทำยังไง
docker: command not foundยังไม่ติดตั้ง หรือไม่ได้เปิด Docker Desktopติดตั้งจาก docker.com แล้วเปิดโปรแกรมก่อน
port is already allocatedมี PostgreSQL อื่นใช้พอร์ต 5432เปลี่ยนเป็น -p 5433:5432
Conflict ชื่อ pg-lab ซ้ำเคยสร้างไว้แล้วdocker start pg-lab หรือลบก่อนตามหัวข้อเก็บกวาดด้านล่าง
psql B ไม่ค้างตอน FOR UPDATEA ยังไม่ได้พิมพ์ BEGIN หรือ COMMIT ไปแล้วเริ่มหัวข้อล็อกใหม่ทั้งสองหน้าต่าง
current transaction is abortedคำสั่งก่อนหน้าใน transaction ผิดพิมพ์ ROLLBACK; แล้วเริ่มใหม่
relation "seats" does not existยังไม่ได้สร้างตาราง หรือต่อคนละ DBสร้างตารางในหัวข้อขายที่นั่งซ้ำใหม่

ข้อความ error ตัวเต็มที่จะเห็นในสองกรณีหลัง

ตัวอย่าง error ใน psql
ERROR:  current transaction is aborted, commands ignored until end of transaction block
ERROR:  relation "seats" does not exist

เก็บกวาด

ลองครบแล้วหยุดและลบ container

Terminal / PowerShell
docker stop pg-lab
docker rm pg-lab
ผลลัพธ์ประมาณนี้
pg-lab
pg-lab

ข้อมูลในแล็บหายไปพร้อม container ซึ่งตั้งใจให้เป็นแบบนั้น ถ้าเคยสร้าง pg-tiny แล้วยังไม่ลบ ใช้ docker rm -f pg-tiny ด้วย

สรุป

  • เพิ่มเว็บเซิร์ฟเวอร์ได้ แต่ทุกเครื่องต้องไปถามฐานข้อมูลตัวเดียวกัน คอขวดจึงมักอยู่ที่ฐานข้อมูล
  • PostgreSQL รับ connection ได้จำกัด (ค่าทั่วไป 100) เต็มแล้วคนต่อไปเจอ sorry, too many clients already
  • ไม่ล็อก สองคนจองที่นั่งเดียวกันได้ทั้งคู่ ใช้ SELECT ... FOR UPDATE หรือ UPDATE แบบมีเงื่อนไขเพื่อกันขายซ้ำ
  • ล็อกทำให้ถูกแต่ไม่ได้ทำให้เร็ว ต้องใช้คู่กับ connection pool คิวงาน และห้องรอ
อยากรู้ / อยากทำคำตอบ
ทำไมเพิ่มเครื่องแล้วยังล่มทุกเครื่องต้องไปถาม DB ตัวเดียวกัน
ดูขีดจำกัดSHOW max_connections;
ดูว่าใช้ไปกี่สายSELECT count(*) FROM pg_stat_activity;
กันขายซ้ำBEGIN; SELECT ... FOR UPDATE; ... COMMIT;
ไม่ให้ DB เต็มconnection pool (และ PgBouncer)
ไม่ให้คนท่วมระบบคิวงานและห้องรอ

อ่านต่อจากเอกสารทางการ

พร้อมแล้วก็ไปทำ Quiz ท้ายบทด้านล่างเพื่อรับ XP ได้เลยครับ

Quiz ท้ายบท

5 ข้อ · ข้อละ 10 XP

ทดสอบความเข้าใจเรื่อง คอขวดและห้องรอ ตอบถูกข้อละ 10 XP

  • 5 ข้อ
  • ไม่จับเวลา
  • เฉลยทันทีทุกข้อ