数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

本文由云+社区发表 作者:技术沙龙
All in 云+时代,数据库的高可用性、按需付费、按需扩展等属性解放了大批开发者。腾讯发布的自研数据库CynosDB作为国内首款同时兼容MySQL和PG的云原生数据库在业内引发热议,还不够了解TA?那么本期分享你一定不能错过!
本期云+社区技术沙龙将全方位解读CynosDB,揭秘技术内幕,解读兼容两大主流开源数据库的一主多读架构、高可用架构及快速恢复实现、可计算智能存储和分布式存储。
点击这里 ,免费报名参加活动!
此文已由腾讯云+社区在各渠道发布
获取更多新鲜技术干货,可以关注我们 腾讯云技术社区-云加社区官方号及知乎机构号
数据库
2019-03-04 17:30:00
本文整理自虎牙中间件团队在 Nacos Meetup 的现场分享,阿里巴巴中间件受权发布。
这次分享的是全球 DNS 秒级生效在虎牙的实践,以及由此产生的一些思考,整体上,分为以下5各部分: 背景介绍; 方案设计和对比; 高可用; 具体实践和落地; 规划;
背景介绍
虎牙用到的基础技术很多,DNS 是其中比较重要的一个环节。
DNS 的解析过程很关键,例如上图中的 DNS 解析器通过一个定位解析追踪到我们的 DNS,再到本地域名服务器迭代解析,经过根域再到.com名,最后到huya.com的根域名,获取最终的解析结果。
在这个过程中, DNS解析是天然的分布式架构,每一层都会有缓存,上一层出现问题挂掉,下一层都会有缓存进行容灾。另外,整个 DNS 协议支持面广,包括手机和 PC,我们用的编程框架里也有 DNS 解析器,服务器也会配 DNS 解析引擎,因此,DNS 在虎牙的基础设施中是很重要的部分。
虎牙的 DNS 的应用现状
虎牙当前主要是依赖于公共的 DNS,相信在座的小伙伴们或多或少都会遇到过下面这些问题: 依赖公共 localDNS,解析不稳定,延迟大。 记录变更生效时间长,无法及时屏蔽线路和节点异常对业务的影响。例如,权威 DNS 全球各节点数据同步时间不可控,全局生效时间超过10分钟;localDNS 缓存过期时间不可控,部分 localDNS 不遵循TTL时间,缓存时间超过48小时。 内部 DNS 功能缺失,无法解决内部服务调用面临挑战。例如,时延大、解析不准、支持多种调度策略。 无法满足国外业务的快速发展,虽然一些海外云厂商提供了基于 DNS 的快速扩容方案,以及基于 DNS 的数据库切换方案。
方案设计和对比
基于以上的问题,我们开始重新规划 DNS 的设计。
名字服务架构
整个规划会分三个方面,核心是我们做了「名字服务」的中心点,基于此,可以满足我们的需求。
一方面通过 Nacos Sync,将现有多个注册中心的服务, 同步到「名字服务」中, 通过 DNS 实现不同框架之间的 Rest 服务方式的调用, 实现例如 Eureka,Consul,Taf等框架之间的服务调用。
另一方面,在全球负载均衡的场景下,由于虎牙是以音视频业务为主,而音视频业务对节点的延迟是非常敏感的,所以我们希望一旦出现节点延迟的情况,能立马做切换。
第三个是传统 DNS 的场景, 可以满足容器和物理机的 DNS 需求, 提供本机 Agent 和集群两种方案, 通过缓存和 prefect 大大提高 DNS 解析的可用性和加快生效时间。
对于名字服务的总体设计主要分3部分,接入层需要提供 API,消息通知和 DNS 接入的能力。核心功能需要能在基于现有网络数据,CMDB 和 IP 库的数据基础上,提供灵活的负载均衡能力,全球数据的秒级同步,多个数据源的同步,能对全网服务的健康状态进行监控,及时感知到大范围的节点异常,并且能够及时将节点的屏蔽的信息推送到端上。
最终,我们选择 Nacos 作为名字服务的核心,提供统一的 API ,包括名字注册、变化推送、负载均衡等;Nacos Sync 作为全球集群间数据同步的组件;DNS - F是客户端组件,用于拦截 DNS 请求,实现基于 DNS 的名字服务。
改造前后 DNS 变更生效流程的不同
接下来,我们通过对比看下改造前后 DNS 变更生效流程的差异。
原有 DNS 变更生效流程中,对 DNS 生效时间有影响的是:
Auth DNS:
跨区域、跨国数据同步慢,不稳定。
bind 在数据量比较大的时候,同步比较慢。
Local DNS:
根据 TTL 缓存,过期后才会刷新数据。
部分厂商不遵循 TTL 时间缓存,超过24小时的缓存时间。
服务器:
服务器开启 nscd 做 DNS 缓存。
业务进程:
应用的 DNS 缓存,比如 Java 虚拟机、框架层的 DNS 缓存。
以上四种情况会比较影响 DNS 的变更生效流程,下图是我们现有的 DNS 变更生效流程:
整体上相对简单,只要业务进程这边将自己内部的 DNS 缓存关掉, 通过 DNS-F 进行查询的时候, 会直接到最近的 Nacos 集群拉取最新的服务节点信息, 而且后续节点的变化也会推送到 DNS-F 中, 后续可以直接在缓存中获取最新信息。
国内 Nacos 集群:
集群内通过 raft 协议同步数据,毫秒级别完成同步。
Nacos Sync:
Nacos 推送变化到 Nacos Sync,跨区域、跨国网络差的情况下可能会导致推送结果丢失,或者延迟加大。
Nacos Sync 会主动拉取实例变更,拉取周期和监听的服务数量会影响到变更时效。
DNS - F:
Nacos 会将变更推送到 DNS - F,网络差的情况可能会导致推送结果丢失,或者延迟加大。
DNS - F 会主动拉取实例变更,拉取周期和监听的服务数量会影响到变更时效。
业务进程:
通过应用禁用 DNS 缓存来解决。
核心设计 Nacos
Nacos 有两套推送机制。
一种是通过客户端来选择一个可获节点,比如它第一次拉取的是一个正常节点,这个正常节点就会跟它维护一个订阅关系,后面有变化就会有一个相应的实地变化推送给我。如果当前节点挂掉, 他会通过重连, 在节点列表上,连上一个正常的节点。这时候会有新的 DNS 关系出现。
另一种是通过 SDK 的方式,在服务端寻找可获节点。服务端每个节点之间, 会进行一个可活的探测, 选择其中一个可活节点用户维护这个订阅关系。 当这个节点出现问题, 连接断开后, SDK 重新发送订阅请求,服务端会再次选择另外一个可活的节点来维护这个订阅关系。这就保证整了推送过程不会因为某个节点挂掉而没有推送。
推送的效率方面,主要是用 UDP 的方式,这个效率不像 TCP 消耗那么高。
以上两个方案都比较适合我们目前的场景。
核心组件设计 Nacos Sync
我们选择 Nacos Sync 作为多集群数据同步的组件,主要是从以下4方面进行考虑的。 同步粒度:
Nacos Sync 同步数据的时候是以服务为维度, 比较容易做最终一致性处理, 同时可以提供保活的机制,满足节点维持的场景。 数据库通过 Binlog 同步的方式只能局限于事务粒度, 而文件同步只能通过单个文件的粒度, 在服务同步这个维度并不是很合适。 可用性方面:
Nacos Sync 作为一个中间件,是以集群方式进行的,传统的数据库和文件方式基本是单进程进行的,可用性方面可能不太满足要求。 同步方式方面:
Nacos Sync 通过在服务粒度的全量写入,满足服务注册和 DNS 这两种场景, 不需要额外的事务消耗, 能保证最终一致即可。 环形同步:
我们国内有多个可获的节点,希望它们之间的数据可以进行环形同步,每个节点之间是相互备份的,这时候用 Nacos Sync 的话,是支持的。虽然数据库方面,比较经典的是主主同步,但如果同时对一个主件进行更新的话,每一个点进行协助是会有问题的,而且文件方面是不支持的。
Nacos Sync 和开源版本的不同
我们对 Nacos Sync 开源方案上做了几处修改,以更好的适用于现在的场景:
第一,通过配置方式对任务进行分拆。因为在实际应用场景里面,因为 Nacos Sync 的任务达一两万,单机很容易到达瓶颈,所以我们通过配置的方式将这些分片到多台 Nacos Sync 机器上。
第二,通过事件合并和队列控制的方式控制 Nacos 集群的写入量,以保证后端的稳定性。虽然下发事件一秒钟只有一个,但在很多场景中,例如需要 K8s 或者 Taf 进行数据同步的时候,变化的频率是非常高的,这时候通过事件合并,每个服务单独进行一个写入进程。这样通过队列控制的方式可以控制整个 Nacos 集群的写入量。
第三,添加了能支持从K8s 和 Taf 同步数据的功能。后期我们会将这个特性提交给 Nacos,让更多的开发者使用。
核心组件设计 DNS - F
DNS - F是基于 CoreDNS 上开发的,我们扩展了以下 4 个组件:
Nacos 插件:查询 Nacos 服务信息,监听 Nacos 服务变化,并将服务转化为域名,实现以 DNS 协议为基础的服务发现;
Cache 插件:提供域名缓存服务;
Log 插件:将 DNS 解析日志上报到日志服务;
Proxy 插件:代理解析外部域名;
DNS - F 和开源版本的不同
第一,在日志组件里面将日志上传到自己的日志服务。
第二,对缓存功能做了一个增强。一般的缓存功能可能根据 TTL 时间会过期,我们把这个过期时间给去掉了,直接令到缓存永远不会过期,然后通过异步将这个缓存进行刷新。比如 TTL 可能快到到时间了,我们就会主动做一个查询或者推送查询,这样,服务端或者公共 DNS 出现问题的时候,就不会影响到整体服务。
第三,增强了高可用的保障能力。包括进程监控、内部运营和外部运营的探测。另外,原来的开源版本用的是本机部署的方式,我们做成了集群化的部署,解决了服务推送、服务负载均衡方面的问题。
高可用
接下来由我们团队的李志鹏,分享一下虎牙在高可用方面的实践。
周健同学跟大家介绍了项目的背景跟方案设计,我来和大家介绍一下具体的实践和落地,实践过程中的主要关注点是高可用。
全球化部署方案
这是虎牙的一个全球化的部署方案,我们在全球部署了两个大区,分别是国内和国外。这两个大区是指定服务同步的,走的是专线,这样可以保障同步的稳定性。在一个大区内我们又部署了多个接入点,例如在国内大区,我们部署了深圳和无锡两个接入点,这两个节点的数据是互相同步、互为备份,保证在一个集群挂掉下可以切换到另外一个集群。
多个接入点的情况下,我们通过 HttpDNS 实现客户端的就近接入。客户端定期请求 HttpDNS,HttpDNS 能根据地域寻找就近接入点。如果接入点出现故障,我们就直接在HttpDNS 把这个节点给摘除,这样客户端就能快速地切换到另外一个接入点。
接下来讲一下单个集群下的部署方案。
单个集群部署了多个 Nacos 节点,并通过7层负载均衡的方式暴露给外面使用,并且提供了多个 VIP,满足不同线路和区域的接入要求。同时,Nacos Sync 做了分片处理,将同步压力分散到各个分片上,一个分片下我们又部署了多个 Nacos Sync 的节点,以保障多活和高可用。
线上演练
演练的场景是模拟一个单个集群挂了和两个集群都挂了。
从图中可以看到,把深圳的流量切走之后,无锡的流量就涨上去了,然后再把无锡的流量切走,再关闭服务,这样就可以看到两边的流量已经没了。之后,再去恢复两个集群的流量,看一下整个切换过程中对服务的影响。
首先看一下对写入的影响,在单个集群挂了的情况下,是没有任何影响的。如果是两个集群都挂了,写入就会失败。可以看到,这个图有一个波峰,这个波峰就是我们两个集群都挂了的情况下,写入失败延迟加大。
但是切换的整个过程对 DNS-F 是没有任何影响的,延迟保持平稳。此外,在集群重新上线前,我们需要做数据校验,保证集群之间元数据和实例数据的最终一致。
可用性级别方面,我们可以保障: 单集群挂掉后不会有影响; 双集群挂掉后只会影响域名变更,不影响域名解析;
线上演练数据校验机制
运行过程中,我们也要保证集群间数据的一致性。我们通过全量校验和增量校验两种手段去保证,全量校验方式如下: 大区内部做10分钟的全量校验,保证大区内各个集群数据的一致; 大区之间做2分钟做一次全量校验,保证大区之间被同步的服务的数据一致性。
增量校验方式如下: 从其他数据源同步的数据,通过数据源的时间戳,做增量校验; 基于API的写入日志,定期校验写入的内容是否已经全部同步。
DNF - S 高可用
关于 DNS - F 的高可用,我们主要做了以下5个点: Agent 的健康状态监测,包括进程存活和是否能正常解析; 缓存内部域名,并做持久化处理,保证 Nacos 集群出现问题时不会影响内部域名的解析; 提供备用节点,保证在 DNS-F 挂了,或者是 DNS-F 需要升级的情况下,也不会影响到内部域名解析; resolv.conf 配置检查,发现127.0.0.1不在配置中会自动添加; 限制 Agent 的 CPU 的使用,避免对业务进程造成影响。
具体的实践和落地
实践一:数据库域名改造
之前的数据库是用 IP 方式接入的,在数据库切换的时候,需要通知每个业务方修改配置,重启服务,这样就带来一个问题:整个过程是不可控的,取决于业务方的响应速度,生效时间通常超过十分钟。
提升数据库切换的关键点,第一个就是切换时不需要业务方参与,能在业务方无感知的情况下进行切换;第二个是实例变化能秒级推送到我们的应用,将应用快速切换到一个新的实例上。
大家可以看一下这个图,这是我们现在做的一个改造,图中的 DMX 是虎牙内部的一个数据库管理系统,思路就是把 DMX 和名字服务打通。DMX 会把数据库实例信息以服务的形式注册到名字服务,服务名就是域名。
实际应用过程中,通过这个域名去访问数据库,应用在访问前首先会经过 DNS - F 去做域名的解析,解析的时候是从名字服务查询实例信息,然后把实例的IP返回给应用。这样,应用就能通过 IP 和我们的数据库实例进行连接。
切换的时候,在 DMX 平台修改域名对应的实例信息,并把变更推送到名字服务,名字服务再推送给 DNS-F,应用在下一次解析的时候就能拿到新的实例 IP,达到切换数据库实例的目的。
这套方案落地后,虎牙的数据库切换基本上在10秒钟之内能够完成。
实践二:内部调用使用内部域名
虎牙部分内部系统之间调用是通过7层负载均衡,但是由于没有内部 DNS,需要通过的公共的 LocalDNS 来解析,这就带来一些问题:
问题一:扩缩容的时候要去修改 DNS 记录,整个过程生效时间可能会超过10分钟,故障的节点会影响业务较长的时间。
问题二:公共的 LocalDNS 智能解析不准确,比如无锡的机器可能会解析到深圳的一个接入点,影响接入质量。
问题三:不支持定制化的负载均衡策略,例如同机房、同大区优先的策略,通过公共 LocalDNS 是实现不了的。
如果想要提升内部服务调用质量,一是 DNS 记录变更绕过 LocalDNS,把 DNS 的记录变更直接推到 DNS-F。二是与内部系统打通,从 CMDB 等内部系统获取机器信息,支持多种负载均衡策略。
大家可以看一下上面的图,这个改造和数据库域名的改造思路是一样的,最右上角有一个7层负载管理系统,我们把这个系统和名字服务打通,7层负载管理系统会把域名信息以服务形式注册到名字服务,变更域名记录时直接从7层负载管理系统推送到名字服务,名字服务再推送到 DNS-F,达到快速切换的目的。
如果域名配置了负载均衡策略,名字服务会从 CMDB 获取机器、机房等信息,打标到域名的实例信息。然后,DNS-F 查询名字服务时,会携带 ClientIp,名字服务根据 ClientIp 的CMDB 信息过滤实例列表,返回同机房的实例给 DNS-F,达到同机房优先的目的。
由此带来的效果是:
第一,服务扩缩容能够秒级完成,减少了故障时间。
第二,扩展了 DNS 的负载均衡策略,例如有些业务是需要在不同区域有不同的接入点的,而且不能跨区域调用,之前的 DNS 负载均衡策略是不能满足这个需求的,但在改造之后,我们能根据 CMDB 信息去做同区域调度的负载均衡策略。
第三,业务在接入内部域名之后,延迟会有明显的下降。上图显示的就是某个服务在接入到内部域名之后,延迟出现明显的下降。
另一个落地的效果就是我们对主机上的域名解析的优化。因为我们的 DNS - F 是部署在每台主机上的,然后提供一个缓存的功能。带来的效果就是: 平均解析延迟会从之前的200毫秒下降到现在的1毫秒;
缓存命中率会从之前的90%上升到99.8%,90%是用 CoreDNS 原生的那个 Cache,99.8%是在这个 Cache 的组件下做了优化之后的效果; 解析失败率是从之前的0.1%下降到0%;
这里再总结一下项目落地的技术价值:
第一,提供了基于 DNS 服务发现的能力,消除异构系统之间互相调用的障碍。
第二,填补了没有内部域名解析能力的空白。
第三,解决我们上面说的内部服务调用面临的挑战:延时大、解析不准、不支持多种负载均衡策略、故障牵引慢。
第四,优化外部域名的解析,屏蔽 LocalDNS 的故障。
落地规模是:DNS - F 覆盖率100%,完成 Taf 和 Eureka 注册中心的数据同步。
后续规划
LocalDNS:
解决公共 DNS 节点位置影响域名解析准确性的问题;
解决内部使用公共 DNS 不稳定的问题;
优化内外网解析;
精准调度:
解决全球 DNS 节点生效慢的问题。
本文作者:
周健:GitHub ID @nanamikon,虎牙中间件团队成员,2012年毕业于中山大学,主要负责名字和配置服务,以及虎牙 DNS 和微服务相关的工作。
李志鹏:GitHub ID @lzp0412,虎牙中间件团队成员,主要负责 DNS,以及服务注册与发现、服务治理、Service Mesh 等相关工作。
阅读原文 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-05-27 12:20:00
文件头部注释
文件头部注释主要是表明该SQL语句的一些基本信息,是该SQL语句实现功能的总体说明,可以增强SQL语句的可读性和可维护性。文件头部注释一般位于 文件顶部。要求至少写出文件名、创建者、创建时间和内容描述。其格式应该尽量约束如下
/**
* @author("张三")
* @description("表描述")
* @version("1.0.0")
* @reference(依赖的表1,依赖的表2)
*/
字段排列要求 SELECT 语句选择的字段按每行一个字段方式编排; SELECT 单字后一个缩进量后直接跟首个选择的字段,即字段离首起二个缩进量; 其它字段首起二个缩进量,逗号之后放置字段名; 两个字段之间的逗号分割符紧跟在第二个字段前; ‘AS’语句应与相应的字段在同一行; 多个字段的‘AS’建议尽量对齐在同一列上;
 
SELECT子句排列要求
SELECT 语句中所用到的FROM、WHERE、GROUP BY、HAVING、ORDER BY、J OIN、UNION等子句,需要遵循如下要求: 换行编写; 与相应的SELECT 语句左对齐编排; 子句后续的代码离子句首字母二个缩进量起编写; WHERE 子句下的逻辑判断符 AND、OR等与WHERE左对齐编排; 超过两个缩进量长度的子句加一空格后编写后续代码
 
运算符前后间隔要求
算术运算符、逻辑运算符的前后要保留一个空格。
CASE语句的编写
SELECT 语句中对字段值进行判断取值的操作将用到CASE语句,正确的编排CASE语句的写法对加强代码行的可阅读性也是很关键的一部分。对CASE语句编排作如下约定: WHERE 子句下的逻辑判断符 AND、OR等与WHERE左对齐编排; 每个WHEN子语句一行编写,当然如果语句较长可换行编排; CASE语句必须包含ELSE子句,ELSE子句与WHEN子句对齐。
子查询嵌套编写规范
子查询嵌套在数据仓库系统ETL开发中是经常要用到,因此代码的分层编排就非常重要。
表别名定义约定
所有的表建议都加上别名。因为一旦在SELECT 语句中给操作表定义了别名,那么在整个语句中对此表的引用都必须惯以别名替代,因此考虑到编写代码的方便性,约定别名尽量简单、简洁,同时避免使用关键字。 表别名采用简单字符命名; 多层次的嵌套子查询别名之前要体现层次关系,SQL语句别名的命名,分层命名,从第一层次至第四层次,分别用P 、S、 U 、D表示,取意为Part , Segment , Unit , Detail。也可用a、b、c、d来表示第一层次到第四层次;对于同一层次的多个子句,在字母后加1、2、3、4……区分; 有需要的情况下对表别名加注释。
SQL注释 每条SQL语句均应添加注释说明; 每条SQL语句的注释单独成行、放在语句前面; 字段注释紧跟在字段后面; 应对不易理解的分支条件表达式加注释; 对重要的计算应说明其功能; 过长的函数实现,应将其语句按实现的功能分段加以概括性说明; 常量及变量注释时,应注释被保存值的含义(必须),合法取值的范围(可选)。
数据库
2019-05-13 15:32:00
Redis 提供了两种方式,实现数据的持久化到硬盘。 1、【全量】RDB 持久化,是指在指定的时间间隔内将内存中的 数据集快照 写入磁盘。实际操作过程是,fork 一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。 - 默认开启rdb持久化 2、【增量】AOF持久化,以日志的形式记录服务器所处理的每一个 写、删除操作 ,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。 - 默认关闭 每次写,删除操作就同步或者每秒同步
(1)RDB 优缺点
优点: 灵活设置备份频率和周期。你可能打算每个小时归档一次最近 24 小时的数据,同时还要每天归档一次最近 30 天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。 非常适合冷备份,对于灾难恢复而言,RDB 是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。推荐,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说 Amazon 的 S3 云服务上去,在国内可以是阿里云的 OSS 分布式存储上。 性能最大化。对于 Redis 的服务进程而言,在开始持久化时,它唯一需要做的只是 fork 出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行 IO 操作了。也就是说,RDB 对 Redis 对外提供的读写服务,影响非常小,可以让 Redis 保持高性能。 恢复更快。相比于 AOF 机制,RDB 的恢复速度更更快,更适合恢复数据,特别是在数据集非常大的情况。
缺点: 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么 RDB 将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。 所以,RDB 实际场景下,需要和 AOF 一起使用。 由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。 所以,RDB 建议在业务低估,例如在半夜执行。
(2)AOF 优缺点
优点: 该机制可以带来更高的 数据安全性 ,即数据持久性。Redis 中提供了 3 种同步策略,即每秒同步、每修改(执行一个命令)同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。 由于该机制对日志文件的写入操作采用的是  append  模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。因为以 append-only 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高。另外,如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在 Redis 下一次启动之前,我们可以通过 redis-check-aof 工具来帮助我们解决数据一致性的问题。 如果日志过大,Redis可以自动启用  rewrite  机制。即使出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。 AOF 包含一个格式清晰、易于理解的日志文件用于记录所有的 修改操作 。事实上,我们也可以通过该文件完成数据的重建。
缺点: 对于相同数量的数据集而言,AOF 文件通常要大于 RDB 文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。 根据同步策略的不同,AOF 在运行效率上往往会慢于 RDB 。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和 RDB 一样高效。 以前 AOF 发生过 bug ,就是通过 AOF 记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似 AOF 这种较为复杂的基于命令日志/merge/回放的方式,比基于 RDB 每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug 。不过 AOF 就是为了避免 rewrite 过程导致的 bug ,因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
(3)如何选择 不要仅仅使用 RDB,因为那样会导致你丢失很多数据 也不要仅仅使用 AOF,因为那样有两个问题,第一,你通过 AOF 做冷备,没有 RDB 做冷备,来的恢复速度更快; 第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug 。 Redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。 如果同时使用 RDB 和 AOF 两种持久化机制,那么在 Redis 重启的时候,会使用  AOF  来重新构建数据,因为 AOF 中的 数据更加完整 。 一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化,但并不推荐这种方式:因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用 RDB 还可以避免之前提到的 AOF 程序的 bug。
数据库
2019-04-30 17:10:00
作者介绍: 于振华,北京银行软件开发部资深架构师,长期从事银行核心系统研发、规划,参与过多个核心信息系统建设工作,包括一、二代支付系统、第四代银行核心系统建设、分布式核心系统建设等企业级项目工作。当前主要研发方向集中在构建先进、高效、面向 OLTP 的银行交易系统,提升银行信息系统服务能力。
本文整理自于振华老师在  TiDB DevCon 2019 上的演讲实录,演讲主题为《TiDB 在银行核心金融领域的研究与实践》。
今天参加 TiDB DevCon 2019 能够和这么多各行各业的朋友一起来交流 TiDB 的实践情况,这个机会非常难得,因为平时都是我们技术团队和 TiDB 团队单向的交流,横向的这种客户之间交流的机会很少,像刚才几位老师讲的,我觉得都很有意思,也希望通过咱们这次大会,大家能擦出不一样的火花。
北京银行和 PingCAP 团队进行了深度的合作,目前有几套重要的实时交易类系统已经对接,包括比较重要网联系统、银联无卡支付、金融互联服务平台等。现在怎么来评价一款产品到底稳不稳,很大程度上要看这款产品在金融,尤其是核心金融的场景有没有应用,能不能支持金融场景的要求。我们是在 2018 年 3 月份、5 月份、6 月份进行了投产。经过半年多的时间,我们看到 TiDB 也能够支持金融场景了。从侧面来讲,分布式数据库技术,确实已经到达了一定的成熟度。
一、背景介绍
我相信这几年,尤其是这三四年,大家应该都有感触。无论是工作方式,还是生活方式,都发生了很大的变化,各种信息、科技产品铺面而来,有人说是这种变化叫 工业科技革命 4.0 。不知道这种提法准确不准确,但这种变化确实对我们银行的系统产生了比较大的挑战。
图 1

