dedecms网站如何上线,建设一个网站需要用到几个语言,网站 相对路径,上市公司排行榜Mysql存储-EAV模式 最近又又又搞一点新东西#xff0c;要整合不同业务进行存储和查询#xff0c;一波学习过后总结了一下可扩展性MAX的eav模式存储。 在eav这里的数据结构设计尤为关键#xff0c;需要充分考虑你需要使用的字段、使用场景#xff0c;当数据结构设计完成后便…Mysql存储-EAV模式 最近又又又搞一点新东西要整合不同业务进行存储和查询一波学习过后总结了一下可扩展性MAX的eav模式存储。 在eav这里的数据结构设计尤为关键需要充分考虑你需要使用的字段、使用场景当数据结构设计完成后便会发现eav模型需要多次join操作才能完成查询因此性能优化的难点也是在如何充分使用索引 一、简介
1、概念
EAVEntity-Attribute-Value模式也称为对象-属性-值模式是一种常用于数据库设计的灵活模式适用于具有大量属性和属性值的实体。它在MySQL数据库中的实现可以解决一些传统关系型数据库表结构无法轻松满足的需求例如动态属性、稀疏属性等。
EAV模式的核心思想是将实体Entity的属性Attribute和值Value分别存储在不同的表中。这样可以在不修改表结构的情况下轻松添加或删除属性从而提高数据库的灵活性。
EAV模式在MySQL数据库中通常包含以下三个表
实体表Entity Table存储实体的基本信息如ID、名称等。每个实体对应该表中的一行记录。属性表Attribute Table存储属性的元数据如属性ID、属性名称、数据类型等。每个属性对应该表中的一行记录。值表Value Table存储实体的属性值。每个属性值对应该表中的一行记录包括实体ID、属性ID和属性值。 2、特点
EAV模式的优点
高度灵活可以轻松添加、删除或修改属性而无需更改表结构。节省存储空间对于具有大量稀疏属性的实体EAV模式可以避免在数据表中存储大量NULL值。
EAV模式的缺点
查询复杂由于属性和值分散在多个表中查询和聚合操作通常需要多表连接导致查询性能较差。数据完整性EAV模式较难实现属性值的数据类型和约束检查可能导致数据完整性问题。
二、详细设计 写入时 在实际业务上会接入不同领域的数据不同领域数据内容也不尽相同在领域分治的情况下便只需要考虑单一的固定数据。 同一领域内数据具有一定的相似性将较多出现的数据存放于entity表中以减少多次join操作的情况性能 同一领域内的相同扩展字段名称可能会出现不同数据类型的情况因此需要在attributes表中增加name、type的唯一键进行upsert操作保证该表数据满足全部场景 根据传入的interface类型将数据存储到对应的字段中。例如如果传入的数据是整数类型将数据存储到int_value字段中
查询时
需要增加表用于记录单个领域下的entity中的固定字段在查询时先查询该领域的固定字段是否cover查询要求的字段如果cover住则不需要查询values表。根据attributes表中的type字段进行“类型断言”。例如如果attributes表中的type值为’int’则从values表中的int_value字段中读取数据应在各场景下最大程度地减少使用断言 类型断言是Golang内置的特性不需要额外引入包反射是指在运行时动态获取变量的类型信息、操作变量的方法
三、demo
SQL:
CREATE TABLE entities (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) NOT NULL,status VARCHAR(255) NOT NULL,type VARCHAR(255) NOT NULL
);CREATE TABLE attributes (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) NOT NULL UNIQUE
);CREATE TABLE values (entity_id INT,attribute_id INT,value VARCHAR(255) NOT NULL,PRIMARY KEY (entity_id, attribute_id),FOREIGN KEY (entity_id) REFERENCES entities(id),FOREIGN KEY (attribute_id) REFERENCES attributes(id)
);Golang:
package mainimport (database/sqlfmt_ github.com/go-sql-driver/mysql
)type Data struct {ID intName stringStatus stringType stringExtraData map[string]string
}func main() {db, err : sql.Open(mysql, username:passwordtcp(localhost:3306)/dbname)if err ! nil {panic(err)}defer db.Close()// 插入数据extraData : map[string]string{check_data: 2021-10-01,start_time: 10:00:00,}entityID, err : insertData(db, name1, status1, type1, extraData)if err ! nil {panic(err)}// 查询数据data, err : getData(db, entityID)if err ! nil {panic(err)}fmt.Printf(Data: %v\n, data)
}func insertData(db *sql.DB, name, status, dataType string, extraData map[string]string) (int, error) {res, err : db.Exec(INSERT INTO entities (name, status, type) VALUES (?, ?, ?), name, status, dataType)if err ! nil {return 0, err}entityID, err : res.LastInsertId()if err ! nil {return 0, err}for attributeName, value : range extraData {attributeID, err : getOrCreateAttribute(db, attributeName)if err ! nil {return 0, err}_, err db.Exec(INSERT INTO values (entity_id, attribute_id, value) VALUES (?, ?, ?), entityID, attributeID, value)if err ! nil {return 0, err}}return int(entityID), nil
}func getData(db *sql.DB, entityID int) (*Data, error) {row : db.QueryRow(SELECT id, name, status, type FROM entities WHERE id ?, entityID)var data Dataerr : row.Scan(data.ID, data.Name, data.Status, data.Type)if err ! nil {return nil, err}rows, err : db.Query(SELECT a.name, v.value FROM attributes a JOIN values v ON a.id v.attribute_id WHERE v.entity_id ?, entityID)if err ! nil {return nil, err}defer rows.Close()data.ExtraData make(map[string]string)for rows.Next() {var attributeName, value stringif err : rows.Scan(attributeName, value); err ! nil {return nil, err}data.ExtraData[attributeName] value}return data, nil
}func getOrCreateAttribute(db *sql.DB, attributeName string) (int, error) {var attributeID interr : db.QueryRow(SELECT id FROM attributes WHERE name ?, attributeName).Scan(attributeID)if err sql.ErrNoRows {res, err : db.Exec(INSERT INTO attributes (name) VALUES (?), attributeName)if err ! nil {return 0, err}id, err : res.LastInsertId()if err ! nil {return 0, err}attributeID int(id)} else if err ! nil {return 0, err}return attributeID, nil
}