電子商務網站多語言版設計思路
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我就不解釋了,很簡單的道理)。這樣多個語言的數據就通過此字段來獲取(因爲這個值是事先生成的,每個庫的值都是一樣的,所以不會衝突)。