> For the complete documentation index, see [llms.txt](https://www.saladpuk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.saladpuk.com/basic/bottlenecks/work-with-db.md).

# หัวใจที่สำคัญที่สุดของฐานข้อมูล

## 😢 ปัญหา

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

{% hint style="success" %}
**แนะนำให้อ่าน**\
บทความนี้เป็นส่วนหนึ่งของบทความ [👦 Bottlenecks of Software](https://saladpuk.gitbook.io/learn/basic/bottlenecks) หากเพื่อนๆสนใจอยากรู้สาเหตุว่าทำไมระบบถึงช้าลง หรือมันเกิดจากอะไรได้บ้าง ก็สามารถศึกษาได้จากบทความหลักได้เลยโดยการคลิกที่ชื่อบทความสีฟ้าๆนั่นแหละ
{% endhint %}

## 😄 วิธีแก้ปัญหา

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

{% hint style="info" %}
**เรื่องตลกร้าย**\
หลายคนที่ทำงานในวงการนี้แม้จะมีประสบการณ์ในการทำงานมาเป็น 10 ปี ออกแบบฐานข้อมูลมาได้เป็นอย่างดี รู้จัก database family ต่างๆ ก็อาจจะตกม้าตายเพราะแค่เรื่องพื้นฐานตัวนี้ก็ได้นะเชื่อไหม?
{% endhint %}

## 🤔 พื้นฐานที่สำคัญที่สุดคืออะไร ?

พื้นฐานที่สำคัญที่สุดในการทำงานกับฐานข้อมูลนั้นสามารถเขียนไว้ภายในรูปเดียวเท่านั้นแหละ ซึ่งถ้าเราเข้าใจเรื่องนี้เพียงแค่เรื่องเดียวแล้วล่ะก็ ของที่เหลือไม่ว่าจะเป็นการทำ indexing, backup, cleansing, design หรือแมวน้ำอะไรก็แล้วแต่ ก็จะเป็นเรื่องที่เอาไว้สนับสนุนเรื่องนี้ล้วนทั้งสิ้น ดังนั้นบทความนี้เราจะอธิบายและยกตัวอย่างให้เข้าใจว่า **หัวใจหลักในการทำงานกับฐานข้อมูลคืออะไร**เพียงเรื่องเดียวเท่านั้นครัช อย่ารอช้าดูรูปด้านล่างกันเบย

![](/files/-LpTvmWAxmM5WcCoS3Oo)

## 😫 นี่มันรูปบร้าไร ?

ผมขออธิบายเป็นแบบนี้ละกันว่า ไม่ว่าจะเป็นโปรแกรมบ้าบออะไรก็แล้วแต่ สุดท้ายมันก็จะมีแค่ **ตัวแอพ** ที่จะคอยเรียกใช้ **ตัวฐานข้อมูล** ใช่ป่ะ ดังนั้นมองรูปดีๆเราก็จะเห็นว่ามันแบ่งออกเป็น 2 ฝั่ง นั่นคือฝั่ง **ตัวแอพ (App)** กับฝั่ง **ตัวฐานข้อมูล (DB)** ยังไงล่ะ

![](/files/-LusmjKjP0nZ36DRrXr5)

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

![](/files/-LnrPAt_Zah9-a5N1uk5)

ซึ่งตัวแอพมันก็จะบอกตัวฐานข้อมูลว่ามันอยากได้ข้อมูลอะไรบ้าง ดังนั้นตัวฐานข้อมูลก็จะต้องใช้ **Computing power** ไปไล่ดูดข้อมูลในตัวมันเอง ตามที่ตัวแอพขอมายังไงล่ะ

![สีม่วงๆคือข้อมูลที่เก็บไว้ในฐานข้อมูล](/files/-Lsf-ETJfjGNzAvzABGz)

หลังจากที่ตัวฐานข้อมูลได้ข้อมูลที่แอพขอมาละ มันก็จะทำการส่งข้อมูลพวกนั้นกลับไปให้ตัวแอพผ่านทาง **Network** ยังไงล่ะ ตามรูปด้านล่างเลย

![](/files/-LshX5j04fUUJSYov0KN)

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

![](/files/-LsoVkM11tltRjskVV7H)

ดังนั้นเราก็จะเห็นว่ามันมีข้อมูลทั้งหมด 3 แบบ

1. **สีม่วง (ขวาสุด)** - คือข้อมูลดิบที่อยู่ในฐานข้อมูล
2. **สีน้ำเงิน (ตรงกลาง)** - คือข้อมูลที่ส่งผ่าน network ระหว่างแอพกับฐานข้อมูล ซึ่งข้อมูลที่ส่งกลับจากฐานข้อมูลอาจจะเป็นตัวเดียวกับสีม่วง หรืออาจจะถูกดัดแปลงเป็นแบบอื่นก่อนส่งไปให้ก็ได้เหมือนกัน และข้อมูลที่รับส่งนี้ก็อาจจะเป็นการ**ส่งจากแอพไปยังฐานข้อมูลได้เช่นกัน**
3. **สีเขียว (ซ้ายสุด)** - คือข้อมูลที่อยู่ในตัวแอพแล้ว ไม่มีความเกี่ยวข้องอะไรกับในฐานข้อมูลอีกต่อไป คิดง่ายๆว่ามันเป็นแค่ตัว copy เท่านั้นแหละ

{% hint style="info" %}
**เกร็ดความรู้**\
โดยปรกติเครื่องที่ใช้ในการทำฐานข้อมูลจะมี computing power ที่ค่อนข้างสูงกว่าตัวแอพอยู่แล้ว เพราะมันจะต้องรอรับโหลดหนักๆยังไงล่ะ (แต่ก็ไม่แน่เสมอไปนะ)
{% endhint %}

## 😫 แล้วมันสำคัญยังไงแฟร๊ะ ?

ใจเย็นแล้วลองนั่งวิเคราะห์รูปให้ดีๆดิ แล้วลองตอบคำถามด้านล่างนี้ดูนะ

![](/files/-LpTvmWAxmM5WcCoS3Oo)

### 🔥 Database Query

🤔 จะเกิดอะไรขึ้นถ้าเราสั่งให้ตัวฐานข้อมูลไปดึงข้อมูลแบบที่มันซับซ้อนมากๆและกินเวลานานๆ\
😱 ตัวดูดข้อมูลของ database ก็จะทำงานหนัก และถ้ามีคำขอแบบนี้เข้ามามากๆมันก็จะทำให้มันทำงานต่อไม่ได้ เพราะงานเก่ามันยังทำไม่เสร็จ งานใหม่เลยต้องรอคิวกันไปเรื่อยๆ ทำให้เกิดสิ่งที่เรียกว่า **Database Overloaded** นั่นเอง

### 🔥 Network Latency

🤔 จะเกิดอะไรขึ้นถ้าตัวแอพขอข้อมูลจากตัวฐานข้อมูลเป็นแสนๆรายการเลย?\
😱 ข้อมูลเป็นแสนๆรายการก็จะต้องถูกส่งผ่าน network กลับมายังไงล่ะ ซึ่งถ้าข้อมูลยิ่งเยอะเท่าไหร่ ตัวแอพก็จะต้องรอนานมากขึ้นเท่านั้น เพราะมันต้องรอโหลดข้อมูลพวกนั้นจาก network อ่ะดิ

### 🔥 App Resource

🤔 จะเกิดอะไรขึ้นถ้าเราเอาข้อมูลปริมาณเยอะๆมาทำงานที่ฝั่งแอพ?\
😱 ตัวแอพมีทรัพยากรที่ค่อนข้างจำกัด เช่น Computing Power หรือ RAM ที่น้อยอยู่แล้ว ถ้าต้องทำงานกับข้อมูลที่เยอะๆก็อาจจะกระตุก พัง และล่วงลับดับขันธ์ไปในที่สุดนั่นเอง และ การจัดการกับหน้าตา UX ก็จะแย่ด้วยอ่ะดิ

## 😭 แล้วแก้ไงอ่ะ ?

หลักในการแก้ปัญหาเรื่องนี้นั้นสุดแสนจะง่าย แค่ทำ 3 เรื่องด้านล่างก็พอละ

1. **ลดงานตัวดูด** - อย่าสั่งอะไรที่มันซับซ้อนมากๆไปให้มันทำ และตัวฐานข้อมูลจริงๆมันก็มีงานที่มันถนัดและไม่ถนัดด้วยนะ ดังนั้นงานที่มันไม่ถนัดก็ส่งไปให้แอพจัดการต่อก็พอ
2. **ลดข้อมูลที่ส่งผ่าน network** - ตัวนี้พูดยากเพราะรายละเอียดมันเยอะ แต่หลักงง่ายๆคือส่งไปแค่เท่าที่จำเป็นต้องใช้ก็พอ และอาจจะใช้เทคนิคต่างๆเข้ามาช่วยก็ได้ เช่น การทำ paging ก็เป็นตัวเลือกที่ดีเหมือนกัน
3. **ลดงานฝั่งแอพ** - ตัวนี้ก็พูดยากอีกตัว แต่หลักง่ายๆคืออย่าให้มันต้องทำงานกับข้อมูลเยอะๆ เช่น ถ้าถามว่าเว็บเรามีหนังทั้งหมดกี่เรื่อง แทนที่จะส่งรายการหนังทั้งหมดมาที่ตัวแอพเพื่อให้มันไปนับเอง เราก็แค่ส่งจำนวนหนังในฐานข้อมูลไปให้แอพซะก็สิ้นเรื่อง

จากที่ว่ามาทั้ง 3 ข้อ เราก็จะได้ข้อสรุปว่า เราควรจะให้การทำงานมันออกมาตามภาพด้านล่างนี้ต่างหาก

![](/files/-LsWvaOv7lHOwJ6YXwoY)

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

{% hint style="info" %}
**คำแนะนำ**\
เทคนิคในการจัดการเรื่องทั้ง 3 ตัวนี้มีเป็นกระบุงเลย ไปไล่หาอ่านเอาได้เลยไม่งั้นผมคงไม่ได้นอนแน่ๆ และแต่ละวิธีก็มีข้อดีข้อเสีย และอย่าลืมดูตามความเหมาะสมของแต่ละวิธีด้วยนะ มันไม่มียาครอบจักรวาลหรอก
{% endhint %}

{% hint style="info" %}
**เกร็ดความรู้**\
**งานที่ฐานข้อมูลถนัดคือคณิตศาสตร์เบื้องต้น**ยังไงล่ะ เช่น การนับจำนวน การหาค่าสูงสุด หาค่าต่ำสุด มากกว่า น้อยกว่า ค่าเฉลี่ย ผลรวม บลาๆ ไรพวกนี้ ดังนั้นงานอะไรที่ใช้เรื่องพื้นฐานพวกนี้ทำได้ ก็อาจจะให้ทำใน database เลยแล้วค่อยส่งแค่ผลลัพท์กลับมาก็ได้ มันจะได้ไม่เปลือง Database computing power มากนัก ข้อมูลที่ส่งผ่าน network ก็น้อย ตัว client ก็ทำงานสบาย วินๆทั้งสองฝ่าย
{% endhint %}

## 😢 ขอตัวอย่างหน่อยได้ไหม ?

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

![](/files/-LqRiLJZ0B09A2cS64DZ)

### 🤔 **ถ้าอยากรู้ว่าเรามีสินค้าทั้งหมดกี่ชิ้นจะทำไง ?**

👎 **สิ่งที่ไม่ควรทำ:** ไปดึงข้อมูลสินค้าทั้งหมดมาจากฐานข้อมูล แล้วให้ client เป็นคนนับเองว่ามีกี่ชิ้น ตามโค้ดด้านล่าง

{% code title="Query command" %}

```sql
SELECT * FROM สินค้า;
```

{% endcode %}

{% code title="โค้ดในฝั่ง client ตอนได้ผลลัพท์กลับมา" %}

```csharp
var totalProducts = allProducts.Count();
```

{% endcode %}

ถ้าใครคิดแบบนี้ให้กลับไปอ่านตั้งแต่ต้นใหม่นะ เพราะมันโหลดข้อมูลทุกอย่างเลย ฐานข้อมูลต้องไปดึงข้อมูลยั้วเยี๊ย แล้วต้องไล่ส่งข้อมูลทั้งหมดผ่าน network อีก แล้วเอาข้อมูลปริมาณมากๆไปยัดให้ client ด้วย ขอแสดงความยินดีด้วย ที่ผมพิมพ์มาทั้งหมดไม่มีความหมายเลย T-T

👍 **สิ่งที่ควรทำ:** ตัวฐานข้อมูลมันเก่งเรื่องคณิตศาสตร์พื้นฐานอยู่แล้ว ดังนั้นก็ให้มันนับ records ทั้งหมดมาซิ แล้วส่งแค่ตัวเลขมาให้ client ก็พอ เพราะมันแทบจะไม่โหลดใครเลย

{% code title="Query command" %}

```sql
SELECT COUNT(*)
FROM สินค้า
```

{% endcode %}

{% code title="โค้ดในฝั่ง client ตอนได้ผลลัพท์กลับมา" %}

```csharp
var totalProducts = allProducts;
```

{% endcode %}

### 🤔 **ถ้าอยากรู้ว่าสินค้าชิ้นนี้ถูกสั่งซื้อไปวันไหนบ้างจะทำไง ?**

👎 **สิ่งที่ไม่ควรทำ:** ไปดึงข้อมูลคำสั่งซื้อทั้งหมดกลับมา แล้วค่อยหาคำสั่งซื้อของสินค้าชิ้นนั้น

{% code title="Query command" %}

```sql
SELECT * FROM คำสั่งซื้อ;
```

{% endcode %}

{% code title="โค้ดในฝั่ง client ตอนได้ผลลัพท์กลับมา" %}

```csharp
var orders = allOrders.Where(it => it.รหัสสินค้า == "P01");
```

{% endcode %}

> รหัส P01 คือรหัสสินค้าที่เราต้องการหา

มันไม่ดีเพราะเหตุผลเดียวกันกับข้อที่แล้วเลย ซ้ำร้ายคือมันทำให้ client ต้องเสียเวลามานั่งประมวลผล filter ต่ออีกด้วย

👍 **สิ่งที่ควรทำ:** ให้ตัวฐานข้อมูลมันเก่งเรื่องคณิตศาสตร์พื้นฐานอยู่แล้ว ดังนั้นก็ให้มันนับ records ทั้งหมดมาซิ แล้วส่งแค่ตัวเลขมาก็พอ แทบจะไม่โหลดอะไรใครเลย

{% code title="Query command" %}

```sql
SELECT * FROM คำสั่งซื้อ
WHERE รหัสสินค้า = 'P01';
```

{% endcode %}

{% code title="โค้ดในฝั่ง client ตอนได้ผลลัพท์กลับมา" %}

```csharp
var orders = allOrdersFromProductP01;
```

{% endcode %}

{% hint style="success" %}
**เพิ่มเติม**\
ในกรณีที่เราต้องการใช้แค่ วันที่สั่งซื้อเท่านั้น เราก็สามารถทำ projection แค่วันที่อย่างเดียวก็ได้ มันจะได้ไม่มีข้อมูลที่ไม่ได้ใช้ส่งผ่าน network ไปยังไงล่ะ
{% endhint %}

### 🤔 **ถ้าอยากรู้ข้อมูลลูกค้าทั้งหมดในระบบละ? สมมุติว่ามีเป็นล้านๆคนเลยนะ**

👎 **สิ่งที่ไม่ควรทำ:** ไปดึงข้อมูลลูกค้าทั้งระบบมา -\*- ไม่ขอเขียนตัวอย่างนะ

👍 **สิ่งที่ควรทำ:** ดึงข้อมูลมาเป็นส่วนๆ หรือที่เราเรียกว่า Paging นั่นเอง และในการดึงข้อมูลควรจะต้องดูด้วยว่าเราจะเอาข้อมูลอะไรของลูกค้าไปใช้บ้าง เช่น ถ้าในหน้านั้นมันเอาไปโชว์แค่ **ชื่อ** กับ **นามสกุล** เท่านั้น เราก็ไม่ต้องส่งข้อมูลจากฐานข้อมูลทั้ง record ก็ได้ โดยเราสามารถทำ **Projection** ได้นั่นเอง

{% hint style="warning" %}
**ข้อควรระวัง**\
ในการทำงานบางอย่างที่ต้องอาศัยการทำงานจากฝั่ง **service** และฝั่ง **client** ให้เราดูด้วยว่ามันเป็นแบบไหน เช่นการทำ paging ถ้าเราทำที่ฝั่ง client เพียงอย่างเดียว แต่เซิฟเวอร์ก็ยังส่งข้อมูลมาเป็นล้านๆตัว ก็ไม่มีประโยชน์อยู่ดี เพราะงานฝั่ง client ส่วนใหญ่จะเป็นเรื่อง **UX** เท่านั้น
{% endhint %}

## 🤔 แนวคิดนี้ใช้กับอะไรได้บ้าง ?

ใช้ได้กับฐานข้อมูลทุกแบบเลย มีฐานข้อมูลตัวไหนไม่ทำงานแบบนี้บ้าง? (อ่อจะบอกว่า embedded database ซินะ คิดเหรอว่ามันจะไม่มี network cost? และเรื่องอื่นๆที่ว่ามา)

{% hint style="danger" %}
**ข้อควรระวัง**\
ในการทำเรื่องนี้ก็ขึ้นอยู่กับเทพเจ้าแต่ละองค์ เช่น องค์ SQL กับ องค์ MySQL หรือองค์ MongoDB, Redis, Neo4J หรือจะองค์ไหนก็แล้วแต่ มันก็จะมีวิธีบูชาเทพไม่เหมือนกัน ... ผมไม่ได้เมากาวอะไรอยู่นะ แต่จะสื่อว่า ตัว database แต่ละตัวมันก็จะมีวิธีการ Optimize หรือการจัดการไม่เหมือนกัน ดังนั้นอย่าตะบี้ตะบันใช้วิธีเดียวกับฐานข้อมูลทุกประเภทนะ ไปไล่อ่าน **Best practices** ของแต่ฐานข้อมูลที่เราจะใช้งานมันด้วยซะ ... เตือนแล้วนะ
{% endhint %}

## 🎯 บทสรุป

จากที่ร่ายยาวมาก็จะเห็นแล้วว่า **หัวใจ** ในการทำงานกับฐานข้อมูลจริงๆก็มีแค่เรื่องนี้เรื่องเดียวแหละ เพราะต่อให้เราไปเตรียมทำ perfect design, indexing, cleansing, read/write replica บ้าบออะไรก็ตาม สุดท้าย **ถ้าจุดเริ่มต้นมันกาก** มันก็จะเหมือนกับแต่งรถไว้สุดเทพ มีเครื่องสุดแจ่ม มีน้ำมันสำรองครบ แต่ดันใส่เบรคมือทิ้งไว้แล้วขับลากเกียร์ 1 ตลอดทาง มันก็เท่านั้นแหละ จะไปนั่ง optimize มันทำไม?

ดังนั้นสุดท้ายก็หาทางปรับพื้นฐานในการทำงานกับ database ให้มันออกมาเป็นภาพนี้ให้ได้มากที่สุดเอาละกันนะจุ๊ฟๆ

![](/files/-LsWvaOv7lHOwJ6YXwoY)
