> 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.md).

# Bottlenecks of Software

## 😢 ปัญหา

เชื่อว่าหลายๆคนที่ได้เขียนโปรแกรมที่ใช้จริงมาซักระดับนึงน่าจะเคยมีประสบการณ์ว่า แอพที่เคยทำงานได้รวดเร็วดุจสายฟ้า แต่อยู่มาก็ดันช้าเป็นเต่าซะงั้น พยายามเปลี่ยนโค้ดจุดนั้นนู้นนี่ก็แล้ว จัดการ database ก็แล้ว (ทำทุกๆอย่างให้เธอแล้ว) แต่ก็ยังเป็นเต่าอยู่ดี หรือบางทีก็อาการดีขึ้น แต่ซักพักก็วนกลายร่างเป็นเต่าเช่นเคย เฮ่อ...เหนื่อยใจ ตอนนี้เลยได้แต่พึ่งเจ้าพ่อนั่งจุดธูปเช้าเย็นหวังว่ามันจะไม่ล่มก็พอใจละ

จากปัญหาที่ว่ามามันเกิดจากอะไรได้บ้าง แล้วเราจะแก้ยังไงดี ?

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

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

## 🤔 ทำไมแอพถึงช้า ?

ก่อนเฉลยผมอยากให้เข้าใจตรงกันก่อนว่า ตอนที่โปรแกรมมันของเราทำงานเรื่องอะไรก็ตาม มันจะประมวลผลเป็นรูปแบบที่เรียกว่า **Pipeline** หรือทำงานกันเป็นทอดๆนั่นเอง เช่น เราสั่งให้มันไปคำนวณให้หน่อยดิ๊ว่านักเรียนแต่ละคนได้เกรดอะไร ซึ่งภายใน pipeline นั้นก็จะไปทำงานประมาณนี้

1. ดึงรายชื่อนักเรียนจาก database
2. คำนวณเกรดของนักเรียนแต่ละคน
3. แสดงผลออกทาง View

สมมุติว่าแอพของเรามันช้า สิ่งที่เราต้องไปไล่ดูคือ

`หาว่ามันช้าจากการทำงานเรื่องอะไรบ้าง แล้วค่อยไปไล่ดู Pipeline ของมันต่อ`

จากที่ว่ามาทั้งหมด ประเด็นสำคัญที่สุดในการแก้ปัญหาเรื่องแอพหน่วงคือการหา **คอขวด** หรือ **Bottleneck** นั่นเอง ซึ่งเจ้า คอขวดนี้แหละมันจะซ่อนตัวอยู่ภายใน Pipeline ของเราอีกที

![คอขวด หรือ Bottleneck ที่ซ่อนอยู่ภายใน Pipeline](/files/-Luj5XWlZ8VWjf74k7Qx)

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

## 🤔 ทำไมต้องต้องไล่แก้คอขวดเพียงอย่างเดียว ?

หลายคนอาจจะสงสัยว่า ทำไมไม่ไปเขียนโค้ดให้มันทำ performance ดีๆ ไม่ก็จัดการเคลีย database จัด index, cleaning บลาๆ ไปเลยล่ะ จะไปไล่หาคอขวดมันทำไม?

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

![](/files/-M-_UQCUusxR6rgFAkA_)

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

## 🤔 อะไรบ้างที่ทำให้เกิดคอขวด ?

เยอะม๊วกกกกกกๆๆๆ แต่ขอลิสต์แค่ตัวหลักๆก่อนนะ ดูได้จากรายการด้านล่างเลย ซึ่งแค่เห็นก็ปวดตับละ

| ชื่อ | ความหมาย |
| ---- | -------- |

| OS | <p>ตัว Operating System แต่ละตัวก็จะเก่งในงานไม่เหมือนกัน</p><p>การจัดการแต่ละเรื่องก็ไม่เท่ากัน</p> |
| -- | ---------------------------------------------------------------------------------------------------- |

| Hardware | อุปกรณ์ต่างๆที่ใช้ เช่น ฮาร์ดดิส แบบ SSD ก็เห็นผลชัดแล้ว |
| -------- | -------------------------------------------------------- |

| Environments | สภาพแวดล้อมของเซิฟเวอร์ก็มีผลนะ ร้อนไปเย็นไป หรือ มีแอพติดตั้งในเซิฟเวอร์นั้นเป็นร้อยตัวดูดิ |
| ------------ | -------------------------------------------------------------------------------------------- |

