数据库架构设计

news2024/11/25 15:56:54

数据库架构设计

数据库架构分类

介绍

介绍常见的 四种 数据库架构设计模型: 单库架构、分组架构、分片架构和分组分片架构 ,以及每种架构的 使用场景、存在的问题和对应的解决方案

一、数据模型

我们以 “ 用户中心 ” 数据库作为 数据模型 ,讲解数据库架构设计的方法。

用户中心是一个 常见业务,主要提供用户注册、登录、查询以及修改等服务,其核心元数据

User(uid, uname, passwd, sex, age,nickname, …)。

uid 为用户的ID,主键。
uname, passwd, sex, age, nickname 为用户的属性。

二、单库架构

单库(数据库单库架构)

通常在 业务初期 ,数据库无需特别的设计, 单库单表就能满足业务需求 ,这也是 最常见的数据库架构

user-service:用户中心服务,为调用者提供 RPC 接口或者 Restful API。
user-db:使用一个库进行数据存储,提供数据库读写服务。

三、分组架构

分组(数据库分组架构)

1. 什么是分组架构

分组架构是最常见的 一主多从,主从同步,读写分离 的数据库架构。

user-service:用户中心服务。
user-db-M(master):主库,提供数据库写服务。
user-db-S(slave):从库,提供数据库读服务。
主库和从库构成的数据库集群称为“组”。

2. 分组架构有什么特点

同一个组里的数据库集群:

主从之间通过 binlog 进行数据同步。
多个范例数据库结构完全相同。
多个范例存储的数据完全相同, 本质上是将数据进行复制

3. 分组架构解决什么问题

大部分互联网业务读多写少,数据库的读往往最先成为性能瓶颈 。如果希望:

线性提升数据库读性能。
通过消除读写锁冲突提升数据库写性能。
通过冗余从库实现数据的“读高可用”。

此时可以使用分组架构。但是, 在分组架构中,数据库的主库依然是写单点

总之, 分组架构是为了解决 “读写并发量高” 的问题,而实施的架构设计。

四、分片架构

分片(数据库分片架构)

1. 什么是分片架构

分片架构是 水平切分(sharding) 数据库架构。

user-service:用户中心服务。
user-db1:水平切分成2份中的第一份。
user-db2:水平切分成2份中的第二份。

分片后,多个数据库范例也会构成一个数据库集群

2. 水平切分,应该分库还是分表

强烈建议分库,而不是分表 ,因为:

分表依然共用一个数据库文件,仍然有磁盘 IO 的竞争。
分库能够将数据迁移到不同数据库范例,甚至数据库服务器,扩展性更好。

3. 水平切分,用什么算法

常见的水平切分算法 有 “范围法” 和 “哈希法”

范围法

范围分片(数据库水平切分的范围法)

以用户中心的业务主键 uid划分依据将数据水平切分到两个数据库范例上去

user-db1:存储0到1千万的 uid 数据。
user-db2:存储0到2千万的 uid 数据。

哈希法

哈希分片(数据库水平切分的哈希法)

以用户中心的业务主键 uid划分依据将数据水平切分到两个数据库范例上去

user-db1:存储 uid 取模为1的 uid 数据。
user-db2:存储 uid 取模为0的 uid 数据。

这两种方法在互联网都有使用,其中 哈希法使用较为广泛

4. 分片架构有什么特点

同一个分片里的数据库集群:

多个范例之间本身不直接产生联系, 不像主从间有 binlog 同步
多个范例的数据库结构完全相同。
多个范例存储的数据之间没有交集, 所有范例的数据并集构成全局数据

5. 分片架构解决什么问题

大部分互联网 业务的数据量很大单库 容量容易 成为瓶颈 ,此时 通过分片可以

线性提升数据库写性能。
线性提升数据库读性能。
降低单库数据容量。

总之, 分片架构是为了解决 “数据量大” 的问题,而实施的架构设计。

