成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

1.5s~0.02s,期間我們可以做些什么?

novo / 2686人閱讀

摘要:而且需要在特定的菜單位置上顯示待辦事項(xiàng)的數(shù)量。以我的博客某篇文章加載為例最右邊有個(gè)紅框標(biāo)識(shí)的就是每條資源的加載耗時(shí),我們可以看到第一條是服務(wù)端的處理速度。接下來我們就可以直接去看代碼了。在大腦中構(gòu)思了一下,其實(shí)這些完全可以通過遞歸來實(shí)現(xiàn)嘛。

原文是在我自己博客中,小伙伴也可以點(diǎn)閱讀原文進(jìn)行跳轉(zhuǎn)查看,還有好聽的背景音樂噢背景音樂已取消~ 2333333

大爺我就算功能重做,模塊重構(gòu),我也不做優(yōu)化?。?!

運(yùn)行真快!


前言

本文主要探討的核心是【為什么不要在循環(huán)中使用數(shù)據(jù)庫(kù)操作?】
用了一個(gè)例子來說明為什么不要這樣做的原因以及當(dāng)遵循了這條規(guī)則后,所帶來的好處:代碼運(yùn)行效率的提升、心情好(亂入-_-)之類的。

起因

最近在對(duì)一個(gè)老項(xiàng)目進(jìn)行維護(hù)的時(shí)候,發(fā)現(xiàn)有一個(gè)頁(yè)面加載很耗時(shí),響應(yīng)速度在1.7s以上,而且這個(gè)頁(yè)面粗略看起來需要加載的東西也不是很多,為什么加載會(huì)這么慢呢?本著一探究竟和對(duì)這些慢響應(yīng)無法忍受的態(tài)度去看了一下,發(fā)現(xiàn)它的代碼寫的很糟糕,到處都是循環(huán),而且還在循環(huán)中進(jìn)行了sql查詢。后來在自己的優(yōu)化下,從均加載1.5s到均0.02s,實(shí)現(xiàn)了一個(gè)質(zhì)的飛躍。
本文,就是總結(jié)一下,自己在遇到這種代碼的處理方式,以及思想的演化

介紹

本文所要優(yōu)化的是一段,由權(quán)限控制的菜單,共有兩級(jí)。而且需要在特定的菜單位置上顯示待辦事項(xiàng)的數(shù)量。普普通通的一段權(quán)限控制菜單訪問的功能,其實(shí)處理起來也就是多了一個(gè)【特定菜單位置上顯示代辦數(shù)量】的功能,簡(jiǎn)單思考一下,只要找到對(duì)應(yīng)的菜單id,在其上面增加一個(gè)對(duì)應(yīng)的數(shù)字就可以了。想是這么想,做起來呢?

確定問題所在

遇到網(wǎng)頁(yè)加載很慢的時(shí)候,首先要確定到底是哪一部分加載很慢??梢酝ㄟ^瀏覽器f12打開調(diào)試工具,在network選項(xiàng)里,查看當(dāng)前頁(yè)面上每條資源的加載耗時(shí)情況來推斷。以我的博客某篇文章加載為例:

最右邊有個(gè)紅框標(biāo)識(shí)的就是每條資源的加載耗時(shí),我們可以看到第一條是php服務(wù)端的處理速度。下面的便是各種資源了。我要優(yōu)化的那段業(yè)務(wù)中,發(fā)現(xiàn)正是由php服務(wù)端處理加載過慢帶來的巨大耗時(shí),平均每次這里加載需要1.5s以上。其他資源的加載速度平均都是在幾十ms,那么就可以確定是這段php寫的有問題了。

接下來我們就可以直接去看php代碼了。

優(yōu)化 檢查代碼,理解代碼