| Programming | ประสิทธิภาพของโค้ดที่เขียนให้กับโปรแกรมนั้นๆ |
| ----------- | -------------------------------------------- |

| Database | การออกแบบและเลือกใช้ฐานข้อมูล |
| -------- | ----------------------------- |

| Network | การเชื่อมต่อต่างๆของเซิฟเวอร์ เช่นเน็ทกาก หรือแบ่ง subnet ไม่ดีใช้อุปกรณ์ไม่เหมาะสมไรงี้ |
| ------- | ---------------------------------------------------------------------------------------- |

| Limitations | ขีดจำกัดทางสายเลือดบางประการ |
| ----------- | ---------------------------- |

`แก้คอขวด 1 เรื่อง ไม่ได้หมายความว่าปัญหาจะหายไปเพราะมันอยู่กันเป็นฝูง`

## 🤔 คอขวดเต็มไปหมดแล้วเอาไง ?

ในบทความนี้ผมจะลงรายละเอียดเพียงแค่ 3 เรื่องเท่านั้น ไม่งั้นมันจะไม่ได้อยู่ในหมวดความรู้พื้นฐานสำหรับมือใหม่ละ ฮ่าๆ ซึ่งทั้ง 3 เรื่องที่ว่านั้นคือ `Programmin`, `Database` และ `Limitations`ดังนั้นไปดูแต่ละเรื่องเลยว่ามันมีอะไรที่เกี่ยวกับมันบ้าง

## 🔥 Programming

ปัจจัยของการเขียนโค้ดแล้วทำให้เกิด **คอขวด** นั้นมีหลายอย่างเลย ซึ่งผมขอยกตัวอย่างเท่าที่นึกออกก่อนนะ

### 🔹 Algorithms and data structures

การเลือกใช้คำสั่งที่เหมาะสมนั้นมีผลสูงมากต่อ performance ของโปรแกรม ถ้าต้องทำในระยะยาว ดังนั้นเวลาที่เราจะทำ Refactor Code เพื่อทำ performance เราจะต้องเลือกใช้คำสั่งที่เหมาะสมกับงานนั้นๆด้วย

**ตัวอย่าง**\
สมมุติว่าผมต้องการให้โค้ดหาผลรวมตั้งแต่เลข 1 จนถึงเลขที่ผมใส่เข้าไป เช่น ผมใส่เลข 3 คำตอบคือ 6 เพราะเกิดจาก 1+2+3 แล้วผมเขียนโค้ดออกมาแบบนี้

```
var sum = 0;
for (int i = 1; i <= N; ++i)
{
    sum += i;
}
```

แล้วลองเปรียบเทียบกับผมเขียนโค้ดแบบนี้

```
var sum = N * (1 + N) / 2;
```

แม้ว่าโค้ดทั้ง 2 แบบจะให้คำตอบที่ถูกได้ทั้งคู่ก็ตาม แต่ **BigO** ของทั้ง 2 ตัวต่างกันคนละโลกเลย (ตัวที่ 2 เร็วกว่า) หรือลองคิดแบบนี้ดูก็ได้ว่า ถ้าเราเลือกใช้ของที่ไม่เหมาะสมกับงาน มันก็เหมือนกับการเอาค้อนไปเลื่อยไม้นั่นแหละ ซึ่งมันก็อาจจะเลื่อยได้(มั้ง) แต่มันก็ไม่ได้เร็วเท่าที่มันควรจะเป็นยังไงล่ะ

### 🔹 Strategies

การคิดวิธีการรับมือของงานแต่ละแบบก็มีผลสูงมากนะ ผมขอยกตัวอย่างให้เห็นภาพตามนี้

**ตัวอย่าง 1** ถ้าเราไปดึงรายการสินค้าที่มีเป็นแสนๆรายการมาแสดงผลในหน้าเดียวมันจะเกิดอะไรขึ้น? แอพอาจจะเด้งเลยก็ได้ อีกทั้งเสียเวลาทั้ง server ทั้ง bandwidth ที่ไปดาวโหลดไฟล์มาอีกด้วย\
**การแก้ปัญหา** แทนที่จะโหลดมาตูมเดียวเราก็แบ่งโหลดก็ได้นิ หรือเราเรียกว่าการทำ **Paging**

