1. 程式人生 > >撥亂反正-重構是門藝術活

撥亂反正-重構是門藝術活

前言

引用自: 《重構 改善既有程式碼的設計》

重構是在不改變軟體可觀察行為的前提下改善其內部結構。當你面對一個最需要重構的遺留系統時,其規模之大、歷史之久、程式碼質量之差,常會使得新增單元測試或者理解其邏輯都成為不可能的任務。此時你唯一能依靠的就是那些已經被證明是行為保持的重構手法: 用絕對安全的手法從焦油坑中整理出可測試的介面,給它新增測試,以此作為繼續重構的立足點。

因為我們部門內容平臺的文章系統之前遺留了很多問題,急需解決這些具有"壞味道"的程式碼。最後因為其他人手頭裡都有其他工作,最後這些任務就交給了我。以下是急需解決的問題。

  1. 內容平臺新增/更新/取消/刪除文章,同步各集團下文章行為狀態,訊息鏈路過長的問題。
  2. article分享錶停止規模新增,之前未做插入前的記錄判斷,通過新增的操作來進行記錄留存。
  3. 文章表拆除大欄位到分表,如content、content_draft等欄位。

鏈路過長概述

內容平臺新增/更新/取消/刪除文章,同步各集團下文章行為狀態,訊息鏈路過長的問題。

  • 問題導火索: 運營後臺文章釋出,傳送訊息到marketing-base

  • 慢鏈路,鏈路過長

    • mysql資料同步,單條執行n次

    • es索引資料同步,dubbo介面呼叫n次


圖1 鏈路圖

鏈路過長剖解及解決思路

具體問題,具體對待

//開啟同步開關的集團
        List<Integer> groupList = autoSyncStatusService.getAutoSyncGroupByManageType(MANAGE_TYPE_GROUP_ARTICLE); 

    for (Integer groupId : syncSubjectList) {
                SiteGroupInfoDTO siteGroupInfo = siteSPI.getGroupInfoById(groupId);
                Set<String> groupBrandSet = carOnSaleManage.getGroupBrandSet(siteGroupInfo);
                List<String> matchedBrandCodes = extractBrandCodesFromArticleLabel(article.getLabelInfos());
                if (CollectionUtils.isEmpty(matchedBrandCodes) || CollectionUtils.containsAny(groupBrandSet, matchedBrandCodes)) {
                    ArticleGroupMaterialBO groupMaterialBO =
                            ArticleBeanConverter.convertMaterial2GroupMaterial(article, groupId, groupList);
                    // 設定對應的集團主題id
                    ArticleGroupSubjectBO groupSubjectBO =
                            articleGroupSubjectService.getGroupSubjectBySoucheId(groupId, article.getSubjectId());
                    if (Objects.nonNull(groupSubjectBO.getId())) {
                        groupMaterialBO.setSubjectId(groupSubjectBO.getId());
                        groupMaterialBO.setMaterialId(myArticleId);
                        articleGroupMaterialService.addArticleGroupMaterial(groupMaterialBO);
                    }
            }
        } else {
                //查詢同步的文章資料是否存在
                List<ArticleGroupMaterialBO> list = articleGroupMaterialService.getListByMaterialId(myArticleId);
                for (ArticleGroupMaterialBO a : list) {
                    if (groupList.contains(a.getGroupId())) {
                        articleGroupMaterialService.changeRecommendStatus(a.getId(), a.getGroupId(), recommend, article.getLastOperatorName(), article.getLastOperatorName());
                    }
                }
        }
  • 第4行中我們可以看到這裡有一個for迴圈♻️,假設開啟同步開關的集體有1000家,則第18行中mysql插入操作就需要執行1000次。

  • 第24行這裡同樣有一個for迴圈體♻️,則26行內部的es資料同步則需要呼叫1000次。它的實現如下:

    @Override
        public boolean changeRecommendStatus(int id, int groupId, int recommended, String lastOperatorUserId, String lastOperatorName) {
            final boolean success = articleGroupMaterialDAO.changeRecommendStatus(
                    id, groupId, recommended, lastOperatorUserId, lastOperatorName) > 0;
            if (success) {
                //更新索引,更改推薦狀態
                articleSearchManage.updateArticleIndex(ArticleIndexUtil.getUpdateRecommendIndex(recommended, id, lastOperatorName));
            }
            return success;
        }

    解決思路

    Mybatis批量插入

    對於第一個迴圈♻️體中,我們需要將資料批量新增到資料庫,mybatis提供了將list集合迴圈新增到資料庫的方法。

    1. mapper層中建立 insertForeach(List < Fund > list) 方法,返回值是批量新增的資料條數
    public interface FundMapper {
      int insertForeach(List<Fund> list);
    }
    1. mybatis的xml檔案中的insert語句如下
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.center.manager.mapper.FundMapper">
    
      <insert id="insertForeach" parameterType="java.util.List" useGeneratedKeys="false">
                  insert into fund
                  ( id,fund_name,fund_code,date_x,data_y,create_by,create_date,update_by,update_date,remarks,del_flag)
                  values
                  <foreach collection="list" item="item" index="index" separator=",">
                      (
                          #{item.id},
                          #{item.fundName},
                          #{item.fundCode},
                          #{item.dateX},
                          #{item.dataY},
                          #{item.createBy},
                          #{item.createDate},
                          #{item.updateBy},
                          #{item.updateDate},
                          #{item.remarks},
                          #{item.delFlag}
                      )
                   </foreach>     
        </insert>    
    </mapper>
    ES批量更新

    com.souche.elastic.search.api.IndexService

    方法:BulkUpdateResponse bulkUpdate(String index, Map<String, Object> event, String query, String origin)
    
    引數:
    
        index:要操作的索引
    
        event:更新的資料,可以只包含需要更新的欄位,相當於mysql的update語句中的set語句中的欄位
    
        query:query中的條件相當於mysql中的where,具體語法與下面的搜尋介面中【querys:string 複雜的複合查詢 不同欄位的OR 查詢】相同
    
        origin:操作源,一般寫呼叫方自己的應用名,用於區分不同調用方
    
    返回值:
    
        BulkUpdateResponse:
    
          {
    
            requestId:本次操作的唯一標示
    
            status:狀態,目前返回預設都是true
    
            updated:成功更新的條數
    
            failed:更新失敗的條數
    
            message:第一條更新失敗的原因
    
          }
    
    呼叫示例:
    1Map<String, Object> data = new HashMap<>();
    2        data.put("id", 20);
    3        data.put("title", "xue yin");
    4        data.put("content", "kuang dao");
    5        BulkUpdateResponse response = indexService.bulkUpdate("test_index", data, "address=bj AND contry=cn", "shenfl");

    這條更新將test_index索引中所有 address是bj並且contry是cn 的資料的 title更新成‘xue yin’ content更新成‘kuang dao’,注意:address和contry兩個欄位在索引中需要加索引