找到對(duì)應(yīng)的代碼塊,測(cè)試了一下這段代碼塊的處理時(shí)間,發(fā)現(xiàn)用時(shí)1.5s之多,有點(diǎn)震驚。簡(jiǎn)單看了一下代碼,兩大段過百行的代碼塊,經(jīng)過一段時(shí)間的分析,發(fā)現(xiàn)有很多重復(fù)的、不必要的地方,現(xiàn)整理代碼邏輯(偽代碼)如下:

 $value1) {
    
    /**
     * 2、取出二級(jí)菜單 并循環(huán)二級(jí)菜單
     */
    foreach ($second_menu as $key2 => $value2) {
        
        /**
         * 3、取出三級(jí)菜單 循環(huán)三級(jí)菜單 當(dāng)前菜單項(xiàng)含有url信息
         * 4、對(duì)權(quán)限進(jìn)行驗(yàn)證 判斷當(dāng)前主菜單下是否擁有可以訪問的權(quán)限
         * 5、對(duì)頂級(jí)菜單需要顯示的待辦事項(xiàng)做處理
         */
        foreach ($third_menu as $key3 => $value3) {
            // 權(quán)限驗(yàn)證
            $flag = $this->auth->check($ctrl, $action);

            /**
             * 做處理 在頂級(jí)菜單上增加待辦事項(xiàng)數(shù)
             * to do something
             */
            
            // ............
            // ............


            /**
             * 這里奇葩的是又調(diào)用了另外一個(gè)方法
             * 傳遞了一個(gè)top_id 一級(jí)菜單ID
             * 然后根據(jù)一級(jí)菜單重復(fù)2、3在對(duì)應(yīng)的三級(jí)菜單上再增加待辦事項(xiàng)
             */
            $this->handle_son_backlog($top_id, $backlog_data);
        }
    }
}

這段代碼塊都做了什么呢?文字簡(jiǎn)述如下:

取出一級(jí)菜單

循環(huán)一級(jí)菜單,根據(jù)一級(jí)菜單id,取出二級(jí)菜單

循環(huán)二級(jí)菜單,根據(jù)二級(jí)菜單id,取出三級(jí)菜單,三級(jí)菜單包含url信息

循環(huán)三級(jí)菜單,驗(yàn)證權(quán)限,并決定一級(jí)菜單是否顯示:將url拆分成uri塊,生成驗(yàn)證權(quán)限所需要的參數(shù)ctrl(控制器)和action(方法)

根據(jù)確定好的一級(jí)菜單,增加一級(jí)菜單需要顯示的待辦事項(xiàng)數(shù)

好了,以上就是第一個(gè)函數(shù)的作用,然而,這還沒完,在循環(huán)三級(jí)菜單的時(shí)候,又調(diào)用了另外一個(gè)方法handle_son_backlog(),這個(gè)方法傳了兩個(gè)參數(shù),一個(gè)是一級(jí)菜單id,另外一個(gè)是待辦事項(xiàng)數(shù)組,那么這個(gè)方法又做了什么呢?

根據(jù)一級(jí)菜單id,取出二級(jí)菜單

循環(huán)二級(jí)菜單,取出三級(jí)菜單

菜單權(quán)限驗(yàn)證

在對(duì)應(yīng)的三級(jí)菜單上增加待辦事項(xiàng)數(shù)

理解完原來代碼的用意后,再修改起來就不難。本來打算再原本的基礎(chǔ)上修改,但是用了一段時(shí)間發(fā)現(xiàn),代碼寫得太亂,根本沒辦法在看,于是我決定,自己寫,先改造一部分,去掉多余的第二個(gè)函數(shù)

第一次嘗試修改

改變代碼塊的可讀性

經(jīng)過第一次想法的修改之后,去掉了第二個(gè)方法多余的循環(huán)、重復(fù)驗(yàn)證的問題,代碼變得稍微精簡(jiǎn)一些了:

/**
 * 對(duì)特定的菜單進(jìn)行處理 增加待辦事項(xiàng)
 * @param  array  &$son_data    子菜單信息
 * @param  array  $backlog_data 待辦事項(xiàng)數(shù)據(jù)
 * @return array 
 */
