• 一个关于购物车的数据库课设, 内容:代码+数据库备份 开发环境:Myeclipes+access
  • 淘宝商品数据库设计

    2016-08-19 14:54:35
    这几个月都在做一个通过淘宝API线下管理淘宝店的系统,学习了很多东西,这里想对淘宝商品表设计用自己的想法表现出来,如果你觉得很扯淡,可以写下自己的看法.OK,切入正题.  淘宝的商品这块的复杂程度,是我见...

    这几个月都在做一个通过淘宝API线下管理淘宝店的系统,学习了很多东西,这里想对淘宝商品表设计用自己的想法表现出来,如果你觉得很扯淡,可以写下自己的看法.OK,切入正题.

      淘宝的商品这块的复杂程度,是我见过的电子商务网站中最复杂的,灵活性最高的.在看下文之前,先说一下在淘宝中的以下名词:关键属性,销售属性,非关键属性。如下图:

    关键属性:能够确认唯一产品的属性,可以是一个,或者多个关键属性的组合,比如:相机的"品牌""型号"能确定唯一的产品,服装的"品牌""货号"能确定唯一的产品

    销售属性:组成SKU的特殊属性,它会影响买家的购买和卖家的库存管理,如服装的"颜色"、"套餐"和"尺码",注意这里的SKU,淘宝销售属性组合成SKU

    非关键属性:就是商品属性了,一些对商品进行描述的属性

    特征量,特征值的设计

    最初了解这种设计,是项目经理的指导,他瞅一眼数据结构之后立马说:典型的特征量对应特征值的设计。额,佩服。恩,看下下图这2个表的数据,就很清楚了,

    在特征量这个表中,存放所有的例如:品牌,系列,颜色,尺码 这些名称,在特征值中存放具体的值,比如衣服有12种颜色,那么左表中有 ID:1, 名字:颜色,在右表中存放

    黄色,绿色等等具体的值,特征量表是一个父子关系,比如有的品牌下面有系列,系列下面可能还有系列,右表的数据存在冗余是不可避免的,比如衣服有24个颜色,鞋有24个颜色,虽然特征值是一样,但是是属于不同的特征量的。

    那么如上图所示,(注意,上图中特征量表我并没有添加父子关系,在设计的时候是有父子关系的)我要添加一个避孕套商品,最少分为2步,首先在类别表中选中计生用品-->一直到避孕套这个子分类,通过子分类的ID,级联查询特征量和特征值这个表,得到诸如品牌,大小,颜色这种属性,如果存在层级关系,比如品牌下面的系列,系列下面还可能有系列,通过特征量表中的父子关系,继续查询出来,当然,也可以通过类目ID一次性查询出来,然后进行关系显示。

    对具有不同属性的同种商品做的不同价格,数量设计?

      

      我们看在淘宝中添加衣服和数码相机的情况,这里会出现选择"关键属性" 颜色+尺码之后,出现需要对不同颜色尺码的组合填写价格和数量和商家外部码,相机则是颜色+套餐+外部码,而到了洗发水就是容量+多买多便宜+外部码,这里就是上面介绍的SKU,如果你还不了解SKU,可以做下了解,这样子做的直接好处就是,不同的搭配可能有不同的价格,做库存和销量的统计的时候就能做到准确统计,通过商家的外部编码能让商家关联本地系统,也是为了做统计和库存的方便。

    恩 大伙瞅瞅我的设计吧,要拍,请轻拍

    2011-03-23 修正:这篇文章以我现在的理解来说,表设计存在问题!!请看到这里马上离开!你也可以关注我后续淘宝分析相关文章~

    1.类目和特征量,特征量和特征值并不是多对多的关系,是一对多的关系

    2.类目和特征量都是父子表

    3.商品的关键属性如:品牌 系列(只存放子及ID),其它属性,比如说裤子的裤型,适合人群,等属性存入基本属性表,这里品牌系列只存放子及ID是因为考虑到,不想建立过多的表,用户搜索用例如lunce把全部信息建好索引,做统计或者数据分析的时候也是可以通过父子特征量的父子关系进行的。

    4.销售属性的分开是因为,比如洗发水,我们的特征值表中的值可能不能符合用户需求,那么可以让用户自定义值,颜色可以上传图片等,而且销售属性一般为多个组合,所以单独建立一个表。

    5.商品的SKU表,这个特征量ID的组合实际上就是销售属性表的ID组合

    当我们添加一个商品的时候,在不考虑页面展现的情况下需要这几步:

    1.插入商品基本信息到商品表 

    2.插入基本属性和关键属性到商品基本属性表 

    3.插入销售属性到销售属性表

    4.插入销售属性ID组合,数量,价格等到商品SKU表。

    当我们在页面上展示商品的时候:

    1.级联6个表查询出商品所需要的信息,由于关键属性有多级,这里只存了子及,需要做一次递归。当然可以考虑缓存等自己的实现方法

    2.分析商品SKU组合,生成销售属性组合及约束,比如颜色+尺码 选择之后的不同价格,是否还有货。颜色有图片的用上传的图片代替默认背景

    难点在于:

    1.页面的展现不管前台还是后台都必须动态构建,在动态构建的基础上加上基本的验证,比如用户选择了 颜色A+尺码,库存只有3件,数量填写了5,必要提示用户没有了这么多数量,动态构建中还存在某些属性有层级关系,需要按照自己的策略选择一次性加载或者AJAX加载,进行分析显示

    2.几个表关联查询的设计,何种方法能最大化减小程序的复杂程度,是直接在数据库中搞定,还是数据库结合内存表(缓存特征量和特征值).

    页面上的展现和其它设计

    我在实际项目中添加商品的做法是,序列化属性成json字符串到页面,根据特征量中的是否是枚举,是否是父属性,是否关键属性,是否销售属性来动态创建页面表单,效果如他的API页面,如果你有兴趣,可以参考他的JS

    杨过大哥的博客中,这篇讲到了他的网站添加商品的时候动态创建表单的做法,还有一篇类目属性的设计,那种类目属性设计也是一种不错的设计,有兴趣可以看看。

    恩 文章到这完了,睡觉去了。 今天可能是博客园360 和 qq 的软文的又一个小高潮,悲剧啊!

    展开全文
  • 天猫网购的数据库课程设计,界面用vf做成,连接数据库
  • 仿天猫商城数据库关系 仿天猫商城系统页面 后台数据页面、功能和SQL语句 1、 功能:向数据库category表中插一条数据,后台记录商品类 INSERT INTO category(name)VALUES(...) 2、 向数据库...

    仿天猫商城数据库关系

    这里写图片描述


    仿天猫商城系统页面

    后台数据页面、功能和SQL语句

    1、

    功能:向数据库category表中插一条数据,后台记录商品类

    INSERT INTO category(name)VALUES(...)

    2、这里写图片描述
    向数据库property表中插一条数据
    功能:向数据库property表中插一条数据,往后台添加类属性,

    INSERT INTO property(cid,name)VALUES(...)

    3、这里写图片描述
    功能:向数据库product表中插一条数据,后台添加商品

    INSERT INTO product(name,subTitle,originalPrice,promotePrice,stock,cid,createDate)VALUES(...)

    4、这里写图片描述
    功能:修改数据库category表中的数据,修改类名称

    UPDATE category SET name='..' WHERE id=..

    5、这里写图片描述
    功能:删除数据库category表中的一条数据,删除类

    DELETE FROM category WHERE id=..

    6、这里写图片描述
    功能:向数据库productimage表添加数据,添加商品照片

    INSERT INTO productimage(pid,type)VALUES(...)

    7、这里写图片描述
    功能:向数据库productimage表删除数据,删除照片

    DELETE FROM productimage WHERE id=..

    8、这里写图片描述
    功能:查询category表数据增加分页

    SELECT* FROM category LIMIT 0,5

    前台页面、功能和SQL语句

    1、这里写图片描述
    功能:向数据库user表中添加数据,注册表

    INSERT INTO user(name,password)VALUES(..)

    2、这里写图片描述
    功能:查找数据库user中的数据,登录页面

    SELECT COUNT(name) FROM user WHERE name='' AND password=''

    3、这里写图片描述
    功能:查找数据库product表数据,搜索

    SELECT * FROM product WHERE name ='%...%'

    4、这里写图片描述
    功能:查找数据库order表数据,我的订单数据

    SELECT * FROM product WHERE uid=...  AND status ='finish'

    5、这里写图片描述
    功能:查找数据库order表数据,我的购物车

    SELECT* FROM product WHERE uid=...  AND status =waiterPay

    6、这里写图片描述
    功能:查找数据库review表数据,查看评论

    SELECT * FROM review WHERE pid= ...

    7、这里写图片描述
    功能:查找单个商品页面

    SELECT p.name,p.subTitle,p.originalPrice,p.promotePrice,p.stock,pi.type
    FROM product AS p
    LEFT JOIN productimage AS pi
    ON p.id=pi.pid

    8、这里写图片描述
    功能:向order表添加数据,填写地址信息

    INSERT INTO order(orderCode,address,post,receiver,mobile,userMessage,createDate,payDate)VALUES(...)
    展开全文
  • 首页所需数据库分析

    首先先分析我现在的状况,只会 mysql 和 mongodb,而 thinkphp 框架与这两者的连接我也是成功的,只能进行简单的CUED操作,难一点的数据库操作我就不会了尴尬

    先从首页开始进行分析:



    现在单纯考虑数据库的情况的话,要满足首页的话我需要三张表,一张当然是产品表,现在需要的列有:产品id,产品名称,产品分类id,一张销售表,但是这个销售表存在问题,例如后台管理者想了解某一商品的销量当然容易,但是想要了解这款产品的不同型号的情况的话就不能只记录这款产品的销量了,拿衣服举例,想统计这款产品的各个颜色销量如何,还有各个尺码的销售情况,这个情况当然是从销售表中读了,那么销售表要怎么设计才能满足这个要求?

    拿 mysql 来存可以吗?以产品id为外键,以产品的属性为单独一列,例如如下:

    ProductID AttributeName Vallue
    1 red 100
    1 blue 90
    1 XL 80
    1 L 70
    1 M 60
    可能觉得应该把 red 和 XL 分开两个字段进行存储,但是不能确定产品的到底有几个属性,例如还有面料这一属性,难道再增加一个字段?所以还不如存储在一个字段中,但是这样会有一个大麻烦,超级大的麻烦!我想要知道 red 下 XL 有多少件?瞬间傻掉了,而且这样对于后台进行产品录入的时候也是一个难题,所以这个方案不行,这个时候貌似使用 mysql 搞不定,因为表的字段的限制,导致存储数据不太灵活,这个时候就想到了 mongodb 了,mongodb 采用 json 形式存储数据

    例如,插入数据形式如下:

    db.admin1.insert({
    "ProductID":[{
    "red":[{
    "xl":100},{     //表示红色下 XL 的存货有100件
    "l":90},{
    "m":80}
    ]
         }
        ]
    }

    ...其他颜色的数据,格式如上...

    )


    采取 json 的数组形式存储数据,就很好的解决了我们上面的问题,而且 json 的格式内容不受约束,可以满足不同类型的产品需求,但是就是不知道读写速度比起 mysql 怎么样。

    其实如果一定要采用 mysql 进行存储也不是不行,借用无限分类表的设计理念,可以这样设计表:

    AttributeID attribute_name value parent_AttributeID ProductID
    1 red 100 0 1
    2 XL 60 1 1
    3 L 40 1 1
    4 blue 200 0 1
    5 XL 100 4 1
    6 L 100 4 1






    使用这样的形式就可以存储这样的数据

    ProductID 1

    red: 100 其中 XL:60 L:40

    blue:200 XL:100 L:100
    至于如何实现的,具体看 parent_AttributeID ,这样做就是这张表的记录条数会超级多,比如商品有1000件,每件有3个属性,那么每件商品在这张表中拥有的记录数是:3*3*3=27,这张表上的记录就会有1000*27=27,000条。

    至于如何抉择那就看个人了,我的话选择 mongodb 进行存储

    上面说了两张表,现在说第三张表,分类表,分类表采用无限分类的表设计

    CatagoryID category_name parent_Category
    1 服装 0
    2 男装 1
    3 女装 1
    4 西装 2
    5 休闲装 2
    6 运动服 2

    只要添加 parent_Category,就可以一直无限分类下去,而且这张表也是搜索框的灵魂,我个人的搜索习惯是不会直接打品牌名称进行搜索,而是按照分类和功能来打,例如想找男士秋装,那么我会输入:

      男士  秋装

    之后就会出现一些提示,这些提示一般是热搜的词语,但是我想要直接一点,在用户输入关键字之后,搜索这个分类表,之后把他的父级分类加一个字分类和他本身一起,组成提示关键字,例如淘宝上这样:


    总结以下,现在需要的三张表的情况如下:

    product:ProductID product_name CategoryID 

    category:CategoryID category_name parent_CategoryID 

    salerecord:采用 mongodb 存储

    这三张表在后面还需要改的,特别是 product

    展开全文
  • 模仿淘宝网的设计,其中设计到存储过程,索引的建立,对概念模型,逻辑设计,物理设计需求分析各部分模块都有很好的分析等等vb.net+sql2005。希望对大家有用。 网上购物系统的应用性较强,所以在制作网站之前,大致...
  • 前言 这是关于 MySQL 系列文章的第三篇,在前两篇文章 《MySQL —— 数据库基础》 和 《MySQL —— SQL 语句总结》 中,...来简单聊一聊数据库设计,还有一句话不得不再次赘述,数据库博大精深,本系列文章内容较...

    在这里插入图片描述


    阅读原文


    前言

    这是关于 MySQL 系列文章的第三篇,在前两篇文章 《MySQL —— 数据库基础》《MySQL —— SQL 语句总结》 中,主要介绍了一些数据库的基础概念、创建表的方式以及 SQL 语句的使用,本篇在使用的基础上做一个小小的升华,来简单聊一聊数据库的设计,还有一句话不得不再次赘述,数据库博大精深,本系列文章内容较浅,适合于前端的同学们对 MySQL 的入门,这也是我的学习笔记,希望可以帮助大家。


    为什么设计数据库

    说到为什么要设计数据库,就要说到数据的完整性,我们要在设计数据库时保证域的完整性和实体的完整性,同时从性能出发,我们要保证最大限度的节省存储空间,比如一张成绩表,上面没必要存储学生的姓名、年龄等信息,只需要存储成绩,如果一个数据库设计的合理,最后的结果就是方便我们对数据库的开发和扩展。

    如果是一个 “糟糕” 的数据库设计会造成一系列的不良反应,比如数据冗余,存储空间浪费,内存浪费,有时甚至会造成数据插入和更新的异常,比如学生表存了学生信息,而成绩表也存了,这样在修改时没有全部修改就会出现错误。


    软件项目开发中数据库设计的生命周期

    软件项目开发中数据库设计的生命周期可大概分为以下几个阶段:

    • 需求分析阶段,分析客户的业务和数据处理需求;
    • 概要设计阶段,设计数据库 E-R 模型图,确认需求的正确和完整性;
    • 详细设计阶段,应用三大范式审核数据库;
    • 代码编写阶段,物理实现数据库,编码实现应用;
    • 软件测试阶段;
    • 安装部署阶段。

    上面数据库的设计经历了从 “现实世界” 到 “信息世界” 到 “数据库模型” 再到 “数据库” 产生的一个完整过程。


    设计数据库的步骤

    收集信息:收集信息其实就是与相关人员进行交流、访谈、调研,充分了解用户需求,理解整个项目的完整流程,并理解数据库需要完成的任务,这部分工作大部分由需求人员完成,并根技术人员进行对接。

    标识实体和实体属性:开发人员在明确需求和流程之后,标识数据库的实体,比如学生信息表,每一条实体中应该由哪些字段组成,成绩表中实体由哪些字段组成等等。

    标识实体之间的关系:其实就是通过表之间的某字段对表进行关联,对表的实体之间建立对应关系,如学生表的 id 字段会关联成绩表的 student_id 字段,用来查找某个学生的成绩。


    数据库 E-R 图

    1、E-R 图基本概念

    E-R 图也叫做实体关系图,是指用实体、关系、属性三个基本概念概括数据的基本结构,从而描述静态数据的概念模型。

    E-R 图的实体:即数据模型中的数据对象,每一张表就是一个 E-R 图的实体。

    E-R 图的属性:即数据对象中所具有的属性,例如学生表的学生、姓名、年龄等,属性又分为唯一属性和非唯一属性,唯一属性如经过唯一约束和主键约束的属性,不可重复,其他的都是非唯一属性。

    E-R 图的关系:用来表示每一个数据对象与数据对象之间的联系,即每一个实体之间的联系,例如学生表和成绩表之间的联系,因为每个学生都有自己的成绩。

    2、E-R 图的关联关系

    (1) 1 对 1 (1 : 1)

    11 关系是指对于实体集 A 和 实体集 BA 中的每一个实体最多与 B 中的一个实体有关系,反之在实体集 B 中的每一个实体之多与实体集 A 中的一个实体有关系。

    在这里插入图片描述

    (2) 1 对多(1 : N)

    1 对多关系是指实体集 A 与实体集 B 中至少有 N (N > 0) 个实体有关系,并且实体集 B 中最多与实体集 A 中的一个实体有关系。

    在这里插入图片描述

    (3) 多对多(M : N)

    多对多关系是指实体集 A 中的每一个实体与实体集 B 中至少有 M (M > 0) 个实体有关系,并且实体集 B 中的每一个实体与实体集 A 中至少有 N (N > 0) 个实体有关系。

    在这里插入图片描述


    数据库设计的三大范式

    1、确保每列的原子性

    如果每列都是不可再分的最小单元信息,则满足第一范式,比如下图中,地址是由国家和城市组成的,显然可以继续在拆分成两个列,国家和城市,是不满足第一范式的,需要将地址列差分成国家和城市两个列。

    在这里插入图片描述

    举一个简单的例子,我们平时在淘宝购物的时候需要添加地址,在填写新地址时,都是让我们选择国家、省、城市、区、街道、小区这样的方式,而不是让我们自己将这些地址写在一起,其原因就是因为淘宝的数据库设计严格遵循每列的原子性,这样的提交可以方便后端获取每一个列的信息在数据库中进行存储。

    2、每个表只能描述一件事情

    如下图中所示,在左侧的表中,描述了学生信息和课程信息,这明显是两件事情,假设再有一张成绩表,也要描述学生信息,课程信息和成绩等多件事情,就会造成数据的重复、冗余,也可能会导致更新、插入、删除数据异常的现象。

    在这里插入图片描述

    所以正确的做法是应该将左侧表差分成两张表分别为学生表和课程表,并使用学生编号与课程编号进行关联。

    3、其他列都不传递依赖于主键列

    其他列都不传递依赖于主键列的意思是表中各列必须都与主键直接相关,不能简介相关,从下图左表可以看出,学生编号为主键,年级 ID 也应该为主键,正常应该通过学生编号找到年级 ID,再找到年级名称,这样年级名称与学生编号之间就形成了一个传递并且依赖于主键年级 ID,即年级 ID 做为主键在中间隔了一层,这样就使年级名称与主键学生编号间接相关,如果在同一张表中,所有的字段都是应该直接依赖于主键,而不是再通过其他的主键传递。

    在这里插入图片描述

    如果一个表中表述了多件事情并有多个作为主键的列,与上一条的处理方式相同,应该拆成多张表,并且每张表只有一个主键列。


    RBAC 基于角色的访问控制

    1、RBAC 的含义

    RBAC(Role-Based Access Control)基于角色的访问控制,就是用户通过角色与权限进行关联,简单的说,一个用户拥若干个角色,每个角色拥有若干个权限,这样就构造成了 “用户 → 角色 → 权限 → 资源” 的授权模型,在这个模型中,用户与角色之间,角色与权限之间,权限与资源之间,一般都是多对多的关系,在 RBAC 中最重要的概念主要有四部分,就是用户(User)、角色(Role)、权限(Permission)和资源(Resource)。

    2、RBAC 的安全原则

    • 最小权限原则:最小权限原则之所以被 RBAC 所支持,是因为 RBAC 可以将其角色配置成完成任务所需要的最小的权限集;
    • 责任分离原则:可以通过调用相互独立互斥的角色来共同完成敏感的任务而体现,比如要求一个计帐员和财务管理员共参与同一个帐目;
    • 数据抽象原则:数据抽象可以通过权限的抽象来体现,如财务操作用借款、存款等抽象权限,而不用操作系统提供的典型的读、写、执行权限。

    3、RBAC 的 E-R 图

    之前说 RBAC 最重要的概念由四部分,其实体现在数据库的表中有主要三部分,因为角色和用户是重叠的,那么主要有三张表分别为用户表、权限表和资源表,其中用户表与权限表之间有一张关联表,权限表与资源表之间有一张关联表,E-R 图如下。

    在这里插入图片描述


    事务

    1、为什么需要事务?

    在生活中我们经常使用银行转账或者支付宝和微信支付,这种操作每一次至少影响两个用户的数据信息,比如一方给另一方转钱,如果成功则转钱方余额减去转出金额,而收钱方余额增加收到的金额,这应该是一个请求操作了数据表中的俩个实体,如果在两个操作数据的环节任意一个失败了,都会影响两个人数据的正确性,这种时候需要两个操作同时失败或同时成功,就是说有一个操作出现失败的情况,即使另一个成功了也需要进行回滚操作,这就是事务的由来。

    2、什么是事务

    事务是作为单个逻辑工作单元执行的一系列操作,多个操作作为一个整体向系统提交,要么都执行,要么都不执行,是一个不可分割的工作逻辑单元。

    转账过程就是一个整体,它需要两条 UPDATE 语句,如果任何一个出错,则整个转账业务取消,两个账户的余额都恢复到原来的数据(回滚),确保总余额不变。

    这里再举一个例子,有一个上传文件的功能,后端接收到文件流时是需要先写入的,当写入成功后,会将上传成功的结果返回给客户端,如果文件很大,写入的时间就会长,如果在此期间突然写入失败,则会删除之前写入的内容,将整个操作回滚到写入之前,这里面主要两步操作,创建一个新文件并写入,写入成功删除旧文件,如果写入失败,两个操作将会同时失败,即不会删除旧文件,这也是一个事务的例子,只是没有转账那么明显。

    3、事务的特性 ACID

    事务具有以下特性,被简称为 ACID:

    • 原子性(Atomicity):事务是一个完整的操作,事务各个部分是不可分的,要么都执行,要么都不执行;
    • 一致性(Consistency):当事务完成后,数据必须处理完整的状态;
    • 隔离性(Isolation):并发事务彼此隔离、独立,它不应该以任何方式依赖于其它事务;
    • 持久性(Durability):事务完成后,它对数据库的修改被永久保持。

    4、如何创建事务

    (1) 创建表

    创建表 accountid 列为主键列,name 列为姓名,balance 为余额。

    CREATE TABLE `account` (
        `id` INT(11) NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(64) NOT NULL,
        `balance` INT(11) DEFAULT 0
        PRIMARY KEY (`id`)
    );
    

    (2) 添加数据

    将表 account 添加两条数据,分别为 “张三” 和 “李四”,余额都为 100

    INSERT INTO `student` (`name`, `balance`) VALUES ("张三", 100);
    INSERT INTO `student` (`name`, `balance`) VALUES ("李四", 100);
    

    (3) 使用 NodeJS 实现事务

    const mysql = require("mysql");
    
    // 创建数据库连接
    const connection = mysql.createConnection({
        host: "localhost", // 主机名
        port: "3306", // 数据库服务端口号
        username: "root", // 数据库名称
        pwd: "123456", // 数据库密码
        database: "school"  // 连接的数据库名称
    });
    
    connection.connect();
    
    // 开启事务
    connection.beginTransaction(err => {
        // 回调参数为错误对象,返回结果,返回字段描述
        connection.query("UPDATE account SET balance - 50 WHERE id = 1", (err, result, fields) => {
            if (err) {
                connection.rollback(); // 如果失败直接回归
            } else {
                connection.query("UPDATE account SET balance - 50 WHERE id = 1", (err, result, fields) => {
                    if (err) {
                        connection.rollback(); // 如果失败直接回归
                    } else {
                        connection.commit(); // 如果两个都成功了则提交事务
                    }
                });
            }
        });
    });
    

    总结

    到此关于 MySQL 的系列文章就告一段落了,希望前端的同学们在看了这几篇文章后对你们入门 MySQL 有一些帮助,那这几篇的文章就达到目的了,也欢迎后端的小伙伴来指出文章中的错误和不足。


    展开全文
  • 关于论坛数据库设计 文章分类:数据库 一个简单的论坛系统 1:包含下列信息: 2:每天论坛访问量300万左右,更新帖子10万左右。 请给出数据库表结构设计,并结合范式简要说明设计思路。 一. 发帖主题和回复...

    数据库该如何设计,一直以来都是一个仁者见仁智者见智的问题。

    对于某一种数据库设计,并不能简单的用好与不好来区分。或许真的应了那句话,没有最好,只有最适合。讨论某种数据库设计的时候,应该在某种特定的需求环境下讨论。

    下面来讨论一下在项目中经常碰到的用户的联系方式储存的问题。

    我在这里套用之前网络上流行“普通——文艺——二逼”的分类方式来描述我下文中提及的三种数据库设计思路,并且通过查询数据(对数据增删改,三种设计要付出的代码成本都差不多)和数据库面临需求变动两个方面来思考这三种设计各有怎样的优劣。

    普通青年:

    或许我们都这样设计过数据库

    学生表 tb_Student:

    Name varchar(100) 名字
    Telphone varchar(200) 联系电话
    Email varchar(200) 你懂的
    Fax varchar(200) 传真

    这应该是最容易想到的一种思路,简单、明了。

    比如说我要查询某个人的联系方式,那么我只用一条语句就能实现:

    select Name,Telphone,Email,Fax from 表 where 条件

    在查询的时候,这种数据库设计十分清晰,没有任何思维的难度,没有任何逻辑的挑战。但是当面临需求变动的时候,那将会是一场灾难。

    比如现在要新增一类用户:校长。那么我们要如何处理?

    答案是:再加一张表 tb_Headmaster。

    事实上,再加一张表其实修改并不大,因为我们完全不需要修改学生表的存储逻辑,换句话说,这种设计是遵循了开闭原则的

    但如果学生要添加一种联系方式HomePhone的时候,灾难发生了

    怎么办?

    在tb_Student中加一列HomePhone?这意味着至少要修改整个Model层(或者说DAL层),这种改动是十分巨大的,而且容易造成错误。

    或者再建一张表tb_Student2,来储存HomePhone,然后以ID来关联两张表?按改动规模来说,这种改动相对简单而且不容易出错,但是在今后的维护中会增加逻辑成本。当你一而再再而三的以这样的方式来应对需求变动的时候,你的程序将变得不可理解。

    文艺青年:

    UserRole int 对应用户类型(None = 0, Student = 1, Teacher = 2, Headmaster = 4)
    OwnerID int 对应用户ID
    ContactMethod int 联系方式(None = 0, Email = 1, HomePhone = 8, WorkPhone = 16,MobilePhone = 32,Fax=64)
    ContactInfo varchar(255) 联系信息

    这种是一个一对多关系。当我们要查询某个用户对应的联系方式的时候,那是一场逻辑上的浩劫:

    select ContactInfo from 表 where UserRole=某种用户类型 and OwnerID=某用户ID

    这种写法是一次性取出某个用户所有的联系方式,包括Email,HomePhone,WorkPhone等,之后我们可以在程序中判断ContactMethod的类型,将具体的联系方式加以区分。你可以简单的想到用switch-case的写法,类似这样:

    var contact = 上面的SQL语句取出来的用户所有的联系方式;
                foreach (var item in contact)
                {
                    switch (item.ContactMethod)
                    {
                        case ContactMethod.WorkPhone:
                            txtWorkPhone.Text = item.ContactInfo;break;
                        case ContactMethod.Email:
                            txtEmail.Text = item.ContactInfo;
                            break;
                        case ContactMethod.Fax:
                            txtFax.Text = item.ContactInfo;
                            break;
                        case ContactMethod.OtherPhone:
                            txtOtherPhone.Text = item.ContactInfo;
                            break;
                        case ContactMethod.MobilePhone:
                            txtMobilePhone.Text = item.ContactInfo;
                            break;
                    }
                }

    当然你也可以尝试下面这种写法,我个人认为这种写法更优雅

    var contact = 上面的SQL语句取出来的用户所有的联系方式;            
    txtWorkPhone.Text = (from a in contact
                         where a.ContactMethod == ContactMethod.Work_Phone
                         select a.ContactInfo).ToString();//后面以此类推,你懂的

    注意,请不要试图使用类似下面这类语句来查询某用户的联系方式:

    select ContactInfo from 表 where UserRole=某种用户类型 and OwnerID=某用户ID and ContactMethod=1    
    //取出某用户的Email
    select ContactInfo from 表 where UserRole=某种用户类型 and OwnerID=某用户ID and ContactMethod=8   
     //取出某用户的HomePhone

    相信我,这种做法非常愚蠢:每当你要取出这个用户的一种联系方式,就要和数据库建立一次连接,打开/关闭一次数据库;这种做法代价是十分巨大的,即使有数据库连接池,即使有数据库缓存,都应该避免这种愚蠢的做法

    唔,用了那么多的代码,终于查出了某个用户的联系信息了。反正我个人觉得这种设计方式在查询的时候,是逻辑上的浩劫。什么?你说你很享受?好吧,看来是我脑容量不够…

    不过当我们面临需求变动的时候,那就非常愉快了

    什么,要加一类用户?简单,UserRole加一个枚举就好了。

    什么,要加一种联系方式?ContactMethod加一个枚举就OK。

    使用了这种表设计的时候,相信你会微笑着面对需求变动的

    二逼青年

    昨天和同事也探讨了下这个问题,按他的说法就是:哪个表要联系方式,我就扔个字段进去,存json

    Contact varchar(8000) 用于储存json

    举例来说,有这么一个用户:

    ID:1 Name:张三 Telphone:1234 Email:123@123.com Fax:5678

    那么数据库中就这样存:

    [{"ID":1,"Name":"张三","Telphone":"1234","Email":"123@123.com","Fax":"5678"}]

    当我听到这种设计思路的时候,虎躯微微一震:靠,这都行。按这种设计,我整张表都放进一个json里面一股脑的存进去就算了。不过震惊之后仔细想一想,其实这种设计也是有可取之处

    首先,从查询来说,和普通青年一样,只需一句SQL:

    select Contact from 表 where 条件

    查询之后,就可以通过json处理函数将想要的数据取出来,在此就不赘述了

    那么当面临需求变动的时候会发生什么:

    加一类用户的时候,要添加一张表。也是符合开闭原则,原有代码没有改动

    加一种联系方式,只用存json的时候多存一点东西

    不过这种设计如果要更新某条数据的话要稍微麻烦一点:先查询一条数据,重组json之后再Update

    最近公司要开发新系统,基本决定使用ORM(高层还在犹豫,担心效率问题)。既然使用了ORM,那么自然而然的就想到了用面向对象的思想来设计数据库

    本篇文章旨在讨论如何抽象(以用户作为抽象的例子),并提出一些解耦的思路

    我也是第一次在实际项目中使用面向对象的思想来设计数据库,写下这篇博客,也是希望与大家多多交流

    正文开始

    首先来需求分析

    我们的系统有前台和后台,前台用户有:Man,Woman,SuperMan,SpiderMan与IronMan。后台用户为Administrator

    前台用户都要填写联系方式与地址,然后SuperMan,SpiderMan与IronMan都有Ability

    需求很简单。那么按照这个需求,我们来随手画一个继承关系图。其中V代表抽象类(应该是abstract,画图的时候脑抽想着是virtual就用V开头了,懒得改图了大家凑合着看吧),I代表Interface。如下图:

    从图中可以看出,由抽象类Person派生出Administration与抽象类User。类Man与类Womam实现了接口Address与接口Contact,Inhumans则实现了Ability接口

    然后抽象类代码:

    View Code 
    
        public abstract class Person
        {
            public string Username { get; set; }
            public string Password { get; set; }
        }
    
        public abstract class User : Person
        {
            public string Name { get; set; }
        }

    接口代码:

    View Code 
    
        public interface IAddress
        {
            string Address { get; set; }
        }
    
        public interface IContact
        {
             string Email{get;set;}
             string WorkPhone { get; set; }
             string MobilePhone { get; set; }
             string Fax { get; set; }
        }

    最后是Man类和Woman类:

    View Code 
    
        public class Man : User, IContact, IAddress
        {
            public string Address { get; set; }
            public string Email { get; set; }
            public string WorkPhone { get; set; }
            public string MobilePhone { get; set; }
            public string Fax { get; set; }
    
            public bool HasCar { get; set; }       //如果这三项都为false的话
            public bool HasHouse { get; set; }     //这辈子就甭想结婚了
            public bool HasMoney { get; set; }     //T T我泪涌
        }

    View Code 
    
        class Woman : User, IAddress, IContact
        {
            public string Address { get; set; }
            public string Email { get; set; }
            public string WorkPhone { get; set; }
            public string MobilePhone { get; set; }
            public string Fax { get; set; }
    
            public bool IsBeauty { get; set; }  //这个为true,一辈子不愁吃喝
        }

    代码非常简单。其他几个类限于篇幅就不说那么细了

    那么按照这个model,使用EF Model First来建立数据库,得到的Woman表如下:

    那么接下来就是重点了:为什么不把Contact和Address分表储存。这样与Man表、Woman表写在一起的话,出现改动(如新增一种联系方式),会不会非常痛苦

    如果不是使用ORM,那么这个改动的确是很痛苦;但是如果使用了(这里默认使用的ORM可以从Model生成/改动数据库),那么这个改动是没什么大不了的了,只需要修改一下接口定义,然后根据报错去改就好了。至于数据库的变动,就交给ORM去做就OK了

    这样有一个好处,可以在有限的范围内实现解耦,部分减少了关系——若将Contact和Address分表的话,取Woman要Join两次,这看起来没什么大不了的,但是如果放大了看,如果是join十次呢?这样弄出来的东西很难去维护(现在公司老系统就是这样,动不动就join十次二十次的,改动起来十分费力)

    具体怎么去解耦,这个问题相当相当的深奥,就不敢在这班门弄斧了

    在上面,园友Jacklondon Chen提出了一些问题,大致如下:

    “man/woman应该设计在同一张表中。 用户表大多都设计成一个表。连分 administrator 和 user 都不应该。”

    我想还是因为我举例太随意,因为博文中Man和Woman只有4个差异属性:HasCar\HasHouse\HasMoney,以及IsBeauty

    其实对于这个问题我无力吐槽什么,简单的说说吧:假设为Man用户实现的是一个征婚系统,而Woman用户实现的是一个选美系统。这么说应该能理解Man和Woman的不能并同一张表的原因了吧

    废话说完,正文开始

    /*=============================================================*/

    现在有一个系统,我们暂时假设为学校选课系统。有两类用户Teacher和Student,还有一张Curriculum表是课程总表,来储存学校一共有哪些课程,每门课的学分什么的。然后一个老师,一门课程和多名学生,就可以开始上课了

    表结构如下图:

    逻辑很简单,一目了然

    但是问题在于,我们的系统要按学校来卖。每个学校的选课逻辑都是一样的,而表中的数据有共性,但是也有差异性。比如说基本的Teacher表结构是这样的:

    现在把系统卖给A学校。A学校除了的Teacher表除了用户名和密码之外,还要储存老师的FirstName和LastName,那么表结构变化如下:

    现在B学校也买了我们的系统。他们的Teacher表不要FirstName和LastName,但是要储存教师的工号“Number”,表结构如下:

    好,现在我们的问题出来了:怎么去解决这种差异性

    最简单的思路莫过于表中加冗余字段。比如说将表设计成这样:

    如果我们的系统只卖两三个学校,这样是可行的。但是打个比方,我们的系统卖了30所学校,每个学校有一个自己的差异字段,那么这个表就要有30个冗余字段来应对这种差异性。且不说每次加冗余都要改动系统,且不说冗余多了浪费空间降低传输效率,光说怎么维护这些冗余,我就已经觉得是灾难了:Teacher表有差异字段,其他表也会有。假设一个中型系统,60张表,其中30张实体表30张关系表不算过分吧。那么总共要维护 30(表数量)*30(冗余数量) = 900 个差异字段

    第二个想法是建立一张冗余表来储存差异。这种其实和表中加冗余异曲同工,就不多加分析了,留给大家自己思考

    第三个想法是建立不同的数据库。其实本来每个学校的数据库就是不同的,唔……怎么说呢,A学校自己的数据库中的表,存的是A学校自己的特有字段,B学校存B学校的特有字段。两者之间并无关系,然后Model用l继承的思路来设计(详见上一篇文章),通过配置文件来选择恰当的数据库和其对应的Model

    是的,这种方法挺好的,唯一的不足可能就是比较依赖于ORM——使用ORM来生成数据库,以及T-SQL语句

    如果您是一个关系型数据库的重度爱好者,那么这篇文章到这就结束了,下面的东西不会对您胃口的

    /*=============================================================*/

    众所周知,因为大量使用了反射,ORM的效率不是那么的高,而且本身关系型数据库的可拓展性也不是那么的好

    作为一个激进的开发者,我一直希望在项目中尝试NoSql




    一、一个简单的论坛系统


    1:包含下列信息:

    2:每天论坛访问量300万左右,更新帖子10万左右。

    请给出数据库表结构设计,并结合范式简要说明设计思路。

    一. 发帖主题和回复信息存放在一张表,并在这个表中增加user_name字段 
    对数据库的操作而言,检索数据的性能基本不会对数据造成很大的影响(精确查找的情况下),而对表与表之间的连接却会产生巨大的影响, 特别在有巨量数据的表之间;因此对问题的定位基本可以确定:在显示和检索数据时,尽量减少数据库的连接以及表与表之间的连接; 
    引用
    1: user:用户基本信息表 
    字段有:user_id,user_name,email,homepage,tel,add...

    2: forum_item:主题和回复混合表 
    字段有:id,parent_id,user_id,user_name,title,content,....

    parent_id=0或者null表示是主题,否则=n表示是id=n那条帖子的回复 
    UserName字段是冗余的,因此在用户修改UserName的时候就会产生同步数据的问题,这个需要程序来进行弥补

    二. 主题表和主题回复分开保存 
    引用
    1: user:用户基本信息表 
    字段有:user_id,user_name,email,homepage,tel,add...

    2: forum_topic:主题表 
    字段有:id,user_id,title,content,....

    3: forum_topic_back:主题回复表 
    字段有:id,topic_id,user_id,title,content,....

    三. 主题表的内容单独设计成一个表 
    引用
    1: user:用户基本信息表 
    字段有:user_id,user_name,email,homepage,tel,add...

    2: forum_topic:主题表 
    字段有:id,user_id,title,....

    3: forum_topic_content:主题内容表 
    字段有:id,topic_id,content

    4: forum_topic_back:主题回复表 
    字段有:id,topic_id,user_id,title,content,....

    四.用户信息分2个表保存,并对相关表进行分表处理 
    引用
    1: 简单用户表 tb_user: 
    id , username

    2: 用户详细信息表 tb_userinfo 
    id,userid , email , homepage , phone , address ...

    3: 论坛主题表 tb_bbs 
    id , userid , title , ip , repleycount , replyuserid , createtime , lastreplytime

    4: 论坛内容标 tb_bbs_content (此表可按照bbsid进行分表存储) 
    id,bbsid , content;

    5: 论坛回复表 tb_bbs_reply (此表可按照bbsid进行分表存储) 
    id , bbsid , userid , content , replytime , ip

    五.增加一个主题缓存表,取每个区的前面100条记录 
    引用
    1: 简单用户表 tb_user: 
    字段有:id , username

    2: 用户详细信息表 tb_userinfo 
    字段有:id,userid , email , homepage , phone , address ...

    3: 论坛主题表 tb_bbs 
    字段有:id , userid , title , ip , repleycount , replyuserid , createtime , lastreplytime

    4: 论坛内容标 tb_bbs_content (此表可按照bbsid进行分表存储) 
    字段有:id,bbsid , content;

    5: 论坛回复表 tb_bbs_reply (此表可按照bbsid进行分表存储) 
    字段有:id , bbsid , userid , content , replytime , ip

    6: 主题缓存表 tb_bbs_cache 
    字段有:id , userid , title , ip , repleycount , replyuserid , createtime , lastreplytime

    ------------------------------------------------------------------------------ 
    下面是针对上面的方案展开的讨论:

    1:方案一表面上看起来好像少查了一张表,但由于冗余,因为帖子数量极大,会占用大量的空间。这种数据量大,但是对实时和数据绝对安全性要求较低的应用,大量使用缓存的话可以极大提高处理能力。

    2:方案一你这么设计的话,索引怎么建比较好呢,还有就是会不会造成这个表过热,还有…… 我觉得像论坛这样的系统,使用缓存可以大大降低数据库的负载

    3:大家的意思是分成主题表、回复表等多个表? 还是合成一个表然后做物理分区? 哪种更好呢?

    4:再这么高插入更新的频率下 索引就有些不实用了,创建索引会降低插入更新的速度而且访问量这么大的情况下,索引不建议采用

    5:就这样的一个论坛,实时在更新、发帖、回帖。我觉得在数据库上建立索引不太好,但是如果不建立索引如何来提高查询等方面性能呢?

    6:都是分布式数据库了。放在多个表中,直接关联一点都没问题。重要是横向切分

    7:认同分表,分库,缓存的做法 
    引用
    问题分析: 
    每天论坛访问量300万左右,更新帖子10万左右。 
    1. 读写比例在30:1左右, 应向读取效率方面倾斜. 索引建立需参考常用读取的主关键字. 
    2. 每月数据在10W*30=300W. 可按月分表 
    3. 每年帖子在300W*12=3600W, 推算数据不会小于30T. 可按年分库

    结构: 
    用户信息:独立表,userid主键 
    发帖、回帖:按月表存储,帖子唯一ID主键,日期索引。 
    帖子内容明细:按月存储,帖子唯一ID主键

    8:拿一张500万的表来说事 
    引用
    更新的时候如果没有索引的话 
    更新时间大概需要30秒左右 指的是全表更新~~ 
    而查询某单行记录 却需要10秒左右~~

    而加入索引的话 
    更新时间差不多慢了一倍有余 
    而查询记录则缩减到毫秒级~~ 
    快了百倍有余~~

    孰重孰轻 自己选

    9:自己的一点经验: 
    引用
    1.分表存储; 
    2.建立索引;SQL按所以查询的速度还是很快的; 
    3.避免整表扫描;先读取主题,在按照主题ID读取回复;再按照用户ID读取用户;而不要使用关联; 
    4.使用缓存;

    10:需要分3张表,且建立索引。。。 
    理由如下: 
    引用
    1:建立3张表可以避免冗余数据,维护起来方便。。。 
    2:每天论坛访问量300万左右,可见主要的压力来自于查询,sql查询的效率在于避免全表扫描,可见建立索引是必须的。。。 
    3:关于创建索引会降低插入更新的速度这个问题是不存在的。。。 因为,索引之所以会降低更新的速度的速度,是因为在更新完对应字段后还需要更新对应字段的索引。 
    4:看到更新帖子10万左右,这句话是说,我们可能对发帖标题,发帖内容,回复标题,回复内容这4个字段做更新。。。需要注意的是,这四个字段并不是用来建立表连接的字段,为了优化查询速度我们不会在这四个字段上建立索引,所以从这道题目出发,我们建立的索引不会影响更新帖子的性能。。。

    所以,我认为最后的答案是建立3张表,在连接用到的字段上建立索引。。。

    11:
    引用
    兩個表然後建一個視圖是否可行呢?

    视图也是很慢的。

    12:每天就更新10万个帖子,每天访问那么多,肯定是不能把所有的主贴放在一个表里,大表分小表,建立常用字段的索引,然后配置缓存。级联关系最好不要配置,等需要的时候再查询。

    13:虽然题目中没有说明,但实际应用中,查阅帖子通常只会分页显示,而一页最多也就显示几十个帖子,那么实际上只要SQL语句构造得好,T_USER表其实只是跟一个只有几十行结果集的的子查询进行连接,应该基本不用担心出现性能问题。

    而且实际上,一个万行级的表简单关联百万行级的表(其实镇魂歌数量级在我看来其实也算不上很大的表),在数据库方面完全有很多优化方式,甚至可以通过提高硬件配置来改善性能,实在没有很大必要进行结构上的冗余。一旦结构有冗余,为了保证数据一致性,往往你还要消耗更多的资源,反而得不偿失。

    14:分表有垂直和水平分表 
    引用
    1:无论你拿多少记录(甚至是1条),如果两个大表关联都可能会产生非常大的中间值,如果你排序(排序字段没有用到索引),你都可能导致数据库采用各种各样的方式来计算。

    2:索引会导致插入、更新记录很慢,大家都是知道的。

    3:水平分表可以解决这个问题,只要你能保证每个表只存适合的记录数(例如100W一个表) (水平分区也可以解决IO的一些问题)

    4:还有就读写分离,master是写,slave是读 (再加上cache,一般问题都还好了)

    上面都是比较大的工作量,最好是保证你的数据库设计是合理的(范式是第一步,然后考虑反范式),基本上也能满足很多问题了。

    15:方案四 把内容与其它信息分开的好处就是可以让每个表的文件最小化,对数据库操作压力会减小,操作速度会快,还可以搭配缓存,把内容根据情况进行缓存,可以尽量很少访问表数据。 
    引用
    1:对于上述分表方式也可以适用于分库操作,这样就降低了数据库单库的压力,把压力分散到各个机器 
    2:我的做法就是尽量避免表关联 
    3:再就是对于sql语句尽量都保证索引有效,不能索引的sql,尽量采用能索引的高效方式解决

    16:外围的方案: 
    引用
    1 读方面,生成静态页,或者缓存最新最热的帖子。 
    2 写方面,估计主要是INSERT吧,这个可以异步操作的。所有的写贴操作放到一个队列然后批量执行插入数据库操作。

    17:方案四比较靠谱,再加上定期转储,海量的cache,大型论坛就此搞定。

    18:我觉得应该还是使用3张表比较合适。 
    引用
    1:业务上说,很可能主贴跟回复贴拥有不同的扩展,比如附件什么的,都放在一张表里面,假如主贴跟回复存在个性需求,怎么办?无限加字段么? 
    2:主贴跟回复在同一张表里,会增大锁表的几率。 
    3:索引的确会降低表更新的速度,但是带来的查询效率提升也是很可观的,因此我觉得,索引不能不用,但是要少用。 
    4:建立表时,确实可以通过楼上某位仁兄回复所言,用水平分表的方式,其实原理就是用先算再查嘛。 
    5:在前端表现上,可以使用ajax等方式,分步骤取数据,比如主贴的内容先取出来,然后再逐步加载回复信息等。

    19:提高速度的关键: 
    引用
    1.建立索引并在查询时充分利用; 
    2.避免使用关联,这样避免整表扫描;使用关联不如多次使用主键查询来的快; 
    3.一些处理的功能尽可能放到内存中来做,比如组织主题和回复; 
    4.使用静态页面也是个不错的做法;

    20:方案三是延续了hibernate二级缓存的思想, 对于经常更新的数据都设计成单独表,这样可以最大程度的利用hibernate缓存

    21:没有fast=true的设置,有人说or比in 好,exists比in 好,索引比全表扫描好,分区能提高查询效率,但是分区要降低插入效率 
    我要说的是,没有fast=true的选项, 如果能找到一步,或者几步公式化的方法能提高效率,那么优化器自己就会做了,根本不用用户担心。 
    假设 or比in好,数据库优化器把in语法和or语法走的执行计划一样就可以了,何必折磨用户呢。 
    说点实际的,很多人张嘴就说,SQL优化就是避免全表扫描,不知道大家有没有了解过索引查找的原理.索引查找数据,有两步要做,第一步是索引中快速查询,索引里只存储了对应表数据的rowid, 所以还有第二步,根据rowid去得到全部的数据, 所以需要一次磁盘i/o, 不要小看磁盘I/O,通过索引查询出的结果比较多的时候,磁盘i/o的时间是非常大的,这个时候比全表扫描慢得多, 实际上,oracle 10g基于成本的优化器(CBO),选择性不高的索引,优化器根本不会使用,而自动采用全表扫描的方式来做.

    22:这个量级的bbs我设计过,当时是这样做的(方案五): 
    引用
    共四个表: 
    1. 用户表 
    2. 主题表(包含最后回复信息,最后回复人,最后回复id等) 
    3. 回复表 
    4. 主题缓存表(这个取每个区的前面100条记录),一般来说负载最大的就是主题的第一页,所以缓存表是个小表。

    共3台app集群,1台web,2台oracle一主一备,运行下来速度还是可接受的。

    23:不建议进行表的设计冗余,感觉就想重复代码一样,有坏味道 
    引用
    1:缓存常用的页面和数据 
    2:读写表或库分开(基于垂直分隔) 
    3:数据库可以进行垂直分隔(字段分到多个表中),再进行水平分隔(数据分到多个表中) 
    4:论坛功能可以进行分隔,不同的服务器负责不同的功能,如图片服务器,web服务器,邮件服务器等

    总之,就是要细化分工

    24:支持方案三的设计 
    读取的操作: 
    引用
    1:显示帖子列表界面,如果主贴内容放在forum_topic表,那么这就是冗余的,假设都要获取100个帖子,一行的数据长度越大,数据库需要扫描的数据块就越多,性能也越差。 
    2:在打开一个帖子时,读操作通过索引关联到两张表(forum_topic和forum_topic_content)性能消耗对整个数据库来说不多。

    写帖子的操作: 
    引用
    发表帖子,对标题表和内容表分别作一个插入

    更新非索引列不会引起索引更新: 
    引用
    只要被索引的列(例如回复表的标题ID)不被频繁更新,即使索引所在地行的其它列被频繁update,索引也不会被更新从而产生性能消耗,一张表一天30万次的索引更新,因它引起的性能消耗小到即使数据库安装在奔腾3单核CPU下都能轻松承担下来, 为什么会有人对索引有这么大的误解呢?。对一个论坛(或者绝大部分的系统)来说,检索(SELECT)数据耗费的系统资源远远高于更新数据(INSERT/UPDATE)本身,而索引是专门为检索数据服务的,难道就为了节省更新数据的小小的性能消耗,付出检索100条数据时需要数据库扫描几千万上亿条数据进行数据匹配的代价?如果是这样的话,即使是有32核顶级CPU的数据库作并行查询都未必顶得住。

    做数据库设计,还是多了解数据库的原理才好。

    25:数据库切分是必须的。 
    引用
    1:垂直切分:用户表、用户信息表、主题表、主题内容表、回复表 
    2:水平切分:主题1、主题2、主题3、...、主题n 
    3:缓存:缓存路由表 
    4:再配合数据库读写分离和集群吧

    另:其实论坛修改标题、内容的概率是很小的。大部分都是新增


    二、以铁路的售票系统来说明分库分表对架构的影响。

     

     一、问题:铁路的售票系统的数据量是海量吗?
     
      不是。因为数据量不大,真不大。

      每一个车次与车次间是独立的,每车次不超过2000张票,一天发车不超过50万车次;
     以预售期15天来讲,15*0.1亿张不超过1.5亿笔的热线数据,称不上海量数据的。
     再加上可以按线路分库,更是不到千万级的单表容量。已经发车完成的进入归档分析。
     即数据库按路线使用不同的服务器,不同的车次放在不同的表中。并发量锁真不大。

     当然,如果不分库分表,再加上不归档处理,铁路的售票系统的数据量看起来是海量的;
    关键是这海量的数据没有意义。


    二、如何分库分表?

     2.1 分库,考虑数据间没有直接关系和服务器如何部署

      铁路的售票系统为例来说,按路线分库,再按车次分表是合理的。
      设路线有1万条,按每1000条需要两台服务器(一台热机沉余),不到20台服务器
      如果使用SAN存储,则使用SAN作为存储,本机作为热机沉余,只需要10台。
      当然使用mySQL这种经济型数据库,服务器需要更多来防灾;
      即可以采用双写或多写的方式来保证数据的绝对安全。

     2.2分表,考虑数据间不存在重叠,即数据满足二分原则

      铁路的售票系统的任意两个车次是没有关系的,所以可以分表。
      电信的某个用户的通话和其它用户的通话记录,也是没有关系,所以可以分表处理
      (实际上电信的系统,分库分表后也是不大的,难在后台的计费、结算等规则)

     

    三、数据库访问接口

     

      1. 元数据:如何识别到当前要处理的数量在哪张表?

        铁路的售票系统会有一个车次管理系统,例2012年2月12日 D3206 车次,
        按预先设计的在哪台服务器的哪个库,建哪个表。

      2.建立元数据的规则:即具体如何分库分表的规则

        这个就是数据库的访问接口。

      3.数据库访问接口的透明程度

      即哪个层知道哪些元数据信息。
     例,是否让窗口售票的客户端来解析元数据的规则然后缓存,还是通过中间件来解析缓存的

     具体各层使用怎样透明程度,和业务性质、节点和数据中心的拓扑等有关。

     

    四、历史数据归档与分析

      1.使用分库分表后,数据需要归档,分析处理的程序变得复杂,但使联机交易变得简单
      2.分析:要注意是针对热线数据分析、归档数据分析、混合分析有关,
       通过分库分表和归档,更方便使用分布式的统计方案。

      具体可以参考,淘宝的开放平台架构师写的文章

       Beatles小记-分布式数据流分析框架(一)     http://www.blogjava.net/cenwenchu/archive/2011/12/07/365776.html

     

     结论:分库分表跟不分库分表,整个架构是完全不一样的。

       像铁票的售票系统、淘宝、电信、银行等,绝对要采用分库分表的数据存储方案,

       来解决数据量的增长而不影响性能的问题。

       像淘宝等互联网应用还要解决带宽即CDN问题。


    三、如何构建千万用户级别后台数据库架构设计的思路


    关于如何构建千万级别用户的后台数据库架构话题,在ITPUB及CSDN论坛都有不少网友提问,新型问答网站知乎上也有人提问,并且顺带梳理了下思路,方便更多的技术朋友有章可循,整理一篇抛砖引玉性的文章。

      一、技术朋友给出的背景资料:

      (1). 网站型应用,主要指:SNS社交网站、新闻门户型网站、邮件系统、SNS Game社交游戏、电子商务网站、即时通信IM等类型系统;

      (2). 注册用户为千万级别,也即1KW注册用户以内;

      二、要求

      构建千万级别用户的后台数据库架构分析思路,对数据层架构设计的有章可循,必须考虑数据量的大小,以及数据库提供服务的性能和系统的可靠性,适当地考虑用户量超过,以及需要使用的服务器资源等信息。

      三、构建千万级别用户的后台数据库架构的分析思路

    曾经发过一篇文章,关于千万级别用户应用架构设计的歌谣,供大家参考 千万级架构设计诀窍,接下来我们针对如何构建千万级别用户的后台数据库架构,给出通用性分析思路的建议,未必完全靠谱,但求基本靠谱(注:毕竟很多事情需要看具体业务而定的):

      (1). 一定要区分业务类型,可能达到千万用户级别的应用业务场景,可归类描述为: SNS社交平台、SNS社交游戏、即时通信IM系统、电子商务、邮件系统、新闻门户网站等,这些不同类型的业务场景做法会不一样,主要是由他们业务性质决定,后续分析项中逐一描述;

      (2). 应用业务的核心KPI数值,产品每天的日活跃用户量大概多少?若是网站类型应用,还需要加入其他参数PV,UV等数据辅助决策,即时通信IM的消息量,邮件系统的新增邮件数,SNS社交平台的Feeds量等核心数据;

      (3). 系统中每个用户可能产生的数据量大概多大,分固定部分,以及动态部分的方式统计分析,对非固定部分以参考值和结合实践跨度(注释:1年为硬性指标,2年为预期,3年可选,再长的时间段不考虑)的方式进行分析,然后预测出整个系统的用户锁产生的数据条数和数据容量大概的估值;

      (4). 注册用户并不等于活跃用户,为此需要预估日活跃用户量大概多少?周活跃用户量大概?月活跃用户量大概多少?系统设计的最高并发量为多少?这数字还是非常有必要,不管是对数据量预估,还是对技术实现方案的选择都有帮助;

      (5). 根据应用业务的特点,以及系统不同模块的功能特点,初期必须判断出可能负载最大的系统模块,对于可以静态化模块或功能,尽量要Cache起来,以降低系统的负载和提高前端响应速度;若是非Cache技术能解决的,是否可以考虑独立或通过整体水平扩展方式解决系统的负载和性能问题;

      (6). 针对系统中各个模块的功能或业务特点,大致那些用户数据会累计比较大,以及那些数据操作频率比较高;

      (7). 不同的业务其对数据操纵不一样,要大致明白自己的应用:读写比如关系,也即:SELECT:UPDATE:DELETE:UPDATE=?;

      (8). 系统的整体架构中,必须考虑系统的稳定性、负载均衡和响应速度,为此必须考虑一些模块借助Cache、异步、消息队列等技术,进行一些特殊处理或折中做法,以达到目标;

      (9). 若使用MySQL数据库产品作为后台数据库提供数据服务,建议尽量使简洁的SQL语句,并不是说不用JOIN,而是要考虑MySQL对JOIN的实现算法,符合Nested loop join优缺点中的优点;

      (10). 数据库结构设计

      既然说是构建千万级别用户的后台数据库架构,前面讲清楚如何熟悉和理解业务模型,清楚系统可能存在的瓶颈、技术难度,以及一些技术实现方案,现在必须回归到数据库设计阶段。

      开始讨论如何收集、分析和设计数据库结构之前,我们先简单地对数据库,什么情况下要考虑进行水平拆分?尤其针对互联网行业流行的数据库产品MySQL来讨论,各大数据库技术论坛或个人博客型技术网站,有不少名言式的基调:数据量超过100W,就需要分表,MySQL无法支持大数据量的服务等等?

      以前的MySQL(主要指:MySQL5.0以前版本)版本确实存在诸多问题,以及当时跑MySQL的服务器一般都是超低配置,还依然记得当时我们的数据库服务器最高也就8G内存,一般都是2G内存,硬盘都是单盘且转速是10K,甚至7500转的,而当时大多主流数据库产品Oracle跑的服务器配置却很少这么差。我们大家要一时俱进,技术人员尤其DBA或架构师,要学会以数据说话,以其中一测试用例,DELL 2950 4*15K*146G RAID1+0 ,16G内存,E5410 CPU*2 ,一张分31个分区的区表业务模型只有INSERT+SELECT,且以50个INSERT+10个SELECT线程并发执行,总记录写入数据量6KW行左右,单表数据容量超过100G,并发从最高的9100 TPS/S下降到8700 TPS/S之后就稳定在此值

      上面论述MySQL支持大表并没有太大的性能方面的下降,但是并不表示笔者建议大家这么做,至少有二点MySQL的备份和数据库结构变更非常麻烦,尤其数据库结构变更其特殊的做法,使其成为一大瓶颈,也不知道要牺牲我们多少DBA的睡眠,但是对于新闻内容的存储需求,可以把新闻主题内容单独放到一张表中,以子表的方式存在,提供数据服务器,且该数据很少做变更,一般情况下是INSERT之后就只有读为主,那么就不会是任何问题,阿里巴巴旺旺弹出的新闻页面的内容就是采用此方式存储,当时单表容量接近1T,早高峰的时候也不会出现服务器性能问题,以及系统负载都非常稳定。

      我们继续回到构建千万级别用户的后台数据库架构的话题上,具体建议或做法如下所示:

      10.1> 数据库的设计开始之前,必须优先进行业务的数据流梳理(注释:必须尽量考虑应用所有可能的功能模块),以及对业务优先进行优化和规划,然后根据数据流和功能 考虑数据库的结构设计和优化;

      10.2> 千万级别用户量,若是非游戏行业的产品(SNS游戏除外),建议考虑用户数据拆分架构设计,以及考虑后续未来1-2年的承受量,若是SNS平台必须考虑拆分,除非考虑上SSD、Fusion-io、存储等更高端的设备,用金钱换时间的方式支持技术改造;

      10.3> 数据拆分的核心与难处:同一个用户的数据尽量放一起(拆分规则要尽量简单可执行),拆分之后用户关系的数据如何保存的抉择有多种(存2份或存1份放一个地方),难处数据的分页,统计合并等;

      10.4> 要考虑一些冗余的方式解决SQL性能问题,但是又不能过多引入冗余而造成IO开销增加太多,冗余字段要尽量整型字段;

      10.5> 数据库表对象的字段属性,要尽量考虑数字化,尤其游戏行业;

      10.6> 数据库设计过程中,对于索引组织结构要偏向共同操作最优先,其次应用外部用户级别的操作性能优先,最后内部用户的操作,硬尽量隔离,例如:搜索引擎Build操作、内部编辑团队审核等操作;

      10.7> 数据库要从设计角度规避一些无法通过其他技术手段解决的模糊查询,类似全文索引的模糊查询,要走搜索引擎的模式,再通过数据库读具体的数据,一些必要的计数类型的数据,适当地考虑缓存;

      10.8> 重点解决数据库级别的数据分页问题,要学会从前端应用用户的体验不降低的情况下,达到更高效的数据分页做法,类如论坛中帖子分楼的做法;

      10.9> 数据库的设计必须考虑使用什么类型配置的物理服务器,核心参数:内存、CPU、硬盘(这个是关键:硬盘类型(注: SATA、SAS、SSD)、多少块盘、转速、容量,以及做RAID几),RAID卡内存及RAID写模式也需要考虑进去,必须结合数据量和读写能力要求进行一个预算规划,不一定超准确,但是要八九不离十;

      【结束】

      主要是想通过回答网友的提问“如何构建千万用户级别后台数据库”,把对于此类用户级别通用性分析和设计的思路描述清楚,实在不善于文字描述,可能很多地方没有讲述到位,还请各位一起补充完毕和纠正。



    展开全文
  • JSP课程设计购物网站

    2020-05-31 23:31:39
    项目为 类似淘宝网的购物站,它只为大学 生服务, 拥有用户前台和管理员平台。 本项目展现了个人的自学能力。 具体功能有:用户注册与 登陆 (信息保存在数据库中,并 信息保存在数据库中,并 信息保存在数据库中,...
  • Assignment 2 - Monash Library Services (MonLib) Case StudyDue Date: Wednesday 10th October 2018 5PM (Week 11)The local Monash Municipality maintains several libraries for its residents across the muni...
  • 最近学数据库数据库学得飞起,学数据库当然少不了学sql,老师讲得很多很系统,但是我相信很多初学者不可能在短短40分钟之内把所有老师将的课程都吸收,个人认为,数据库的精髓在于多实践,多练习,把老师所讲的内容...
  • 数据库设计1. 多表之间的关系2. 数据库设计的范式3.数据库的备份和还原 第一章 DQL查询语句 1. 排序 eg:select * from student order by math ASC; 单列排序: 只按某一个字段进行排序,单列排序...
  • 版权声明:对于本博客所有原创文章,允许个人、教育和非商业目的使用,但务必保证文章的完整性且不作...一、 概要设计 2.1  意义  伴随着信息化的发展,计算机走进了千家万户,人们的交流变得方便,方便了网民的
  • 这个课程设计,花了自己几乎三个星期的时间才完成,因为其他的时间都是满课,只能借助不多的时间才能开始着手弄,进度不是一般的慢,也了解到了一些差距。别人能自己研究出来,而我暑假接触过了,却没有别人那样...
  • 杭州湖畔网络技术有限公司是一家专业提供SaaS化电商ERP服务的创业公司,主要用户群体为经营淘宝、天猫、京东等主流电商平台、自建商城、线下渠道的商家及中小企业。作为SaaS服务提供商,服务数万乃至数十万级用户是...
  • 在了解数据库设计的基本流程、规范以后,为了学以致用,本节将以电子商务网站为例,演示如何进行数据库设计。通过实际操作,加深对数据库设计的理解。 1、需求分析 电子商务网站是一种在互联网上开展电子商务的平台...
  • 文章目录二、数据库设计与前端框架1、多租户SaaS平台的数据库方案<1>、多租户概述<2>、需求分析<3>、多租户的数据库方案分析(1)、独立数据库(2)、共享数据库、独立Schema(3)、共享数据库、共享...
  • 下载就可用!!下载就可用!!下载就可用!!下载就可用!!
  • python课程设计爬虫篇

    2019-08-10 13:45:07
    最近发现一个秘密,用python可以完成批量的诸如课程设计、学年设计、毕业论文模版,即从数据采集,数据分析,数据可视化,GUI界面等不同模块组合。下面介绍我自己写的一个简单例子。 一 摘要 定向网络爬虫可以...
  • 数据库设计(1)转载

    2018-12-20 16:15:44
    前言 这是关于 MySQL 系列文章的第三篇,在前两篇文章 《MySQL —— 数据库基础》 和 《MySQL —— SQL 语句总结》 中,主要介绍了一些数据库的基础概念、创建表的方式...来简单聊一聊数据库设计,还有一句话不得...
1 2 3 4 5 ... 20
收藏数 3,927
精华内容 1,570