数据库架构设计

数据库架构设计

数据库架构分类

介绍

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

一、数据模型

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

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

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 名用户数据,然后再对这些数据进行合并计算,才能得出结果。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python与深度学习(八):CNN和fashion_mnist二

目录 1. 说明2. fashion_mnist的CNN模型测试2.1 导入相关库2.2 加载数据和模型2.3 设置保存图片的路径2.4 加载图片2.5 图片预处理2.6 对图片进行预测2.7 显示图片 3. 完整代码和显示结果4. 多张图片进行测试的完整代码以及结果 1. 说明 本篇文章是对上篇文章训练的模型进行测…

Spring(二):更简单的存储与读取 Bean

通过上一章的Spring,我们基本实现了Spring 的读取与存储,但是在操作过程中,读取与存储并没有那么得“简单” 一套流程还是很复杂,所以,本章来介绍更加简单得读取与存储。 在 Spring 中想要更简单的存储和读取对象的核…

Rust vs Go:常用语法对比(八)

题目来自 Golang vs. Rust: Which Programming Language To Choose in 2023?[1] 141. Iterate in sequence over two lists Iterate in sequence over the elements of the list items1 then items2. For each iteration print the element. 依次迭代两个列表 依次迭代列表项1…

【数据结构】实验四:循环链表

实验四 循环链表 一、实验目的与要求 1)熟悉循环链表的类型定义和基本操作; 2)灵活应用循环链表解决具体应用问题。 二、实验内容 题目一:有n个小孩围成一圈,给他们从1开始依次编号,从编号为1的小孩开…

【项目开发】商城 - 三级分类 - 简单笔记

目录标题 后端业务类实体类 前端最终实现效果排序变化批量删除 后端 业务类 // 省略其他简单的CRUDOverridepublic List<CategoryEntity> listWithTree() {// 1、查出所有分类List<CategoryEntity> list baseMapper.selectList(null);// 2. 找出所有的一级分类Li…

数据库监控工具-PIGOSS BSM

PIGOSS BSM 运维监控系统的重要功能之一是数据库监控&#xff0c;它能够帮助数据库管理员(DBA)和系统管理员监控包含Oracle、SQL Server、MySQL、DB2、PostgreSql、MongoDB、达梦、南大通用、人大金仓、神州通用等多种类异构型的数据库环境。PIGOSS BSM通过执行数据库查询来采集…

【Spring Cloud Alibaba】限流--Sentinel

文章目录 概述一、Sentinel 是啥&#xff1f;二、Sentinel 的生态环境三、Sentinel 核心概念3.1、资源3.2、规则 四、Sentinel 限流4.1、单机限流4.1.1、引入依赖4.1.2、定义限流规则4.1.3、定义限流资源4.1.4、运行结果 4.2、控制台限流4.2.1、客户端接入控制台4.2.2、引入依赖…

基于深度学习的CCPD车牌检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于CCPD数据集的高精度车牌检测系统可用于日常生活中检测与定位车牌目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的车牌目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训练数据集…

【Spring篇】初识 Spring IoC 与 DI

目录 一. Spring 是什么 ? 二. 何为 IoC ? 三. 如何理解 Spring IoC ? 四. IoC 与 DI 五 . 总结 一. Spring 是什么 ? 我们通常所说的 Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;有着活跃⽽ 庞⼤…

逻辑回归概述

逻辑回归介绍 1. 逻辑回归的应用场景 逻辑回归(Logistic Regression)是机器学习中的 一种分类模型 ,逻辑回归是一种分类算法,虽然名字中带有回归。由于算法的简单和高效,在实际中应用非常广泛 广告点击率是否为垃圾邮件是否患病信用卡账单是否会违约 逻辑回归就是解决二…

day14 | 100.二叉树的最大深度 111.二叉树的最小深度 222.完全二叉树的节点个数

文章目录 一、二叉树的最大深度二、二叉树的最小深度三、完全二叉树的节点数 一、二叉树的最大深度 100.二叉树的最大深度 因为题给出的高度和深度是一样的&#xff0c;所以求得结果都能过。 class Solution { public:int getHeight(TreeNode *node){if (node nullptr)retu…

Pytest学习教程_基础知识(一)

前言 pytest是一个用于编写和执行Python单元测试的框架。它提供了丰富的功能和灵活性&#xff0c;使得编写和运行测试变得简单而高效。 pytest的一些主要特点和解释如下&#xff1a; 自动发现测试&#xff1a;pytest会自动查找以"test_"开头的文件、类和函数&#x…

腾讯 SpringBoot 高阶笔记,限时开源 48 小时,真香警告

众所周知&#xff0c;SpringBoot 最大的一个优势就是可以进行自动化配置&#xff0c;简化配置&#xff0c;不需要编写太多的 xml 配置文件&#xff1b;基于 Spring 构建&#xff0c;使开发者快速入门&#xff0c;门槛很低&#xff1b;SpringBoot 可以创建独立运行的应用而不需要…

【学习笔记】目标跟踪领域SOTA方法比较

目录 前言方法1 TraDeS:2 FairMOT:3 SMILEtrack:4 ByteTrack: 前言 常用于行人跟踪的多目标跟踪数据集包括&#xff1a;MOT 15/16/17/20、PersonPath22等… 为更好比较现有SOTA算法的检测性能&#xff0c;本博客将针对在各数据集上表现较优的算法模型进行介绍。&#xff08;表…

ip校园广播音柱特点

ip校园广播音柱特点IP校园广播音柱是一种基于IP网络技术的音频播放设备&#xff0c;广泛应用于校园、商业区、公共场所等地方。它可以通过网络将音频信号传输到不同的音柱设备&#xff0c;实现远程控制和集中管理。IP校园广播音柱具备以下特点和功能&#xff1a;1. 网络传输&am…

SSM框架 基础

1.数据库 2.工程 3.pom 4.web.xml 5.spring配置文件头部 6.实体类 7.StudentMapper接口 8. StudentMapper.xml 9.StudentService 10. StudentServiceImpl 11.StudentController 实战 查询所有 StudentMapper StudentService StudentServiceImpl StudentMapper.xml Stude…

效率与质量兼备的6个设计工具!

今天本文为大家推荐的这6个设计工具&#xff0c;将帮助设计师实现高效工作&#xff0c;同时也更好地展示自己的创作力&#xff0c;一起来看看吧&#xff01; 1、即时设计 即时设计是一款国内的设计工具&#xff0c;它为设计师提供了非常多实用的设计功能和精致的设计素材&…

TCP状态转换图

TCP状态转换图 了解TCP状态转换图可以帮助开发人员查找问题. 说明: 上图中粗线表示主动方, 虚线表示被动方, 细线部分表示一些特殊情况, 了解即可, 不必深入研究. 对于建立连接的过程客户端属于主动方, 服务端属于被动接受方(图的上半部分) 而对于关闭(图的下半部分), 服务端…

day41-Verify Account Ui(短信验证码小格子输入效果)

50 天学习 50 个项目 - HTMLCSS and JavaScript day41-Verify Account Ui&#xff08;短信验证码小格子输入效果&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name&qu…

【西安交通大学】:融合传统与创新的学府之旅

【西安交通大学】&#xff1a;融合传统与创新的学府之旅 引言历史与发展学校特色学科优势院系专业校园环境与设施学生生活与社团活动校友荣誉与成就未来发展展望总结&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&…