Article表插入邏輯優化,停止規模新增概述

Article邏輯優化剖解及解決思路

具體問題及解決思路

當前article資料表資料量:

select count(*) as 總數 from article;

結果如下:

總數
369737
  @Override
    public String addSharedArticle(ArticleBO articleBO) {
        ArticleDO articleDO = new ArticleDO();
        BeanUtils.copyProperties(articleBO, articleDO);
        String shortUUID = UUIDUtil.getShortUUID();
        articleDO.setUid(shortUUID);
        if (articleDAO.addSharedArticle(articleDO) > 0) {
            return shortUUID;
        }
        return StringUtil.EMPTY_STRING;
    }

從上面這個業務邏輯實現類中,我們可以看到事實上我們想得到的是插入表資料的uid。但是之前的邏輯中,我們並沒有判斷該條資料是否已經存在,我們需要在上面程式碼中判斷資料是否存在,已存在,查詢最後一天資料的uid返回給上層。不存在的話,執行插入操作。

文章表拆除大欄位到分表

article_material表結構設計

article_material | CREATE TABLE `article_material` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `my_article_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '內容平臺我的文章id',
  `status` tinyint(3) unsigned NOT NULL COMMENT '1-待發布、2-釋出、3-取消釋出',
  `subject_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '主題id',
  `platform_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '平臺id',
  `source` varchar(32) NOT NULL DEFAULT '' COMMENT '版塊',
  `crawler_article_id` varchar(32) NOT NULL DEFAULT '0' COMMENT '爬蟲的文章id',
  `title` varchar(64) NOT NULL DEFAULT '' COMMENT '標題',
  `cover_img` varchar(128) NOT NULL COMMENT '封面圖',
  `summary` varchar(255) NOT NULL DEFAULT '' COMMENT '摘要',
  `labels` varchar(512) NOT NULL DEFAULT '' COMMENT '標籤',
  `label_infos` varchar(1024) NOT NULL DEFAULT '' COMMENT '標籤詳細資訊',
  `content` text NOT NULL COMMENT '內容,使用者看到的',
  `content_imgs` text NOT NULL COMMENT '內容中圖片',
  `content_videos` varchar(255) NOT NULL DEFAULT '' COMMENT '內容中視訊',
  `content_draft` text NOT NULL COMMENT '草稿內容,編輯後儲存到這裡,釋出後內容會複製到content,此欄位清空',
  `content_imgs_draft` text NOT NULL COMMENT '草稿內容的圖片,同上',
  `content_videos_draft` varchar(255) NOT NULL DEFAULT '' COMMENT '草稿內容的視訊',
  `recommended` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0-不推薦、1-推薦',
  `author_user_id` varchar(64) NOT NULL DEFAULT '' COMMENT '作者userId',
  `author_name` varchar(16) NOT NULL COMMENT '作者名稱',
  `last_operator_user_id` varchar(64) NOT NULL DEFAULT '' COMMENT '最後操作人userId',
  `last_operator_name` varchar(16) NOT NULL COMMENT '最後操作人名字',
  `publish_date` datetime DEFAULT NULL COMMENT '釋出時間',
  `publisher_user_id` varchar(64) NOT NULL DEFAULT '' COMMENT '釋出者userId',
  `publisher_name` varchar(16) NOT NULL DEFAULT '' COMMENT '釋出者名字',
  `pv` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '流量pv',
  `uv` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '流量uv',
  `share_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分享次數',
  `share_people_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分享人數',
  `date_create` datetime NOT NULL,
  `date_update` datetime NOT NULL,
  `date_delete` datetime DEFAULT NULL,
  `deleted` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '0 表示未刪除,刪除後是毫秒級時間戳',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_id` (`my_article_id`),
  KEY `idx_title_label_status` (`subject_id`,`platform_id`,`title`,`label_infos`(255),`source`)
) ENGINE=InnoDB AUTO_INCREMENT=861 DEFAULT CHARSET=utf8 COMMENT='文章素材庫,給集團提供文章素材'

