1、表设计

1.1、列式存储

StarRocks的表和关系型数据相同, 由行和列构成. 每行数据对应用户一条记录, 每列数据有相同数据类型. 所有数据行的列数相同, 可以动态增删列.  StarRocks中, 一张表的列可以分为维度列(也成为key列)和指标列(value列), 维度列用于分组和排序, 指标列可通过聚合函数SUM, COUNT, MIN, MAX, REPLACE, HLL_UNION, BITMAP_UNION等累加起来. 因此, StarRocks的表也可以认为是多维的key到多维指标的映射.

在StarRocks中, 表中数据按列存储, 物理上, 一列数据会经过分块编码压缩等操作, 然后持久化于非易失设备, 但在逻辑上, 一列数据可以看成由相同类型的元素构成的数组.  一行数据的所有列在各自的列数组中保持对齐, 即拥有相同的数组下标, 该下标称之为序号或者行号. 该序号是隐式, 不需要存储的, 表中的所有行按照维度列, 做多重排序, 排序后的位置就是该行的行号.

查询时, 如果指定了维度列的等值条件或者范围条件, 并且这些条件中维度列可构成表维度列的前缀, 则可以利用数据的有序性, 使用range-scan快速锁定目标行. 例如: 对于表table1: (event_day, siteid, citycode, username)➜(pv); 当查询条件为event_day > 2020-09-18 and siteid = 2, 则可以使用范围查找; 如果指定条件为citycode = 4 and username in ["Andy", "Boby", "Christian", "StarRocks"], 则无法使用范围查找.

1.2、稀疏索引

当进行范围查询时,StarRocks如何快速定位到起始目标行呢?答案是使用shortkey index. shortkey index为稀疏索引。

表中组织由三个部分组成:

  1. shortkey index表: 表中数据每1024行, 构成一个逻辑block. 每个逻辑block在shortkey index表中存储一项索引, 内容为表的维度列的前缀, 并且不超过36字节. shortkey index为稀疏索引, 用数据行的维度列的前缀查找索引表, 可以确定该行数据所在逻辑块的起始行号。
  2. Per-column data block: 表中每一列数据按64KB分块存储, 数据块作为一个单位单独编码压缩, 也作为IO单位, 整体写回设备或者读出。
  3. Per-column cardinal index: 表中的每列数据有各自的行号索引表, 列的数据块和行号索引项一一对应, 索引项由数据块的起始行号和数据块的位置和长度信息构成, 用数据行的行号查找行号索引表, 可以获取包含该行号的数据块所在位置, 读取目标数据块后, 可以进一步查找数据

由此可见, 查找维度列的前缀的查找过程为:  先查找shortkey index, 获得逻辑块的起始行号, 查找维度列的行号索引, 获得目标列的数据块, 读取数据块, 然后解压解码, 从数据块中找到维度列前缀对应的数据项。

1.3、加速数据处理

  1. 预先聚合:  StarRocks支持聚合模型, 维度列取值相同数据行可合并一行, 合并后数据行的维度列取值不变, 指标列的取值为这些数据行的聚合结果, 用户需要给指标列指定聚合函数.  通过预先聚合, 可以加速聚合操作.
  2. 分区分桶:  事实上StarRocks的表被划分成tablet, 每个tablet多副本冗余存储在BE上, BE和tablet的数量可以根据计算资源和数据规模而弹性伸缩. 查询时, 多台BE可并行地查找tablet快速获取数据. 此外, tablet的副本可复制和迁移, 增强了数据的可靠性, 避免了数据倾斜. 总之, 分区分桶保证了数据访问的高效性和稳定性.
  3. RollUp表索引: shortkey index可加速数据查找, 然后shortkey index依赖维度列排列次序. 如果使用非前缀的维度列构造查找谓词, 则无法使用shortkey index. 用户可以为数据表创建若干RollUp表索引, RollUp表索引的数据组织和存储和数据表相同, 但RollUp表拥有自身的shortkey index. 用户创建RollUp表索引时, 可选择聚合的粒度, 列的数量, 维度列的次序; 使频繁使用的查询条件能够命中相应的RollUp表索引.
  4. 列级别的索引技术:  Bloomfilter可快速判断数据块中不含所查找值, ZoneMap通过数据范围快速过滤待查找值, Bitmap索引可快速计算出枚举类型的列满足一定条件的行.

1.4、数据模型

目前StarRocks根据摄入数据和实际存储数据之间的映射关系,分为明细模型(Duplicate key)、聚合模型(Aggregate key)、更新模型(Unique key)和主键模型(Primary key),其中主键模型和更新模型很类型,但是主键模型速度会更快,要求也更高。默认使用明细模型。

四中模型分别对应不同业务场景。

1.4.1、明细模型

StarRocks建表默认采用明细模型,排序列使用稀疏索引,可以快速过滤数据。明细模型用于保存所有历史数据,并且用户可以考虑将过滤条件中频繁使用的维度列作为排序键,比如用户经常需要查看某一时间,可以将事件时间和事件类型作为排序键。

使用:

CREATE TABLE IF NOT EXISTS detail (event_time DATETIME NOT NULL COMMENT "datetime of event",event_type INT NOT NULL COMMENT "type of event",user_id INT COMMENT "id of user",device_code INT COMMENT "device of ",channel INT COMMENT "")
DUPLICATE KEY(event_time, event_type)  -- 指定排序列为事件时间和事件类型
DISTRIBUTED BY HASH(user_id) BUCKETS 8  -- 根据user_id分为8个桶INSERT INTO detail VALUES('2021-11-18 12:00:00.00',1,1001,1,1);
INSERT INTO detail VALUES('2021-11-17 12:00:00.00',2,1001,1,1);
INSERT INTO detail VALUES('2021-11-16 12:00:00.00',3,1001,1,1);
INSERT INTO detail VALUES('2021-11-15 12:00:00.00',1,1001,1,1);
INSERT INTO detail VALUES('2021-11-14 12:00:00.00',2,1001,1,1);

1.4.2、聚合模型

在数据分析中,很多场景需要基于明细数据进行统计和汇总,这个时候就可以使用聚合模型了。比如:统计app访问流量、用户访问时长、用户访问次数、展示总量、消费统计等等场景。

适合聚合模型来分析的业务场景有以下特点:

  1. 业务方进行查询为汇总类查询,比如sum、count、max
  2. 不需要查看原始明细数据
  3. 老数据不会被频繁修改,只会追加和新增

使用:

CREATE TABLE IF NOT EXISTS aggregate_tbl (site_id LARGEINT NOT NULL COMMENT "id of site",DATE DATE NOT NULL COMMENT "time of event",city_code VARCHAR(20) COMMENT "city_code of user",pv BIGINT SUM DEFAULT "0" COMMENT "total page views",mt BIGINT MAX
)
DISTRIBUTED BY HASH(site_id) BUCKETS 8;INSERT INTO aggregate_tbl VALUES(1001,'2021-11-18 12:00:00.00',100,1,5);
INSERT INTO aggregate_tbl VALUES(1001,'2021-11-18 12:00:00.00',100,1,10);
INSERT INTO aggregate_tbl VALUES(1001,'2021-11-18 12:00:00.00',100,1,15);
INSERT INTO aggregate_tbl VALUES(1001,'2021-11-18 12:00:00.00',100,1,100);
INSERT INTO aggregate_tbl VALUES(1001,'2021-11-18 12:00:00.00',100,1,20);
INSERT INTO aggregate_tbl VALUES(1002,'2021-11-18 12:00:00.00',100,1,5);
INSERT INTO aggregate_tbl VALUES(1002,'2021-11-18 12:00:00.00',100,3,25);
INSERT INTO aggregate_tbl VALUES(1002,'2021-11-18 12:00:00.00',100,1,15);
INSERT INTO aggregate_tbl VALUES(1002,'2021-11-19 12:00:00.00',100,1,15);
INSERT INTO aggregate_tbl VALUES(1002,'2021-11-19 12:00:00.00',200,1,15);

1.4.3、更新模型

有些分析场景之下,数据需要进行更新比如拉链表,StarRocks则采用更新模型来满足这种需求,比如电商场景中,订单的状态经常会发生变化,每天的订单更新量可突破上亿。这种业务场景下,如果只靠明细模型下通过delete+insert的方式,是无法满足频繁更新需求的,因此,用户需要使用更新模型来满足分析需求。但是如果用户需要更加实时/频繁的更新操作,建议使用主键模型。

使用更新模型的场景特点:

  1. 已经写入的数据有大量的更新需求
  2. 需要进行实时数据分析
CREATE TABLE IF NOT EXISTS update_detail (create_time DATE NOT NULL COMMENT "create time of an order",order_id BIGINT NOT NULL COMMENT "id of an order",order_state INT COMMENT "state of an order",total_price BIGINT COMMENT "price of an order"
)
UNIQUE KEY(create_time, order_id) -- 指定唯一键
DISTRIBUTED BY HASH(order_id) BUCKETS 8INSERT INTO update_detail VALUES('2011-11-18',1001,1,1000);
INSERT INTO update_detail VALUES('2011-11-18',1001,2,2000);
INSERT INTO update_detail VALUES('2011-11-17',1001,2,500);
INSERT INTO update_detail VALUES('2011-11-18',1002,3,3000);
INSERT INTO update_detail VALUES('2011-11-18',1002,4,4500);

1.4.4、主键模型

相比较更新模型,主键模型可以更好地支持实时/频繁更新的功能。虽然更新模型也可以实现实时对数据的更新,但是更新模型采用Merge on Read读时合并策略会大大限制查询功能,在主键模型更好地解决了行级的更新操作。配合Flink-connector-starrocks可以完成Mysql CDC实时同步的方案。

需要注意的是:由于存储引擎会为主键建立索引,导入数据时会把索引加载到内存中,所以主键模型对内存的要求更高,所以不适合主键模型的场景还是比较多的。

目前比较适合使用主键模型的场景有这两种:

  1. 数据冷热特征,比如最近几天的数据才需要修改,老的冷数据很少需要修改,比如订单数据,老的订单完成后就不在更新,并且分区是按天进行分区的,那么在导入数据时历史分区的数据的主键就不会被加载,也就不会占用内存了,内存中仅会加载近几天的索引。如果很久远的历史数据也会被修改,不建议使用主键模型。
  2. 大宽表(数百列数千列),主键只占整个数据的很小一部分,内存开销比较低。比如用户状态/画像表,虽然列非常多,但总的用户数量不大(千万-亿级别),主键索引内存占用相对可控。

原理:由于更新模型采用Merge策略,使得谓词无法下推和索引无法使用,严重影响查询性能。所以主键模型通过主键约束,保证同一个主键仅存一条数据的记录,这样就规避了Merge操作。

StarRocks收到对某记录的更新操作时,会通过主键索引找到该条数据的位置,并对其标记为删除,再插入一条数据,相当于把update改写为delete+insert。

CREATE TABLE users (user_id BIGINT NOT NULL,NAME STRING NOT NULL,email STRING NULL,address STRING NULL,age TINYINT NULL,sex TINYINT NULL
) PRIMARY KEY (user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 4INSERT INTO users VALUES(1001,'张三','111@qq.com','AAA',17,'0');
INSERT INTO users VALUES(1001,'李四','222@qq.com','BBB',18,'1');
INSERT INTO users VALUES(1002,'aaa','222@qq.com','aaa',18,'0');
INSERT INTO users VALUES(1002,'bbb','222@qq.com','bbb',18,'1');

1.4.5、排序键

StarRocks中为加速查询,在内部组织并存储数据时,会把表中数据按照指定的列进行排序,这部分用于排序的列(可以是一个或多个列),可以称之为Sort Key。明细模型中Sort Key就是指定的用于排序的列(即 DUPLICATE KEY 指定的列),聚合模型中Sort Key列就是用于聚合的列(即 AGGREGATE KEY 指定的列),更新模型中Sort Key就是指定的满足唯一性约束的列(即 UNIQUE KEY 指定的列)。下面的建表语句中Sort Key都为 (site_id、city_code)。

CREATE TABLE site_access_duplicate(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT DEFAULT '0')
DUPLICATE KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;CREATE TABLE site_access_aggregate(
site_id INT DEFAULT '10',
city_code SMALLINT,
pv BIGINT SUM DEFAULT '0')
AGGREGATE KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;CREATE TABLE site_access_unique(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT DEFAULT '0')
UNIQUE KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;

三种表对应的sort key都为site_id,city_code。创建排序列需要注意以下两点:

  1. 排序列的定义必须出现在建表语句中其他列的定义之前。以图中的建表语句为例,三个表的排序列可以是site_id、city_code,或者site_id、city_code、user_name,但不能是city_code、user_name,或者site_id、city_code、pv。

  2. 排序列的顺序是由create table语句中的列顺序决定的。DUPLICATE/UNIQUE/AGGREGATE KEY中顺序必须和create table语句保持一致,否则会报错。

使用时注意事项:

  • 用户查询时如果条件包含上述两列,则可以大幅地降低扫描数据行;如果查询只包含site_id一列,也能定位到只包含site_id的数据行;如果查询只包含city_code一列,那么需要扫描所有的数据行,排序的效果相当于大打折扣。(使用时和mysql索引规则一样,缺少最佳左前缀原则,索引会失效)。
  • 使用排序键本质就是在进行二分查找,所以排序列指定的越多,那么消耗的内存也会越大,StarRocks为了避免这种情况发生也对排序键做了限制:

    • shortkey的列只能是排序键的前缀;

    • shortkey列数不超过3;

    • 字节数不超过36字节;

    • 不包含FLOAT/DOUBLE类型的列;

    • VARCHAR类型列只能出现一次, 并且是末尾位置;

    • 当shortkey index的末尾列为CHAR或者VARCHAR类型时, shortkey的长度会超过36字节;

    • 当用户在建表语句中指定PROPERTIES {short_key = "integer"}时, 可突破上述限制;

1.4.6、物化视图

Materialized Views 表:简称 MVs,物化视图

使用场景

在实际的业务场景中,通常存在两种场景并存的分析需求:对固定维度的聚合分析 和 对原始明细数据任意维度的分析。

例如,在销售场景中,每条订单数据包含这几个维度信息(item_id, sold_time, customer_id, price)。在这种场景下,有两种分析需求并存:

  1. 业务方需要获取某个商品在某天的销售额是多少,那么仅需要在维度(item_id, sold_time)维度上对 price 进行聚合即可。
  2. 分析某个人在某天对某个商品的购买明细数据。

在现有的 StarRocks 数据模型中,如果仅建立一个聚合模型的表,比如(item_id, sold_time, customer_id, sum(price))。由于聚合损失了数据的部分信息,无法满足用户对明细数据的分析需求。如果仅建立一个 Duplicate 模型,虽可以满足任意维度的分析需求,但由于不支持 Rollup,分析性能不佳,无法快速完成分析。如果同时建立一个聚合模型和一个 Duplicate 模型,虽可以满足性能和任意维度分析,但两表之间本身无关联,需要业务方自行选择分析表。不灵活也不易用。

如何使用

使用聚合函数(如sum和count)的查询,在已经包含聚合数据的表中可以更高效地执行。这种改进的效率对于查询大量数据尤其适用。表中的数据被物化在存储节点中,并且在增量更新中能和 Base 表保持一致。用户创建 MVs 表后,查询优化器支持选择一个最高效的 MVs 映射,并直接对 MVs 表进行查询而不是 Base 表。由于 MVs 表数据通常比 Base 表数据小很多,因此命中 MVs 表的查询速度会快很多。

1、基于文档上述明细模型表,创建测试物化视图

CREATE MATERIALIZED VIEW test_detail_view AS SELECT user_id,MAX(event_type),COUNT(device_code),SUM(channel) FROM detail GROUP BY user_id;

2、创建完视图后,用户并不感知创建成功,可以通过explain来分析是否命中视图。可以看到上面物化视图对event_type字段使用max函数,那么rollup命中的数据源为创建的物化视图。

explain select max(event_type) from detail;

3、那么如果使用对event_type字段使用count函数,又可以看到rollup命中的是detail表。

4、那么建立物化视图,就可以帮助用户对于不同场景都起到加速查询的作用。目前物化视图支持的函数如下有:count、max、min、sum、percentile_approx、hill_union、bitmap_union。

5、当有创建多个物化视图,会自动选择最优的那个。没有匹配上,则会直接从原表查询数据。

1.4.7、Bitmap索引

StarRocks支持基于BitMap索引,对于Filter的查询有明显的加速效果。

        1、原理:

Bitmap是元素为bit的, 取值为0、1两种情形的, 可对某一位bit进行置位(set)和清零(clear)操作的数组。Bitmap的使用场景有:

  1. 用一个long型表示32位学生的性别,0表示女生,1表示男生。
  2. 用Bitmap表示一组数据中是否存在null值,0表示元素不为null,1表示为null。
  3. 一组数据的取值为(Q1, Q2, Q3, Q4),表示季度,用Bitmap表示这组数据中取值为Q4的元素,1表示取值为Q4的元素, 0表示其他取值的元素。

2、什么是Bitmap索引:

Bitmap只能表示取值为两种情形的列数组, 当列的取值为多种取值情形枚举类型时, 例如季度(Q1, Q2, Q3, Q4),  系统平台(Linux, Windows, FreeBSD, MacOS), 则无法用一个Bitmap编码; 此时可以为每个取值各自建立一个Bitmap的来表示这组数据; 同时为实际枚举取值建立词典.

如上图所示,Platform列有4行数据,可能的取值有Android、Ios。StarRocks中会首先针对Platform列构建一个字典,将Android和Ios映射为int,然后就可以对Android和Ios分别构建Bitmap。具体来说,我们分别将Android、Ios 编码为0和1,因为Android出现在第1,2,3行,所以Bitmap是0111,因为Ios出现在第4行,所以Bitmap是1000。

假如有一个针对包含该Platform列的表的SQL查询,select xxx from table where Platform = iOS,StarRocks会首先查找字典,找出iOS对于的编码值是1,然后再去查找 Bitmap Index,知道1对应的Bitmap是1000,我们就知道只有第4行数据符合查询条件,StarRocks就会只读取第4行数据,不会读取所有数据。

3、适用场景:

使用Bitmap可以大大减少判断过滤时间,提高查询效率

  1. 当需要对表数据进行非前置列(排序键)进行过滤时,可以创建bitmap索引加速效率。
  2. 对表数据进行多列过滤,也可以考虑对多列分别创建bitmap索引加速效率

4、使用:

1、创建测试数据:

-- 创建测试表
CREATE TABLE IF NOT EXISTS user_dup (user_id INT,sex INT ,age INT
)DUPLICATE KEY(user_id)DISTRIBUTED BY HASH(user_id) BUCKETS 8;-- 写入数据
INSERT INTO user_dup VALUES(1001,0,18);
INSERT INTO user_dup VALUES(1002,1,18);
INSERT INTO user_dup VALUES(1003,0,18);
INSERT INTO user_dup VALUES(1004,1,18);
INSERT INTO user_dup VALUES(1005,0,18);
INSERT INTO user_dup VALUES(1006,1,18);
INSERT INTO user_dup VALUES(1007,0,18);
INSERT INTO user_dup VALUES(1008,1,18);-- 创建位图索引
CREATE INDEX user_sex_index ON user_dup(sex) USING bitmap;-- 创建完后查看表中索引
SHOW INDEX FROM user_dup;

2、查看结果:

3、注意事项

  1. 对于明细模型,所有列都可以建Bitmap 索引;对于聚合模型,只有Key列可以建Bitmap 索引。
  2. Bitmap索引, 应该在取值为枚举型, 取值大量重复, 较低基数, 并且用作等值条件查询或者可转化为等值条件查询的列上创建。
  3. 不支持对Float、Double、Decimal 类型的列建Bitmap 索引。
  4. 如果要查看某个查询是否命中了Bitmap索引,可以通过查询的Profile信息查看。

1.4.8、Bloom Filter索引

1、什么是Bloom Filter:

Bloom Filter(布隆过滤器)是用于判断某个元素是否在一个集合中的数据结构,优点是空间效率和时间效率都比较高,缺点是有一定的误判率。

布隆过滤器是由一个Bit数组和n个哈希函数构成。Bit数组初始全部为0,当插入一个元素时,n个Hash函数对元素进行计算, 得到n个slot,然后将Bit数组中n个slot的Bit置1。

当我们要判断一个元素是否在集合中时,还是通过相同的n个Hash函数计算Hash值,如果所有Hash值在布隆过滤器里对应的Bit不全为1,则该元素不存在。当对应Bit全1时, 则元素的存在与否, 无法确定.  这是因为布隆过滤器的位数有限,  由该元素计算出的slot, 恰好全部和其他元素的slot冲突.  所以全1情形, 需要回源查找才能判断元素的存在性。

2、什么是Bloom Filter索引:

StarRocks的建表时, 可通过PROPERTIES{"bloom_filter_columns"="c1,c2,c3"}指定需要建BloomFilter索引的列,查询时, BloomFilter可快速判断某个列中是否存在某个值。如果Bloom Filter判定该列中不存在指定的值,就不需要读取数据文件;如果是全1情形,此时需要读取数据块确认目标值是否存在。另外,Bloom Filter索引无法确定具体是哪一行数据具有该指定的值。

3、使用

-- 创建一张测试表
CREATE TABLE test_bf(
id INT,
event_type INT,
email INT,
sex INT,
age INT
)DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 8
PROPERTIES("bloom_filter_columns"="event_type,sex");-- 查看Bloom Filter索引。使用show index查看不到Bloom Filter索引,得用show create table命令
SHOW CREATE TABLE test_bf;-- 删除索引
alter table test_bf set("bloom_filter_columns"="");

4、注意事项

  1. 对于明细模型,所有列都可以建Bitmap 索引;对于聚合模型,只有Key列可以建Bitmap 索引。
  2. Bitmap索引, 应该在取值为枚举型, 取值大量重复, 较低基数, 并且用作等值条件查询或者可转化为等值条件查询的列上创建。
  3. 不支持对Float、Double、Decimal 类型的列建Bitmap 索引。
  4. 如果要查看某个查询是否命中了Bitmap索引,可以通过查询的Profile信息查看。

StarRocks(二)表设计相关推荐

  1. 【酒店管理系统】(二)表设计

    有了基础需求,接下来要梳理系统中的数据结构,构建这些实体间的关联关系,最终落地的表现形式就是表设计. 一.类图 我们先简单看一张类图,梳理一下我们系统里面的结构,同时看看类中的实物关系是如何的. 员工 ...

  2. (二)购物商城数据库设计-商品表设计

    大家好,今天我们来设计一下购物商城的商品表. 我们的目标是表结构能够满足下面这张图的搜索: 在设计表之前,我们先来了解下商品中的两个概念:SPU和SKU SPU SPU(Standard Produc ...

  3. 聊聊数据仓库中维度表设计的二三事

    前言 大家好,我是云祁!今天和大家聊聊数据仓库中维度表设计的那些事. 维度表是维度建模的灵魂所在,在维度表设计中碰到的问题(比如维度变化.维度层次.维度一致性.维度整合和拆分等)都会直接关系到维度建模 ...

  4. 人行二代征信企业表设计

    企业部分中间表设计,

  5. 万亿级企业MySQL海量存储分库分表设计实践

    互联网业务往往使用MySQL数据库作为后台存储,存储引擎使用InnoDB.我们针对互联网自身业务特点及MySQL数据库特性,讲述在具体业务场景中如何设计表和分表.本文从介绍MySQL相关基础架构设计入 ...

  6. Oracle数据库表设计时的注意事项

    Oracle数据库表设计时的注意事项  表是Oracle数据库中最基本的对象之一.万丈高楼从平地起,这个基础对象对于数据库来说,非常重要.因为其设计是否合理,直接跟数据库的性能相关.从Oracle数据 ...

  7. RBAC、控制权限设计、权限表设计 基于角色权限控制和基于资源权限控制的区别优劣

    RBAC.控制权限设计.权限表设计 基于角色权限控制和基于资源权限控制的区别优劣 一.介绍 二.基于角色的权限设计 三.基于资源的权限设计 四.主体.资源.权限关系图 主体.资源.权限相关的数据模型 ...

  8. 数据库表设计、 数据库分层、myslq水平拆分、oracle表分区

    数据库表设计 数据库表结构设计方法及原则(li)数据库设计的三大范式:为了建立冗余较小.结构合理的数据库,设计数据库时必须遵循一定的规则.在关系型数据库中这种规则就称为范式.范式是符合某一种设计要求的 ...

  9. 万字归纳总结 | 数据库表设计与SQL编写技巧

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"加入公众号专属技术群 前言 随着移动云平台系统业务不断增长,必然需要对各系统进行 ...

最新文章

  1. JVM基础面试题及原理讲解
  2. 【Python】编程笔记1
  3. 最详细的创建虚拟机_minmal版本
  4. oh-my-zsh 国内网络快速安装方法 | How to install oh-my-zsh in China
  5. 手机看直播时卡屏幕显示无法连接服务器,看直播不再卡!教你几招提升网速方法...
  6. vivado2018.3根据板卡Boards直接创建工程(比如basys3和Arty A7)
  7. 如何启动Intel VT-X及合理利用搜索
  8. 在GRIDVIEW中合并单元格
  9. 未知高度的非表格垂直对齐
  10. U盘安装系统 从此告别光盘
  11. XP框架的另外选择→太极
  12. iOS SQLite 读书笔记
  13. 牵手爱情,绎一份不了情缘
  14. bomb和mysql_bomb学习
  15. 基于Lua的游戏服务端框架简介
  16. win7查找计算机图片,如何在 win7电脑上查看 HEIC 照片的内容?
  17. Arduino控制WS2812灯带(灯环)
  18. vue:element ui分页改变pageSize,触发两次回调请求
  19. CSDN学习业务年终总结
  20. 2017 主流手机分辨率与尺寸

热门文章

  1. R语言ggplot2可视化:使用ggpubr包的ggscatter函数可视化散点图、使用xscale函数指定X轴坐标轴度量调整方式、设置x轴坐标为sqrt显示平方根坐标值
  2. 关于Thread.IsBackground属性的理解
  3. android 8.0 恢复出厂设置 最简单的方法
  4. 3.2 数据结构——栈与递归
  5. qml的ListView控件添加ScrollBar
  6. java计算机毕业设计干洗店订单管理系统设计与实现(附源码、数据库)
  7. MySQL JDBC常用知识,封装工具类,时区问题配置,SQL注入问题
  8. 树种统计 (25分)
  9. 基于监控视频的前景目标提取-数学建模
  10. 风控每日一问:FICO(费埃哲)评分系统有什么优缺点?在国内的发展怎么样?