精华内容
下载资源
问答
  • 在传统的关系数据库设计中,每一行和每一行都是列必须只存储一个值.不要存储以逗号分隔的列表或任何古怪的东西.例如,运动队有七名成员.你可以这样做:CREATE TABLE team (team_id INT PRIMARY KEY,team_name VARCHAR...

    在传统的关系数据库设计中,每一行和每一行都是列必须只存储一个值.

    不要存储以逗号分隔的列表或任何古怪的东西.

    例如,运动队有七名成员.你可以这样做:

    CREATE TABLE team (

    team_id INT PRIMARY KEY,team_name VARCHAR(50),team_members VARCHAR(200)

    );

    INSERT INTO team VALUES (1,'Dwarfs','Sleepy,Dopey,Sneezy,Happy,Grumpy,Doc,Bashful')

    但最好这样做:

    CREATE TABLE team (

    team_id INT PRIMARY KEY,);

    INSERT INTO team (team_name) VALUES ('Dwarfs');

    CREATE TABLE team_members (

    team_id INT,member_name VARCHAR(20),FOREIGN KEY (team_id) REFERENCES team(team_id)

    );

    INSERT INTO team_members VALUES

    (LAST_INSERT_ID(),'Sleepy'),(LAST_INSERT_ID(),'Dopey'),'Sneezy'),'Happy'),'Grumpy'),'Doc'),'Bashful');

    nb:LAST_INSERT_ID()是一个MySQL函数.其他品牌的数据库也提供类似的解决方案.

    展开全文
  • 这些系统共同的特点, 都是在主体内容上会携带属性, 并且属性需要随时能调整, 并且要求能兼容旧属性, 还需要频繁的通过属性组合进行检索. 对于属性的管理, 可以参考58同城的这个解决方案 https://mp.we...

    主要是以下几类系统:

    1. 生活信息系统, 内容:小, 属性:大,
    2. 电商商品系统, 内容:大, 属性:大,
    3. 风控征信系统, 内容:小, 属性:大,
    4. 新闻系统, 内容:大, 属性:小,

    这些系统共同的特点, 都是在主体内容上会携带多个属性, 并且属性需要随时能调整, 并且要求能兼容旧属性, 还需要频繁的通过属性组合进行检索.

    对于属性的管理, 可以参考58同城的这个解决方案

    https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959855&idx=1&sn=f33abe8ec598c273f29cebb9365ece59

    其方式, 就是通过JSON化的字段, 来避免使用纵表, 这样可以i减轻开发的工作量, 以及实际运行时的数据量. 在这个之上, 使用一些手段压缩了JSON字段的尺寸, 以及通过属性, 属性分组, 属性枚举等结构增加了数据的灵活性. 在搜索上, 58是使用自研的系统, 这个在普通应用中可以使用elastic search代替.

    另外关于电商SKU的数据设计, 也可以采用上面的方式避免纵表带来的巨大记录数量. SKU一般是按 产品分类, 产品, 产品SKU, 属性分组, 属性, 属性枚举 这样的结构来实现业务数据管理的.

    可以参考这篇里面的说明 http://www.cnblogs.com/winstonyan/archive/2012/01/07/b2c_research_product_sku_analyse_design2.html

     

    携程在Elastic Search上的介绍

    【携程旅行网 吴晓刚】
    ElasticSearch目前在互联网公司主要用于两种应用场景,其一是用于构建业务的搜索功能模块且多是垂直领域的搜索,数据量级一般在千万至数十亿这个级别;其二用于大规模数据的实时OLAP,经典的如ELKStack,数据规模可能达到千亿或更多。 这两种场景的数据索引和应用访问模式上差异较大,在硬件选型和集群优化方面侧重点也会有所不同。一般来说后一种场景属于大数据范畴,数据量级和集群规模更大,在管理方面也更有挑战。

    应Medcl大大的邀请,为ES中文社区做今年的Advent开篇,分享一下我在管理自家公司用于日志分析的ES集群方面的一点心得,蜻蜓点水,泛泛而谈,希望大方向上能对大家提供一些帮助。

    这里的自家,即是携程旅行网。从2013年开始接触ES,我们团队先后实践过0.9.x -> 5.0.0中间各个版本,从最初只用于运维内部IIS日志的分析,到如今支持IT、呼叫中心、安全、测试、业务研发等多个部门超过200种日志型数据的实时检索与分析。 一路走来,愉悦了大家,也死磕了自己。

    目前我们最大的日志单集群有120个data node,运行于70台物理服务器上。数据规模如下:
    单日索引数据条数600亿,新增索引文件25TB (含一个复制片则为50TB)
    业务高峰期峰值索引速率维持在百万条/秒
    历史数据保留时长根据业务需求制定,从10天 - 90天不等
    集群共3441个索引、17000个分片、数据总量约9300亿, 磁盘总消耗1PB
    Kibana用户600多人, 每日来自Kibana和第三方的API调用共63万次
    查询响应时间百分位 75%:0.160s 90%:1.640s 95%:6.691s 99%:14.0039s

    运维这样大规模的ES集群,有哪些值得注意的地方?

    一. 必不可少的工具
    工欲善其事必先利其器,从一开始,哪怕就只有几个node,就应该使用分布式配置管理工具来做集群的部署。随着应用的成熟,集群规模的逐步扩大,效率的提升会凸显。 官方提供了ES Puppet Module和Chef Cookbook,熟悉这两个工具的同学可以直接拿过来用。 我们自己则是采用的Ansible,编写了一套Playbook来达到类似的效果。 用熟这类工具,对于集群的初始部署,配置批量更改,集群版本升级,重启故障结点都会快捷和安全许多。
    第二个必备利器就是sense插件。通过这个插件直接调用集群的restful API,在做集群和索引的状态查看,索引配置更改的时候非常方便。语法提示和自动补全功能更是实用,减少了翻看文档的频率。在Kibana5里面,sense已经成为一个内置的控制台,无需额外安装。

    二. 硬件配置
    我们采用的是32vcoreCPU + 128GB RAM的服务器,磁盘配置大部分服务器是12块4TB SATA机械磁盘做的Raid0,少部分机器是刚上了不久的6块800GB SSD raid0,主要目的是想做冷热数据分离,后面谈到集群架构的时候,再进一步解释一下如何利用硬件资源。

    三. 集群的管理
    首先很有必要对ES的结点做角色划分和隔离。大家知道ES的data node除了放数据以外,也可以兼任master和client的角色,多数同学会将这些角色混入到data node。然而对于一个规模较大,用户较多的集群,master和client在一些极端使用情况下可能会有性能瓶颈甚至内存溢出,从而使得共存的data node故障。data node的故障恢复涉及到数据的迁移,对集群资源有一定消耗,容易造成数据写入延迟或者查询减慢。如果将master和client独立出来,一旦出现问题,重启后几乎是瞬间就恢复的,对用户几乎没有任何影响。另外将这些角色独立出来的以后,也将对应的计算资源消耗从data node剥离出来,更容易掌握data node资源消耗与写入量和查询量之间的联系,便于做容量管理和规划。
    避免过高的并发,包括控制shard数量和threadpool的数量。在写入量和查询性能能够满足的前提下,为索引分配尽量少的分片。分片过多会带来诸多负面影响,例如:每次查询后需要汇总排序的数据更多;过多的并发带来的线程切换造成过多的CPU损耗;索引的删除和配置更新更慢Issue#18776; 过多的shard也带来更多小的segment,而过多的小segment会带来非常显著的heap内存消耗,特别是如果查询线程配置得很多的情况下。 配置过大的threadpool更是会产生很多诡异的性能问题Issue#18161里所描述的问题就是我们所经历过的。 默认的Theadpool大小一般来说工作得很不错了。
    冷热数据最好做分离。对于日志型应用来说,一般是每天建立一个新索引,当天的热索引在写入的同时也会有较多的查询。如果上面还存有比较长时间之前的冷数据,那么当用户做大跨度的历史数据查询的时候,过多的磁盘IO和CPU消耗很容易拖慢写入,造成数据的延迟。所以我们用了一部分机器来做冷数据的存储,利用ES可以给结点配置自定义属性的功能,为冷结点加上"boxtype":"weak"的标识,每晚通过维护脚本更新冷数据的索引路由设置index.routing.allocation.{require|include|exclude},让数据自动向冷结点迁移。 冷数据的特性是不再写入,用户查的频率较低,但量级可能很大。比如我们有个索引每天2TB,并且用户要求保持过去90天数据随时可查。保持这么大量的索引为open状态,并非只消耗磁盘空间。ES为了快速访问磁盘上的索引文件,需要在内存里驻留一些数据(索引文件的索引),也就是所谓的segment memory。稍微熟悉ES的同学知道,JVM heap分配不能超过32GB,对于我们128GB RAM, 48TB磁盘空间的机器而言,如果只跑一个ES实例,只能利用到32GB不到的heap,当heap快用饱和的时候,磁盘上保存的索引文件还不到10TB,这样显然是不经济的。 因此我们决定在冷结点上跑3个ES实例,每个分配31GB heap空间,从而可以在一台物理服务器上存储30多TB的索引数据并保持open状态,供用户随时搜索。 实际使用下来,由于冷数据搜索频率不高,也没有写入,即时只剩余35GB内存给os做文件系统缓存,查询性能还是可以满足需求的。
    不同数据量级的shard最好隔离到不同组别的结点。 大家知道ES会自己平衡shard在集群的分布,这个自动平衡的逻辑主要考量三个因素。其一同一索引下的shard尽量分散到不同的结点;其二每个结点上的shard数量尽量接近;其三结点的磁盘有足够的剩余空间。这个策略只能保证shard数量分布均匀,而并不能保证数据大小分布均匀。 实际应用中,我们有200多种索引,数据量级差别很大,大的一天几个TB,小的一个月才几个GB,并且每种类型的数据保留时长又千差万别。抛出的问题,就是如何能比较平衡并充分的利用所有节点的资源。 针对这个问题,我们还是通过对结点添加属性标签来做分组,结合index routing控制的方式来做一些精细化的控制。尽量让不同量级的数据使用不同组别的结点,使得每个组内结点上的数据量比较容易自动平衡。
    定期做索引的force merge,并且最好是每个shard merge成一个segment。前面提到过,heap消耗与segment数量也有关系,force merge可以显著降低这种消耗。 如果merge成一个segment还有一个好处,就是对于terms aggregation,搜索时无需构造Global Ordinals,可以提升聚合速度。

    四. 版本选择
    我们在2.4版本上稳定跑了很长时间,比较保守的同学可以上2.4,激进有精力折腾的可以考虑最新的5.0。 我们集群两周前从v2.4.0升级到了v5.0.0这个版本,除了升级第一周遇到一个不稳定的问题以外,感觉新版本带来的以下特性还是非常值得去升级的:
    结点启动的Bootstrap过程加入了很多关键系统参数设置的核验,比如Max File Descriptors, Memory Lock, Virtual Memory设置等等,如果设置不正确会拒绝启动并抛出异常。 与其带着错误的系统参数启动,并在日后造成性能问题,不如启动失败告知用户问题,是个很好的设计!
    索引性能提升。升级后在同样索引速率下,我们看到cpu消耗下降非常明显,除了对索引速率提升有帮助,也会一定程度提升搜索速率。
    新的数值型数据结构,存储空间更小,Range和地理位置计算更快速
    Instant Aggregation对于类似now-7d to now这样的范围查询聚合能够做cache了,实际使用下来,效果明显,用户在Kibana上跑个过去一周数据的聚合,头2次刷新慢点,之后有cache了几乎就瞬间刷出!
    更多的保护措施保证集群的稳定,比如对一次搜索hit的shard数量做了限制,增强了circuit breaker的特性,更好的防护集群资源被坏查询耗尽。

    升级第一周,我们的冷数据结点出现间歇性不响应问题,从而刨出3个issue提交给官方:
    Issue#21595 Issue#21612 Issue#21611
    第一个问题确认为Bug,将在5.0.2修复,其他两个目前还不清楚根源,看起来也只在我们的应用场景里遇到了。所幸问题都找到了了规避措施,实施这些措施以后,最近一周我们的集群重新回到以前2.4版本时期的稳定状态。


    五. 监控
    不差钱没空折腾的建议还是买官方的xpack省心,有精力折腾的,利用ES各种丰富的stats api,用自己熟悉的监控工具采集数据,可视化出来就好了。 那么多监控指标,最最关键的还是以下几类:
    各类Thread pool的使用情况,active/queue/reject可视化出来。 判断集群是否有性能瓶颈了,看看业务高峰期各类queue是不是很高,reject是不是经常发生,基本可以做到心里有数。
    JVM的heap used%以及old GC的频率,如果old GC频率很高,并且多次GC过后heap used%几乎下不来,说明heap压力太大,要考虑扩容了。(也有可能是有问题的查询或者聚合造成的,需要结合用户访问记录来判断)。
    Segment memory大小和Segment的数量。节点上存放的索引较多的时候,这两个指标就值得关注,要知道segment memory是常驻heap不会被GC回收的,因此当heap压力太大的时候,可以结合这个指标判断是否是因为节点上存放的数据过多,需要扩容。Segement的数量也是比较关键的,如果小的segment非常多,比如有几千,即使segment memory本身不多,但是在搜索线程很多的情况下,依然会吃掉相当多的heap,原因是lucene为每个segment会在thread local里记录状态信息,这块的heap内存开销和(segment数量* thread数量)相关。
    很有必要记录用户的访问记录。我们只开放了http api给用户,前置了一个nginx做http代理,将用户第三方api的访问记录通过access log全部记录下来。通过分析访问记录,可以在集群出现性能问题时,快速找到问题根源,对于问题排查和性能优化都很有帮助。

    最后就是多上手实践,遇到问题多查官方资料,多Google看是否有其他人遇到同类问题,精力充足有编程背景的同学也可以多刨刨源码。

    展开全文
  • 逻辑数据库设计 - 属性(列转行) 假设有一个要开发一个试题系统,全是不定项选择题。一道题可能有2,3,4...个答案,数据应如何设计呢?本处旨在说明问题所在,例如同类问题还有存储电话,一个人可能...

      假设有一个要开发一个试题系统,全是不定项选择题。一道题可能有2,3,4...个答案,数据应如何设计呢?本处旨在说明问题所在,例如同类问题还有存储电话,一个人可能有多个号码等等。

    一、存储多值属性

      反模式:创建多个列。

      我们知道每列最好只存储一个值,因此先看如下设计:

      CREATE TABLE Question(
          QuestionId int   PK,
          QuestionBody nvarchar(500),
          Answer1  nvarchar(500),
          Answer2  nvarchar(500),
          Answer3  nvarchar(500),
          Answer4  nvarchar(500)
      )

      类似上面的设计,假设答案只有两个,那么后面的两个Answer3,Answer4就为NULL,展示表如下:

      

      这是很传统的属性设计,导致即使现在很简单的任务也变得很简单了!

      1、查询数据

      假设有一道题的答案出错了,但是你只记得答案。

    SELECT * FROM Question WHERE 
        Answer1 = '猪有4条腿'
        OR Answer2 = '猪有4条腿'
        OR Answer3 = '猪有4条腿'
        OR Answer4 = '猪有4条腿'

      显示结果如下:

      

      假如,另外也有一道题目,也有一个答案猪有3条腿,那么你要查得这条数据,就需要这样:

    SELECT * FROM Question WHERE 
        (Answer1 = '猪有4条腿' OR Answer2 = '猪有4条腿' OR Answer3 = '猪有4条腿' OR Answer4 = '猪有4条腿')
        AND
        (Answer1 = '猪有3条腿' OR Answer2 = '猪有3条腿' OR Answer3 = '猪有3条腿' OR Answer4 = '猪有3条腿')

      怎么样又长又臭吧!不怕,哥安慰下你,其实有简单点的方法:

      SELECT * FROM Question WHERE 
          '猪有3条腿' IN (Answer1,Answer2,Answer3,Answer4)
          AND
          '猪有4条腿' IN (Answer1,Answer2,Answer3,Answer4)

      怎么样短了很多吧,相信你也还是不会卖帐。

      2、添加、更新以及删除值

      在以上设计中,假设我要删除答案'猪有3条腿'的SQL语句怎么写呢?大家被考到了吧。

      UPDATE Question
          SET Answer1 = NULLIF(Answer1,'猪有3条腿'),
              Answer2 = NULLIF(Answer2,'猪有3条腿'),
              Answer3 = NULLIF(Answer3,'猪有3条腿'),
              Answer4 = NULLIF(Answer4,'猪有5条腿')
      WHERE QuestionId = 2

      添加一个答案,更新一个答案都有点难写, 有兴趣的朋友可以自己敲敲。

      3、确保唯一性

      如何确保同一个值不出现在多个列中,即有可能你并不想一道题中有两个答案是一样的。我们很难阻止重复的答案出现,因为Unique只能用于行。

      4、值在不断增长

      当我们有一道题有5个答案的时候,悲剧,你要更改表结构了。

      以上种种问题说明,以上设计根本不堪一击。

    二、解决方案 - 从属表

      问题的根源在于:存储一个具有多个值的属性。对于以上设计,如果每道题都规定是4个答案,以上设计是可以用的。问题在于,答案个数不确定。

      因此,我们需要创建一个从属表,将不确定个数的值提取出来作为行存储,而不是列。

      总体设计如下:

    CREATE TABLE Question(
        QuestionId    int    PK,
        QuestionBody Body nvarchar(500)
    )
    
    CREATE TABLE Answer(
        AnswerId    int    PK,
        AnswerBody    nvarchar(500),
        QuestionId    int,
        FOREIGN KEY(QuestionId) REFERENCES Question(QuestionId)
    )

      对于以上设计,多少个答案都没问题了,而且增删查改都简单了不止一个档次。

      其实,只不过是一个一对多的关系。而且这个问题很容易一眼就看出,不过变种问题你却未必会条件反应式地提出一样从属表。

     

    posted on 2013-09-05 22:40 逆心 阅读(...) 评论(...) 编辑 收藏

    转载于:https://www.cnblogs.com/kissdodog/p/3304523.html

    展开全文
  • 我的问题就是 to B项目中因为每家客户需求不一样,需要个自定义字段,这时数据库结构如何设计或者如何实现查询速度等性能方面会好一点? https://ningyu1.github.io/site/post/108-custom-field/ 我目前了解到...
  • 中小型商城系统中的分类/产品属性/扩展属性数据库设计  正文:  之前发表过一篇"商城系统中【商品扩展属性】的表单生成及客户端验证",部分童鞋对于后台数据库的设计比较感兴趣,于是今天把这部分也补上...

    中小型商城系统中的分类/产品属性/扩展属性的数据库设计

          正文:

          之前发表过一篇"商城系统中【商品扩展属性】的表单生成及客户端验证",部分童鞋对于后台数据库的设计比较感兴趣,于是今天把这部分也补上。

          一、产品分类设计
          越来越多的商城系统都热衷于选择“无限级分类”的设计,我也不例外,因为它方便扩展。这部分就不详细展开了,详见

          无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(1)表结构 
          无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(2)插入记录

          无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(3)删除记录

          无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(4)显示记录

          稍微啰唆几句:
          1.1 我习惯于把所有表加上前缀T_(即Table的第一个字母),把所有字段加上前缀F_(即Field的第一个字母),把视图加上前缀V_(即View的第一个字母)...,这样最大的好处是在写linq to Sql时,智能提示(感应)会很爽,所有相关的东西全部列在一起,省去记忆脑细胞无数

          1.2 这是我06年时从动网论坛剥下来的表结构(当然我又做了一些改造),它的最大特点在于:通过添加一些辅助字段完全避开了无限分类中的递归问题。
          1.3 之所以用guid做为主键,主要是考虑到数据库迁移时的方便,当然guid主键会带来一些性能上的损失(二害相权,取其轻也),为了改进可将guid改为comb主键(详见 “Integer GUID和Comb做主键的效率测试”)
          1.4 考虑到很多人为了各种原因喜欢把分类页生成静态页,而为了url地址的简短/友好,guid主键有些长,所以我又添加了一个辅助字段F_AutoId,这样生成的静态页可以从类似"43A6162C-308A-4112-86F8-6E6B6B76FC6E.html"变成"1234.html"

          二、产品分类--产品关联设计

          如果“产品:分类”是1:1关系(即:一个产品只能归到一个分类下),则直接在产品表(T_Product)中增加一个字段(F_ClsId)即可。
          如果“产品:分类”是1:N关系(即:一个产品能同时归到多个分类下),则必须新建一个表(T_ProductInClass),建二个字段(F_ClsId,F_ProductId),并把这二个字段设置为联合主键即可。

          注:1:1还是1:N取决于需求,没有谁好谁坏之说。

          三、扩展属性
          终于到了正题了,对于产品的扩展属性,因为(在产品分类未选择之前)无法事先确定产品的扩展属性有哪些,所以这部分属性显然不适合通过在T_Product中预留一大堆字段来解决(而且这样性能也不好)

          考虑到扩展属性总是基于分类的(比如:电脑类的产品应该具有"CPU频率、内存容量、显示器尺寸、硬盘大小"等扩展属性,而服装类产品应该具有“颜色、尺码、品牌、面料”等扩展属性),所以可以新建一个"分类扩展表"用于存储分类的扩展属性基础定义。

          1

          2

          3

          4

          5

          6

          7

          8

          9

          10

          11

          12

          13

          14

          15

          16

          17

          18

          CREATE TABLE [dbo].[T_ClassAppend](

          [F_Id] [uniqueidentifier] NOT NULL,--主键

          [F_ClsId] [uniqueidentifier] NOT NULL,--关联外键(分类ID)

          [F_DisplayName] [nvarchar](50) NOT NULL,--(扩展属性)显示名称(比如:品牌,内存大小,颜色之类)

          [F_FieldName] [nvarchar](50) NOT NULL,--(扩展属性)字段名称(比如:Brand,Memoery,Color之类)

          [F_ValueType] [nvarchar](50) NOT NULL,--字段类型(比如:字符串,整数,日期之类)

          [F_ValueLength] [int] NOT NULL,--属性值长度(比如:50)

          [F_InputType] [nvarchar](50) NOT NULL,--输入类型(比如:文本框,文本域,下拉框,复选框之类)

          [F_DefaultValue] [nvarchar](500) NOT NULL,--默认值

          [F_IsRequired] [tinyint] NOT NULL,--是否必须填写

          [F_isShow] [tinyint] NOT NULL,--显否显示

          [F_Orders] [int] NOT NULL,--排序号

          [F_AutoId] [int] IDENTITY(1,1) NOT NULL,--辅助自动ID(预留)

          CONSTRAINT [PK_T_ClassAppend] PRIMARY KEY CLUSTERED

          (

          [F_Id] ASC

          )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

          ) ON [PRIMARY]

          保存数据后的大概样子如下:

          解决了扩展属性的定义问题,接下来再来考虑产品的这些属性到底应该保存在哪里?

          先回顾一下产品上传的基本逻辑,在不考虑扩展属性的传统场景下:用户进入产品发布页面,选择产品分类,然后填写其它产品属性,最终保存到数据库。

          分析一下:只要用户选择了分类,我们就能得到分类ID,进而得到该类的“扩展属性定义”,也就知道了该产品应该具有哪些扩展属性,如果这时有一张特定的产品扩展属性表(来对应这些扩展属性的定义),那么直接把这些扩展属性insert进这张表即可。

          技术处理:保存产品时,先得到用户选择的分类ID,然后根据上面定义的T_ClassAppend分类扩展属性定义表,去检查是否已经生成了该类的"产品扩展属性表",如果没有则动态生成一个类似T_Product_XXXX的表(注:XXXX即为分类的F_AutoId值),然后该干嘛干嘛,常规公共属性保存进T_Product,扩展属性保存进T_Product_XXXX。

          注:T_Product 与 T_Product_XXXX 之间应该有(产品ID的)主外键关联

          如下图:

          注:图中的F_ID其实可去掉,没啥实际意义(最初我是打算用来保存记录流水号的,后来发现完全没啥必要,这一点精减工作就留给大家去做吧)

          可能存在的问题:
          1、产品修改时,如果从分类A改成了分类B(即发布时,产品分类选错了,在编辑时,重新指定产品分类),那么扩展属性对应的保存表名,可能会从T_Product_123 变成 T_Product_456,这时要注意先把原来的T_Product_123中的记录删除,不然时间长了,会留下一堆冗余记录。

          2、分类扩展属性有变动时,比如电脑类,又新增了一项属性:"是否支持双显卡(F_IsDoubleVGA)",那么这里原来的产品扩展属性表T_Product_123,也要相应的增加一个类似F_IsDoubleVGA的字段类型

          四、搜索问题的解决

          这部分其实是最难解决的,不过也不是没有办法,分二种情况:

          1、第一种情况

          通常情况下,用户其实很少一上来就搜索扩展属性,很多场合下,用户会先浏览某感兴趣分类列表,然后再挑选商品。

          这种情况就好办多了,因为用户一旦确定了分类,也就意味着能得到分类ID,进而得到最终的产品扩展属性表名(比如T_Product_123),这时搜索就简化为搜索 T_Product 与 某一个特定的T_Product_XXX

          2、第二种情况

          如果系统要求用户直接在搜索栏里输入 "ThinkPad 双显卡 4G内存 T系列"这类关键词,然后自动匹配出所有属性/扩展属性的商品。

          那么,其实这已经演化成网站搜索引擎的需求,传统的 select... like ...在这种需求下基本上已经没啥用处了,可以考虑用lucene或园子里"盘古分词"作者eaglet (注:该兄是搜索引擎方面的资深人士,值得信赖!)的解决方案来实现站内搜索引擎。

          后记:早上写得匆忙,有些细节没写详细,现在补上。

          看到回复中有些朋友觉得这种设计表太多,太复杂,这个嘛...其实我觉得还好啦,只是在原来的分类表T_Class的基础上,增加了一个T_ClassAppend表而已,至于那一堆的T_Product_XXX 表,我写了一个分类管理器来自动生成,不用手动来做,另外再封装一些逻辑(比如根据分类ID自动返回扩展属性表名之类),对于普通开发人员来讲,开发难度只是轻微增加而已.

          上图为分类管理器的界面。

          继续更新:

          看到越来越多的人回复来讨论这个问题,很是高兴(相互交流才能进步),特地又加了一张图,建议大家先在完全明白我的意图之后,再讨论如何改进,不然大家你用你的说法,我用我的说法,但其实完全有可能只是同一个问题的不同说法,这样沟通起来比较费劲。

          其实我的想法很简单:

          将商品的共用属性(比如:价格,商品名称,商品编码这些肯定要有的东西)与扩展属性(即:根据分类不同而不同的非共用属性,比如电脑类的"CPU/硬盘容量...",充值卡类的"面值/适用地区...",服装类的"尺码/颜色...")分开保存而已,相当于原本应该保存一张表T_Product中的东西,拆分成二部分。(但由于每个分类的特性不同,每个类对应的产品都有不同的扩展属性值,所以不适合把所有产品的扩展属性保存在同一张表中,如果这样可以的话,干嘛还要费心把共用属性与扩展属性分开,搞拆分这么麻烦?)

          共用属性保存到 T_Product表,而扩展属性保存到 T_Product_N 表(其中N 与 某一个分类记录的ID对应)

          在逻辑上可以认为,每条产品记录的全部属性,都是 "T_Product中的一条记录" + "T_Product_N(N为该产品所属分类的对应Id)中的一条记录" 共同描述的

          至于表的个数,其实只增加了“ 1 + N ” 张表(1即多出来的分类扩展属性定义表,而N即为T_Class中产品分类的记录条数),所以只要分类数不变,产品扩展表的数量也是一定的。

          至于大家提到的品牌问题,品牌其实最适合与分类当作平行处理,毕竟很多时候搞活动做专题,都是以品牌作为主要选择依据的(如果当成扩展属性处理,并非行不通,只是不适合)

          另外之所以会引出搜索问题,其实反过来考虑很容易理解,如果不做拆分,按传统方式,用Sql语句直接搜索T_Product表即可。但现在把属性分成了二部分,所以很难确定用户搜索时,是想搜索共用属性,还是扩展属性?所以有上面提到搜索的二种情况。

          最后谈一下数据库查询的问题,看到'小菁菁'同学的观点:数据库就是为了查询方便。基本上,这个没错!但是如果您遇到过单表千万级数量的情况,而且基于某些要求又无法利用索引。(比如最纠结的like该不该用的老问题:如果不用like,强制用=来处理,当然速度会快很多,但是查询就不准确,比如用户就记得产品描述中的几个字,别的都不记得,这时候站在用户的立场,无疑模糊查询是最好的,但是在海量数据情况下,这样做又几乎是灾难)。在这些特定情况下,关系型数据库(不管是sqlserver还是oracle)的查询能力都是无能为力的,如果您去百度一下关于搜索引擎的数据库设计,几乎看不到采用关系型数据库做为查询核心的。推荐几篇我以前收集的搜索引擎架构文章给您参考下http://www.cnblogs.com/yjmyzz/archive/2009/06/18/1506083.html

          当然"学无先后,达者为先"。如果大家有更好的解决方案,欢迎回贴指导。

          最后YY一句,就这种拍脑袋的设计,07年时曾经受住了日均IP10万的访问量,当时日均IP从中国站长站监测的结果,已经达到了同期中国新蛋日均IP的1.5倍(当然还有其它优化措施,这个就不一一展开了)

    展开全文
  • 然而在实际的工作中,维护数据库设计文档,查阅数据库设计文档对我们这些软件开发人员来说是个比较头疼的工作,往往由于维护的不及时,或得到的文档的版本不是最新的,而造成对数据库设计的理解出现偏差。...
  • 数据库设计

    2020-09-07 10:47:10
    数据库设计的步骤(过程) 需求分析 概念结构设计 逻辑结构设计 数据库物理设计 数据库实施 数据库运行和维护 什么是E-R图?构成E-R图的基本要素是什么? E-R图为实体-联系图,提供了表示实体型、属性和联系的方法...
  • 之前发表过一篇"商城系统中【商品扩展属性】的表单生成及客户端验证",部分童鞋对于后台数据库设计比较感兴趣,于是今天把这部分也补上。   一、产品分类设计 越来越的商城系统都热衷于选择“无限级
  • 逻辑数据库设计 - 可变属性(继承)  可变属性的需求:我们需要在数据库里面存储很电器,比如电视,冰箱等等。通常,在程序中,我们的类图为: EVA设计  对于这种继承下来的可变属性时,有一种...
  • 数据库之数据库设计

    2019-07-22 08:50:55
    数据库设计共有三大范式: 第一范式:无重复的列 第二范式:属性完全依赖于主键 第三范式:属性不能依赖于主属性 第一范式 数据库表中的每一列都是不可分割的基本数据项,同一列中不能有个值。具体而言,有以下两...
  • 2. 概念数据库设计 需求 -> 概念模型(E-R图、IDEF1X图) 3.逻辑数据库设计 概念模型->逻辑模型: E-R图->关系模式: 实体 -> 关系: 实体的属性 -> 关系的属性 实体的关键字 -> 关系的关键字 ...
  • 理清数据库设计

    2020-06-01 10:59:38
    本文参考于如何进行数据库设计,侵删 1. 需求分析 数据库需求的作用点: 数据是什么 数据有哪些属性 数据和属性各自的特点有哪些 搞清楚一些问题 实体与实体之间的关系(如1对1,1对) 实体所包含的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,617
精华内容 1,046
关键字:

多属性数据库设计