在图 1 中 ,我列出了几项,比如 高并发的要求 ,要求你具备很快的扩展能力。再比如产品发布,要求你 具备快速的发布能力 ,在座的应该有很多做产品、做实施的团队,大家应该很有感触,比如可能前一天还无人问津的产品,第二天可能就会卖的很火爆,来的每个项目都是紧急项目,都要求你在最快的时间发布出去。当然还包括一些老生常谈的问题,像 传统架构成本难以控制 ,还有 自主可控亟待攻关 ,其实在传统闭源的生态里面,我们很难达到自主可控的要求。
二、系统分析
图 2

在这种背景下,我们从全局的角度出发,对银行以往的技术形态做了系统性的分析,图 2 中列举了一些典型的架构形态,有一些在现在的银行架构里边还是存在的,比如单体的应用,再比如传统的数据库,现在用的最多的 DB2 和 Oracle,还有传统的单机或者集群部署模式,以及瀑布开发模型,当然还有面向传统架构的运维模式。
今天我们来谈分布式数据库,它是一个新技术,但不能说把以往技术架构就否定掉。以往的技术形态好不好?坦白讲,我认为很好,不好的话不可能支撑了这么多年的金融业务发展,但站在今天这样的时间点来说问题也是存在的。像刚才讲到的,高并发的要求、扩展能力、成本、以及产品交付能力都存在一些不尽如人意的地方。
在这种情况下,我们启动了北京银行新一轮的架构转型的工作,分布式数据库也纳入到我们的工作范围里。我们和 PingCAP 很早就接触了,在一年多的工作过程中,要谈的技术细节、技术方案、工作流程等等这些内容会很多,如果真的来总结一下这项工作是怎么做的话,我总结出以下三条。大家一看可能会觉得很虚,但是你如果真的来实践这件事,也许会有同样的感触。
第一个就是「务实」 。架构转型不是一个为了技术而技术,为了新产品而新产品的工作,而是确实要对你的业务发展、开发、运维的效率有所提升。
第二个,我觉得可能是最重要的,就是要做到「速赢」 。无论是你在什么样的企业来做技术升级,技术转型,或多或少的都会遇到一些阻力,尤其是在传统企业。那做到速赢,迅速的释放价值,让你周围的人、让你的团队、让你的组织,迅速看到它的价值,会让你未来的工作开展更加平滑。
第三个是「全栈」 。因为是整体的架构转型工作,我们希望建设一套平台,它能够释放整体的价值,而不是在乎一城一池的得失。今天本来我想介绍北京银行的应用架构和分布式数据库架构,因为时间关系今天只说一下分布式数据库建设的情况。
三、进展情况
图 3

在介绍具体内容之前,先跟大家同步一下,我们现在的工作进展。2018 年 3 月,我们投产了行业内首个面向核心金融业务的分布式数据库,采用的是 两地三中心五副本 的架构模式。以分布式数据库为基础,5 月份我们投产了网联支付清算平台,这也是很重要的一个带资金业务的实时交易系统,6 月份投产了银联无卡支付平台。这张图(图 3)可能稍微有点老,现在我们投产的还包括金融互联服务平台,IFRS9 减值系统。我们未来要做的事其实和刚才 刘奇 讲的比较一致,包括 HTAP,包括容器云的这些方案等等,这也是我们目前最迫切的需求。
3.1 专项评测
现在回想起来,北京银行开展分布式数据库建设的工作,其实是在行业里面算很早的,也是因为我们开展这件工作的时间比较早,所以在整个过程中遇到了很多的困难困惑。行里的技术力量集中在 DB2、Oracle 上可能比较多,对于分布式数据库的掌握来讲,需要有一个周期。 我们做的第一步,为了保证产品可用,建设了面向金融业务的评测体系 。
图 4

图 4 左上角是面向这个功能的测试,比如数据库有没有高可用性,能不能做线性扩展,有没有在线升级能力,这些都是我们的测试点。图 4 左下角这块,是面向性能的测试, 我们并没有采用市面上已经有的工具,比如 TPCC、Sysbench 等等。因为我们实际分析下来觉得市面已经有的这些工具和我们的金融场景有一些距离,用它们来测试可能不会有很好的参考意义,所以我们自研了这套面向分布式数据库的金融性能评测体系,能够让我们明确出分布式数据库可以应用在金融场景,并且对于功能和性能,让大家能有一个可度量的工具 。
在这个过程中,要感谢支付清算协会、信通院等上级单位和组织给予我们的帮助,另外,我们也和硬件厂商英特尔进行了比较深的合作,比如今年(2018 年)新的硬件平台,我们也做了专项的分布式数据库测试,为未来我们硬件的架构选型提供了有效的参考。
3.2 部署模式
图 5

对于分布式数据库的技术层面来讲,刚才几位讲师介绍的比较多了,我就来讲一些北京银行比较不一样的、走在前面的一些地方。 大家看到图 5 这套架构是北京银行的数据存储层的架构。 北京银行的架构采用两地三中心五副本的模式部署 。
跨城长距离的分布式数据库建设具有很大的挑战。比如北京和西安大概一千多公里,两地距离比较远,延时比较高,我们实测的延时大概是十七毫秒左右。这十七毫秒,如果放在一条 SQL 来讲,一来一回三十几毫秒,这样的延时我们肯定是接受不了。所以在这种情况下, 我们用了一个五副本的模式:北京两个 IDC,各放置两副本,西安一个 IDC 放置一个副本,采用 2:2:1 的模式。这样做的好处就是当前端应用请求过来之后,不需要用到北京到西安的这个网络,北京的四个副本中成功三个,就可以给前端实时返回,而且北京的部分实例允许失效。这样做 SQL 平均延时,大概在 1.2 毫秒左右,.95 延时大概 5 毫秒左右,这是比较不错的一个成绩(网联、银联的业务其实要比互联网业务复杂很多) 。
这里给大家分享一个我们实际在生产过程中遇到的一个小故事。在某个周六的中午我接到我们运维值班人员的电话,他说 TiKV 存储服务器坏了一台,当日我第一时间问的是:坏了一台有没有影响服务。他说没有影响服务,服务还是正常的。我说那就赶紧找硬件厂商给修一下机器。当时还觉得挺高兴的,不经意间在生产系统验证了一把。到了第二天周日的中午,他又给我打了一个电话,说又坏了一台服务器。当时有一些担心,是不是我们这批采购的硬件服务器有什么问题,想到这点就立马做排查,当然第一时间问的还是有没有影响服务,他说没有影响服务。 这样连着两天坏了两台存储服务器都没有影响服务,也证明了多副本方案的有效性 。
3.3 两地三中心
图 6

图 6 展示的是整个包括应用、F5 到 TiDB、PD、TiKV 等整个部署的模式。目前我们接着有网联、银联这两个比较大的系统,这两个系统业务量相对来讲比较大,每天有一两百万笔的业务。在西安,我们还部署了一个从集群,那这个从集群是做什么呢?这个从集群就是为了对接一些 OLAP 或者说比较大的报表的情况,从而避免它对主集群的负载产生过大的影响。
四、应用实践
4.1 出现过的问题
图 7

有人说“当你有了锤子,好像什么问题都看上去像钉子”。我们期待从传统数据库过渡到分布式数据库,什么问题都可以解决。但事实上,肯定是没有一个万能的技术方案。图 7 右下角,我列了一些从我们项目开展之初到现在,产生一些问题或者说一些小插曲。
比如我们刚才介绍了行里的 DB2、Oracle 应用的比较多。DB2、Oracle 以前用的是 READ COMMITTED 的隔离级别,那现在到了 TiDB 的 Repeatable Read 的这种形式可能还需要适应。我们建设初期也出现过这种问题:这边 Insert 的数据,那边却查不到,就因为 TiDB 是这种快照的隔离级别。
还有执行计划的索引没有选中的问题,这个在我们实际的生产过程中也遇到过,明明有索引,却没有精确选中那一个索引。造成 SQL 运行的特别慢,内存吃的也比较多。这个问题,我觉得是可以解决好的,临时解决方案就是手动强制加 Hint,未来我相信 TiDB 在版本升级上也会考虑这一点,让执行计划更加准确。
还有热点数据的问题,热点数据指望数据库来解决,现阶段来看是不可能了。无论是传统数据库,还是分布式数据库,要引入另外的应用缓存的组件才可以解决,在传统方案里边,我们做的技术方案也有很多,像比较传统的散列方式,把热点数据散列出去来解决,现在有了缓存,可以引入缓存解决这件事。
我们应用架构采用微服务的形态,对比单体应用形态,微服务对于数据库的要求会更高。因为传统的单体应用,事务的 SQL 数量比较多,划分成微服务的话,无论是应用逻辑,还是数据库的处理逻辑,都会比较细粒度,事务提交次数成倍增长,对于 MVCC 的乐观提交模型有一定的压力,在我们实测的过程中,越细粒度的可能表现的性能越不好。
以上就是我们实践过程中出现的一些小插曲。
4.2 与互联网行业在应用实践上的区别
图 8

今天很多来自互联网企业的朋友也分享了自己的经验, 那在金融行业做分布式数据库落地和互联网行业有什么不同呢 ?
首先来讲,银行的发展时期和很多互联网新兴科技公司是不同的,银行有很成熟的硬件体系、部署模式、软件的设计模式、开发模式、运维模式,站在这种平台上来做新型技术落地会更加的困难 。为什么会得到这个结论?因为现在也有很多的软件厂商,很多做产品的人,大家都希望做新建系统的事情。但对于庞大的历史系统做迁移的话,肯定不会是一刀切的方案,因为代价太大了。所以需要并行运行,对于这种新旧架构并行,很多时候就没有了方案,做不了。其实现在我们也在做这项工作,做一个新旧系统优雅的并行方案,包括业务逻辑的并行,还有业务数据的并行,如果大家有兴趣的话,也可以和我们私下交流这部分内容,我觉得这是很重要的一个事情。
第二点就是组织架构不同 。就拿微服务来说,单体的应用发展这么多年,每一个应用它的技术负责人是谁,对应的业务负责人是谁,是哪个部门,都很明确。如果做微服务化,进行拆分,很多情况下很难确定权责,如果要企业组织架构来适应系统架构也不太现实。当然历史资产、业务场景和互联网企业也是不一样的,银行信息化历史资产更多、业务比互联网更加复杂。
4.3 新型架构
图 9

图 9 是我们系统建设架构图的一部分,最底下是分布式 NewSQL 数据库的基础平台,上边是应用系统,目前是传统架构和新型微服务架构并存。
五、未来展望
图 10

最后再介绍一下未来我们的建设方向。
第一,经过阶段性的实践,新的架构仍需要进行多方位的验证,来确保高可用性、扩展性、成本等方面的优势。下一个阶段我们希望扩大应用范围,把业务发展快、规模大、对并发要求高的系统,逐步的迁移过去。
第二,我们要建立一套应用规范,或者说面向 TiDB 的金融级开发的规范指引。目前我们正在做这个事儿,包括最佳研发应用实践以及新老架构并行方案。建设传统数据库和 TiDB 之间的异构数据库传输的中间件是我们目前很重要的一项工作,这部分做完之后,相信对我们扩大应用会比较有好处。
第三,我们还要做 HTAP,这点和刚才 刘奇 谈到的可能会比较契合。之前我看过 TiFlash 的设计理念和设计方式,我觉得是比较新颖的一种方式,比现在有些还需要 T+1 的数据分析方案会好很多,技术架构更加一体化、业务过程更加流畅。另外,我们一直在做性能提升、网络依赖消减等工作。
最后,我们也希望能够把北京银行的经验和大家多多分享,让大家不再遇到我们建设过程中遇到的问题和麻烦,更加顺畅的进行架构转型工作。
以上就是我今天分享的内容,谢谢大家。
数据库
2019-04-18 11:29:00
一、前言
MySQL 的锁按照范围可以分为全局锁、表锁、行锁,其中行锁是由数据库引擎实现的,并不是所有的引擎都提供行锁,MyISAM 就不支持行锁,所以文章介绍行锁会以InnoDB引擎为例来介绍行锁。
二、全局锁
MySQL 提供全局锁来对整个数据库实例加锁。
语法: FLUSH TABLES WITH READ LOCK
这条语句一般都是用来备份的,当执行这条语句后,数据库所有打开的表都会被关闭,并且使用全局读锁锁定数据库的所有表,同时,其他线程的更新语句(增删改),数据定义语句(建表,修改表结构)和更新类的事务提交都会被阻塞。
在mysql 8.0 以后,对于备份,mysql可以直接使用备份锁。
语句: LOCK INSTANCE FOR BACKUP UNLOCK INSTANCE
这个锁的作用范围更广,这个锁会阻止文件的创建,重命名,删除,包括 REPAIR TABLE TRUNCATE TABLE, OPTIMIZE TABLE 操作以及账户的管理都会被阻塞。当然这些操作对于内存临时表来说是可以执行的,为什么内存表不受这些限制呢?因为内存表不需要备份,所以也就没必要满足这些条件。
三、表锁
Mysql的表级别锁分为两类,一类是元数据锁(Metadata Lock,MDL),一种是表锁。
元数据锁(MDL) 不需要显式使用,在访问一个表的时候会被自动加上。这个特性需要MySQL5.5版本以上才会支持,当对一个表做增删改查的时候,该表会被加MDL读锁;当对表做结构变更的时候,加MDL写锁。MDL锁有一些规则: 读锁之间不互斥,所以可以多线程多同一张表进行增删改查。 读写锁、写锁之间是互斥的,为了保证表结构变更的安全性,所以如果要多线程对同一个表加字段等表结构操作,就会变成串行化,需要进行锁等待。 MDL的写锁优先级比MDL读锁的优先级,但是可以设置max_write_lock_count系统变量来改变这种情况,当写锁请求超过这个变量设置的数后,MDL读锁的优先级会比MDL写锁的优先级高。(默认情况下,这个数字会很大,所以不用担心写锁的优先级下降) MDL的锁释放必须要等到事务结束才会释放
所以我们在操作数据库表结构时候必须要注意不要使用长事务,这里具体是什么意思呢?我举个例子说明下:
![](
上图表示演示了4个session执行语句,首先SessionA开启了事务没有提交,接着sessionB执行查询,因为是获取MDL读锁,所以互相不影响,可以正常执行,SessionC新增一个字段,由于MDL写和读是互斥的,所以SessionC会被阻塞,之后SessionD开始执行一个查询语句,由于SessionC的阻塞,所以SessionD也阻塞了。所以,我们模拟的SessionA的事务是长事务,然后后面执行了修改表结构,会导致后续对该表所有的读写操作都不可行了。所以在实际场景中,如果业务请求比较频繁的时候,对表结构进行修改的时候就有可能导致该库的线程被阻塞满。
表锁 的语法如下: LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias] lock_type] ... lock_type: { READ [LOCAL] | [LOW_PRIORITY] WRITE } UNLOCK TABLES
表锁分为读锁和写锁 ,读锁不互斥,但是获取读锁不能写入数据,其他没有获取到读锁的session也是可以读取表的,所以读锁的目的就是限制表被写。如果表被读锁锁住后,再执行插入语句会报错,报错如下: 1099 - Table 'XXXX' was locked with a READ lock and can't be updated
写锁 被获取后可以对表进行读写,写锁是互斥的,一旦某个session获取到表的写锁,另外的session无法访问这个表,直到写锁被释放。
表的解锁可以使用 unlock tables 解锁,也可以客户端口自动解锁。 lock tables 锁表会独占式的锁住表,除了限制其他线程对该表的读写,也会限制本线程接下来的操作对象。
四、行锁(InnoDB)
MySQL的行锁是在引擎层面实现的,所以这里讨论的也是InnoDB引擎下的行锁,下面会详细介绍InnoDB下常见的几种行锁
4.1 共享锁
共享锁能允许事务获取到锁后进行读操作,共享锁是不互斥的,一个事务获取到共享锁后,另外一个事务也可以获取共享锁,获取共享锁后不能进行写操作。
4.2 排它锁
排他锁允许事务获取到锁后进行更新一行或者删除某一行操作,排他锁顾名思义是互斥的,一个事务获取到排他锁后,其他事务不能获取到排他锁,直到这个锁被释放。
4.3 意向锁
InnoDB支持多种粒度的锁,允许行锁和表锁共存,这里说的 意向锁其实是一种表级别的锁 ,但是我把它放在行锁里面是因为它不会单独存在,它的出现肯定会伴随着行锁(共享锁或者排他锁),它主要的目的就是表示将要锁定表中的行或者正在锁定表中的行。
意向锁根据和行锁的组合可以分为: 意向排他锁:表明将要在表中的某些行获取排他锁 意向共享锁:表明将要在表中的某些行获取共享锁
意向锁的获取必须在行锁获取之前,也就是说获取共享锁之前必须先要获取共享意向锁,对于排他锁也是一样的道理。
那么这个意向锁到底有什么作用呢?
解释这个之前,我们先看看意向锁和行锁之前的兼容关系:
--- 排他锁(X) 意向排他锁(IX) 共享锁(S) 意向共享锁(IS)
排他锁(X) 意向排他锁(IX) 共享锁(S) 意向共享锁(IS)
冲突 冲突 冲突 冲突
冲突 兼容 冲突 兼容
冲突 冲突 兼容 兼容
冲突 兼容 兼容 兼容
我们假设有2个事务A和事务B,事务获取到了共享锁,锁住了表中的某一行,这一行只能读,不能写,现在事务B要申请整个表的写锁。如果事务B申请成功,那么肯定是可以对表中所有的行进行写操作的,那么肯定与A获取的行锁冲突。数据库为了避免这种冲突,就会进行冲突检测,那么如何去检测呢?有两种方式: 判断表是否已经被其他事务用表级锁锁住。 判断表中的每一行是否被行锁锁住。
判断表中的每一行需要遍历所有记录,效率太差,所以数据库就用第一种方式去做冲突检测,也就是用到了意向锁。
总结
本文主要从MySQL的加锁范围来分析了MySQL的锁,MySQL根据加锁范围可以分为全局锁、表锁、行锁。全局锁和表锁是MySQL自己实现,行锁都是由引擎层面去实现。InnoDB下的行锁主要分为共享锁和排他锁。共享锁请求后,行只能读,共享锁之间不互斥。排他锁获取后能更新和删除行,排他锁与其他锁都互斥。最后我在行锁的基础上提到了意向锁,意向锁主要表示正在锁住行或者即将锁住行,为了在锁冲突检测中提高效率。当然InnoDB下还有其他锁,比如间隙锁,记录锁,Next-Key锁等,这些都不在本文的探讨范围之内,如有兴趣的同学可以自行研究。
参考 MySQL用户手册之InnoDB锁 MySQL用户手册之事务和锁 《MySQL实战45讲》
数据库
2019-04-15 09:19:00
如上所示的一个sql查询,在hourflag字段已经加了索引的情况下 ,仍然使用的是ALL扫描全表
查资料得出原因是因为mysql优化器认为走全表扫描更快
那么此时就需要告诉优化器,强制使用XXX索引,可以增快查询速度,
此时把sql给改成 explain SELECT city_name, hourflag, eletricflag FROM t_eletric_segment_info force index(index_hourflag) WHERE hourflag >= '2019032600' AND hourflag <= '2019040123' 关键就是where前面的这个 force index(index_hourflag)
此时可以看到,索引生效,查询速度快了5倍
数据库
2019-04-02 16:15:00
Mybatis 配置初始化过程
测试代码
SqlMapConfig.xml
StudentMapper.xml
Student package com.zhiwei.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student { private int sid; private String stuName; private String stuPasswd; private int stuAge; private String stuGender; private float stuScore; }
StudentMapper package com.zhiwei.advanced.mapper; import com.zhiwei.advanced.pojo.StudentCustomer; import com.zhiwei.advanced.pojo.StudentQueryVo; import com.zhiwei.entity.Student; import java.util.List; public interface StudentMapper { public Student findStudentById(Integer sid); }
启动类 sqlSession.getMapper(StudentMapper.class).findStudentById(id);
工作流程
解析XML映射为org.apache.ibatis.session.Configuration对象,并以此为基础创建SessionFactory, SessionFactory创建的Session与Executor绑定,交由Executor执行
配置加载过程
SessionFactory构建代码 Inputstream is = Resources.getResourceAsStream("SqlMapConfig.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SessionFactory构建
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse());
XMLConfigBuilder 工具类:主要解析SqlMapConfig.xml private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
new Configuration(): 默认填充配置:类似缓存别名映射类 public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class); typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); }
XML解析:parser.parse()
parsed解析标志,底层调用 parseConfiguration(parser.evalNode("/configuration")); 按照Xpath语法解析SqlMapConfig.xml根节点 private void parseConfiguration(XNode root) { try { // 解析SqlMapConfig.xml的properties标签, configuration.setVariables(defaults)解析properties文件属性到configuration缓存 propertiesElement(root.evalNode("properties")); // setting配置: configuration.setVfsImpl(vfsImpl) Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); //别名配置:configuration.typeAliasRegistry //dtd约束:mybatis-3-config.dtd //package: key:类简单名驼峰命名法,class:类全限定名 //typeAlias: key: alias自定义别名 type:类全限定名 //注意:最好不用直接引用package形式,否则多个包存在相同名的类会出现冲突 typeAliasesElement(root.evalNode("typeAliases")); //插件配置:相当于拦截器可自定义操作,例如分页、CRUD操作拦截,类似AOP:configuration.interceptorChain pluginElement(root.evalNode("plugins")); //设置对象工厂:哦、configuration.objectFactory objectFactoryElement(root.evalNode("objectFactory")); //社会组obectMapper工厂:configuration.objectWrapperFactory objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //设置反射器工厂:configuration.reflectorFactory,反射机制操作 reflectorFactoryElement(root.evalNode("reflectorFactory")); //mybatis全局配置:若无自定义则使用默认配置 settingsElement(settings); //设置环境:configuration.environment,本质缓存当前工作数据库信息 //注意:Environment可设置多套数据库环境,通过default属性指定具体数据库 environmentsElement(root.evalNode("environments")); //明确指定mybatis工作的数据库ID:数据库产品名称 databaseIdProviderElement(root.evalNode("databaseIdProvider")); //类型处理器:解析具体的Mapper数据类型: configuration.typeHandlerRegistry //作用:数据库操作Mapper的解析和转换工作 typeHandlerElement(root.evalNode("typeHandlers")); //mapper处理:configuration.mapperRegistry mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
Mapper标签解析 private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { //package形式批量配置:常用 if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); //mapper单个独立配置 } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
mapperParser.parse(); 解析 //解析流程 public void parse() { //判断StudentMapper.xml是否已加载 if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } //XML解析可能出现问题,重复处理,例如子ResultMap先于父ResultMap加载 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
configurationElement(parser.evalNode("/mapper")); 解析Mapper标签 private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); //解析parameterMap标签:configuration.parameterMaps parameterMapElement(context.evalNodes("/mapper/parameterMap")); //解析resultMap标签:configuration.resultMaps resultMapElements(context.evalNodes("/mapper/resultMap")); //解析sql标签:configuration.sqlFragments sqlElement(context.evalNodes("/mapper/sql")); //解析:CRUD标签映射为MapperStatement //分析:构建sql原材料ParameterMaps、ResuldMap、sql片段、namespace准备后开始构建 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } }
构建MapperStatement private void buildStatementFromContext(List list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
statementParser.parseStatementNode(); 解析成MapperStatement public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); Class resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); //填充include refid引用sql片段 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); //解析mybatis selectkey主键生成策略标签 processSelectKeyNodes(id, parameterTypeClass, langDriver); // 解析sql SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; //MapperStatementId生成策略:名称空间 + id + !操作类型key String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
configuration添加MapperStatement public MappedStatement addMappedStatement( String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class parameterType,String resultMap,Class resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang, String resultSets) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); } id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); return statement; }
绑定命名空间: bindMapperForNamespace();
org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace
本质:命名空间和Mapper接口绑定,方便后续通过Mapper操作动态生成代理类 private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); //mapperRegistry:保存代理工厂类 configuration.addMapper(boundType); } } } }
至此MapperStatement初始化工作完成
数据库
2019-04-02 11:44:00
《漫步华尔街》的读书笔记作文2200字:
艾略特波浪理论:艾略特波浪理论的立论前提是:股市存在着可以预测的如同波浪一样波动的投资者心理,并且波动的投资者心理会推动市场自然而然地潮起潮落、涨涨跌跌。艾略特认为,通过观察投资者的心理波动可以识别市场的重要变动。
对投资者的启示:股价过去的走势不可能以任何有意义的方式用于预测未来的股价。…各种技术理论只是富了炮制和推销技术服务的人,以及雇佣技术分析师的证券经纪公司。这些证券经纪公司希望分析师的分析有助于鼓动投资者更频繁的进行买入卖出的交易,从而为公司带来更多的佣金收入。使用技术分析确定股票买卖时机的做法尤为危险。因为股票市场有着长期向上的趋势,所以持有现金可能风险非常大。为避开市场阶段性下跌而经常保持大量现金头寸的投资者,很可能在市场迅猛涨升的某些阶段被排斥在市场之外。
小结:本章作者分析了许多技术分析存在的问题,对投资者有很深的教育意义。技术分析忽视了政策与外部环境对大盘和公司的影响,也忽视了公司自身发展对股价的影响。技术分析可能预测对几次,但是人们往往忽视了他们没有预测对的多次。国内个别的所谓“专家”、“股神”也是一样。在股市不长的历史中,偶尔在一个小的时间段出现几个认识正确的人是正常的,但是他们的理论却不一定永远有效。“一个坏的钟表一天中还有两次撞对的时候”,因此,对于他们的理论,还需要自己认真学习、辨识,切忌人云亦云,毫无主见。
第七章.基本面分析究竟有多出色
证券分析师果真是天眼通吗?
在20世纪80年代之后,即便强大的IBM也未能延续可靠的增长模式,宝丽来、柯达、北电网络、施乐以及众多的公司,它们都曾经实现过持续的高速增长,道德经读后感(http://www.simayi.net/duhougan/8842.html)直到有一天“屋顶坍塌”,神话终结。…即使在20世纪90年代经济繁荣发展的时期,也只有1/8的大公司每年成功实现了持续增长;而在进入新千年的头几年里甚至没有一家大公司实现增长。分析师不可能预测连续的长期增长,因为这根本就不存在。
直言不讳地说,这些证券分析师认真估测的数据(基于行业研究、工厂参观等)并不比通过简单的外推以往趋势得到的估计情况准确多少,……任何行业都不是容易预测的。
在某一年比一般同行表现出色的分析师,在下一年丝毫不比其他人更有可能做出上乘的预测。
水晶球为何浑浊不清
我认为有五个因素可以帮助解释证券分析师为何在预测未来时困难非常之大。这五个因素是:
1)随机事件的影响;
2)公司利用“寻机性”会计处理方法制作靠不住的盈利报告;
3)许多分析师基本不称职;
4)最佳分析师流向营销部门或转而管理投资组合、对冲基金;
5)研究部门与投资银行部门之间的利益冲突。
在20世纪90年代和21世纪初,会计舞弊行为看来更加司空见惯了。破产倒闭的网络公司、高科技领先企业,甚至“旧经济”的蓝筹公司全都卖力的通过媒体大肆宣传自己的盈利,误导投资者。
众多分析师纷纷效仿“路易(一个数据计算错误而误导他人购买证券的分析师)”。一般情况下,由于太懒惰不想亲自做预测,他们宁愿抄袭其他分析师的预测结果,或者不加咀嚼便囫囵吞下公司管理层发布的“指导性意见”。
证券分析师习惯上很少建议投资者卖出股票,这一点表明了他们与投资银行业务之间存在密切关系。…分析师不愿意得罪作建议时涉及的公司。…在互联网泡沫期间,大多数个人投资者却令人悲哀的接受了分析师的字面(“买入”和“持有”)意思。
中间道路:我的个人观点
我们先简要复述一下有关股市如何运行的两种截然不同的观点:大多数投资经理的观点是:专业人士在资金管理方面毋庸置疑会胜过所有业余和漫不经心的投资者;另一方面,学术界的多数人士认为:由专业人士管理的投资组合的绩效并不能超过随机选取的具有相同风险特征的股票组合。
我走的是中间道路。
此外,我也不造成全盘接受市场有效理论的主张,部分原因是该理论建立在几个脆弱的假设之上。第一个经不住推敲的假设是市场存在充分定价。…这种推理思路是接近于“博傻”理论的思考方式。股票市场有时并非根据什么人的价值估测值进行交易——投资者经常在投机狂潮中被冲昏头脑。专业投资者在很大程度上对20世纪的数次股市狂潮包括世纪之交的互联网泡沫负有责任。有效市场理念的第二个脆弱假设是信息瞬间完成传播。第三个是将一只股票已知的信息转换成真实价值的估计值会面临极大的困难。
小结:
一、虽然基本面分析不一定在短期内有效,但是在长时间内是有效的。作者把短期与长期股市的走势与预测弄混了。如茅台、苏宁等几年涨了十几倍,而许多股几年未涨过。
二、虽然许多证券分析师对基本面分析存在偏差,甚至错误,但也说明了整体收益仍大于大多数散户。这也是作者鼓励买共同基金的原因。
三、个人仍然要选择基本面好、管理严格、业绩稳定增长的公司,虽然这些公司以后发展速度无法预测准确,但总比差公司稳定,且共同基金也是会买入这些公司的股票。四、后面分析的三点是对市场有效性致命的三点驳斥。
作者在本章列举了美国七十年代、八十年代、21世纪初许多基金排名的变化和业绩增长速度的变化。从中可以看出,股市如人生,某些与指数变化相同的基金就如同我们普通人的财富增长与社会财富增长同步一样。而在某一段时间表现特别好的基金在另一段时间却表现一般,就像某一段时间的明星、名人、学习尖子在以后的岁月中不一定出类拔萃一样。唯有巴菲特管理的公司能够持续稳定地增长50多年,也与巴菲特不断的学习进步和不随波逐流有很大的关系。
数据库
2019-03-28 22:25:00
一、语法: CAST (expression AS data_type)
参数说明: expression:任何有效的SQServer表达式。
AS:用于分隔两个参数,在AS之前的是要处理的数据,在AS之后是要转换的数据类型。 data_type:目标系统所提供的数据类型,包括bigint和sql_variant,不能使用用户定义的数据类型。
使用CAST函数进行数据类型转换时,在下列情况下能够被接受: (1)两个表达式的数据类型完全相同。 (2)两个表达式可隐性转换。 (3)必须显式转换数据类型。 如果试图进行不可能的转换(例如,将含有字母的 char 表达式转换为 int 类型),SQServer 将显示一条错误信息。 如果转换时没有指定数据类型的长度,则SQServer自动提供长度为30。
二、注意事项:
(1).CAST()函数的参数是一个表达式,它包括用AS关键字分隔的源值和目标数据类型。以下例子用于将文本字符串'12'转换为整型: SELECT CAST('12' AS int)
(2).返回值是整型值12。如果试图将一个代表小数的字符串转换为整型值,又会出现什么情况呢? SELECT CAST('12.5' AS int)
(3).CAST()函数和CONVERT()函数都不能执行四舍五入或截断操作。由于12.5不能用int数据类型来表示,所以对这个函数调用将产生一个错误: Server: Msg 245, Level 16, State 1, Line 1 Syntax error converting the varchar value '12.5' to a column of data type int.
(4).要返回一个合法的数值,就必须使用能处理这个值的数据类型。对于这个例子,存在多个可用的数据类型。如果通过CAST()函数将这个值转换为decimal类型,需要首先定义decimal值的精度与小数位数。在本例中,精度与小数位数分别为9 与2。精度是总的数字位数,包括小数点左边和右边位数的总和。而小数位数是小数点右边的位数。这表示本例能够支持的最大的整数值是9999999,而最小的小数是0.01。 SELECT CAST('12.5' AS decimal(9,2)) decimal数据类型在结果网格中将显示有效小数位: 12.50
(5).精度和小数位数的默认值分别是18与0。如果在decimal类型中不提供这两个值,SQL Server将截断数字的小数部分,而不会产生错误。 SELECT CAST('12.5' AS decimal) 结果是一个整数值:12
数据库
2019-03-28 20:55:00
结构化查询语言(Structured Query Language),简称SQL,是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。
结构化查询语言是高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统, 可以使用相同的结构化查询语言作为数据输入与管理的接口。结构化查询语言语句可以嵌套,这使它具有极大的灵活性和强大的功能。
开始学习: SQL完全自学手册
SQL 是一种标准 - 但是...
虽然 SQL 是一门 ANSI(American National Standards Institute 美国国家标准化组织)标准的计算机语言,但是仍然存在着多种不同版本的 SQL 语言。
然而,为了与 ANSI 标准相兼容,它们必须以相似的方式共同地来支持一些主要的命令(比如 SELECT、UPDATE、DELETE、INSERT、WHERE 等等)。
在您的网站中使用 SQL
要创建一个显示数据库中数据的网站,您需要:
RDBMS 数据库程序(比如 MS Access、SQL Server、MySQL)
使用服务器端脚本语言,比如 PHP 或 ASP
使用 SQL 来获取您想要的数据
使用 HTML / CSS
RDBMS
RDBMS 指关系型数据库管理系统,全称 Relational Database Management System。
RDBMS 是 SQL 的基础,同样也是所有现代数据库系统的基础,比如 MS SQL Server、IBM DB2、Oracle、MySQL 以及 Microsoft Access。
RDBMS 中的数据存储在被称为表的数据库对象中。
表是相关的数据项的集合,它由列和行组成。
更多精品课程:
阿里云云计算助理工程师认证(ACA)
阿里云大数据助理工程师认证(ACA)
阿里云云安全助理工程师认证(ACA)
数据库
2019-03-28 18:26:00
要想知道每个数据库的大小的话,步骤如下:
1、show DATABASES;
2、进入information_schema 数据库(存放了其他的数据库的信息)
use information_schema;

