PHP Db類強制讀主庫(master)的設(shè)計

這段時間Db不給力常柄,經(jīng)常出現(xiàn)主從同步延遲或者掛掉的情況,導(dǎo)致很多業(yè)務(wù)出現(xiàn)異常飘痛,大家就討論怎么樣讓程序強制讀master,關(guān)于這個方面的討論比較激烈容握,主要為兩種宣脉。

  • 底層DB類不應(yīng)該關(guān)注主從的抉擇,應(yīng)該交于業(yè)務(wù)側(cè)的用戶抉擇剔氏,這樣業(yè)務(wù)層使用起來比較靈活塑猖。
  • 業(yè)務(wù)層的用戶不應(yīng)該關(guān)注主從的抉擇,應(yīng)該交給DB層解決谈跛,因為如果業(yè)務(wù)層人為不小心把強制讀master的代碼上到了壓力大的線上羊苟,會對Db造成很大的壓力,會出現(xiàn)很多不可控的因素感憾。

我本人持第二種觀點蜡励,程序中的bug,大部分都是人的失誤造成的阻桅,在編程的世界里凉倚,人才是最大的bug,不應(yīng)該把業(yè)務(wù)的穩(wěn)定性依賴于人嫂沉。

但是現(xiàn)在業(yè)務(wù)上主從的問題要解決稽寒,并且再快的主從同步也會出現(xiàn)延遲,在不依賴其他的存儲工具的情況下趟章,使用強制讀master是必須要實現(xiàn)的功能杏糙,那么怎么才能設(shè)計出來一個安全性高一些的操作方式呢慎王?

最初的想法,通過在調(diào)用查詢函數(shù)時加入強制讀master參數(shù)。

$daoCity = new \dao\City();

$ret = $daoCity->findAllBySql("select * from city limit 1",$useMaster=true);

這個想法很快被淘汰了搔啊,原因如下柬祠。

  • 所有要修改的讀取函數(shù)太多,太麻煩负芋。
  • 使用方法不優(yōu)美漫蛔,忍不了。
  • 如果出現(xiàn)不了解的程序員直接拷貝代碼旧蛾,將其上線到線上環(huán)境莽龟,可能出現(xiàn)事故。

接著出現(xiàn)了第二版的設(shè)計,通過一個函數(shù)來開啟master讀取锨天,再主動關(guān)閉master讀取毯盈。

$daoCity = new \dao\City();
$daoCity->forceMaster();
$ret = $daoCity->findAllBySql("select * from city limit 1");
$ret = $daoCity->findAllBySql("select * from city limit 1");
$ret = $daoCity->findAllBySql("select * from city limit 1";
$ret = $daoCity->findAllBySql("select * from city limit 1");
$daoCity->forceMasterOver();

第二版的想法比第一版好了一些,但是還不是我想要的病袄,否定的原因如下搂赋。

  • 需要額外的函數(shù)。
  • 大概率忘記關(guān)掉master查詢益缠。
  • 解決方法還是不夠優(yōu)雅脑奠,不能忍。

仔細思考幅慌,衍生出了第三版的設(shè)計宋欺。通過鏈?zhǔn)秸{(diào)用實現(xiàn)強制讀master。

$daoCity = new \dao\City(false);
/**
 * 自動主從
 */
$ret = $daoCity->findAllBySql("select * from city limit 1");

/**
 * 強制master
 */
$ret = $daoCity->forceMaster()->findAllBySql("select * from city limit 1");

  • 解決方式對老代碼幾乎沒有入侵胰伍。
  • 解決方法優(yōu)雅齿诞。
  • 通過直接的函數(shù)名可以有效的提示使用者,這是master操作骂租。
  • 無需主動關(guān)閉祷杈,程序?qū)崿F(xiàn)隔離。

