1. 程式人生 > 其它 >負載均衡的計算方法和含義

負載均衡的計算方法和含義

技術標籤:linux系統性能

我們在執行top 或者uptime時,會計算當前系統的負載平衡。
當前系統的負載平衡是看我們cpu對於當前執行任務的一個執行情況。同時也判斷我們系統的任務
對於cpu的需求情況。
這句話是對於負載平衡的一個很好的總結:

The CPU percentage shows us how much the cars are using the freeway, but the load averages show us the whole picture, including pent-up demand.

以下是我收集的負載平和的計算方法
負載的檢視

在Linux系統中,使用uptime、w和top命令都可以檢視負載,或者直接通過cat /proc/loadavg檢視。

比如說uptime命令:

20:56:33 up 22 min, 3 users, load average: 0.39, 0.25, 0.10

可以看到有三個值,這三個值分別表示:

前1分鐘系統的平均負載
前5分鐘系統的平均負載
前15分鐘系統的平均負載

下面就來看看負載的含義。
負載的含義

負載的含義是執行佇列的平均長度。

具體而言,對於Linux系統,其統計了最近1分鐘、最近5分鐘、最近15分鐘處於running或者uninterruptible sleep狀態的程序數。

文末參考資料裡給出了一個車道的例子進行類比,這兒就不贅述了。舉幾個例子:

負載為0.5,表示CPU有平均一半的時間是空閒的
負載為1,這時就表示CPU被充分利用了
負載為2,則表示負載過大,有一半的任務在等待CPU執行

一般認為負載為0.7是警戒線。
負載與多處理器

對於多處理器,相當於多車道。比如對於雙處理器,那麼負載為2才算滿。 可以簡單地將負載除以CPU核心數換算成單處理的情況。
負載與CPU使用率

文末參考資料中提到:

The CPU percentage shows us how much the cars are using the freeway, but the load averages show us the whole picture, including pent-up demand.

相比於CPU使用率,負載可能展示更多的資訊,即CPU的需求量。
負載和多執行緒

Wikipedia提到在不同的Unix系統中多執行緒負載的計算方式有很多種,比如執行緒與核實體間不同的模型就有所不同。而在Linux系統中每個執行緒應該都是獨立計算的。

負載的計算

我並不瞭解統計學等知識,以下只是個人一些粗淺的理解。

負載計算原理是統計學的EWMA(Exponentially Damped/Weighted Moving Average),利用指數衰減函式遞推出當前序列平均值。

Wikipedia裡提到:

Hence, the 1-minute load average consists of 63% (more precisely: 1 - 1/e) of the load from the last minute and 37% (1/e) of the average load since start up, excluding the last minute. For the 5- and 15-minute load averages, the same 63%/37% ratio is computed over 5 minutes and 15 minutes respectively. Therefore, it's not technically accurate that the 1-minute load average only includes the last 60 seconds of activity (it actually includes 37% of the activity from the past), but it is correct to state that it includes mostly the last minute.

即統計計算出一段時間的負載,其本質是計算均值。比如說:

    要計算——最近x分鐘負載:$la_{-x}$
    假設已知——自系統啟動到x分鐘前的負載為:$la_{+x}$
    假設可以得到——近x分鐘的執行的任務數為:$n_x$

那麼 l a − x = l a + x t i m e s ( e − 1 ) + n x t i m e s ( 1 − e − 1 ) la_{-x} = la_{+x} times (e^{-1}) + n_x times (1-e^{-1}) lax=la+xtimes(e1)+nxtimes(1e1) l a − x = l a + x t i m e s 0.37 + n x t i m e s 0.63 la_{-x} = la_{+x} times 0.37 + n_x times 0.63 lax=la+xtimes0.37+nxtimes0.63

可以看出 x x x分鐘前的負載權重是比較小的,隨著時間的推移前面的資訊影響會越來越小,直到趨近於0,最近時間的資訊影響的比重比較大。

而在Linux系統中,是每間隔 5 H Z 5HZ 5HZ或者 5 秒 5秒 5統計一次執行的任務數,然後計算均值。那麼隨著時間推移,可以得出這麼一個序列:

n 1 , n 2 , n 3 , . . . , n t − 1 , n t n_1, n_2, n_3, ..., n_{t-1}, n_t n1,n2,n3,...,nt1,nt

當前時刻 t t t的Moving Average—— m v t mv_t mvt同樣根據 m v t − 1 mv_{t-1} mvt1計算出:

m v t = m v t − 1 t i m e s ( e − l a m b d a ) + n t t i m e s ( 1 − e − l a m b d a ) mv_t = mv_{t-1} times (e^{-lambda}) + n_t times (1-e^{-lambda}) mvt=mvt1times(elambda)+nttimes(1elambda)

對於要統計最近1分鐘(60秒)的均值,由於時間間隔為5秒,那麼 l a m b d a lambda lambda的取值應為 f r a c 560 frac 5{60} frac560;同理,最近5分鐘(300秒)的均值, l a m b d a lambda lambda的取值應為 f r a c 5300 frac 5{300} frac5300;最近15分鐘(900秒)的均值, l a m b d a lambda lambda的取值應為 f r a c 5900 frac 5{900} frac5900

下面來看看Linux中如何實現負載的計算。
負載計算的實現

Linux核心中維護了一個Jiffies變數,記錄自開機一開經歷的Tick數,每次更新Jiffies時系統會呼叫timer.c中的do_timer();

unsigned long avenrun[3];
static inline void (unsigned long ticks)
{
	unsigned long active_tasks; 
	static int count = LOAD_FREQ;
	count -= ticks;
	if (count < 0) {
		count += LOAD_FREQ;
		active_tasks = count_active_tasks();
		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
		CALC_LOAD(avenrun[1], EXP_5, active_tasks);
		CALC_LOAD(avenrun[2], EXP_15, active_tasks);
	}
}

在sched.h中可以看到上面calc_load()裡一些變數、巨集的定義:

extern unsigned long avenrun[];	/* Load averages */
#define FIXED_1  (1<<FSHIFT)	/* 1.0 as fixed-point */
#define LOAD_FREQ (5*HZ)	/* 5 sec intervals */
#define EXP_1  1884		/* 1/exp(5sec/1min) as fixed-point */
#define EXP_5  2014		/* 1/exp(5sec/5min) */
#define EXP_15 2037		/* 1/exp(5sec/15min) */
#define CALC_LOAD(load,exp,n) 
	load *= exp; 
	load += n*(FIXED_1-exp); 
	load >>= FSHIFT;

可以看到,每次Jiffies+1時呼叫calc_load(),並不會馬上統計計算負載資訊,而是等到一個LOAD_FREQ的週期結束,隨後又開始新的LOAD_FREQ週期;

通過呼叫count_active_tasks()可以獲得處於running或者uninterruptible sleep狀態的程序數;

最後執行三次的CALC_LOAD,分別更新統計的1分鐘、5分鐘和15分鐘的負載資訊。

值得一提的就是CALC_LOAD的實現,其本質是將上面的Moving Average計算的式子乘以 2 11 2^{11} 211,最後再除以 2 11 2^{11} 211,採用位運算並且避免了浮點數運算,提高了計算效率。