首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

数据库表设计中应用横向表切割来降低并发冲突

2012-10-18 
数据库表设计中使用横向表切割来降低并发冲突近日在设计《威博文件管理系统》的文件标签系统中的一点体会。?

数据库表设计中使用横向表切割来降低并发冲突

近日在设计《威博文件管理系统》的文件标签系统中的一点体会。

?

近来根据用户反馈和原定的开发计划,为《威博文件管理系统》设计一个“文件标签”模块,实现的功能类似于很多大型网站的标签功能,能够实现智能标签功能。

?

开始的数据库表结构,使用一个单一的“文件标签”表,文件标签表,结构如下:

?

主键:char(36)

标签名称:varchar(50)

标签排序:int

主观权重:bit

主观推荐:bit

时间戳:bigint

版本号:int

引用计数:int

点击计数:int

?

@Entity@Table(name = "文件标签")public class FileTagModel implements Serializable {    /**     * 使用java的UUID唯一号,36长度的字符串     */    @Id    @Column(name = "主键")    private String id;    /**     * 标签名称     */    @Column(name = "标签名称")    private String tagName;    /**     * 标签排序     */    @Column(name = "标签排序")    private int px;    /**     * 主观权重     */    @Column(name = "主观权重")    private boolean tagWeight;    /**     * 主观推荐     */    @Column(name = "主观推荐")    private boolean tagSuggest;    /**     * 实体创建时的时间戳     */    @Column(name = "时间戳")    private long timestamp;    /**     * 实体对象的版本号     */    @Column(name = "版本号")    @Version    private int version;    /**     * 点击计数     */    @Column(name = "点击计数")    private int clickCount;    /**     * 引用计数     */    @Column(name = "引用计数")    private int quoteCount;}

?

经过一段时间的测试后发现,“文件标签”表中,各个字段的的变化速度有很大的不同,其中引用计数、点击计数、变化很频繁,而其它字段的内容相对变化就比较稳定,一旦生成,相对比较稳定,这样在并发程度高时,常常导致数据更新冲突,虽然可以通过@version注解可以保证数据的乐观锁定发生作用,数据的一致性不被破坏。但是由于把变化频度差异很大的字段放在同一张表上,导致冲突发生的概率增大,降低了系统的效能。

?

于是想到了对该表的横向切割,以便获得更好的并发性能。

?

切割后,分为三个表,《文件标签》作为主表,《文件标签引用计数》《文件标签点击计数》作为从表,三个表共用主键,三表结构如下:

?

《文件标签》表结构

主键:char(36)

标签名称:varchar(50)

标签排序:int

主观权重:bit

主观推荐:bit

时间戳:bigint

版本号:int

?

《文件标签引用计数》表结构

主键:char(36)

引用计数:int

时间戳:bigint

版本号:int

?

《文件标签点击计数》表结构

主键:char(36)

点击计数:int

时间戳:bigint

版本号:int

?

?

《文件标签》表实体Bean

package cn.sh.webfile.model;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.OneToOne;import javax.persistence.PrimaryKeyJoinColumn;import javax.persistence.Table;import javax.persistence.Version;import org.apache.commons.lang.builder.ReflectionToStringBuilder;/** * 开始于 2010-06-09 * 文件标签符号表的模型 * [2010-06-18修]新增加一个字段 * [2010-06-21修]根据应用程序的使用反馈,把引用计数、点击计数字段,放入两个不同的表 * 以实现更好的多工状态下的并发操作,减少状态冲突。分拆理由,表《文件标签》主表、《文件标签引用计数》、 * 《文件标签点击计数》作为从表,主表变化慢,从表变化快。 * 最后修改于 2010-06-21 * @author 万继斌 * @version 1.3 */@Entity@Table(name = "文件标签")public class FileTagModel implements Serializable {    /**     * 使用java的UUID唯一号,36长度的字符串     */    @Id    @Column(name = "主键")    private String id;    /**     * 标签名称     */    @Column(name = "标签名称")    private String tagName;    /**     * 标签排序     */    @Column(name = "标签排序")    private int px;    /**     * 主观权重     */    @Column(name = "主观权重")    private boolean tagWeight;    /**     * 主观推荐     */    @Column(name = "主观推荐")    private boolean tagSuggest;    /**     * 实体创建时的时间戳     */    @Column(name = "时间戳")    private long timestamp;    /**     * 实体对象的版本号     */    @Column(name = "版本号")    @Version    private int version;    /**     * 点击计数     */    @OneToOne    @PrimaryKeyJoinColumn    private FileTagClickCountModel clickCount;    /**     * 引用计数     */    @OneToOne    @PrimaryKeyJoinColumn    private FileTagQuoteCountModel quoteCount;    @Override    public String toString() {        return ReflectionToStringBuilder.toString(this);    }    /**     * @return the id     */    public String getId() {        return id.trim();    }    /**     * @param id the id to set     */    public void setId(String id) {        this.id = id.trim();    }    /**     * @return the timestamp     */    public long getTimestamp() {        return timestamp;    }    /**     * @param timestamp the timestamp to set     */    public void setTimestamp(long timestamp) {        this.timestamp = timestamp;    }    /**     * @return the version     */    public int getVersion() {        return version;    }    /**     * @param version the version to set     */    public void setVersion(int version) {        this.version = version;    }    /**     * @return the tagName     */    public String getTagName() {        return tagName.trim();    }    /**     * @param tagName the tagName to set     */    public void setTagName(String tagName) {        this.tagName = tagName.trim();    }    /**     * @return the tagWeight     */    public boolean getTagWeight() {        return tagWeight;    }    /**     * @param tagWeight the tagWeight to set     */    public void setTagWeight(boolean tagWeight) {        this.tagWeight = tagWeight;    }    /**     * @return the px     */    public int getPx() {        return px;    }    /**     * @param px the px to set     */    public void setPx(int px) {        this.px = px;    }    /**     * @return the tagSuggest     */    public boolean isTagSuggest() {        return tagSuggest;    }    /**     * @param tagSuggest the tagSuggest to set     */    public void setTagSuggest(boolean tagSuggest) {        this.tagSuggest = tagSuggest;    }    /**     * @return the clickCount     */    public FileTagClickCountModel getClickCount() {        return clickCount;    }    /**     * @param clickCount the clickCount to set     */    public void setClickCount(FileTagClickCountModel clickCount) {        this.clickCount = clickCount;    }    /**     * @return the quoteCount     */    public FileTagQuoteCountModel getQuoteCount() {        return quoteCount;    }    /**     * @param quoteCount the quoteCount to set     */    public void setQuoteCount(FileTagQuoteCountModel quoteCount) {        this.quoteCount = quoteCount;    }}