上表中content, content_imgs,content_videos都是text型別等大欄位,對於這種型別,我們需要把這種型別的表拆分成2張表 article_metedata和article_content 兩張表。

表拆分圖示

相關推薦

撥亂反正-重構藝術

前言 引用自: 《重構 改善既有程式碼的設計》 重構是在不改變軟體可觀察行為的前提下改善其內部結構。當你面對一個最需要重構的遺留系統時,其規模之大、歷史之久、程式碼質量之差,常會使得新增單元測試或者理解其邏輯都成為不可能的任務。此時你唯一能依靠的就是那些已經被證明是行為保持的重構手法: 用絕對安全的手法

張書樂:自媒體是藝術,賣萌的藝名比“實名”更吸粉

自媒體通過上一篇《自媒體起步先避坑,選什麽昵稱是一個不容忽視的坑》裏對熱門微博昵稱的比對,我們不難發現微博命名的3個規律,也是自媒體昵稱的規律:原則一:真名可以用,但用真名真的很少特別是像筆者這樣又用真名還用自己大頭貼,顯得格外自戀的微博主近乎異類,這裏留個梗,咱們後面會說道。至於醫療類的為何真名多一些,其實

重構藝術 深入認識

設計 行為 提高 開發者 內部 代碼 觀察 開發 調整 重構是什麽? 重構就是對軟件內部結構的一種調整,目的是不改變軟件可觀察行為的前提下,提高可理解性,降低修改成本。不是為了提高性能,是為了提高可讀性,可維護性。 重構,從某種角度講,就是整理代碼。整理歸類。 程序員,要戴

程式設計不過是一失傳的藝術的別名,這藝術的名字叫做“思考”

由於php7的升級,環境不一樣了,很多關於socket的程式碼出了點問題。然後需要在另一個環境下測試php7.2下socket是否有問題,就寫了個簡單的例子: 根據網上查詢的資料寫了一個很基本的serv

程式設計師趣圖 — Bug 是一藝術

Bug 是一門藝術 頁面的背景圖片 前端美如畫 程式碼與啤酒更配 小編推薦一個學C語言/C++的學習群:948954484,入群即送C/C++全套學習資料,滿滿的乾貨! 等我喝完咖啡再寫程式碼 PHP 是世界上最好的語言的原因是這個嗎?

修改軟體的藝術:如何重構遺留程式碼

重構是指在不改變外部行為的前提下對程式碼的內部結構進行重組或重新包裝。 想象一下,如果你是若干年前的我,正在對經理說你要讓整個團隊花上兩週(一個完整的迭代週期)來重構程式碼。經理問:“好的。你會給我什麼樣的新功能呢?” 我說:“等等。我是說重構。重構修改內部結構而不改變外部行為。不會有任何新

[Xcode10 實際操作]一、博主領進-(12)程式碼重構

本文將演示如何重構程式碼。 在專案導航區,開啟檢視控制器的程式碼檔案【ViewController.swift】   【快速更改同名變數或常量】 在程式碼編輯區域,點選需要更改名稱的常量。 【Editor】編輯器->【Refactor】重構->【Rename】重新命名->