function handle_son_backlog(array &$son_data, array $backlog_data)
{
    if (empty($son_data["id"])) {
        return false;
    }
    switch ($son_data["id"]) {
        case "":
            $son_data["backlog_num"] = (isset($backlog_data["xxx"]) && empty($backlog_data["xxx"])) ? $backlog_data["xxx"]: "";
            break;
        default:
            # code...
            break;
    }

    return $son_data;
}

/**
 * 獲取菜單
 * @param  array  $backlog_data 待辦事項(xiàng)數(shù)據(jù)
 * @return array
 */
function get_menu()
{
    /**
     * 1、取出一級(jí)菜單 并循環(huán)一級(jí)菜單 
     */
    foreach ($top_menu as $key1 => $value1) {
        
        /**
         * 2、取出二級(jí)菜單 并循環(huán)二級(jí)菜單
         */
        foreach ($second_menu as $key2 => $value2) {
            
            /**
             * 3、取出三級(jí)菜單 循環(huán)三級(jí)菜單 當(dāng)前菜單項(xiàng)含有url信息
             * 4、對(duì)權(quán)限進(jìn)行驗(yàn)證 判斷當(dāng)前主菜單下是否擁有可以訪問的權(quán)限
             * 5、對(duì)頂級(jí)菜單需要顯示的待辦事項(xiàng)做處理
             */
            foreach ($third_menu as $key3 => $value3) {
                // 權(quán)限驗(yàn)證
                $flag = $this->auth->check($ctrl, $action);

                /**
                 * 做處理 在頂級(jí)菜單上增加待辦事項(xiàng)數(shù)
                 * to do something
                 */

                /**
                 * 對(duì)子菜單的待辦事項(xiàng)做處理
                 */
                $this->handle_son_backlog($value3, $backlog_data);
            }
        }
    }
}

修改好之后,運(yùn)行0.6s,快了一倍,但是這肯定是不夠的。還是慢?。。?/p> 還能不能再快?

使用遞歸結(jié)構(gòu)

略看第一次修改后的代碼還是有可以提速的地方。三層循環(huán)寫的著實(shí)讓人辣眼睛啊,因?yàn)樵谘h(huán)中還有數(shù)據(jù)庫(kù)操作,請(qǐng)注意:任何在循環(huán)中參與數(shù)據(jù)庫(kù)的處理都是不明智的選擇。在大腦中構(gòu)思了一下,其實(shí)這些完全可以通過遞歸來實(shí)現(xiàn)嘛。只需要把菜單一股腦取出來,在用遞歸形成樹形結(jié)構(gòu)就可以了。說干就干

先說說我這段處理大致思路:

取出菜單表里所有的菜單數(shù)據(jù)

調(diào)用遞歸方法,形成樹形結(jié)構(gòu)

遞歸的方法中,做一些特殊處理

確定是第三層菜單

對(duì)第三層菜單做權(quán)限處理

對(duì)第三層菜單做待辦事項(xiàng)處理

差不多就是如上幾步思路,完成版?zhèn)未a如下:

/**
 * 對(duì)菜單進(jìn)行遞歸處理 并驗(yàn)證權(quán)限 增加待辦事項(xiàng)數(shù)量
 * @param  array       &$menu        菜單
 * @param  array       $backlog_data 待辦事項(xiàng)數(shù)據(jù)
 * @param  array       $menu_list    原來的菜單
 * @param  int         $pid          pid
 * @param  int|integer $last_pid     父菜單id
 * @param  int|integer $i            遞歸標(biāo)識(shí)(用于執(zhí)行特定操作)
 */
function get_handle(array &$menu, array $backlog_data, array $menu_list, int $pid, int $last_pid = 0, int $i = 0)
{
    foreach ($menu_list as $key => $value) {
        if ($value["pid"] == $pid) {
            if ($i == 1) {
                // 要驗(yàn)證的url
                $check_url     = explode("?", $value["url"]);
                
                // 拆分成uri數(shù)據(jù)段
                $check_url_arr = explode("/", $check_url[0]);
                // 控制器名
                $ctrl          = $check_url_arr[0] . "_" . $check_url_arr[1];
                // 方法名
                $action        = isset($check_url_arr[2]) ? $check_url_arr[2] : "index";
                if ($this->auth->check($ctrl, $action)) {
                    $menu[$last_pid]["zi"][$value["type_id"]] = $this->handle_son_backlog($value, $backlog_data);
                }
            } else {
                $this->get_handle($menu, $rule_list, $backlog_data, $menu_list, $value["type_id"], $pid, 1);
            }
        }
    }
}