**ตัวอย่าง 2** ถ้าโค้ดมีการสร้าง database connection ใหม่เรื่อยๆโดยไม่คืน resource เลยสุดท้าย database ก็ตายแอพก็ตายตามกันไป\
**การแก้ปัญหา** เปิด connection แล้วปิดด้วย หรือใช้พวกที่มีการจัดการ connection pooling ที่เหมาะสม

**ตัวอย่าง 3** การทำงานต่างๆ ถ้ามันมีเวลาในการทำงานที่สูงมากก็อาจจะเกิดปัญหาภายหลังได้\
**การแก้ปัญหา** พยายามให้ **Roundtrip** มันสั้นเข้าไว้

### 🔹 Language limitations

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

**Recursive function** - บางภาษาการเขียน recursive function ไม่ใช่เรื่องดีนัก แต่ในขณะเดียวกันบางภาษาจะเก่งกับการทำ recursive function เอามากๆ

**Structural & Unstructured** - บางภาษาเวลาที่เราจะใช้มันต้องสร้างโครงสร้างให้มันก่อนถึงจะทำงานได้ เช่น Class แต่ในขณะเดียวกันบางภาษาสามารถเรียกใช้งานโดยไม่ต้องสร้างโครงสร้างให้มันก็ได้ เช่น Javascript

### 🔹 Build & Compile level

บางภาษาพอเขียนเสร็จปุ๊ปก็เอาไปใช้งานได้เลย เช่น php แต่ก็มีอีกหลายๆภาษาที่ต้องทำการ compile เสียก่อนถึงจะใช้งานได้ เช่น C# ซึ่งในการ build ก็มีหลายระดับ เช่น debug, release ไรพวกนี้ ซึ่งประสิทธิภาพของพวกนี้ก็ไม่เหมือนกันด้วย

## 🔥 Database

ปัจจัยที่เกิด **คอขวด** ของ database ก็มีหลายอย่างเช่นกัน ขอยกตัวอย่างเท่าที่นึกออกเช่นกัน (เพราะอันที่นึกไม่ออกก็ไม่รู้จะเขียนไร ฮา)

### 🔹 Poor design

การออกแบบที่ไม่เหมาะสมนี่เจอบ่อยฝุดๆ เช่นกำหนดขนาดที่ไม่เหมาะสม varchar, nvarchar หรือไปกำหนดของให้มันเป็น text ทั้งๆที่มันไม่ควรเป็น text เลวร้ายกว่านั้นคือไปใช้ char แทน boolean ไรงี้ (พูดเรื่องพวกนี้แล้วปวดใจ) และรวมถึงการตั้งชื่อหัวตารางที่ไม่เหมาะสมด้วย N1, N2, N3, N4 ... เฮ่อเหนื่อยใจ

### 🔹 Normalization

การทำ normalization มากจนเกินไปบางทีก็ไม่ดีนะจ๊ะ และอย่าเมากาวเอาแต่ยึดติดกับการทำ normalize ด้วย เพราะเป้าหมายของ database ไม่ใช่การทำ normalize แต่เป็นการเก็บและรักษาข้อมูลได้ต่างหาก ซึ่งในบางทีเราอาจจะไม่ต้องทำ normalize ถึงระดับ 3 ก็ได้ **ขึ้นอยู่กับความเหมาะสมของหน้างานที่เจอ** ว่าคนเอาไปใช้ในเคสนั้นๆเหมาะกับแบบไหน

### 🔹 Redundancy

ตรงตัวเลยจะเก็บข้อมูลซ้ำๆกันไปทำไม ? แต่ถ้ามันจำเป็นที่จะต้องทำก็ทำไปเถอะ ขึ้นกับความเหมาะสมของหน้างาน

### 🔹 Bad Referential Integrity

การเชื่อมตารางแบบต่างๆ เช่น 1-1, 1-M และ M-M ไรพวกนี้ก็มีผลกับ performance นะ ซึ่งยิ่งเยอะ performance ยิ่งตก

### 🔹 Not Taking Advantage of DB Engine Features

ตัว database แต่ละตัวมันก็มีความสามารถที่ติดมากับมันอยู่แล้วนะ ลองไปศึกษาใช้งานมันดูบ้างก็จะทำ performance กลับมาได้เยอะพอตัวเลย เช่น การใช้ Views, Indexes, Stored procedures, Constraints, Triggers อะไรเทือกนี้

### 🔹 No limitations on table or column name size