那么接下來我們看下怎么實現(xiàn)這個鏈?zhǔn)秸{(diào)用呢渗饮?并且無需關(guān)注主從的切換但汞。

這是未修改過的代碼。

class TqtDaoBase
{
    protected $dbConf;
    protected $table;
    protected $pk = "id";
    protected $tqtMysqli;

    /**
     * TqtDaoBase constructor.
     *
     * @param bool $needReconnect
     * @throws \Exception
     */
    public function __construct()
    {
        if (empty($this->dbConf)) {
            throw new \Exception("db config is null", 1);
        }

        $this->tqtMysqli = TqtMysqli::getInstance($this->dbConf);
      
    }


    /**
     * 根據(jù)sql查詢多條數(shù)據(jù)
     *
     * @param $sql
     * @return mixed
     */
    public function findAllBySql($sql)
    {
        return $this->tqtMysqli->queryRows($sql);
    }

代碼邏輯不用細講了抽米,就是一個簡單的Db操作父類。子類集成后實現(xiàn)對個表的操作糙置。

下面加入對這個類的第一次修改云茸。

class TqtDaoBase
{
    protected $dbConf;
    protected $table;
    protected $pk = "id";
    public $tqtMysqli; //將這個屬性改成 公開的

    protected static $MASTER_INSTANCE;

    /**
     * TqtDaoBase constructor.
     *
     * @param bool $needReconnect
     * @throws \Exception
     */
    public function __construct()
    {
        if (empty($this->dbConf)) {
            throw new \Exception("db config is null", 1);
        }

        $this->tqtMysqli = TqtMysqli::getInstance($this->dbConf);

    }
    
    /**
     * @return TqtDaoBase
     */
    public function forceMaster()
    {
        if (empty(self::$MASTER_INSTANCE)) {
            $className = get_called_class();
            $dbLink = new $className;
            $dbLink->tqtMysqli->forceMaster();
            self::$MASTER_INSTANCE = $dbLink;
        }

        return self::$MASTER_INSTANCE;
    }

代碼的邏輯主要就是new一個新的自己出來,將其中的Db 連接指向master谤饭,這樣就可以實現(xiàn)鏈?zhǔn)秸{(diào)用了标捺,是不是感覺大功告成了懊纳。
慢著,這里有個安全問題亡容,或者說是漏洞嗤疯,將原有的
protected $tqtMysqli; 改成了 public $tqtMysqli;
帶來了幾個問題

  • 將不應(yīng)該暴露給使用者的屬性暴露出去了
  • 用戶可以繞過我的 forceMaster()方法直接使用 $daoCity->tqtMysqli->forceMaster();操作master,這樣我們的設(shè)計很可能被偷懶的程序員繞過闺兢。
  • 這樣不優(yōu)雅茂缚,不能忍。

接下來思考了各種方法屋谭,從對象復(fù)制clone中找到了靈感脚囊。
先看下php的文檔的描述。

對象復(fù)制可以通過 clone 關(guān)鍵字來完成(如果可能桐磁,這將調(diào)用對象的 __clone() 方法)悔耘。對象中的 __clone() 方法不能被直接調(diào)用。

通過這個方法的幫助我擂,做出以下設(shè)計

class TqtDaoBase
{
    protected $dbConf;
    protected $table;
    protected $pk = "id";
    protected $tqtMysqli;

    protected static $MASTER_INSTANCE;

    /**
     * TqtDaoBase constructor.
     *
     * @throws \Exception
     */
    public function __construct()
    {
        if (empty($this->dbConf)) {
            throw new \Exception("db config is null", 1);
        }

        $this->tqtMysqli = TqtMysqli::getInstance($this->dbConf);
    }

    /**
     * 發(fā)生clone 新建一個Db連接衬以,并指向master
     */
    public function __clone()
    {
        $this->tqtMysqli = TqtMysqli::getInstance($this->dbConf);
        $this->tqtMysqli->forceMaster();
    }

    /**
     * @return TqtDaoBase
     */
    public function forceMaster()
    {
        if (empty(self::$MASTER_INSTANCE)) {
            self::$MASTER_INSTANCE = clone $this;
        }

        return self::$MASTER_INSTANCE;
    }

  • __clone 在對象被復(fù)制的時候可以修改復(fù)制的對象屬性,符合我們new一個類校摩,做master操作看峻。
  • __clone 函數(shù)不能被直接調(diào)用,保證了強制讀master的操作權(quán)限收斂秧耗,避免人為的繞過
  • 解決方式優(yōu)雅备籽。

以上就是我對Db加入強制讀master的一個設(shè)計。任何一個核心功能的添加都不能隨意分井,都要深思熟慮车猬,盡量的收斂權(quán)限,對使用者一定要報以不信任尺锚。代碼設(shè)計盡量的優(yōu)雅簡潔珠闰。
如果有問題可以留言溝通。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘫辩,一起剝皮案震驚了整個濱河市伏嗜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伐厌,老刑警劉巖承绸,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挣轨,居然都是意外死亡军熏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進店門卷扮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荡澎,“玉大人均践,你說我怎么就攤上這事∧︶#” “怎么了彤委?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長或衡。 經(jīng)常有香客問我焦影,道長,這世上最難降的妖魔是什么薇宠? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任偷办,我火速辦了婚禮,結(jié)果婚禮上澄港,老公的妹妹穿的比我還像新娘椒涯。我一直安慰自己,他們只是感情好回梧,可當(dāng)我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布废岂。 她就那樣靜靜地躺著,像睡著了一般狱意。 火紅的嫁衣襯著肌膚如雪湖苞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天详囤,我揣著相機與錄音财骨,去河邊找鬼。 笑死藏姐,一個胖子當(dāng)著我的面吹牛隆箩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羔杨,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼捌臊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兜材?” 一聲冷哼從身側(cè)響起理澎,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎曙寡,沒想到半個月后糠爬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡举庶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年执隧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡殴玛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出添祸,到底是詐尸還是另有隱情滚粟,我是刑警寧澤,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布刃泌,位于F島的核電站凡壤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏耙替。R本人自食惡果不足惜亚侠,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俗扇。 院中可真熱鬧硝烂,春花似錦、人聲如沸铜幽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽除抛。三九已至狮杨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間到忽,已是汗流浹背橄教。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喘漏,地道東北人护蝶。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像陷遮,于是被迫代替她去往敵國和親滓走。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,435評論 2 348

推薦閱讀更多精彩內(nèi)容