$ git clone https://github.com/Krados/asiayoapi.git
$ cd asiayoapi
$ docker build -t asiayoapi .
$ docker run -itd --name asiayoapi -p 8090:80 asiayoapi
SELECT
b1.bnb_id,
bnbs.name as bnb_name,
b1.may_amount
FROM
bnbs
INNER JOIN(
SELECT
bnb_id,
SUM(amount) as may_amount
FROM
`orders`
WHERE
created_at >= '2023-05-01'
AND created_at < '2023-06-01'
GROUP BY
bnb_id
ORDER BY
may_amount DESC
LIMIT
10
) as b1 ON bnbs.id = b1.bnb_id;
有些人認為等待 1 秒就算慢,有些人等待 0.01 秒也覺得慢,每個人的判斷標準皆不同,應該要討論一個合理的時間,並朝這一個方向優化。
(1)添加 index:ALTER TABLE `orders` ADD INDEX `idx_created_at_bnb_id_amount` (`created_at`, `bnb_id`, `amount`);
(2)塞入假資料, 2022-2023 年 1-12 月份每個月有 100000 筆資料, 每個月平均有 10000 家不同的旅宿, 共計 2400000 筆資料
(3)下 EXPLAIN 命令檢查是否有使用到 index
+----+-------------+------------+------------+--------+------------------------------+------------------------------+---------+-----------+-------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+------------------------------+------------------------------+---------+-----------+-------+----------+-----------------------------------------------------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL |
| 1 | PRIMARY | bnbs | NULL | eq_ref | PRIMARY | PRIMARY | 8 | b1.bnb_id | 1 | 100.00 | NULL |
| 2 | DERIVED | orders | NULL | range | idx_created_at_bnb_id_amount | idx_created_at_bnb_id_amount | 4 | NULL | 22004 | 100.00 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+------------+------------+--------+------------------------------+------------------------------+---------+-----------+-------+----------+-----------------------------------------------------------+
(4)查看加入 index 後,執行時間是否有提升,同樣使用題目 1 的Query,有加入 index 的查詢時間約為 0.0133 秒,沒有使用 index 則為 0.1296 秒,相差約 10 倍。
缺點:
index 為空間換時間的做法,index 佔的空間大小需要考慮。
資料到達某一個數量級別後 index 查詢時間提升不多。
(1)將每年每月的資料另外開一張表 ex:
CREATE TABLE `order_202305_sums` (
`id` bigint NOT NULL AUTO_INCREMENT,
`bnb_id` bigint NOT NULL,
`amount` bigint NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_amount` (`amount`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
缺點:
需要另外一個 Worker 程序來定時計算並把資料搬移到指定的年月表內。
跨月查詢涉及到多表,些許增加程式的複雜度。
這一張表只有一個 usecase 能使用。
S:
(1)將訂單格式的檢查拆成多個檔案方便後續擴充,每個檔案很純粹的負責一個邏輯。ex:App\FormatChecks\OrderWithCurrency, App\FormatChecks\OrderWithName, App\FormatChecks\OrderWithPrice
(2)將訂單轉換邏輯拆成多個檔案方便後續擴充,每個檔案很純粹的負責一個邏輯。ex:App\Transforms\OrderWithCurrency
O:
(1)在 OrderController 中依賴於介面 IOrderService 而不是實例,方便後續抽換底層的實作而不需要再次更改 OrderController。
I:
(1)在 IOrderService 中盡可能不要讓此介面變成一個寬泛用途的介面。
D:
(1):在 OrderController 中依賴於介面 IOrderService,實例的注入依靠 laravel 的 IOC 來實現依賴反轉,而不是直接在 OrderController 內直接創建一個實例。
Strategy Pattern:在 OrderController 中依賴於介面 IOrderService 而不是實例,可以在注入階段決定實作為何。
Option Pattern:OrderService 的 checkFormat 和 transform method 可以動態的給定待執行的邏輯,而不是使用一堆的 if-else 判斷式。