คือการที่เราปล่อยให้เขามาดึงข้อมูลได้โดยไม่ใส่ข้อจำกัดอะไรเลย เช่นดึงไปทีเดียวเป็นล้าน records เลย แล้วถ้าเขาส่งมาขอแบบนี้รัวๆ database มันจะไม่ล่มได้ไง? ดังนั้นจัดการกำหนด limit มันบ้างซะ

### 🔹 One table to hold all domain values

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

### 🔹 Lack of testing

ตัว database เราทำเทสครั้งสุดท้ายเมื่อไหร่ ? แล้วจะรู้ได้ไงว่าที่ออกแบบมามันออกแบบได้เหมาะสมกับงานมันแล้ว ? หรือรอให้มันขึ้น production แล้วให้ user จริงมาเทส ?

**ตัวอย่างที่จะเกิดคอขวดที่ database**

* เขียน query ที่ทำให้มันรอนานมากๆ เช่น ดึงข้อมูลจากตาราง A แล้วเอาไป Join กับตาราง B แล้วก็ intersect กับตาราง C .... Z ไรงี้ อย่าทำ เสียทรัพยากรเครื่องอันมีค่าโดยใช่เหตุ
* ออกแบบกฎที่อาจจะมีปัญหา เช่น A reference B และ B ก็ reference A ส่วนถ้าจะลบ A ต้องลบ B ด้วย และถ้าจะลบ B ก็ต้องลบ A ด้วยไรงี้

### 🔹 Limitation

ตัว database เองก็มีขีดจำกัดทางสายเลือดของมันเหมือนกัน เช่น database บางตัวทำงานกับกับข้อมูลที่ไม่เกินระดับ GB เท่านั้น หรือบางตัวต้องออกแบบโครงสร้างก่อนถึงจะใช้งานได้ แต่บางตัวไม่ต้องมีโครงสร้างเลย บลาๆ ดังนั้นไปศึกษาให้ดีเสียก่อนที่จะเลือกใช้ database

## 🤔 เรื่องเยอะจุงขอสั้นๆได้ไหม ?

เอาง่ายๆนะแค่ทำตาม **Design guideline & Best practices** ของภาษาหรือ database ที่เราใช้เพียงแค่นี้ก็ช่วยได้เยอะแล้ว เพราะ 90% ของ Developer และ DBA บ้านเราไม่ยอมไปอ่านของพวกนี้กัน แล้วก็มาจับงานจริงเลยทำให้ **คอขวด** กระจายเต็มไปหมดเบย *(ขอบคุณครับผมจะได้มีงานไปบรรยายเรื่องพวกนี้หากินต่อ ผมคิดในใจนะไม่ได้พูดออกมา ฮี่ๆ)* ซึ่งในพวก guideline พวกนั้นก็จะแนะนำไว้หลายเรื่องเลย เช่น เรื่องของ database

* Caching
* Hot & Cold data
* Vertical & Horizontal Scaling
* Federation - Splitting into multiple DBs based on function
* Sharding - Splitting one dataset up across multiple hosts
* Moving some functionality to other types of DBs

## 😁 แถมของ database ให้ไหนๆก็ยาวละ

ตัว database ในโลกนี้ (ณ ตอนที่เขียนบทความนี้) มีทั้งหมด 2 ตระกูล หลักคือ

### 🔹 Relational database

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

![](/files/-M1VF9jZAzlkZj5MXW3j)

### 🔹 Non-Relational database (NoSQL)

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

ซึ่งเจ้าตระกูลนี้จะแตกย่อยออกเป็น 4 สายตามนี้เลย

| ชื่อประเภท database | ตัวอย่าง product                                                    |
| ------------------- | ------------------------------------------------------------------- |
| Graph               | [neo4J](https://console.neo4j.org/), OrientDB, Titan                |
| Key-Value store     | [Redis](https://try.redis.io/), Amazon DynamoDB                     |
| Document database   | [MongoDB](https://www.mplay.run/mongodb-online-terminal), Couchbase |
| Column store        | Apache HBase, Cassandra                                             |

ตัวสีน้ำเงินสามารถกดไปลองเล่นได้นะ ส่วนรายละเอียดแต่ละตัวเป็นยังไง เดี๋ยวว่างๆจะมาเขียนให้ละกัน ลองติดตามดูได้จาก side menu ละกัน

## 🎯 บทสรุป

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

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