/**
 * 獲取菜單
 * @param  array  $backlog_data 待辦事項(xiàng)數(shù)據(jù)
 * @return array
 */
function get_menu(array $backlog_data)
{
    // 獲取菜單列表
    $menuList = $menuModel->get_list(["id", "name", "pid", "url"], ["version" => 1]);
    // 取得一級(jí)菜單
    foreach ($menuList as $key => $info) {
        if ($info["pid"] == 0) {
            $menu[$info["id"]] = $info;
        }
    }

    foreach ($menu as $id => $info) {
        // 對(duì)菜單作遞歸處理
        $this->get_handle($menu, $backlog_data, $menuList, $info["id"]);

        /**
         * 判斷當(dāng)前主菜單下是否有子菜單 如果沒有則釋放掉當(dāng)前一級(jí)菜單
         * 如果有則對(duì)當(dāng)前一級(jí)菜單進(jìn)行待辦事項(xiàng)處理
         */
        
        //
        //    
        //            
    }

    return $menu;
}

差不多了就來進(jìn)行調(diào)試一下吧,運(yùn)行一看0.3s,感覺跟第一次修改的時(shí)候運(yùn)行的也差不多嘛?。ㄟ@時(shí)候已經(jīng)比最初的運(yùn)行速度提升了差不多4倍。)但隱隱覺得這還不夠...

還能不能更快?

減少數(shù)據(jù)庫(kù)查詢次數(shù);

重新梳理一下代碼邏輯,試圖找到可以優(yōu)化的點(diǎn)。在梳理的時(shí)候注意到一個(gè)地方,就是$this->auth->check()這個(gè)檢查權(quán)限的方法了。去跳轉(zhuǎn)查看了一下,發(fā)現(xiàn)這方法也是查一次查一下數(shù)據(jù)庫(kù),這樣的話,綜合起來,這里還是牽涉到在循環(huán)中查詢數(shù)據(jù)庫(kù)的操作了。這塊必須優(yōu)化。

如果把當(dāng)前登陸者已擁有的全部權(quán)限都取出來,替換掉check()這一塊,是不是效率就會(huì)更快些?感覺答案應(yīng)該是肯定的!

在經(jīng)過一些調(diào)整之后,發(fā)現(xiàn)程序執(zhí)行的速度有了極大的提升,增加了一段取出所有權(quán)限的操作:

/**
 * 獲取用戶所有權(quán)限列表
 * @param  int $user_id 用戶id
 * @return array/boolean
 */
function get_user_operation_list(int $user_id)
{
    $group_ids = $this->get_value_by_pk($user_id, "groupid");

    if ($group_ids) {
        $group_ids_arr = explode(",", $group_ids);
        // 取出用戶所擁有的權(quán)限 控制器和方法名
        $result = $this->db->select("o.module, o.action")
            ->from("admin_group_operations ago")
            ->join("operations o", "ago.operations_id = o.operation_id", "left")
            ->where_in("ago.group_id", $group_ids_arr)
            ->where("o.operation_id >", 0)
            ->get()
            ->result_array();
            
        if (!empty($result)) {
            $new_data = [];
            // 生成指定的鍵值對(duì)
            foreach ($result as $key => $value) {
                $new_data[] = $value["module"] . "/" . $value["action"];
            }
            return $new_data;
        }
    }
    return false;
}

