天天看點

10分鐘掌握資料庫模組化

快速掌握資料庫模組化 

文章出處: 

http://netkiller.sourceforge.net 

http://netkiller.github.com 

資料庫性能問題,主要來自3三個部分。 

1. 資料庫配置 

2. sql查詢語句的性能 

3. 模組化的合理性 

用戶端性能這裡不談,硬體的影響這裡也不讨論,這裡隻談資料庫伺服器本身的問題。 

首先是資料庫的配置,這個比較好解決,有經驗的dba都能搞定,無非是連接配接數,cpu與記憶體優化等等。 

其次是sql 查詢性能問題,比較要命,不可能每個開發人員都能寫出高性能的查詢語句,查詢與索引息息相關,優化索引是一種手段,通過sql review 來解決索引失效的sql。資料也可能列印出查詢性能差的sql,也是可控的。 

最後是模組化,很多企業忽略這塊。資料庫模組化非常重要,資料結構一旦确認,後面變更都會影響整個項目的進行。是以對模組化人員要求相當的高。目前招聘到一個合格的模組化人員真的很難。 

目前國内企業資料模組化都是由開發人員完成。随心所欲建表,加字段,我就發現過一個表有200多個字段的情況,還有字段中存儲以逗号分隔的資料。這樣的情況非常普遍。 

另外有些企業讓dba負責模組化,由于dba不參與開發,不了解宏觀需求,根據開發人員的描述建表,也有很多不合理之處。 

如何建立高性能,可伸縮的資料庫呢? 

經過多年總結與摸索,我找到一個小技巧,請閱讀下面文章,當你閱讀完後,你就會感覺大徹大悟。 

如果你能掌握這個技巧,在未來開發或資料管理方面會得心應手。 

如何設計user表 

使用者帳号表 

使用者帳号或通行證系統設計,下面以我的數庫為例講解。 

我一般使用兩個表 passport,profile 完成網站會員系統。 

首先說說passport表,你也要以使用user或member等等命名,這個表設計盡可能地簡單,不要使用過多字段。僅儲存登入所必須用到的字段,如user,password,nickname,email... 登入帳号和密碼做複合索引。 

然後是profile表,這個表與passport是1:1關系,儲存使用者詳細資訊 

這樣設計可以保證海量使用者登入時的速度。 

+----------+ 

| user     | 

|----------| 

|id        | <---+ 

|user      |     | 

|passwd    |     | 

|nickname  |     | 

|status    |     | 

+----------+     | 

                1:1 

| profile  |     | 

|----------|     | 

|user_id   | o---+ 

|name      | 

|sex       | 

|passwd    | 

|nickname  | 

|status    | 

如何設計分類表? 

樹形分類表 

+-----------+ 

| category  | 

|-----------| 

|id         | <---+ 

|title      |     | 

|description|    1:n 

|status     |     | 

|parent_id  | o---+ 

