偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java 程序員的 Linux 性能調(diào)優(yōu)寶典:三大經(jīng)典場景深度剖析

系統(tǒng) Linux 開發(fā)
本文針對應(yīng)用延遲、系統(tǒng)卡頓、偶發(fā)頻繁卡頓三種常見的系統(tǒng)故障給出通用普適的排查思路。

本文整理了Linux系統(tǒng)性能問題排查的通用方法論和實(shí)踐,將針對以下三個經(jīng)典場景展開探討:

  • I/O性能瓶頸
  • CPU飆升
  • 偶發(fā)CPU飆升

同時考慮到筆者文章的受眾面大部分都是Java開發(fā)人員,所以復(fù)現(xiàn)問題故障的例子也都采用Java進(jìn)行編碼部署復(fù)現(xiàn),對應(yīng)的示例也都會在案例排查的最后展開說明。

一、應(yīng)用程序延遲升高

第一個案例是用戶反饋系統(tǒng)延遲升高,網(wǎng)卡打開緩慢。從開發(fā)者的角度一定要明白,所有表現(xiàn)為卡頓、延遲的原因很大概率是系統(tǒng)資源吃緊,只有在資源分配不足的情況下,才會導(dǎo)致程序運(yùn)行阻塞,無法及時處理用戶的請求。

關(guān)于服務(wù)器的閾值指標(biāo),按照業(yè)界通用的經(jīng)驗(yàn),對應(yīng)CPU和內(nèi)存的負(fù)載要求的合理上限為:

  • CPU使用率控制在75%左右
  • 內(nèi)存使用率控制在80%以內(nèi)
  • 虛擬內(nèi)存盡可能保持在0%
  • 負(fù)載不超過CPU核心數(shù)的75%

筆者一般會先通過top查看操作系統(tǒng)的CPU利用率,這里筆者因?yàn)槭莻€人服務(wù)器原因則采用更強(qiáng)大、更直觀的htop查看個人服務(wù)器的資源使用情況,對應(yīng)的安裝指令如下:

sudo apt update
sudo apt install htop

而本次htop輸出的指標(biāo)如下:

  • 服務(wù)器為6核,對應(yīng)的CPU使用率分別是3.9、0、0、2.7、0.7、1.4,按照業(yè)界的通用標(biāo)準(zhǔn),當(dāng)前服務(wù)器各核心CPU使用率較低,但需結(jié)合系統(tǒng)負(fù)載綜合判斷
  • Mem代表了內(nèi)存使用率,內(nèi)存一般情況下是用于存儲程序代碼和數(shù)據(jù),分為物理內(nèi)存和虛擬內(nèi)存,物理內(nèi)存顯示內(nèi)存接近8G僅用了1G不到,使用率不到80%,說明資源冗余
  • Swp顯示交換空間即虛擬內(nèi)存的使用情況,可以看到也僅僅用了32M,并沒有大量的內(nèi)存數(shù)據(jù)被置換到交換空間,結(jié)合第2點(diǎn)來看,內(nèi)存資源充足
  • Tasks顯示進(jìn)程數(shù)和線程數(shù)一共有35個進(jìn)程,這35個進(jìn)程對應(yīng)100個線程處理,Kthr顯示指標(biāo)為0說明有0個內(nèi)核線程,而Running為1說明有一個用戶進(jìn)程在運(yùn)行
  • 而系統(tǒng)平均負(fù)載近1分鐘為4.96,按照業(yè)界標(biāo)準(zhǔn)CPU核心數(shù)*0.75作為系統(tǒng)負(fù)載的運(yùn)算閾值,如果超過這個值則說明系統(tǒng)處于高負(fù)載狀態(tài),很明顯我們的6核服務(wù)器系統(tǒng)負(fù)載偏高了

綜合來看,服務(wù)器系統(tǒng)負(fù)載偏高但各CPU核心使用率較低,結(jié)合內(nèi)存使用情況,問題可能出現(xiàn)在I/O資源等待上,此時我們就要從I/O資源角度進(jìn)一步排查問題:

我們從I/O資源排查入手,通過vmstat 1執(zhí)行每秒一次的監(jiān)控指標(biāo)輸出,以筆者的服務(wù)器為例,可以看到如下幾個指標(biāo):

  • r:按照文檔解釋為The number of runnable processes (running or waiting for run time)即正在運(yùn)行或等待運(yùn)行的進(jìn)程數(shù),如果大于CPU核心數(shù)則說明CPU處于過載狀態(tài),而當(dāng)前服務(wù)器這個值為0,說明隊(duì)列處理狀態(tài)良好
  • b::按照文檔解釋為The number of processes blocked waiting for I/O to complete即等待I/O完成的進(jìn)程數(shù),從參數(shù)b可以看出有大量進(jìn)程等待I/O,說明當(dāng)前服務(wù)器存在I/O瓶頸。
  • swpd:the amount of swap memory used即交換空間也就是虛擬內(nèi)存的使用,而當(dāng)前服務(wù)器已被使用30468說明存在緩存置換,由此參數(shù)結(jié)合buff(緩存中尚未寫入磁盤的內(nèi)容)和cache(從磁盤加載出來的緩存數(shù)據(jù))來看,當(dāng)前內(nèi)存資源持續(xù)升高,存在讀寫虛擬內(nèi)存的情況,存在I/O性能瓶頸。
  • 從bo來看有大量任務(wù)進(jìn)行每秒寫塊
  • 針對CPU一個板塊輸出的us(用戶代碼執(zhí)行時間)、sy(內(nèi)核執(zhí)行時間)、id(空閑時間)、wa(等待I/O的時間),其中wa即等待I/O的時間持續(xù)處于一個高數(shù)值的狀態(tài),更進(jìn)一步明確CPU在空轉(zhuǎn),等待I/O完成,而I/O資源處于吃緊的狀態(tài)

考慮為I/O資源瓶頸,我們優(yōu)先從網(wǎng)絡(luò)I/O角度排查問題,這里筆者采用nload進(jìn)行網(wǎng)絡(luò)資源診斷,如果沒有下載的可以自行通過yum或者apt的方式自行下載,這里筆者也貼出ubuntu服務(wù)器的下載指令:

# ubuntu下載安裝nload
sudo apt install nload -y

鍵入nload實(shí)時輸出網(wǎng)絡(luò)帶寬的使用情況,可以看到:

  • 輸入流量(incoming)即下載流量,當(dāng)前網(wǎng)速基本控制在1KB,僅在最大網(wǎng)速的20%左右,一般認(rèn)為只有當(dāng)前網(wǎng)速無限接近于最大網(wǎng)速才可認(rèn)為帶寬使用率接近飽和
  • 輸出流量(outgoing)即上傳流量,同理當(dāng)前也僅僅使用8%,也未達(dá)到飽和的閾值

所以I/O資源吃緊的問題原因并非網(wǎng)絡(luò)I/O,我們需要進(jìn)一步從服務(wù)器磁盤I/O角度進(jìn)一步排查:

所以從這些指標(biāo)來看,存在大量的線程在等待I/O資源的分配而進(jìn)入阻塞,所以筆者基于iostat -x 1使每一秒都輸出更詳細(xì)的信息,可以看到sdd盤對應(yīng)的磁盤忙碌百分比util基本跑滿100%,已基本接近飽和,此時基本是確認(rèn)有大量線程在進(jìn)行I/O讀寫任務(wù)了,且查看I/O讀寫指標(biāo):

  • 每秒讀寫的吞吐量w/s為175
  • 每秒寫入wkB/s的172704KB
  • SSD盤util即I/O資源利用率為100%,已經(jīng)遠(yuǎn)超業(yè)界閾值60%,說明存在I/O性能瓶頸,需要補(bǔ)充說明的是當(dāng)CPU100%時進(jìn)程調(diào)度會因?yàn)椴僮飨到y(tǒng)優(yōu)先級設(shè)置的原因并不會導(dǎo)致進(jìn)程阻塞,但是I/O設(shè)備則不同,它不能區(qū)分優(yōu)先級進(jìn)行I/O中斷響應(yīng),所以這個數(shù)值高的情況下就會使得大量I/O請求阻塞
  • 寫請求的平均等待時間w_await為5151ms

