電商網站多語言架構

電子商務網站多語言版設計思路

1. 數據庫設計

1. 分庫,還是分表

2. 單表還是多表

這步其實很關鍵:首先 這直接涉及到代碼邏輯,一有不慎,可能會導致原有方案失效

說說我們的設計思路吧!

我們採用的是分庫方式!

一種語言一個數據庫

這樣的好處有哪些呢?

1.避免單庫數據量過大,導致查詢難以優化。

  舉個例子:假設有2種語言,中文和英文。在商品表中,一個商品在不同語言下,其實還是同一件商品,他們有共同的屬性,共同的價格,共同的庫存等等;如果放在一個庫中那麼本來商品數據有100W,現在直接變成了200W。so優化起來是不是更麻煩了呢。而且對於像商品這種表,更新操作是非常頻繁的,這樣就要不斷的去維護索引,這其中開銷也是蠻大的。

- 爲什麼不用分表?
假如一種語言一個表,那跟分庫道理不就一樣了嗎?
其實我個人覺得分表有很多不足的地方。

  • 如果採用一個語言一張表,那麼我們表的數據過大怎麼辦呢?這時候是不是還要繼續分表呢?就目前的狀況來看,很多公司都會採用分表去解決。也就是說要分2次表,這樣不是很麻煩嗎,而且表在同一個數據庫中,數據量變大的時候怎麼辦呢,當達到Mysql儲存上限(這裏指的是mysql存儲到一定數據時候就變得難以優化的情況)不就更麻煩嗎?
    所以我們採用了分庫策略。

表如何設計

需要中英文對於的表,放到對於的庫中即可,無需把所有的表都copy

舉個例子
商品表:

  id  title  stock status

假設我們的商品字段如上
這個時候我們添加商品的時候肯定是要往數據庫(多個語言)裏面添加數據。也就是說我添加了一箇中文語言的商品,對應的英文數據庫也應該插入同樣的數據。然後再去編輯英文的商品數據。這樣就保證了一個商品是一定會有多語言的。
那麼我們如何做到同時添加
1. 剛開始想用觸發器直接同步添加,可是這樣一個庫就要建立一個觸發器?很顯然,這樣肯定就會陷入一個死循環中。(事實上MySQL是會報錯的,爲什麼我就不說了)。所以這種偷懶的方案是不能實現得了啊。
2. 後來我想用存儲過程去實現,但是呢,存錯過程確實存在不少問題,可以參考我的一片文章 MySQL 存儲過程的優缺點 。so只能放棄這種方案
3. 修改PHP框架的數據庫操作方法。
我們採用的是THINKPHP。 這裏說下如何修改tp 的 add .
tp的add方法是聲明在Model下。所以我們新建一個commonModel來繼承他的Model類

首先要在配置文件中配置好多個數據庫dsn

eg : 'DB_LIST' => array('DN_CN' => 'mysql:XXXX',
                          'DN_EN' => 'mysql:XXXX'
                          );

配置公共數據庫的表

eg 'LANG_TABLE' => array(
    'goods' => array('stock,status'), //這裏配置的是該表需要更新的字段;比如商品的庫存和狀態肯定都是一致的
)
以下是僞代碼
protected $dbName;  //數據庫名
protected $tableName; //表名
protected $data;  // ORM創建的數據對象
protecteds $option;  //查詢條件等
function __construct(){

}

function add($data,$option,$replace){
    if(!$data)
    $data = $this->data;//如果是create的話,那麼add一次之後,$this->data 會被清空

    parent::add($data,$option,$replace); //往當前庫插入數據
    //切換數據庫
    $this->dbName = 'XXXX';  //這裏我們的庫都在同一臺服務器上,所以不用重新連接
    //如果在不用數據庫,那麼就讀取DB_LIST配置來重新連接數據庫。$this->db($id,$dsn);這樣切換了數據庫
    parent::add($data,$option,$replace); 

    //切換回當前數據庫
    $this->dbName = 'xxx';
}

刪除也是同理

更新數據

一個表中肯定會有一些字段是不用做多語言的,但是必須保證不用語言下的數據是一致的。比如商品價格,商品庫存等
這個時候更新了一條記錄,也要去同步下其他庫的數據

正如上面配置。 我們只需要把需要同步的字段配置好就行了

function save($data,$option)
{
    parent::save($data,$option);
    //切換數據庫
    //對比$data 的key也配置的字段,取交集 $field
    parent::save($field,$option);  //如果是create的數據,記得對data進行賦值
}
其他tp的操作方法(如execute  addAll)我就不演示了。

其實上面是很簡單的。真正的重點在下面

我們取商品的數據的時候通常是通過id去查詢。

那麼我們多個庫的時候,要取數據還能用id嗎?

如果能用id那麼這篇文章已經結束了,哈哈!

那麼爲什麼不能用id了呢?

其實很簡單的道理:你沒辦法保證多個語言庫的id是一致的,因爲我們id是increament,so id沒辦法同步?舉個例子:A用戶往中文庫插入一條數據,id 爲100,這時候b用戶往英文表插入一條數據id也有100,(這種情況是很有可能發生的,所以id肯定會不一樣)?

換個角度想想:如果我們加事務呢,這樣是不是能保證數據一致性呢?其實這更可怕,很可能會造成死鎖!

Why ? 死鎖其實道理很簡單。

A用戶往中文庫插入一條數據,id 爲100,這時候中文庫的表肯定被鎖住了,這時候b用戶往英文表插入一條數據id也有100,這時候英文庫的表也被鎖住了。但是他們又要同步到其他庫。這時候A這邊會發現英文庫的表鎖了,那麼它要插入數據就必須等英文庫的表解鎖。但是與此同時b用戶也會發現中文庫的表鎖了,它也要等解鎖。這樣就造成了雙方都在等。死鎖就這樣形成了。
所以id是很不靠譜的。

解決方案:
新增一個字段,類型爲int,該字段的生成規則可是時間戳+隨機數(這裏面還涉及到一些算法,我就不公佈了)。(爲什麼是int我就不解釋了,很簡單的道理)。這樣多個語言的數據就通過此字段來獲取(因爲這個值是事先生成的,每個庫的值都是一樣的,所以不會衝突)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章