首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
182 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
28
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
5
篇与
的结果
2024-01-11
一张图总结系统设计中的33个黄金法则
一张图总结系统设计中的33个黄金法则系统设计很复杂,它需要设计者有深厚的计算机知识和专业知识,没有一种通用的设计可以适应所有业务场景。尽管如此,还是有一些比较通用的规则可以供你设计系统时参考,也能帮助你在面试中摆脱困境。对于数据密集型系统 — 考虑使用缓存。对于写入量大的系统 — 使用消息队列进行异步处理对于低延迟要求 — 考虑使用缓存和 CDN。需要𝐀原子性、𝐂一致性、𝐈隔离性、𝐃耐用性兼容数据库 — 选择 RDBMS/SQL 数据库。对于非结构化数据——可以选择NoSQL数据库。拥有复杂的数据(视频、图像、文件)——选择 Blob/对象存储。复杂的计算——使用消息队列和缓存。海量数据搜索——考虑搜索索引、尝试搜索引擎。扩展 SQL 数据库 — 实施数据库分片。高可用性、性能和吞吐量 — 使用负载均衡器。全球数据交付——考虑使用 CDN。图形数据(具有节点、边和关系的数据)——利用图形数据库。扩展各种组件——实现水平扩展。高性能数据库查询——使用数据库索引。批量作业处理——考虑批处理和消息队列。服务器负载管理和防止 DOS 攻击 - 使用速率限制器。微服务架构——使用 API 网关。针对单点故障——实施冗余。为了容错性和持久性——实施数据复制。对于用户到用户的快速通信 — 使用 Websockets。分布式系统中的故障检测——实现心跳。数据完整性——使用校验和算法。高效的服务器扩展——一致性哈希。去中心化数据传输——考虑 Gossip 协议。基于位置的功能 — 使用四叉树、Geohash 等。避免特定的技术名称 — 使用通用术语。高可用性和一致性的权衡——最终一致性。对于IP解析和域名查询——DNS。处理网络请求中的大数据——实施分页。缓存删除策略 — 首选 LRU(最近最少使用)缓存。处理流量高峰:实现自动扩展以动态管理资源审计跟踪 — 考虑使用数据湖处理高并发连接 — 使用连接池并考虑使用 Protobuf 来最小化数据
2024年01月11日
26 阅读
0 评论
0 点赞
2023-11-27
哈希分表方法
/* * 根据账号名获取分表 * 函数接受两个参数,$u代表账号名,$n代表分表的数量,默认为256(即账号表分256张表) */ function calc_hash_tbl($u, $n = 256) { $h = sprintf('%u', crc32($u));//使用crc32函数计算账号名的哈希值,并使用sprintf函数将其转换为无符号整数 $h1 = intval($h / $n);//将哈希值除以分表数量$n得到$h1 return $h1 % $n;//将$h1对$n取模得到最终的分表索引值 }这个函数默认输出的值范围是从0到255,因为在最后一行代码中,对$h1取模$n的操作会得到0到$n−1的余数。$accountname = "xx"; $table = "account_info_".calc_hash_tbl($accountname); ...
2023年11月27日
9 阅读
0 评论
0 点赞
2023-08-31
什么是高可用
什么是高可用一、什么是高可用高可用HA(High Availability) 是 分布式系统架构设计 中必须考虑的因素之一,它通常是指, 通过设计减少系统不能提供服务的时间 。假设系统一直能够提供服务,我们说系统的可用性是100%。如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。很多公司的高可用目标是4个9,也就是99.99%,这就意味着,系统的年停机时间为8.76个小时。百度的搜索首页,是业内公认高可用保障非常出色的系统,甚至人们会通过www.baidu.com 能不能访问来判断“网络的连通性”,百度高可用的服务让人留下啦“网络通畅,百度就能访问”,“百度打不开,应该是网络连不上”的印象,这其实是对百度HA最高的褒奖。二、如何保障系统的高可用我们都知道,单点是系统高可用的大敌,单点往往是系统高可用最大的风险和敌人,应该尽量 在系统设计的过程中避免单点 。方法论上, 高可用保证的原则是“集群化”,或者叫“冗余”:只有一个单点,挂了服务会受影响;如果有冗余备份,挂了还有其他backup能够顶上。 保证系统高可用,架构设计的核心准则是:冗余。 有了冗余之后,还不够,每次出现故障需要人工介入恢复势必会增加系统的不可服务实践。所以,又往往是通过“自动故障转移”来实现系统的高可用。 接下来我们看下典型互联网架构中,如何通过冗余+自动故障转移来保证系统的高可用特性。三、常见的互联网分层架构常见互联网分布式架构如上,分为:(1)客户端层: 典型调用方是浏览器browser或者手机应用APP(2)反向代理层: 系统入口,反向代理(3)站点应用层: 实现核心应用逻辑,返回html或者json(4)服务层: 如果实现了服务化,就有这一层(5)数据-缓存层: 缓存加速访问存储(6)数据-数据库层: 数据库固化数据存储整个系统的高可用,又是通过每一层的冗余+自动故障转移来综合实现的。四、分层高可用架构实践【客户端层->反向代理层】的高可用【客户端层】到【反向代理层】的高可用, 是通过反向代理层的冗余来实现的 。以nginx为例:有两台nginx,一台对线上提供服务,另一台冗余以保证高可用,常见的实践是keepalived存活探测,相同virtual IP提供服务。自动故障转移:当nginx挂了的时候,keepalived能够探测到,会自动的进行故障转移,将流量自动迁移到shadow-nginx,由于使用的是相同的virtual IP,这个切换过程对调用方是透明的。【反向代理层->站点层】的高可用【反向代理层】到【站点层】的高可用, 是通过站点层的冗余来实现的 。假设反向代理层是nginx,nginx.conf里能够配置多个web后端,并且nginx能够探测到多个后端的存活性。自动故障转移:当web-server挂了的时候,nginx能够探测到,会自动的进行故障转移,将流量自动迁移到其他的web-server,整个过程由nginx自动完成,对调用方是透明的。【站点层->服务层】的高可用【站点层】到【服务层】的高可用, 是通过服务层的冗余来实现的 。“服务连接池”会建立与下游服务多个连接,每次请求会“随机”选取连接来访问下游服务。自动故障转移:当service挂了的时候,service-connection-pool能够探测到,会自动的进行故障转移,将流量自动迁移到其他的service,整个过程由连接池自动完成,对调用方是透明的(所以说RPC-client中的服务连接池是很重要的基础组件)。【服务层>缓存层】的高可用【服务层】到【缓存层】的高可用, 是通过缓存数据的冗余来实现的 。缓存层的数据冗余又有几种方式 :第一种是利用客户端的封装,service对cache进行双读或者双写。缓存层也可以通过支持主从同步的缓存集群来解决缓存层的高可用问题。以redis为例,redis天然支持主从同步,redis官方也有sentinel哨兵机制,来做redis的存活性检测。自动故障转移:当redis主挂了的时候,sentinel能够探测到,会通知调用方访问新的redis,整个过程由sentinel和redis集群配合完成,对调用方是透明的。说完缓存的高可用,这里要多说一句, 业务对缓存并不一定有“高可用”要求,更多的对缓存的使用场景,是用来“加速数据访问”:把一部分数据放到缓存里,如果缓存挂了或者缓存没有命中,是可以去后端的数据库中再取数据的。 这类允许“cache miss”的业务场景,缓存架构的建议是:将kv缓存封装成服务集群,上游设置一个代理(代理可以用集群冗余的方式保证高可用),代理的后端根据缓存访问的key水平切分成若干个实例,每个实例的访问并不做高可用。缓存实例挂了屏蔽:当有水平切分的实例挂掉时,代理层直接返回cache miss,此时缓存挂掉对调用方也是透明的。key水平切分实例减少,不建议做re-hash,这样容易引发缓存数据的不一致。【服务层>数据库层】的高可用大部分互联网技术,数据库层都用了“主从同步,读写分离”架构,所以数据库层的高可用,又分为“读库高可用”与“写库高可用”两类。【服务层>数据库层“读”】的高可用【服务层】到【数据库读】的高可用,是通过读库的冗余来实现的。 既然冗余了读库,一般来说就至少有2个从库,“数据库连接池”会建立与读库多个连接,每次请求会路由到这些读库。自动故障转移:当读库挂了的时候,db-connection-pool能够探测到,会自动的进行故障转移,将流量自动迁移到其他的读库,整个过程由连接池自动完成,对调用方是透明的(所以说DAO中的数据库连接池是很重要的基础组件)。【服务层>数据库层“写”】的高可用【服务层】到【数据库写】的高可用,是通过写库的冗余来实现的。 以mysql为例,可以设置两个mysql双主同步,一台对线上提供服务,另一台冗余以保证高可用,常见的实践是keepalived存活探测,相同virtual IP提供服务。自动故障转移:当写库挂了的时候,keepalived能够探测到,会自动的进行故障转移,将流量自动迁移到shadow-db-master,由于使用的是相同的virtual IP,这个切换过程对调用方是透明的。五、总结高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。 方法论上,高可用是通过冗余+自动故障转移来实现的。 整个互联网分层系统架构的高可用,又是通过每一层的冗余+自动故障转移来综合实现的,具体的:(1)【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余实现的,常见实践是keepalived + virtual IP自动故障转移(2)【反向代理层】到【站点层】的高可用,是通过站点层的冗余实现的,常见实践是nginx与web-server之间的存活性探测与自动故障转移(3)【站点层】到【服务层】的高可用,是通过服务层的冗余实现的,常见实践是通过service-connection-pool来保证自动故障转移(4)【服务层】到【缓存层】的高可用,是通过缓存数据的冗余实现的,常见实践是缓存客户端双读双写,或者利用缓存集群的主从数据同步与sentinel保活与自动故障转移;更多的业务场景,对缓存没有高可用要求,可以使用缓存服务化来对调用方屏蔽底层复杂性(5)【服务层】到【数据库“读”】的高可用,是通过读库的冗余实现的,常见实践是通过db-connection-pool来保证自动故障转移(6)【服务层】到【数据库“写”】的高可用,是通过写库的冗余实现的,常见实践是keepalived + virtual IP自动故障转移
2023年08月31日
36 阅读
0 评论
0 点赞
2023-08-30
详解10 种常见的软件架构模式
详解10 种常见的软件架构模式
2023年08月30日
14 阅读
0 评论
0 点赞
2023-07-26
数据库架构设计
数据库架构设计数据库架构分类介绍介绍常见的 四种 数据库架构设计模型: 单库架构、分组架构、分片架构和分组分片架构 ,以及每种架构的 使用场景、存在的问题和对应的解决方案 。一、数据模型我们以 “ 用户中心 ” 数据库作为 数据模型 ,讲解数据库架构设计的方法。用户中心是一个 常见业务,主要提供用户注册、登录、查询以及修改等服务,其核心元数据 :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=124 的数据,取模后能够直接定位db-user1。大多数场景下,我们都不会通过 uid 进行的登录,而是使用 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中。 假设分 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 名用户数据,然后再对这些数据进行合并计算,才能得出结果。
2023年07月26日
21 阅读
0 评论
0 点赞