換算下來172704KB/175每秒寫入的速率為987KB每秒,由此可確定因?yàn)榇疟P性能讀寫性能瓶頸導(dǎo)致大量I/O讀寫任務(wù)阻塞,進(jìn)而導(dǎo)致服務(wù)器卡頓,用戶體驗(yàn)差:

所以,對于系統(tǒng)延遲嚴(yán)重的情況,整體排查思路為:

  • 通過top指令查看CPU使用率,若正常進(jìn)入步驟2
  • 基于vmstat查看內(nèi)存使用率和I/O資源情況
  • 基于nload查看網(wǎng)絡(luò)I/O使用情況
  • 基于iostat查看網(wǎng)絡(luò)I/O和磁盤I/O情況最終確認(rèn)問題

本例子的最后筆者也給出本次故障問題的示例代碼:

/**
     * 啟動磁盤I/O操作以模擬高I/O負(fù)載
     * 通過創(chuàng)建多個I/O任務(wù)線程來模擬高磁盤I/O負(fù)載
     */
    private static void startDiskIOOperations() {
        log.info("開始高I/O磁盤操作...");
        log.info("在另一個終端中運(yùn)行 'iostat -x 1' 來監(jiān)控磁盤利用率。");

        // 創(chuàng)建固定線程數(shù)的線程池
        executor = Executors.newFixedThreadPool(NUM_THREADS);

        // 提交多個任務(wù)以連續(xù)寫入磁盤
        for (int i = 0; i < NUM_THREADS; i++) {
            executor.submit(new IOTask(i));
        }

        log.info("磁盤I/O操作已啟動,使用 {} 個線程", NUM_THREADS);
    }


/**
     * 執(zhí)行連續(xù)寫入操作以模擬高I/O的任務(wù)
     * 該類負(fù)責(zé)執(zhí)行磁盤I/O操作,通過不斷寫入和清空文件來模擬高I/O負(fù)載
     */
    static class IOTask implements Runnable {
        private final int taskId;

        public IOTask(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            // 每個線程寫入自己的臨時文件
            String filename = "/tmp/disk_io_test_" + taskId + ".tmp";

            try (FileOutputStream fos = new FileOutputStream(filename)) {
                log.info("線程-{} 正在寫入 {}", taskId, filename);

                // 連續(xù)將數(shù)據(jù)寫入文件并在每次寫入后清空文件
                while (!Thread.currentThread().isInterrupted()) {
                    performDiskIOOperation(fos, taskId);
                    ThreadUtil.sleep(500);
                }
            } catch (IOException e) {
                log.error("線程-{} 發(fā)生錯誤: {}", taskId, e.getMessage());
            }
        }
    }

二、系統(tǒng)操作卡頓

第二個例子也同樣是用戶反饋系統(tǒng)操作卡頓感嚴(yán)重,整體點(diǎn)擊響應(yīng)非常慢,我們還是考慮資源吃緊,優(yōu)先使用top指令查看資源使用情況,從top指令來看:

  • 輸出us查看各個核心的CPU使用率跑滿
  • 系統(tǒng)平均負(fù)載基本超過70%(6*0.7)已經(jīng)超過4.2

這就是經(jīng)典的計(jì)算密集型任務(wù)跑滿所有線程的經(jīng)典例子:

一般針對CPU跑滿的問題,筆者一般會通過mpstat -P ALL 1查看CPU時間片是否分配均衡,是否出現(xiàn)偏斜導(dǎo)致CPU過熱的情況,例如所有運(yùn)算任務(wù)都往一個CPU核心上跑,經(jīng)過筆者每秒1次的輸出持續(xù)觀察來看,整體資源吃緊,但并沒有出現(xiàn)資源分配偏斜的情況,同時內(nèi)存資源使用率也不高,也沒有大量的iowait等待:

結(jié)合第一步top指令定位到的進(jìn)程是Java進(jìn)程,筆者索性通過Arthas直接定位故障代碼,首先通過thread鎖定問題線程,可以看到pool-前綴的線程基本都是跑滿單個CPU,所以我們就可以通過thread id查看線程的棧幀:

最終鎖定到了這段代碼段,即一個密集的循環(huán)運(yùn)算的線程:

圖片

對應(yīng)的筆者也貼出故障代碼段示例,來總結(jié)一下系統(tǒng)使用卡頓的排查思路:

  • 基本top查看用戶態(tài)和內(nèi)核態(tài)CPU使用率
  • 用戶態(tài)使用率偏高,通過mpstat查看CPU使用是否偏斜,是否保證CPU親和力
  • 如果CPU使用沒有出現(xiàn)偏斜,則直接通過問題定位到Java進(jìn)程,結(jié)合Arthas快速定位問題線程進(jìn)行診斷
/**
     * 模擬CPU使用率過高的情況
     * 通過創(chuàng)建多個CPU密集型任務(wù)線程來模擬高CPU使用率
     */
    public static void startHighCPUUsage() {
        log.info("開始模擬高CPU使用率...");

        // 創(chuàng)建CPU密集型任務(wù)的線程池
        ExecutorService cpuExecutor = Executors.newFixedThreadPool(NUM_THREADS);

        // 提交多個CPU密集型任務(wù)
        for (int i = 0; i < NUM_THREADS; i++) {
            cpuExecutor.submit(new CPUIntensiveTask(i));
        }

        log.info("高CPU使用率模擬已啟動,使用 {} 個線程", NUM_THREADS);
    }
 /**
     * CPU密集型任務(wù),用于模擬高CPU使用率
     * 該類通過執(zhí)行復(fù)雜的數(shù)學(xué)計(jì)算來占用CPU資源,從而模擬高CPU使用率場景
     */
    static class CPUIntensiveTask implements Runnable {
        private final int taskId;

        public CPUIntensiveTask(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            log.info("CPU密集型任務(wù)-{} 已啟動", taskId);

            // 執(zhí)行CPU密集型計(jì)算以提高CPU使用率
            while (!Thread.currentThread().isInterrupted()) {
                // 執(zhí)行一些復(fù)雜的數(shù)學(xué)計(jì)算
                double result = 0;
                for (int i = 0; i < 1000000; i++) {
                    result += Math.sqrt(Math.log(i + 1) * Math.cos(i) * Math.sin(i));
                }
                log.debug("CPU任務(wù)-{} 完成一輪計(jì)算,結(jié)果: {}", taskId, result);
            }

            log.info("CPU密集型任務(wù)-{} 已結(jié)束", taskId);
        }
    }

三、持續(xù)的偶發(fā)性系統(tǒng)卡頓問題排查

此類問題比較棘手,系統(tǒng)偶發(fā)卡頓意味著是瞬時、頻繁的資源吃緊,我們還是直接使用top指令無法明確立刻捕捉到進(jìn)程,可能剛剛一看到飆升的進(jìn)程就消失了。

同理使用mpstat、vmstat指令無法準(zhǔn)確定位到超短期飆升問題的進(jìn)程,而基于iostat也沒有看到明顯的I/O資源吃緊,所以我們可以采用perf指令解決問題,以筆者的Ubuntu服務(wù)器為例,對應(yīng)的安裝步驟:

# 安裝perf工具
sudo apt install linux-tools-generic
sudo apt install linux-tools-common