五、分组+分片架构

分组+分片(数据库分组+分片架构)

如果业务读写并发量很高,数据量也很大,通常需要实施分组+分片的数据库架构

通过分片来降低单库的数据量,线性提升数据库的写性能。
通过分组来线性提升数据库的读性能,保证读库的高可用。

六、数据库垂直切分

除了水平切分, 垂直切分也是一类常见的数据库架构设计 ,垂直切分 一般和业务结合比较紧密。

垂直切分

用户中心 为例,可以这么 进行垂直切分

User(uid, uname, passwd, sex, age, …)

User_EX(uid, intro, sign, …)

垂直切分开的表,主键都是 uid。
登录名,密码,性别,年龄等属性放在一个垂直表(库)里。
自我介绍,个人签名等属性放在另一个垂直表(库)里。

1. 如何进行垂直切分

根据业务情况对数据进行垂直切分时,一般 要考虑属性的“长度”和“访问频度”两个因素

长度较短,访问频率较高的放在一起。
长度较长,访问频度较低的放在一起。

这是因为,数据库会以行(row)为单位,将数据加载到内存。在内存容量有限的情况下,长度短且访问频度高的属性,内存可以加载更多的数据,命中率提高,磁盘IO减少,数据库的读性能得到提升。

2. 垂直切分有什么特点

垂直切分 和水平切有相似的地方,又不太相同

多个范例之间也不直接产生联系,即 没有 binlog 同步
多个范例 数据库结构都不相同
多个范例存储的数据之间 至少有一列交集,一般来说是业务主键,所有范例间数据并集构成全局数据

3. 垂直切分解决什么问题

垂直切分即 可以降低单库的数据量,还可以降低磁盘IO从而提升吞吐量,但它与业务结合比较紧密,并不是所有业务都能够进行垂直切分的。

数据库架构设计

介绍

数据库架构设计是 针对海量数据的数据库,通过数据结构、存储形式和部署方式等方面的规划和设计,以解决数据库服务的高并发、高可用、一致性、可扩展以及性能优化等问题。

一、可用性设计

可用性是 指在某个考察时间,系统能够正常运行的概率或时间占有率的期望值。 通常, 我们都要求某个系统具备“高可用性”

所谓 “高可用性”(High Availability) 是指系统经过专门的设计, 从而减少停工时间,保持其服务的高度可用。

数据库的高可用, 通常采用的解决方式为:复制+冗余。

1. 保证 “读” 高可用的方法

数据库主从复制,冗余数据。如图所示:
数据库主从复制,冗余数据

主库用于写数据,从库用于读数据 。在 一主多从 的数据库架构中, 多份从库数据保证了读数据高可用

数据库主从复制 可能带来的问题:主从数据不一致。

2. 保证 “写” 高可用的方法

双主模式,即复制主库,冗余数据。如图所示:
双主模式,复制主库,冗余数据

很多公司 采用单主模式,这是无法保证数据库写的高可用性。

数据库双主模式 可能带来的问题:双主同步 key 冲突,引起数据不一致。

解决方案:

a)方案一:由数据库或者业务层保证 key 在两个主库上不冲突。
b)方案二:“双主” 当 “主从” 用,不做读写分离,当主库挂掉时,启用从库。如图下图:

不做读写分离,当主库挂掉时,启用从库

优点:读写都到主,解决了一致性问题;“双主”当“主从”用,解决了可用性问题

带来的问题:读性能如何扩充?解决方案见下文

二、读性能设计:如何扩展读性能

1. 建立索引

建立太多的索引,会带来以下问题

a)降低了写性能。
b)索引占用内存多了,内存存放的数据就会减少,数据命中率降低,IO次数随之增加。

对于索引过多的问题,有以下 解决方案

不同的库可以建立不同索引。
主库只提供写,不建立索引。

建立索引

online 从库只提供 online 读,建立 online 读索引。

offline 从库只提供 offline 读,建立 offline 读索引。