3、查询所有数据的大小:
select concat(round(sum(data_length/1024/1024),2),'MB') as data from tables;

4、查看指定数据库的大小:
比如查看数据库home的大小
select concat(round(sum(data_length/1024/1024),2),'MB') as data from tables where table_schema='home';

5、查看指定数据库的某个表的大小
比如查看数据库home中 members 表的大小
select concat(round(sum(data_length/1024/1024),2),'MB') as data from tables where table_schema='home' and table_name='members';
数据库
2019-03-28 14:19:00
要在启动时自动运行Flyway数据库迁移,请将其添加 org.flywaydb:flyway-core 到类路径中。
迁移是表单中的脚本 V__.sql (使用 下划线分隔的版本,例如“1”或“2_1”)。默认情况下,它们位于名为的文件夹中 classpath:db/migration ,但您可以通过设置修改该位置 spring.flyway.locations 。这是一个或多个 classpath: 或 filesystem: 位置的逗号分隔列表。例如,以下配置将在默认类路径位置和 /opt/migration 目录中搜索脚本: spring.flyway.locations = classpath:db / migration,filesystem:/ opt / migration
您还可以添加特殊 {vendor} 占位符以使用特定于供应商的脚本。假设如下: spring.flyway.locations = classpath:db / migration / {vendor}
db/migration 前面的配置不是使用,而是根据数据库的类型(例如 db/migration/mysql MySQL)设置要使用的文件夹。支持的数据库列表可在以下位置找到 DatabaseDriver 。
FlywayProperties 提供了大部分Flyway的设置和一小组附加属性,可用于禁用迁移或关闭位置检查。如果您需要更多控制配置,请考虑注册 FlywayConfigurationCustomizer bean。
Spring Boot调用 Flyway.migrate() 以执行数据库迁移。如果您想要更多控制权,请提供 @Bean 实施方案 FlywayMigrationStrategy 。
Flyway支持SQL和Java 回调 。要使用基于SQL的回调,请将回调脚本放在该 classpath:db/migration 文件夹中。要使用基于Java的回调,请创建一个或多个实现的bean Callback 。任何此类bean都会自动注册 Flyway 。它们可以通过使用 @Order 或实施来订购 Ordered 。 FlywayCallback 也可以检测实现已弃用接口的Bean ,但它们不能与 Callback bean 一起使用。
默认情况下,Flyway会在您的上下文中自动装配( @Primary ) DataSource 并将其用于迁移。如果您想使用其他的 DataSource ,可以创建一个并将其标记 @Bean 为 @FlywayDataSource 。如果您这样做并想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary 。或者,您可以 DataSource 通过设置 spring.flyway.[url,user,password] 外部属性来使用Flyway的原生。设置任何一个 spring.flyway.url 或 spring.flyway.user 足以使Flyway使用它自己的 DataSource 。如果未设置三个属性中的任何一个, spring.datasource 则将使用其等效属性的值。
有一个 Flyway示例 ,您可以看到如何设置。
您还可以使用Flyway为特定方案提供数据。例如,您可以放置​​特定于测试的迁移, src/test/resources 并且仅在应用程序启动测试时才运行它们。此外,您可以使用特定于配置文件的配置进行自定义, spring.flyway.locations 以便某些迁移仅在特定配置文件处于活动状态时运行。例如,在中 application-dev.properties ,您可以指定以下设置: spring.flyway.locations = classpath:/ db / migration,classpath:/ dev / db / migration
使用该设置, dev/db/migration 仅在 dev 配置文件处于活动状态时才会运行迁移。
数据库
2019-03-26 21:28:00
CentOS7.5 install Oracle12.1 and update
1.1 、 install centos7.5
1.2 、 vi etc/sysctl.conf
add row:
fs.aio-max-nr = 1048576
fs.file-max = 6815744
kernel.shmmni = 4096
kernel.shmall = 1073741824
kernel.shmmax = 4398046511104
kernel.sem = 250 32000 100 128
net.ipv4.ip_local_port_range = 1024 65500
net.core.rmem_default = 262144
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048576
vm.swappiness = 0
vm.vfs_cache_pressure = 62
# net.core.default_qdisc = fq
# net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
#### net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
1.3 、 vi etc/security/limits.conf
add row:
oracle soft nofile 1024
oracle hard nofile 65536
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft stack 10240oracle hard stack 32768
1.4 、 vi etc/pam.d/login
add row:
session required /lib64/security/pam_limits.so
session required pam_limits.so
1.5 、 vi etc/profile
add row:
if [ $USER = "oracle" ]; then
if [ $SHELL = "/bin/ksh" ]; then
ulimit -p 16384
ulimit -n 65536
else
ulimit -u 16384 -n 65536
fi
fi
1.6 、 vi etc/hosts
add row:127.0.0.1 oracle12c oracle12c.localdomain
sysctl -p
1.7 、 for group in oinstall dba backupdba oper dgdba kmdba; do
groupadd $group
; done
1.8 、 useradd -g oinstall -G dba,oper,backupdba,dgdba,kmdba -d
/home/oracle -m
oracle
1.9 、 passwd oracle
1.10 、 vi home/oracle/.bash_profile
add row:
export ORACLE_BASE=/opt/oracle #oracle 数据库安装目录
export ORACLE_HOME=$ORACLE_BASE/12c #oracle 数据库
路径
export ORACLE_SID=oracle12c #oracle 启动数据库实例名
export ORACLE_TERM=xtermexport ORACLE_HOSTNAME=oracle12c.localdomain
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib
if [ $USER = "oracle" ]; then
if [ $SHELL = "/bin/ksh" ]; then
ulimit -p 16384
ulimit -n 65536
else
ulimit -u 16384 -n 65536
fi
umask 022
fi
export LANG=C # 防止安装过程出现乱码
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK # 设
置 Oracle 客户端字符集,必须与 Oracle# 安装时设置的字符集保持
一致,如: ZHS16GBK ,否则出现数据导入导出中文乱码问题
1.11 、 mount -B /run/media/birdofprey/CentOS\ 7\ x86_64/ /mnt
1.12 、 cp -r /etc/yum.repos.d/ /etc/yum.repos.d.backup
1.13 、 vi etc/yum.repos.d/CentOS-Base.repo
contents:
[base]
name=CentOS-$releasever - Base
baseurl=file:///mnt
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1
1.14 、 yum -y install binutils compat-libstdc++ compat-libstdc++-
33 elfutils-libelf-devel gcc gcc-c++ glibc-devel glibc-headers ksh
libaio-devel libstdc++-devel make sysstat unixODBC-devel
binutils-* compat-libstdc++* elfutils-libelf* glibc* gcc-* libaio*
libgcc* libstdc++* make* sysstat* unixODBC* wget unzip
compat-libcap1.i686 compat-libcap1.x86_64
1.15 、 upload oracle12cinstall.zip
1.16 、 ./runInstaller2、update 12.1
2.1、download zip
p28259833_121020_Linux-x86-64.zip #### Database Patch Set Update 12.1.0.2.181016
p28440711_121020_Linux-x86-64.zip #### OJVM PATCH SET UPDATE 12.1.0.2.181016
p6880880_121010_Linux-x86-64.zip #### OPatch Version: 12.2.0.1.16
2.2、以 oracle 登陆用户,vi .bash_profile :
add row:
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin:$ORACLE_HOME/OPatch
2.3、mv $ORACLE_HOME/Opatch $ORACLE_HOME/Opatch.backup
2.4、unzip p6880880_121010_Linux-x86-64.zip
2.5、mv Opatch/ $ORACLE_HOME/
2.6、sqlplus / as sysdba
SQL>SHUTDOWN IMMEDIATE;
2.7、lsnrctl stop
2.8、cd /28440711
2.9、opatch prereq CheckConflictAgainstOHWithDetail -ph ./ && opatch apply
2.10、cd cd 28259833 && opatch apply
2.11、sqlplus / as sysdba
SQL>startup;
SQL>alter pluggable database {databasename} open;
SQL>show pdbs;
2.12、cd $ORACLE_HOME/Opatch
2.13、./datapatch -verbose
2.14、shutdown
2.15、startup
2.16、alter pluggable database all open;
2.17、sqlplus / as sysdba
SQL> select comments from dba_registry_history;
COMMENTS
--------------------------------------------------------------------------------
RAN jvmpsu.sql
RDBMS_12.1.0.2.0DBPSU_LINUX.X64_161210
SQL>select * from product_component_version
3、update linux kernel and configure autostart oracle12c
3.1、[oracle @localhost ~]$ vi /lib/systemd/system/oracle-rdbms.service
row:
# /etc/systemd/system/oracle-rdbms.service
# Invoking Oracle scripts to start/shutdown Instances defined in /etc/oratab
# and starts Listener
[Unit]
Description=Oracle Database(s) and Listener
Requires=network.target
[Service]
Type=forking
Restart=no
ExecStart=/opt/oracle/12c/bin/dbstart /opt/oracle/12c ### 按照用户自己的 ORACLE_HOME 编写ExecStop=/opt/oracle/12c/bin/dbshut /opt/oracle/12c ### 按照用户自己的 ORACLE_HOME 编写
User=oracle
[Install]
WantedBy=multi-user.target
# kongxx
# CSDN
# https://blog.csdn.net/kongxx/article/details/82155876
3.2、[oracle @localhost ~]$ systemctl daemon-reload
[oracle @localhost ~]$ systemctl enable oracle-rdbms.service
3.3、[oracle @localhost ~]$ vi /etc/oratab
rewrit:
oracle12c:/opt/oracle/12c:Y
3.4、download linux-kernel package:
kernel-ml-4.18.3-1.el7.elrepo.x86_64.rpm,
kernel-ml-devel-4.18.3-1.el7.elrepo.x86_64.rpm,
kernel-ml-doc-4.18.3-1.el7.elrepo.noarch.rpm,
kernel-ml-headers-4.18.3-1.el7.elrepo.x86_64.rpm,
kernel-ml-tools-4.18.3-1.el7.elrepo.x86_64.rpm,
kernel-ml-tools-libs-4.18.3-1.el7.elrepo.x86_64.rpm,
perf-4.18.3-1.el7.elrepo.x86_64.rpm,
python-perf-4.18.3-1.el7.elrepo.x86_64.rpm
rpm -ivh *.rpm --force –nodeps
cat /boot/grub2/grub.cfg | grep menuentry
menuentry 'CentOS Linux (4.18.3-1.el7.elrepo.x86_64) 7 (Core)'
grub2-set-default "CentOS Linux (4.18.3-1.el7.elrepo.x86_64) 7 (Core)"
grub2-editenv list
4、update oracle12c is archivelog mode and rman dbbackup full database
4.1、sqlplus / as sysdba
SQL>shutdown immediate;
SQL>startup mount;
SQL>alter database archivelog;
SQL>alter database open;
SQL>alter pluggable database oracle12cpdb open;
SQL> select log_mode from v$database;
4.2、rman target /
CONFIGURE CONTROLFILE AUTOBACKUP ON; # default
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO
'/opt/oracle/oracle12cdbbackupfull/oracle12c/control%F.bak';
CONFIGURE CHANNEL DEVICE TYPE DISK MAXPIECESIZE 1024 M;
4.3、cd $ORACLE_BASE && mkdir oracle12cdbbackupfull && cd oracle12cdbbackupfull/
vi dbfullbackup.rman
row:
run{
crosscheck backup of database;
delete noprompt backupset;
backup as backupset database
format
'/opt/oracle/oracle12cdbbackupfull/oracle12c/oracle12cdbbackupfull%U_%d_%T_%s_
%p.bak'include current controlfile
plus archivelog format '/opt/oracle/oracle12cdbbackupfull/oracle12c/oracle12carchivelog%U_%d_
%T_%s_%p.bak' delete all input;
report obsolete;
delete noprompt obsolete;
}
4.4、vi oracle12cdbfullbackup.sh
row:
!#/bin/bash
cd /opt/oracle/oracle12cdbbackupfull
rman target / @dbfullbackup.rman
chmod +x oracle12cdbfullbackup.sh
4.5、crontab -e
row:
0
3
*
*
*
/opt/oracle/oracle12cdbbackupfull/oracle12cdbfullbackup.sh
/opt/oracle/oracle12cdbbackupfull/rman.log 2>&1
>
感谢打赏,不论多少,都感激不尽
数据库
2019-03-26 20:41:00
SQL Server MySQL
查询限制条数 语法:
SELECT TOP number|percent column_name(s) FROM table_name
例子;
SELECT TOP 3 * FROM Customers;
语法:
SELECT * FROM table_name LIMIT number;
例子:
SELECT * FROM ratings ORDER BY category LIMIT 5;
数据类型转换
创建临时表
语法:CONVERT(data_type(length), expression, style)
说明:
data_type:【必要】支持的数据类型
bigint,
int,
smallint,
tinyint,
bit,
decimal,
numeric,
money,
smallmoney,
float,
real,
datetime,
smalldatetime,
char,
varchar,
text,
nchar,
nvarchar,
ntext,
binary,
varbinary,
image

