• [交流吐槽] 【数据库】云数据库rds是什么意思?有什么优势?
    云数据库rds是什么意思?有什么优势?这两个问题是很多运维人员都想了解的问题,因为大多运维人员思想还停留在自建数据库这种意识上,并没有真正地了解到云数据库的优势,今天跟小编小编一起来简单了解一下。云数据库rds是什么意思?云数据RDS是关系型数据库服务(Relational Database Service)的简称,是一种即开即用、稳定可靠、可弹性伸缩的在线数据库服务;具有多重安全防护措施和完善的性能监控体系,并提供专业的数据库备份、恢复及优化方案,使您能专注于应用开发和业务发展。云数据库rds有什么优势?1、轻松部署使用云数据库RDS可以让用户轻松地完成数据库的申请和创建,只需要在几分钟内就可以部署投用,并且用户还可以通过rds控制台来对所有的实例进行统一管理,轻松完成部署。2、高可靠性在高可靠性方面表现得也很有优势,是可以进行故障自动单点切换,可以进行自动备份,能够确保高可用性和安全性。3、低成本用户可以根据自己的实际需求来支付费用,可以用最低的价格得到自己想要的一整套专业的数据库支持服务。4、高安全提供白名单访问策略;可自行设置允许访问的IP及IP段,有效防止黑客扫描端口进行服务器攻击等等。不同厂商的安全策略不同。知识拓展1:华为云-云数据库RDS简单介绍华为云-云数据库RDS服务具有完善的性能监控体系和多重安全防护措施,并提供了专业的数据库管理平台, 让用户能够在云上轻松的进行设置和扩展云数据库。通过云数据库RDS服务的管理控制台,用户无需编程就可以执行所有必需任务,简化运营流程,减少日常运维工作量,从而专注于开发应用和业务发展。知识拓展2:行云管家数据库审计支持云厂商RDS行云管家数据库审计全面支持Oracle、MySQL、SQLServer等主流数据库的审计和保护功能,同时适用于本地数据库与云厂商RDS。简单来说就是行云管家数据库审计支持云厂商RDS。
  • [热门活动] 开年采购季,8000元见面礼+0门槛抽奖,云数据库包年18元起!
    活动时间:2020/3/1~3/31活动主题:开年采购季,华为云数据库三重福利送不停活动内容:开年采购季,0元免费试用,云数据库包年18元起,还有“8000元见面礼+抽奖” 福利哦!【福利一】华为云数据库0元免费试用注册华为云账号,即可免费试领取华为云数据库领取方式:https://activity.huaweicloud.com/dbs_Promotion/index.html【福利二】8000元见面礼+0门槛抽奖开年采购享好价,集云福卡兑好礼。1、登陆即可领8000元开年红包2、集云福,领虎年福礼,最高可兑换华为笔记本3、企业注册并实名认证即可参与抽奖,最高抽奖可得WATCH GT典藏版4、消费满额送华为笔记本点击进入:https://activity.huaweicloud.com/newyear_promotion/index.html【福利三】开年采购享好价,爆款云数据库包年18元起云数据库MySQL、企业级Redis包年18元起点击进入:https://activity.huaweicloud.com/dbs_Promotion/index.html其他活动【参与有奖】GaussDB(for MySQL) 关键特性发布和技术解读《数据库原理》MOOC第3季 春日携礼归来,MOOC免费学, 分享得更多!
  • [热门活动] 【华为伙伴暨开发者大会·技术宝典】10分钟快速入门RDS
    参与活动前请先报名华为伙伴暨开发者大会技术前瞻【点击报名】活动时间:5月23日-6月30日活动内容:《10分钟快速入门RDS》沙箱实验活动奖励:奖励一:勇往直前进阶等级 LV+1 级奖励二:在所有完成技术宝典任一任务的用户中抽取40个幸运奖,奖品为文件收纳包。参与方式:Step1:点击就可参与《10分钟快速入门RDS》沙箱实验Step2:参与回复,截本实验进度100%结果到下方评论区回复方式:华为云账号+实验进度100%的截图活动规则:1. 回复非示例要求图片或其他无效信息,视为无效楼层,并取消抽奖资格。2. 全部活动结束后,将符合抽奖条件的用户名单导入至巨公摇号平台(https://www.jugong.wang/random-portal/)内,抽取40个幸运奖,并截屏公示抽奖过程。如您不同意此抽奖规则,请勿参加本次活动。Tips:1. 请务必使用个人账号参与活动(IAM、企业账号等账号参与无效)。2. 所有获得奖项的获奖用户,请于获奖后3日内完成实名认证,否则视为放弃奖励。3. 收货信息填写说明:1)为保证您顺利领取活动奖品,请您提前填写奖品收货信息,如您没有填写,视为放弃奖励。收货信息请【点击此处填写】2)填写时间截至2022年7月10日3)在华为伙伴暨开发者社区活动中完成一次填写即可。我们最终将会按照您填写的信息发放奖励。4. 活动规则请戳https://bbs.huaweicloud.com/forum/thread-180208-1-1.html
  • [数据库] 【第36课】如何在DRS上搭建MySQL异地单主灾备
    当某一地区故障而导致业务不可用,可以使用数据复制服务DRS推出的灾备场景,为业务连续性提供数据库的同步保障。本节小课为您介绍RDS for MySQL实例通过DRS服务搭建异地单主灾备的过程。实现原理RDS跨Region容灾实现原理说明:在两个数据中心独立部署RDS for MySQL实例,通过DRS服务将生产中心MySQL库中的数据同步到灾备中心MySQL库中,实现RDS for MySQL主实例和跨Region灾备实例之间的实时同步。更多关于MySQL实例灾备须知请单击这里了解。一、生产中心RDS for MySQL实例准备创建MySQL业务实例,选择已规划的业务实例所属VPC,并为实例绑定EIP。1.   登录华为云控制台。2.   单击管理控制台左上角的,选择区域“华北-北京一”。3.   单击左侧的服务列表图标,选择“数据库 > 云数据库 RDS”。4.   单击“购买数据库实例”。5.   填选实例信息后,单击“立即购买”。 选择引擎版本信息。选择规格信息。选择已规划的网络信息。设置管理员密码。6.   为创建的RDS实例绑定弹性公网IP。二、灾备中心RDS for MySQL实例准备创建MySQL灾备实例,选择已规划的灾备实例所属VPC。1.   单击管理控制台左上角的,选择区域“华北-北京四”。2.   单击左侧的服务列表图标,选择“数据库 > 云数据库 RDS”。3.   单击“购买数据库实例”。4.   填选实例信息后,单击“立即购买”。选择灾备实例引擎版本信息选择灾备实例规格信息选择灾备实例已规划的网络信息设置灾备实例管理员密码三、搭建容灾关系创建DRS灾备实例,创建时选择灾备中心创建的RDS for MySQL实例。1.   在“华北-北京四”区域,单击左侧的服务列表图标,选择“数据库 > 数据复制服务 DRS”。2.   选择左侧“实时灾备管理”,单击右上角“创建灾备任务”。3.   灾备类型选择“单主灾备”,灾备关系选择“本云为备”,灾备数据库实例选择在“华北-北京四”新创建的MySQL灾备实例,单击“下一步”,开始创建灾备实例。设置基本信息设置灾备实例信息4.   返回“实时灾备管理”页面,可以看到新创建的灾备实例。创建完成5.   在灾备实例上,单击“编辑”。6.   根据界面提示,将灾备实例的弹性公网IP加入生产中心MySQL实例所属安全组的入方向规则,选择TCP协议,端口为生产中心MySQL实例的端口号。添加安全组规则      源库信息中的“IP地址或域名”填写生产中心MySQL实例绑定的EIP,“端口”填写生产中心MySQL实例的端口号。测试通过后,单击“下一步”,直到任务启动,任务状态为“灾备中”。编辑灾备任务灾备中四、容灾切换生产中心数据库故障时,需要手动将灾备数据库实例切换为可读写状态。切换后,将通过灾备实例写入数据,并同步到源库。1.   生产中心源库发生故障,例如:源库无法连接、源库执行缓慢、CPU占比高。2.   收到SMN邮件通知。邮件通知3.   查看灾备任务时延异常。时延异常4.   用户自行判断业务已经停止。具体请参考如何确保业务数据库的全部业务已经停止。5.   选择“批量操作 > 主备倒换”,将灾备实例由只读状态更改为读写状态。主备倒换倒换完成6.   在应用端修改数据库连接地址后,可正常连接数据库,进行数据读写。
  • [数据库] 【第35课】其他云MySQL迁移到RDS for MySQL实例
    数据复制服务(Data Replication Service,简称DRS)支持将其他云MySQL数据库的数据迁移到本云云数据库MySQL。通过DRS提供的实时迁移任务,实现在数据库迁移过程中业务和数据库不停机,业务中断时间最小化。本节小课为您介绍将其他云MySQL迁移到RDS for MySQL实例。部署架构更多关于MySQL数据迁移须知请单击这里了解。一.  创建RDS for MySQL实例创建MySQL业务实例,选择已规划的业务实例所属VPC和安全组。1.   登录华为云控制台。2.   单击管理控制台左上角的,选择区域“华南-广州”。3.   单击左侧的服务列表图标,选择“数据库 > 云数据库 RDS”。4.   单击“购买数据库实例”。5.   配置实例名称和实例基本信息。      6.   选择实例规格。      7.   选择实例所属的VPC和安全组、配置数据库端口。      8.   配置实例密码。      9.   单击“立即购买”。10.   返回云数据库实例列表。当RDS实例运行状态为“正常”时,表示实例创建完成。二、其他云MySQL实例准备帐号权限要求当使用DRS将其他云MySQL数据库的数据迁移到本云云数据库MySQL实例时,帐号权限要求如下表所示,授权的具体操作请参考授权操作。迁移帐号权限迁移类型全量迁移全量+增量迁移源数据库(MySQL)SELECT、SHOW VIEW、EVENT。SELECT、SHOW VIEW、EVENT、LOCK TABLES、REPLICATION SLAVE、REPLICATION CLIENT。网络设置源数据库MySQL实例需要开放外网域名的访问。白名单设置其他云MySQL实例需要将目标端DRS迁移实例的弹性公网IP添加到其网络白名单中,目标端DRS迁移实例的弹性公网IP在创建完DRS迁移实例后可以获取到,确保源数据库可以与DRS实例互通,各厂商云数据库添加白名单的方法不同,请参考各厂商云数据库官方文档进行操作。三、创建DRS迁移任务1.   登录华为云控制台。2.   单击管理控制台左上角的,选择区域,即为目标实例所在的区域。3.   单击左侧的服务列表图标,选择“数据库 > 数据复制服务 DRS”。4.   单击“创建迁移任务”。5.   填写迁移任务参数。      配置迁移任务名称。            填写迁移数据并选择模板库。这里的目标库选择创建的RDS实例。      6.   单击“下一步”。      迁移实例创建中,大约需要5-10分钟。迁移实例创建完成后可获取弹性公网IP信息。      7.   配置源库信息和目标库数据库密码。      8.   单击“下一步”。9.   在“迁移设置”页面,设置流速模式、迁移用户和迁移对象。流速模式:不限速迁移对象:全部迁移10.   单击“下一步”,在“预检查”页面,进行迁移任务预校验,校验是否可进行任务迁移。查看检查结果,如有不通过的检查项,需要修复不通过项后,单击“重新校验”按钮重新进行迁移任务预校验。预检查完成后,且所有检查项结果均成功时,单击“下一步”。11.   参数对比。若您选择不进行参数对比,可跳过该步骤,单击页面右下角“下一步”按钮,继续执行后续操作。若您选择进行参数对比,对于常规参数,如果源库和目标库存在不一致的情况,建议将目标数据库的参数值通过“一键修改”按钮修改为和源库对应参数相同的值。12.   单击“提交任务”。      返回DRS实时迁移管理,查看迁移任务状态。      启动中状态一般需要几分钟,请耐心等待。            当状态变更为“已结束”,表示迁移任务完成。四、确认迁移结果确认迁移结果可参考如下两种方式:DRS会针对迁移对象、用户、数据等维度进行对比,从而给出迁移结果,详情参见在DRS管理控制台查看迁移结果。直接登录数据库查看库、表、数据是否迁移完成。手工确认数据迁移情况,详情参见在RDS管理控制台查看迁移结果。在DRS管理控制台查看迁移结果1.   登录华为云控制台。2.   单击管理控制台左上角的,选择目标区域。3.   单击左侧的服务列表图标,选择“数据库 > 数据复制服务 DRS”。4.   单击DRS实例名称。5.   单击“迁移对比”,选择“对象级对比”,单击“开始对比”,校验数据库对象是否缺失。6.   选择“数据级对比”,单击“创建对比任务”,查看迁移的数据库和表内容是否一致。7.   选择“用户对比”,查看迁移的源库和目标库的账号和权限是否一致。在RDS管理控制台查看迁移结果1.    登录华为云控制台。2.   单击管理控制台左上角的,选择目标区域。3.   单击左侧的服务列表图标,选择“数据库 > 云数据库 RDS”。4.   单击迁移的目标实例的操作列的“更多 > 登录”。      5.   在弹出的对话框中输入密码单击“测试连接”检查。6.   连接成功后单击“登录”。7.   输入实例密码,登录RDS实例。8.   查看并确认目标库名和表名等。确认相关数据是否迁移完成。
  • [数据库] 【第34课】如何将自建MySQL迁移到RDS for MySQL
    数据复制服务DRS支持将本地MySQL数据库的数据迁移至RDS for MySQL。通过DRS提供的实时迁移任务,实现在数据库迁移过程中业务和数据库不停机,业务中断时间最小化。本节小课为您介绍将自建MySQL迁移到RDS for MySQL的过程。部署架构本示例中,数据库源端为ECS自建MySQL,目的端为RDS实例,同时假设ECS和RDS实例在同一个VPC中。更多关于MySQL数据迁移须知请单击这里了解。一.  创建ECS(MySQL服务器)并安装MySQL社区版购买并登录弹性云服务器,用于安装MySQL社区版。1.   登录华为云控制台。2.   单击管理控制台左上角的,选择区域“华东-上海一”。3.   单击左侧的服务列表图标,选择“计算 > 弹性云服务器 ECS”。4.   单击“购买云服务器”。5.   配置弹性云服务器参数,填选信息后,单击“立即购买”。            选择镜像和磁盘规格。      6.   在创建的ECS上单击“远程登录”。选择“CloudShell登录”。7.   输入root用户密码,完成登录。8.   执行如下命令,创建mysql文件夹。      mkdir /mysql9.   执行如下命令,查看数据盘信息。      fdisk -l10.   执行如下命令,初始化数据盘。      mkfs.ext4 /dev/vdb11.   执行如下命令,挂载磁盘。      mount /dev/vdb /mysql12.   执行如下命令,查看磁盘是否挂在成功。      df -h      当回显出现 /dev/vdb的数据时,表示挂载成功。13.   依次执行如下命令,创建文件夹并切换至install文件夹。      mkdir -p /mysql/install/data      mkdir -p /mysql/install/tmp      mkdir -p /mysql/install/file      mkdir -p /mysql/install/log      cd /mysql/install14.   下载依赖包并上传到/mysql/install/file命令。15.   下载并安装社区版MySQL。二. 创建ECS并安装MySQL客户端1.   创建MySQL客户端的弹性云服务器。确保和MySQL服务器所在ECS配置成相同Region、相同可用区、相同VPC、相同安全组。不用购买数据盘。云服务器名配置为:ecs-client。其他参数同MySQL服务器的ECS配置。2.   下载并安装MySQL客户,请参考安装MySQL客户端。三.  创建RDS实例本章节介绍创建RDS实例,该实例选择和自建MySQL服务器相同的VPC和安全组。1.   登录华为云控制台。2.   单击管理控制台左上角的,选择区域“华东-上海一”。3.   单击左侧的服务列表图标,选择“数据库 > 云数据库 RDS”。4.   填选信息后,单击“购买数据库实例”。            选择实例规格。            选择实例所属的VPC和安全组、配置数据库端口。            配置实例密码。      四. 创建DRS迁移任务介绍自建MySQL服务器上的loadtest数据库迁移到RDS MySQL实例的详细操作过程。1.   登录华为云控制台。2.   单击管理控制台左上角的,选择区域“华东-上海一”。3.   单击左侧的服务列表图标,选择“数据库 > 数据复制服务 DRS”。4.   单击“创建迁移任务”。5.   填写迁移任务参数,直到任务创建完成。      配置迁移任务名称。            填写迁移数据并选择模板库。这里的目标库选择创建的RDS实例。      6.   配置源库信息和目标库数据库密码。      7.   单击“下一步”,直到迁移任务提交成功,数据迁移完成。
  • [参赛经验分享] 2019年华为云数据库TaurusDB挑战赛冠军赛题总结
    1.概述            华为云TaurusDB是华为云自主研发的最新一代云原生分布式数据库,采用计算与存储分离、日志即数据的架构设计,实现数据库性能方面的大幅提升,具备极致可靠、极致性价比、多维拓展、完全可信等诸多特性。            赛题以此为背景,目标是设计一个计算存储分离的KV存储引擎。首先回顾下赛题,本次大赛的目的是设计一个KV存储引擎,复赛加入了计算存储分离的要求,引入了网络通信。同时,赛题要求程序能保证在应用层崩溃的情况下的数据安全性,追求更高的性能。因此,大赛主要考察点有5点:即读写吞吐量最大化;支持异常退出的缓冲设计;高效紧凑的索引结构;合理的缓存预读机制;以及高速稳定的RPC设计。主要考察点集中在文件IO和网络IO上,需要选手对操作系统底层有较多的了解。2.测试为了达到最优的性能目标目标,首先进行的是性能测试,这里对测评环境下的SSD,网络进行了详细的测试,结合运行环境的限制,最终确定磁盘采用direct方式调用,以2M单位写,16M或32M读。 由于网络环境在测评阶段发生过变动,限速环境下丢包率高,使用16连接4K大小调用,pipeline的方式请求大块数据,即柱状图最右侧1212M/s,用多连接打满带宽,后期去除限速后,采用单tcp连接128K调用,即最高的1939M/s。3 具体设计3.1整体架构设计确定了硬件的吞吐量,就可以对程序进行整体的设计了,计算节点和存储节点都根据其功能分为3层。计算节点前部分的KV接口层负责适配调用接口及记录必要的参数,因为计算节点无状态,没有持久化功能,KV抽象层通过对RPC client的包装,抽象出KV的存储层,实现接口和代码复用。存储节点除了RPC服务层,KV管理层负责管理到存储层的读写缓冲,文件系统层则负责将抽象的存储调用映射到多个磁盘文件。可以看出,计算节点和存储节点都有使用读缓冲提升性能,读取时,计算节点负责建立索引和预读,而存储节点抽象为一个块存储,写入时,存储节点则抽象为一个RPC服务端,计算节点远程调用。3.2存储设计存储引擎,首要设计存储的结构,这里采用KV分离的日志式存储的方式,KV在顺序上一一对应,可以通过读key文件快速建立索引,同时考虑到文件管理迁移的情况,文件以1G进行分片。3.3索引设计索引是加速读取必不可少的,为了将6400w索引到内存中并能提供高性能插的入和检索,采用了hash+array+linked list结构,同时能应对数据倾斜的情况,通过细粒度锁提升并发度,hash和array的长度也是可调的以适应不同场景。       索引以key和offset作为一个单元,将key文件全部读入内存,插的入新KV时直接hash到对应slot,append到后面,当需要查找时,对索引进行排序,key为第一优先级,offset为第二优先级,通过二分查找upper_bound方式,找到最大的offset值,即最新value对应offset,具备处理重复key的能力。3.4RPC协议设计而针对网络传输,设计了二进制的RPC协议,整体协议由计算端发起,无状态。设计考虑到应对各种网络环境和传输方式,请求和响应具备batch能力,最大化利用带宽。同时包头尽量4bytes对齐,提升payload的拷贝效率。4 具体功能实现 结构和协议设计完成后,下面就需要实现具体功能了。4.1日志式存储引擎实现首先是基于日志的存储部分,该部分抽象为writer,reader和file operator三部分,writer负责写入,通过mmap使用page cache作为缓冲、meta和keys的存储,同时使用精心设计的lock free ring buffer提升高并发写入性能,flusher和loader负责异步刷盘,重叠CPU/IO时间,达到最大吞吐量。读缓存使用最简单的hash,在顺序读时高效实现最优缓存策略。reader会读写缓冲,保证任何情况下写即可见。4.2单机KV存储引擎实现基于之前的日志式存储引擎,加上索引模块,记录key和对应存储偏移,即可完成单机版的KV存储引擎。系统初始化时,restorer负责多线程读取keys,并以(key,offset)进行排序,即前文介绍的索引初始化及查找算法。索引基于linked list+array的结构,固定大小分配自内存池,无碎片,统一生命周期管理。同时基于底层存储引擎特性,保证KV写入即可见的基本要求。4.3RPC实现对于client有两套调用流程,分别用于适配延迟敏感的写入操作和追求极致吞吐量的读取操作。首先,每个KV agent初始化一个client,client预先建立多条tcp连接。对于时延敏感的写入,采用单路阻塞IO模型,确认持久化后返回;对于追求吞吐量的读取,采用pipeline请求+多路复用IO模型,pipeline并行度可根据网络状况进行调整。      考虑到多路复用IO模型带来的时延问题,server线程采用简单的阻塞IO模型,单线程单socket。keys数据传输zero copy,提升性能。协议解析使用会话协议缓冲,采用生产消费模型,非常便于扩展协议。4.4计算存储分离的KV存储引擎实现上图展示的是初始化和写入的流程,首先计算节点restorer通过RPC拉取已有的keys数据,完成索引建立,保证能感知到已写入的KV。然后写入时,计算节点先通过RPC协议包装请求,等待写入完成后,将key和data offset记录到索引中,最后返回set。这样保证数据持久化后才返回请求,同时保证计算节点和存储节点实时一致。由于计算节点的实时性,读取并不需要做额外同步,直接通过索引获取当前key的offset,然后调用计算节点的reader,这时存储节点抽象为一个块存储,reader通过RPC完成cache miss时的读取功能,这里关闭了计算节点的loader跨块预读的功能,降低网络带宽占用,降低get KV的时延。5 细节优化5.1无锁环形缓冲由于写入是顺序进行的,低写入延迟是提升性能的决定性因素。这里将meta存储在page cache(mmap)中,对抗应用层崩溃,同时提升写入速度。meta数据按操作线程进行CPU cache line对齐,避免写入造成cache invalidate。通过filled数组和多个bound变量CAS操作保证write和flush操作安全,所以在写缓冲充足时,所有写入操作都是无锁无等待的。本地测试多线程写同一writer,flush到内存,可打满内存带宽到40GBps。上图为cache line对齐的meta结构。上图为bound的逻辑关系示意及具体实现结构。            上图为对filled数组和bound移动的核心代码。5.2 SpinLock & SpinRWLock锁操作是在多线程编程中比不可少的,但mutex是一个非常重的操作。这里针对多线程快速同步设计了两套基于原子操作和自旋的锁。实现基于原生C++11,且占用非常小的空间,RW lock仅占2个ptr大小。RW lock代码比较多就不贴这,实现参考java的ReentrantReadWriteLock非公平实现方法。非公平锁吞吐量高,故这里选择非公平方式。5.3 多存储单元虽然针对多线程传统设计了诸多优化,多存储单元的方式还是一种非常简单直接的避免冲突方式。根据线程id路由到不同存储单元上写入,能直接消除线程冲突。但在读的时候,不会总是落在同一单元中,这里通过prefer项,利用读写聚集性,减少多单元检索的开销。5.4 预读预读是重叠CPU和IO的一种方案。这里,预读由独立线程loader完成的,初赛阶段由于不存在网络延迟,预读能够提升整体吞吐量,提升若干秒的性能。而复赛阶段由于存在网络传输:  ∵网络吞吐量 < 磁盘吞吐量 and 网络延迟 >> 磁盘延迟  ∴关闭跨块预读的收益 > 预读收益复赛阶段预读仅包含values整块预读,块大小设定为32MB,由于reader的cache极大(使用了2GB),也起到了预读作用。            下图所示为预读预测代码,通过分析跨块时的访问模式,智能地进行正向和反向预读。    5.5内存管理 & 对齐 & Misc这里总结下内存相关优化点,不展开:内存管理-            VM使用RAII思想管理-            预分配一大块mmap-            offset原子加分配内存-            生命周期统一管理内存分配-            固定大小&对齐的内存分配-            array+linked list方式实现动态数组对齐-            DIO操作内存和缓存的VM都是4KB对齐,单独管理+populate&lock-            代码中设计了AVX2的memcpy(由于gcc版本没开)Misc-            索引重载符号使用std::sort,std::upper_bound-            keys传输使用mmap实现zero copy-            RPC请求栈上内存构建,减少系统调用,利用CPU cache-            数据分片参数使用类模板,编译阶段优化除法为位运算6 最优成绩复赛:-            最优成绩性能    484.889s(写)+133.776s(读)+134.711s(索引+随机读)+0.012s(Misc)=753.388s-            由于网络存在波动且传输数据较多,很难遇到各阶段都达到最优    写阶段最优: 484.889s    读取并恢复索引:~0.6s    读阶段最优:132.263s-(~0.6s(恢复索引))=131.663s 初赛(保留kill恢复能力,未达到最大吞吐量):-            最优成绩性能130.782s(写)+73.802s(顺序读)+72.827s(随机读)+0.008(Misc)=277.419s7 总结-            现实问题受环境影响,不存在永恒的最优方案,需要测试实验作为先导,知己知彼才能百战不殆-            良好的抽象有利于最大程度复用已有代码,同时具备良好的可维护性和扩展性-            追求极致性能时,在细节上的优化是必不可少的 最后感谢华为云提供这次比赛机会,通过这次比赛学习到了很多知识,使我受益匪浅。作者:0xCC
  • [参赛经验分享] 2019华为云数据库TaurusDB性能挑战赛亚军参赛总结
    缘起TaurusDB是华为云自主研发的最新一代云原生分布式数据库,完全兼容mysql8.0,采用计算与存储分离、日志即数据的架构设计,支持1写15读,性能达到原生Mysql的7倍。Taurus构建在共享分布式存储上,存储空间最高达128T,能跨AZ部署,具有可靠、高性能、易伸缩等特性。华为云TaurusDB性能挑战赛是由华为云主办的数据库领域顶级赛事,大赛将综合科研教学成果及商业领域需求,探索数据库领域的技术问题的可行性,为需求方和开发者提供联接的桥梁;并联合合作伙伴,搭建一个技术交流、人才培养、机遇共创数据库开发者平台和生态。听起来很激动人心吧,关键是奖金也很多, 同时还能刷脸! 最早是听一个同事说起的,恰好本人的工作也是“新一代数据库”开发,看着这么多人工智能的比赛不能参加,只能后悔自己选错了专业,但是机会来了,终于有自己擅长的领域了,心想不能错过这次机会,一边默默加了收藏。比赛内容一句话概括,就是实现一个kvstore,初赛是单机引擎,复赛要求存储计算分离,并且kv都是定长的,如果通用性做得好,可以直接用作块存储。题目初赛和复赛稍有区别,这里只说复赛的题目。存储引擎K,V都是定长的,key 8bytes, value 4K bytes。 测试分为三轮,第一轮, 16个线程写,每个线程写400万次;第二轮,16个线程读,每个线程按顺序读取400万次;第三轮,16个线程读,每个线程逆序读取400万次。第三轮的逆序是局部随机,总体逆序,也即在10M的数据范围内随机读,然后以10M为单位逆序推进。 成绩就是看总时间,时间越短越好。前面已经说过,初赛要实现的是单机引擎,复赛要实现的是存储计算分离的引擎,最大的限制是计算节点不能持久化任何数据。预选赛不得不说,华为可真会玩,这次预选赛竟然还要做题,并且是必须要学习他们的资料才能通过的题目,题目包括数据库的基本常识,但是也有产品介绍,经过深夜两个小时的学习,我算是第一次知道了这么多种不同的产品究竟是干嘛的。第一次答题还错了一道,以防万一,又做了一遍,这次错了两道,算了不玩了,95分也够了,睡觉去。但是心里总是不舒服,究竟是哪错了,又看了一遍,原来是幻读的理解这道题错了。初赛不出意外,预选赛顺利通过,初赛就开始写代码了,初赛只要写对差不多就可以晋级复赛。 当然任何一个程序员都不会满足刚好够用的状态,因此正常能想到的优化都加上了: 比如Direct IO, 多文件做数据分区,引入写buffer,比较频繁修改元数据,读取使用自己实现的page cache,以免4K读不能打满磁盘带宽。复赛复赛要求存储计算分离, 我认真做了一些分析,同时也对他们的硬件做了测试,分析下来,这个比赛比拼的点和我平时在工作中要追求的还是很不一样的。 平时无论做什么系统,都是在延迟差不多的情况下,把吞吐量做高,这次比赛最关键的一个点是:延迟第一,延迟是最重要的,特别是写的阶段,因为并发只有16,想通过聚合换吞吐量都不好使了。首先考虑持久化方法:4K恰好能对齐IO,所以key和value要分开存储,      想使用rocksdb之类的存储引擎即使不被禁止也是没有竞争力的。value非常随机,基本不用考虑压缩,就算有一点点好处,实现起来也太复杂了。再次考虑如何优化IO:SSD的IO吞吐量高于4K * iops, 不管是读还是写,IO聚合是必须的。单文件会遇到文件系统瓶颈,需要多文件, 也即要对数据做partition。关于同步IO还是异步IO的选择, 因为延迟优先,所以应该选同步IO。最后考虑网络框架:首先,我直接放弃考虑任何已有的网络框架,因为这是benchmark, 再小的开销也是开销,都会让程序变慢。其次,实际上任何IO      multiplexing的框架都会造成额外的延迟,不仅复杂,也是得不尝失的。综上,我决定就使用最简单的阻塞式IO来做socket通信。总体设计计算节点和存储节点分工, 关键就是索引维护在哪?经过思考, 决定索引维护在计算节点。 索引的内容是: key -> <file_id, pos>, 索引在计算节点的内存中维护为hash表。因为build索引大概需要400ms,写入再读取index比重新构建一遍index时间还长, 所以索引不需要持久化, 这其实是一个反直觉的决定。 计算节点在发送第一个读请求之前,会从存储节点把所有写入的key和pos发送过来, 然后由计算节点构造索引。存储节点起16个线程,listen在16个不同的端口。计算节点也会有16个线程, 每个计算节点的线程只会连接一个存储节点的端口,从而和一个存储节点线程通信。写入请求:写入过程不同的线程完全独立,每个线程负责一部分数据。请求发到存储节点后由接受请求的线程写入。读取过程:读取过程每个存储线程会读取任意一个线程写入的数据。由计算节点指定要读取那个分区的数据。也即写请求发到哪个存储线程,就由那个存储线程写入,每个存储线程实际上就对应了一个数据分区。 读请求发到一个存储线程,它可能要跨线程读取其它分区的数据。存储文件存储节点把数据分为16个partition,每个partition由一个线程负责写入。 每个partition共三个文件: 之所以有三个文件,是因为首先key和value要分开存储,这就要用两个文件;其次为了优化写,额外引入了文件做写入buffer。文件命名规则如下, 以第一个分区为例:00.k: 保存写入的key。 fallocate 4M * 8B, mmap到内存。00.v:      保存写入的value。fallocate 4M *      4K, DIO读写。00.b:      用作写入buffer。fallocate 16K      buffer + 4K 元数据,mmap到内存。写入原子性先写key和value,再更新key count。 key count改成功,则写成功;否则写失败,下次重启进程当作这次写没发生过。换句话说,key count改成功实际上表示这次写入commit成功。key count记录在00.k的第一个8字节, 如前所诉, 00.k是mmap到内存的,所以更新key count是没有什么代价的。value先记录到写buffer里,然后批量刷到00.v文件。key file内容如下:key countk1k2k3k4k5k6……value file内容如下:value countv1v2v3v4v5v6……buffer file内容如下:b0b1b2b3…………b15flushed poswrite buffer也是mmap到内存的, 前64K记录数据。 紧跟64K的8个字节记录flushed pos。 因为mmap必须以page为单位,所以实际内存占用64K + 4K。 它实际上是一个mmap持久化的ring buffer。 Ring Buffer的元数据包括filled pos和flushed pos。 filled pos由key count可以算出来, 所以不需要单独再记。build indexbuild index实际上是构造一个key -> offset的hash表。 要在400ms内完成build index,难点是如何并行:基本思路是把key做partition,每个partition内独立构造hash。这本质上是一个MapReduce的过程。map阶段: 并行划分range, 16个线程,每个线程负责处理一个key文件。reduce阶段: 并行构造hash, 16个线程,每个线程处理一个range。线程同步: 第二阶段开始之前要等第一全部完成,也即两个Stage之间是个Barrier, 这个同步过程和map reduce的shuffle是类似的。读取cache的实现cache是value的镜像,value文件分成了16个,cache也对应分成16组。 因为顺序和热点访问模式对cache都很友好,cache不需要特别大,每一组cache大小为64M。为了应对热点读,cache的最小单元设为16M,借用CPU cache的术语,我把它叫cache line,这是一个很大的值。 cache的内存因为是定长的,所以通过mmap一次申请好,不需要动态分配。 cache的索引本质是一个hash,但是不用解决冲突, 用file offset直接计算得到索引的下标。对16组cache中的每一组来说,有4个cache line, 每个cache line有三种状态,用一个uint32_t表示:Invalid: UINT32_MAXLocked:      UINT32_MAX - 1Valid:      file_offset »12cache line的状态被叫做indexStat,它被单独记在另外一个小数组里。读取不用加锁,使用double check即可:读取之前检查IndexStat有效,并且offset和我要读取的内容匹配。拷贝cache line的内容到私有buffer。拷贝完cache line的内容后再检查IndexStat是否发生过变化, 如果indexStat没有变化过,则说明我们拷贝的cache      line内容是对的。为了让上述double check生效, 更新cache的线程需要在写入之前把indexStat改为Locked,更新完成后再把indexStat改为有效值。关于网络通信关于网络框架还有一个问题,就是是否有必要用UDP,犹豫之下,觉得UDP还是会更复杂,稳妥起见,选择了TCP。后来得知在测试环境里UDP是不同的,也就释然了。但是TCP看起来开箱即用,用起来却暗藏机关,至少要调整以下三个参数:tcp_nodelaytcp_quickacksend/recv      buf最后为了规避TCP的流控,我们要避免另外两个坑:避免发送太快,超过交换机的队列长度,从而导致丢包,因为TCP的工作方式就是,只要你给它数据,它就会不停的加大,发送窗口,直到发生丢包,然后触发限流。要避免这种情况,本质是要限制TCP的连接数, 因为只要连接数限制住了,发送窗口总长度也就限制住了。避免TCP”冷却”之后重新进入慢启动状态,如果有root权限,是可以通过sysctl关闭slow start的,但是我们没法控制系统参数,这就要求我们一个连接最好要不停的发包,不要停下来。有人定义了packet格式,而我遵循一切从简的原则,没有实现通常RPC要有的功能:没有定义pcode,因为整个通信过程就只有读和写两种request,每一个socket只用来发送一种类型的request,如果要发送不同的request,只需要切换socket即可。没有实现序列化,因为request很简单,只需要把结构体直接发送到socket即可。细节决定性能细节也就是很多关于性能的小点:首先关于文件IO: 同步IO比异步IO延迟要小; open的时候加入O_NOATIME避免读取操作也修改元数据其次关于内存分配: 分配完内后提前触发page fault至少可以让性能更稳定,如果不能提高性能的话;      分配内存可以尝试hugepage, 失败之后再使用4K page; mmap的内存可以不用清零最好关于CPU, 绑核,通用可以让延迟更稳定, 理论上对性能是有提升的。正确性测试如前所属,我基本上是一切从简,但即使如此,提交了好多次,都通不过正确性测试。为了排查问题,专门写了一个随机的验证程序,这个比官方的测试强度高多了。确实发现了一个readv之后更新iovec的bug,还有一处cache的bug。 事后看来,在这个测试上花的时间非常值得,否则我可能到最后都没有成绩。总结这次比赛感觉有点像马拉松,听完大家的分享,感觉每个人的时间都比较有限,大家都是争分夺秒,每个人都有一些优化没来得及试。本人最大的一个遗憾是读预取没有实现。另外一个是写的过程中网络和IO并行化做的不好。最后听了第一名的方案,深有体会,要想拿到最好的成绩,需要把写和读分开考虑,同时把网络传输和本地IO分开考虑。单纯从工程上说,我的代码有一些特有的风格, 简单来说就是总爱重复造轮子,不愿意引入依赖:没有用pthread mutex/cond,全部用atomic      ops + futex没有spin,等待的地方都有睡眠和唤醒机制另外,本人虽然工作中用C++, 但是我情愿用plain old C, 所以我很少用高级C++特性。如果未来还做这种系统实现的比赛,希望有可以利用RDMA的和NVM的比赛, 毕竟,新硬件总是有更多的可能。当然比赛的设置要考虑引入更多自由度,这样会更加有趣。
  • [参赛经验分享] 2019华为云数据库TaurusDB性能挑战赛亚军参赛总结
    大家好,我们是watermelon团队,今天我们的答辩展示主要分为四个部分来介绍。首先是我们的团队和成员的简介然后是我们对这次题目重点的理解接下来,我会介绍一下我们最基础的架构,包括网络、磁盘、缓存等最后,重点介绍我们每一个性能优化点团队名叫watermelon,我们三个人都是在读的研究生,分别来自浙江大学和上海交通大学,都是明年毕业。初赛和复赛的成绩都是第四名,历史成绩非常的稳定。我们总结了几个题目重点:首先,KV 都是定长的:这样简化了⽂件和内存的操作和管理。第二点,value远大于key:把 KV 分离存储,解除索引和数据之间的耦合性。第三点,线程数固定:测评程序固定使用 16 个线程访问数据库。第四点,只需要保证进程意外退出时的持久性:所以可以利用操作系统的缓存对写入方式进行一些优化。第五点,分阶段测评:随机写、随机读、顺序读三个阶段互相没有重叠。第六点,计算节点无状态:我们知道,在这种基于共享存储的计算存储分离架构下,所有持久化数据只能存于存储节点,计算节点只进行逻辑操作。第七点,数据读取的线程和数据写入线程之间没有绑定关系:就是说,每个线程不是只读取自己写入的数据。  //与初赛不一样,读取线程id不一读的是相同id写入的数据最后一点,随机读的随机性在每个时刻只局限在一个 10M 热点分区内:并且热点分区按写入的顺序逆序推进。 下面是我们方案的核心架构:    我们计算节点和存储节点的线程采用一对一的tcp连接,因为测评程序是16线程,所以连接数就是16。在存储节点,我们有数据持久化产生的文件,以及读写文件的缓存。在计算节点,我们维护了读数据的缓存,但是没有写数据的缓存,因为要保证计算节点被 kill 的数据持久性。并且我们的索引也是只在计算节点上维护的,在数据库启动阶段从存储节点把索引数据拉到计算节点。接下来,具体介绍一下我们的文件组织形式:首先,按写入线程进行数据的分区,就是说,每个写入线程只顺序写自己的分区文件,这样就避免了多线程写同一个文件冲突的问题。然后,在每个分区内,将 key 和 value 分离存储为两个文件,一个是key log,一个是value log。可以解除索引和数据之间的耦合性。并且我们为了提高写入value 的速度,对 value 进行了缓存,缓存中凑齐若干个value后,再一起进行刷盘。为了保证缓存不丢失,缓存也使用了mmap的形式,因此对应有一个缓存文件。我们的索引也是按分区进行构建的,每个分区是一个hash,里面存的是该分区所有数据的索引项,索引项就是一个 key 和 一个value offset复赛的优化历程,我们从最开始跑通的 2300 分,到最后的 780 分,中间经过了好几次架构和方法的改变。首先我们的第一个优化是,我们在启动阶段把 key 从存储节点批量的传到计算节点,这样相比每个 key 都请求一次,批量的方法相当于减少了一半的网络请求,使得时间提升了 300 秒。接下来,由于我们无法搞定网络传输大数据包的问题,因此我们选择先在存储节点实现顺序读和随机读的缓存,这样减少了存储节点的 io 次数,时间提升了 500s再后来,我们解决了网络传输的问题,所以先在计算计算节点进行了顺序读的缓存,成绩提升到 1100s这样紧接着,我们按存储节点缓存一样的思路,把随机读缓存也拿到了计算节点,这样做之后我们的成绩就已经突破了 800s在最后阶段,我们又优化了一些细节问题,最终成绩是 781s围绕读写文件,缓存策略,和网络传输这三个方面,来讲解我们是如何把这个系统的性能压榨到极致的。对于 key 这种小数据量的读写模式,采用 mmap 可以利用 page cache 将小数据读写转换为整个内存页的读写,减少了系统调用的时间消耗。value 的大小为固定 4KB,我们知道,写入数据大小要对齐 ssd 的内部 page 时,可以达到最优写入性能。我们经过线上测试,按 1M 大小顺序写入数据可以达到最大吞吐量。所以,为每一个分区分配一个容量为 1M 的写缓冲区,写满 256 个 value 再将缓存数据一起刷盘,刷盘方式采用 direct io。并且,用 mmap 做缓存,可以保证数据持久性。第二点,我们来看,如何做随机读的缓存,使得缓存命中率最高。现在背景是,随机读在每个时刻只局限在一个 10M 热点分区内,并且热点分区按写入的顺序逆序推进。看图,上面是我们实际存储的文件,按10M分为了若干个block,就是说,我们的缓存只能按 block 对齐进行加载数据。然后,下面是测评的热点分区示意图,我们发现一共 400w 数据,热点分区的边界跟实际数据 10M 边界很可能是不对齐的,并且边界值未知。所以,如果我们只用单个 10M 的缓存,会出现下面这样的问题。假设当前阶段,测评程序要随机访问 hot1 这个热点分区。第一个位置,我们缓存加载了block3,然后,访问第二个位置,发现缓存失效,加载了block2,继续访问第三个位置,缓存又失效了,重现加载回了block3所以,这样就产生了一个缓存来回震荡的问题,极端情况是,我要访问400万数据,一共要加载400万次大块的缓存,肯定超时,还不如不做缓存。我们的方案是,采用两个 10M 的buffer,一前一后,一起往前推进,这样非常完美了避免了缓存来回震荡的问题。并且这个 buffer 不一定是10M,只要大于10M都可以解决这个问题。第三点,我们来看,如何使得网络的传输效率最高。我们发现,写入阶段的网络传输时间主要瓶颈是在周转时间上,也就是说,不是网络有多么拥塞导致网络传输变慢,而是说,value发过去再回来,本身就需要这么长的时间。所以我们的优化只能是让存储节点尽可能快的返回ack信号,我们的做法是在数据写入存储节点的mmap之后,就返回ack,而不用等待page cache 刷盘。对于持久性,recover 阶段会把 cachebuffer 里面的数据都重新写进 value 文件里。对于大块数据进行拆分,然后进行多次发送,可以在发送的同时进行流量控制,使吞吐量保持在一个较高的水平。我们的流量控制方法非常简单粗暴,在发送每两个 4k 数据之间,直接加一个延时,延时的方法是让 CPU 自旋一会。并且,存储节点也做了读缓存,把存储节点的读缓存,批量拆分传到计算节点。最后,回到一个宏观的位置上来看,通过这次比赛,我们严格按照计算存储分离的思想来设计了我们的系统架构。对基于共享存储的计算存储分离架构有了一些认知和理解。首先,我们看计算节点我们的架构只支持一个 RW 节点,进行数据的写入。但是 RO 节点在理论上是可以无限扩展的。并且,由于底层的共享存储,所以主从复制的延迟可以做到非常低。当 RW 写入一个 kv 数据,对于 RO 节点,它只需要更新已经存在自己 buffer pool 中的数据,而如果发现 RW 写入的数据不在它的 buffer pool 中,那它什么也不做。只有在 RO 节点读取数据时,发现要请求的数据不在自己的buffer pool中,它才去下面的存储节点中拉取这个数据到自己的 buffer pool 中。这样看来,我们在实现高可用功能时候,可以很直接的进行主从切换,RO 节点可以迅速提升为 RW 节点,直接开始对外服务。再看存储节点我们当前只是在单个节点上保证了数据的持久性,而这种 kv 存储可以扩展为分布式架构,采用多副本存储,从而能获得更好的容错性,和更好的读写性能。所以这样一整套架构,可以解决很多实际的痛点,因此,会成为当今云数据库的一个趋势。
  • [参赛经验分享] 2019华为云数据库TaurusDB性能挑战赛季军赛题总结
    1 前言回顾第一次参加性能挑战赛—第四届阿里中间件性能挑战赛,那时候真的是什么都不会,只有一腔热情,借着比赛学会了 Netty、学会了文件 IO 的最佳实践,到了这次华为云举办的 TaurusDB 性能挑战赛,已经是第三次参加比赛了,同时也是最“坎坷”的一次比赛。经过我和某位不愿意透露姓名的 96 年小迷妹的不懈努力,最终跑分排名为第 3 名。如果要挑选一个词来概括这次比赛的核心内容,那非”计算存储分离“莫属了,通过这次比赛,自己也对计算存储分离架构有了比较直观的感受。为了比较直观的体现计算存储分离的优势,以看电影来举个例子:若干年前,我总是常备一块大容量的硬盘存储小电影,但自从家里带宽升级到 100mpbs 之后,我从来不保存电影了,要看直接下载/缓冲,基本几分钟就好了。这在几年前还不可想象,如今是触手可及的事实,归根到底是随着互联网的发展,网络 IO 已经不再是瓶颈了。计算存储分离架构相比传统本地存储架构而言,具有更加灵活、成本更低等特性,但架构的复杂性也会更高,也会更加考验选手的综合能力。计算存储分离架构的含义:存储端有状态,只存储数据,不处理业务逻辑。计算端无状态,只处理逻辑,不持久化存储数据。2 赛题概览比赛整体分成了初赛和复赛两个部分,初赛要求实现一个简化、高效的本地 kv 存储引擎,复赛在初赛的基础上增加了计算存储分离的架构,计算节点需要通过网络传输将数据递交给存储节点存储。计算节点和存储节点共用上述的接口,评测程序分为 2 个阶段:正确性评测此阶段评测程序会并发写入随机数据(key 8B、value 4KB),写入数据过程中进行任意次进程意外退出测试,引擎需要保证异常中止不影响已经写入的数据正确性。异常中止后,重启引擎,验证已经写入数据正确性和完整性,并继续写入数据,重复此过程直至数据写入完毕。只有通过此阶段测试才会进入下一阶段测试。性能评测随机写入:16 个线程并发随机写入,每个线程使用 Set 各写 400 万次随机数据(key 8B、value 4KB)顺序读取:16 个线程并发按照写入顺序逐一读取,每个线程各使用 Get 读取 400 万次随机数据热点读取:16 个线程并发读取,每个线程按照写入顺序热点分区,随机读取 400 万次数据,读取范围覆盖全部写入数据。热点的逻辑为:按照数据的写入顺序按 10MB 数据粒度分区,分区逆序推进,在每个 10MB 数据分区内随机读取。随机读取次数会增加约 10%。语言限定CPP & Java,一起排名3 赛题剖析初赛主要是文件 IO 和存储架构的设计,实现一个简单的本地KV存储引擎,如果对文件 IO 常识不太了解,可以先行阅读 《文件IO操作的一些最佳实践》。大赛的重头戏基本是在复赛网络通信的比拼上,在引入计算/存储分离架构后,计算节点无状态,计算节点和存储节点需要通过网络交互,网络将引入大于服务器内部的时延,因此,结合应用场景的良好架构和缓存设计,将是此类引擎设计的一个重点。3.1 架构设计计算节点只负责生成数据,在实际生产中计算节点还承担额外的计算开销,由于计算节点是无状态的,所以不能够聚合数据写入、落盘等操作,但可以在 Get 触发网络 IO 时一次读取大块数据用作缓存,减少网络 IO 次数。存储节点负责存储数据,考验了选手对磁盘 IO 和缓存的设计,可以一次使用缓存写入/读取大块数据,减少磁盘 IO 次数。所以选手们将会围绕网络 IO、磁盘 IO 和缓存设计来设计整体架构。3.2 正确性检测赛题明确表示会进行 kill -9 并验证数据的一致性,正确性检测主要影响的是写入阶段。存储节点负责存储数据,需要保证 kill -9 不丢失数据,但并不要求断电不丢失,这间接地阐释了一点:我们可以使用 PageCache 来做写入缓存;正确性检测对于计算节点与存储节点之间通信影响便是:每次写入操作都必须 ack,所以选手必须保证同步通信,类似于 ping/pong 模型。3.3 性能评测性能评测由随机写、顺序读、热点读(随机读取热点数据)三部分构成。随机写阶段与 PolarDB 的评测不同,TaurusDB 随机写入 key 的 16 个线程是隔离的,即 A 线程写入的数据只会由 A 线程读出,可以认为是彼此独立的 16 个实例在执行评测,这大大简化了我们的架构。顺序读阶段的描述也很容易理解,需要注意的是这里的顺序是按照写入顺序,而不是 Key 的字典序,所以随机写可以转化为顺序写,也方便了选手去设计顺序读的架构。热点读阶段有点故弄玄虚了,其实就是按照 10M 数据为一个分区进行逆序读,同时在 10M 数据范围内掺杂一些随机读,由于操作系统的预读机制只会顺序预读,无法逆序预读,PageCache 将会在这个环节会失效,考验了选手自己设计磁盘 IO 缓存的能力。4 架构详解4.1 全局架构计算存储分离架构自然会分成计算节点和存储节点两部分来介绍。计算节点会在内存维护数据的索引表;存储节点负责存储持久化数据,包括索引文件和数据文件;计算节点与存储节点之间的读写都会经过网络 IO。4.2 随机写架构随机写阶段,评测程序调用计算节点的 set 接口,发起网络 IO,存储节点接收到数据后不会立刻落盘,针对 data 和 index 的处理也会不同。针对 data 部分,会使用一块缓冲区(如图:Mmap Merge IO)承接数据,由于 Mmap 的特性,会形成 Merge File 文件,一个数据缓冲区可以聚合 16 个数据,当缓冲区满后,将缓冲区的数据追加到数据文件后,并清空 Merge File;针对 index 部分,使用 Mmap 直接追加到索引文件中。F: 1. data 部分为什么搞这么复杂,需要聚合 16 个数据再刷盘?Q: 针对此次比赛的数据盘,实测下来 16 个数据刷盘可以打满 IO。F: 2. 为什么使用 Mmap Merge IO 而不直接使用内存 Merge IO?Q: 正确性检测阶段,存储节点可能会被随机 kill,Mmap 做缓存的好处是操作系统会帮我们落盘,不会丢失数据F: 3. 为什么 index 部分直接使用 Mmap,而不和 data 部分一样处理?Q: 这需要追溯到 Mmap 的特点,Mmap 适合直接写索引这种小数据,所以不需要聚合。4.3 热点读&顺序读架构热点读取阶段 & 顺序读取阶段 ,这两个阶段其实可以认为是一种策略,只不过一个正序,一个逆序,这里以热点读为例介绍。我们采取了贪心的思想,一次读取操作本应该只会返回 4kb 的数据,但为了做预读缓存,我们决定会存储节点返回 10M 的数据,并缓存在计算节点中,模拟了一个操作系统预读的机制,同时为了能够让计算节点精确知道缓存是否命中,会同时返回索引数据,并在计算节点的内存中维护索引表,这样便减少了成吨的网络 IO 次数。4.4 存储设计站在每个线程的视角,可以发现在我们的架构中,每个线程都是独立的。评测程序会对每个线程写入 400w 数据,最终形成 16 * 16G 的数据文件和 16 * 32M 左右的索引文件。数据文件不停追加 MergeFile,相当于一次落盘单位是 64K(16 个数据),由于自行聚合了数据,所以可以采用 Direct IO,减少操作系统的 overhead。索引文件由小数据构成,所以采用 Mmap 方式直接追加写计算节点由于无状态的特性,只能在内存中维护索引结构。4.5 网络通信设我们都知道 Java 中有 BIO(阻塞 IO)和 NIO(非阻塞 IO)之分,并且大多数人可能会下意识觉得:NIO 就是比 BIO 快。而这次比赛恰恰是要告诉大家,这两种 IO 方式没有绝对的快慢之分,只有在合适的场景中选择合适的 IO 方式才能发挥出最佳性能。稍微分析下这次比赛的通信模型,写入阶段由于需要保证每次 set 不受 kill 的影响,所以需要等到同步返回后才能进行下一次 set,而 get 本身依赖于返回值进行数据校验,所以从通信模型上看只能是同步 ping/pong 模型;从线程数上来看,只有固定的 16 个线程进行收发消息。以上两个因素暗示了 BIO 将会非常契合这次比赛。在很多人的刻板印象中,阻塞就意味着慢,非阻塞就意味着快,这种理解是完全错误的,快慢取决于通信模型、系统架构、带宽、网卡等因素。我测试了 NIO + CountDownLatch 和 BIO 的差距,前者会比后者整体慢 100s ~ 130s。5 细节优化点5.1 最大化磁盘吞吐量但凡是涉及到磁盘 IO 的比赛,首先需要测试便是在 Direct IO 下,一次读写多大的块能够打满 IO,在此基础上,才能进行写入缓冲设计和读取缓存设计,否则在这种争分夺秒的性能挑战赛中不可能取得较好的名次。测试方法也很简单,如果能够买到对应的机器,直接使用 iostat 观察不同刷盘大小下的 iops 即可,如果比赛没有机器,只能祭出调参法,不停提交了,这次 TaurusDB 的盘实测下来 64k、128K 都可以获得最大的吞吐量。5.2 批量回传数据计算节点设计缓存是一个比较容易想到的优化点,按照常规的思路,索引应该是维护在存储节点,但这样做的话,计算节点在 get 数据时就无法判断是否命中缓存,所以在前文的架构介绍中,我们将索引维护在了计算节点之上,在第一次 get 时,顺便恢复索引。批量返回数据的优势在于增加了缓存命中率、降低总网络 IO 次数、减少上行网络 IO 数据量,是整个比赛中分量较重的一个优化点。5.3 流控在比赛中容易出现的一个问题,在批量返回 10M 数据时经常会出现网络卡死的情况,一时间无法定位到问题,以为是代码 BUG,但有时候又能跑出分数,不得以尝试过一次返回较少的数据量,就不会报错。最后还是机智的小迷妹定位到问题是 CPU 和 IO 速率不均等导致的,解决方案便是在一次 pong 共计返回 10M 的基础上,将报文拆分成 64k 的小块,中间代入额外的 CPU 操作,最终保证了程序稳定性的同时,也保障了最佳性能。额外的 CPU 操作例如:for(int i=0;i<700;i++),不要小看这个微不足道的一个 for 循环哦。流控其实也是计算存储分离架构一个常见设计点,存储节点与计算节点的写入速度需要做一个平衡,避免直接打垮存储节点,也有一种”滑动窗口“机制专门应对这种问题,不在此赘述了。5.4 预分配文件在 Cpp 中可以使用 fallocate 预先分配好文件大小,会使得写入速度提升 2s。在 Java 中没有 fallocate 机制,但是可以利用评测程序的漏洞,在 static 块中事先写好 16 * 16G 的文件,同样可以获得 fallocate 的效果。5.5 合理设计索引结构get 时需要根据 key 查询到文件偏移量,这显示是一个 Map 结构,在这个 Map 上也有几个点需要注意。以 Java 为例,使用 HashMap 是否可行呢?当然可以,但是缺点也很明显,其会占用比较大的内存,而且存取性能不好,可以使用 LongIntHashMap 来代替,看过我之前文章的朋友应该不会对这个数据结构感到陌生,它是专门为基础数据类型设计的 Map 容器。每个线程 400w 数据,每个线程独享一个索引 Map,为了避免出现扩容,需要合理的设置扩容引子和初始化容量:new LongIntHashMap(410_0000, 0.99)。5.6 Direct IO最终进入决赛的,有三支 Java 队伍,相比较 Cpp 得天独厚的对操作系统的灵活控制性,Java 选手更像是带着镣铐在舞蹈,因为有过参赛经验,我提前封装好了 Java 的 Direct IO 类库:https://github.com/lexburner/kdio,相比 FileChannel,它能够使得磁盘 IO 效率更高。得知有 Java 选手真的在比赛中使用了我的 Direct IO 类库,也是比赛中实实切切的乐趣之一。6 失败的优化点6.1 预读线程先行考虑到网络 IO 还是比本地磁盘 IO 要慢的,一个本以为可行的方案是单独使用预读线程进行存储节点的磁盘 IO,设计一个 RingBuffer,不断往前预读,直到环满,计算阶段 get 时会消费 RingBuffer 的一格缓存,从而使得网络 IO 和磁盘 IO 不会相互等待。实际测试下来,发现瓶颈主要还是在于网络 IO,这样的优化徒增了不少代码,不利于进行其他的优化尝试,最终放弃。6.2 计算节点聚合写入缓冲既然在 get 阶段时存储节点批量返回数据给计算节点可以提升性能,那 set 阶段聚合批量的数据再发送给存储节点按理来说也能提升性能吧?的确如此,如果不考虑正确性检测,这的确是一个不错的优化点,但由于 kill 的特性使得我们不得不每一次 set 都进行 ACK。但是可以将 4/8/16 个线程编为一组进行聚合,通过调整参数来确定该方案是否可行。然而事与愿违,该方案并没有取得成效。7 聊聊比赛吧之前此类工程性质的性能挑战赛只有阿里一家互联网公司承办过,作为热衷于中间件性能优化的参赛选手而言,非常高兴华为也能够举办这样性质的比赛。
  • [参赛经验分享] 2019华为云数据库TaurusDB性能挑战赛季军参赛总结
    赛题分析赛题要求一个无状态的计算节点和一个存储节点,并要求保证程序崩溃时的数据完整性,所以当计算节点写入时必须等待存储节点的 ack,而存储节点需快快确保数据写入到 page cache。赛题说明 16 个线程,每个线程写入 4 百万条记录,按写入顺序读取及热点区间读取,所以数据按写入线程分离存储。此外 Key 大小固定 8B,value 大小固定 4KB,所以需要批量写入才能打满带宽,并且索引只需要保存位置序号就可以,减小了索引的大小。整体架构针对赛题的要求,可以把问题分解成三个模块来看,分别是计算节点,存储节点和网络通信,因此我们设计了如下图所示的总体结构。在存储节点上,数据分为 16 组存储,按照写入线程分组,保证同一线程的数据写入到同一分组,这样在读取阶段不论是按写入顺序读取还是热点区间读取都可以一次从一个分组中读取一整块 cache,降低网络开销,另外这样并发管理也比较简单,因为一个线程只会对应到一个文件,所以读写的时候是不需要对分组加锁的。具体到到每个分组内部,key 和 value 是分离存储的,因为 key 和 value都是定长的,kv 分离以后恢复索引时只需要读取 key 文件就够了。最后存储节点会保证一旦写入成功数据就不会再丢失,也就是可以容忍 kill -9 退出。我们把索引放在计算节点,并且使用 TCP 进行节点间通信。由于并发数目不高,所以计算节点初始化时创建 16 条 tcp 连接,每个前台线程对应一条连接,在存储节点为每个 tcp 连接创建一个新线程。数据在写入时首先由计算节点发送写请求到存储节点,存储节点收到请求并确认数据成功写入不会丢失后,会把存储的位置回复给计算节点,然后计算节点把位置代入内存索引后返回。读取时计算节点首先根据内存索引判断数据在不在 cache pool 里,如果不在就去向存储节点读取一块新的 cache,然后从 cache 中读数据。存储节点之前我们也提到存储节点按写入线程分组,每个分组里包含四类文件,value data 文件保存 value,key data文件保存 key,两者各有一个 mmap buffer 文件,用来将写入 batch 到一起再刷盘。然后有一个全局的 mmap meta file,记录各个分组当前的数据长度,也就是各分组分别有多少个 kv 对,主要用于数据恢复和重建索引。数据写入时直接将key和value写入对应分组的mmap buffer,两者都写入成功后更新 meta file。只有当 mmap buffer 写满了以后才会 flush 到磁盘中,并且 flush 操作都是用 directIO 进行的。采用这种设计使得写入均是内存操作,将数据写入到 mmap 管理的 page cache 中即可返回,并且我们对mmap使用了mmap_lock标记使其一直锁定在内存中,只有当程序退出时才会刷盘。每次向data文件刷盘都是以一个 buffer 为单位,这样可以最大化利用 nvme 磁盘带宽,采用 directIO 的方式刷盘可以跳过 page cache,一方面减少了一次内存复制,另一方面降低了阶段切换时清空 page cache 的时间。而 mmap 的机制保障了即使进程意外退出,操作系统也会让 mmap buffer 中的数据安全落盘,不会引起数据丢失。我们实际为每个 key 和 data 文件维护了多个 mmap buffer,当一个 buffer 写满时转入后台 flush,接下来的写入会写进另一个 buffer。这是因为写入操作的开销是由网络传输开销和磁盘 IO 开销两部分组成的,单个buffer的情况下两者完全串行执行,在发生刷盘时写入操作需要消耗网络传输加上磁盘IO的时间。采用多个 buffer 以后相当于将磁盘 IO 与网络开销并行化了,产生的阻塞会减少很多。对于数据读取操作,读请求会将 offset 对齐到 cache 长度,也就是 10MB,然后存储节点直接以 directIO 方式从对齐后的 offset 处读出这一块数据返回即可。当存储节点意外关闭时,因为我们已经确保了所有数据都能安全落盘,唯一需要恢复的就是 mmap buffer 文件当前的写入位置,所以只需要从 meta file 中读取个分组的长度,对 buffer 长度取余便可以计算出各 buffer 的当前写入位置了。计算节点计算节点中主要包含了索引和 cache 两部分。索引部分,索引存储在 hash 表中,每个 entry 的格式为 (key, 分组编号+分组内偏移量)。因为 key 和 value 都是大小固定的,因此偏移量只需要保存它是分组内的第几个 kv 对,所以只需要 4 字节。因此每个 entry 的空间占用为 key 8字节加分组编号 4 字节加偏移量 4 字节,总共 16 字节,那么 6400 万个 entry 一共需要大约 1G 左右空间,因此可以完全保存在内存里。我们实现了一个原子的 hash 表。hash 表底层是一个大小为 8000 万左右的数组,以线性探测法处理 hash 冲突。说hash表的每一个 entry 由两个 64 位无符号整数组成,分别代表 key 与索引,并初始化为0。因为 key 本身可能是0,所以我们让分组编号从1开始,然后对索引部分的原子变量做 cas 操作来实现原子代入。代码如下:因为计算节点本身是无状态的,所以重启时需要恢复索引。我们把索引恢复的时机放在第一次 get 操作的时候,这样可以避免写入阶段计算节点请求恢复索引。索引恢复由 16 个线程并行执行,每一个线程向存储节点请求一整个 key 文件,因为 key 在 key 文件中的位置与 value 在 value 文件中的位置是一样的,所以遍历 key 文件就可以重建索引了。如果每次 get 都去存储节点读取,那么网络延迟开销太大,无法打满带宽。所以我们每次向存储节点请求数据时都读取一整块数据保存在内存 cache 里,大小为 10MB。由于我们不确定是不是完全按照 10MB 对齐的,因此我们为每个分组准备了两个 cache,这样当 cache miss 时,可以保证前一块 cache 的数据一定已经被读完了,可以被新的 cache 置换掉。此外我们还实现了 cache 预取机制。由于赛题要求顺序读取及热点区间读取,热点区间读取是按照数据的写入顺序以 10MB 数据粒度分区,分区逆序推进,在每个 10MB 数据分区内随机读取,所以当向存储节点请求一个新 cache 完成时,另一个线程会根据当前两个 cache 的 offset 之差向存储节点预取下一块 cache。在顺序读取阶段差为整数,因此会顺序预取,在热点区间读取阶段差为负数,因此会逆序预取。总结我们的最好成绩是 786 秒,其中写用了 505 秒,顺序读取用了 134 秒,热点读取用了 147 秒,最终排名第 5 名。此外各阶段分别跑出的最佳成绩为505/132/142秒,可惜我们对读操作的优化是最后一天完成的,但是最后一天的评测环境不太好,写性能一直上不去(我们的写入时间基本稳定在520秒以内,但是最后一天一直在560秒+)。在热点读阶段实际消耗在读取上的时间为133秒,但是恢复索引使用了9 秒,这里还有很大的改进空间。考虑到评测系统的带宽大约15Gbps,我们的读取性能已经基本跑满网络带宽了。第一次参加这种类型的比赛,我们学习和积累了很多经验,希望来年还有机会参加,取得更好的成绩。 
  • [参赛经验分享] 2019华为云数据库TaurusDB性能挑战赛优秀选手参赛总结
    1、前言华为云TaurusDB结束有一段时间了,这几天抽时间写一下参赛总结,我是从阿里第三届中间件比赛开始参加类似比赛的,TaurusDB这次是第三次,虽然有过两次参赛经验,但是数据库比赛还是第一次,报名也是无意中看到朋友在朋友圈的分享,正好想要学习一下数据库相关知识,于是报名参加了比赛,熬了无数个夜晚,很幸运最终以第10名的成绩擦边入围,主要还是cpp大佬和其他选手承让^_^。2、赛题回顾比赛分成了初赛和复赛两个部分,初赛要求实现一个简化、高效的本地 kv(为简化开发,K固定大小8B,V固定大小4KB)存储引擎,其中每个KVStore为一个实例,每个线程操作独立的KV实例,线程间互不影响,复赛在初赛的基础上增加了计算存储分离的架构,计算节点需要通过网络传输将数据递交给存储节点存储。程序主要提供三个接口:评测程序分为2个阶段正确性评测此阶段评测程序会并发写入随机数据(key 8B、value 4KB),写入数据过程中进行任意次进程意外退出测试,引擎需要保证异常中止不影响已经写入的数据正确性。异常中止后,重启引擎,验证已经写入数据正确性和完整性,并继续写入数据,重复此过程直至数据写入完毕。只有通过此阶段测试才会进入下一阶段测试。性能评测随机写入:16个线程并发随机写入,每个线程使用Set各写400万次随机数据(key 8B、value 4KB)。顺序读取:16个线程并发按照写入顺序逐一读取,每个线程各使用Get读取400万次随机数据。热点读取:16个线程并发读取,每个线程按照写入顺序热点分区,随机读取400万次数据,读取范围覆盖全部写入数据。热点的逻辑为:按照数据的写入顺序按10MB数据粒度分区,分区逆序推进,在每个10MB数据分区内随机读取,随机读取次数会增加约10%。评测环境计算节点:内存占用不得超过4G(CPP), 5G(JAVA),不得写入和读取磁盘空间。存储节点:内存占用不得超过2G(C++),3G(JAVA),磁盘占用不得超过320G。注:后台将CPU统一限制为16核。语言限定CPP & Java,一起排名3、赛题分析拿到赛题后首先我们对赛题已知条件做下分析:数据总量为4M*16(64M)条数据Key数据总量为64M*8(B),索引可以完全加载到内存定长Key(8B)和Value(4KB)极大简化索引构建和写入缓存设计读取分为顺序和逆序遍历(10M内随机)可以考虑使用LRU作为缓存策略kill -9保证数据不丢需使用mmap保证数据最终落盘因为我们比系统更了解程序的缓存策略读写考虑使用DirectIO+自管理缓存计算存储分离可以把存储节点当作一块磁盘对外提供read, write接口,把索引构建及索引查找放到计算节点因为DB是分布式的应考虑使用大块网络传输来减少rtt带来的性能损耗可以看出此次大赛的核心考察点是计算存储分离,因为计算量不大,存储介质性能又非常强悍,程序的瓶颈可能是在网络层面,如何高效利用网络是本次比赛重点思考内容。4、程序设计4.1 功能设计首先要考虑的是功能评测,即保证kill -9数据不丢,因为一个程序只有功能正确才能谈性能,保证kill -9数据不丢有两种方式,第一种是来一条数据写一次盘,每条数据都即时落盘,这个方式虽然可以保证数据不丢,但是性能堪忧,好在操作系统给我们提供了另一种方式mmap(memory mapping),通过mmap可以将文件直接映射到user内存区,user可以直接操作这块内存,程序可以通过主动(force)或者被动(程序结束或崩溃)方式将内存中的数据持久到磁盘,这个过程不经过kernel内存区,省去一次内存拷贝,同时也保证了数据安全。保证了存储节点数据不丢后下面要考虑在有网络传输的情况下数据不丢,如果允许数据丢失则可以使用异步io方式传输数据,但是要保证数据不丢则计算机点需要一直阻塞直到存储节点数据落盘并返回ack后计算节点set方法才能返回,这样网络模型为PING/PONG模型。4.2 文件分布设计因为程序是多实例数据库,实例之间互不干扰,这里没有对数据进行分片操作,其实换一个角度看我们可以把每个实例看作一个数据库分片,因为分片的目的是为了让分片之间互不干扰,提高程序并行能力,每个实例(分片)内的写操作都比较简单,采用追加写的方式。每个实例包含3个文件: MergeIO(mmap):merge io索引文件(mmap):Key实际存储文件数据文件:Value实际存储文件写入Value时先写入MergeIO文件,Merge满后批量写入数据文件,即使程序被kill,也可以从MergeIO文件恢复Value数据。这里感谢一下徐靖峰(岛风)同学开源的kdio(java操作DirectIO的库)4.3 索引设计数组二分查找:构建索引时需要排序,单点查询对cpu分支预测不友好,但是方便进行范围LongIntMap(开放寻址法):可以动态构建索引,单点查询可快速定位(key冲突量不大时),难以范围查询因为这里考虑只有单点get没有范围查询,所以用了LongIntMap 4.4 读取设计读取采用LRU算法对数据进行缓存读取,每个LRU Item包含4M Value数据(1K个Value,参数可调)每个实例包含14个LRU Item,包含4M(总消息数)/1K个空Item,比如说通过Key找到Index为1,对1取模得到缓存位置0,因为缓存已存在则直接使用item.get(),如果Index为1024,因为缓存不存在,则根据缓存最后访问时间找到需要失效的缓存,通过read方法从存储节点读取该缓存的内容,然后get出结果。4.5 网络传输设计存储节点:存储节点采用Epoll EventLoop模式,节点启动16条线程,计算节点与存储节点tcp连接均匀分布在16条线程上,后面数据交互均在固定线程完成,充分利用多核优势。计算节点:计算节点采用bio方式与存储节点建立连接,计算节点与存储节点交互均采用req/res阻塞模式交互。计算节点从存储节点read数据时,存储节点dio读取的数据直接使用ByteBuf.wrap,没有内存拷贝(程序层面),直接发送给计算节点。连接建立后开启tcp_nodelay以减少网络延迟。在比赛结束最后一天之前计算节点一直用的nio开发,写入耗时始终在719秒往上,因为一直觉得虽然nio在发送接收时与set/get线程有cpu切换,但是这些切换耗时与网络rtt相比应该不算什么,可事实总是在啪啪啪打脸,最后一天(其实也就写好两三天的样子,java环境最后几天才好,(/ □ \))把nio改写成bio传输,写入时间从725降到560左右(不确定是否是因为cpu切换导致),总结一下就是不能搞经验主义,一定要benchmark everything。5、可探索的改进一般情况下我们使用nio都是将其fd绑定到某个event loop上,该event loop监听该fd的事件,这里其实我们可以改变nio使用方式,在set/get当前线程监听fd事件来读写数据,有set/get线程充当event loop,可能会有一个不错的提升。6、一些感想第一次参加数据库相关的比赛,收获颇多,对数据库相关知识有了初步的了解,也学到了其他选手不错的分享,同时也非常感谢举办方举办这次比赛,给选手提供一个展示和学习的平台,预祝比赛越办越好。源码地址:https://github.com/wangkaish/hw_race2019_r2_kvstore
  • [技术干货] 近数据处理(NDP),为GaussDB(for MySQL)性能提升“加冕”
    在上一篇文章《首席科学家为您揭秘:我们介绍了GaussDB(for MySQL)的体系架构,这篇文章我们将重点介绍GaussDB(for MySQL)如何将查询处理卸载到存储层,我们将这一特性称之为近数据处理(Near Data Processing),简称NDP。NDP出现的契机在计算节点实例上执行查询操作首先需要将数据页面加载到InnoDB缓冲池(buffer pool)。相对传统数据库使用本地存储,云数据库需要通过网络获取数据,因此从存储节点读取页面数据的延迟要高得多。相比社区版MySQL,GaussDB(for MySQL)支持并行查询,可通过多线程并行将数据读取到缓冲池中,但当表数据量较大(包含数百万甚至更多的数据行),分析查询需要扫描大量数据时,将所需数据全部加载到缓冲池中,IO成本将变得非常高。因此,我们需要一种更优的方法来解决此问题。我们的解决方案是基于GaussDB(for MySQL) 计算节点与存储节点之间的紧密集成,将部分查询处理操作下推至靠近数据的分布式存储系统,数据库术语中称为算子下推。通过这种方式,我们可以利用多存储节点的总带宽。在云环境中,存储系统包含数百节点,我们希望充分利用存储系统的可扩展性,同时避免网络成为性能瓶颈点。NDP允许部分查询处理以大规模并行的方式在存储节点执行,并显著的减少网络IO。NDP有诸多好处,包括:利用多租户大规模分布式云存储系统,在多节点并行处理数据显著减少网络IO,只返回满足WHERE条件的行(过滤)和查询涉及的列(投影)或聚合操作的结果,而不是将完整的数据页面从存储节点返回至计算节点避免大数量扫描导致经常访问的数据页面从缓存池中移除那么存储层是如何处理的呢?算子下推通常适用于全表扫描、索引扫描、范围查询等场景。WHERE条件可下推至存储层,当前支持的数据类型包括:数值类型(numeric, integer, float, double)时间类型(date, time, timestamp)字符串类型(char, varchar)等算子下推可以与计算节点的并行查询完美结合,从概念上讲,一个查询首先在计算层(垂直扩展)拆分为多个worker线程并行处理,每个worker线程均可触发算子下推。由于分布式存储中数据分布的策略,每个worker线程的负载将分配至存储系统的多个节点上(水平扩展),每个存储节点都有线程池处理算子下推请求。查询是否启用算子下推,是在查询优化阶段,优化器根据统计信息和执行计划自动决策的。此外用户还可以使用 hint 来控制查询操作是否开启算子下推。算子下推可以很好地处理冷数据,然而,GaussDB(for MyQL)是一个OLTP系统,通常包含并发更新操作。当前计算下推实现,MVCC处理仅在计算节点进行,存储节点只处理可见的行,针对无法判断可见性的行,原样返回至计算节点,通过undo-log回放出对应的数据。 通过算子下推,我们将获得怎样的收益呢?以TPC-H标准测试集(scale factor: 100)为例,CPU:16核,内存: 128GB,计算节点数据库缓冲池大小设置为80GB,采用冷数据进行验证。下图展示了TPC-H Q6, Q12, Q14,  Q15  4 个Query的查询结果,均有20-40倍的性能提升。以Q12为例,只开启NDP,借助分布式存储算力和网络IO缩减,性能提升5倍,同时在计算节点开启并行查询,又获得7倍性能提升,总体提升约35倍,这个提升效果是非常显著的。本文中提到的这些功能都可在实际生产环境中使用,而这只是开始,随着我们将更多计算推送到存储层,更多的查询将从此优化中受益,我们可以期待更大的性能提升。如何启用NDP?开启NDP开关,对当前Session生效,优化器自动判断是否进行计算下推。mysql> show variables like 'ndp_mode'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | ndp_mode | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> explain select count(*) from lineitem; +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------------------------------------------------------------+ | 1 | SIMPLE | lineitem | NULL | index | NULL | PRIMARY | 8 | NULL | 594000899 | 100.00 | Using pushed NDP columns; Using pushed NDP aggregate; Using index | +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------------------------------------------------------------+ 1 row in set, 1 warning (0.00 sec)通过hint方式,使NDP对当前Query生效。mysql> show variables like 'ndp_mode'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | ndp_mode | OFF | +---------------+-------+ 1 row in set (0.00 sec) mysql> explain select count(*) from lineitem; +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------+ | 1 | SIMPLE | lineitem | NULL | index | NULL | PRIMARY | 8 | NULL | 594000899 | 100.00 | Using index | +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> explain select/*+ ndp_pushdown() */ count(*) from lineitem; +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------------------------------------------------------------+ | 1 | SIMPLE | lineitem | NULL | index | NULL | PRIMARY | 8 | NULL | 594000899 | 100.00 | Using pushed NDP columns; Using pushed NDP aggregate; Using index | +----+-------------+----------+------------+-------+---------------+---------+---------+------+-----------+----------+-------------------------------------------------------------------+ 1 row in set, 1 warning (0.00 sec)挑战与今后方向NDP有诸多好处,但它也有一些技术挑战需要我们解决。如分布式存储系统为多租户共享,为了避免不同租户对资源争抢使用,我们需要实现单租户级的资源管控。另外是优化器决策使用NDP的时机,需结合触发网络IO读取的数据量以及已缓存在缓冲池中的数据量综合考虑。GaussDB(for MySQL)是一款云原生数据库,该体系结构支持极其强大和灵活的NDP框架。未来,我们计划利用此框架不仅仅是做查询处理,还将进一步扩展存储层中的数据库功能,这些功能可以与查询下推结合使用。我们相信,云堆栈的深度集成是释放云数据库力量的关键,华为云在实现这一目标方面处于领先地位,正如GaussDB(for MySQL)所证明的那样。请大家保持关注,我们后续将会给大家带来更多精彩技术分享。也欢迎大家前往华为云官网,了解更多GaussDB(for MySQL)详情:https://www.huaweicloud.com/product/gaussdb_mysql.html
  • [版主精选] 连续两年入选Gartner®云数据库管理系统魔力象限™
    近日,国际研究机构Gartner发布2021年全球云数据库魔力象限报告《Magic Quadrant for Cloud DatabaseManagement Systems》华为云凭借GaussDB系列数据库产品入选魔力象限的特定领域者成为亚洲唯二入选的云厂商自2020年首次入选,华为云GaussDB已经连续两年入选 Gartner云数据库管理系统魔力象限。华为云GaussDB在行业实力、亚太市场影响力、云产品组合和生态广度方面十分具有优势,从技术到服务都充分满足了客户业务场景需求。 金融政企优选,市场影响力扩大得益于长期服务政企客户的经验与优势,华为云GaussDB目前已成功在1500+大客户规模商用,广泛应用于金融、泛政府、电信、能源、交通、医疗、物流、电商等领域,历经各种企业级严苛场景考验。在2021年6月Gartner发布的《Market Share **ysis: Database Management Systems, Worldwide, 2020》报告中,华为云凭借数据库在全球营收中排名第十,全球增速48.5%,在数据库市场取得了显著的成绩。持续创新与拥抱开源,让企业上云更轻松高效产品方面,华为云GaussDB围绕“金融级高可用、云栈垂直整合、企业级混合负载、云原生架构、密态数据库、AI-Native自治”等6大基础研究方向,紧密结合客户业务场景,不断进行产品完善和服务创新,积极打造面向客户全场景的一站式数据库服务。生态方面,一个开源开放并凝聚产学研用的良性循环的生态,是大势所趋。华为云积极拥抱并兼容业界主流的关系型数据库生态如MySQL及非关系型数据库MongoDB、Redis等生态。同时对外开源openGauss单机主备能力,积极推进openGauss开源社区“共建共享共治”。此外,还联合产业联盟与标准组织、高校、合作伙伴、开发者打造数据库开放生态,共同繁荣数据库产业。数字化创新之路还在继续,华为云GaussDB会将产品能力应用到更多商业实践,让客户“选型安心、迁移放心、管理省心”,做企业核心数据上云信赖之选。来源:1. Gartner,Magic Quadrant for Cloud Database Management Systems,Henry Cook等人,2021年12月14日2. Gartner,Market Share **ysis: Database Management Systems, Worldwide, 2020,Sharat Menon,2021年6月30日免责声明:Gartner不认可其研究出版物中描述的任何供应商、产品或服务,也不建议技术用户只选择评级最高或其他名称的供应商。Gartner研究出版物由Gartner研究组织的意见组成,不应解释为事实陈述。Gartner不承担与本研究有关的所有明示或暗示的保证,包括任何适销性或适用于特定目的的保证。Gartner和魔力象限是Gartner, Inc.和/或其附属公司在**和国际上的注册商标和服务标记,经许可在此使用。Gartner保留所有权利。
  • [行业资讯] 华为云数据库亮相2021PG中国技术大会并斩获4项大奖
    1月7日-9日,以“开源论道·数据驱动·共建数字化未来”为主题的第十一届PostgreSQL中国技术大会隆重举办。华为多位数据库专家出席并发表了重要演讲,分享了华为云数据库最新技术与实践,积极推动商业落地,使能企业智能升级。会上,华为云RDS for PostgreSQL荣获“数据库最具潜力产品奖”,华为流程IT DBA团队荣获“PostgreSQL中国最具价值团队奖”,华为数据库架构师陈华军和华为云数据库高级专家赵全明分别荣获“十周年卓越贡献特别奖”、“中国PostgreSQL最具价值专家MVP奖”。大会主会场,华为质量与流程IT数据库架构师陈华军分享了《华为集团IT PostgreSQL运用实践》主题演讲。他介绍到,流程IT作为服务华为公司19万员工的企业IT部门,支持华为公司全球业务、全球研发、全球协同,客户覆盖全球170多个国家、服务上亿消费者。为了应对日益增长的业务数据和海量并发问题,华为集团对交易库选择主要采用云数据库PostgreSQL去“O”,通过架构部署、参数调优、客户端配置、监控告警、性能诊断工具、闪回实现等能力,很好地解决了数据库兼容性、扩展性、数据一致性、性能等方面的问题。目前,华为集团IT去“O”和上云工作已完成一半。华为云数据库高级工程师刘志俊在分论坛分享了《Oracle迁移到PostgreSQL的挑战与突破》。他提到,近年来去“O”的呼声越来越大,大量的传统企业开始脱离“IOE”集中式架构,进行云端分布式改造。针对扩展性、技术不可控、高成本等去“O”难题,华为云GaussDB推出了数据库和应用迁移UGO+数据复制服务DRS专属组合解决方案,该方案从迁移评估、结构迁移、应用迁移、全量+增量数据迁移、数据一致性动态校验、流量回放等方面提供了一站式迁移上云方案,让客户上云更轻松、更高效。华为云数据库产品经理冯万里分享了《解锁华为云数据库PostgreSQL高速增长密码》。他表示,华为云数据库PostgreSQL连续3年保持5倍营收增长,持续为5000+客户创造了价值。取得这一重大成果的原因在于,华为云RDS for PostgreSQL具备卓越的高可靠、高安全、插件丰富、兼容开源、便捷上云、低成本等能力优势,并在国家地理信息服务平台天地图和中国一汽等企业中成功落地。华为云数据库高级工程师杨科伟在分论坛分享了《GaussDB(for Redis)的云原生存算分离架构实践》。他指出,存算分离的分布式架构是大势所趋,目前已经有越来越多企业选择将核心业务上云到该架构。华为云GaussDB(for Redis)基于该架构,完美避开了开源Redis的主从堆积、主从不一致、fork抖动、内存利用率只有50%等问题,构筑了强一致、高可用、冷热分离、弹性伸缩、高性能等极具竞争力的能力优势,极大促进了企业业务的发展。华为云数据库高级工程师党李飞分享了《华为云DDS数据库容灾关键技术》。他介绍到,华为云DDS是一款兼容MongoDB 3.4/4.0版本的文档数据库服务,目前支持分片集群、副本集和单节点三种部署架构。华为云DDS基于华为云数据复制服务DRS灾备技术,支持主备倒换,通过数据抓取和数据回放,保障数据一致性。通过优化原生Change Streams,华为云DDS支持更丰富的DDL语句,对于集合删除、数据库删除事件,日志拉取不中断,支撑近50000 ops,与社区版相比,性能得到大幅提升。技术不止步,服务更贴心,华为云数据库未来将会打造更优、更专业高效的数据库服务,赋能企业挖掘数据价值,推动业务革新,加速企业数字化转型。 错过直播不要怕,点击下面链接可直接回顾精彩:1月7日,华为数据库架构师陈华军演讲链接:https://live.csdn.net/room/postgresqlchina/1Ppndzfv?spm=1001.2014.3001.55011月8日,华为云数据库高级工程师刘志俊演讲链接:https://live.csdn.net/room/postgresqlchina/FS0reRwq?spm=1001.2014.3001.55011月9日,华为云数据库高级工程师杨科伟和党李飞、华为云数据库产品经理冯万里演讲链接:https://live.csdn.net/room/postgresqlchina/XB2Uc2AA?spm=1001.2014.3001.5501 【阅读原文】https://www.huaweicloud.com/product/dbs.html
总条数:347 到第
上滑加载中