在筆者完成安裝并啟動之后,系統(tǒng)拋出WARNING: perf not found for kernel xxxx的異常,對應(yīng)的解決方案是要主動安裝linux-tools-generic并定位到linux-tools目錄下找到自己的generic版本完成符號鏈接,以筆者本次安裝為例就是6.8.0-79:

sudo ln -s /usr/lib/linux-tools/6.8.0-79-generic/perf /usr/bin/perf

完成上述安裝之后,我們就可以通過將頻率降低設(shè)置為99并將監(jiān)控結(jié)果導(dǎo)出到tmp目錄下的perf.data中:

sudo perf record -F 99 -a -g -o /tmp/perf.data sleep 10

可能很多讀者好奇為什么需要將頻率設(shè)置為99Hz,這樣做的目的是為了避免與系統(tǒng)定時器中斷頻率(通常為100Hz)同步,從而避免鎖步采樣導(dǎo)致的偏差。

鎖步采樣是指采樣頻率與系統(tǒng)定時器中斷頻率相同或成倍數(shù)關(guān)系時,采樣點(diǎn)會固定在相同的時間位置上,導(dǎo)致采樣結(jié)果不能準(zhǔn)確反映系統(tǒng)整體的性能狀況。

使用99Hz這樣的素?cái)?shù)頻率可以減少與系統(tǒng)周期性活動同步的概率,從而獲得更全面、更準(zhǔn)確的性能數(shù)據(jù)。

舉個簡單的例子,若我們試圖確定道路是否出現(xiàn)擁堵,且通過24h一次的抽檢查,那么當(dāng)前樣本就可能與交通保持一個平行的同步狀態(tài),例如:

  • 交通車流情況在每天8點(diǎn)-12點(diǎn)擁堵,而我們的程序也是恰好在每天9點(diǎn)采集,那么它就會認(rèn)為交通情況異常擁堵
  • 若每天14點(diǎn)進(jìn)行一次采集那么就避開了交通阻塞的高峰期則會得到一個相反的、也是不正確的結(jié)論

為了規(guī)避相同周期頻率導(dǎo)致的lockstep即鎖同步采樣,我們可以適當(dāng)降低頻率避免與交通周期時間同步,保證一天的數(shù)據(jù)能夠在一個周期內(nèi)被完整地采集到,而本例最好的做法就是將定時間隔改為23h,這樣一來每個23天的樣本周期就會得到一天中所有時間的數(shù)據(jù)就能做到全面地了解到交通情況:

等待片刻后perf指令就會將結(jié)果輸出到perf.data目錄下:

[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.701 MB /tmp/perf.data (586 samples) ]

此時,通過sudo perf report查看報(bào)告,可以看到一個pid為1115751的Java進(jìn)程對應(yīng)線程CPU使用率飆升到86,此時我們就可以基于這條信息到指定的進(jìn)程上查看該線程是否存在密集的運(yùn)算:

最后我們也給出本示例的問題代碼:

/**
     * 模擬CPU瞬間飆高然后降低的情況
     * 實(shí)現(xiàn)每10秒一次的CPU使用率飆高和降低循環(huán)(僅使用單核)
     */
    public static void startCPUSpikeAndDrop() {
        log.info("開始模擬CPU瞬間飆高然后降低...");

        // 創(chuàng)建用于CPU飆高的線程池(僅使用單核)
        ExecutorService spikeExecutor = Executors.newFixedThreadPool(1);

        // 提交單個CPU密集型任務(wù)來制造飆高
        spikeExecutor.submit(new CPUSpikeTask(0));

        log.info("CPU瞬間飆高已啟動,使用 {} 個線程", 1);

        // 每隔10秒切換CPU飆高狀態(tài),實(shí)現(xiàn)周期性飆高和降低
        Thread spikeController = new Thread(() -> {
            boolean isSpiking = true;
            ExecutorService currentExecutor = spikeExecutor;

            while (!Thread.currentThread().isInterrupted()) {
                try {
                    // 等待10秒
                    Thread.sleep(10000);

                    if (isSpiking) {
                        // 停止當(dāng)前的CPU飆高任務(wù)
                        currentExecutor.shutdownNow();
                        log.info("CPU使用率已降低");
                    } else {
                        // 啟動新的CPU飆高任務(wù)
                        currentExecutor = Executors.newFixedThreadPool(1);
                        currentExecutor.submit(new CPUSpikeTask(0));
                        log.info("CPU使用率已飆高");
                    }

                    // 切換狀態(tài)
                    isSpiking = !isSpiking;
                } catch (InterruptedException e) {
                    log.error("CPU飆高控制線程被中斷", e);
                    break;
                }
            }
        });

        spikeController.setDaemon(true);
        spikeController.start();
    }
 /**
     * CPU瞬間飆高任務(wù),用于模擬CPU瞬間飆高然后降低的情況
     * 該類通過執(zhí)行密集的數(shù)學(xué)計(jì)算來模擬CPU使用率的瞬時飆高,并在指定時間后自動停止
     */
    static class CPUSpikeTask implements Runnable {
        private final int taskId;

        public CPUSpikeTask(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            log.info("CPU瞬間飆高任務(wù)-{} 已啟動", taskId);

            // 執(zhí)行空循環(huán)以提高CPU使用率
            while (!Thread.currentThread().isInterrupted()) {
                // 空循環(huán)消耗CPU
            }

            log.info("CPU瞬間飆高任務(wù)-{} 已結(jié)束", taskId);
        }
    }

四、小結(jié)

本文針對應(yīng)用延遲、系統(tǒng)卡頓、偶發(fā)頻繁卡頓三種常見的系統(tǒng)故障給出通用普適的排查思路,整體來說此類問題歸根結(jié)底都是系統(tǒng)資源吃緊,需要找到飽和的資源結(jié)合代碼推測根源并制定修復(fù)策略,以本文為例,通用的排查思路都為:

  • 基于top查看CPU、內(nèi)存、負(fù)載
  • 若CPU未飽和則通過vmstat查看I/O資源使用情況
  • 明確I/O瓶頸通過nload和iostat查詢是網(wǎng)絡(luò)I/O還是磁盤I/O
  • 若上述排查都無果,且CPU負(fù)載偶發(fā)飆高,可通過perf并調(diào)整頻率監(jiān)控系統(tǒng)定位系統(tǒng)中異常運(yùn)行的資源
  • 結(jié)合上述推斷結(jié)果查看是否是異常消耗,如果是則優(yōu)化代碼,反之結(jié)合情況增加硬件資源
責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2020-08-03 07:00:00

Snowflake數(shù)據(jù)庫性能調(diào)優(yōu)

2010-08-31 14:01:48

CSS

2015-08-05 15:42:10

程序員面試問題

2013-03-18 15:07:10

Linux系統(tǒng)性能調(diào)優(yōu)

2015-06-02 09:17:10

程序員學(xué)習(xí)經(jīng)驗(yàn)

2017-11-23 10:38:01

2019-08-29 15:32:03

Zookeeper場景ZAB

2019-05-20 15:28:27

流量 NginxLinux

2011-03-10 14:40:54

LAMPMysql

2020-06-19 07:00:00

LinuxPython IDE

2017-07-21 08:55:13

TomcatJVM容器

2022-10-28 10:23:27

Java多線程底層

2022-09-29 09:35:56

線程池

2013-03-20 17:18:07

Linux系統(tǒng)性能調(diào)優(yōu)

2023-08-16 11:39:19

高并發(fā)調(diào)優(yōu)

2012-06-20 11:05:47

性能調(diào)優(yōu)攻略

2009-02-23 20:45:54

程序員基本素質(zhì)

2021-03-04 08:39:21

SparkRDD調(diào)優(yōu)

2010-03-03 12:57:02

Web

2024-11-11 17:39:01

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號