expression:【必要】转换成其他数据类型的值
length:【可选】数据类型的长度
例子:
SELECT CONVERT(varchar, 25.65);
表名必须以“#“开头
CREATE TABLE #MaleStudents
(
name VARCHAR(50),
age int,
gender VARCHAR (50)

)
语法:CONVERT(expr, type) 等价于 CAST(expr AS type)
说明:
expr:转换成其他数据类型的值
type:数据类型
BINARY[(N)]
CHAR[(N)] [charset_info]
DATE
DATETIME
DECIMAL[(M[,D])]
JSON
NCHAR[(N)]
SIGNED [INTEGER]
TIME
UNSIGNED [INTEGER]
例子:
SELECT CONVERT('test', CHAR CHARACTER SET utf8);
SELECT CAST('test' AS CHAR CHARACTER SET utf8);
语法:CREATE TEMPORARY TABLE new_tbl SELECT * FROM orig_tbl LIMIT 0;
数据库
2019-03-26 16:02:00
摘要:也许你在别的地方听说过Git。也许有人告诉过你,Git只适合软件开发人员。如果你是数据科学家,那么Git其实对你很重要。本文作者希望能够通过经验分享让你了解Git的重要性,以及如何在你的数据科学工作中使用它。
什么是 Git ?
Git是一个分布式版本控制系统,用于在软件开发期间跟踪源代码的更改。看看维基百科给出的这个定义,好像Git专门是为软件开发人员而设计的。实际上,Git是当今世界上使用最广泛的现代版本控制系统,它是以分布式的协作方式为项目(开源或商业)做出了伟大的贡献。除了分布式版本控制系统之外,Git的还考虑了性能、安全性和灵活性。现在你已经了解了Git是什么,但是你脑海中的问题可能是,“如果我是做数据科学项目的人,它与我的工作有什么关系?”以前我也一样不能理解Git的重要性,直到我开始在现实工作环境中,我才发现它时如此重要!
为什么是 Git ?
我们来谈谈为什么?一年前,我决定学习Git。我在Github上分享并发布了 我的代码 ,这是我在CERN的论文项目。虽然很难理解Git中常用的术语(git-add、commit、push、pull等),但我知道这在数据科学领域很重要,这使我的数据科学工作比以往任何时候都更加充实。
所以我保持学习状态,并坚持“committing”。当我加入我目前的公司时,我在Git方面的经验就派上了用场,因为Git是跨不同团队进行代码开发和协作的主要方式。更重要的是,当你的组织遵循敏捷软件开发框架时,Git尤其有用,在该框架中,Git的分布式版本控制使整个开发工作流更加高效、快速且易于适应变化。那么什么是版本控制呢?版本控制是一个系统记录一个文件或一组文件随时间的变化,以便你以后可以调用特定的版本。比如说,你是一个数据科学家,与一个团队合作,在这个团队中你和另一个数据科学家在构建机器学习模型的时候,对同一个特征进行工作。如果你对该特征做了一些更改并上传到远程存储库,并且这些更改与主分支合并,那么你的项目现在变成了1.1版本。另一位数据科学家也对版本1.1的相同功能进行了一些更改,新的更改现在与主分支合并。模型就变成1.2版本。在任何时候,如果你的团队发现版本1.2在发布期间有一些错误,他们随时可以调用以前的版本1.1,这就是版本控制的美妙之处。
作为数据科学家如何使用 Git ?
我们已经讨论过什么是Git及其重要性。现在的问题归结为:作为数据科学家如何使用Git?作为数据科学家,你不需要成为一个Git领域的专家。关键是要理解Git技术的工作流程以及如何在日常工作中使用Git。准确地说,我在这里使用的是Git Feature Branch Workflow,它通常被开源和商业项目使用。如果你想更多地了解这里使用的术语, 点击这里进行了解 。
Git Feature Branch Workflow
Feature Branch Workflow像一个中央存储库,master分支代表正式的项目历史记录。开发人员每次开始处理一个新特性时,都会创建一个新的分支,而不是直接提交到他们的本地主分支上。新的分支可以(也应该)推送到中央存储库。在这种情况下,可以在不修改master分支的情况下与其他开发人员共享一个该分支。
在开始执行任何操作之前,请键 入 git remote -v
以确保工作区指向要使用的远程存储库。
1、从主分支开始,创建一个新分 支 git checkout master git pull git checkout -b branch-name
如果总是维护和更新主分支,则切换到本地主分支,并将最新的提交和代码提取到本地主分支。假设你希望创建一个本地分支,向代码中添加一个新功能,并稍后上传到远程存储库。一旦你将最新的代码更新到本地master分支,我们就创建并checkout出一个名为branch-name的新分支,所有的更改都将在此本地分支上进行。这意味着你本地的master分支不会受到任何影响。
2、更新、添加、提交并将更改推送到远程存储库 git status git add git commit -m 'your message' git push -u origin branch-name
上面我们做了很多操作,让我们详细了解它。一旦发生了一些更新,就将新的操作add到本地分支,并且希望将该操作上传到远程分支,以便合并到远程主分支。git status将输出你对文件的所有更改(跟踪或未跟踪)。在使用git commit-m“your message”提交消息更改之前,你将使用git add 决定要暂存哪些文件。
在此阶段,你的更改仅显示在本地分支中。为了使你的更改显示在BitBucket上的远程分支中,你需要使用git push -u origin branch-name命令进行提交。此命令将该分支推送到中央存储库,并且-u表示将其添加为远程跟踪分支。在设置了跟踪分支之后,可以在没有任何参数的情况下调用git push,以自动将新的功能分支推送到BitBucket上的中央存储库。
3、 创建 pull 请求
现在你已经成功地添加了一个新功能并推送到远程分支。你为自己的贡献感到骄傲,你希望在将远程分支与远程主分支合并之前得到团队成员的反馈。在该分支合并到主分支之前,让其他团队成员有机会对其进行审查。你可以在BitBucket上创建pull请求。现在,你的团队成员已经查看了你的代码,并决定在代码可以合并到主代码库-master分支之前,需要你进行一些其他更改。 git status git add git commit -m 'your message' git push
现在,你可以按照与之前相同的步骤进行更改、提交并最终将更新推送到中央存储库。一旦使用了git push,你的更新将自动显示在pull请求中。如果其他人已将目标更改为你所接触的同一代码,则会发生合并冲突,这在工作中很常见。你可以在 这里 看到如何解决合并冲突。一旦一切顺利完成,这些功能将会合并到master分支中。
当我第一次开始学习Git时,我感到非常沮丧,因为我仍然没有真正理解工作流。这也是写这篇文章的主要原因之一,它真正分解并在更高层次的理解上向你解释工作流程。因为我相信,对工作流程中发生的事情有一个清晰的了解将使学习过程更加有效。
作者:【方向】
原文链接
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-03-25 13:03:00
一个划时代的知识女性——《杨绛传》读书笔记心得感想4300字:
读书可以让我无比的快乐,但是这本书读完心中却满是苦闷,这不是杨先生的苦闷是我的杞人忧天,特别是讲到文革时期的钱杨和女儿、钱老相继撒手人寰的时候,我甚至边落泪边看完的!
以下是我阅读过程中的一些记录,很零星也很琐碎,就全当个人的心得略加记录了!
一、恋家的杨先生
杨绛非常恋家,并不贪玩却贪看书,回家还帮助父亲做些事情。有一次,杨荫杭(杨绛的父亲)问她:“阿季(杨绛原名杨季康),三天不让你看书,你怎么样?”“不好过。”杨绛说。一星期不让你看书呢?”“一星期都白过了。”杨荫杭笑道:“我也这样。
也许我们现在娱乐活动太丰富,科技发展太快,如果他们父女间对话中的书改成手机是不是和当下贴切多了。
二、钱老的“痴气”
杨绛介绍说:“锺书的‘痴气’书本里灌注不下,还洋溢出来。我们在牛津时,他午睡,我临帖,可是一个人写写字困上来,便睡着了。他醒来见我睡了,就饱蘸浓墨,想给我画个花脸。
钱老的“痴”其实也是很可爱的!
比如:
那时杨先生刚刚生完孩子,还在医院里,钱老每天都到产院探望,常苦着脸对杨绛说“我做坏事了”。原来他打翻了墨水瓶,把房东家的桌布染了。杨绛说:“不要紧,我会洗。”“墨水呀!”“墨水也能洗。”他就放心回去。第二天他又“做坏事了”,把台灯砸了。杨绛问明是怎样的灯,她说:“不要紧,我会修。”他又放心回去。下一次他又满面愁虑,说是把门轴弄坏了,门轴两头的门球脱落了一个,门不能关了。杨绛说:“不要紧,我会修。”他又放心回去。
看吧,这哪是什么“痴”啊,明明是可爱,而且从杨先生口中讲出来还略带炫耀的意思呢!这样鲜活的两位大家也只能在书中慢慢品味了!
三、文学创作的小感悟
在创作过程中,无论所介绍的内容是有关别人的,还是她自己的,行文都含蓄、简约,其思想、情感,不予以特别强调,宁肯少说一点儿,给人多些可以回味的东西。所以杨绛的散文处处散发着大气的美,成熟的美。
在杨绛的笔下,没有高大的英雄人物,只有很平常、很普通的人物,不管是可亲可爱的,还是可憎可恶的,抑或是可悲可叹的人物,他们在日常生活中很常见,演绎的是这些寻常人物的家长里短,因而更带有生活气息。
在艺术创造中,想象极其重要,但它必须贴切真实,离不开“判断力”的调控。杨绛通过对众多作品的分析,理出形象思维与逻辑思维的关系:“小说家的构思,一方面靠想象力的繁衍变幻,以求丰富多彩,一方面还靠判断力的修改剪裁,以求贴合人生真相。前者是形象思维,后者是逻辑思维,二种思维同时并用。
近日在看这本书的同时也找了些杨绛的散文来看,看完以后受益匪浅,然而自己的创作却进入的死胡同,每每一篇很简单的文章都要几经修改,甚至写到一半写不下去,骆驼祥子好词好句(http://www.simayi.net/dushubiji/735.html)又不得不推翻重来,这个过程很痛苦,我和朋友玩笑这种感觉有点像不孕不育或者根本就是难产。曾经一气呵成的写作状态突然没有了,苦闷至极。
四、传统的知识女性和一生的相爱
杨绛待人接物,一招一式无不透出中国传统文化的底蕴。陈子谦曾告诉读者他“一直难以忘怀”的一件小事,他写道:“1984年5月我去拜访钱锺书先生,那天是杨先生开的门,她是那样温文尔雅,一副娇小文弱的样子。当钱先生让我坐下以后,杨先生从里屋用旧式茶盘端出两杯茶来,递一杯给我,递一杯给钱先生,然后双手托着茶盘一直背朝里屋退下,直到我告别时她才从里屋出来,满脸微笑送我到门口,我连忙请杨先生留步,只有钱先生送我下楼。杨先生端茶的动作,特别是她的‘却行’显然是一种旧式礼节,这在当时我真还觉得不好理解,特别是对一位后生晚学,何必如此‘讲礼’,这般客气?我真是谜一般地猜不透他们的心蕴。现在看来,这就叫文化,这就是我们的传统,不管你如何漂洋过海,懂得多少门外语,受多少西洋风气的影响,到头来骨子里的还是本民族的东西,根子还得牢牢地扎在民族文化的传统中。
是的就是他们对传统的认知,也是相互的爱慕。
又比如:
杨绛夫妇医院病床对谈的情景:夜渐深,敲窗的雨声时缓时紧,大颗小粒的雨珠沿着玻璃拉长,零碎地折射进星星点点的光亮。
“季康,不是说咱们找的人手明天就来吗?明天你就回家吧。”黑暗里钱锺书说。“这怎么行?咱这只是从帮忙辅助的意义上找的人,我不走。”折叠床上的杨绛说。“你可以站在一旁看看她做,看过了你总该放心,就明天一天啊。”
“默存,我发现《槐聚诗存》上有几处我抄错了字,书都印出来了,这可怎么好?”“打岔,说你该回家的事。”“我怎么能把你的诗抄错了呢?真是的。我怎么会抄错了呢……”小床上她叹着气。“明天你就回家去吧。……”没有回答。在被街衢道路包围的医院里,夜深时总能听见车声。雨地过车声又有不同。床头柜那边传来钱锺书摸索的动静。杨绛问:“找安眠药?”“睡不着,闹离愁了吧?吃一片吧。不用你,不用开灯。”
杨绛起身,按亮壁灯,端上温开水,看着丈夫服下舒乐安。她自己也拈出一片,钱锺书伸手接住。杨绛争道:“这不公平,在家时不是我吃安眠药你也陪着吃吗?你说过中毒俩一块中,岂可让我独中乎?”
就是这样,连安眠药都要分享,这就他们一辈子的爱,在钱老生病的过程中他们的唯一的孩子去世了,读着读着我的眼眶也湿了,好像还轻轻的呜咽起来!????
五、反倒成了被安抚者
据舒展介绍,钱锺书离世后,他的老伴去看望杨绛,一进门还没说话,只见杨绛孤身一人,老伴就抑制不住抽泣,后来干脆放声大哭起来。杨绛拉着她的手,让她坐到沙发上说:“你比钱瑗小四岁吧?傻孩子,我都挺过来了,你还这样哀伤?你不懂呀,如果我走在女儿和锺书前面,你想想,钱瑗、锺书受得了吗?所以,这并不是坏事,你往深处想想,让痛苦的担子由我来挑,这难道不是一件好事吗?”舒展的老伴回来向他传述以后,他说:“瞧你这点出息,让你去安慰老太太,反倒成了被安抚者。”
六、读书就是精神家园
解放前钱锺书和我寓居上海。我们必读的刊物是《生活周报》。寓所附近有一家生活书店,我们下午四点后经常去看书看报;在那儿会碰见许多熟人,和店里工作人员也熟。有一次,我把围巾落在店里了。回家不多久就接到书店的电话:“你落了一条围巾。恰好傅雷先生来,他给带走了,让我通知你一声。”傅雷带走我的围巾是招我们到他家去夜谈;嘱店员打电话是免我寻找失物。这件小事唤起了我当年的感受:生活书店是我们这类知识分子的精神家园。
是啊,读书就是可以丰富人精神的,他就是所有人的精神家园只是我们现在的阅读渠道太多了反而被束缚住了吧。
七、她就是我唯一的杰作
杨绛在《我们的钱瑗》中以《尖兵钱瑗》作为代序,说钱瑗:“她既然只求当尖兵,可说有志竟成,没有虚度此生。”杨绛回忆早年与丈夫钱锺书在探讨女儿个性时,钱锺书说她:“刚正,像外公;爱教书,像爷爷。”两位祖父迥然不相同的性格,在钱瑗身上表现得都很突出。杨绛在文章中提及钱瑗坚强不屈,正直不阿。北师大曾和英国合作培养“英语教学”研究生。钱瑗常和英方管事人争执,怪他们派来的专家英语水平不高,不合北师大英语研究生的要求。结果英国大使请她吃晚宴,向她道歉,同时也请她说说她的计划和要求。钱瑗的回答头头是道,英大使听了点头称善。杨绛听女儿讲了,也明白她是在建立一项有用的学科。
所以说杨先生总说钱瑗是他唯一的杰作,但从钱授的治学上来说,这是令人佩服的,当然英年早逝,也确实让人觉得万分惋惜。
八、论语的指导意义到今天还是有用的
杨绛对《论语》有很独特的见解。她说《论语》最有趣,“读《论语》,读的是一句一句话,看见的却是一个一个人,书里的一个个弟子,都是活生生的,一人一个样儿,各不相同”。
很久之前看了《孔子如来》好像也是这样的,孔夫子与弟子们的对话确实是鲜活的,却又可以把道理说得浅显,让人一看就明白,当然真要明白其所以然的话,还要颇费周章的,毕竟太过久远了,当然还有一层原因,就是我的悟性太低了吧。
九、杨先生的教育观念是超前的
我体会,‘好的教育’首先是启发人的学习兴趣,学习的自觉性,培养人的上进心,引导人们好学,和不断完善自己。要让学生在不知不觉中受教育,让他们潜移默化。这方面榜样的作用很重要,言传不如身教。我自己就是受父母师长的影响,由淘气转向好学的。爸爸说话入情入理,出口成章,《申报》评论一篇接一篇,浩气冲天,掷地有声。我佩服又好奇,请教秘诀,爸爸说:‘哪有什么秘诀?多读书,读好书罢了。杨绛称:“我对现代教育知道的不多。从报上读到过美术家韩美林作了一幅画,送给两三岁的小朋友,小孩子高高兴兴地回去了,又很快把画拿来要韩美林签名,问他签名干什么,小孩说:‘您签了名,这画才值钱!’可惜呀,这么小的孩子已受到社会不良风气的影响,价值观的教育难道不应引起注意吗?”
关于教育这个话题,杨先生的教育方式还是非常超前的,关于韩美林的这个小故事来说,也确实是让我们有所反思。我们当前的教育是不是存在着价值观的取向问题,是我们相关做教育的人来说颇值得关注的,也是应该予以纠偏的事实所在。
十、过年的年味丧失和我们的价值观有关
旧派过旧历年,新派过新历年。但此所谓过年,非空言度过之谓,其意盖指祭祖报神……今世年终所祭之神,固非耶教之上帝,亦非儒家之先圣先贤,不过五路财神耳。此所谓神,近于魔鬼,此所谓祭,近于行贿。”
一句贿赂五路财神,诠释了我们当前对于过年的偏颇,也一针见血的指出了当前社会的唯金钱论的诡异的价值观。现在的人们不得不从中汲取些营养,让我们的小一辈能够在正常的价值观中茁壮的成长。
十一、钱杨的婚姻是经过风雨洗刷的
对于时代,我是落伍者,没有什么良言贡献给现代婚姻。只是在物质至上的时代潮流下,想提醒年轻的朋友,男女结合最最重要的是感情,双方互相理解的程度,理解深才能互相欣赏吸引、支持和鼓励,两情相悦。我以为,夫妻间最重要的是朋友关系,即使不能做知心的朋友,也该是能做得伴侣的朋友或互相尊重的伴侣。门当户对及其他,并不重要。”
看看,杨先生对于婚姻的现状是有预知的,我们的婚姻与爱情已经是如此的物质化了,所以那些所谓不相信爱情的实质是自己把爱情弄岔了,还是杨先生的爱情是经得起风雨和细细品位的。杨先生也从未落伍。
十二、最大的功劳是保住了钱老的痴气
我最大的功劳是保住了钱锺书的淘气和那一团痴气。这是钱锺书的最可贵处。他淘气、天真,加上他过人的智慧,成了现在众人心目中博学而又风趣的钱锺书。他的痴气得到众多读者的喜爱。但是这个钱锺书成了他父亲一辈子担心的儿子,而我这种“洋盘媳妇”,在钱家是不合适的。
一个饱读诗书,留学海外,精通多国外语的时代女性却又是如此的传统。这种相夫教子中成长起来的女性,一路成全丈夫又不断成就自己远比一门心思嫁入豪门来的有趣的多。
数据库
2019-03-19 22:21:00
概述
阿里云数据管理DMS在云端可提供专业的数据库服务,除对标本地数据库软件的基础功能外,还包含性能诊断、数据追踪、跨实例SQL查询(含异构数据库类型之间)等专业性功能,同时提供审计安全和企业级数据库管理服务。
如果您的数据库在阿里云上均可享受这些服务,但在此之前,如果您的数据库在本地或其它云上,则无法免费享受这些服务。为满足自建数据库的管理诉求,主流所使用的解决方案如: 购买VPC专用网络,需要耗费大量成本,一般不适用于小企业或个人用户; 使用给数据库暴露公网端口的方式,这显然是极为不安全的做法,尤其是生产环境不能接受此类处理; 由用户自己做服务请求的转发,不但稳定性得不到保障,对于用户而言,门槛也很高。
因此DMS正式推出了: “数据库网关”服务 ,让您可安全、0成本地将本地数据库或其它云端数据库 (包含本地IDC自建、其他云数据库、其他云服务器上自建的数据库) 接入阿里云DMS,帮助用户打通云上云下资源,使用一套工具即可轻松实现数据库统一管理。从此您不必再担心因为给数据库开放公网端口产生的安全问题、维护成本等问题。
全新接入方案
1、架构图:
(1)本地机房需安装一个数据库网关 (1个互通的网络内仅需要安装1个网关) 。
(2)数据库网关所在机器通过访问公网的DMS Proxy建立连接,该连接将使用https进行加密。
(3)云端DMS(及后续其它数据库产品)对用户进行鉴权后,访问到DB Proxy,DB Proxy再通过步骤2建立的加密通信连接访问到本地数据库网关,本地数据库网关再访问到您的具体数据库。
2、接入云服务方式对比:
3、实操接入,仅需四步:
(1)添加网关 :进入DMS控制台,从左侧:“网关配置”中进去,点击添加网关,如下图所示(一个网关可对应一个内部网络,一个网关上可以添加多个数据库地址):
(2)下载网关程序 :该程序由Java编写,可反编译进行Review代码,DMS承诺只提供云端您主动发起过的数据库操作。您可以直接点击下载安装包或拷贝wget命令到对应需要安装网关的机器上进行执行(请确保该机器事先安装JRE 1.7以上版本且能够顺畅地访问公网)
(3)启动本地网关 :下载后的网关程序解压,进入目录:dms-db-gateway/bin,该目录有2个文件:start.sh/start.bat(Linux和Mac使用start.sh文件,Windows使用start.bat),如下图所示:
此时本地网关如果启动成功,状态会自动更新,并显示“下一步”按钮。本地安装需输入一些阿里云的账号认证信息及随机验证码,便于确认本地网关由本人启动及后期的安全认证,请根据页面上具体的操作提示完成即可。
(4)向网关中添加数据库 :一个网关可以添加多个数据库,本地启动后就可以开始添加了,填写的地址也是本地内网地址(即通过网关所在宿主机去访问数据库的地址),如下图所示:
接下来您就可以通过DMS控制台的“快捷登录->非阿里云库(自建IDC)”进行数据库登录了,也可以在“资源列表”页面登录这些数据库。同时,在这2个页面上可以继续添加本地数据库也可以。
补充说明 :在这里添加的数据库本地数据库,可以在实例授权功能中,授权给子账号,与云上RDS的操作非常类似,后续还会提供这些资源的安全管控和审计等功能。
释放强大服务能力
数据库网关是DMS刚推出的新特性,支持的数据库种类和功能还需有更多提升,未来我们将从以下几个角度为大家提供更多的服务:
1、数据库类型增加:
目前仅支持MySQL数据库类型,后续将会逐步完善支持的数据库类型与阿里云上对齐。
2、功能增强:
目前还未支持性能诊断、数据追踪、跨实例SQL操作等功能,由于在新的技术架构下,这些功能还需进行兼容性处理。还将会为大家提供线上和线下数据资源的对比、迁移、同步、JOIN等操作。
3、相关数据库产品服务:
除DMS产品外,后期会有更多的数据库产品会通过该数据库网关访问本地数据库及其他云端的数据库,例如:企业级DMS、HDM等等,这将为您提供更强大的数据库支持能力。
总结
数据库网关接入后,您的本地数据库享受到阿里云上所提供的数据库产品服务,使得云上云下、跨云之间实现统一管理和自由的数据流动和合并。
详情可移步了解: https://help.aliyun.com/document_detail/102974.html
点击观看DMS发布会 → https://yq.aliyun.com/live/851
专家解读产品年度升级,更有阿里云数据库实验室重磅亮相
DMS企业版超低门槛体验,了解更多产品特性,欢迎访问 :
https://promotion.aliyun.com/ntms/act/dmsenterprise.html
原文链接
数据库
2019-03-06 17:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1、汇总
1.1、问题
多套tidb集群的pd 部署在同样的机器,pd的服务相同,导致pd无法启动
版本:2.1.2
1.2、问题及解决
修改相关文件的端口部分解决
2、具体
2.1、具体问题
2.1.1、系统服务
/etc/systemd/system
pd.service
2.1.2、pd的启停脚本
【${deploy_dir}/scripts/start_pd.sh】
#!/bin/bash
set -e
# WARNING: This file was auto-generated. Do not edit!
# All your edit might be overwritten!
sudo systemctl start pd.service
【 ${deploy_dir} /scripts/stop_pd.sh】
#!/bin/bash
set -e
# WARNING: This file was auto-generated. Do not edit!
# All your edit might be overwritten!
sudo systemctl stop pd.service
郑州冶疗男性不孕不育医院哪家好:http://byby.zztjyy.com/
2.2、修复
tidb中控机:
【1、更改部署的】
/work/tidb/tidb-ansible-2.1/roles/pd/tasks/ systemd_deployment.yml
更改:
service_name: pd- {{ pd_client_port }}
【2、滚动升级的】
/work/tidb/tidb-ansible-2.1/ rolling_update.yml
更改:
- name: stop PD by systemd
systemd: name=pd -{{ pd_client_port }} .service state=stopped
http://www.chacha8.cn/detail/1132398232.html
- name: start PD by systemd
systemd: name=pd -{{ pd_client_port }} .service state=started


【3、更改start的】
/work/tidb/tidb-ansible-2.1/ start.yml
- name: start PD by systemd
systemd: name=pd- {{ pd_client_port }} .service state=started

2.3、修复后结果
手动删除目标pd机器的:
${deploy_dir}/scripts/start_pd.sh
${ deploy_dir } /scripts/stop_pd.sh
${ deploy_dir } /scripts/run_pd.sh
中控机重新部署: ansible-playbook deploy.yml -l pd机器IP

检查:
start_pd.sh
#!/bin/bash
set -e
# WARNING: This file was auto-generated. Do not edit!
# All your edit might be overwritten!
sudo systemctl start pd-10000 .service

stop_pd.sh
#!/bin/bash
set -e
# WARNING: This file was auto-generated. Do not edit!
# All your edit might be overwritten!
sudo systemctl stop pd-10000 .service

cd /etc/systemd/system
pd-10000 .service
数据库
2019-09-26 16:55:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 声东 阿里云售后技术专家
以我的经验来讲,理解 Kubernetes 集群服务的概念,是比较不容易的一件事情。尤其是当我们基于似是而非的理解,去排查服务相关问题的时候,会非常不顺利。
这体现在,对于新手来说,ping 不通服务的 IP 地址这样基础的问题,都很难理解;而就算对经验很丰富的工程师来说,看懂服务相关的 iptables 配置,也是有相当的挑战的。
今天这边文章,我来深入解释一下 Kubernetes 集群服务的原理与实现,便于大家理解。

Kubernetes 集群服务的本质是什么
概念上来讲,Kubernetes 集群的服务,其实就是负载均衡、或反向代理。这跟阿里云的负载均衡产品,有很多类似的地方。和负载均衡一样,服务有它的 IP 地址以及前端端口;服务后边会挂载多个容器组 Pod 作为其“后端服务器”,这些“后端服务器”有自己的 IP 以及监听端口。
当这样的负载均衡和后端的架构,与 Kubernetes 集群结合的时候,我们可以想到的最直观的实现方式,就是集群中某一个节点专门做负载均衡(类似 LVS)的角色,而其他节点则用来负载后端容器组。
这样的实现方法,有一个巨大的缺陷,就是单点问题。Kubernetes 集群是 Google 多年来自动化运维实践的结晶,这样的实现显然与其智能运维的哲学相背离的。

自带通信员
边车模式(Sidecar)是微服务领域的核心概念。边车模式,换一句通俗一点的说法,就是自带通信员。熟悉服务网格的同学肯定对这个很熟悉了。但是可能比较少人注意到,其实 Kubernetes 集群原始服务的实现,也是基于 Sidecar 模式的。
在 Kubernetes 集群中,服务的实现,实际上是为每一个集群节点上,部署了一个反向代理 Sidecar。而所有对集群服务的访问,都会被节点上的反向代理转换成对服务后端容器组的访问。基本上来说,节点和这些 Sidecar 的关系如下图所示。

把服务照进现实
前边两节,我们看到了,Kubernetes 集群的服务,本质上是负载均衡,即反向代理;同时我们知道了,在实际实现中,这个反向代理,并不是部署在集群某一个节点上,而是作为集群节点的边车,部署在每个节点上的。
在这里把服务照进反向代理这个现实的,是 Kubernetes 集群的一个控制器,即 kube-proxy。关于 Kubernetes 集群控制器的原理,请参考我另外一篇 关于控制器的文章 。简单来说,kube-proxy 作为部署在集群节点上的控制器,它们通过集群 API Server 监听着集群状态变化。当有新的服务被创建的时候,kube-proxy 则会把集群服务的状态、属性,翻译成反向代理的配置。
那剩下的问题,就是反向代理,即上图中 Proxy 的实现。

一种实现
Kubernetes 集群节点实现服务反向代理的方法,目前主要有三种,即 userspace、iptables 以及 IPVS。今天我们只深入分析 iptables 的方式,底层网络基于阿里云 Flannel 集群网络。

过滤器框架
现在,我们来设想一种场景。我们有一个屋子。这个屋子有一个入水管和出水管。从入水管进入的水,是不能直接饮用的,因为有杂质。而我们期望,从出水管流出的水,可以直接饮用。为了达到目的,我们切开水管,在中间加一个杂质过滤器。

过了几天,我们的需求变了,我们不止要求从屋子里流出来的水可以直接饮用,我们还希望水是热水。所以我们不得不再在水管上增加一个切口,然后增加一个加热器。

很明显,这种切开水管,增加新功能的方式是很丑陋的。因为需求可能随时会变,我们甚至很难保证,在经过一年半载之后,这跟水管还能找得到可以被切开的地方。
所以我们需要重新设计。首先我们不能随便切开水管,所以我们要把水管的切口固定下来。以上边的场景为例,我们确保水管只能有一个切口位置。其次,我们抽象出对水的两种处理方式:物理变化和化学变化。

基于以上的设计,如果我们需要过滤杂质,就可以在化学变化这个功能模块里增加一条过滤杂质的规则;如果我们需要增加温度的话,就可以在物理变化这个功能模块里增加一条加热的规则。
以上的过滤器框架,显然比切水管的方式,要优秀很多。设计这个框架,我们主要做了两件事情,一个是固定水管切口位置,另外一个是抽象出两种水处理方式。
理解这两件事情之后,我们可以来看下 iptables,或者更准确的说法,netfilter 的工作原理。netfilter 实际上就是一个过滤器框架。netfilter 在网络包收发及路由的管道上,一共切了 5 个口,分别是 PREROUTING,FORWARD,POSTROUTING,INPUT 以及 OUTPUT;同时 netfilter 定义了包括 nat、filter 在内的若干个网络包处理方式。

需要注意的是,routing 和 forwarding 很大程度上增加了以上 netfilter 的复杂程度,如果我们不考虑 routing 和 forwarding,那么 netfilter 会变得跟我们的水质过滤器框架一样简单。

节点网络大图
现在我们看一下 Kubernetes 集群节点的网络全貌。横向来看,节点上的网络环境,被分割成不同的网络命名空间,包括主机网络命名空间和 Pod 网络命名空间;纵向来看,每个网络命名空间包括完整的网络栈,从应用到协议栈,再到网络设备。
在网络设备这一层,我们通过 cni0 虚拟网桥,组建出系统内部的一个虚拟局域网。Pod 网络通过 veth 对连接到这个虚拟局域网内。cni0 虚拟局域网通过主机路由以及网口 eth0 与外部通信。
在网络协议栈这一层,我们可以通过编程 netfilter 过滤器框架,来实现集群节点的反向代理。

实现反向代理,归根结底,就是做 DNAT,即把发送给集群服务 IP 和端口的数据包,修改成发给具体容器组的 IP 和端口。
参考 netfilter 过滤器框架的图,我们知道,在 netfilter 里,可以通过在 PREROUTING,OUTPUT 以及 POSTROUGING 三个位置加入 NAT 规则,来改变数据包的源地址或目的地址。
因为这里需要做的是 DNAT,即改变目的地址,这样的修改,必须在路由(ROUTING)之前发生以保证数据包可以被路由正确处理,所以实现反向代理的规则,需要被加到 PREROUTING 和 OUTPUT 两个位置。
其中,PREOURTING 的规则,用来处理从 Pod 访问服务的流量。数据包从 Pod 网络 veth 发送到 cni0 之后,进入主机协议栈,首先会经过 netfilter PREROUTING 来做处理,所以发给服务的数据包,会在这个位置做 DNAT。经过 DNAT 处理之后,数据包的目的地址变成另外一个 Pod 的地址,从而经过主机路由,转发到 eth0,发送给正确的集群节点。
而添加在 OUTPUT 这个位置的 DNAT 规则,则用来处理从主机网络发给服务的数据包,原理也是类似,即经过路由之前,修改目的地址,以方便路由转发。

升级过滤器框架
在过滤器框架一节,我们看到 netfilter 是一个过滤器框架。netfilter 在数据“管到”上切了 5 个口,分别在这 5 个口上,做一些数据包处理工作。虽然固定切口位置以及网络包处理方式分类已经极大的优化了过滤器框架,但是有一个关键的问题,就是我们还是得在管道上做修改以满足新的功能。换句话说,这个框架没有做到管道和过滤功能两者的彻底解耦。
为了实现管道和过滤功能两者的解耦,netfilter 用了表这个概念。表就是 netfilter 的过滤中心,其核心功能是过滤方式的分类(表),以及每种过滤方式中,过滤规则的组织(链)。
把过滤功能和管道解耦之后,所有对数据包的处理,都变成了对表的配置。而管道上的5个切口,仅仅变成了流量的出入口,负责把流量发送到过滤中心,并把处理之后的流量沿着管道继续传送下去。
如上图,在表中,netfilter 把规则组织成为链。表中有针对每个管道切口的默认链,也有我们自己加入的自定义链。默认链是数据的入口,默认链可以通过跳转到自定义链来完成一些复杂的功能。这里允许增加自定义链的好处是显然的。为了完成一个复杂过滤功能,比如实现 Kubernetes 集群节点的反向代理,我们可以使用自定义链来模块化我们规则。

用自定义链实现服务的反向代理
集群服务的反向代理,实际上就是利用自定义链,模块化地实现了数据包的 DNAT 转换。KUBE-SERVICE 是整个反向代理的入口链,其对应所有服务的总入口;KUBE-SVC-XXXX 链是具体某一个服务的入口链,KUBE-SERVICE 链会根据服务 IP,跳转到具体服务的 KUBE-SVC-XXXX 链;而 KUBE-SEP-XXXX 链代表着某一个具体 Pod 的地址和端口,即 endpoint,具体服务链 KUBE-SVC-XXXX 会以一定算法(一般是随机),跳转到 endpoint 链。
而如前文中提到的,因为这里需要做的是 DNAT,即改变目的地址,这样的修改,必须在路由之前发生以保证数据包可以被路由正确处理。所以 KUBE-SERVICE 会被 PREROUTING 和 OUTPUT 两个默认链所调用。

总结
通过这篇文章,大家应该对 Kubernetes 集群服务的概念以及实现,有了更深层次的认识。我们基本上需要把握三个要点: 服务本质上是负载均衡; 服务负载均衡的实现采用了与服务网格类似的 Sidecar 的模式,而不是 LVS 类型的独占模式; kube-proxy 本质上是一个集群控制器。除此之外,我们思考了过滤器框架的设计,并在此基础上,理解使用 iptables 实现的服务负载均衡的原理。

原文链接
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-09-24 13:53:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1. 什么是事务?事务的特性(ACID)?
答:事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么都成功,要么都失败)。 事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所有操作。 事务的特性有:原子性(Atomicity),隔离性(Isolation),一致性( Consistency),持续性(Durability) 原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做  一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。  隔离性 :一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 持续性 :也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
2.事务的隔离级别有几种?最常用的隔离级别是哪两种(提交读、可重复读)?
答:并发过程中会出现的问题: 丢失更新:提交一个事务时,把其他事务已提交的更新数据覆盖。 脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。 幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象。 不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。
事务的四种隔离级别: 未提交读:就是一个事务读取到其他事务未提交的数据,是级别最低的隔离机制。缺点: 会产生脏读、不可重复读、幻读。 提交读:就是一个事务读取到其他事务提交后的数据。Oracle默认隔离级别。缺点:会产生不可重复读、幻读。 可重复读:就是一个事务对同一份数据读取到的相同,不在乎其他事务对数据的修改。MySQL默认的隔离级别。缺点:会产生幻读。 可串行化:事务串行化执行,隔离级别最高,牺牲了系统的并发性。缺点:可以解决并发事务的所有问题。但是效率地下,消耗数据库性能,一般不使用。
3. 什么是索引?
答: 索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。
4. 索引具体采用的哪种数据结构呢?
答: 常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,通常使用的是InnoDB引擎,默认的是B+树。当选用memory引擎的时候,就是用的Hash索引。
5. B+ Tree索引和Hash索引区别? 哈希索引适合等值查询,但是无法进行范围查询。 哈希索引没办法利用索引完成排序。 哈希索引不支持多列联合索引的最左匹配规则。 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题。
6. B+ Tree的叶子节点都可以存哪些东西吗? 他们之间有什么区别?
答:InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值。区别:在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。
7. 聚簇索引和非聚簇索引,在查询数据的时候有区别吗?
答:聚簇索引查询会更快,因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询。
8. 主键索引查询只会查一次,而非主键索引需要回表查询多次(这个过程叫做回表)。是所有情况都是这样的吗?非主键索引一定会查询多次吗?
答:覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。 如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。当我们通过SQL语句:select key2 from covering_index_sample where key1 = 'keytest';的时候,就可以通过覆盖索引查询,无需回表。
9. 在创建索引的时候都会考虑哪些因素呢?在创建联合索引的时候,需要做联合索引多个字段之间顺序,这是如何选择的呢?
答:一般对于查询概率比较高,经常作为where条件的字段设置索引。在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。 所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。
10. 那什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢?
在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。优化过程大致过程:根据搜索条件,找出所有可能使用的索引;计算全表扫描的代价;计算使用不同索引执行查询的代价;对比各种执行方案的代价,找出成本最低的那一个。
11. 为什么索引结构默认使用B+Tree,而不是Hash,二叉树,红黑树? B+tree是一种多路平衡查询树,节点是天然有序的,非叶子节点包含多个元素,不保存数据,只用来索引,叶子节点包含完整数据和带有指向下一个节点的指针,形成一个有序链表,有助于范围和顺序查找。因为非叶子节点不保存数据,所以同样大小的磁盘页可以容纳更多的元素,同样能数据量的情况下,B+tree相比B-tree高度更低,因此查询时IO会更少。 B-tree不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低; Hash索引底层是基于哈希表,就是以key-value存储数据的结构,多个数据在存储关系上是没有任何顺序关系的。只适合等值查询,不适合范围查询,而且也无法利用索引完成排序,不支持联合索引的最左匹配原则,如果有大量重复键值的情况下,哈希索引效率会很低,因为存在哈希碰撞。 二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。 红黑树:树的高度随着数据量增加而增加,IO代价高。
12. 如何优化MySQL?
答:MySQL优化大致可以分为三部分:索引的优化、SQL语句优化和表的优化 索引优化可以遵循以下几个原则: 联合索引最左前缀匹配原则( 但是这里有一种特殊的情况,如果将一个表中除ID意外的所有字段都组成一个联合索引,那么这个时候就不会遵循最左原则,这个时候无论怎么以哪个字段为where条件,都将用到这个索引。但是当有一个字段没有在这个联合索引索引里面的时候,就会遵循最左原则 ) 尽量把字段长度小的列放在联合索引的最左侧(因为字段越小,一页存储的数据量越大,IO性能也就越好) order by 有多个列排序的,应该建立联合索引 对于频繁的查询优先考虑使用覆盖索引 前导模糊查询不会使用索引,比如说Like '%aaa%'这种,但是可以在mysql中创建全文索引 负向条件不会使用索引,如!=,<>,not like,not in,not exists 索引应该建立在区分度比较高的字段上,一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 对于where子句中经常使用的列,最好设置索引 or两边的字段需要索引 SQL语句优化,可以通过explain查看SQL的执行计划,优化语句原则可以有: 在where和order by涉及的列上建立合适的索引,避免全表扫描 任何查询都不要使用select * ,而是用具体的字段列表代替(增加了消耗,e.g CPU,IO等,增加了使用覆盖索引的可能性) 多表连接时,尽量小表驱动大表,即小表join大表 用exists代替in(IN是走rang,就是最差的索引=没有索引,而EXISTS走的ref,已经算是比较好的检索方式了) 尽量避免在where字句中对字段进行函数操作(主要是不会触发索引) SQL语句中in包含的值不应过多(In中的常量全部存储在一个数组中,而且这个数组是排好序的,如果数值较多,产生的消耗也是比较大的) 当只需要一条数据的时候,使用limit 1 如果排序字段没有用到索引,就尽量少排序 尽量用union all代替union(union需要将结果集合并后再进行唯一性过滤操作,会增加大量的CPU运算,加大资源消耗和延迟。当然,前提是union all的前提是两个结果集没有重复的数据) 不要使用Order by rand() in和exists主要是驱动顺序的改变,exists是以外层表为驱动,先被访问;in是先执行子查询。所以in适合于外表大而内表小,exists适合于外表小而内表大的情况, not in和not exists同理 对于分页查询,可以考虑获取前一页的最大的id,用where id > num left join是以左边表为驱动;right join是以右边表为驱动,inner join会自动找出那个数据少的表作为驱动表 数据库表优化 表字段尽可能用not null 字段长度固定表查询会更快 将数据库大表按照时间或者一些标志拆分成小表 水平拆分:将记录散列到不同的表中,每次从分表查询 垂直拆分:将表中的大字段单独拆分到另一张表,形成一对一的关系
数据库
2019-09-20 12:41:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 元乙 阿里云日志服务数据采集客户端负责人,目前采集客户端 logtail 在集团百万规模部署,每天采集上万应用数 PB 数据,经历多次双 11、双 12 考验。 导读: 随着 K8s 不断更新迭代,使用 K8s 日志系统建设的开发者,逐渐遇到了各种复杂的问题和挑战。本篇文章中,作者结合自己多年经验,分析 K8s 日志系统建设难点,期待为读者提供有益参考。
在 Logging 这块做了几年,最近 1 年来越来越多的同学来咨询如何为 Kubernetes 构建一个日志系统,或者是来求助在这过程中遇到一系列问题如何解决,授人以鱼不如授人以渔,于是想把我们这些年积累的经验以文章的形式发出来,让看到这篇文章的同学能少走弯路。这个系列文章定位为长篇连载,内容偏向落地实操以及经验分享,且内容会随着技术的迭代而不定期更新。