create table `category` ( 

`id` smallint(10) unsigned not null auto_increment, 

`name` varchar(10) not null, 

`description` varchar(255) null, 

`status` enum('enable','desable') not null default 'enable', 

`parent_id` smallint(10) unsigned not null default '0', 

primary key (`id`), 

constraint `fk1` foreign key (`parent_id`) references `category` (`id`) 

comment='goods category' 

engine=innodb 

row_format=default 

多對多分類 

多對多分類,主要用于滿足,一個産品/文章屬于多個分類的需求。 

      +------------+ 

      | category   | 

      |------------| 

+--> |id          | <---+ 

|    |title       |     |     +----------------------+ 

1:n   |description |    1:n    | categroy_has_product | 

|    |status      |     |     +----------------------+ 

+--o |parent_id   |     |     | id                   | 

      +------------+     +---o | category_id          | 

                         +---o | product_id           | 

      +------------+     |     +----------------------+ 

      | product    |    1:n 

      +------------+     | 

      |id          | <---+ 

      |price       | 

      |quantity    | 

      |...         | 

      |status      | 

快速檢索子分類設計 

上面我剛剛講過怎樣實作“不限子樹的分類樹”,我們可以實作不限層次的無線分類表。 

問題出來了,當我需要讀取一個分類(任意分類)下的所有子分類,怎樣實作,很多人會說用“遞歸”。 當然“遞歸”可是現實我們的需求,在幾百個分類的項目中,使用遞歸也不是不可以的,但是當數量非常龐大時怎麼辦? 

當然有更好的解決方案,請看下面 

|path       | 

+-------------------------------------------------------------------------+ 

| category                                                                | 

+----+-----------+-----------------------+--------+-----------+-----------+ 

| id | name      | description           | status | parent_id | path      | 

|  1 | 中國    | 中華人民共和家                                    | y      |      null | 1/        | 

|  4 | 廣東省 | 廣東省                                                      | y      |         1 | 1/4       | 

|  5 | 深圳市 | null                      | y      |         4 | 1/4/5     | 

|  6 | 寶安區 | null                      | y      |         5 | 1/4/5/6   | 

|  7 | 龍華鎮 | null                      | y      |         6 | 1/4/5/6/7 | 

`id` int(10) unsigned not null auto_increment comment '分類id', 

`name` varchar(50) not null comment '分類名稱', 

`description` varchar(200) null default null comment '分類描述', 

`status` enum('y','n') not null default 'y' comment '分類狀态有繼承性', 

`parent_id` int(10) null default '1' comment '分類父id', 

`path` varchar(255) not null comment '分類遞歸路徑索引', 

index `pk` (`id`), 

index `relation` (`id`, `parent_id`), 

index `fk_category_category` (`parent_id`), 

index `path` (`path`) 

comment='分類表' 

auto_increment=0 

insert into category(`name`,`description`,`status`,`parent_id`,`path`) values('中國','中華人民共和家','y',null,'1/') 

alter table `category` 

add constraint `fk_category_category` foreign key (`parent_id`) references `category` (`id`) 

抽取廣東子樹 

select * from category where path like '1/4%'; 

mysql> select * from category where path like '1/4%'; 

+----+-----------+-------------+--------+-----------+-----------+ 

| id | name      | description | status | parent_id | path      | 

|  4 | 廣東省 | 廣東省   | y      |         1 | 1/4       | 

|  5 | 深圳市 | null        | y      |         4 | 1/4/5     | 

|  6 | 寶安區 | null        | y      |         5 | 1/4/5/6   | 

|  7 | 龍華鎮 | null        | y      |         6 | 1/4/5/6/7 | 

4 rows in set (0.00 sec)

文章表設計 

看具體情況,拆分表,可按“日”,“月”,“年”等等 

      +-----------+ 

      | category  | 

      |-----------| 

  +-->|id         | <---+ 

  |   |title      |     | 

  |   |description|    1:n 

  |   |status     |     | 

  |   |parent_id  | o---+ 

  |   +-----------+ 

  | 

1:n 

  |   +-----------------+            +------------------+ 

  |   | article_2008_01 |            | feedback_2008_01 | 

  |   |-----------------|            |------------------| 

  |   |id               |<--1:n--+   |id                | 

  |   |title            |        |   |title             | 

  |   |content          |        |   |content           | 

  |   |datetime         |        |   |datetime          | 

  |   |status           |        |   |status            | 

  +--o|category_id      |        +--o|news_id           | 

  +--o|user_id          |        +-->|user_id           | 

  |   +-----------------+        |   +------------------+ 

  |                              | 

1:n  +----------+     +---1:n---+ 

  |   | user     |     | 

  |   |----------|     | 

  +-->|id        | <---+ 

      |user      | 

      |passwd    | 

      |nickname  | 

      |status    | 

      +----------+ 

4.1. 分區表設計 

分區表可以通過表空間,等等技術實作,優點是解決了union查詢問題,保證了資料的一緻性。 

  |   +-----------------+            +-----------------+ 

  |   | article         |            | feedback        | 

  |   |-----------------|            |-----------------| 

  |   |id               |<--1:n--+   |id               | 

  |   |title            |        |   |title            | 

  |   |content          |        |   |content          | 

  |   |datetime         |        |   |datetime         | 

  |   |status           |        |   |status           | 

  +--o|category_id      |        +--o|news_id          | 

  +--o|user_id          |        +-->|user_id          | 

  |   +-----------------+        |   +-----------------+ 

  |   | 2007,2008,2009  |        |   | 2007,2008,2009  | 

評論表 

                1:n 

+-----------+    |      +-----------+ 

| feedback  |    |      | news      | 

|-----------|    |      |-----------| 

|id         |    |  +-->|id         | 

|title      |    |  |   |title      | 

|content    |    |  |   |content    | 

|datetime   |    | 1:n  |datetime   | 

|status     |    |  |   |status     | 

|user_id    |o---+  |   |user_id    | 

|news_id    |o------+   +-----------+ 

記錄點選率,閱讀次數,及評分表 

+--------------+             +--------------+ 

| article      |             | article_rank | 

|--------------|             |--------------| 

|id            | <---1:1---o |article_id    | 

|title         |             |click         | 

|content       |             |read          | 

|datetime      |             |score         | 

|status        |             |...           | 

|category_id   |             |...           | 

|user_id       |             |...           | 

産品屬性表 

7.1. 簡單實作 

+------------+           +--------------------------+           +-----------------------+ 

| product    |           | product_attribute        |           |product_attribute_key  | 

|id          | <--1:1--o |product_id                |     +---> |id                     | 

|price       |           |product_attribute_key_id  | o---+     |name                   | 

|quantity    |           |product_attribute_value_id| o---+     +-----------------------+ 

|...         |           +--------------------------+     |     +-----------------------+ 

|category_id |                                           1:n    |product_attribute_value| 

+------------+                                            |     +-----------------------+ 

                                                          +---> |id                     | 

                                                                |name                   | 

                                                                +-----------------------+ 

7.2. 實作屬性組管理 

product attribute group 

+------------+           +--------------------------+           +--------------------------+           +-----------------------+ 

| category   |           | product_attribute_group  |           | product_attribute        |           |product_attribute_key  | 

|id          |     +---> |id                        | <--1:n--o |product_attribute_group_id|     +---> |id                     | 

|title       |     |     |name                      |           |product_attribute_key_id  | o---+     |name                   | 

|description |    1:1    |status                    |           |product_attribute_value_id| o---+     +-----------------------+ 

|status      |     |     +--------------------------+           +--------------------------+     |     +-----------------------+ 

|parent_id   |     |                                                                            1:n    |product_attribute_value| 

|default_pag | o---+                                                                             |     +-----------------------+ 

+------------+                                                                                   +---> |id                     | 

                                                                                                       |name                   | 

                                                                                                       +-----------------------+ 

7.3. 可編輯屬表 

    +------------+           +------------------+           +--------------------------+           +---------------------------------+ 

    | category   |           | attribute_group  |           | group_has_attribute      |           |attribute_key                    | 

+->|id          |     +-->  |id                | <--1:n--o |attribute_group_id        |     +-+-> |id                               | 

|  |title       |     |     |name              |           |attribute_key_id          | o---+ |   |name                             | 

|  |description |    1:1    |status            |           |                          |       |   |type enum('bool','list','input') | 

|  |status      |     |     +------------------+           +--------------------------+       |   |default array()                  | 

|  |parent_id   |     |                                                                       |   +---------------------------------+ 

|  |default_pag | o---+                                                                       | 

|  +------------+                                                                             | 

1:n                                                                                            | 

|  +-------------+             +--------------------------+                                   | 

|  | product     |             | product_attribute        |                                   | 

|  |id           |         +-> |product_id                |                                   | 

|  |price        |         |   |attribute_key_id          | o---------------1:n---------------+ 

|  |quantity     |         |   |attribute_value           | 

|  |...          |         |   +--------------------------+ 

+-o|category_id  |         | 

    |attr_group_id| <--1:n--o 

    +-------------+ 

+--------------------------------------------------+ 

| product_attribute_key                            | 

| 1 | color | list | red,green,blue                | 

| 2 | sex   | bool | female,male                   | 

| 3 | qty   | input| ''                            | 

國際化語言表 

      +-----------+             +---------------+ 

      | category  |    .---+    | category_lang | 

      |-----------|   /    |    +---------------+ 

  +-->|id         | <---+  +--o |category_id    | 

  |   |title      |     |       |language_id    | o---+ 

  |   |description|    1:n      |name           |     |      +-------------+ 

  |   |status     |     |       +---------------+     .      | language    | 

  |   |parent_id  | o---+                              \     +-------------+ 

  |   +-----------+                                     >--> |id           | 

1:n                                                   /     |lang         | 

  |   +------------+                                  '      |status       | 

  |   | product    |                                  |      +-------------+ 

  |   +------------+           +--------------+       | 

  |   |id          | <---+     | product_lang |       | 

  |   |price       |     |     +--------------+       | 

  |   |quantity    |     +---o |product_id    |       | 

  |   |...         |           |language_id   | o-----+ 

  +-o |category_id |           |name          | 

      +------------+           +--------------+ 

workflow 

   +------------+     +---------------+     +-----------+ 

   | user       |     | role_has_user |     | role      | 

   |id          |o-+  |id             |  +->|id         |<-+ 

   |node_id     |  +->|user_id        |  |  |name       |  | 

   |up_id       |     |role_id        |o-+  |description|  | 

   +------------+   +---------------+     +-----------+  | 

                                                   | 

   +----------------+     +------------+                   | 

   | workflow       |     | job        |                   | 

+->|id              |  +->|id          |                   | 

|  |job_id          |o-+  |name        |                   | 

+-o|up_id           |     |role_id     |o------------------+ 

   |                |     |description | 

   +----------------+   +------------+ 

内容版本控制 

主表 

create table `article` ( 

`article_id` mediumint(8) unsigned not null auto_increment, 

`cat_id` smallint(5) not null default '0', 

`title` varchar(150) not null default '', 

`content` longtext not null, 

`author` varchar(30) not null default '', 

`keywords` varchar(255) not null default '', 

primary key (`article_id`), 

index `cat_id` (`cat_id`) 

engine=myisam 

auto_increment=1 

本版控制表,用于記錄每次變動 

create table `article_history` ( 

`id` mediumint(8) unsigned not null auto_increment, 

`article_id` mediumint(8) unsigned not null, 

index `article_id` (`article_id`) 

版本控制觸發器 

drop trigger article_history; 

delimiter // 

create trigger article_history before update on article for each row 

begin 

insert into article_history select * from article where article_id = old.article_id; 

end; // 

delimiter; 

我的建表規則很多是基于form模組化(但不是全部都按form走,這樣說讓各位好了解),例如使用者登入表單: 

user 

password 

submit 

當做使用者認證的時候隻需要讀user即可。此時profile空閑 

當點選編輯個人資訊的時候才讀取profile表。 

使用者user表隻有 user,passwd等幾個字段,性能遠比一個大user表好。 

像mysql這樣的資料庫,有些操作會鎖表,将user 分為兩個1:1的表可以避開一部分鎖表影響 

現在我們來設計個order(訂單系統表),會用到上面的product與user表。 

order 表 

id 

user_id 

sn 訂單編号 

... 

created 建立時間 

order_item表 

order_id 

product_id 

.... 

當點選我的訂單時查詢 order表,當點選定點細節時讀order_item表。以此類推,不多舉例。 

延伸閱讀 

http://netkiller.github.com