并且在$this->auth->check()這行替換成了in_array($ctrl . "/" . $action, $operation_list。這樣就差不多了。

運(yùn)行一看,速度也挺喜人。竟然達(dá)到了0.014,比最原始的快了百倍不止。
然后再去看網(wǎng)頁(yè)運(yùn)行,發(fā)現(xiàn)我優(yōu)化的這塊,明顯比網(wǎng)頁(yè)上的其他模塊加載速度要快了許多(因?yàn)轫?xiàng)目用了iframe),之前是其他模塊的內(nèi)容出來了,頭部的菜單還沒出來?,F(xiàn)在的情況恰恰相反,頭部菜單最先加載出來,然后等待其他iframe的加載。

做完這番工作,長(zhǎng)舒一口氣,這一番coding沒有白費(fèi)。

總結(jié)

從這個(gè)例子中,我們可以得到一些,代碼優(yōu)化的技巧:

減少數(shù)據(jù)庫(kù)的操作

好像就只有這個(gè)吧....2333333

思考

能不能夠繼續(xù)優(yōu)化呢?放在緩存中會(huì)如何?
如果放在緩存中的話,也不是不行,但是這里有一個(gè)點(diǎn)就是這里的待辦事項(xiàng)是可變的。而且項(xiàng)目中也沒有使用socket的技術(shù)。如果單單存儲(chǔ)在緩存中的話,那么更新緩存里的這塊數(shù)據(jù)就會(huì)變得更加啰嗦。索性就暫時(shí)這樣放著,能以后性能指標(biāo)提高了,再來優(yōu)化。

結(jié)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/29082.html

相關(guān)文章

  • 譯文-JVM中CMS收集器

    摘要:原文出處這種垃圾收集器的官方名稱是。使用收集器的名稱。事件時(shí)長(zhǎng)記錄不同的類型回收期間垃圾收集器線程消耗事件調(diào)用操作系統(tǒng)活著等待系統(tǒng)事件消耗時(shí)間應(yīng)用停頓的時(shí)鐘時(shí)間。現(xiàn)在我們看一些一些任務(wù)的時(shí)間,垃圾收集器線程等待很長(zhǎng)時(shí)間。 原文出處:Concurrent Mark and Sweep 這種垃圾收集器的官方名稱是Mostly Concurrent Mark and Sweep Garbag...

    morgan 評(píng)論0 收藏0
  • 前端每日實(shí)戰(zhàn):101# 視頻演示如何用 CSS 和 D3 創(chuàng)作一組擺線

    摘要:效果預(yù)覽按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽??山换ヒ曨l此視頻是可以交互的,你可以隨時(shí)暫停視頻,編輯視頻中的代碼。最后,把擺線的數(shù)量調(diào)整為個(gè)。 showImg(https://segmentfault.com/img/bVbe6re?w=400&h=301); 效果預(yù)覽 按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。 https://code...

    gaomysion 評(píng)論0 收藏0
  • 前端每日實(shí)戰(zhàn):101# 視頻演示如何用 CSS 和 D3 創(chuàng)作一組擺線

    摘要:效果預(yù)覽按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。可交互視頻此視頻是可以交互的,你可以隨時(shí)暫停視頻,編輯視頻中的代碼。最后,把擺線的數(shù)量調(diào)整為個(gè)。 showImg(https://segmentfault.com/img/bVbe6re?w=400&h=301); 效果預(yù)覽 按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。 https://code...

    Dean 評(píng)論0 收藏0
  • 譯文-G1收集器

    摘要:原文出處設(shè)計(jì)的一個(gè)重要目標(biāo)是設(shè)置階段的持續(xù)時(shí)長(zhǎng)和頻率,因?yàn)槔占骺深A(yù)測(cè),可配置。收集器盡自己最大努力高概率實(shí)現(xiàn)目標(biāo)但不是必然,它會(huì)是硬實(shí)時(shí)。因此名稱是收集器。運(yùn)行不同使用獨(dú)立的收集器。 原文出處:G1 – Garbage First G1設(shè)計(jì)的一個(gè)重要目標(biāo)是設(shè)置stop-the-world階段的持續(xù)時(shí)長(zhǎng)和頻率,因?yàn)槔占骺深A(yù)測(cè),可配置。事實(shí)上,G1是一款軟實(shí)時(shí)的收集器,意味著你...

    missonce 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<