?

《文件标签引用计数》表实体Bean

package cn.sh.webfile.model;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.Table;import javax.persistence.Version;import org.apache.commons.lang.builder.ReflectionToStringBuilder;/** * 开始于 2010-06-21 * 文件标签引用计数表的模型 * 《文件标签引用计数》作为从表,主表变化慢,从表变化快。 * 最后修改于 2010-06-21 * @author 万继斌 * @version 1.0 */@Entity@Table(name = "文件标签引用计数")public class FileTagQuoteCountModel implements Serializable {    /**     * 使用java的UUID唯一号,36长度的字符串     */    @Id    @Column(name = "主键")    private String id;    /**     * 引用计数     */    @Column(name = "引用计数")    private int count;    /**     * 实体创建时的时间戳     */    @Column(name = "时间戳")    private long timestamp;    /**     * 实体对象的版本号     */    @Column(name = "版本号")    @Version    private int version;    @Override    public String toString() {        return ReflectionToStringBuilder.toString(this);    }    /**     * @return the id     */    public String getId() {        return id.trim();    }    /**     * @param id the id to set     */    public void setId(String id) {        this.id = id.trim();    }    /**     * @return the timestamp     */    public long getTimestamp() {        return timestamp;    }    /**     * @param timestamp the timestamp to set     */    public void setTimestamp(long timestamp) {        this.timestamp = timestamp;    }    /**     * @return the version     */    public int getVersion() {        return version;    }    /**     * @param version the version to set     */    public void setVersion(int version) {        this.version = version;    }    /**     * @return the count     */    public int getCount() {        return count;    }    /**     * @param count the count to set     */    public void setCount(int count) {        this.count = count;    }}

?

《文件标签点击计数》表实体Bean

package cn.sh.webfile.model;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.Table;import javax.persistence.Version;import org.apache.commons.lang.builder.ReflectionToStringBuilder;/** * 开始于 2010-06-21 * 文件标签点击计数表的模型 * 《文件标签点击计数》变化较快的表。 * 最后修改于 2010-06-21 * @author 万继斌 * @version 1.0 */@Entity@Table(name = "文件标签点击计数")public class FileTagClickCountModel implements Serializable {    /**     * 使用java的UUID唯一号,36长度的字符串     */    @Id    @Column(name = "主键")    private String id;    /**     * 点击计数     */    @Column(name = "点击计数")    private int count;    /**     * 实体创建时的时间戳     */    @Column(name = "时间戳")    private long timestamp;    /**     * 实体对象的版本号     */    @Column(name = "版本号")    @Version    private int version;    @Override    public String toString() {        return ReflectionToStringBuilder.toString(this);    }    /**     * @return the id     */    public String getId() {        return id.trim();    }    /**     * @param id the id to set     */    public void setId(String id) {        this.id = id.trim();    }    /**     * @return the timestamp     */    public long getTimestamp() {        return timestamp;    }    /**     * @param timestamp the timestamp to set     */    public void setTimestamp(long timestamp) {        this.timestamp = timestamp;    }    /**     * @return the version     */    public int getVersion() {        return version;    }    /**     * @param version the version to set     */    public void setVersion(int version) {        this.version = version;    }    /**     * @return the count     */    public int getCount() {        return count;    }    /**     * @param count the count to set     */    public void setCount(int count) {        this.count = count;    }}

?

经过实地测试,并发效能大大提高,而通过对主表的分割,而导致的连接操作带来的速度损失相对较小,某些方面还可以增大综合读写速度。

?

体会总结如下:

当我们在设计时,发现同一张表内,字段的变化频度差异过大时,为了获得更好的并发操作性能,降低潜在冲突,这时可以对表,进行横向分割,按照字段的不同的变化频度,合理地置于不同的主从表内,以此来提高性能。特别是在Web应用程序中,使用版本号来进行乐观锁定时,对性能的提高更加有好处,可以大大降低冲突发生的概率。

?

?

?

热点排行