留白,是一生活的藝術

老子《道德經》雲:大音希聲,大象無形;天下萬物生於有,有生於無。無,即是有;空,即是色。大響,卻沒有聲音;大象,卻沒有形狀。這種大、無、空,就是留白。 中國書畫的最高境界,在於水墨留白。虛實相生,無畫處皆成妙境。名家作畫,都是惜墨如金,計白當黑。留白是一種境界,非高手而不能

IT人的前沿技術書單:學不到老,就不到老

伸縮 以及 了解 必須 設置 plot hold 設計模式 海量數據 IT人作為這個互聯網時代的弄潮兒,我們天生就處於技術浪潮的中心地帶,技術的變革讓我們不得不掌握終生學習的能力和多元化的知識領域,技術的浪潮時而沖天而起,時而又歸於平靜,但是始終在推動著人類社會向前發展。你

windows server 2008 r2激工具,親測有效!

framework windows server 2008 激活 激活 此工具名為:Re-Loader Activator,支持一鍵激活Office/Windows/Windows Server所有版本,參考鏈接:http://www.uusofts.com/soft/xitonggongju/r

星際之(一)

輸出 detail return 技術 gin ace problem pau ans 星際之門(一) 時間限制:3000 ms | 內存限制:65535 KB 難度:3 描寫敘述 公元3000年。子虛帝國統領著N個星系,

IntelliJ Idea 2017 免費激方法

int 均可 intellij http 獲取 dea 方法 網站 tell 1. 到網站 http://idea.lanyus.com/ 獲取註冊碼。 2.填入下面的license server:   http://intellij.mandroid.cn/  http:

TwinCAT常見問題-點擊激配置進入到運行模式直接死機或藍屏怎麽辦

png 點擊 pan 而且 優酷 image 交流 更多 com 下載我提供的TCRtime.sys文件,替換掉TwinCAT/Driver目錄下的原有文件(原有文件要小一點,這個是159KB的) 如果你同時也安裝了TwinCAT3,請不要替換這個,他是398

漢化入教程(絕對適合新手) By:1595901624

刮刮樂 常見 界面 not 鏡像 class pac hot 中文 高手勿噴,此漢化教程僅適合新手,高手請繞道 有人說圖片太小,我不太會弄,word文檔已上傳 本文所用到的所有軟件都會上傳到百度雲盤 所謂漢化就是,將非中文的軟件國語化,我們我們常見的需要漢化軟件大

office 激教程

技術討論 eid 9.png div com aid .cn 下載 顯示 首先在我的百度雲裏下載:http://pan.baidu.com/share/link?shareid=2200272243&uk=1985086665激活工具安裝包 如果遇到問題,可以加我Q

從籃球到雨傘 “共享產業”到底是不是好生意?

互聯網創業 應用程序 人民幣 創業者 王思聰 近日,共享籃球項目“豬了個球”成為籃球愛好者熱議的對象。據悉,豬了個球允許用戶以2元/小時元租借籃球。該服務作為一個迷你應用程序放置於微信中,用戶可從ZhuLeGeQiu(豬了個球)掃描QR碼,以解鎖持有籃球的櫃子。從大環境來看,之所以小小的籃

pycharm-professional-2017.1.1.exe專業版激方法

jet border pop hle rri xv6 baidu msf lrj pycharm 2017.1專業版破解碼只用了一個月就到期了,又找到一個方法,又可以用了~~~ 1.在server選項裏邊輸入 http://elporfirio.com:1017/就可以了。

特效模擬:SPH流體模擬及液面重構問題

爆炸 方程 力學 壓力 大學 png sch 技術 當前 這裏是關於特效模擬算法的一些敘述,主要是流體模擬部分的研究。 目前動畫領域內的流體模擬主要是拉格朗日法無網格法和歐拉網格法,兩種方法更有利弊。 我研究的主要是拉格朗日法中的SPH模型,即光滑粒子流體動力學模型。

asp.net mvc 註冊中的郵箱激功能實現

名稱 work 點擊 rom urn 內容 string 電子郵件 amp 基本流程圖 註冊頁面就不再寫出,現在將發送郵件的代碼粘貼出來 public ActionResult SendEmial() { int

JavaWeb網上圖書商城完整項目--day02-12.激功能各層實現

find java writer 之間 and service() 配置文件 private pop 1、我們來看程序的代碼 數據庫層: 1、通過激活碼查找到對應的用戶 2、設置用戶的激活狀態 2、業務層 1、通過數據庫接口通過驗證碼得到對應的用戶 2、判斷當用戶是否為