前言
第一次听到 Kubernetes 的名字是在 2016 年,那个时候 Kubernetes 还处于和 Docker Swarm、Mesos 方案的“三国鼎立时代”,Kubernetes 由于一系列优势(可扩展、声明式接口、云友好)在这一竞争中崭露头角,最终获得统治地位。
Kubernetes 作为 CNCF 最核心的项目(没有之一),是 Cloud Native(云原生)落地的底座,目前阿里已经全面基于 Kubernetes 在开展全站的云原生改造,在 1-2 年内,阿里巴巴 100% 的业务都将跑在公有云上。
CloudNative 在 CNCF 的定义 的核心是:在公有云、私有云、混合云等环境中,通过 Containers、Service Meshes、 MicroServices、Immutable Infrastructure、Declarative APIs 构建和运行可弹性扩展的且具有高容错性、易于管理、可观察、松耦合的应用系统。可观察性是应用系统必不可少的一个部分,云原生的设计理念中就有一条:面向诊断性设计(Diagnosability),包括集群级别的日志、Metric 和 Trace。

为何我们需要日志系统
通常一个线上问题的定位流程是:通过 Metric 发现问题,根据 Trace 定位到问题模块,根据模块具体的日志定位问题原因。在日志中包括了错误、关键变量、代码运行路径等信息,这些是问题排查的核心,因此日志永远是线上问题排查的必经路径。

在阿里的十多年中,日志系统伴随着计算形态的发展在不断演进,大致分为 3 个主要阶段: 在单机时代,几乎所有的应用都是单机部署,当服务压力增大时,只能切换更高规格的 IBM 小型机。日志作为应用系统的一部分,主要用作程序 Debug,通常结合 grep 等 Linux 常见的文本命令进行分析; 随着单机系统成为制约阿里业务发展的瓶颈,为了真正的 Scale out,飞天项目启动:2013 年飞天 5K 项目正式上线。在这个阶段各个业务开始了分布式改造,服务之间的调用也从本地变为分布式,为了更好的管理、调试、分析分布式应用,我们开发了 Trace(分布式链路追踪)系统、各式各样的监控系统,这些系统的统一特点是将所有的日志(包括 Metric 等)进行集中化的存储; 为了支持更快的开发、迭代效率,近年来我们开始了容器化改造,并开始了拥抱 Kubernetes 生态、业务全量上云、Serverless 等工作。在这阶段,日志无论从规模、种类都呈现爆炸式的增长,对日志进行数字化、智能化分析的需求也越来越高,因此统一的日志平台应运而生。

可观察性的终极解读
在 CNCF 中,可观察性的主要作用是问题的诊断,上升到公司整体层面,可观察性(Observability)不仅仅包括 DevOps 领域,还包括业务、运营、BI、审计、安全等领域,可观察性的最终的目标是实现公司各个方面的数字化、智能化。

在阿里,几乎所有的业务角色都会涉及到各式各样的日志数据,为了支撑各类应用场景,我们开发了非常多的工具和功能:日志实时分析、链路追踪、监控、数据加工、流计算、离线计算、BI 系统、审计系统等等。日志系统主要专注于数据的实时采集、清洗、智能分析与监控以及对接各类各样的流计算、离线系统。

Kubernetes 日志系统建设难点

单纯日志系统的解决方案非常多,相对也比较成熟,这里就不再去赘述,我们此次只针对 Kubernetes 上的日志系统建设而论。Kubernetes 上的日志方案相比我们之前基于物理机、虚拟机场景的日志方案有很大不同,例如: 日志的形式变得更加复杂,不仅有物理机/虚拟机上的日志,还有容器的标准输出、容器内的文件、容器事件、Kubernetes 事件等等信息需要采集; 环境的动态性变强,在 Kubernetes 中,机器的宕机、下线、上线、Pod销毁、扩容/缩容等都是常态,这种情况下日志的存在是瞬时的(例如如果 Pod 销毁后该 Pod 日志就不可见了),所以日志数据必须实时采集到服务端。同时还需要保证日志的采集能够适应这种动态性极强的场景; 日志的种类变多,上图是一个典型的 Kubernetes 架构,一个请求从客户端需要经过 CDN、Ingress、Service Mesh、Pod 等多个组件,涉及多种基础设施,其中的日志种类增加了很多,例如 K8s 各种系统组件日志、审计日志、ServiceMesh 日志、Ingress 等; 业务架构变化,现在越来越多的公司开始在 Kubernetes 上落地微服务架构,在微服务体系中,服务的开发更加复杂,服务之间的依赖以及服务底层产品的依赖越来越多,这时的问题排查将更加复杂,如果关联各个维度的日志将是一个困难的问题; 日志方案集成困难,通常我们都会在 Kubernetes 上搭建一套 CICD 系统,这套 CICD 系统需要尽可能的自动化的完成业务的集成和部署,其中日志的采集、存储、清洗等也需要集成到这套系统中,并和 K8s 的声明式部署方式尽可能一致。而现有的日志系统通常都是较独立的系统,集成到 CICD 中代价极大; 日志规模问题,通常在系统初期的时候我们会选择自建开源的日志系统,这种方式在测试验证阶段或公司发展初期是没有什么问题的,但当业务逐渐增长,日志量增长到一定规模时,自建的开源系统很多时候都会遇到各种各样的问题,例如租户隔离、查询延迟、数据可靠性、系统可用性等。日志系统虽不是 IT 中最核心的路径,但一旦关键时刻出现这些问题都将是非常可怕的影响,例如大促的时候出现紧急问题,排查时多个工程师并发查询把日志系统打爆,导致故障恢复时间变长,大促收到影响。
相信在搞 K8s 日志系统建设的同学看到上面的难点分析都会深有感触,后面我们会从落地角度出发,详细介绍在阿里我们如何去搭建 K8s 的日志系统,敬请关注。

原文链接
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-09-19 14:03:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
sql_mode
是一个很容易被忽视的配置,宽松模式下可能会被输入一些非准确数据,所以生产环境下会要求为严格模式,为了保持生产环境和开发环境,测试环境一致性,我们开发环境和测试环境也要配置成为严格模式。
sql_mode常用值
ONLY_FULL_GROUP_BY:在分组查询语句中如果一个select中的列没有在group by中出现,则该语句是不合法的。
NO_AUTO_VALUE_ON_ZERO:在默认情况下自增长列在插入0或NULL时会自动插入下一个自增长值。当设置该模式情况下,插入0时不会进行自增长依然插入0值。
STRICT_TRANS_TABLES:对事务表进行限制,当一个数据不能插入到事务表中时中断当前操作。对非实物表不做限制。
NO_ZERO_IN_DATE:只要日期的月和日中含有0值都报错,但是‘0000-00-00’除外。
NO_ZERO_DATE:只有‘0000-00-00’报错。
ERROR_FOR_DIVISION_BY_ZERO:在INSERT或UPDATE过程中,如果数据被零除,则产生错误而非警告。如果非该模式下被0除时mysql返回NULL。
NO_AUTO_CREATE_USER:禁止创建密码为空的用户。
NO_ENGINE_SUBSTITUTION:如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常。
PIPES_AS_CONCAT:将"||"视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似。
ANSI_QUOTES:启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符。
Oracle数据库切换为mysql数据库
只需要修改sql_mode为:PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER。
修改sql_mode模式
通过 "SELECT @@sql_mode;"查看当前数据库模式,修改“SET sql_mode = ‘修改为模式’; ”。这种方式只在当前session生效。
同理:select @@global.sql_mode 和 set global sql_mode = '修改后的值';只在当前服务中生效,重启后失效。
修改my.cnf文件重启,永久生效。
数据库
2019-09-17 20:41:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
最近我们扩展了 TiDB 表达式计算框架,增加了向量化计算接口,初期的性能测试显示,多数表达式计算性能可大幅提升,部分甚至可提升 1~2 个数量级。为了让所有的表达式都能受益,我们需要为所有内建函数实现向量化计算。
TiDB 的向量化计算是在经典 Volcano 模型上的进行改进,尽可能利用 CPU Cache,SIMD Instructions,Pipeline,Branch Predicatation 等硬件特性提升计算性能,同时降低执行框架的迭代开销,这里提供一些参考文献,供感兴趣的同学阅读和研究: MonetDB/X100: Hyper-Pipelining Query Execution Balancing Vectorized Query Execution with Bandwidth-Optimized Storage The Design and Implementation of Modern Column-Oriented Database Systems
在这篇文章中,我们将描述: 如何在计算框架下实现某个函数的向量化计算; 如何在测试框架下做正确性和性能测试; 如何参与进来成为 TiDB Contributor。
表达式向量化
1. 如何访问和修改一个向量
在 TiDB 中,数据按列在内存中连续存在 Column 内,Column 详细介绍请看: TiDB 源码阅读系列文章(十)Chunk 和执行框架简介 。本文所指的向量,其数据正是存储在 Column 中。
我们把数据类型分为两种: 定长类型: Int64 、 Uint64 、 Float32 、 Float64 、 Decimal 、 Time 、 Duration ; 变长类型: String 、 Bytes 、 JSON 、 Set 、 Enum 。
定长类型和变长类型数据在 Column 中有不同的组织方式,这使得他们有如下的特点: 定长类型的 Column 可以随机读写任意元素; 变长类型的 Column 可以随机读,但更改中间某元素后,可能需要移动该元素后续所有元素,导致随机写性能很差。
对于定长类型(如 int64 ),我们在计算时会将其转成 Golang Slice(如 []int64 ),然后直接读写这个 Slice。相比于调用 Column 的接口,需要的 CPU 指令更少,性能更好。同时,转换后的 Slice 仍然引用着 Column 中的内存,修改后不用将数据从 Slice 拷贝到 Column 中,开销降到了最低。
对于变长类型,元素长度不固定,且为了保证元素在内存中连续存放,所以不能直接用 Slice 的方式随机读写。我们规定变长类型数据以追加写( append )的方式更新,用 Column 的 Get() 接口进行读取。
总的来说,变长和定长类型的读写方式如下: 定长类型(以 int64 为例)
a. ResizeInt64s(size, isNull) :预分配 size 个元素的空间,并把所有位置的 null 标记都设置为 isNull ;
b. Int64s() :返回一个 []int64 的 Slice,用于直接读写数据;
c. SetNull(rowID, isNull) :标记第 rowID 行为 isNull 。 变长类型(以 string 为例)
a. ReserveString(size) :预估 size 个元素的空间,并预先分配内存;
b. AppendString(string) : 追加一个 string 到向量末尾;
c. AppendNull() :追加一个 null 到向量末尾;
d. GetString(rowID) :读取下标为 rowID 的 string 数据。
当然还有些其他的方法如 IsNull(rowID) , MergeNulls(cols) 等,就交给大家自己去探索了,后面会有这些方法的使用例子。
2. 表达式向量化计算框架
向量化的计算接口大概如下( 完整的定义在这里 ): vectorized() bool vecEvalXType(input *Chunk, result *Column) error XType 可能表示 Int , String 等,不同的函数需要实现不同的接口; input 表示输入数据,类型为 *Chunk ; result 用来存放结果数据。
外部执行算子(如 Projection,Selection 等算子),在调用表达式接口进行计算前,会通过 vectorized() 来判断此表达式是否支持向量化计算,如果支持,则调用向量化接口,否则就走行式接口。
对于任意表达式,只有当其中所有函数都支持向量化后,才认为这个表达式是支持向量化的。
比如 (2+6)*3 ,只有当 MultiplyInt 和 PlusInt 函数都向量化后,它才能被向量化执行。
为函数实现向量化接口
要实现函数向量化,还需要为其实现 vecEvalXType() 和 vectorized() 接口。 在 vectorized() 接口中返回 true ,表示该函数已经实现向量化计算; 在 vecEvalXType() 实现此函数的计算逻辑。
尚未向量化的函数在 issue/12058 中,欢迎感兴趣的同学加入我们一起完成这项宏大的工程。
向量化代码需放到以 _vec.go 结尾的文件中,如果还没有这样的文件,欢迎新建一个,注意在文件头部加上 licence 说明。
这里是一个简单的例子 PR/12012 ,以 builtinLog10Sig 为例: 这个函数在 expression/builtin_math.go 文件中,则向量化实现需放到文件 expression/builtin_math_vec.go 中; builtinLog10Sig 原始的非向量化计算接口为 evalReal() ,那么我们需要为其实现对应的向量化接口为 vecEvalReal() ; 实现完成后请根据后续的说明添加测试。
下面为大家介绍在实现向量化计算过程中需要注意的问题。
1. 如何获取和释放中间结果向量
存储表达式计算中间结果的向量可通过表达式内部对象 bufAllocator 的 get() 和 put() 来获取和释放,参考 PR/12014 ,以 builtinRepeatSig 的向量化实现为例: buf2, err := b.bufAllocator.get(types.ETInt, n) if err != nil { return err } defer b.bufAllocator.put(buf2) // 注意释放之前申请的内存
2. 如何更新定长类型的结果
如前文所说,我们需要使用 ResizeXType() 和 XTypes() 来初始化和获取用于存储定长类型数据的 Golang Slice,直接读写这个 Slice 来完成数据操作,另外也可以使用 SetNull() 来设置某个元素为 NULL 。代码参考 PR/12012 ,以 builtinLog10Sig 的向量化实现为例: f64s := result.Float64s() for i := 0; i < n; i++ { if isNull { result.SetNull(i, true) } else { f64s[i] = math.Log10(f64s[i]) } }
3. 如何更新变长类型的结果
如前文所说,我们需要使用 ReserveXType() 来为变长类型预分配一段内存(降低 Golang runtime.growslice() 的开销),使用 AppendXType() 来追加一个变长类型的元素,使用 GetXType() 来读取一个变长类型的元素。代码参考 PR/12014 ,以 builtinRepeatSig 的向量化实现为例: result.ReserveString(n) ... for i := 0; i < n; i++ { str := buf.GetString(i) if isNull { result.AppendNull() } else { result.AppendString(strings.Repeat(str, int(num))) } }
4. 如何处理 Error
所有受 SQL Mode 控制的 Error,都利用对应的错误处理函数在函数内就地处理。部分 Error 可能会被转换成 Warn 而不需要立即抛出。
这个比较杂,需要查看对应的非向量化接口了解具体行为。代码参考 PR/12042 ,以 builtinCastIntAsDurationSig 的向量化实现为例: for i := 0; i < n; i++ { ... dur, err := types.NumberToDuration(i64s[i], int8(b.tp.Decimal)) if err != nil { if types.ErrOverflow.Equal(err) { err = b.ctx.GetSessionVars().StmtCtx.HandleOverflow(err, err) // 就地利用对应处理函数处理错误 } if err != nil { // 如果处理不掉就抛出 return err } result.SetNull(i, true) continue } ... }
5. 如何添加测试
我们做了一个简易的测试框架,可避免大家测试时做一些重复工作。
该测试框架的代码在 expression/bench_test.go 文件中,被实现在 testVectorizedBuiltinFunc 和 benchmarkVectorizedBuiltinFunc 两个函数中。
我们为每一个 builtin_XX_vec.go 文件增加了 builtin_XX_vec_test.go 测试文件。当我们为一个函数实现向量化后,需要在对应测试文件内的 vecBuiltinXXCases 变量中,增加一个或多个测试 case。下面我们为 log10 添加一个测试 case: var vecBuiltinMathCases = map[string][]vecExprBenchCase { ast.Log10: { {types.ETReal, []types.EvalType{types.ETReal}, nil}, }, }
具体来说,上面结构体中的三个字段分别表示: 该函数的返回值类型; 该函数所有参数的类型; 是否使用自定义的数据生成方法(dataGener), nil 表示使用默认的随机生成方法。
对于某些复杂的函数,你可自己实现 dataGener 来生成数据。目前我们已经实现了几个简单的 dataGener,代码在 expression/bench_test.go 中,可直接使用。
添加好 case 后,在 expression 目录下运行测试指令: # 功能测试 GO111MODULE=on go test -check.f TestVectorizedBuiltinMathFunc # 性能测试 go test -v -benchmem -bench=BenchmarkVectorizedBuiltinMathFunc -run=BenchmarkVectorizedBuiltinMathFunc
在你的 PR Description 中,请把性能测试结果附上。不同配置的机器,性能测试结果可能不同,我们对机器配置无任何要求,你只需在 PR 中带上你本地机器的测试结果,让我们对向量化前后的性能有一个对比即可。
如何成为 Contributor
为了推进表达式向量化计算,我们正式成立 Vectorized Expression Working Group,其具体的目标和制度详见 这里 。与此对应,我们在 TiDB Community Slack 中创建了 wg-vec-expr channel 供大家交流讨论,不设门槛,欢迎感兴趣的同学加入。
如何成为 Contributor: 在此 issue 内选择感兴趣的函数并告诉大家你会完成它; 为该函数实现 vecEvalXType() 和 vectorized() 的方法; 在向量化测试框架内添加对该函数的测试; 运行 make dev ,保证所有 test 都能通过; 发起 Pull Request 并完成 merge 到主分支。
如果贡献突出,可能被提名为 reviewer,reviewer 的介绍请看 这里 。
如果你有任何疑问,也欢迎到 wg-vec-expr channel 中提问和讨论。 原文阅读: https://pingcap.com/blog-cn/10mins-become-contributor-of-tidb-20190916/
数据库
2019-09-17 14:06:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
平时用来测试的异常处理
我们都是通过dbms_output.put_line来输出异常信息,但是在实际的应用中,需要把异常信息返回给调用的客户端。
其实 RAISE_APPLICATION_ERROR 是将应用程序专有的错误从服务器端转达到客户端应用程序(其他机器上的SQLPLUS或者其他前台开发语言)
raise_application_error(异常类型,传递信息)
异常类型:number 值域:-20000 到-20999
传递信息:varchar2(2000)
DBMS_STANDARD包的RAISE_APPLICATION_ERROR过程,可以重新定义异常错误消息,它为应用程序提供了一种与ORACLE交互的方法。语法如下
RAISE_APPLICATION_ERROR(errorNumber,errorString)
errorNumber是数值在-20000到-20999之间,errorString为自定义的错误信息。
如:
update jobs set job_title = v_newJobTitle where job_id = v_jobid;
if sql%notfound then
RAISE_APPLICATION_ERROR(-20167,'update failure!');
end if;
..........
当在sqlpus中测试时,一旦没有要更新的行,则抛出这样的异常:
ORA-20167: update failure!
oracle的异常分为编译时异常(错误)和运行时异常,前者不能被处理,后者可以被处理。
我们主要讨论运行时异常。
异常类型:
a、预定义异常
已命名的预定义异常有CURSOR_ALREADY_OPEN、INVALID_NUMBER、TOO_MANY_ROWS等
b、用户定义异常
c、已命名异常(已命名异常不能单独归为一类异常,但它有点特别,所以我将它单独罗列说明)
如果希望处理一个异常(被when子串处理),那么异常必须有一个名字,如TOO_MANY_ROWS;
数据库错误有数千个,但是只有不到25个是内建的已命名异常(这些异常都声明在standard包中);
要处理那些未命名的异常时,你可以将一个名字和一个错误号联系在一起,达到这个目的的语句是pragma exception_init语句;
抛出异常:
a、通过pl/sql运行时引擎
当数据库或pl/sql在运行时发生错误时,一个异常被pl/sql运行时引擎自动抛出
b、使用raise语句
异常也可以通过raise语句抛出:raise exception_name;
c、调用raise_application_error存储过程
处理异常&异常传播:
a、一旦程序进入异常部分就不能再回到同一块的执行部分;当异常被处理后,控制返回到外层执行部分的下一条语句;
b、如果有when others子串,则必须放置在最后面作为缺省处理器处理没有显式处理的异常;
c、执行部分抛出的异常将首先传递到同一块的异常部分,如果在同一块的异常部分没有处理这个异常的处理器,那么异常将会传播到上一层的异常部分中,一直到最外层;
d、异常被处理后如果你仍然希望它继续传播,可以在异常处理的最后执行不带参数的raise语句(raise语句将重新抛出出现的异常,允许它继续传播);
--这是一个上面部分知识点的示例(伪代码)说明
declare
...
user_define_exception exception; --用户定义异常
invalid_column_name exception;
--补充说明:如果我们在程序块中使用了无效列名,会有括号中的错误提示(ORA-00904:invalid column name)
--下面我们将这个异常代码号与我们自定义的异常进行关联,即为异常命名
pragma exception_init(invalid_column_name,-904);
begin
...
--raise user_define_exception; --可以显式引发异常
exception
when TOO_MANY_ROWS then --预定义异常处理
...;
when user_define_exception then --用户定义异常处理
...;
when invalid_column_name then --PRAGMA EXCEPTION_INIT异常处理
...;
raise; --继续传播该异常
end;
sqlcode和sqlerrm:
a、另外一种处理数据库错误的方法是使用内建函数sqlcode和sqlerrm;
b、sqlcode将返回现行数据库错误号,这些错误号中除了no_data_found是+100外其他都是负数;
c、sqlerrm返回文本描述的错误信息;
d、为了获得用户自定义异常返回的sqlerrm和sqlcode,你需要使用raise_application_error函数给自定义异常标注错误号
给自定义错误标注号码:
a、raise_application_error内建函数用于抛出一个异常并给异常赋予一个错误号以及错误信息;
b、自定义异常的缺省错误号是+1,缺省信息是user_defined_exception。来自未处理的异常的一般信息对于识别导致错误的原因没有帮助,
c、raise_application_error函数能够在pl/sql程序块的执行部分和异常部分调用,显式抛出带特殊错误号的命名异常;
d、使用语法:raise_application_error(error_no,error_message[,{true|| false}]);
e、错误号的范围是-20,001到-20,999;错误信息是文本字符串,最多为2048字节;true和false表示是添加(true)进错误堆(error stack)还是覆盖(overwrite)错误堆(false)。缺省情况下是false。
数据库
2019-09-16 15:49:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
摘要
最近发现服务器磁盘空间不足,发现是数据库占用空间太大了,决定删一部分数据,释放一些空间。结果,发现删除数据之后,linux磁盘空间并没有释放,opimize 之后,依然没有释放。各种折腾之后才搞清楚,建立数据库的时候,使用的表空间是默认表空间,也就是所有的数据都会写入到 ibdata1中,这种情况删除表内容的数据并optimize是没有作用的,必须是单表独立文件才能优化空。
最后解决方法,备份所有表数据,删除1bdata1文件(这中间也做了一些尝试,差点数据库起不来了),设置 单表独立空间参数(innodb_file_per_table=1),重新导入数据,这之后,每一个表都是一个独立的文件,删除数据之后是可以用optimize优化的
顺带提一下分区表,也就是按照一定的逻辑分表规则(没有命中规则的插入会失败),将数据落入到不同的数据文件中,这样加载数据的时候可能会效率高一些,但是受一些索引文件大小的影响,分区表还是不能解决大数据问题。
以后有空把这篇博客补充完整。
经过
忽略_bak,原路径是 /var/lib/mysql
innodb_file_per_table=1
数据库
2019-09-12 11:05:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
应用系统的更新发布在企业日常运维中是一件比较常见工作,有文件的备份替换,也会有数据库方面的更新操作。本文介绍如何通过一台装有sqlplus工具的中转机对不同应用的Oracle数据库进行自动化发布。

经常遇到使用PL/SQL图形化工具对Oracle数据库进行相关的更新操作,例如程序包、触发器、存储过程、视图以及表中的数据。如果是单用户对单台数据库更新少量的内容,这个操作还可以接受,如果数据库服务器比较多,并且要切换不同的用户去更新大量的内容,这种方式就会比较低效繁琐,面对这种场景,实现后台自动化更新就很有必要。

如何通过一台sqlplus中转机对不同的Oracle数据库在后台完成更新示意如下:
规范要求
按照上图示意在后台完成数据库的发布更新需要一定的规范支持,简单来讲就是要让sqlplus工具能很明确的获取到以下几点信息:
更新时用什么账号去连接哪台服务器的哪个实例? 账号对应的密码应该如何传递给sqlplus命令? 更新的是程序包、存储过程还是视图? 多个更新之间是否有前后依赖关系?
更新文件的命名规范
通过sqlplus命令去连接Oracle数据库完成一次更新需要账号、密码、Oracle服务器的IP地址、侦听的端口、实例名、更新文件所在的路径,用法如下:
其中账号、IP地址、端口、实例名属于可公开的信息,这一部分内容可以连同更新次序和更新类型一起组织到文件名称中

例如【次序_账号_IP_实例名_XXX.类型】,示例:
01_admin_1.1.1.1_insA_package.pck
密码和相关信息储存
通过sqlplus进行数据库更新时用到的相关信息在本例中是储存在sqlplus中转机上,只有root账号可以获取到,并且密码是以密文的形式储存,密码的密文储存在以下路径的文件中(文件名是账号的密文): .XXX/.IP地址/.账号的密文 ,数据库实例名和端口号储存在以下路径中的文件中: .YYY/.IP地址/.实例名 ,整个路径只有root账号可以访问。示例如下:

发布过程
更新文件相关规范已经确定好,发布过程可以使用编写好的脚本(shell或bat等)轮询执行要更新的文件,从sqlplus中转机依次将内容更新到对应的Oracle服务器。

本例结合 嘉为蓝鲸应用发布 这款基于蓝鲸平台的SaaS将数据库发布过程流程化,调用发布脚本实现定时或实时更新,具体情况见下图:

无需逐个登录Oracle服务器,来回切换不同账号,更新过程中的日志如下图所示:


其它说明
本例通过sqlplus发布Oracle更新,需要有以下几个前提: 账号和密码信息要提前录入到sqlplus中转机(务必确保账号安全) Oracle服务器相关信息(IP、端口、实例名)也需要提前录入到sqlplus中转机 更新文件中的语法和符号必须规范(例如行尾的“;”号,包头包体结束位置的“/”)等等 数据库、sqlplus客户端的编码尽量保持一致。

作者: 徐晗

文章到此结束了~
想到中秋即将来临啦!有没有很期待呀~
在此先预祝大家 中秋节快乐 !

佳节好文
业务复杂、数据庞大、应用广怎办?了解下分布式事务的解决思路!
赣州银行增强科技创新,实现一键灾备切换
SaaS设计:自动化服务启停设计示例
这里有份选择云服务商的攻略,请查收…
如何设计大型集团一体化IT运维系统
数据库
2019-09-10 17:42:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
create table test(id number);
create table audit_table(table_name varchar2(20),ins int,upd int,del int);
create or replace trigger test_tri
after insert or update or delete on test
declare
v_count int;
begin
select count(*) into v_count from audit_table where table_name='TEST';
if v_count=0 then
insert into audit_table values( 'TEST',0,0,0);
end if;
case
when insertin then
update test set ins=ins+1 where table_name='TEST';
when updating then
udpate test set upd=upd+1 where table_name='TEST';
when deleting then
update test set del=del+1 where table_name='TEST';
end case;
end;
begin
for i in 1..100 loop
insert into test values(i);
end loop;
end;
select * from audit_table;
table_name ins upd del

TEST 1 0 0
=====
alter trigger as:
=====
create or replace trigger test_tri
after insert or update or delete on test
for each row
declare
v_count int;
begin
select count(*) into v_count from audit_table where table_name='TEST';
if v_count=0 then
insert into audit_table values( 'TEST',0,0,0);
end if;
case
when insertin then
update test set ins=ins+1 where table_name='TEST';
when updating then
udpate test set upd=upd+1 where table_name='TEST';
when deleting then
update test set del=del+1 where table_name='TEST';
end case;
end;
truncate audit_table;
table has been truncated.
truncate test;
table has been truncated.
begin
for i in 1..100 loop
insert into test values(i);
end loop;
end;
select * from audit_table;
table_name ins upd del

TEST 100 0 0
数据库
2019-09-05 16:34:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> SELECT * FROM 表名 t WHERE IF (t.年龄>18,t.性别='M',1=1) AND t.性别='F'
数据库
2019-08-30 11:34:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在现实的开发中,我们很少用到让MySQL自己生成uuid,因为在高并发场景下,这是不被允许的。通常是在代码中生成id,或者是使用专业的id服务器( Twitter-Snowflake )。我们要讨论的内容是,当我们在手动输入元数据的时候,如果恰巧有一列uuid,我们不想手动输入,而是希望MySQL为我们自动创建,那么我们可以使用触发器。 DELIMITER ;; CREATE TRIGGER `foo_before_insert` BEFORE INSERT ON `foo` FOR EACH ROW BEGIN IF new.id IS NULL THEN SET new.id = uuid(); END IF; END;; DELIMITER ;
最后,希望您谨慎地使用触发器、视图、外键等等,它们都会影响MySQL的性能。 本文的sql来自: MySQL set default id UUID
数据库
2019-08-30 10:37:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
8月28日,腾讯云数据库在京正式启动战略升级,宣布未来将聚焦云原生、自治、超融合三大战略方向,以用户为中心,联接未来。并在现场面向全球用户同步发布五大战略级新品,包括数据库智能管家DBbrain、云数据库TBase、数据库备份服务DBS、云数据库Redis混合存储版,以及自研云原生数据库CynosDB商业化版本。
战略聚焦三大主航道,推进百万企业全面上云
作为企业IT的核心系统之一,数据库市场正在面临根本性变革。在全面上云的大趋势下,传统的数据库上云模式逐渐无法满足客户业务的快速扩展和智能运维需求。客户需要的是一套能够灵活扩展、智能诊断,支持跨云融合的新一代云端原生数据库系统,未来数据库发展方向将从“数据库+云”模式全面转向“云+数据库”模式。
基于这样的判断,腾讯云数据库的未来将战略聚焦云原生、自治、超融合三大主航道,完善自身产品矩阵。让融合了云与AI能力的数据库变得更智能、稳定、可靠、安全、便宜,推进百万企业全面“上云”进程。
腾讯云数据库产品总监王义成表示:“全球数据库的发展即将进入下一战场,以腾讯云为代表的云厂商,基于在云计算、大数据以及人工智能领域的规模和技术积累,在这一轮技术变革中将具备众多先天优势。有鉴于此,腾讯云数据库充分整合多年服务各行各业的成功经验,推出多款能够真正满足用户需求的数据库产品,助力企业快速数字化、智能化转型。”
重磅发布五大战略级新品,布局未来数据库场景
云原生赛道,腾讯云宣布自研新一代企业级分布式数据库CynosDB正式商业化。不同于过去云数据库在传统数据库之上扩展一部分云的能力,CynosDB是纯粹的云原生数据库,融合了传统数据库、云计算和新硬件的优势,支持海量存储、百万级查询和秒级的故障恢复,100%兼容MySQL和PostgreSQL,提供更智能的运维管理和更可靠的安全保障。与高性能形成对比的是,CynosDB价格仅为市面上商业数据库的1/15,极大降低了企业“上云”成本。
数据库自治领域,腾讯云结合前沿的人工智能技术推出了数据库智能管家DBbrain。它将大量数据库问题的诊断优化工作自动化、智能化和可视化,利用机器学习、大数据技术快速学习资深数据库管理员的成熟经验,服务于云上和云下企业,构建一套“会说话”的数据库服务系统。作为一款数据库智能诊断和优化产品,DBbrain为用户提供实时的性能诊断和安全防护,高效的帮助用户定位故障原因、优化建议、协助用户从源头进行预防,并通过AI调参能力,提升数据库整体性能。
超融合领域,腾讯云发布云数据库TBase及数据库备份服务DBS,并启动了腾讯云Redis混合存储版邀约测试。
详细来说,TBase是腾讯自主研发的分布式国产数据库,提供领先的HTAP能力,在提供NewSQL便利性的同时完整支持分布式事务并保持SQL兼容性,支持RR、RC、SSI三种隔离级别,同时兼容Oracle语法。对于日益多元化的企业客户,TBase满足了他们对业务融合、场景融合、管理融合的更高诉求。强大的安全和容灾能力,让TBase已经成功应用在腾讯内部的微信支付,以及外部众多金融、政府、电信、医疗等行业的核心业务系统。
数据库备份DBS拥有一套完整的数据备份和数据恢复解决方案,具备实时备份和秒级的数据恢复能力,可以为公有云及混合云环境下的用户数据库提供强有力的容灾服务。该产品的推出,有望让“光缆挖断”导致业务停摆成为历史,最大程度的降低非可控因素比如地震、火灾、海啸、甚至挖掘机等对业务造成的影响。
云数据库Redis混合存储版本是腾讯云自主研发的,100%兼容Redis协议和数据结构,具备自动降冷能力的版本。产品特性上,Redis混合存储版支持所有数据存储到磁盘,热数据自动缓存,冷数据自动落盘,能够显著降低客户运营成本,提高研发效率。目前,该产品最大提供36TB存储规格。
产品家族进一步丰富,增速连续两年全球前三
随着战略定位的进一步聚焦以及众多新产品的推出,腾讯云数据库产品家族将进一步丰富,企业上云不仅更为方便,成本也会越来越低。截止目前,腾讯云数据库服务涵盖总计20多种数据库服务。同时,还在以每年发布5大产品、50多个新功能的速度递增。
腾讯云数据库产品线涵盖了业内主流的数据库产品,包括开源数据库MySQL、MariaDB、MongoDB、Redis;商业数据库Oracle、SQL Server;自研数据库TDSQL、TBase以及云原生数据库CynosDB。同时,随着5G时代的到来,针对物联网、大数据等海量时序数据的场景,腾讯云推出时序数据库CTSDB。除此之外,腾讯云也已经启动图数据库等新型数据库领域的开发工作。
今年以来,腾讯云数据库先后获得Forrester以及Gartner等权威机构的一致认可。在6月份,腾讯云数据库被Forrester评为全球数据库领域 “实力竞争者” ,同时,Gartner报告也显示,2018年腾讯云数据库市场份额增速达123%,位列国内所有数据库厂商之首,在全球范围内保持了连续两年增速前三的迅猛势头。
未来,腾讯云数据库将深耕云原生、自治、超融合三大战场,以 “云+数据库” 新模式助力产业腾飞,为各行业用户数字化转型助力!
数据库
2019-08-29 10:51:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
数据库的高可用是指最大程度地为用户提供服务,避免服务器宕机等故障带来的服务中断。数据库的高可用性不仅仅体现在数据库能否持续提供服务,而且也体现在能否保证数据的一致性。
SequoiaDB 巨杉数据库作为一款100%兼容 MySQL 的国产开源分布式数据库,它在高可用方面的表现如何?它的高可用性是如何实现的?本文将详细描述SequoiaDB巨杉数据库的高可用性原理,并进行测试验证。

01 巨杉分布式集群架构
SequoiaDB 巨杉数据库采用计算与存储分离架构,SequoiaSQL-MySQL 是 SQL 计算层,存储层由协调节点、编目节点和数据节点组成。
图1 SequoiaDB分布式架构
如图1所示是最简单的 SequoiaDB 分布式数据库集群架构图,由1个协调节点,1个编目节点,3个数据节点和 SequoiaSQL-MySQL 构成。其中数据节点在三个服务器上,包括三个数据复制组1、2、3,每个数据复制组由3个完全相同的数据副本组成,数据副本之间通过日志同步保持数据一致。
A, A1, A2组成数据复制组1,三者是完全相同数据副本。数据复制组2、3类似于数据复制组1。在 SequoiaDB 分布式集群中,每个复制组最多支持 7 个数据副本。
本文的高可用测试环境采用图1所示的分布式集群架构,其中主节点有读写功能,两个备副本可以执行读操作或备份。
02 巨杉数据库高可用实现
SequoiaDB 高可用采用 Raft 算法实现,多副本之间通过日志同步保持数据一致性。
图2 三个节点之间保持连接

如图2所示,SequoiaDB 集群三个副本之间通过心跳保持连接。
数据组副本之间通过共享心跳信息 sharing-beat 进行状态共享。如图3所示,sharing-beat 心跳信息结构包括心跳 ID、自身开始LSN、自身终止LSN、时间戳、数据组版本号、自身当前的角色和同步状态。
图3 心跳状态信息结构

每个节点都维护一张 status-sharing table 表,用来记录节点状态。sharing-beat 每2秒发送一次,采集应答信息,若连续N秒未收到应答信息,则认为节点宕机。

集群中只有一个节点作为主节点,其他节点为备节点。如果出现多主或者双主,需要根据 LSN 对比进行降备,保证集群中只有一个主节点。
Note:
1)当主节点宕机时,需要从备节点中选举出一个新的节点作为新的主节点。
2)当备节点宕机时,主节点不受影响,等备节点恢复后,通过日志同步继续与主节点保持数据一致即可。

下面介绍当主节点宕机时,选举新主节点的过程。
选举条件
满足下面2个条件可以被选举成为主节点:
1. 多数备节点投票通过
2. 该节点LSN最大

选举过程
1)当主节点A宕机时,A自动降为备节点,关闭协调节点的业务连接。
图4 集群中主节点挂掉

2)A1和A2都会判断自身是否具备升为主节点的条件,若符合即发起选举请求。
条件内容:
自己不是主节点
剩下的备节点占半数以上
自己的LSN比其它备节点的LSN新
3)其它备节点会把被投票节点的 LSN 与自己的 LSN 做对比,若比自己的 LSN 新,则投赞成票,否则投反对票。
4)若赞成票超过(n/2+1),则支持该节点为主节点,选举成功。否则保持备节点角色,选举失败。
5)选举成功后,通过心跳状态信息共享数据组信息给其它节点。

03 高可用容灾验证
一般分布式数据库 POC 测试包含功能测试、性能测试、分布式事务测试、高可用容灾测试和兼容性测试等。下面将对 SequoiaDB 巨杉数据库的高可用性进行验证测试。
测试环境说明

本文测试环境采用分布式集群,包含1个 SequoiaSQL-MySQL,3个数据节点,1个编目节点,1个协调节点,搭建集群方式具体可参考巨杉官网虚拟机镜像搭建教程。在 kill 掉主节点进程之后,我们对分布式数据库集群进行读写操作,来验证高可用性。

查看服务器集群状态
# service sdbcm status
.....
Main PID: 803 (sdbcm)
Tasks: 205 (limit: 2319)
CGroup: /system.slice/sdbcm.service
├─ 779 sdbcmd
├─ 803 sdbcm(11790)
├─1166 sequoiadb(11840) D
├─1169 sequoiadb(11810) S
├─1172 sequoiadb(11830) D
├─1175 sdbom(11780)
├─1178 sequoiadb(11820) D
├─1181 sequoiadb(11800) C
1369 /opt/sequoiadb/plugins/SequoiaSQL/bin/../../../java/jdk/bin/java -jar /opt/sequoiadb/plugins/SequoiaSQL
.....

SequoiaDB 分布式集群中数据节点端口在11820,11830,11840;编目节点11800,协调节点在11810
sdbadmin @sequoiadb :~$ ps -ef|grep sequoiadb
sdbadmin 1166 1 0 Aug20 ? 00:02:23 sequoiadb(11840) D
sdbadmin 1169 1 0 Aug20 ? 00:01:43 sequoiadb(11810) S
sdbadmin 1172 1 0 Aug20 ? 00:02:24 sequoiadb(11830) D
sdbadmin 1178 1 0 Aug20 ? 00:02:33 sequoiadb(11820) D
sdbadmin 1181 1 0 Aug20 ? 00:04:01 sequoiadb(11800) C
kill 掉11820的主节点,执行查询和写入sql
sdbadmin @sequoiadb :~$ kill 1178
sdbadmin @sequoiadb :~$ ps -ef|grep sequoiadb
sdbadmin 1166 1 0 Aug20 ? 00:02:24 sequoiadb(11840) D
sdbadmin 1169 1 0 Aug20 ? 00:01:43 sequoiadb(11810) S
sdbadmin 1172 1 0 Aug20 ? 00:02:24 sequoiadb(11830) D
sdbadmin 1181 1 0 Aug20 ? 00:04:01 sequoiadb(11800) C
sdbadmin 1369 1 0 Aug20 ? 00:01:33 /opt/sequoiadb
....
执行查看 sql,查看插入操作之前数据为121
mysql> select * from news.user_info;
+------+-----------+
| id | unickname |
+------+-----------+
| 1 | test1 |
........
| 119 | test119 |
| 120 | test120 |
| 121 | test121 |
+------+-----------+
121 rows in set (0.01 sec)
执行写入 sql,查看插入是否成功
mysql> insert into news.user_info(id,unickname)values(122,"s
uccess");
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from news.user_info;
+------+-----------+
| id | unickname |
+------+-----------+
| 1 | test1 |
.........
| 120 | test120 |
| 121 | test121 |
| 122 | success |
+------+-----------+
122 rows in set (0.00 sec)
数据(122, “success”)数据插入成功,在其中一个主节点挂掉情况下,读写都没有受到影响,数据读写保持一致,高可用性得到验证。

现在执行导入1000w数据写入脚本 imprt.sh,在执行过程中 kill 掉主数据节点,模拟主节点故障场景,在巨杉数据库图形化监控界面 SAC 上查看集群读写变化。
Note:
如果需要获取 imprt.sh 脚本,关注“巨杉数据库”公众号回复 “imprt” 即可获取。

执行导入数据脚本
./imprt.sh 协调节点主机 协调节点端⼝ 次数
./imprt.sh 192.168.1.122 11810 100
如图5所示,在执行导入数据时刻,kill 掉主数据节点,insert 写入下降,之后集群恢复高可用
图5 SAC监控界面集群读写变化示意图
图6 SAC查看tpcc写入数据量示意图
从 SAC 可视化界面中可以看到,当主数据节点在我们执行插入1000w数据操作的过程中出现故障,数据读写受到影响的持续时间很短。最后通过使用 imprt.sh 脚本重新导入插入失败的数据,则可以保证数据最终一致性。

04 总结
SequoiaDB 分布式集群具备较好的高可用性,集群可以设置多个数据复制组,每个数据复制组由多个完全相同的副本构成,副本之间通过 Raft 算法和日志同步方式保持数据一致性。最后,本文也验证了在执行千万级数据写操作时,若集群主数据节点宕机,分布式集群可以正常读写数据,并且数据保持最终一致,高可用性得到验证。
数据库
2019-08-28 11:55:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
表结构:
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四
李四
数学
物理
84
94
转化结果:
姓名 语文 数学 物理 平均分 总分
张三
李四
74
74
83
84
93
94
83.33
84.00
250
252
sql实现: SELECT t.student AS '姓名', max( CASE WHEN course_name = '语文' THEN '分数' ELSE 0 END ) AS '语文', max( CASE WHEN course_name = '数学' THEN '分数' ELSE 0 END ) AS '数学', max( CASE WHEN course_name = '物理 ' THEN '分数' ELSE 0 END ) AS '物理 ', cast(avg(t.score) as decimal(18,2)) AS '平均分', sum(t.score) AS '总分' FROM course t GROUP BY t.student
数据库
2019-08-19 10:35:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
转发: https://blog.csdn.net/zuozewei/article/details/82911173
借鉴: https://testerhome.com/topics/16548

唯一需要修改的是:
数据库
2019-07-31 09:51:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
原创: 陈俊聪
背景
很神奇,5.7.17 和 8.0.17,连续两个17小版本都让人眼前一亮。前者加入了组复制(Group Replication)功能,后者加入了克隆插件(Clone Plugin)功能。今天我们实战测试一下这个新功能。

克隆插件简介
克隆插件允许在本地或从远程 MySQL 实例克隆数据。克隆数据是存储在 InnoDB 其中的数据的物理快照,其中包括库、表、表空间和数据字典元数据。克隆的数据包含一个功能齐全的数据目录,允许使用克隆插件进行 MySQL 服务器配置。

克隆插件支持两种克隆方式 本地克隆 远程克隆

本地克隆
本地克隆操作将启动克隆操作的 MySQL 服务器实例中的数据克隆到同服务器或同节点上的一个目录里
远程克隆
默认情况下,远程克隆操作会删除接受者(recipient)数据目录中的数据,并将其替换为捐赠者(donor)的克隆数据。(可选)您也可以将数据克隆到接受者的其他目录,以避免删除现有数据。
远程克隆操作和本地克隆操作克隆的数据没有区别,数据是相同的。
克隆插件支持复制。除克隆数据外,克隆操作还从捐赠者中提取并传输复制位置信息,并将其应用于接受者,从而可以使用克隆插件来配置组复制或主从复制。使用克隆插件进行配置比复制大量事务要快得多,效率更高。

实战部分
一、本地克隆
安装克隆插件
启动前 [mysqld] plugin-load-add=mysql_clone.so
或运行中 INSTALL PLUGIN clone SONAME 'mysql_clone.so';
| 第二种方法会注册到元数据,所以不用担心重启插件会失效。两种方法都很好用
检查插件有没有启用 mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'clone'; +-------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +-------------+---------------+ | clone | ACTIVE | +-------------+---------------+ 1 row in set (0.01 sec)
设置强制启动失败参数 [mysqld] plugin-load-add=mysql_clone.so clone=FORCE_PLUS_PERMANENT

| 如果克隆插件对你们很重要,可以设置clone=FORCE_PLUS_PERMANENT或clone=FORCE。作用是:如果插件未成功初始化,就会强制mysqld启动失败。
克隆需要的权限
需要有备份锁的权限。备份锁是 MySQL 8.0 的新特性之一,比5.7版本的flush table with read lock要轻量。 mysql> CREATE USER clone_user@'%' IDENTIFIED by 'password'; mysql> GRANT BACKUP_ADMIN ON *.* TO 'clone_user'; # BACKUP_ADMIN是MySQL8.0 才有的备份锁的权限
执行本地克隆 mysql -uclone_user -ppassword -S /tmp/mysql3008.sock mysql> CLONE LOCAL DATA DIRECTORY = '/fander/clone_dir';
| 例子中需要 MySQL 的运行用户拥有 fander 目录的rwx权限,要求 clone_dir 目录不存在
本地克隆的步骤如下 DROP DATA FILE COPY PAGE COPY REDO COPY FILE SYNC
观察方法如下 mysql> SELECT STAGE, STATE, END_TIME FROM performance_schema.clone_progress; +-----------+-------------+----------------------------+ | STAGE | STATE | END_TIME | +-----------+-------------+----------------------------+ | DROP DATA | Completed | 2019-07-25 21:00:18.858471 | | FILE COPY | Completed | 2019-07-25 21:00:19.071174 | | PAGE COPY | Completed | 2019-07-25 21:00:19.075325 | | REDO COPY | Completed | 2019-07-25 21:00:19.076661 | | FILE SYNC | Completed | 2019-07-25 21:00:19.168961 | | RESTART | Not Started | NULL | | RECOVERY | Not Started | NULL | +-----------+-------------+----------------------------+ 7 rows in set (0.00 sec)
当然还有另外一个观察方法 mysql> set global log_error_verbosity=3; Query OK, 0 rows affected (0.00 sec) mysql> CLONE LOCAL DATA DIRECTORY = '/fander/clone6_dir'; Query OK, 0 rows affected (0.24 sec) [root@192-168-199-101 data]# tailf mysql-error.err 2019-07-25T22:22:58.261861+08:00 8 [Note] [MY-013457] [InnoDB] Clone Begin Master Task by root@localhost 2019-07-25T22:22:58.262422+08:00 8 [Note] [MY-013457] [InnoDB] Clone Apply Master Loop Back 2019-07-25T22:22:58.262523+08:00 8 [Note] [MY-013457] [InnoDB] Clone Apply Begin Master Task ... 2019-07-25T22:22:58.471108+08:00 8 [Note] [MY-013458] [InnoDB] Clone Apply State FLUSH DATA: 2019-07-25T22:22:58.472178+08:00 8 [Note] [MY-013458] [InnoDB] Clone Apply State FLUSH REDO: 2019-07-25T22:22:58.506488+08:00 8 [Note] [MY-013458] [InnoDB] Clone Apply State DONE 2019-07-25T22:22:58.506676+08:00 8 [Note] [MY-013457] [InnoDB] Clone Apply End Master Task ID: 0 Passed, code: 0: 2019-07-25T22:22:58.506707+08:00 8 [Note] [MY-013457] [InnoDB] Clone End Master Task ID: 0 Passed, code: 0:
测试,起一个克隆的实例 /opt/mysql8.0/bin/mysqld --datadir=/fander/clone_dir --port=3333 --socket=/tmp/mysql3333.sock --user=mysql3008user --lower-case-table-names=1 --mysqlx=OFF #解释,因为我没有使用my.cnf,所以加了比较多参数 #--datadir 指定启动的数据目录 #--port 指定启动的MySQL监听端口 #--socket 指定socket路径 #--user `捐赠者`的目录权限是mysql3008user:mysql3008user,用户也是mysql3008user,我没有修改 #--lower-case-table-names=1 同`捐赠者` #--mysqlx=OFF 不关闭的话,默认mysqlx端口会合`捐赠者`的33060重复冲突 #登录检查 mysql -uroot -proot -S /tmp/mysql3333.sock mysql> show master status\G # 可见GTID是`捐赠者`的子集,所以说明`接受者`直接和`捐赠者`建主从复制是很简单的

二、远程克隆
远程克隆的前提条件和限制 捐赠者和接受者都需要安装克隆插件 捐赠者和接受者分别需要有至少BACKUP_ADMIN/CLONE_ADMIN权限的账号
| 暗示了接受者必须先启动一个数据库实例(空或有数据的实例均可,因为都会被删除) 克隆目标目录必须有写入权限 克隆操作期间不允许使用 DDL,允许并发DML。要求相同版本号,您无法MySQL 5.7和MySQL 8.0之间进行克隆,而且要求版本>=8.0.17 同一平台同一架构,例如linux to windows、x64 to x32 是不支持。 足够的磁盘空间 可以克隆操作一般表空间,但必须要有目录权限,不支持克隆使用绝对路径创建的一般空间。与源表空间文件具有相同路径的克隆表空间文件将导致冲突 远程克隆时不支持CLONE INSTANCE FROM中通过使用mysqlx的端口 克隆插件不支持克隆MySQL服务器配置my.cnf等 克隆插件不支持克隆二进制日志。 克隆插件仅克隆存储的数据 InnoDB。不克隆其他存储引擎数据。MyISAM并且 CSV存储在包括sys模式的任何模式中的表都被克隆为空表。 不支持通过MySQL router连接到捐赠者实例。 一些参数是必须一致的,例如innodb_page_size、innodb_data_file_path、--lower_case_table_ names 如果克隆加密或页面压缩数据,则捐赠者和接收者必须具有相同的文件系统块大小 如果要克隆加密数据,则需要安全连接 clone_valid_donor_list 在接受者的设置必须包含捐赠者 MySQL 服务器实例的主机地址。 必须没有其他克隆操作正在运行。一次只允许一次克隆操作。要确定克隆操作是否正在运行,请查询该 clone_status表。 默认情况下,克隆数据后会自动重新启动接受者 MySQL 实例。要自动重新启动,必须在接收方上提供监视进程以检测服务器是否已关闭。否则,在克隆数据后,克隆操作将停止并出现以下错误,并且关闭接受者 MySQL 服务器实例:

| 此错误不表示克隆失败。这意味着必须在克隆数据后手动重新启动接受者的 MySQL 实例。
远程克隆实战
假设前提条件都满足,步骤如下
和本地克隆一样,远程克隆需要插件安装和用户授权。捐赠者、接受者的授权略有不同。
1. 确保捐赠者和接受者都安装了克隆插件 INSTALL PLUGIN clone SONAME 'mysql_clone.so';
2. 用户账号授权
捐赠者授权 mysql> CREATE USER clone_user@'192.168.199.101' IDENTIFIED by 'password1'; mysql> GRANT BACKUP_ADMIN ON *.* TO 'clone_user'@'192.168.199.101'; # BACKUP_ADMIN是MySQL8.0 才有的备份锁的权限
接受者授权 mysql> CREATE USER clone_user@'192.168.199.102' IDENTIFIED by 'password2'; mysql> GRANT CLONE_ADMIN ON *.* TO 'clone_user'@'192.168.199.102';

| CLONE_ADMIN权限 = BACKUP_ADMIN权限 + SHUTDOWN权限。SHUTDOWN仅限允许用户shutdown和restart mysqld。授权不同是因为,接受者需要restart mysqld。
3. 接受者设置捐赠者列表清单 mysql -uclone_user -ppassword2 -h192.168.199.102 -P3008 mysql> SET GLOBAL clone_valid_donor_list = '192.168.199.101:3008';

| 这看起来是一个安全相关参数。多个实例用逗号分隔,例如“HOST1:PORT1,HOST2:PORT2,HOST3:PORT3”
接受者开始拉取克隆捐赠者数据​​​​​​​ CLONE INSTANCE FROM clone_user@'192.168.199.101':3008 IDENTIFIED BY 'password1';
远程克隆步骤如下 mysql> SELECT STAGE, STATE, END_TIME FROM performance_schema.clone_progress; +-----------+-----------+----------------------------+ | STAGE | STATE | END_TIME | +-----------+-----------+----------------------------+ | DROP DATA | Completed | 2019-07-25 21:56:01.725783 | | FILE COPY | Completed | 2019-07-25 21:56:02.228686 | | PAGE COPY | Completed | 2019-07-25 21:56:02.331409 | | REDO COPY | Completed | 2019-07-25 21:56:02.432468 | | FILE SYNC | Completed | 2019-07-25 21:56:02.576936 | | RESTART | Completed | 2019-07-25 21:56:06.564090 | | RECOVERY | Completed | 2019-07-25 21:56:06.892049 | +-----------+-----------+----------------------------+ 7 rows in set (0.01 sec)
接受者如果非mysqld_safe启动的,会报错误,但不影响克隆,需要您人手启动mysqld即可。 ERROR 3707 (HY000): Restart server failed (mysqld is not managed by supervisor process).
实操后小结:克隆与xtrabackup的对比 克隆和xtrabackup备份都属于物理热备,备份恢复原理也近似。 克隆在恢复实例时需要先启动一个实例并授权,而xtrabackup不需要。 xtrabackup在backup后需要apply log,克隆是类似mysqlbackup提供的backup-and-apply-log,合并在一起做。 xtrabackup备份文件的权限等于执行命令的人的权限,恢复实例时需要人手chown回实例权限,克隆备份后权限和原数据权限一致,无需再人手chown,方便恢复。 xtrabackup恢复时需要在mysql中执行reset master;然后set global gtid_purged="UUID:NUMBER",具体的UUID:NUMBER的值为备份文件中的xtrabackup_info文件的内容;克隆不需要这个操作步骤,默认克隆完就可以建立复制了。 xtrabackup备份完一般是scp拷贝到另外一台机器恢复,走的是22端口;克隆走的是MySQL的监听端口。所以在目录权限正确的情况下,甚至根本不需要登录Linux服务器的权限。如下: [root@192-168-199-103 ~]# mysql -uroot -ppassword2 -h192.168.199.102 -P3008 -e "SET GLOBAL clone_valid_donor_list = '192.168.199.101:3008';" mysql: [Warning] Using a password on the command line interface can be insecure. [root@192-168-199-103 ~]# mysql -uroot -ppassword2 -h192.168.199.102 -P3008 -e "CLONE INSTANCE FROM root@'192.168.199.101':3008 IDENTIFIED BY 'password1';" mysql: [Warning] Using a password on the command line interface can be insecure.
三、利用克隆建立主从复制
克隆出来的接受者实例,可以与捐赠者建立主从复制。当然建立组复制也是可以的,鉴于篇幅的原因,不做示例有兴趣可以阅读官方文档18.4.3.1节“克隆分布式恢复”。
| https://dev.mysql.com/doc/refman/8.0/en/group-replication-cloning.html
传统复制时,通过以下命令查看postion位置 mysql> SELECT BINLOG_FILE, BINLOG_POSITION FROM performance_schema.clone_status; +------------------+-----------------+ | BINLOG_FILE | BINLOG_POSITION | +------------------+-----------------+ | mysql-bin.000014 | 2179 | +------------------+-----------------+ 1 row in set (0.01 sec)
GTID复制时,通过以下命令查看GTID位置 ​​​​​​​ mysql> SELECT @@GLOBAL.GTID_EXECUTED; +-------------------------------------------+ | @@GLOBAL.GTID_EXECUTED | +-------------------------------------------+ | 990fa9a4-7aca-11e9-89fa-000c29abbade:1-11 | +-------------------------------------------+ 1 row in set (0.00 sec)
假设已经有如下授权,我们就可以建立复制啦 create user repl@'%' identified WITH 'mysql_native_password' by 'password'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';
建立GTID主从复制关系
(接受者上) CHANGE MASTER TO MASTER_HOST='192.168.199.101', MASTER_USER='repl', MASTER_PASSWORD='password', MASTER_PORT=3008, MASTER_AUTO_POSITION=1;

监控克隆操作
检查克隆进度 mysql> SELECT STATE FROM performance_schema.clone_status; +-----------+ | STATE | +-----------+ | Completed | +-----------+ 1 row in set (0.00 sec)
SELECT STATE, ERROR_NO, ERROR_MESSAGE FROM performance_schema.clone_status;
检查克隆是否出错 mysql> SELECT STATE, ERROR_NO, ERROR_MESSAGE FROM performance_schema.clone_status; +-----------+----------+---------------+ | STATE | ERROR_NO | ERROR_MESSAGE | +-----------+----------+---------------+ | Completed | 0 | | +-----------+----------+---------------+ 1 row in set (0.00 sec)
检查克隆次数 show global status like 'Com_clone'; # `捐赠者` 每次+1,`接受者` 0
随时可以kill掉克隆 使用 SELECT * FROM performance_schema.clone_status\G 或 show processlist 查看线程ID,然后kill ID
总结
克隆功能非常有趣。他操作简单,可以用于快速搭建、恢复主从复制或组复制,可以部分取代开源热备软件xtrabackup,期待未来官方会把他做得越来越好,功能越来越丰富。
数据库
2019-07-29 11:00:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
安装Oracle 11g后,共有7个服务,这七个服务的含义分别为:
1. Oracle ORCL VSS Writer Service:Oracle卷映射拷贝写入服务,VSS(Volume Shadow Copy Service)能够让存储基础设备(比如磁盘,阵列等)创建高保真的时间点映像,即映射拷贝(shadow copy)。它可以在多卷或者单个卷上创建映射拷贝,同时不会影响到系统的系统能。(非必须启动)
2. OracleDBConsoleorcl:Oracle数据库控制台服务,orcl是Oracle的实例标识,默认的实例为orcl。在运行Enterprise Manager(企业管理器OEM)的时候,需要启动这个服务。(非必须启动)
3. OracleJobSchedulerORCL:Oracle作业调度(定时器)服务,ORCL是Oracle实例标识。(非必须启动)
4. OracleMTSRecoveryService:服务端控制。该服务允许数据库充当一个微软事务服务器MTS、COM/COM+对象和分布式环境下的事务的资源管理器。(非必须启动)
5. OracleOraDb11g_home1ClrAgent:Oracle数据库.NET扩展服务的一部分。 (非必须启动)
6. OracleOraDb11g_home1TNSListener:监听器服务,服务只有在数据库需要远程访问的时候才需要。(非必须启动,下面会有详细详解)。
7. OracleServiceORCL:数据库服务(数据库实例),是Oracle核心服务该服务,是数据库启动的基础, 只有该服务启动,Oracle数据库才能正常启动。(必须启动)
那么在开发的时候到底需要启动哪些服务呢?
对新手来说,要是只用Oracle自带的sql*plus的话,只要启动OracleServiceORCL即可,要是使用PL/SQL Developer等第三方工具的话,OracleOraDb11g_home1TNSListener服务也要开启。OracleDBConsoleorcl是进入基于web的EM必须开启的,其余服务很少用。
注:ORCL是数据库实例名,默认的数据库是ORCL,你可以创建其他的,即OracleService+数据库名。
数据库
2019-07-25 17:20:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> Nebula Graph :一个开源的分布式图数据库。作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,还能够实现服务高可用且保障数据安全性。
本篇导读
HBaseCon Asia2019 活动于 2019 年 7 月 20 日于北京金隅喜来登酒店举办,应主办方邀请,Nebula Graph 技术总监-陈恒在活动中发表演讲 “Nebula: A Graph DB based on HBase” 。本篇文章是根据此次演讲所整理出的技术干货,全文阅读需要 30 分钟。
大家下午好,我是陈恒,来自 VESoft,是开源图数据库 Nebula Graph 的开发者。同时,我也是 HBase 的 Commiter(刚才在后面和各位大佬谈笑风生),今天和大家分享的,是我们最近刚开源的分布式图数据库 Nebula Graph。
Nebula Graph 简要介绍
首先,介绍一下我们公司:欧若数网科技有限公司(英文名:VESoft),是 2018 年 10 月份成立的。我们的核心产品是分布式图数据库 Nebula Graph,这个项目从开始到现在,大概做了半年多时间。目前已经 release alpha 版本,还有可以试用的 docker,正式的 release 预计会在 10 月份。
NebulaGraph 是完全开源的,可以在 GitHub 上访问  https://www.github.com/vesoft-inc/Nebula
NoSQL 类数据库的发展趋势
下面我们看一张图,是 DB-Engine 的统计,反映了从 2013 年到今年所有的 NoSQL 类数据库的发展趋势。横轴代表的是时间,纵轴可以认为是受众程度,或者社区里的讨论热烈程度。大家可以看到绿色的线就是图数据库。这些年一共翻了十倍,增长还是非常迅猛的。
图数据库的简要介绍
在座的同事是 HBase 专家,部分可能对于图数据库有些不太了解,我这里简单介绍一下图数据库。它的主要应用场景其实是针对于各种各样的点和边组成的数据集合。
图数据库典型应用场景
举几个例子来讲,典型图数据库的应用场景有以下几个。
社交场景
社交场景是一个我们常见的社交场景,就是好友之间的关系。这个自然而然构成了一张图。比如说:在社交场景里面,做推荐算法。为某个人推荐他的好友。现在典型的方法都是去找好友的好友,然后看好友的好友有没有可能成为新的好友。这里面可能会有各种亲密度关系,以及各种各样的排序。传统来讲,我们可能会利用 MySQL 或者是 HBase 存各种各样的好友关系,然后通过多个 串行的 Key Value 访问来查 。但是真正在线上场景里面的话,是很难满足性能要求的。所以,另外一种做法是利用 离线计算 。把可能的好友关联和推荐都离线计算好。然后,查的时候直接去读已经准备好的数据,但这个问题就在于时效性比较差。对于有一些对于时效性要求比较高的场景,这样是不可以接受的。后面我有一些 case 可以来谈这个事情。
商业关系网络
商业关系网络:数据库的使用场景除了社交网络外,还有商业关系网络也很常见。在金融和风控领域,商业关系其实是很典型的一张图,多个公司之间有贸易往来,公司和银行之间也有往来。比如说一个公司去银行申请贷null款的时候,银行在审查这家公司可能本身没有什么问题,但是这家公司可能控制或者被控制的其他公司,或者整个集团的其他子公司,早已经进入了黑名单,甚至要控制一组相互担保的公司之间的总体贷款额度。这个时候就需要构成一些风控的功能来拒掉这笔贷款。商业关系里面还有人与人之间的转账关系,比如去查一个信用卡反套null现的网络。很典型的一个网络社群就是:A 转账到 B,B 转账到 C,C 又转让回 A 就是一个很典型的一个环。更多的黑产可能会是一个很大的黑产社区。对于这样的闭环,这类查询在图数据库大规模应用之前,大部分都是采用离线计算的方式去找。但是离线场景很难去控制当前发生的这笔交易。例如一个信用卡交易或者在线贷null款,整个作业流程很长,在反套null现这块的审核时间可能只有秒级。这也是图数据库非常大的一个应用场景。
知识图谱
知识图谱:除了商业关系还有一个就是知识图谱。知识图谱这个概念提出来已经很早了,大概是在 2004,2005 年的时候,Google 把自己的 search engine 慢慢的从倒排转到知识图谱上。这样在 search 的时候可以回答一些问题,比如说今天的天气怎么样?比如说现在谁的老婆的外甥的二大爷是谁。根据一些类似于问题式的知识,而不是基于关键字进行 search。知识图谱这几年非常的火,主要的原因就是一些垂直领域,慢慢的发现知识图谱非常的有价值。比如说:在金融领域上会有自己金融领域的知识图谱,结合一些 NLP 技术做大量的财报自动分析,或者像 Kensho 这样的新闻事件驱动自动生成分析报表和投资建议;还可以做一些投资组合,发现一二级市场公司内的关联关系,财务造假或者地方债暴雷造成的风险传播。再比如很多做在线教育的同事可能知道,在线教育的领域里面也有不少知识图谱。比如说有哪些考点,这个题是属于哪些考点,考点之间是怎么关联起来的,做错的考点附近的知识点都需要加强,这样也是一张关系网络。也是图数据库一个广泛应用的场景。
IoT 物联网(Internet of Things)
IoT 物联网(Internet of Things)是目前非常火的。以前,每一个设备都是单纯的 Device,设备和设备之间是没有关系的。但是,物联网的出现打通了这些设备之间的关系。比如:账号和设备都是有绑定关系的,账号和账号之间又有一定的关系,通过这样设备之间也可以建立出一张网络来。
一般来说,图数据库并不是简单的关联关系查询,在图遍历的过程,通常要根据属性做一些计算 。所以一个单单的图关联是完全不能满足要求的。举个例子,这是一个股票新闻实时推荐的场景。
大家知道一般散户炒股并不是高频这样事件驱动的,但是他们也会边看新闻边盯着 K线。所以在这个时候要给他们推荐一些什么新闻呢。一般常见的办法是爬虫获得新闻,清洗完毕后,NLP 分词,提取出关键词后,然后跟股票代码关联上。一般根据监管,可能还要加上一些编辑审核和推送。但大家知道中国股市好的时候,是线下口口相传的。所以有个思路是通过好友圈来推荐新闻:首先会去找用户的好友,然后这些好友可能浏览过一些文章,那么根据驻留时间等,好友对这个文章可能会有一个评分,这个评分用 WL 来表示。好友和好友之间也有一定的亲密度,用 WC 表示。这个亲密度可以来源于之前的聊天次数、转账次数等。同一个人到一篇文章之间可能会有多条路径。比如说到第二篇文章,可能会有几十条路径。所以说这么多条路径的权值之和代表了我和这篇文章的之间的评分。最终给用户推荐文章的时候,希望是把所有的计算结果做 Top N。从线上的结果来看,转化效率比其他几种都要高。
图数据库面临的挑战
再回到技术底层,图数据库面临的技术挑战。
低延时高吞吐
一个挑战就是极低的 latency 和极高的 throughput。比如说查寻好友的好友这样的两跳场景,我们的 Nebula Graph 能在十毫秒以内返回,同时能做到单机 10 万的 pps。
大数据量
另外一个挑战就是数据量。一个典型的银行金融图谱,数据量已经可以达到百 T 规模,大概是几十亿个点和千亿条边的规模。当然,如果图本身只是存点 ID 和点 ID 之间的关联关系,这样的数据量千亿级别其实单机也能存储。但是实际上就像刚才说的,光纯点和边是不够的,还需要有很多图属性做分析,这样单机是肯定不够的。另外就是数据量增长的速度相当快,即使今天单机做个 POC 能存下,可能几个月之后又要扩容了。
复杂的商业逻辑
还有一个挑战就是现在越来越复杂的商业逻辑,用传统的数据库去分析,基本上每一个逻辑都要写 Java 代码来完成查询数据。我们希望不要那么依赖于开发人员,分析人员可以像写 SQL 一样,自己可以搞定。
高可用
最后一个挑战就是关于高可用要求。图数据库刚开始出现的时候,基本上就是一个二级索引。相当于只缓存了点和边之间的关系,真正的属性可能是在别的地方(比如 HBase 或者 MySQL 里面)。Data 本身的正确性和高可用都是都是依赖于这些组件的。但随着使用场景越来越多,这样分离的存储方式性能跟不上,自然就希望数据直接放在图数据库里面。那么各种传统数据库和分布式系统要面临的技术问题也要解决。比如数据多副本的强一致性问题,ACID。对于图数据库的要求越来越多,客户更希望作为一个数据库,而不是作为一个索引或者关系 cache。
Nebula Graph 的特点
存储计算分离
对于 Nebula Graph 来讲,有这么几个技术特点:第一个就是采用了存储计算分离的架构。这样架构主要的考虑其实前面几个 Talk大家都已经讨论了很多,主要好处就是为了上云或者说 弹性   , 方便单独扩容   。上午的 Talk:HBase on Cloud 也有提到,业务水位总是很难预测的,一段时间存储不够了,有些时候计算不够了。在云上或者使用容器技术,计算存储分离的架构运维起来会比较方便,成本也更好控制。大家使用 HBase 那么久,这方面的感触肯定很多。
查询语言 nGQL
Nebula Graph 的第二个技术特点是它的查询语言,我们称为 nGQL,比较接近 SQL。唯一大一点的语法差异就是 不用嵌套  (embedding)。大家都知道嵌套的 SQL,读起来是非常痛苦的,要从里向外读。
支持多种后端存储
第三个特点就是 Nebula Graph 支持多种后端存储,除了原生的引擎外,也支持 HBase。因为很多用户,对 HBase 已经相当熟悉了,并不希望多一套存储架构。从架构上来说,Nebula Graph 是完全对等的分布式系统。
计算下推
和 HBase 的 CoProcessor 一样,Nebula Graph 支持数据计算下推。数据过滤,包括一些简单的聚合运算,能够在存储层就做掉,这样对于性能来讲能提升会非常大。
多租户
多租户,Nebula Graph是通过多 Space 来实现的。Space 是物理隔离。
索引
除了图查询外,还有很常见的一种场景是全局的属性查询。这个和 MySQL 一样,要提升性能的主要办法是为 属性建立索引  ,这个也是 Nebula Graph 原生支持的功能。
图算法
最后的技术特点就是关于图算法方面。这里的算法和全图计算不太一样,更多是一个子图的计算,比如最短路径。大家知道数据库通常有 OLTP 和 OLAP 两种差异很大的场景,当然现在有很多 HTAP 方面的努力。那对于图数据库来说也是类似,我们在设计 Nebula Graph 的时候,做了一些权衡。我们认为全图的计算,比如 Page Rank,LPA,它的技术挑战和 OLTP 的挑战和对应的设计相差很大。我们希望 Nebula Graph 能够在 OLTP 这块提供最好的表现。
Nebula Graph 的架构图
下面为大家介绍 Nebula Graph 的架构图:下图(图1)是基于原生存储的,图3 是基于 HBase 的,有一些不同。
基于原生存储的架构图
这条虚线上面是计算层,下面是存储层。
图 1:基于原生存储的架构图
我们先看下计算层,计算层可以理解为把一个 query 翻译成真正需要的执行计划,产生执行计划之后,到下面存储层拿需要的数据。这和传统数据库非常类似,查询语言也很类似。这里面比较重要的事情是查询优化,除了前面提到的计算下推到存储层外,目前一个主要的实现是并发执行。
查询服务架构图
图 2:Query Service
举个例子,查好友的大于 18 岁的好友,可能好友有很多,没有必要等一度好友都返回后,再去查二度好友。当然应该异步来做,一度好友部分返回后,就立刻开始二度查询。有点类似图计算里面的 BSP 和 ASP。这个优化对提升性能有非常大作用。对于 Storage 层来说,又分为上面的 Storage Engine 和 KV Store。因为 Nebula Graph 是分布式系统,数据是分片的。目前的分片方法是静态哈希,和 HBase 不一样。主要是因为图查询基本都是特定 Prefix 的 Scan,Get 很少。比如一个人的好友这样,就是同一个 prefix 的 scan。每一个数据分片会通过 RAFT 协议来保证数据的强一致。这和 HBase 写到底层 HDFS 也不一样。3 台机器上的 Partition1 组成一个 Raft Group。Storage Engine 做的事情比较简单,就是把图语义的查询请求翻译成 KV 的查询请求。关于图右边的 Meta Service,它的主要作用是 schema 管理和集群管理。Nebula Graph 中的点和边都是有属性和版本的,那每个属性的类型和版本在 MetaService 中统一管理。集群管理主要用于运维和鉴权目的,负责机器上下线和对用户做 ACL。
Nebula Graph 基于 HBase 的架构图
图 3:基于 HBase 的架构图
Nebula Graph 的 HBase 版本略微有些不一样,可以看到虚线往下挪了,Storage Service 嵌入到 Query Engine 里面,这是因为 HBase 是独立的服务,Storage Service 进程可以和 Query Engine 放同一台机器上,直接一对一服务了。另外,因为 HBase 自己有 partition,就没必要再分一次了。
Nebula Graph 的数据模型和 Schema
下面介绍下 Nebula Graph 的数据模型和 Schema(每种标签有一组相对应的属性,我们称之为 schema)。前面说到 Nebula Graph 是有向属性图,点和边都有各自属性。点有点属性,Nebula Graph 里面称为 Tag(类型),点可以有多种类型,或者叫多种 Tag。比如一个点既可以是 Tag  Person,有属性姓名、年龄;也是 Tag developer,属性是擅长的语言。Tag 是可以像类一样继承的,一个 Tag 继承自另外一个 Tag。边和点略微有点不一样,一条边只能有一种类型。但是两个点之间可以有多种类型的边。另外,像 TTL 这种功能,Nebula Graph 也是支持的。
Nebula Graph 的数据模型和 Schema 这里需要提一下,对于 HBase 和 Nebula Graph 原生存储来说,Schema Version 处理上是不一样的。HBase 是没有 Schema 的。
强 Schema 最大的好处,是知道这条数据什么时候结束的,一行有多少属性是知道的。所以在根据某个版本号取数据的时候,从某一行跳到下一行,不需要对每个属性都扫描一遍。但如果像 HBase 这样弱 Schema 系统,性能消耗是非常大。但相应的,问题就来了,如果要支持强 Schema,要变更 Schema 怎么办?更改 Schema 在 MySQL 里面,往往要锁表的。对于 Nebula Graph 来说,当更改 Schema 时,并不是在原来的数据上进行更改,而是先插入一个新的 Schema,当然这要求写入数据的时候就要记录版本号。这样的话在读的时候,才能知道要用版本。
再解释一下关于 Key 的设计,点和边的 Key 设计是像上面两行这样,一个点是一个 Key,一条边是两个 Key,这样的目的是把起点和出边存在一起,对端的终点和入边存储在一起。这样在查询的时候,可以减少一次网络。另外,对于 Nebula Graph 原生存储和 HBase 存储,属性采用的存储方式也是不一样的。在 HBase 里面,一个属性放在一个 Column 里面。在原生存储里面,所有属性放在一个 Column 里面。这样设计的主要原因是,如果用户使用 HBase 作为底层存储,其实他更期望 HBase 这张表在别的系统里面也可以用,所以说我们考虑把它打散开了。
Nebula Graph 查询语言 nGQL
最后讲一下我们的 Query Language:nGQL
我们说它比较类似 SQL,比如说看这个例子,从某一个点开始向外拓展,用 nGQL 来写就是,GO FROM $id,起始点,然后 OVER edge,沿着某条边,还可以设置一些过滤条件,比如说,从这个点出发,沿着我的好友边,然后去拿,里面年龄大于十八岁的。
那要返回哪些属性?可以用 YIELD,比如说年龄,家庭住址。前面提到,不希望嵌套,嵌套太难读了,所以作为替代, Nebula Graph 用 PIPE 管道。前一条子查询的结果,PIPE 给第二条,然后第二条可以 PIPE 给第三条。这样从前往后读起来,和我们人的思考顺序是一样的。但是管道只解决了一个输入源的问题。
多个输入源的时候怎么办?Nebula Graph 还支持定义某个变量,这个和存储过程有点像。像上面这样,把 $var 定义成一个子查询的结果,再把这个结果拿去给其他子查询使用。还有种很常见的查询是全局匹配,这个和 SQL 里面的 SELECT … FROM … WHERE 一样,它要找到所有满足条件的点或者边。我们这里暂时取名叫做 FIND。大家要是觉得其他名字好也可以在 GitHub 上给我们提 issue。
最后就是一些 UDF,用户自己写的函数,有些复杂的逻辑用 SQL 可能不好描述,Nebula Graph 也希望支持让用户自己写一些代码。因为我们代码是 C++ 写的,再考虑到用户群体,做数据分析用 Python 比较多,我们考虑先支持 C++ 和 Python。
Q&A
现场参会者提问:你们这个架构是 TinkerPop 的实现嘛?区别在哪里?
陈恒的回复如下: 不是的。我们是完全自研的。 首先我们查询语法并不是 Gremlin 那样命令式的,而是更接近 SQL 这种描述式的。可以说下我们的思考,我们主要考虑了查询优化的问题。 你知道像 Gremlin 那样的命令式语言,query 是 .in() .out() 这样一步步组成的,所以查询引擎只能按照给定的命令一步步把数据捞上来,每次序列化速度都很慢的,像 Titan 那样。我们的测试结果应该有几十倍的性能差距。 另外既然用户每步指令是明确的,查询引擎要做优化很难,要非常智能推测出用户整一串指令的最终期望才能优化,这样难度比较高。这个有点像编译器的优化,优化错就很麻烦了。 我们希望像 SQL 这样描述式的,用户说明最终目的就行,怎么处理交给执行引擎来优化,这样做优化比较容易完善,可借鉴的算法也比较多。 当然也和我们以 C++ 做开发语言有关。我们也有想过做 Gremlin 和 nGQL 之间的 driver,主要看社区,欢迎给我们提 issue
上面就是本次 Nebula Graph 参会 HBaseCon Asia2019 的全部内容,最后和大家再说下 Nebula Graph 是完全开源的项目,对于图数据库比较感兴趣的同学可以加 wechat 讨论群,或者在 GitHub 上提 Issue,也欢迎大家给我们支持 star 项目。 Nebula Graph:一个开源的分布式图数据库。 GitHub: https://github.com/vesoft-inc/nebula 官方博客: https://nebula-graph.io/cn/posts/ 微博: https://weibo.com/nebulagraph
数据库
2019-07-24 17:56:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1. 10g之前
用户的连接将产生会话,当前会话记录保存在v$session中;处于等待状态的会话会被复制一份放在v$session_wait中。当该连接断开后,其原来的连接信息在v$session和v$session_wait中就会被删除。这是10g之前的状况。
2. v$session_wait_history与ASH
若是一个普通的会话(我是指没有大量地耗费资源),则对于性能调整来说无足轻重。但若该会话在活动时大量占用了资源(比如:CPU,内存,I/O等),该会话信息的丢失,将无法评测当时的系统瓶颈究竟是什么。令DBA高兴的是,oracle10g中保留下了v$session_wait中的这些信息。
在10g中新出现了一个视图:v$session_wait_history。这个视图保存了每个活动session在v$session_wait中最近10次的等待事件。但这对于一段时期内的数据库性能状况的监测是远远不够的,为了解决这个问题,在10g中还新添加了一个视图:v$active_session_history。这就是ASH(active session history)。
典型的情况下,为了诊断当前数据库的状态,需要最近的五到十分钟的详细信息。然而,由于记录session的活动信息是很费时间和空间的,ASH采用的策略是:保存处于等待状态的活动session的信息,每秒从v$session_wait中采样一次,并将采样信息保存在内存中。
3. AWR
注意,ASH的采样数据是保存在内存中。而分配给ASH的内存空间是有限的,当所分配空间占满后,旧的记录就会被覆盖掉;而且数据库重启后,所有的这些ASH信息都会消失。这样,对于长期检测oracle的性能是不可能的。在Oracle10g中,提供了永久保留ASH信息的方法,这就是AWR(auto workload repository)。
由于全部保存ASH中的信息是非常耗费时间和空间的,AWR采用的策略是:每小时对v$active_session_history进行采样一次,并将信息保存到磁盘中,并且保留7天,7天后旧的记录才会被覆盖。这些采样信息被保存在视图wrh$_active_session_history中。而这个采样频率(1小时)和保留时间(7天)是可以根据实际情况进行调整的,这就给DBA们提供了更加有效的系统监测工具。
AWR永久地保存系统的性能诊断信息,由SYS用户拥有。一段时间后,你可能想清除掉这些信息;有时候为了性能诊断,你可能需要自己定义采样频率来获取系统快照信息。Oracle 10g在包dbms_workload_repository中提供了很多过程,通过这些过程,你可以管理快照并设定基线(baselines)。
4. 小结
这样,我们就知道了ASH和AWR产生的原因和功能。ASH保存了系统最新的处于等待的会话记录,可以用来诊断数据库的当前状态;而AWR中的信息最长可能有1小时的延迟,所以其采样信息并不能用于诊断数据库的当前状态,但可以用来作为一段时期内数据库性能调整的参考。
对于这些视图间的继承关系,eygle给出了一个关系图:
图1 各个视图的层次
其中视图dba_hist_active_sess_history是wrh$_active_session_history和其他几个视图的联合展现,通常通过这个视图进行历史数据的访问。
分享: 0
喜欢
数据库
2019-07-22 17:28:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
select * from ti_road_node_code a where a.road_node_id not in ( select en_road_node_id from ti_road_node_relation b) 15毫秒
select * from ti_road_node_code a left join ti_road_node_relation b where a.road_node_id =en_road_node_id and isnull(b.ex_road_node_id) - 报错
select a.* from ti_road_node_code a left join ti_road_node_relation b on a.road_node_id =b.en_road_node_id where isnull(b.ex_road_node_id) 282毫秒 select a.* from ti_road_node_code a left join ti_road_node_relation b on a.road_node_id =b.en_road_node_id where isnull(b.en_road_node_id) 一样
SELECT a.* FROM ti_road_node_code a WHERE NOT EXISTS (SELECT 1 FROM ti_road_node_relation b WHERE b.en_road_node_id = a.road_node_id) 540毫秒 https://blog.csdn.net/a3060858469/article/details/79651428
数据库
2019-07-19 10:57:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
上周五(7月12日)巨杉数据库参与了由得到App主办八里庄技术沙龙活动,分享主题是关于分布式数据库架构与实战。
以下就是根据巨杉数据库现场分享的内容进行的分享实录整理。
巨杉数据库简介
巨杉,专注新一代分布式数据库技术研发,自2011年成立以来,坚持从零开始打造分布式开源数据库引擎,是中国首家连续两年入选 Gartner 数据库报告的数据库厂商。
巨杉数据库的主要产品包括 SequoiaDB 分布式关系型数据库与 SequoiaCM 企业内容管理软件,应用场景包括分布式在线交易、数据中台、分布式内容管理等。
目前,巨杉数据库已在近百家大型商业银行核心生产业务上线,并广泛应用于金融、电信、政府、互联网、交通等领域,企业用户总数超过1000家。
数据库应如何应对微服务发展趋势
很多企 业 内部的 应 用开 发 都在从 传统 中 间 件加数据 库 的 “ 烟囱式 ” 开 发 ,向微服 务 架构 转 型。而在微服 务 体系架构中,几乎每个微服 务 都需要提供数据持久化的能力,而用 户 也希望每个微服 务 所承 载 的数据量能 够 无限的 弹 性 扩张 。但是,在采用微服 务 架构的 过 程中,每个微服 务 使用自身独立的数据 库 存 储 又会使 过 去集中在一个地方的数据分散到很多不同的 设备 中,造成整个 IT 架构的数据 严 重碎片化。
实际上,当企业用户采用微服务体系架构的时候,从数据管理的角度,业界有两种做法。
第一种:就是 对应 用程序 进 行微服 务 改造,底 层 数据 库 使用 传统 集中式数据 库进 行存 储 。 这 种做法学 习 成本也 较 低,其存在数据 紧 耦合,无法 弹 性 扩张 ,以及可能存在 单 点故障等 问题 。
第二种:每一 组 微服 务对应 一个独立的小数据 库 ,往往使用 MySQL 或 PostgreSQL , 业 界使用 较 多的。 这 种机制能 够 解决集中式存 储 的 问题 ,但是也 带 来了新的挑 战 ,包括数据极度碎片化,在微服 务 之 间 无法共享,运 维 成本极其高昂。
两种 办 法都不能很好的解决微服 务 下数据存 储 管理的 问题 ,因此分布式数据 库 就是要解决上述的两个 问题 。第一就是 针对 每个微服 务 做到数据 弹 性 扩张 ,第二就是 对 整个企 业 IT 做到数据的 统 一治理从而避免碎片化存 储 。
联机交易需要什么样的分布式数据库
联机交易数据库特 性
适合微服 务 的分布式数据 库 都 应该 具有特性,主要 应该 从两大 维 度。一是 对传统 技 术 的兼容,二是技 术 和架构的 创 新。
传统技术的兼容方面,必须支持 ACID 和 SQL 的完整性。
从新技 术 的前瞻性来看,首先,分布式数据 库 的核心价 值 在于数据 库资 源池在保 证 与 传统 数据 库 100% 兼容的基 础 上,必 须满 足分布式 弹 性 扩张 ,当 资 源池里面空 间 和 计 算能力不足 时 ,需要通 过动态 增加 计 算存 储节 点的方式 进 行 扩 容。
其次,大家采用的开 发 流程、 SQL 标准、以及安全策略各不相同,因此分布式数据库必须能够支持多种模式的访问接口 。
最后, HTAP ,即交易分析混合 处 理能力。 联 机交易数据与 实时 数据分析在 资 源池内 进 行 资 源隔离, 对 同一份数据 库访问 并可以做到互不干 扰 。适合微服 务 的数据 库 必 须 有 较强 的交易分析混合 处 理能力。
分布式数据库架构及关键特性
巨杉数据 库实例化 架构
要打造适合微服 务 架构的数据 库 ,巨杉数据 库 采用了 计 算存 储 分离的架构。其中存 储层 采用自研的原生分布式数据 库 引擎,上 层计 算 层则 可以 创 建成百上千个数据 库实 例,同 时 每个数据 库实 例 对应 用完全透明,不需感知。如 图 5 所示。
计算与存储分离架 构
对于计算和存储分离,把分布式存储层展开, SeuqoiaDB 分布式存 储 引擎有很多 节 点角色,其中有 协调节 点 编 目 节 点和数据 节 点和 编 目 节 点。 协调节 点是数据路由,数据存 储 在数据 节 点。 编 目 节 点保存整个数据集群系 统 信息。数据 节 点把数据打散到不同的分区中,使用三副本架构,任何一个 节 点出故障,不影响正常运行。
巨杉数据 库 核心 应 用 场 景
巨杉数据 库 大 维 度下的定位是一款真正的金融 级 分布式关系型数据 库 。 巨杉数据 库 目前在企 业级应 用 场 景主要包括分布式在 线 交易、数据中台以及分布式内容管理。
在 线 交易是数据 库 最广泛 应 用的 场 景之一,通常用来支撑核心 业务 运 营 。分布式在 线 交易数据 库 核心 业务 价 值 包括,分布式架构 转 型,高并 发 、高 处 理能力, 业务 持 续扩 展能力以及自主可控与数据安全要求。
数据中台提供全量数据的 实时 在 线 服 务 ,泛指 传统 核心交易以外的所有 对 外服 务业务 。
内容管理平台 为 企 业 提供存 储 、管理和使用海量非 结 构化数据能力。常 见应 用包括影像平台、文档管理平台、音 视频 双 录 系 统 等。
分布式事 务 能力
巨杉数据 库 支持分布式事 务 ,使用二段提交确保多个 节 点之 间 数据一致, 锁 机制是悲 观锁 ,支持 MVCC 的 读 写提交能力。
HTAP 读写分 离
多租户物理隔离能力
通过数据库引擎的分布式架构、Multimodel多模数据存储类型以及实例化的数据库实例管理,巨杉数据库可以实现多租户啊管理的能力、HTAP等等云化数据库必须的技术能力。
最后八里庄技 术 委 员 会主席李丹与巨杉数据 库 技 术专 家合照留念,期待更多数据 库 技 术 交流。
数据库
2019-07-18 18:35:00