1. 程式人生 > >通過減少 IO 實現效能的優化

通過減少 IO 實現效能的優化

原始地址見 這裡

本文是工作中一點點簡單的思考,不能保證是完全正確的,可能也僅僅是適用於部分場景。

場景 1:獲取使用者關注的好友列表中,每個好友的名字、頭像等資訊。

在很多公司中,不同的服務是由不同的人甚至是不同的部門維護的,這中間會通過一些定義好的介面進行互動(這裡就用 RPC介面來說明了)。假設我們的服務是維護使用者的關注關係,而使用者的基本資訊會維護在使用者服務中。使用者服務提供瞭如下兩個介面:

get_user_info(user_id):
    return {name='name', avatar='avatar'}
    
batch_get_user_info(user_ids):
    return map{user_id=user_info, user_id2=user_info}

假設我們提供給使用者的關注好友列表,開始的時候是先獲取使用者的所有關注好友,然後依次去獲取每個關注好友的資訊,則提供給使用者的介面 P95 可能會是 300ms,初始虛擬碼如下:

def get_following_info():
    following_ids = [1, 2, 4, 5]
    user_infos = []
    for f_id in following_ids:
        user_infos.append(get_user_info(f_id))
    return user_infos

我們知道,和 IO 比起來,一般來說,記憶體計算要快得多,所以,針對以上程式碼,我們可以使用批量介面,直接獲取到所有的使用者資訊。

def get_following_info():
    following_ids = [1, 2, 4, 5]
    return batch_get_user_info(following_ids)

使用了以上程式碼之後,可能介面的效能會提高到 100 ms,此外,由於網路 IO 的減少,介面的錯誤也會少了很多。這就是一個優化。

當然,如果一個使用者關注了很多的好友,可能沒辦法一次獲取到所有的好友訊息,這時候就需要分頁了。

場景2: 批量查詢表中資料

一般情況下,應用伺服器和資料庫儲存並不是在同一臺伺服器上,如果要進行資料庫的查詢操作,很多時候就需要通過網路來進行。

對於 MySQL 來說,資料是按照主鍵來進行物理儲存的,如果表中沒有主鍵,則會設定一個預設的主鍵。二級索引都會查詢到主鍵之後,再次回表查詢。也就是說,根據主鍵,可以認為更快的進行資料的查詢。對於下面的表,如果我們想進行表的掃描,獲取到 user_id,則可以首先獲取到表的最大和最小主鍵 ID,然後批量獲取兩個 ID 之間的資料。

CREATE TABLE `user_name` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `user_id` bigint(20) NOT NULL COMMENT ' 使用者 id',
  `user_name` varchar(64) NOT NULL COMMENT '姓名',
  PRIMARY KEY (`id`),
  UNIQUE KEY `unq_uid` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

select max(id) from `user_name`;
select min(id) from `user_name`;

獲取到了最大最小的 ID 之後,就可以批量獲取兩個 ID 之間的資料了,不過多數情況下,兩個 ID 之間是有幾百萬甚至上億資料的,這時候就需要在程式中設定一個閾值,避免一下獲取太多,反而減緩速度,甚至沒法獲取到。當然,一次獲取資料太多,記憶體也可能存在不足的問題了。

select user_name from `user_name` where 0 < id < 200; 

這樣一來,只需要在程式中設定好每次查詢的資料量,就可以一次查詢多條資料了。