-
背景自 2016 年以来,为了支撑在线推荐的存储需求而诞生的——字节跳动自研高可用 KV 存储 Abase,逐步发展成支撑包括推荐、广告、搜索、抖音、西瓜、飞书、游戏等公司内几乎所有业务线的 90% 以上的 KV 存储场景,已成为公司内使用最广泛的在线存储系统之一。Abase 作为一款由字节跳动完全自研的高性能、大容量、高可用的 KV 存储系统,支撑了业务不断快速增长的需求。但随着公司的持续发展,业务数量、规模持续快速增长,我们业务对系统也提出了更高的要求,比如:极致高可用:相对于一致性,信息流等业务对可用性要求更高,希望消除宕机选主造成的短时间不可用,和慢节点问题;全球部署:无论是边缘机房还是不同地域的机房,同一个 Abase2 集群的用户都可以就近访问,获取极快的响应延迟;CRDT 支持:确保多写架构下的数据能自动解决冲突问题,达成最终一致;更低成本:通过资源池化解决不同用户资源使用不均衡,造成资源利用率不足问题,降低成本;极致高性能:相同的资源使用下,要求提供尽可能高的写/读吞吐,和较低的访问延迟。适配 IO 设备和 CPU 性能发展速度不匹配趋势,极致高效对 CPU 的使用;兼容 Redis 协议:为了让 Redis 用户可以无障碍的接入 Abase,以满足更大容量的存储需求,我们需要完全兼容 Redis 协议。在此背景下,Abase 团队于 2019 年年底开始孵化第二代 Abase 系统。结合业界的先进架构方案及公司内部实践过程中的积累和思考,团队推出了资源池化,支持多租户、多写、CRDT 的软硬件一体化设计的新一代 NoSQL 数据库 —— Abase2。架构概览数据模型Abase 支持 Redis 的几种主要数据结构与相应接口:String: 支持 Set、Append、IncrBy,是字节线上使用最为广泛的数据模型;Hash/Set:使用率仅次于 String,在部分更新/查询的结构化数据存取场景中广泛使用;ZSet: 广泛应用于榜单拉链等在线业务场景,区别于直接使用 String+Scan 方式进行包装,Abase 在 ZSet 结构中做了大量优化,从设计上避免了大量 ZIncrBy 造成的读性能退化;List/TTLQueue: 队列接口语义使业务在对应场景下非常方便地接入。架构视图图 1:Abase2 整体架构图Abase2 的整体架构主要如上图所示,在用户、管控面、数据面三种视角下主要包含 5 组核心模块。RootServer线上一个集群的规模大约为数千台机器,为管理各个集群,我们研发了 RootServer 这个轻量级组件。顾名思义,RootServer 拥有全集群视角,它可以更好地协调各个集群之间的资源配比,支持租户在不同集群之间的数据迁移,提供容灾视图并合理控制爆炸半径。MetaServerAbase2 是多租户中心化架构,而 MetaServer 则是整个架构的总管理员,它主要包括以下核心功能:管理元信息的逻辑视图:包括 Namespace,Table,Partition,Replica 等状态和配置信息以及之间的关系;管理元信息的物理视图:包括 IDC,Pod,Rack,DataNode,Disk,Core 的分布和 Replica 的位置关系;多租户 QoS 总控,在异构机器的场景下根据各个租户与机器的负载进行副本 Balance 调度;故障检测,节点的生命管理,数据可靠性跟踪,在此基础上进行节点的下线和数据修复。DataNodeDataNode 是数据存储节点。部署时,可以每台机器或者每块盘部署一个 DataNode,为方便隔离磁盘故障,线上实际采用每块盘部署一个 DataNode 的方式。DataNode 的最小资源单位是 CPU Core(后简称 Core),每个 Core 都拥有一个独立的 Busy Polling 协程框架,多个 Core 共享一块盘的空间与 IO 资源。一个 Core 包含多个 Replica,每个 Replica 的请求只会在一个 Core 上 Run-to-Complete,可以有效地避免传统多线程模式中上下文切换带来的性能损耗。Replica 核心模块如下图所示,整个 Partition 为 3 层结构:数据模型层:如上文提到的 String, Hash 等 Redis 生态中的各类数据结构接口。一致性协议层:在多主架构下,多点写入势必会造成数据不一致,Anti-Entropy 一方面会及时合并冲突,另一方面将协调冲突合并后的数据下刷至引擎持久化层并协调 WAL GC。数据引擎层:数据引擎层首先有一层轻量级数据暂存层(或称 Conflict Resolver)用于存储未达成一致的数据;下层为数据数据引擎持久化层,为满足不同用户多样性需求,Abase2 引设计上采用引擎可插拔模式。对于有顺序要求的用户可以采用 RocksDB,TerarkDB 这类 LSM 引擎,对于无顺序要求点查类用户采用延迟更稳定的 LSH 引擎。图 5: Replica 分层架构Client/Proxy/SDKClient 模块是用户侧视角下的核心组件,向上提供各类数据结构的接口,向下一方面通过 MetaSync 与 MetaServer 节点通信获取租户 Partition 的路由信息,另一方面通过路由信息与存储节点 DataNode 进行数据交互。此外,为了进一步提高服务质量,我们在 Client 的 IO 链路上集成了重试、Backup Request、热 Key 承载、流控、鉴权等重要 QoS 功能。结合字节各类编程语言生态丰富的现状,团队基于 Client 封装了 Proxy 组件,对外提供 Redis 协议(RESP2)与 Thrift 协议,用户可根据自身偏好选择接入方式。此外,为了满足对延迟更敏感的重度用户,我们也提供了重型 SDK 来跳过 Proxy 层,它是 Client 的简单封装。DTS (Data Transfer Service)DTS 主导了 Abase 生态系统的发展,在一二代透明迁移、备份回滚、Dump、订阅等诸多业务场景中起到了非常核心的作用,由于篇幅限制,本文不做更多的详细设计叙述。关键技术一致性策略我们知道,分布式系统难以同时满足强一致性、高可用性和正确处理网络故障(CAP )这三种特性,因此系统设计者们不得不做出权衡,以牺牲某些特性来满足系统主要需求和目标。比如大多数数据库系统都采用牺牲极端情况下系统可用性的方式来满足数据更高的一致性和可靠性需求。Abase2 目前支持两种同步协议来支持不同一致性的需求:多主模式(Multi-Leader):相对于数据强一致性,Abase 的大多数使用者们则对系统可用性有着更高的需求,Abase2 主要通过多主技术实现系统高可用目标。在多主模式下,分片的任一副本都可以接受和处理读写请求,以确保分片只要有任一副本存活,即可对外提供服务。同时,为了避免多主架构按序同步带来的一些可用性降低问题, 我们结合了无主架构的优势,在网络分区、进程重启等异常恢复后,并发同步最新数据和老数据。此外,对于既要求写成功的数据要立即读到,又不能容忍主从切换带来的秒级别不可用的用户,我们提供无更新场景下的写后读一致性给用户进行选择。实现方式是通过 Client 配置 Quorum 读写(W+R>N),通常的配置为 W=3,R=3,N=5。单主模式(Leader&Followers):Abase2 支持与一代系统一样的主从模式,并且,半同步适合于对一致性有高要求,但可以忍受一定程度上可用性降低的使用场景。与 MySQL 半同步类似。系统将选择唯一主副本,来处理用户的读写请求,保证至少 2 个副本完成同步后,才会通知用户写入成功。以保证读写请求的强一致性,并在单节点故障后,新的主节点仍然有全量数据。未来也会提供更多的一致性选择,来满足用户的不同需求。读写流程下面我们将详细介绍在多主模型下 Abase 的数据读写流程以及数据最终一致的实现方案。对于读请求,Proxy 首先根据元信息计算出请求对应的分片,再根据地理位置等信息将请求转发到该分片某一个合适的 Replica 上,Replica Coordinator 根据一致性策略查询本地或远端存储引擎后将结果按照冲突解决规则合并后返回给 Proxy,Proxy 根据对应协议将结果组装后返回给用户。对于写请求,Proxy 将请求转发到合适的 Replica 上,Replica Coordinator 将写请求序列化后并发地发送至所有 Replica,并根据一致性策略决定请求成功所需要的最少成功响应数 W。可用性与 W 成反比,W=1 时可获得最大的写可用性。如图 6 所示,假设分片副本数 N=3,当用户写请求到达 Proxy 后,Proxy 根据地理位置等信息将请求转发到分片的某一个副本(Replica B),Replica B 的 Coordinator 负责将请求写入到本地,且并发地将请求 forward 到其他 Replica,当收到成功写入的响应数大于等于用户配置的 W 时(允许不包括本地副本),即可认为请求成功,若在一定时间内(请求超时时间)未满足上述条件,则认为请求失败。在单个副本内,数据首先写入到 WAL 内,保证数据的持久化,然后提交到引擎数据暂存层。引擎在达到一定条件后将缓存数据下刷到持久化存储,然后 WAL 对应数据即可被 GC。一个 Core 内所有 Replica 共享一个 WAL,可以尽量合并不同 Replica 的碎片化提交,减少 IO 次数。引擎层则由 Replica 独占,方便根据不同业务场景对引擎层做精细化配置,同时也便于数据查询、GC 等操作。图 6: 写流程示意图用户可以根据一致性、可用性、可靠性与性能综合考虑 NWR 的配比,W(R)为 1 时可获得最大的写(读)可用性与性能;调大 W/R 则可在数据一致性和可靠性方面取得更好的表现。Anti-Entropy由上述写流程可以看到,当 W<N时,部分副本写入成功即可认为请求成功,而由于网络抖动等原因数据可能并未在所有副本上达成一致状态,我们通过 Anti-Entropy 机制异步地完成数据一致性修复。为了便于检测分片各个 Replica 间的数据差异,我们在 WAL 之上又构建了一层 ReplicaLog(索引),每个 Replica 都对应一个由自己负责的 ReplicaLog,并会在其他 Replica 上创建该 ReplicaLog 的副本,不同 Replica 接收的写请求将写到对应的 ReplicaLog 内,并分配唯一严格递增的 LogID,我们称为 Seqno。每个 Replica 的后台 Anti-Entropy 任务将定期检查自身与其他 Replica 的 ReplicaLog 的进度,以确定自身是否已经拥有全部数据。流程如下:获取自身 ReplicaLog 进度向量[Seqno1, Seqno2..., SeqnoN];与其他 Replica 通信,获取其他 Replica 的进度向量;比对自身与其他 Replica 进度向量,是否有 ReplicaLog 落后于其他 Replica,如果是则进入第 4 步,否则进入第 5 步;向其他 Replica 发起数据同步请求,从其他 Replica 拉取缺少的 ReplicaLog 数据,并提交到引擎层若已就某 ReplicaLog 在 SeqnoX 之前已达成一致,回收 SeqnoX 之前的 ReplicaLog 数据。另外,正常情况下副本间数据能做到秒级达成一致,因此 ReplicaLog 通常只需要构建在内存中,消耗极少的内存,即可达到数据一致的目的。在极端情况下(如网络分区),ReplicaLog 将被 dump 到持久化存储以避免 ReplicaLog 占用过多内存。与 DynamoDB、Cassandra 等通过扫描引擎层构建 merkle tree 来完成一致性检测相比,Abase 通过额外消耗少量内存的方式,能更高效的完成数据一致性检测和修复。冲突解决多点写入带来可用性提升的同时,也带来一个问题,相同数据在不同 Replica 上的写入可能产生冲突,检测并解决冲突是多写系统必须要处理的问题。为了解决冲突,我们将所有写入数据版本化,为每次写入的数据分配一个唯一可比较的版本号,形成一个不可变的数据版本。Abase 基于 Hybrid Logical Clock 算法生成全局唯一时间戳,称为 HLC timestamp,并使用 HLC timestamp 作为数据的版本号,使得不同版本与时间相关联,且可比较。通过业务调研,我们发现在发生数据冲突时,大部分业务希望保留最新写入的数据,部分业务自身也无法判断哪个版本数据更有意义(复杂的上下游关系),反而保留最新版本数据更简洁也更有意义,因此 Abase 决定采用 Last Write Wins 策略来解决写入冲突。在引擎层面,最初我们采用 RocksDB 直接存储多版本数据,将 key 与版本号一起编码,使得相同 key 的版本连续存储在一起;查询时通过 seek 方式找到最新版本返回;同时通过后台版本合并任务和 compaction filter 将过期版本回收。在实践中我们发现,上述方式存在几个问题:多版本数据通常能在短时间内(秒级)决定哪个版本最终有效,而直接将所有版本写入 RocksDB,使得即使已经确定了最终有效数据,也无法及时回收无效的版本数据;同时,使用 seek 查询相比 get 消耗更高,性能更低。需要后台任务扫描所有版本数据完成无效数据的回收,消耗额外的 CPU 和 IO 资源。引擎层与多版本耦合,使得引擎层无法方便地做到插件化,根据业务场景做性能优化。为了解决以上问题,我们把引擎层拆分为数据暂存层与通用引擎层,数据多版本将在暂存层完成冲突解决和合并,只将最终结果写入到底层通用引擎层中。得益于 Multi-Leader 与 Anti-Entropy 机制,在正常情况下,多版本数据能在很小的时间窗口内决定最终有效数据,因此数据暂存层通常只需要将这个时间窗口内的数据缓存在内存中即可。Abase 基于 SkipList 作为数据暂存层的数据结构(实践中直接使用 RocksDB memtable),周期性地将冲突数据合并后写入底层。CRDTs对于幂等类命令如 Set,LWW 能简单有效地解决数据冲突问题,但 Redis String 还需要考虑 Append, Incrby 等非幂等操作的兼容,并且,其它例如 Hash, ZSet 等数据结构则更为复杂。于是,我们引入了 CRDT 支持,实现了 Redis 常见数据结构的 CRDT,包括 String/Hash/Zset/List,并且保持语义完全兼容 Redis。以 IncrBy 为例,由于 IncrBy 与 Set 会产生冲突,我们发现实际上难以通过 State-based 的 CRDT 来解决问题, 故而我们选用 Operation-based 方案,并结合定期合并 Operation 来满足性能要求。为了完全兼容 Redis 语义,我们的做法如下:给所有 Operation 分配全球唯一的 HLC timestamp,作为操作的全排序依据;记录写入的 Operation 日志(上文 ReplicaLog), 每个 key 的最终值等于这些 Operation 日志按照时间戳排序后合并的结果。副本间只要 Operation 日志达成一致,最终状态必然完全一致;为了防止 Operation 日志过多引发的空间和性能问题,我们定期做 Checkpoint,将达成一致的时间戳之前的操作合并成单一结果;为了避免每次查询都需要合并 Operation 日志带来的性能开销,我们结合内存缓存,设计了高效的查询机制,将最终结果缓存在 Cache 中,保证查询过程不需要访问这些 Operation 日志。全球部署结合多主模式,系统可以天然支持全球部署,同时,为了避免网状同步造成的带宽浪费,Abase2 在每个地域都可以设置一个 Main Replicator,由它来主导和其它地域间的数据同步。典型的应用场景有多中心数据同步场景以及边缘计算场景。多租户 QoS为了实现资源池化,避免不同租户间资源独占造成浪费,Abase2 采用大集群多租户的部署模式。同时,为了兼顾不同场景优先级的资源隔离需求,我们在集群内部划分了 3 类资源池,按照不同服务等级进行部署。在资源池内的多租户混部要解决两个关键问题:1、DataNode 的 QoS 保障DataNode 将请求进行分类量化:用户的请求主要归为 3 类:读、写、Scan,三类请求优先级各不相同;不同数据大小的请求会被分别计算其成本,例如一个读请求的数据量每 4KB 会被归一化成 1 个读取单位。所有的用户请求都会通过这两个条件计算出 Normalized Request Cost(NRC)。基于 NRC 我们构建了 Quota 限制加 WFQ 双层结构的服务质量控制模块。图 12:IO 路径上的 QoS 示意图如上图所示,用户请求在抵达租户服务层之前需要迈过两道关卡:Tenant Quota Gate: 如果请求 NRC 已经超过了租户对应的配额,DataNode 将会拒绝该请求,保证 DataNode 不会被打垮;分级 Weight Fair Queue: 根据请求类型分发至各个 WFQ,保证各个租户的请求尽可能地被合理调度。如图 13(2)所示,部分租户突增流量涌入后(蓝绿线)并未对其它租户造成较大影响。流量突增的租户请求延迟受到了一定影响,并且出现请求被 Tenant Quota Gate 拦截的现象,而其它租户的请求调度却基本不受影响,延迟基本保持稳定。2、多租户的负载均衡负载均衡是所有分布式系统都需要的重要能力之一。资源负载实际上有多个维度, 包括磁盘空间、IO 负载, CPU 负载等。我们希望调度策略能高效满足如下目标:同一个租户的 Replica 尽量分散,确保租户 Quota 可快速扩容;不会因为个别慢节点阻塞整体均衡流程;最终让每个机器的各个维度的资源负载百分比接近。负载均衡流程的概要主要分为 3 个步骤:根据近期的 QPS 与磁盘空间使用率的最大值,为每个 Core 构建二维负载向量;计算全局最优二维负载向量,即资源池中所有 Core 负载向量在两个维度上的平均值;将高负载 Core 上的 Replica 调度到低负载 Core 上,使高、低负载的 Core 在执行 Replica 调度后,Core 的负载向量与最优负载向量距离变小。上图是线上负载均衡前后各的负载分布散点图,其中:红点是最优负载向量,横纵分别表示 Core 负载向量的第一和第二维度,每个点对应一个 Core。从图可以看出,各个 Core 的负载向量基本以最优负载向量为中心分布。现状与规划目前 Abase2 正在逐渐完成对第一代 Abase 系统的数据迁移和升级,使用 Abase2 的原生多租户能力,我们预计可提升 50%的资源使用率。通过对异地多活架构的改造,我们将为 Abase 用户提供更加准确、快速的多地域数据同步功能。同时,我们也在为火山引擎上推出 Abase 标准产品做准备,以满足公有云上用户的大容量、低成本 Redis 场景需求。未来的 Abase2 会持续向着下面几个方向努力,我们的追求是技术先进性:在自研多写架构上做更多探索,通过支持 RDMA/io_uring/ZNS SSD/PMEM 等新硬件新技术,让 Abase2 的各项指标更上一个台阶。易用性:建设标准的云化产品,提供 Serverless 服务,和更自动的冷热沉降,更完善的 Redis 协议兼容,更高鲁棒性的 dump/bulkload 等功能。极致稳定:在多租户的 QoS 实践和自动化运维等方面不断追求极致。我们的目标是成为像水和电那样,让用户感觉不到存在的基架产品。结语随着字节跳动的持续发展,业务数量和场景快速增加,业务对 KV 在线存储系统的可用性与性能的要求也越来越高。在此背景下,团队从初期的拿来主义演进到较为成熟与完善的 Abase 一代架构。秉持着追求极致的字节范儿,团队没有止步于此,我们向着更高可用与更高性能的目标继续演进 Abase2。由于篇幅限制,更多的细节、优化将在后续文章中重点分期讲述。
-
前言毫不夸张地说,咱们后端工程师,无论在哪家公司,呆在哪个团队,做哪个系统,遇到的第一个让人头疼的问题绝对是数据库性能问题。如果我们有一套成熟的方法论,能让大家快速、准确的去选择出合适的优化方案,我相信能够快速准备解决咱们日常遇到的80%-90%的性能问题。从解决问题的角度出发,我们得先了解到 问题的原因; 其次我们得有一套 思考、判断问题的流程方式, 让我们合理的站在哪个层面选择方案;最后从众多的方案里面选择一个适合的方案进行解决问题, 找到一个合适的方案的前提,是我们自己对各种方案之间的优缺点、场景有足够的了解, 没有一个方案是完全可以通吃通用的,软件工程没有银弹。下文的我工作多年以来,曾经使用过的八大方案,结合了平常自己学习收集的一些资料,以系统、全面的方式整理成了这篇博文,也希望能让一些有需要的同行在工作上、成长上提供一定的帮助。一、为什么数据库会慢?无论是关系型数据库还是 NoSQL,任何存储系统决定于其查询性能的主要有三种:查找的时间复杂度数据总量高负载而决定于查找时间复杂度主要有两个因素:查找算法存储数据结构无论是哪种存储,数据量越少,自然查询性能就越高,随着数据量增多,资源的消耗(CPU、磁盘读写繁忙)、耗时也会越来越高。从关系型数据库角度出发,索引结构基本固定是 B+Tree,时间复杂度是 O(log n),存储结构是行式存储。因此咱们对于关系数据库能优化的一般只有数据量。而高负载造成原因有高并发请求、复杂查询等,导致 CPU、磁盘繁忙等,而服务器资源不足则会导致慢查询等问题。该类型问题一般会选择集群、数据冗余的方式分担压力。二、应该站在哪个层面思考优化?从上图可见,自顶向下的一共有四层,分别是 硬件、存储系统、存储结构、具体实现。层与层之间是紧密联系的,每一层的上层是该层的载体;因此越往顶层越能决定性能的上限,同时优化的成本也相对会比较高,性价比也随之越低。以最底层的具体实现为例,那么索引的优化的成本应该是最小的,可以说加了索引后无论是 CPU 消耗还是响应时间都是立竿见影降低。然而一个简单的语句,无论如何优化加索引也是有局限的,当在具体实现这层没有任何优化空间的时候就得往上一层【存储结构】思考,思考是否从物理表设计的层面出发优化(如分库分表、压缩数据量等),如果是文档型数据库得思考下文档聚合的结果。如果在存储结构这层优化得没效果,得继续往再上一次进行考虑,是否关系型数据库应该不适合用在现在得业务场景?如果要换存储,那么得换怎样得 NoSQL?所以咱们优化的思路,出于性价比的优先考虑具体实现,实在没有优化空间了再往上一层考虑。当然如果公司有钱,直接使用钞能力,绕过了前面三层,这也是一种便捷的应急处理方式。该篇文章不讨论顶与底的两个层面的优化,主要从存储结构、存储系统中间两层的角度出发进行探讨。三、八大方案总结数据库的优化方案核心本质有三种: 减少数据量、用空间换性能、选择合适的存储系统, 这也对应了开篇讲解的慢的三个原因: 数据总量、高负载、查找的时间复杂度。这里大概解释下收益类型:短期收益,处理成本低,能紧急应对,久了则会有技术债务;长期收益则跟短期收益相反,短期内处理成本高,但是效果能长久使用,扩展性会更好。静态数据意思是,相对改动频率比较低的,也无需过多联表的,where 过滤比较少。动态数据与之相反,更新频率高,通过动态条件筛选过滤。减少数据量减少数据量类型共有四种方案: 数据序列化存储、数据归档、中间表生成、分库分表。就如上面所说的,无论是哪种存储,数据量越少,自然查询性能就越高,随着数据量增多,资源的消耗(CPU、磁盘读写繁忙)、耗时也会越来越高。目前市面上的 NoSQL 基本上都支持分片存储,所以其天然分布式写的能力从数据量上能得到非常的解决方案。而关系型数据库,查找算法与存储结构是可以优化的空间比较少,因此咱们一般思考出发点只有从 如何减少数据量 的这个角度进行选择优化,因此本类型的优化方案主要针对 关系型数据库 进行处理。1)数据归档注意点: 别一次性迁移数量过多,建议低频率多次限量迁移。 像 MySQL 由于删除数据后是不会释放空间的,可以执行命令 OPTIMIZE TABLE 释放存储空间,但是会锁表,如果存储空间还满足,可以不执行。建议优先考虑该方案,主要通过数据库作业把非热点数据迁移到历史表,如果需要查历史数据,可新增业务入口路由到对应的历史表(库)。2)中间表(结果表)中间表(结果表)其实就是利用调度任务把复杂查询的结果跑出来存储到一张额外的物理表,因为这张物理表存放的是通过跑批汇总后的数据,因此可以理解成根据原有的业务进行了高度的数据压缩。以报表为例,如果一个月的源数据有数十万,我们通过调度任务以月的维度生成,那么等于把原有的数据压缩了几十万分之一。接下来的季报和年报可以根据月报*N 来进行统计,以这种方式处理的数据,就算三年、五年甚至十年数据量都可以在接受范围之内,而且可以精确计算得到。那么数据的压缩比率是否越低越好?下面有一段口诀:字段越多,粒度越细,灵活性越高,可以以中间表进行不同业务联表处理。字段越少,粒度越粗,灵活性越低,一般作为结果表查询出来。3)数据序列化存储在 数据库以序列化存储的方式,对于一些不需要结构化存储的业务来说是一种很好减少数据量的方式,特别是对于一些M*N的数据量的业务场景,如果以M作为主表优化,那么就可以把数据量维持最多是M的量级。另外像订单的地址信息,这种业务一般是不需要根据里面的字段检索出来,也比较适合。这 种方案我认为属于一种临时性的优化方案,无论是从序列化后丢失了部份字段的查询能力,还是这方案的可优化性都是有限的。4)分库分表分库分表作为数据库优化的一种非常经典的优化方案,特别是在以前 NoSQL 还不是很成熟的年代,这个方案就如救命草一般的存在。如今也有不少同行也会选择这种优化方式,但是从我角度来看,分库分表是一种优化成本很大的方案。这里我有几个建议:分库分表是实在没有办法的办法,应放到最后选择。优先选择 NoSQL 代替,因为 NoSQL 诞生基本上为了扩展性与高性能。究竟分库还是分表?量大则分表,并发高则分库不考虑扩容,一部做到位。因为技术更新太快了,每 3-5 年一大变。① 拆分方式只要涉及到这个拆,那么无论是微服务也好,分库分表也好,拆分的方式主要分两种: 垂直拆分、水平拆分。垂直拆分更多是从业务角度进行拆分,主要是为了降低业务耦合度 ;此外以 SQL Server 为例,一页是 8KB 存储,如果在一张表里字段越多,一行数据自然占的空间就越大,那么一页数据所存储的行数就自然越少,那么每次查询所需要 IO 则越高因此性能自然也越慢。 因此反之,减少字段也能很好提高性能。 之前我听说某些同行的表有 80 个字段,几百万的数据就开始慢了。水平拆分更多是从 技术角度 进行拆分,拆分后每张表的结构是一模一样的,简而言之就是把原有一张表的数据,通过 技术手段 进行分片到多张表存储,从根本上解决了数据量的问题。② 路由方式进行水平拆分后,根据分区键(sharding key)原来应该在同一张表的数据拆解写到不同的物理表里,那么查询也得根据分区键进行定位到对应的物理表从而把数据给查询出来。路由方式一般有三种 区间范围、Hash、分片映射表 ,每种路由方式都有自己的优点和缺点,可以根据对应的业务场景进行选择。区间范围 根据某个元素的区间的进行拆分,以时间为例子,假如有个业务我们希望以月为单位拆分那么表就会拆分像 table_2022-04,这种对于文档型、ElasticSearch 这类型的 NoSQL 也适用,无论是定位查询,还是日后清理维护都是非常的方便的。那么缺点也明显,会因为业务独特性导致数据不平均,甚至不同区间范围之间的数据量差异很大。Hash 也是一种常用的路由方式,根据 Hash 算法取模以数据量均匀分别存储在物理表里,缺点是对于带分区键的查询依赖特别强。 如果不带分区键就无法定位到具体的物理表导致相关所有表都查询一次,而且在分库的情况下对于 Join、聚合计算、分页等一些 RDBMS 的特性功能还无法使用。一般分区键就一个,假如有时候业务场景得用不是分区键的字段进行查询,那么难道就必须得全部扫描一遍? 其实可以使用 分片映射表 的方式,简单来说就是额外有一张表记录额外字段与分区键的映射关系。举个例子,有张订单表,原本是以 UserID 作为分区键拆分的,现在希望用 OrderID 进行查询,那么得有额外得一张物理表记录了 OrderID 与 UserID 的映射关系。 因此得先查询一次映射表拿到分区键,再根据分区键的值路由到对应的物理表查询出来。可能有些朋友会问,那这映射表是否多一个映射关系就多一张表,还是多个映射关系在同一张表。 我优先建议单独处理,如果说映射表字段过多,那跟不进行水平拆分时的状态其实就是一致的,这又跑回去的老问题。用空间换性能该类型的两个方案都是用来应对高负载的场景,方案有以下两种: 分布式缓存、一主多从。与其说这个方案叫用空间换性能,我认为用空间换资源更加贴切一些。因此两个方案的本质主要通过 数据冗余、集群等 方式分担负载压力。对于关系型数据库而言,因为他的 ACID 特性让它天生不支持写的分布式存储, 但是它依然天然的支持分布式读。 1)分布式缓存缓存层级可以分好几种: 客户端缓存、API 服务本地缓存 和 分布式缓存 ,咱们这次只聊分布式缓存。一般我们选择分布式缓存系统都会优先选择 NoSQL 的键值型数据库,例如 Memcached、Redis,如今 Redis 的数据结构多样性,高性能,易扩展性也逐渐占据了分布式缓存的主导地位。缓存策略也主要有很多种: Cache-Aside、Read/Wirte-Through、Write-Back, 咱们用得比较多的方式主要 Cache-Aside。具体流程可看下图:我相信大家对分布式缓存相对都比较熟悉了,但是我在这里还是有几个注意点希望提醒一下大家:① 避免滥用缓存缓存应该是按需使用,从 28 法则来看,80% 的性能问题由主要的 20% 的功能引起。 滥用缓存的后果会导致维护成本增大,而且有一些数据一致性的问题也不好定位。特别像一些动态条件的查询或者分页,key 的组装是多样化的,量大又不好用 keys 指令去处理,当然我们可以用额外的一个 key 把记录数据的 key 以集合方式存储,删除时候做两次查询,先查 Key 的集合,然后再遍历 Key 集合把对应的内容删除。这一顿操作下来无疑是非常废功夫的,谁弄谁知道。② 避免缓存击穿当缓存没有数据,就得跑去数据库查询出来,这就是 缓存穿透。假如某个时间临界点数据是空的例如周排行榜,穿透过去的无论查找多少次数据库仍然是空,而且该查询消耗 CPU 相对比较高,并发一进来因为缺少了缓存层的对高并发的应对,这个时候就会 因为并发导致数据库资源消耗过高, 这就是 缓存击穿 。数据库资源消耗过高就会导致其他查询超时等问题。该问题的解决方案也简单,对于查询到数据库的空结果也缓存起来,但是给一个相对快过期的时间。有些同行可能又会问,这样不就会造成了数据不一致了么? 一般有数据同步的方案像分布式缓存、后续会说的一主多从、CQRS,只要存在数据同步这几个字,那就意味着会存在数据一致性的问题, 因此如果使用上述方案,对应的业务场景应允许容忍一定的数据不一致。③ 不是所有慢查询都适用 一般来说,慢的查询都意味着比较吃资源的(CPU、磁盘 I/O)。举个例子,假如某个查询功能需要 3 秒时间,串行查询的时候并没什么问题,我们继续假设这功能每秒大概 QPS 为 100,那么在第一次查询结果返回之前,接下来的所有查询都应该穿透到数据库。 也就意味着这几秒时间有 300 个请求到数据库,如果这个时候数据库 CPU 达到了 100%,那么接下来的所有查询都会超时,也就是无法有第一个查询结果缓存起来,从而还是形成了缓存击穿。2)一主多从 常用的分担数据库压力还有一种常用做法,就是读写分离、一主多从。 咱们都是知道关系型数据库天生是不具备分布式分片存储的,也就是不支持分布式写,但是它天然的支持分布式读。一主多从是部署多台从库只读实例,通过冗余主库的数据来分担读请求的压力,路由算法可有代码实现或者中间件解决,具体可以根据团队的运维能力与代码组件支持视情况选择。一主多从在还没找到根治方案前是一个非常好的应急解决方案,特别是在现在云服务的年代,扩展从库是一件非常方便的事情,而且一般情况只需要运维或者 DBA 解决就行,无需开发人员接入。当然这方案也有缺点,因为数据无法分片,所以主从的数据量完全冗余过去,也会导致高的硬件成本。从库也有其上限,从库过多了会主库的多线程同步数据的压力。选择合适的存储系统NoSQL 主要以下五种类型: 键值型、文档型、列型、图型、搜素引擎, 不同的存储系统直接决定了 查找算法、存储数据结构, 也应对了需要解决的不同的业务场景。NoSQL 的出现也解决了关系型数据库之前面临的难题(性能、高并发、扩展性等)。例如,ElasticSearch 的查找算法是倒排索引,可以用来代替关系型数据库的低性能、高消耗的 Like 搜索(全表扫描)。而 Redis 的 Hash 结构决定了时间复杂度为 O(1),还有它的内存存储,结合分片集群存储方式以至于可以支撑数十万 QPS。因此本类型的方案主要有两种: CQRS、替换(选择)存储, 这两种方案的最终本质基本是一样的主要使用合适存储来弥补关系型数据库的缺点,只不过切换过渡的方式会有点不一样。1)CQRSCQS(命令查询分离)指同一个对象中作为查询或者命令的方法,每个方法或者返回的状态,要么改变状态,但不能两者兼备。讲解 CQRS 前得了解 CQS,有些小伙伴看了估计还没不是很清晰,我这里用通俗的话解释: 某个对象的数据访问的方法里,要么只是查询,要么只是写入(更新)。而 CQRS(命令查询职责分离)基于 CQS 的基础上,用物理数据库来写入(更新),而用另外的存储系统来查询数据。因此我们在某些业务场景进行存储架构设计时,可以通过关系型数据库的 ACID 特性进行数据的更新与写入,用 NoSQL 的高性能与扩展性进行数据的查询处理。这样的好处就是关系型数据库和 NoSQL 的优点都可以兼得,同时对于某些业务不适于一刀切的替换存储的也可以有一个平滑的过渡。从代码实现角度来看,不同的存储系统只是调用对应的接口 API,因此 CQRS 的难点主要在于如何进行数据同步。2)数据同步方式一般讨论到数据同步的方式主要是分推和拉:推 指的是由数据变更端通过直接或者间接的方式把数据变更的记录发送到接收端,从而进行数据的一致性处理,这种主动的方式优点是实时性高。拉 指的是接收端定时的轮询数据库检查是否有数据需要进行同步,这种被动的方式从实现角度来看比推简单,因为推是需要数据变更端支持变更日志的推送的。而推的方式又分两种:CDC(变更数据捕获)和领域事件。 对于一些旧的项目来说,某些业务的数据入口非常多,无法完整清晰的梳理清楚,这个时候 CDC 就是一种非常好的方式,只要从最底层数据库层面把变更记录取到就可。对于已经服务化的项目来说领域事件是一种比较舒服的方式,因为 CDC 是需要数据库额外开启功能或者部署额外的中间件,而领域事件则不需要,从代码可读性来看会更高,也比较开发人员的维护思维模式。3)替换(选择)存储系统因为从本质来看该模式与 CQRS 的核心本质是一样的,主要是要对 NoSQL 的优缺点有一个全面认识,这样才能在对应业务场景选择与判断出一个合适的存储系统。这里我像大家介绍一本书马丁.福勒《NoSQL精粹》,这本书我重复看了好几遍,也很好全面介绍各种 NoSQL 优缺点和使用场景。当然替换存储的时候,我这里也有个建议:加入一个中间版本,该版本做好数据同步与业务开关,数据同步要保证全量与增加的处理,随时可以重来,业务开关主要是为了后续版本的更新做的一个临时型的功能,主要避免后续版本更新不顺利或者因为版本更新时导致的数据不一致的情况出现。在跑了一段时间后,验证了两个不同的存储系统数据是一致的后,接下来就可以把数据访问层的底层调用替换了。如此一来就可以平滑的更新切换。总结本文到这里就把八大方案介绍完了,在这里再次提醒一句,每个方案都有属于它的应对场景,咱们只能根据业务场景选择对应的解决方案,没有通吃,没有银弹。这八个方案里,大部分都存在数据同步的情况,只要存在数据同步,无论是一主多从、分布式缓存、CQRS 都好,都会有数据一致性的问题导致,因此这些方案更多适合一些只读的业务场景。当然有些写后既查的场景,可以通过过渡页或者广告页通过用户点击关闭切换页面的方式来缓解数据不一致性的情况。通过这篇文章我相信大家对数据库设计优化有了一个全面的认识。来源:https://www.51cto.com/article/707338.html
-
随着数据库应用市场蓬勃发展,需求和产品多样化,用户选择合适的数据库变得越来越难。如何能够客观全面评价数据库产品,成为研发和使用数据库的重要内容之一,数据库竞争力维度也备受关注。放眼世界,俄罗斯的前车之鉴无疑给中国的数据库人敲响了警钟,在国外数据库产品占市场主流的现状下,人们对数据库国产化、信创自主之路的思考不禁更加深入。国内数据库以及相关从业者该走向何方?国产数据库的替代化之路还有多远呢?这些问题的解决迫在眉睫。视野回到国内,自2019年开始,大量资本进入中国数据库行业,其中2021年来更呈井喷式爆发,融资次数达到了20多次,融资额度超过30亿人民币。当前,中国数据库产业呈现出百花齐放、百家争鸣的局面。据估计,中国数据库市场规模2025年有望接近7百亿元。但国内数据库的发展无论是产业规模,还是产品能力,跟国外主流公司和产品还有不小的差距,企业与产品生态可以从何入手调整升级?数据库未来发展趋势是什么?都是亟需思考的问题。为此,墨天轮行业分析研究团队正式发布首份《中国数据库行业分析报告》,从墨天轮“中国数据库流行度排行”切入,探析当前中国数据库产品发展现状,并对数据库技术基本概念进行全方位梳理,根据国内外市场、政治环境分析厘清中国数据库面临的挑战与未来发展趋势,最后浅析当前中国数据库典型产品,为行业发展、企业选型提供一定的案例支持。一、关系型数据库常踞中国数据库流行度排行榜前十,NoSQL 数据库近期悄然崛起2019年起,墨天轮社区聚焦国产数据库的名录和榜单,通过搜索引擎、趋势指数、三方评测、专利论文、招聘岗位等近50个维度的数据来考察当月国产数据库的流行度,推出中国数据库流行度排榜,每月更新一次,当前已经有205个数据库产品参与排行。排行榜上前十被关系型数据库霸占,当所有目光聚焦在这类数据库时,键值、图形以及时序等类型的 NoSQL 数据库也在悄然崛起,奋力向前。墨天轮排行榜上 NoSQL 数据库排名的 TOP 10,几乎被图形和时序数据库“瓜分”。二、数据库基本概念及分类广义的数据库通常指数据库系统(DBS),其包含数据库与数据库管理系统两部分,向上通过数据库管理系统支撑应用引擎,向下以数据库承接文本、图像、声音等数据源,调动计算、网络、存储等基础资源。(一)关系型数据库、NoSQL数据库与NewSQL数据库关系型数据库的基础是关系数据模型,由关系数据集、完整性约束规则和关系运算三部分构成。按业务负载特征进行分类,关系型数据库可分为交易型数据库(OLTP)、分析型数据库(OLAP)和混合负载数据库(HTAP)。当前部署形态主要分为两种,分别是本地部署和云原生数据库。按照架构分类主要有三类:单机、集群、分布式。其中云原生数据库和分布式数据库受到广泛关注,也成为国内数据库厂商重点突破口和主要发力方向。NoSQL,是对不同于传统的关系型数据库的数据库管理系统的统称。NoSQL数据库的产生就是为了解决大规模数据集合和多重数据种类带来的挑战,特别是大数据应用难题。NewSQL是一类现代关系型的DBMS,旨在为NoSQL的OLTP读写负载提供相同的可扩展性能,同时仍然提供事务的ACID保证。简单来讲,NewSQL就是在传统关系型数据库上集成了NoSQL强大的可扩展性。(二)开源基本概念及标准开源(Open Source)即开放源代码。“开源”最大的特点是开放,用户可以在源代码的基础上进行学习和修改,这些源代码(通常)是免费提供给使用者,但开源系统同样也有版权,且受法律保护。开源许可证(Open Source License)是一种法律许可。通过它,版权拥有人明确允许,用户可以免费地使用、修改、共享版权软件。三、中国数据库发展百花齐放、百家争鸣,但仍面临较大挑战当前,市场上涌现出越来越多的国产数据库厂商,同时也受到大量资本的追捧。2019年开始资本大量进入中国数据库行业,其中2021年来更是井喷式爆发,融资次数达到了20多次,融资额度超过30亿人民币。当前,中国数据库产业呈现出百花齐放、百家争鸣的局面。近期俄乌战局日益焦灼,西方各国相继宣布制裁俄罗斯,这一系列制裁动作让技术人认识到“科技无国界”是个伪命题。或许,这对俄罗斯的数据库行业来说是巨大挑战也是他们发展壮大本国科技产业的时机,对中国的数据库行业来说也是应当奋力发展自主创新的信号。当前,中国数据库产业发展规模规模、产品能力,跟国外主流公司和产品还有不小的差距,在政策体系、标准统一、产品能力提升、关键技术攻关、服务体系建立、企业管理规范等方面问题亟待解决,数据库产业化中的技术生态不完善、生态工具缺失、产品同质化严重、产品替换难度高、知识产权意识薄弱、专业人才缺口大等问题依旧严峻。(报告中进行了详细分析,并对数据库未来发展趋势进行了分析预测,此处不作过多介绍。)四、中国数据库典型案例当前中国数据库产品一共多达205款(数据来自墨天轮社区4月中国数据库流行度排行榜),不论是各类关系型数据库,还是键值型、时序型、图形等NoSQL数据库领域都有典型的产品,历经市场的锤炼与产品技术多次打磨,已经较普遍地应用于电信、金融、政务/公共事业、工业、能源、互联网等行业,其产品性能和场景适用上都得到了一定的提升与拓展。本报告的最后也对诸如PolarDB、OceanBase、MogDB、达梦、人大金仓、GBase、优炫、TcaplusDB、TDengine、Galaxybase等国产数据库典型产品的核心架构、优势特点、解决方案与应用场景等进行了具体介绍,希望以此窥得中国数据库行业发展一隅。来源:墨天轮
-
本篇文章我们主要看看关系型数据库和非关系型数据库的差别 关系型数据库 关系型数据库的优点 容易理解:二维表结构是非常贴近逻辑世界的一个概念,关系模型相对网状、层次等其他模型来说更容易理解使用方便:通用的SQL语言使得操作关系型数据库非常方便易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率 关系型数据库的缺点 网站的用户并发性非常高,往往达到每秒上万次读写请求,对于传统关系型数据库来说,硬盘I/O是一个很大的瓶颈网站每天产生的数据量是巨大的,对于关系型数据库来说,在一张包含海量数据的表中查询,效率是非常低的在基于web的结构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,数据库却没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。当需要对数据库系统进行升级和扩展时,往往需要停机维护和数据迁移。性能欠佳:在关系型数据库中,导致性能欠佳的最主要原因是多表的关联查询,以及复杂的数据分析类型的复杂SQL报表查询。为了保证数据库的ACID特性,必须尽量按照其要求的范式进行设计,关系型数据库中的表都是存储一个格式化的数据结构 非关系型数据库 非关系型数据库的优点 用户可以根据需要去添加自己需要的字段,为了获取用户的不同信息,不像关系型数据库中,要对多表进行关联查询。仅需要根据id取出相应的value就可以完成查询适用于SNS(Social Networking Services)中,例如facebook,微博。系统的升级,功能的增加,往往意味着数据结构巨大变动,这一点关系型数据库难以应付,需要新的结构化数据存储。由于不可能用一种数据结构化存储应付所有的新的需求,因此,非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合 非关系型数据库的缺点 只适合存储一些较为简单的数据,对于需要进行较复杂查询的数据,关系型数据库显的更为合适。不适合持久存储海量数据 非关系型数据库的分类 关系型数据库VS非关系型数据库 成本:Nosql数据库简单易部署,基本都是开源软件,不需要像使用Oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。查询速度:Nosql数据库将数据存储于缓存之中,而且不需要经过SQL层的解析,关系型数据库将数据存储在硬盘中,自然查询速度远不及Nosql数据库。存储数据的格式:Nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。Nosql基于键值对,数据之间没有耦合性,所以非常容易水平扩展。持久存储:Nosql不使用于持久存储,海量数据的持久存储,还是需要关系型数据库数据一致性:非关系型数据库一般强调的是数据最终一致性,不像关系型数据库一样强调数据的强一致性,从非关系型数据库中读到的有可能还是处于一个中间态的数据,非关系型Nosql不提供对事务的处理。 智能云网 智能云网社区是华为专为开发者打造的“学习、开发、验证、交流”一站式支持与服务平台,该平台涵盖多领域知识。目前承载了云园区网络,云广域网络,数通网络开放可编程,超融合数据中心网络,数通网络设备开放社区共五个场景。为了响应广大开发者需求,还提供了开发者交流、API 体验中心、多媒体课件、SDK工具包、开发者工具以及远程实验室共六大工具,让开发者轻松开发。欢迎各位前来体验。>>戳我了解更多<<
-
说明本文以原生Redis5.0为例,分析其与GaussDB(for Redis)(下文简称高斯 Redis)的性能、成本比拼。对比成本比拼原生Redis的数据存放在内存中,高斯 Redis的数据存放在磁盘中,我们比较相同数据容量(192G)的成本开销。即使不考虑原生Redis的内存利用率打折,也可以得出,高斯 Redis的成本是原生Redis的1/3。压缩比拼采用Redis社区开源的压测工具memtier_benchmark写入相同的数据量,比较在两种数据库的空间占用:原生Redis实例高斯 Redis实例set命令压入数据31.23GB7.5GBhset命令压入数据53.62GB12.7GB即用户写入数据量一样的情况下,高斯 Redis的数据压缩比是原生Redis的4倍,因此用户在购买相同数据容量的前提下,高斯 Redis可以存4倍用户数据。性能比拼1. 环境准备Redis Labs推出的多线程压测工具memtier_benchmark,它能够产生各种各样的流量模型。因此采用memtier_benchmark对原生Redis实例和高斯 Redis实例进行性能评测。因为高斯 Redis最小实例的容量是48G,因此购买对标的原生Redis实例,为64G主从规格(实际可用内存“号称51.2G”)。2. 测试结果对比指标高斯 Redis原生Redisset(value=128kb)QPS13.06W11.23W平均时延(毫秒)11.6713.20P993822set(value=1024kb)QPS10.28W8.11W平均时延24.2016.63P998031getQPS39.72W16.76W平均时延3.813.20P99244setget(ratio=1:1,value=128kb)QPS19.93W13.11W平均时延7.423.81P99set:32,get:32set:5.8,get:5.9setget(ratio=1:1,value=1024kb)QPS17.86W9.96W平均时延7.2213.59P99set:30,get:30set:22,get:22hset(value=1024kb)QPS9.88W7.17W平均时延15.1120.91P997236hgetQPS41.35W15.96W平均时延3.539.39P993013hset hget(ratio=1:1,value=1024kb) QPS18.47W10.07W平均时延8.1314.87P99hset:54,hget53hset:24hget24总结1. 结论:与原生Redis实例相比,高斯 Redis在成本、可用容量、吞吐、压缩,都有非常巨大的优势,平均时延两者接近,p99时延有1倍差距。开源Redis高斯 Redis可用空间51.2GB192GB每小时¥27¥9压缩率1倍4倍吞吐比1倍1.5倍平均时延不相上下不相上下P991倍2倍2. 体会:在压测过程中,原生Redis由于容量小、数据无压缩,经常碰到OOM问题。推测下OOM原因,规格为64G的实例,其可用内存并没有宣称的51.2G那么大(怀疑只有50%)。另外,写压测时,流量太大很容易导致原生Redis主从堆积,进而触发RDB快照,导致OOM。而高斯 Redis抗写能力更稳定,且数据强一致存储,无主从堆积问题。可轻松应对业务扩张。
-
GaussDB for Redis提供Info命令查询系统内部的一些统计信息,使用方法为:通过redis-cli登录proxy,执行如下命令即可。info Latency cmd: 打印某个cmd的执行信息,包括执行次数,时间。支持的cmd参数有:“all”,“get”,“lindex”,“lpush”,“sadd”,“set”,“spop”,“xadd”,“xread”,“zadd”,“zrem”info ServerLatency serverName [cmd]: 打印特定server的命令统计信息info SystemResource: 打印系统级参数,内存使用量,cpu使用等info stats: 打印服务的状态,包括接受的连接数,总请求数,流量等info servers: 打印各个server(shard)的信息,包括name,请求数等info LatencyMonitor: 执行监控命令的执行信息info route: 打印路由信息info proxy | server: 打印proxy参数信息,包括version,name等info [all]: 打印上述3,4,5,6,8的信息geminidb_fans 发表于2020-11-30 18:14:06 2020-11-30 18:14:06 最后回复 geminidb_fans 2020-11-30 18:14:061142 0
-
GaussDB NoSQL支持多模融合技术,支持MongoDB、Cassandra、Redis和InfluxDB,请问支持跨模型查询吗?如果支持的话,具体又是怎么实现的呢?
-
近期我们团队进行版本质量加固时,踩到了一些比较隐晦的C++的坑,特总结分享在此,供大家参考。1. string的字符串拼接,导致coredump。`该问题的核心点在于第9行,竟然是可以编译通过,其原因是x+"-",会被转成char*,然后与to_string叠加导致BUG。2. map的迭代器删除map要删除一个元素,通常通过erase()函数来完成,但是要注意,如果我们传入了一个iterator作为erase的参数来删除当前迭代器所指向的元素,删除完成后iterator会失效,产生未定义行为。正确的使用方法应该是接收erase()的返回值,让iterator指向被删除元素的下一个元素或者end()。 for (auto iter = m.begin(); iter != m.end(); iter++) { if (...) iter = m.erase(iter); }但是上述代码仍然有错误,因为如果触发了删除,那么iter再下一轮循环时会指向下下个元素,所以正确的写法应该是: for (auto iter = m.begin(); iter != m.end();) { if (...) { iter = m.erase(iter); continue; } else { iter++; } }3. stringstream的性能问题stringstream的清空是clear之后,置空。stringstream在任何情况下都比snprintf慢。memset是个很慢的函数,宁愿新创建对象。上述测试结果是单线程,改成多线程,同样成立。str += “a”, 比 str =str+ “a” 效率高很多,后者会创建新对象。4. 智能指针(shared_ptr)使用注意1.尽量使用make_shared初始化提高性能std::shared_ptr<Widget> spw(new Widget);需要分配两次内存。每个std::shared_ptr都指向一个控制块,控制块包含被指向对象的引用计数以及其他东西。这个控制块的内存是在std::shared_ptr的构造函数中分配的。因此直接使用new,需要一块内存分配给Widget,还要一块内存分配给控制块auto spw = std::make_shared<Widget>();一次分配就足够了。这是因为std::make_shared申请一个单独的内存块来同时存放Widget对象和控制块。这个优化减少了程序的静态大小,因为代码只包含一次内存分配的调用,并且这会加快代码的执行速度,因为内存只分配了一次。另外,使用std::make_shared消除了一些控制块需要记录的信息,这样潜在地减少了程序的总内存占用。异常安全processWidget(std::shared_ptr<Widget>(new Widget), //潜在的资源泄露 computePriority());上述代码存在内存泄漏的风险,上述代码执行分为3个步骤:1. new Widget 2. shared_ptr构造 3. computePriority编译器不需要必须产生这样顺序的代码,但“new Widget”必须在std::shared_ptr的构造函数被调用前执行。如果编译器产生的顺序代码如下:1. new Widget 2. 执行computePriority。 3. 执行std::shared_ptr的构造函数。如果执行步骤2:computePriority的时候程序出现异常,则在第一步动态分配的Widget就会泄露了,因为它永远不会被存放到在第三步才开始管理它的shared_ptr中2. 父类之类智能指针转换C++中是允许裸指针,因此裸指针之间转换方法同C语言指针强转,智能指针转换不能通过上述方法进行强转,必须通过库提供转换函数进行转换。 C++11的方法是:std::dynamic_pointer_cast;boost中的方法是:boost::dynamic_pointer_cast#include <memory> #include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> #include <iostream> class Base { public: Base(){} virtual ~Base() {} }; class D : public Base { public: D(){} virtual ~D() {} }; int main() { //方式一:先初始化子类智能指针,然后调用dynamic_pointer_cast转换成基类智能指针对象 std::shared_ptr<D> d1 = std::make_shared<D>(); std::shared_ptr<Base> b1 = std::dynamic_pointer_cast<Base>(d1); //方式二:先new子类D的指针,然后调用shared_ptr的构造函数初始化基类智能指针 std::shared_ptr<Base> b2 = shared_ptr<Base>(new D()); return 0; }结论方式一和方式二均能够实现基类智能指针指向子类,但建议采用方式1,通过std::make_shared的方式构造智能指针,然后进行转换;5. map的安全查找办法即map[key]这种写法,就是会创建元素(且不一定初始化),因此在业务逻辑是希望查找的时候,就老老实实用find,不然会有脏数据写入。6. string 的指针构造std::string 的构造方式,除了与其它顺序容器相近的方式之外,提供了三种额外的构造方式:string s(cp, n): s 是cp指向的数组中前n个字符的拷贝,该数组至少应该包含n个字符 string s(s2, pos2):s 是string s2从下标pos2开始的字符的拷贝,若pos2>s2.size(),构造函数的行为未定义 string s(s2, pos2, len2):s 是string s2从下标pos2开始len2个字符的拷贝,若pos2>s2.size(),构造函数的行为未定义。不管len2的值是多少,构造函数至多拷贝s2.size()-pos2个字符std::string 未提供 string(cp, pos2, len2) 这种构造方式,如果代码中使用了该方式,最终会将 cp 指向的数组构造成一个string,然后调用string(s2, pos2, len2)这种构造方式。不提供string(cp, pos2, len2)这种构造方式原因在于:使用这种方式构造容易出现问题,cp是一个指针,通常使用时,能获得其数组长度并检查传入参数;若传入两个参数,容易出现越界。7. 变量初始化变量初始化总是没错的,不管后面是否会修改该值。尤其是int等内建的类型,在类或struct中及容易忽略初始化,使变量成为随机值,产生不可预知的错误。变量请初始化变量请初始化变量请初始化
-
Cassandra包中提供了cqlsh工具,cqlsh指的是CQL(Cassandra查询语言) shell。是一种命令行shell,作为客户端,用于执行Cassandra查询语言。 1.下载和安装 https://geminidb-community.obs.cn-north-1.myhuaweicloud.com/Gemini_Cassandra_tools_cqlsh.zip 2. 启动cqlsh 1)解压Cassandra包后,cqlsh脚本在bin目录下。启动命令如下:cqlsh {host} {port} -u {username} -p {password}2)如果您需要经常连接到特定节点,您可以使用配置文件启动, 将参数统一保存在 {cqlshrc_file} 中,执行如下命令即可启动cqlshcqlsh --cqlshrc {cqlshrc_file} cqlshrc_file中配置如下,更多配置参考附件: cqlshrc.txt[authentication] username = {username} password = {password} [connection] hostname = {host} port = {port}3)想要获取更多的cqlsh的命令和参数,执行 cqlsh --help3. cqlsh常用命令 1)使用HELP命令可以查看所有支持的命令rwuser@cqlsh> HELP Documented shell commands: =========================== CAPTURE CLS COPY DESCRIBE EXPAND LOGIN SERIAL SOURCE UNICODE CLEAR CONSISTENCY DESC EXIT HELP PAGING SHOW TRACING CQL help topics: ================ AGGREGATES CREATE_KEYSPACE DROP_TRIGGER TEXT ALTER_KEYSPACE CREATE_MATERIALIZED_VIEW DROP_TYPE TIME ALTER_MATERIALIZED_VIEW CREATE_ROLE DROP_USER TIMESTAMP ALTER_TABLE CREATE_TABLE FUNCTIONS TRUNCATE ALTER_TYPE CREATE_TRIGGER GRANT TYPES ALTER_USER CREATE_TYPE INSERT UPDATE APPLY CREATE_USER INSERT_JSON USE ASCII DATE INT UUID BATCH DELETE JSON BEGIN DROP_AGGREGATE KEYWORDS BLOB DROP_COLUMNFAMILY LIST_PERMISSIONS BOOLEAN DROP_FUNCTION LIST_ROLES COUNTER DROP_INDEX LIST_USERS CREATE_AGGREGATE DROP_KEYSPACE PERMISSIONS CREATE_COLUMNFAMILY DROP_MATERIALIZED_VIEW REVOKE CREATE_FUNCTION DROP_ROLE SELECT CREATE_INDEX DROP_TABLE SELECT_JSON2)如果需要查看特定命令的帮助。可以使用HELP {COMMAND} 去查看特定命令用法比如查看DESC命令的使用方法 HELP DESC,使用DESC命令可以输出集群的相关信息。比如 DESC CLUSTER 可以显示集群的名称及采用的Partitioner;DESC KEYSPACES 可以显示集群中的所有keyspace,包括系统库和自己创建的keyspace。rwuser@cqlsh> DESC CLUSTER; Cluster: Test Cluster Partitioner: Murmur3Partitioner rwuser@cqlsh> desc KEYSPACES; system_traces system_schema system_auth system system_distributed3)DDL 和 DML语句演示创建keyspace语法如下:CREATE KEYSPACE [IF NOT EXISTS] keyspace_name WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : N } | 'class' : 'NetworkTopologyStrategy', 'dc1_name' : N [, ...]、 } [AND DURABLE_WRITES = true|false] ;创建名为 example的keyspace; replcation选项用于指定副本位置策略为简单策略和所需副本的数量为3。CREATE KEYSPACE IF NOT EXISTS example WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3'};使用 DESC example 命令来验证创建。 使用use example 命令切换到创建的keyspacerwuser@cqlsh> DESCRIBE example; CREATE KEYSPACE example WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3'} AND durable_writes = true; rwuser@cqlsh> use example; rwuser@cqlsh:example>创建表语法如下:CREATE TABLE [IF NOT EXISTS] [keyspace_name.]table_name ( column_definition [, ...] PRIMARY KEY (column_name [, column_name ...]) [WITH table_options | CLUSTERING ORDER BY (clustering_column_name order]) | ID = 'table_hash_tag' | COMPACT STORAGE]创建一个测试表test_table,定义三个列,分别是user_id,age,user_name。user_id是int类型,表示用户的ID号;age也是int类型,代表用户的年龄;user_name 是text类型,表示用户的名称。 主键为 user_id。CREATE TABLE test_table(user_id int, age int, user_name text, PRIMARY KEY(user_id));同样使用DESC test_table 命令验证创建的表rwuser@cqlsh:example> desc test_table ; CREATE TABLE example.test_table ( user_id int PRIMARY KEY, age int, user_name text ) WITH bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} AND comment = '' AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'} AND compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1.0 AND dclocal_read_repair_chance = 0.1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND read_repair_chance = 0.0 AND speculative_retry = '99PERCENTILE';插入数据语法如下:INSERT INTO [keyspace_name.] table_name (column_list) VALUES (column_values) [IF NOT EXISTS] [USING TTL seconds | TIMESTAMP epoch_in_microseconds]查询数据语法如下:SELECT * | select_expression | DISTINCT partition FROM [keyspace_name.] table_name [WHERE partition_value [AND clustering_filters [AND static_filters]]] [ORDER BY PK_column_name ASC|DESC] [LIMIT N] [ALLOW FILTERING]插入4条记录后进行查询。INSERT INTO test_table (user_id, age, user_name) VALUES (1, 17, 'ZhangXiao'); INSERT INTO test_table (user_id, age, user_name) VALUES (2, 16, 'DongXin'); INSERT INTO test_table (user_id, age, user_name) VALUES (3, 17, 'HuFang'); INSERT INTO test_table (user_id, age, user_name) VALUES (4, 15, 'WangGuoHua'); rwuser@cqlsh:example> select * from test_table; user_id | age | user_name ---------+-----+------------ 1 | 17 | ZhangXiao 2 | 16 | DongXin 4 | 15 | WangGuoHua 3 | 17 | HuFang (4 rows)更新表结构语法如下:ALTER TABLE [keyspace_name.] table_name [ALTER column_name TYPE cql_type] [ADD (column_definition_list)] [DROP column_list | COMPACT STORAGE ] [RENAME column_name TO column_name] [WITH table_properties];发现创建的表结构中缺少 性别gender字段,我们现在要修改一下表结构,新增一个text类型的字段。同样可以使用DESC test_table 命令验证新增的gender列.rwuser@cqlsh:example> ALTER TABLE test_table ADD gender text; rwuser@cqlsh:example> desc test_table ; CREATE TABLE example.test_table ( user_id int PRIMARY KEY, age int, gender text, user_name text ) WITH bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} AND comment = '' AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'} AND compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1.0 AND dclocal_read_repair_chance = 0.1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND read_repair_chance = 0.0 AND speculative_retry = '99PERCENTILE';更新数据语法如下:UPDATE [keyspace_name.] table_name [USING TTL time_value | USING TIMESTAMP timestamp_value] SET assignment [, assignment] . . . WHERE row_specification [IF EXISTS | IF condition [AND condition] . . .] ;更新每一条记录,增加gender字段的值。UPDATE example.test_table SET gender = 'female' WHERE user_id = 1; UPDATE example.test_table SET gender = 'male' WHERE user_id = 2; UPDATE example.test_table SET gender = 'male' WHERE user_id = 3; UPDATE example.test_table SET gender = 'female' WHERE user_id = 4;删除数据语法如下:DELETE [column_name (term)][, ...] FROM [keyspace_name.] table_name [USING TIMESTAMP timestamp_value] WHERE PK_column_conditions [IF EXISTS | IF static_column_conditions]需要删除user_id 为4的用户的age列rwuser@cqlsh:example> DELETE age FROM example.test_table WHERE user_id=4; rwuser@cqlsh:example> select * from example.test_table WHERE user_id = 4; user_id | age | gender | user_name ---------+------+--------+------------ 4 | null | female | WangGuoHua (1 rows)删除user_id 为3的 整条记录rwuser@cqlsh:example> DELETE FROM example.test_table WHERE user_id=3; rwuser@cqlsh:example> select * from example.test_table WHERE user_id = 3; user_id | age | gender | user_name ---------+-----+--------+----------- (0 rows)清空表数据语法如下:TRUNCATE [TABLE] [keyspace_name.table_name]清空表中的所有记录rwuser@cqlsh:example> truncate example.test_table ; rwuser@cqlsh:example> select * from example.test_table; user_id | age | gender | user_name ---------+-----+--------+----------- (0 rows)删除表语法如下:DROP TABLE [IF EXISTS] keyspace_name.table_name删除创建的 test_table表rwuser@cqlsh:example> DROP TABLE example.test_table ; rwuser@cqlsh:example> desc example.test_table ; 'test_table' not found in keyspace 'example'删除keyspace语法如下:DROP KEYSPACE [IF EXISTS] keyspace_name删除创建的keyspace examplerwuser@cqlsh:example> drop KEYSPACE example; rwuser@cqlsh:example> desc example; Keyspace 'example' not found.geminidb_fans 发表于2020-09-30 12:55:35 2020-09-30 12:55:35 最后回复 geminidb_fans 2020-09-30 12:55:354503 0
-
NoSQL,泛指非关系型的数据库。NoSQL数据库的产生是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。可以认为,NoSQL是对传统关系型数据库的补充与扩展,其本身也得到了非常迅速的发展。——————————————【微话题活动】———————————————今天我们邀请到华为数据库产品专家-青松和大家讨论一下:【你对华为云NoSQL产品了解多少呢?】活动时间2020年8月20日-2020年9月3日参与方式直接在本帖回复关于以下问题的理解或评论:1、您觉得NoSQL的发展前景如何?2、您觉得华为云NoSQL产品是否满足了您的需求?3、您觉得华为云NoSQL产品与友商的差距在哪儿?本期奖品A.回帖参与本次讨论,回复与话题相关的原创内容,即可获得三合一数据线1个(数量有限,先到先得)。B.华为数据库产品专家-青松评选出3名优质评论奖,各送出美的热水壶1个。(两样奖品不可同时获得,奖品于活动结束发放) 评奖标准回复话题数量和内容质量 优质评论奖3名 参与奖眼尖的同学可能已发现,华为云的数据库产品中三款NoSQL产品已经上架,还带着醒目的“NEW”标识。它们分别是 :云数据库 GaussDB(for Cassandra)【https://www.huaweicloud.com/product/gaussdbforcassandra.html】云数据库 GaussDB(for Mongo)【https://www.huaweicloud.com/product/gaussdbformongo.html】云数据库 GaussDB(for Redis)【https://www.huaweicloud.com/product/gaussdbforredis.html】
-
1.摘要GaussDB(for Mongo)是华为云自主研发兼容MongoDB4.0接口的文档数据库。基于共享存储的存算分离架构,对于传统MongoDB社区版有如下优势:秒级添加Secondary节点(相比社区版Mongo小时级添加Secondary节点)基于WAL复制, Secondary节点无写IO,从根本上解决社区版Seconary节点Oplog脱节问题Primary/Seconary无任何IO交互,Secondary节点个数理论无上限, 支持百万OPS的读事务能力LSMTree Compaction 计算/IO卸载到Compaction统一调度池,集中管理,不浪费用户读写IO基于共享存储,Chunk分裂/迁移动作不引起真实IO,只更新路由元数据,秒级分裂/均衡2.GaussDB(for Mongo)技术架构1)容忍更多Shard宕机与社区版MongoDB的`Share-Nothing`模式不同的是,GaussDB(for Mongo)采用`Share-Storage`架构,计算存储分离。集群模式下,N个Shard节点,可以容忍N-1个Shard宕机。某个Shard节点宕机后,其负责的数据由于存在于共享的存储池中,因此不需要物理拷贝数据,只需要修改元数据路由信息,即可被其他分片节点接管。2)更快的分裂与均衡能力此外,由于Chunk数据在存储池中,Chunk的分裂与均衡不涉及到数据拷贝,可以做到分钟级分裂与扩容,分裂与扩容对用户的影响也远比社区版MongoDB小。3)百万级读OPS能力GaussDB(for Mongo)副本集模式下,Primary/Secondary节点之间共享同一份数据库文件。Secondary节点只复制Primary节点的WriteAheadLog以及LSMTree的结构变更信息,并应用到内存中。Secondary节点没有LSMTree的Compaction和Flush任务,因此对用户的读业务影响很小。此外,由于`Share-Storage`的架构优势,添加Secondary节点并不需要拷贝数据,添加Secondary节点的动作可以秒级完成。而Primary/Secondary之间只传递元数据变更,不传递WriteAheadLog,因此Secondary节点的个数即使变多,也不影响Primary节点的写性能。Secondary节点可以水平扩展,支撑百万级的读OPS。4)主节点IO卸载LSMTree的写压力来源于三部分:用户的业务写入导致的Memtable Flush后台SST文件CompactionWAL的持续写入根据线上业务的实际测算,三者的IO资源消耗占比为: 1:10:1。后台的SST文件Compaction占了绝大部分IO带宽,通过将Compaction任务集中化管理,从计算池卸载到存储池,进一步减少了用户计算节点的CPU和IO资源消耗。5)GaussDB(for Mongo) 只读节点设计传统社区版MongoDB副本集基于Oplog做数据复制,只读节点需要镜像主节点的所有写IO操作。GaussDB(for Mongo) 的只读节点和主节点共享同一份底层数据库文件(LSMTree的SST文件),只读节点并不自己生成SST文件。随着业务数据的写入,Compaction的不断执行,LSMTree的当前版本(包含哪些SST文件)不断更新,LSMTree的元数据更新(增删SST文件的记录)被同步到只读节点执行。RocksDB中,数据的变更被持久化到WAL里,元数据的变更(增删文件的操作, 叫做VersionEdit)被持久化到Mainifest里。RocksDB的数据和元数据是分开的,WAL流和VersionEdit流是并行的,没有严格的先后顺序。为了保证只读节点和主节点完全一致的事件回放顺序,WAL和VersionEdit流必须要合并成一个流,在双流合并后,通过LSN就可以为每个事件(WAL的写操作/VersionEdit)定序。基于WAL+VersionEdit复制,而不基于Oplog复制共享文件(sst/wal)的生命周期管理由主节点负责sst文件和wal的文件的生命周期由主节点负责。RocksDB中,SST文件通过层级的引用计数来维持不被删除。如下图,RocksDB的每个游标会维持SuperVersion,如下图中的S0,S1,S2。每个SuperVersion会引用一个Version,一个Version代表LSMTree在不断变形(通过增删SST文件变形)的过程中,某个时间点的形状,最新的Version就代表LSMTree当前的形状。在GaussDB(for Mongo)中,主节点会记录所有只读节点在使用的Version,并为这些Version增加引用计数从而维持SST文件的生命周期。对于WAL,主节点会记录所有只读节点中最老的LSN(`oldestLsn`),最老的LSN来自于复制最慢的只读节点。并删除比oldestLsn还旧的WAL文件。元数据变更通知,无论是oldestLsn还是只读节点的当前在用的活跃的Version,都需要及时推进,这些元数据的变更是通过主从节点的定期心跳上报到主节点上的。主节点利用心跳数据对垃圾版本与WAL做清理。如下图所示,在经历一次心跳后,主节点发现Secondary0的Version0和Secondary1的Version0不再使用。删除这两个Version后,SST0的引用计数为0,表示SST0可以被删除。OldestLsn也从100推进到了250,可以清理掉250之前的WAL。只读节点的memtable的释放:主节点的Memtable不会实时Flush为SST文件。如果只读节点不处理主节点的Memtable的话,只读节点的数据就不是实时的,且存在数据一致性问题。只读节点通过回放WAL到内存的Memtable中,来覆盖SST文件与主节点的Memtable的Gap。上文介绍了只读节点是不往共享存储写入数据的, 所以只读节点上的 Memtable 最后的结局一定是被丢弃掉。但什么时候丢弃这个 Memtable 就是一个问题。过早的丢弃,会造成SST文件与Memtable之间的数据不连续,存在Gap,过晚的丢弃会造成内存的浪费。只有当只读节点识别到SST的数据已经完全能够Cover某个Memtable时,这个Memtable才可以被丢弃。GaussDB(for Mongo)的只读节点在每次应用VersionEdit后,检查所有SST中的最大的LSN与Memtable的最小的LSN的关系,来决定是否要丢弃某个Memtable。内存元数据的反向更新:传统的复制,数据流从Oplog来,走一遍完整的数据库Server层CRUD接口,再落到引擎层。这种逻辑和主节点上业务的写入逻辑是一致的,因此Server层的一些内存元数据结构,在这个过程中就自然而然的得到更新了。但是当采用基于WAL的复制后,整个WritePath并不经过只读节点的Server层。因此Server层的内存元数据更新,就是一个很大的挑战。在这里,只读节点对每一条WAL做分析,如果WAL的内容会影响Mongo内存元数据,就会reload对应的元数据模块。3.总结GaussDB(for Mongo) 基于Share-Storage架构,实现秒级Chunk分裂与均衡,对业务影响更小,水平扩展速度更快,能容忍更多节点宕机。只读节点功能,实现了一份数据多计算节点共用的功能。极大的提升了存储的利用效率,提高了计算节点的读取数据能力。为了让副本节点具有持续的读扩展能力,整个只读方案采用元数据的同步模式,在不降低主节点负载的情况下,极大的提升了整个系统的读数据的处理能力。为3节点,5节点,乃至于15节点以上的副本集的工作提供了可能。
-
1.什么是二级索引对于Cassandra来说,Primary Key 就是一级索引,查询时候可以指定对应Primary Key 就可以直接检索到对应记录。如果需要根据非Primary Key 列进行查询呢? 这个时候就需要用到二级索引。二级索引首先要了解是辅助索引,它的目的就是为了先找到一级索引,然后根据一级索引再找到对应记录。2.二级索引原理二级索引的实现其实就是创建一张隐式的表,该表的Primary Key 是创建索引的列,该表的值是原表的Primary Key.假设我们有一张playlists 表,表结构如下:1 CREATE TABLE playlists ( 2 id int, 3 song_id int, 4 song_order int, 5 album text, 6 artist text, 7 title text, 8 PRIMARY KEY (id, song_id));查询结果如下:现在我们如果对 artist字段建立索引,之前提到的索引隐式的表的结构就是下面这样:其中 artist 就是索引表的 primary key ,列id 是索引表的普通列( 其实就是原表的primary key )3.什么场景下不建议使用索引1.列重复值过多 比如上例中的表有一亿条数据,每首歌的artist 都是一样的,这种情况不建议索引artist列2.counter列不能进行索引3.频繁更新或者删除的列4.索引使用方法索引使用十分简单,执行下面sql就可以创建索引CREATE INDEX artist_names ON playlists( artist );注意:如果原表中有大量数据,会涉及到索引数据的重建,只有当所以数据重建完成以后才能进行索引的查询,查看索引是否重建的方法可以通过查询系统的IndexInfo ,如果有我们创建的索引的名字,则表示索引数据已经重建完成,如下图:索引查询也十分简单,查询时候直接指定索引列即可:每张表可以创建多个索引,索引会影响写入性能,这点需要注意geminidb_fans 发表于2020-07-31 18:11:50 2020-07-31 18:11:50 最后回复 geminidb_fans 2020-07-31 18:11:505704 0
-
在第2期数据库发展历史介绍中,我们介绍到数据库经历了人工管理阶段、文件系统阶段和数据库系统阶段,最终于上世纪70年代末发展进入了关系型数据库。时至今日,关系型数据库在风起云涌的数据库江湖依然占有重要地位。在第3期的介绍中我们说到,SQL(Structured Query Language)及SQL标准围绕的是关系数据库。随着计算机技术的发展,数据量的爆炸性增长,关系型数据库在海量数据的存储及并发读写上遇到了瓶颈,因此近些年涌现了NoSQL和NewSQL数据库。那么,什么是NoSQL数据库,什么是NewSQL数据库呢?这一期我们来尝试聊一聊。SQL,NoSQL及NewSQL浅析——关于什么是NoSQL及NewSQL,小编尝试查阅了一些材料。其中这篇CSDN博客中对OldSQL、NoSQL和NewSQL的对比做了较清晰的说明。以下内容主要转载自此博客,小编只是基于自己的理解,稍做了些文字整理。信息量有些大,大家耐心阅读。一、关系数据库SQL,也叫OldSQL关系数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。简单来说,关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。(编者注:有关什么是数据库关系模型,及如何构建这种关系模型即数据库建模,在很多数据库教材和书籍中都能了解到~~)关系模型的常用概念关系:可以理解为一张二维表,每个关系都具有一个关系名,就是通常说的表名。 ——编者注:这也是为什么数据库使用过程中,会在一些返回信息中经常看到Relation身影的原因~~元组:可以理解为二维表中的一行,在数据库中经常被称为记录。 ——编者注:Tuple、元组、记录,他们原来是同一身份(数据库表中的一行)的不同马甲。属性:可以理解为二维表中的一列,在数据库中经常被称为字段。 ——编者注:是的,列、字段、属性是数据库表中一列的不同马甲。 域:属性的取值范围,也就是数据库中某一列的取值限制。 关键字:一组可以唯一标识元组的属性,数据库中常称为主键,由一个或多个列组成。 关系模式:指对关系的描述。其格式为:关系名(属性1,属性2, … … ,属性N),在数据库中称为表结构。 关系型数据库的优点: 容易理解:二维表结构是非常贴近逻辑世界的一个概念,关系模型相对网状、层次等其他模型来说更容易理解。 使用方便:通用的SQL语言使得操作关系型数据库非常方便。易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率。关系型数据库瓶颈高并发读写需求:用户并发性非常高,往往达到每秒上万次读写请求,对于传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。 海量数据的高效率读写: 网站每天产生的数据量是巨大的,对于关系型数据库来说,在一张包含海量数据的表中查询,效率是非常低。高扩展性和可用性:在基于web的结构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,数据库却没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移。在关系型数据库中,导致性能欠佳的最主要原因是多表的关联查询,以及复杂的数据分析类型的复杂SQL报表查询。为了保证数据库的ACID特性,我们必须尽量按照其要求的范式进行设计,关系型数据库中的表都是存储一个格式化的数据结构。每个元组字段的组成都是一样,即使不是每个元组都需要所有的字段, 但数据库会为每个元组分配所有的字段,这样的结构可以便于表与表之间进行链接等操作,但从另一个角度来说它也是关系型数据库性能瓶颈的一个因素。 ——编者注:看来接下来的一期,我们有必要介绍ACID及数据库范式了~~二、NoSQL近些年蓬勃发展的NoSQL系统最初是宣称不再需要SQL的,但后来也不得不修正为Not Only SQL,意即”不仅仅是SQL”,来拥抱SQL。NoSQL指的是非关系型的数据库。是对不同于传统的关系型数据库的数据库管理系统的统称。NoSQL用于超大规模数据的存储。键值(Key-Value)存储数据库这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。举例如:** Cabinet/Tyrant, Redis, Voldemort, Oracle BDB。列存储数据库。这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:Cassandra, HBase, Riak。文档型数据库文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。如:CouchDB, MongoDb. 国内也有文档型数据库SequoiaDB,已经开源。图形(Graph)数据库: 图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。如:Neo4J, InfoGrid, Infinite Graph。因此,我们总结NoSQL数据库在以下的这几种情况下比较适用: 数据模型比较简单;需要灵活性更强的IT系统;对数据库性能要求较高;不需要高度的数据一致性;对于给定key,比较容易映射复杂值的环境。NoSQL的优势易扩展NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。大数据量,高性能NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一种大粒度的Cache,在针对web2.0的交互频繁的应用,Cache性能不高。而NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多了。同时NoSQL 数据库针对特定的数据模型(如文档、键值和图形)和访问模式进行了优化,这与尝试使用关系数据库完成类似功能相比可实现更高的性能。灵活的数据模型NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。高可用NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。NOSQL的缺点以及一些挑战已有key-value数据库产品大多是面向特定应用自治构建的,缺乏通用性;已有产品支持的功能有限(不支持事务特性),导致其应用具有一定的局限性;已有一些研究成果和改进的NoSQL数据存储系统,但它们都是针对不同应用需求而提出的相应解决方案,如支持组内事务特性、弹性事务等,很少从全局考虑系统的通用性,也没有形成系列化的研究成果;缺乏类似关系数据库所具有的强有力的理论(如armstrong公理系统)、技术(如成熟的基于启发式的优化策略、两段封锁协议等)、标准规范(如SQL语言)的支持。目前,HBase数据库是安全特性最完善的NoSQL数据库产品之一,而其他的NoSQL数据库多数没有提供内建的安全机制,但随着NoSQL的发展,越来越多的人开始意识到安全的重要,部分NoSQL产品逐渐开始提供一些安全方面的支持。BASE原则以及CAP定理BASE原则:Basically Availble –基本可用Soft-state –软状态/柔性事务。 “Soft state” 可以理解为”无连接”的, 而 “Hard state” 是”面向连接”的Eventual Consistency – 最终一致性, 也是是 ACID 的最终目的。CAP定理:在计算机科学中, CAP定理(CAP theorem), 又被称作 布鲁尔定理(Brewer’s theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:一致性(Consistency) (所有节点在同一时间具有相同的数据)可用性(Availability) (保证每个请求不管成功或者失败都有响应)分区容忍性(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。详细请参见总结NoSQL数据库的出现,弥补了关系数据(比如MySQL)在某些方面的不足,在某些方面能极大的节省开发成本和维护成本。 OldSQL和NoSQL都有各自的特点和使用的应用场景,两者的紧密结合将会给web2.0的数据库发展带来新的思路。让关系数据库关注在关系上,NoSQL关注在存储上。三、NewSQLNewSQL 是一种新型关系数据库管理系统,是对各种新的可扩展/高性能数据库的简称,这类数据库不仅具有NoSQL对海量数据的存储管理能力,试图为联机事务处理(OLTP)读写工作负载提供与NoSQL系统相同的可伸缩性能,还保持了传统数据库支持ACID和SQL等特性。因此,可以将典型NewSQL技术理解成分布式关系型数据库,能够支持分布式事务是一个基本前提。NoSQL与NewSQL在技术栈上有很多重叠,但在是否支持关系型模型及对复杂事务的支持力度上是存在明显区别的。NewSQL分类NewSQL系统虽然在的内部结构变化很大,但是它们有两个显着的共同特点:(1)它们都支持关系数据模型,(2) 它们都使用SQL作为其主要的接口。目前NewSQL系统大致分三类:新架构:这一类型的NewSQL系统是全新的数据库平台,它们均采取了不同的设计方法。数据库工作在一个分布式集群的节点上,其中每个节点拥有一个数据子集。 SQL查询被分成查询片段发送给自己所在的数据的节点上执行。这些数据库可以通过添加额外的节点来线性扩展。现有的这类数据库有: Google Spanner, VoltDB, Clustrix, NuoDB.数据库系统通常有一个单一的主节点的数据源。它们有一组节点用来做事务处理,这些节点接到特定的SQL查询后,会把它所需的所有数据从主节点上取回来后执行SQL查询,再返回结果。SQL引擎:这一类是高度优化的SQL存储引擎。这些系统提供了MySQL相同的编程接口,但扩展性比内置的引擎InnoDB更好。这类数据库系统有:TokuDB, MemSQL。透明分片:这一类系统提供了分片的中间件层,数据库自动分割在多个节点运行。这类数据库包括:ScaleBase,dbShards, Scalearc。---------------------------------------------------------------------我是华丽丽的分割线---------------------------------------------------------------------那在数据库选型时,如何在 SQL、NewSQL、NoSQL 之间进行取舍呢?下面两张图能很形象地帮助到你。华为数据库是后起之秀,大家自行将其对号入座到图中哈。图片摘自:https://blog.csdn.net/defonds/article/details/48471087图1 SQL vs. NewSQL vs. NoSQL图2Do I Need SQL or Hadoop?
-
本期小云妹给大家介绍个数据库的新成员——云数据库GaussDB NoSQL云数据库GaussDB(for Cassandra)介绍页入口:https://www.huaweicloud.com/product/gaussdbforcassandra.html云数据库GaussDB(for Mongo) 介绍页入口:https://www.huaweicloud.com/product/gaussdbformongo.html云数据库GaussDB NoSQL成长地图入口:https://support.huaweicloud.com/nosql/index.html
-
多模NoSQL服务GeminiDB盛大公测 兼容Cassandra,MongoDB等接口,超高性价比是物联网TB级数据持久化应用场景首选 立即体验公测福利华为AI音响mini,华为移动电源,华为云数据库纪念T恤 添加小助手微信加入华为云Cassandra技术交流群,面对面与专家交流哦适用场景LoT 多模NoSQL服务GeminiDB兼容Cassandra接口,拥有超强写入性能,专为密集写入而设计。 它适用于完全不同的行业,无论是制造业,物流业,医疗保健业,房地产业,能源生产业,农业等等。 无论传感器类型如何,都可以很好地处理传入数据,并为进一步的数据分析提供了可能性。互联网多模NoSQL服务的快速读取和高可扩展特性使其适用于具有产品目录、推荐、个性化引擎等功能的电子商务网站和娱乐网站,可用于存储访问者的活动,有利于分析工具快速访问数据,为用户生成推荐。金融 多模NoSQL服务GeminiDB结合Spark等大数据分析工具,可应用于金融行业的风控体系,构建反欺诈系统。物联网
上滑加载中
推荐直播
-
全面解析华为云EI-API服务:理论基础与实践应用指南
2024/11/29 周五 18:20-20:20
Alex 华为云学堂技术讲师
本期直播给大家带来的是理论与实践结合的华为云EI-API的服务介绍。从“主要功能,应用场景,实践案例,调用流程”四个维度来深入解析“语音交互API,文字识别API,自然语言处理API,图像识别API及图像搜索API”五大场景下API服务,同时结合实验,来加深开发者对API服务理解。
正在直播 -
企业员工、应届毕业生、在读研究生共探项目实践
2024/12/02 周一 19:00-21:00
姚圣伟 在职软件工程师 昇腾社区优秀开发者 华为云云享专家 HCDG天津地区发起人
大神带你一键了解和掌握LeakyReLU自定义算子在ONNX网络中应用和优化技巧,在线分享如何入门,以及在工作中如何结合实际项目进行学习
即将直播 -
昇腾云服务ModelArts深度解析:理论基础与实践应用指南
2024/12/03 周二 14:30-16:30
Alex 华为云学堂技术讲师
如何快速创建和部署模型,管理全周期AI工作流呢?本期直播聚焦华为昇腾云服务ModelArts一站式AI开发平台功能介绍,同时结合基于ModelArts 的实践性实验,帮助开发者从理论到实验更好地理解和使用ModelArts。
去报名
热门标签