2. 增加从库

增加从库 会引发主从不一致问题,从库越多,主从时延越长,不一致问题越严重 。这种方案很常见,但我们在生产环境中没有采用。

增加从库

3. 增加缓存

传统缓存的使用方案

a)发生写请求时,先淘汰缓存,再写数据库。
b)发生读请求时,先读缓存,hit则返回,miss则读数据库并将数据入缓存(此时可能旧数据入缓存)。

增加缓存

增加缓存 会带来的两个问题

a)数据复制会引发一致性问题,由于主从延时的存在,可能引发缓存与数据库数据不一致。
b)业务层要关注缓存,无法屏蔽“主+从+缓存”的复杂性。

我们缓存的使用方案:服务+数据+缓存。

服务+数据+缓存

这种方案带来的好处:

a)引入服务层屏蔽“数据库+缓存”
b)不做读写分离,读写都到主的模式,不会引发不一致

三、一致性设计

1. 主从不一致的解决方案

a)方案一:引入中间件

引入中间件

中间件将key上的写路由到主,在一定时间范围内(主从同步完成的经验时间),该key上的读也路由到主库。

b)方案二:读写都到主
读写都到主

我们采用的方案, 不做读写分离,数据不会不一致。

2. 数据库与缓存不一致的解决方案

两次淘汰法:

两次淘汰法

异常的读写时序,或导致旧数据入缓存,一次淘汰不够,要进行二次淘汰。

a)发生写请求时,先淘汰缓存,再写数据库,额外增加一个timer,一定时间(主从同步完成的经验时间)后再次淘汰。
b)发生读请求时,先读缓存,hit则返回,miss则读数据库并将数据入缓存(此时可能旧数据入缓存,但会被二次淘汰淘汰掉,最终不会引发不一致)。

四、扩展性设计
https://blog.58heshihu.com/index.php/archives/258/
或者
https://www.cnblogs.com/wintersun/p/4638176.html

数据库水平切分策略

分库寻址

当数据库的数据量很大时,就需要对库或表进行水平切分 。最常见的水平切分方式, 共有四种策略

索引表法
缓存映射法
计算法
基因法

一、数据模型

我们以 “用户中心” 数据库作为数据模型,讲解数据库的水平切分策略。

用户中心是一个常见业务,主要提供用户注册、登录、查询以及修改等服务,其核心元数据:

User(uid, uname, passwd, sex, age,nickname, …)。
uid 为用户的ID,主键。
uname, passwd, sex, age, nickname 为用户的属性。

用户中心是几乎每一个公司必备的基础服务,用户注册、登录、信息查询与修改都离不开用户中心。

二、数据库水平切分方案

当数据量越来越大时,需要多用户中心进行水平切分最常见 的水平切分方式, 按照 uid 取模分库

按照 uid 取模分库

通过 uid 取模, 将数据分布到多个数据库实例上去,提高服务实例个数,降低单库数据量,以达到扩容的目的。

水平切分之后

水平切分之后

uid 属性 上的查询可以 直接路由到库 ,如上图,假设访问 uid=124 的数据,取模后能够直接定位db-user1。

大多数场景下,我们都不会通过 uid 进行的登录,而是使用 uname 登录。那么 对于 uname 上的查询 ,就不能这么幸运了:

uname 访问,遍历库

uname 上的查询,如上图,假设访问 uname=codebaoku 的数据, 由于不知道数据落在哪个库上,往往需要遍历所有库(扫全库法),当分库数量多起来,性能会显著降低

用 uid 分库,如何高效实现 uname 上的查询 ,是本文将要讨论的问题。

三、数据库水平切分查询策略

1)方案一:索引表法

思路: uid 能直接定位到库,uname 不能直接定位到库,如果通过 uname 能查询到 uid,问题解决。

解决方案

