Mình làm web LmssPlus cho phép tra cứu thông tin cá nhân game Liên Minh , có 1 phần cực khoai (đối với mình) đó là người chơi muốn biết con tướng họ đang sử dụng có điểm thông thạo đứng thứ bao nhiêu trong toàn server VN.
Để mình giải thích một chút cho bạn nào không chơi Liên Minh nhé. Đại khái là bạn sẽ có nhiều con tướng khác nhau để lựa chọn trong tổng số 150 con tướng, tuy nhiên mọi người thay vì chơi tất cả 150 con tướng, họ thường chỉ chơi khoảng chục con, và đến khoảng 30 con tướng là nhiều. Với mỗi lần chơi con tướng đó bạn sẽ được cộng thêm điểm thông thạo.
Vậy là nhu cầu của người dùng ở đây:
- Họ muốn biết con tướng X họ chơi có thông thạo đứng thứ bao nhiêu trong server.
- Họ muốn biết top những người chơi nhiều thông thạo nhất server của con tướng X.
Suy nghĩ đầu tiên của mình khá đơn giản, mình dùng Mongodb, trước tiên là model
1 2 3 4 5 6 | <span class="token keyword">var</span> chema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span> accountId<span class="token operator">:</span> Number<span class="token punctuation">,</span> <span class="token comment">//người chơi</span> championId<span class="token operator">:</span> Number<span class="token punctuation">,</span> <span class="token comment">//tướng</span> point<span class="token operator">:</span> Number<span class="token punctuation">,</span> <span class="token comment">//điểm thông thạo</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
- Index { accountId: 1, championId: 1 } để upsert dữ liệu (khi người dùng thay đổi điểm thông thạo).
- Index { championId: 1, point: 1 } để lấy rank.
Câu truy vấn rank thế này:
1 2 | Ssrank<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span>championId<span class="token punctuation">,</span> point<span class="token operator">:</span> <span class="token punctuation">{</span>$gt<span class="token operator">:</span> point<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
Vậy là rank của tướng championId đơn giản là kết quả trên cộng 1.
Ta cần lấy thứ hạng của tất cả các con tướng của họ chơi, rồi hiển thị con tướng có thứ hạng cao nhất, như mình là top 731 Katarina.
Vậy câu truy vấn sẽ trông như thế này:
Với mỗi 1 người dùng vào ta mất khoảng 10-30 truy vấn, tốc độ trả về kết quả khoảng 300-500ms, kết quả con server của mình bị tình trạng này đây:
Dữ liệu sẽ được update mới ngày đêm (có 1 cron làm nhiệm vụ lấy thông tin và cập nhật vào db) và khi người dùng vào web (để có dữ liệu mới nhất)
Hmm, sau khi đọc thêm 1 vài blog, mình quyết định dùng Redis với ưu điểm cực nhanh, và lượng ram sử dụng cũng chỉ tương đương với mongodb (1)
Trước tiên mình viết 1 function để đem dữ liệu từ mongodb qua redis, ở bên mongodb thì mình bỏ collection ssranks
để đỡ tốn ram vào index không cần thiết, thay vào đó data tướng sẽ được lưu vào 1 field champions
trong collection accounts
.
Key
sẽ là cp_id_tướng, score
sẽ là điểm thông thạo, và value
là accountId
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token comment">//khi upsert</span> redis<span class="token punctuation">.</span><span class="token function">zadd</span><span class="token punctuation">(</span><span class="token string">"cp_"</span> <span class="token operator">+</span> championId<span class="token punctuation">,</span> point<span class="token punctuation">,</span> accountId<span class="token punctuation">)</span> <span class="token comment">//truy vấn lấy thứ hạng của tất cả các con tướng của họ chơi</span> <span class="token keyword">await</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span> champions<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> championId<span class="token punctuation">,</span> point <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> redis<span class="token punctuation">.</span><span class="token function">zrevrank</span><span class="token punctuation">(</span><span class="token string">"cp_"</span> <span class="token operator">+</span> championId<span class="token punctuation">,</span> accountId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">rank</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> championId<span class="token punctuation">,</span> rank<span class="token operator">:</span> rank <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> point<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">//à lấy top 10 người chơi có thông thạo đứng đầu server của tướng thì như này</span> <span class="token keyword">let</span> toprank <span class="token operator">=</span> <span class="token keyword">await</span> redis<span class="token punctuation">.</span><span class="token function">zrevrange</span><span class="token punctuation">(</span><span class="token string">"cp_"</span> <span class="token operator">+</span> championId<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token string">"WITHSCORES"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Và kết quả
- Tốc độ 30ms
- Cpu giảm, ổn định ở mức 5%
Trên đây là những gì mình làm, hiểu biết mình còn hạn hẹp nên có lỗi lầm nào mong các bạn lượng thứ. Mình cảm ơn!
Ref: