# 游æ????ç«??§è?ä¸?QPS ????¥å? ## ä¸???????ä¸?????è§? | 项ç? | 说æ? | |------|------| | è¿????? | Webman (?ºä? Workerman)ï¼?¸¸é©»å?å­?| | HTTP ??? | `http://0.0.0.0:6688` | | Worker ?°é? | `cpu_count() * 4`ï¼?? 8 ??= 32 ä¸??ç¨?? | | ?°æ?åº?| MySQLï¼?hinkORMï¼???¥æ? max=20/min=2 | | ç¼?? | Redisï¼?hink-cache é»?? `redis`ï¼??è¿??æ±?max=20/min=2 | | ç¼??驱å?注æ? | `config/cache.php` é»?? `file`ï¼??ä¸??使ç?该é?ç½??ç¼??èµ°æ?ä»¶ï?`config/think-cache.php` é»?? `redis`ï¼?PI ?¨æ?/å¥????½®ç­?½¿??Redis | --- ## äº????å¿???£ä????æ±???? ### 2.1 é«?¹¶????³æ??? | ?¥å? | ?¨é??| 主è???? | é¢?¼°??????ï¼??éª??¼ï? | |------|------|----------|------------------------| | `POST /api/game/playStart` | å¼?å§??å±?游æ?ï¼??å¥?? | å¤?? DB + Redis + äº?? | é«??è§??ï¼?| | `POST /api/game/buyLotteryTickets` | è´?¹°?½å???| äº?????表å???+ Redis ?´æ? | ä¸?| | `GET /api/game/config` | 游æ???½® | ?¨è¡¨ DiceConfig ?¥è?ï¼??ç¼?? | ä½?ä¸?| | `GET /api/game/lotteryPool` | å¥????½® | DiceRewardConfig ç¼?? | ä½?| | `POST /api/v1/getGameUrl` | ?·å?游æ??°å?ï¼?¹³?°ï? | ?»å??»è? + JWT + Redis | ä¸?| | `POST /api/v1/getPlayerInfo` | ?©å?ä¿¡æ? | ??username ??DicePlayer | ä½??建è? username ???ä¸?ç´¢å?ï¼?| | `POST /api/v1/getPlayerGameRecord` | 游æ?è®°å???¡¨ | ??¡µ + ??? N+1 ?¥ç?å®?| ä¸?| | `POST /api/v1/setPlayerWallet` | ?±å?è½??è½?? | äº??ï¼???°ç?å®?+ ???æ°?| ä¸?| ### 2.2 `playStart` ???请æ??????§£ï¼??§è??³é?è·??ï¼? ???游æ?ä¸?次è?æ±?¤§?´ä?è§??ï¼? **?°æ?åº??** 1. `DicePlayer::find($playerId)` ???¡é??©å?ä¸??é¢?2. `DiceRewardConfig::getCachedMinRealEv()` ??é¦??????¥å?å¹¶å?ç¼?? 3. `LotteryService::getOrCreate()` ?????ä¸?Redis ?¶ï?`DicePlayer::find` + `DiceLotteryPoolConfig::where('type',0/1)->find()` 两æ? 4. `DiceLotteryPoolConfig::find($configId)` ?????ç½?ID ???æ±?5. **äº????*ï¼? - `DicePlayRecord::create` - `DicePlayer::find($playerId)`ï¼??次æ??©å?ï¼??ä¼??ï¼? - `DicePlayer::save` - ??? `DicePlayerTicketRecord::create`ï¼?5 ä¸??ï¼? - `DiceLotteryPoolConfig::where('id')->update(['ev' => Db::raw(...)])` ??è¡?º§?´æ? - `DicePlayerWalletRecord::create` 6. äº?????`DicePlayer::find($playerId)`??UserCache::setUser` **Redisï¼?* - `DiceRewardConfig::getCachedInstance()`ï¼?? getCachedByTier ç­????è¯?????±é?ç½??å­?- `LotteryService::getOrCreate()` ??è¯????å®¶å?æ±??å­?- `UserCache::setUser()` ??????·ä¿¡???å­? ?¨ç?å­??ä¸??好ç????ä¸????? `playStart` ä»?º¦??**6ï½? æ¬?DB 访é?**ï¼??äº????2 æ¬?find ?©å??? æ¬?update 彩é?æ±???ï½? æ¬?createï¼??? ?¥ç?å­???½ä¸­ï¼??ä¼??????±é?ç½????æ±??ç½????å®¶ç??¥è?次æ??? --- ## ä¸????§è??¶é?ä¸???©ç? ### 3.1 ?°æ?åº? 1. **è¿??æ±?? Worker ??* - ??? DB è¿??æ±?max=20ï¼?orker ??= CPU?4??? 32 ä¸?Worker ?????»¡ï¼??äº?? 20 ä¸???¥ï??ºç?ç­??ä¸???¶ã?? - 建è?ï¼??????? `DB_POOL_MAX`ï¼?? 32ï½?4ï¼??å¹¶ç???MySQL `max_connections` ä¸???¥è??? 2. **`playStart` ???å¤???©å?** - äº??å¤?·² `DicePlayer::find($playerId)`ï¼???¡å???`DicePlayer::find($playerId)`?? - ???为å?äº?????????¨å·²??`$player` ????¥å?è¦??段ï????ä¸?æ¬?SELECT?? 3. **`DiceLotteryPoolConfig::where('id', $configId)->update(['ev' => Db::raw(...)])`** - æ¯???½æ??°å?ä¸???½®è¡?? `ev`ï¼??å¹¶å?ä¸??è¡??ç«?????ï¼???½æ?为ç?é¢??? - ?????ï¼??步累??????¶é?/次æ??¹é??´æ?ï¼??使ç? Redis 计æ?????¶å?æ­¥å? DB?? 4. **ç´¢å?** - `dice_player.username`??dice_play_record.player_id`??dice_play_record.create_time`?? `dice_player_wallet_record.player_id`??dice_player_ticket_record.player_id` ç­????»ºç´¢å?ï¼??表ä?ç»??ä¼?????? - 建è??³å?ï¼?username` ???ç´¢å?ï¼?player_id` + `create_time` ç»??ç´¢å?ï¼??å®???¥è??¡ä»¶å¾??ï¼??? ### 3.2 ç¼?? 1. **ç¼??驱å?ä¸????* - `config/cache.php` é»?? `file`ï¼??ä¸?????该é?ç½?? Cacheï¼??èµ°æ?ä»¶ï?QPS é«?? I/O ???ä¼??为ç?é¢??? - 建è?ï¼??产ç?ä¸?使ç? Redisï¼?¹¶ç¡?? `CACHE_MODE=redis` ä¸?think-cache ??default ä¸??´ã?? 2. **å¥????½®ç¼??** - `DiceRewardConfig::getCachedInstance()` å·²å???????????+ Redisï¼??ä¸???¶æ?§è?好ã?? - ??ä¿????½®????¶è???`refreshCache()`ï¼?????????·æ??? 3. **UserCache ??§£å¯?* - æ¯?? `getUser`/`setUser` ??AES ??§£å¯??é«?QPS ä¸?CPU ä¼?????ä½???¸¸ä¸??é¦???¶é?ï¼???????? CPU é«?????????¡é???¼©??value?? ### 3.3 ??¡¨?¥å? N+1 - `getPlayerGameRecord`ï¼????¡µ??`DicePlayRecord`ï¼?? `whereIn('id', $playerIds)` ?¥ç?å®¶å¹¶ç»???? å½??å®??å·²å??¹é??¥ç?å®¶ï?ä¸???¸å? N+1ï¼???¥å?é¡?limit å¾?¤§ä»????????å¿??å­???????limit ä¸??ï¼?·²??100 ä¸??ï¼??? - `getPlayerWalletRecord` / `getPlayerTicketRecord` 使ç? `with(['dicePlayer'])` é¢??è½½ï?????? ### 3.4 è¿??ä¸???? - Worker ??= CPU?4 ?¶ï??¥æ?ä¸??æ±????1 ä¸?DB è¿??ä¸??æ±???´é?ï¼?0 ä¸???¥æ?è¢??满ã?? - 建è?ï¼?????ä¸??å¯?`DB_POOL_WAIT_TIMEOUT` ???ç»?¸¸è§??ï¼???³å????å¢?¤§æ±????????äº???? --- ## ????PS é¢?¼°ï¼??éª??¼ï???????¡å?ï¼? ### 4.1 ????¡ä»¶ - 8 ??CPUï¼?2 ä¸?Worker??- MySQL/Redis ??????å»¶è????ï¼??ç£???¶é???- ç¼???½ä¸­???ï¼???±é?ç½???otteryService??serCache å¤?¸º?½ä¸­ï¼??? ### 4.2 ????£ç?ç®? - **playStart**ï¼??次约 50ï½?50 msï¼??äº??ä¸??æ¬?DB/Redisï¼????Worker çº?7ï½?0 QPSï¼?2 Worker çº?**220ï½?40 QPS**?? å®????DB è¡??ï¼?? `dice_lottery_config.ev` ?´æ?ï¼?????¥æ?ç­??å½±å?ï¼?*ä¿??ä¼°è???? 200ï½?00 QPS**??- **buyLotteryTickets**ï¼??次约 20ï½?0 msï¼???ºçº¦ **500ï½?000+ QPS**ï¼?»¥è¿??æ±?????满为???ï¼???- **getGameUrl / å¹³å??¥å?**ï¼??èµ??å½?? DB ?¥è?ï¼??次约 30ï½?0 msï¼???ºçº¦ **400ï½?00 QPS**ï¼?????èµ°ç?å­???? ### 4.3 æ··å?æµ?? ??70% ä¸?`playStart`??0% 为å?ä»???£ï??´ä???? QPS ä¼?? `playStart` ?¾æ?ï¼?*综å????çº?250ï½?00 QPS** ???为å?????´ã?? å®??????ab/wrk/k6 ç­?? `playStart` ??¸»è¦???£å?æµ??ç»?? MySQL ?¢æ?è¯???edis ?½ä¸­??????¥æ?ç­??次æ???????? --- ## äº??????»ºè®???? | ç±»å? | 建è? | |------|------| | ??½® | ??º§è®¾ç½® `CACHE_MODE=redis`ï¼?¹¶ç»??使ç? Redis ä½?¸º Cache 驱å? | | è¿??æ±?| ??? `DB_POOL_MAX`ï¼?? 32ï½?4ï¼??ä¿?? ??Worker ?°ï?å¹¶ç???MySQL max_connections | | 代ç? | `playStart` äº??????¨å·²?¥ç? `$player`ï¼?????æ¬?`DicePlayer::find($playerId)` | | ?°æ?åº?| ä¸?`username`??player_id`??create_time` ç­??é¢??询å?段å?ç´¢å??????´¢å¼?| | ????´æ? | `dice_lottery_config.ev` ???å±??´æ??¹ä¸º Redis ç´?? + å®??/?¹é???? DBï¼??ä½?????äº?| | ??? | å¯?`playStart`??buyLotteryTickets`??setPlayerWallet` ?????ä¸??è¯?????ï¼?¹¶??MySQL ?¢æ?è¯??è¿??æ±??å¾????| | ??? | 使ç? ab/wrk/k6 å¯?`/api/game/playStart` ç­???¶æ????ï¼???°å???P99 å»¶è?ä¸??å¤?QPS | --- ## ?????ä½???°ç?å®?QPS ä¸?P99 1. **??? playStart** - 使ç???? JWTï¼??????»å??¿å? tokenï¼??å¯?`POST /api/game/playStart` ??body `direction=0` ??`1`?? - 示ä?ï¼???¿æ? URL ??tokenï¼?? ```bash # 使ç? ab ab -n 1000 -c 32 -p post_body.json -T application/json -H "token: YOUR_JWT" http://127.0.0.1:6688/api/game/playStart # 使ç? wrkï¼?? lua ???ä¼?token ??bodyï¼? wrk -t4 -c32 -d30s -s play_start.lua http://127.0.0.1:6688/api/game/playStart ``` - ä»????¸­å¾?? Requests per secondï¼?PSï¼?? Time per requestï¼???¢ç? P99 ?¥å·¥?·æ?????? 2. **??????** - åº??ï¼??ä¸???£ç?请æ?????¹³?? P99 ?????xx ä¸???¶æ??°ã?? - MySQLï¼???¥æ?????¥è???nnoDB è¡??ç­???? - Redisï¼???¥æ????ä¸?????令è????? 3. **?¶é??¤æ?** - ??QPS ä¸????CPU ???æ»???ä¼?????代ç?????ºå??? - ??DB è¿??ç­??????¥è?å¢?? ????´¢å¼?????????è¿??æ±???????¡¨?? - ??Redis å»¶è?ä¸?? ???¥å¤§ key????½ä»¤???ç»??è¿??æ±??? --- ## ä¸????ä½??é«??§è?ï¼???????? ??*???产å?æ¯?*ä»???°ä????ï¼????????项å?????¾æ???QPS ä¸?¨³å®??§ã?? ### 7.1 ??½®å±???¹é?ç½???????»£?????? | ä¼??çº?| ??? | 说æ? | |--------|------|------| | é«?| ??º§???设置 `CACHE_MODE=redis` | ?¿å?èµ°æ?ä»¶ç?å­????? I/O ä¸??ç«??ï¼?? think-cache ä¿??ä¸??´ã??| | é«?| ??? DB è¿??æ±??`DB_POOL_MAX=32` ??`64` | ä¿?? ??Worker ?°ï?å¦?32ï¼???¿å?é«?¹¶???è¿??ç­??ä¸???¶ã??| | ä¸?| ??? Redis è¿??æ±??`REDIS_POOL_MAX=32` | ä¸?Worker ?°å??????? Redis ?·å?è¿??ç­????| | ä¸?| ç¡?? MySQL `max_connections` ??åº??è¿??æ±??»å? | å¤??ä¾??ç½²æ?ï¼??ä¾?? ? DB_POOL_MAX ?¿è?è¿?MySQL ä¸????| **示ä? .env ???ï¼?* ```env CACHE_MODE=redis DB_POOL_MAX=32 DB_POOL_MIN=4 REDIS_POOL_MAX=32 ``` ### 7.2 ?°æ?åº??ï¼??ç´¢å??????? | ä¼??çº?| ??? | 说æ? | |--------|------|------| | é«?| ä¸?`dice_player.username` 建å?ä¸?ç´¢å? | ?»å???etPlayerInfo??oken ä¸??ä»¶å???username ?¥ï???´¢å¼???¨è¡¨?????| | é«?| ä¸?`dice_play_record(player_id, create_time)` 建ç???´¢å¼?| 游æ?è®°å???¡¨???计æ??©å?+?¶é??¥è?ï¼?????页ä?ç»????| | ä¸?| 为æ?水表 `player_id`??create_time` 建索å¼?| å¦?`dice_player_wallet_record`??dice_player_ticket_record` ???å®¶æ??¶é?ç­?????¨å?ä¸???| **示ä? SQLï¼??å®??表å?è°??ï¼??** ```sql -- ?¥å?????¨å?æ·»å? ALTER TABLE dice_player ADD UNIQUE INDEX uk_username (username); ALTER TABLE dice_play_record ADD INDEX idx_player_create (player_id, create_time); ALTER TABLE dice_player_wallet_record ADD INDEX idx_player_create (player_id, create_time); ALTER TABLE dice_player_ticket_record ADD INDEX idx_player_create (player_id, create_time); ``` ### 7.3 代ç?å±????? DB è°??ä¸???¹é?ï¼? | ä¼??çº?| ??? | 说æ? | |--------|------|------| | é«?| `playStart` äº??????¨å·²?¥ç? `$player`ï¼????`DicePlayer::find($playerId)` | å°?1 æ¬?SELECTï¼???¡å??¨å?ä¸? `$player` ???å­????? 1 æ¬?SELECT??| | ä¸?| `game/config` ?¥å?ä¸?DiceConfig ???å­?| ??½®???é¢??ä½????Redis ç¼?? 1ï½? ???ï¼??å°??表æ?è¯???| | ä¸?| `dice_lottery_config.ev` ?¹ä¸º Redis ç´?? + å®????? DB | æ¯??ä¸?? `UPDATE dice_lottery_config SET ev=ev-?`ï¼?????è¡???¹é?ï¼??æ¯?????æ¯?N å±????ä¸?次ã??| ### 7.4 ???ä¸???? | ä¼??çº?| ??? | 说æ? | |--------|------|------| | ä¸?| å¯?`playStart`??buyLotteryTickets` ?????ä¸??è¯????? | 便ä?????¢è?æ±??å¼?¸¸æµ????| | ä¸?| å¼???MySQL ?¢æ?询æ?å¿??å¦?>200msï¼?| å®??????´¢å¼??ä¼????SQL??| | ä½?| ???å¾????? QPS ä¸?P99 | ??ab/wrk/k6 å¯?`playStart` ???ï¼?»¥?°æ?å®??????©å??¶æ???| ### 7.5 é¢?????ï¼??éª??¼ï? - ??½®å±?+ ç´¢å?ï¼???¥ä????为ç?é¢????¡¨ä¸??å½???£æ??¾å?å¿???´ä??????º¦ **20%ï½?0%** ??? QPS??- ?»æ? playStart ??2 次å?ä½??åº?????æ±?? 2 æ¬?round-tripï¼?*playStart ???æ±????çº?? 5%ï½?5%**??- ev ?¹ä¸º Redis ç´??ï¼??å¹¶å?ä¸?**playStart** è¡??ç«??ä¸??ï¼????playStart QPS ????????**10%ï½?0%**ï¼??å¹¶å?åº????ï¼??? --- **说æ?**ï¼??è¿?QPS ä¸??????¸º?ºä?代ç?ä¸??ç½??ä¼°ç?ï¼?????§è?以å?æµ??线ä????为å???»ºè®??é¢??/??º§??????å°??è½?`playStart` ä¸??å½????????³æ??£ç??????