(1)建立一个索引表记录 uname 到 uid 的映射关系;
(2)用 uname 来访问时,先通过索引表查询到 uid,再定位相应的库;
(3) 索引表属性较少,可以容纳非常多数据,一般不需要分库;
(4) 如果数据量过大,可以通过 uname 来分库;

潜在不足:多一次数据库查询,性能下降一倍。

2)方案二:缓存映射法

思路: 访问索引表性能较低,把映射关系放在缓存里性能更佳。

解决方案

(1)uname 查询先到 cache 中查询 uid,再根据 uid 定位数据库;
(2)假设 cache miss,采用扫全库法获取 uname 对应的 uid,放入 cache;
(3)uname 到 uid 的映射关系不会变化, 映射关系一旦放入缓存,不会更改,无需淘汰,缓存命中率超高;
(4) 如果数据量过大,可以通过 name 进行 cache 水平切分;

潜在不足:多一次cache查询。

3)方案三:计算法,uname 生成 uid

思路: 不进行远程查询,由 uname 直接得到 uid。

解决方案

(1)在用户注册时,设计函数 uname 生成 uid,uid=f(uname),按 uid 分库插入数据;
(2)用 uname 来访问时,先通过函数计算出 uid,即 uid=f(uname)再来一遍,由 uid 路由到对应库;

潜在不足:该函数设计需要非常讲究技巧,有 uid 生成冲突风险。

4)方案四:基因法,uname 基因融入 uid

思路: 不能用uname生成uid,可以从uname抽取“基因”,融入uid中。

lkjk5zro.png

假设分 8 库,采用 uid%8 路由 ,潜台词是, uid 的最后 3 个 bit 决定这条数据落在哪个库上,这 3 个 bit 就是所谓的“基因”。

解决方案

(1)在用户注册时,设计函数 uname 生成 3bit 基因,uname_gene=f(uname),如上图粉色部分;
(2)同时,生成 61bit 的全局唯一 id,作为用户的标识,如上图绿色部分;
(3)接着把 3bit 的 uname_gene 也作为 uid 的一部分,如上图屎黄色部分;
(4)生成 64bit 的 uid,由 id 和 uname_gene 拼装而成,并按照 uid 分库插入数据;
(5)用 uname 来访问时,先通过函数由 uname 再次复原 3bit 基因,uname_gene=f(uname),通过 uname_gene%8 直接定位到库。

MySQL分库分表设计

1. 为什么要分库分表

物理服务机的CPU、内存、存储设备、连接数等资源有限 ,某个时段 大量连接同时执行操作 ,会导致数据库在处理上遇到 性能瓶颈

为了解决这个问题,行业先驱门充分发扬了 分而治之 的思想,对大库表进行分割, 然后实施更好的控制和管理,同时使用多台机器的CPU、内存、存储,提供更好的性能。

数据库分库分表有 两种实现方式:垂直拆分 和 水平拆分

2. 垂直拆分(Scale Up 纵向扩展)

垂直拆分分为 垂直分库和垂直分表 ,主要 按功能模块拆分,以解决 各个库或者各个表之间的 资源竞争

比如分为订单库、商品库、用户库…这种方式,多个数据库之间的表结构是不同的。

2.1 垂直分库

垂直分库其实 是一种简单的逻辑分割 。比如我们的 数据库中有商品表Products、还有对订单表Orders,还有积分表Scores。

接下来我们就可以创建三个数据库,一个数据库存放商品,一个数据库存放订单,一个数据库存放积分。

垂直分库有一个优点,就是能够根据业务场景进行孵化 ,比如某一单一场景只用到某2-3张表,基本上应用和数据库可以拆分出来做成相应的服务。

拆分方式如下图所示:
垂直分库

2.2 垂直分表

垂直分表,比较 适用于那种字段比较多的表 ,假设我们一张表有100个字段,我们分析了一下当前业务执行的SQL语句,有20个字段是经常使用的,而另外80个字段使用比较少。

这样我们就可以把20个字段放在主表里面,我们再创建一个辅助表,存放另外80个字段。

当然主表和辅助表都是有主键的,他们通过主键进行关联合并,就可以组合成100个字段的表。

拆分方式如下图所示:
垂直分表

除了这种访问频率的冷热拆分之外,还可以按照字段类型结构来拆分,比如大文本字段单独放在一个表中,与基础字段隔离,提高基础字段的访问效率。

也可以将字段按照功能用途来拆分,比如采购的物料表可以按照基本属性、销售属性、采购属性、生产制造属性、财务会计属性等用途垂直拆分。

垂直拆分的优点

跟随业务进行分割,类似微服务的分治理念,方便解耦之后的管理及扩展。
高并发的场景下,垂直拆分使用多台服务器的 CPU、I/O、内存能提升性能,同时对单机数据库连接数、一些资源限制也得到了提升,能实现冷热数据的分离。
垂直拆分的缺点

部分业务表无法 join,应用层需要很大的改造,只能通过聚合的方式来实现,增加了开发的难度。
单表数据量膨胀的问题依然没有得到有效的解决,分布式事务也是一个难题。

3. 水平拆分(Scale Out 横向扩展)

水平拆分又 分为库内分表和分库分表,来解决单表中数据量增长出现的压力,这些数据库中的表结构完全相同。

3.1 库内分表

先说说库内分表。假设 当我们的 Orders 表达到了 5000万 行记录的时候,非常影响数据库的读写效率 ,怎么办呢? 我们 可以考虑按照订单编号的 order_id 进行 rang分区 ,就是把订单编号在 1-1000万 的放在order1表中,将编号在 1000万-2000万 的放在 order2 中,以此类推,每个表中存放 1000万 行数据。

库内分表

关于水平分表的时机 ,业内的标准不是很统一, 阿里的Java 开发手册的标准是当单表行数超过 500万行或者单表容量超过 2 GB时, 才推荐进行分库分表。 百度的则是1000 W行的进行分表 ,这个是百度的DBA经过测试推算出的结果。

但是这边忽略了单表的字段数和字段类型,如果字段数很多,超过50列,对性能影响也是不小的,我们曾经有个业务,表字段是随着业务的增长而自动扩增的,到了后期,字段越来越多,查询性能也越来越慢。

所以个人觉得不必拘泥于500W 还是1000W,开发人员在使用过程中, 如果压测发现因为数据基数变大而导致执行效率慢下来,就可以开始考虑分表了。

3.2 库内分表的实现策略

目前在 MySql 中支持四种表分区的方式 ,分别为 HASH、RANGE、LIST及KEY ,当然在其它的类型数据库中,分区的实现方式略有不同,但是分区的思想原理是相同,具体如下:

3.2.1 HASH(哈希)

HASH 分区 主要用来确保数据在预先确定数目的分区中平均分布 ,而在 RANGE 和 LIST 分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中,而在HASH 分区中,MySQL 自动完成这些工作,

你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。 示例如下:

drop table if EXISTS  `t_userinfo`; 
CREATE TABLE `t_userinfo` (
`id` int(10) unsigned NOT NULL,
`personcode` varchar(20) DEFAULT NULL,
`personname` varchar(100) DEFAULT NULL,
`depcode` varchar(100) DEFAULT NULL,
`depname` varchar(500) DEFAULT NULL,
`gwcode` int(11) DEFAULT NULL,
`gwname` varchar(200) DEFAULT NULL,
`gravalue` varchar(20) DEFAULT NULL,
`createtime` DateTime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH(YEAR(createtime))
PARTITIONS 10;

上面的例子,使用HASH函数对createtime日期进行HASH运算,并根据这个日期来分区数据,这里共分为 10 个分区。

建表语句上添加一个“PARTITION BY HASH (expr)”子句,其中“expr”是一个返回整数的表达式,它可以是字段类型为 MySQL 整型的一列的名字,也可以是返回非负数的表达式。

另外,可能需要在后面再添加一个“PARTITIONS num”子句,其中 num 是一个非负的整数,它表示表将要被分割成分区的数量。

3.2.2 RANGE(范围)

基于属于一个给定连续区间的列值,把多行分配给同一个分区,这些区间要连续且不能相互重叠 ,使用 VALUES LESS THAN 操作符来进行定义。示例如下:

drop table if EXISTS  `t_userinfo`; 
CREATE TABLE `t_userinfo` (
`id` int(10) unsigned NOT NULL,
`personcode` varchar(20) DEFAULT NULL,
`personname` varchar(100) DEFAULT NULL,
`depcode` varchar(100) DEFAULT NULL,
`depname` varchar(500) DEFAULT NULL,
`gwcode` int(11) DEFAULT NULL,
`gwname` varchar(200) DEFAULT NULL,
`gravalue` varchar(20) DEFAULT NULL,
`createtime` DateTime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE(gwcode) (
PARTITION P0 VALUES LESS THAN(101) ,
PARTITION P1 VALUES LESS THAN(201) ,
PARTITION P2 VALUES LESS THAN(301) ,
PARTITION P3 VALUES LESS THAN MAXVALUE
);

上面的示例,使用了范围RANGE函数对岗位编号进行分区,共分为4个分区,

岗位编号为 1~100 的对应在分区P0中,101~200 的编号在分区P1中,依次类推即可。那么类别编号大于 300,可以使用MAXVALUE来将大于 300 的数据统一存放在分区 P3中即可。

3.2.3 LIST(预定义列表)

类似于按 RANGE 分区,区别在于 LIST 分区 是基于列值匹配一个离散值集合中的某个值来进行选择分区的。 LIST 分区 通过使用“PARTITION BY LIST(expr)”来实现 ,其中 “expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式

然后通过“VALUES IN (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表 。 示例如下:

drop table if EXISTS  `t_userinfo`; 
CREATE TABLE `t_userinfo` (
`id` int(10) unsigned NOT NULL,
`personcode` varchar(20) DEFAULT NULL,
`personname` varchar(100) DEFAULT NULL,
`depcode` varchar(100) DEFAULT NULL,
`depname` varchar(500) DEFAULT NULL,
`gwcode` int(11) DEFAULT NULL,
`gwname` varchar(200) DEFAULT NULL,
`gravalue` varchar(20) DEFAULT NULL,
`createtime` DateTime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY LIST(`gwcode`) (
PARTITION P0 VALUES IN (46,77,89) ,
PARTITION P1 VALUES IN (106,125,177) ,
PARTITION P2 VALUES IN (205,219,289) ,
PARTITION P3 VALUES IN (302,317,458,509,610) 
);

上面的例子,使用了列表匹配LIST函数对员工岗位编号进行分区,共分为4个分区,编号为46,77,89的对应在分区P0中,106,125,177类别在分区P1中,依次类推即可。

不同于RANGE的是,LIST分区的数据必须匹配列表中的岗位编号才能进行分区,所以这种方式只是适合比较区间值确定并少量的情况。

3.2.4 KEY(键值)

类似于按HASH分区,区别在于 KEY分区只支持计算一列或多列,且MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。 示例如下:

drop table if EXISTS  `t_userinfo`; 
CREATE TABLE `t_userinfo` (
`id` int(10) unsigned NOT NULL,
`personcode` varchar(20) DEFAULT NULL,
`personname` varchar(100) DEFAULT NULL,
`depcode` varchar(100) DEFAULT NULL,
`depname` varchar(500) DEFAULT NULL,
`gwcode` int(11) DEFAULT NULL,
`gwname` varchar(200) DEFAULT NULL,
`gravalue` varchar(20) DEFAULT NULL,
`createtime` DateTime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY KEY(gwcode)
PARTITIONS 10;

注意: 此种分区算法目前使用的比较少 ,使用服务器提供的哈希函数有不确定性,对于后期数据统计、整理存在会更复杂,所以我们 更倾向于使用由我们定义表达式的 Has h,大家知道其存在和怎么使用即可。

3.2.5 Composite(复合模式)

Composite 是上面几种模式的组合使用,比如你在 Range的基础上,再进行 Hash 哈希分区。

3.3 分库分表

库内分表解决了单表数据量过大的瓶颈问题,但使用还是同一主机的CPU、IO、内存,另外单库的连接数也有限制,并不能完全的降低系统的压力 。 此时,我们就要考虑另外一种技术叫 分库分表 。分库分表 在库内分表的基础上,将分的表挪动到不同的主机和数据库上。可以充分的使用其他主机的CPU、内存和IO资源 。 拆分方式进一步演进到下面:

分库分表

4. 分库分表存在的问题

4.1 事务问题

在执行分库分表之后, 由于数据存储到了不同的库上,数据库事务管理出现了困难 。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

4.2 跨库跨表的join问题

在执行了分库分表之后, 难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上 ,这时, 表的关联操作将受到限制 ,我们无法 join 位于不同分库的表,也无法 join 分表粒度不同的表,结果 原本一次查询能够完成的业务,可能需要多次查询才能完成。

4.3 额外的数据管理负担和数据运算压力

额外的数据管理负担 ,最显而易见的就是 数据的定位问题和数据的增删改查的重复执行问题 ,这些都可以通过应用程序解决,但必然 引起额外的逻辑运算 ,例如,对于一个记录用户成绩的用户数据表 userTable,业务要求查出成绩最好的 100 位,在进行分表之前,

只需一个 order by 语句就可以搞定,但是在进行分表之后,将需要 n 个 order by 语句,分别查出每一个分表的前 100 名用户数据,然后再对这些数据进行合并计算,才能得出结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/795604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python核心-面向对象-方法相关-补充

class Person:__age 18def __run(self):print("pao")def _Person__run(self):print("xxx")p Person # p._Person__run()print(Person.__dict__) 内置特殊方法 信息格式化操作 # class Person: # def __init__(self, n, a): # self.name n …

深入理解Linux 内核追踪机制

Linux 存在众多 tracing tools,比如 ftrace、perf,他们可用于内核的调试、提高内核的可观测性。众多的工具也意味着繁杂的概念,诸如 tracepoint、trace events、kprobe、eBPF 等,甚至让人搞不清楚他们到底是干什么的。本文尝试理清…

硬件——PCI-E接口

简介 PCI PCI(Peripheral Component Interconnect),外设组件互联标准。 并行方式通信 PCI接口通常是白色的。 PCI接口分为32Bit和64Bit,主流的是32Bit,最大传输速度133MB/s。 适用于网卡、声卡等;现在…

flask中的session介绍

flask中的session介绍 在Flask中,session是一个用于存储特定用户会话数据的字典对象。它在不同请求之间保存数据。它通过在客户端设置一个签名的cookie,将所有的会话数据存储在客户端。以下是如何在Flask应用中使用session的基本步骤: 首先…

Midjourney8种风格极其使用场景(1)

目录 ​编辑 引言 等距动画 场景 分析性绘图 场景 着色书 场景 信息图画 场景 双重曝光 场景 图示性绘画 场景 二维插图 场景 图解式画像 场景 总结: 八种风格箴言: 引言 我相信大家都或多或少玩过Midjourney,但是要形…

Excel“牛人”变现方案参考

有几种方式可以通过Excel技能实现变现: 1. 提供Excel咨询和培训服务:如果你对Excel非常熟悉,你可以提供咨询和培训服务,帮助他人解决Excel使用中的问题或提高他们的Excel技能。 2. 制作和销售Excel模板:你可以根据市…

同步和异步——简单的Demo

一、程序中的同步和异步的区别 在编程中,同步和异步是指代码执行的方式和顺序。 1. 同步(Synchronous): 同步代码按照顺序依次执行,每个操作必须等待前一个操作完成后才能执行。在同步模式下,代码会阻塞&…

部署问题集合(十八)Windows环境下使用两个Tomcat

下载Tomcat Tomcat镜像下载地址:https://mirrors.cnnic.cn/apache/tomcat/进入如下地址:zip的是压缩版,exe是安装版 修改第二个Tomcat配置文件 第一步:编辑conf/server.xml文件,修改三个端口,有些版本改…

信号量SytemV与Posix信号量的介绍与用法

目录 1、信号量介绍 2、信号量SystemV介绍 2.1 信号量函数 2.2 信号量C代码实现 3、信号量Posix介绍 3.1 无名信号量 3.2 有名信号量 1、信号量介绍 信号量是一种计数器,用在多进程、多线程的共享资源访问控制上面,防止多进程、多线程对共享资源的…

win下tomcat部署问题积累

1、win下双击tomcat的start.bat出现闪退 检查环境变量是否配置正确: 1.在已解压的tomcat的bin文件夹下找到startup.bat,右击->编辑。在文件头加入下面两行: SET JAVA_HOMED:\Java\jdk1.6.0_10 (java jdk目录)S…

【C语言进阶】程序环境和预处理

🔥博客主页:小王又困了 📚系列专栏:C语言 🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️ 目录 一、程序的翻译环境和执行环境 二、详解编译和链接 2.1翻译环境 2.2编译的过…

Centos yum install出现Error: Unable to find a match: epel-release的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

MySQL基础扎实——MySQL中有哪几种锁

常见锁举例 在MySQL中,常见的锁包括以下几种: 表级锁(Table-level Lock):表级锁是对整张表进行锁定,可以分为两种类型: 共享锁(Shared Lock):也称为读锁&…

Google Earth Engine谷歌地球引擎提取多波段长期反射率数据后绘制折线图并导出为Excel

本文介绍在谷歌地球引擎GEE中,提取多年遥感影像多个不同波段的反射率数据,在GEE内绘制各波段的长时间序列走势曲线图,并将各波段的反射率数据与其对应的成像日期一起导出为.csv文件的方法。 本文是谷歌地球引擎(Google Earth Engi…

Python图像处理【13】使用PIL执行图像降噪

使用PIL执行图像降噪 0. 前言1. 均值滤波器1.1 均值滤波器原理1.2 使用均值滤波器去除椒盐噪声 2. 高斯滤波器2.1 高斯滤波器原理2.2 使用高斯模糊滤波器去除椒盐噪声 3. 中值滤波器3.1 中值滤波器原理3.2 使用中值滤波器去除椒盐噪声 小结系列链接 0. 前言 在本节中&#xff…

MAXENT模型的生物多样性教程

详情点击链接:基于MAXENT模型的生物多样性生境模拟与保护优先区甄选、自然保护区布局优化及未来气候变化下评估中的应用及论文写作 一:生物多样性保护格局与自然保护区格局优化 1.我国生物多样性格局与分布; 2.我国自然保护区格局与分布&…

Emacs之改造搜索文件fd-dired(基于fd命令)(一百二十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

【【51单片机AD转换模块】】

代码是简单的&#xff0c;板子是坏的&#xff0c;电阻是识别不出来的 main.c #include <REGX52.H> #include "delay.h" #include "LCD1602.h" #include "XPT2046.h"unsigned int ADValue;void main(void) {LCD_Init();LCD_ShowString(1,1…

format格式化输出语法详解

hello&#xff0c;这里是Token_w的文章&#xff0c;主要讲解python的基础学习&#xff0c;希望对大家有所帮助 整理不易&#xff0c;感觉还不错的可以点赞收藏评论支持&#xff0c;感谢&#xff01; 使用 % 操作符对各种类型的数据进行格式化输出&#xff0c;这是早期 Python提…