数据专栏

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

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
比如:
我们目前系统中有以下 日志 组:
SQL> select group#, sequence #, members ,status , archived from v$log;
GROUP# SEQUENCE# MEMBERS STATUS ARC
---
1 28 1 CURRENT NO
2 26 1 INACTIVE YES
3 27 1 INACTIVE YES
发现每组都只有一个成员,我们想手工增加一个文件到既有组上去:
1* select group#,member from v$logfile
GROUP# MEMBER

3 / oracle /app/oracle/oradata/sztech1/redo03.log
2 /oracle/app/oracle/oradata/sztech1/redo02.log
1 /oracle/app/oracle/oradata/sztech1/redo01.log
我们给组一增加一个镜像成员,方法如下:
alter database add logfile member '/oracle/app/oracle/oradata/sztech1/redo01b.log' to group 1;
之后,我们再查询,文件应该增加上去:
1* select group#,member from v$logfile
GROUP# MEMBER

3 /oracle/app/oracle/oradata/sztech1/redo03.log
2 /oracle/app/oracle/oradata/sztech1/redo02.log
1 /oracle/app/oracle/oradata/sztech1/redo01.log
1 /oracle/app/ oracle /oradata/sztech1/redo01b.log
SQL> select group#,sequence#,members ,status , archived from v$log;
GROUP# SEQUENCE # MEMBERS STATUS ARC
---
1 28
2 CURRENT NO
2 26 1 INACTIVE YES
3 27 1 INACTIVE YES
至此, 日志组成员 增加上去了.
数据库
2019-04-24 17:01:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
欢迎查看原文 - 本博客仅记录
https://blog.csdn.net/kikajack/article/details/80065753
-- 是否开启bin_log日志: off为关闭 -- show variables like 'log_%'; show variables like 'log_bin'; show full processlist -- 查询非 Sleep 状态的链接,按消耗时间倒序展示,自己加条件过滤 : sleep select id, db, user, host, command, time, state, info from information_schema.processlist where command != 'Sleep' order by time desc show master status;
数据库
2019-04-24 15:37:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
轻松筹首创了“大病救助”模式,帮助了众多病患在第一时间解決了医疗资金等问题,为了从源头解决了医疗资金问题。而在轻松筹这样全球5.5亿用户信赖的大病筹款平台的背后,是日益增长的各种数据。面对这样数据量所造成的巨大挑战,阿里云POLARDB是如何帮助轻松筹践行“善DNA”的呢?本文就为大家分享。
关于轻松筹
2014年9月,轻松筹成立。“轻松筹”作为公司旗下的首要产品,“善DNA”可谓贯穿了整个发展历程。轻松筹将目标聚焦在公众健康保障领域,各功能板块都与百姓的健康息息相关。由轻松筹首创的“大病救助”模式帮助众多病患在第一时间解決了医疗资金等问题。
为了从源头解决医疗资金问题,轻松筹于2016年4月推出了“轻松互助”业务,其目的在于抱团抵抗大病风险,一人患病,众人均推救助金。并与多家保险公司达成合作,推出多款会员定制的保险产品,至此,轻松筹“全民健康保障体系”正式建成。
目前,轻松筹在自主研发的“区块链”技术的加持下,再一次开创了行业先河。“阳光链”将大病救助、公益机构及互助行动的捐赠记录、资金流向公开透明,为公益事业及大病救助的发展指明了新方向。历时4年,轻松筹体系(包含大病救助、轻松互助、轻松e保、轻松公益、轻松健康)在全球183个国家和地区的用户总数超过5.5亿、筹款总额超过255亿元。
轻松筹的“大病救助”场景
由轻松筹首创的“大病救助”模式,通过社交强关系为大病患者提供高效便捷的筹款渠道,目前已经帮助235万个大病家庭,筹集了255亿元善款。
轻松筹大病救助平台能够为多达千万的用户提供筹款服务,每周增加的相关数据量多达10GB,包括发起筹款的项目信息、用户分享信息、订单数据等,不断增加的数据,很容易在目前的RDS数据库上,达到存储的上限。轻松筹通过将数据迁移至阿里云POLARDB,很好的解决了存储容量和性能的瓶颈。
轻松筹最为看重就是阿里云POLARDB存储容量大和免分库分表的特性。因为阿里云POLARDB采用了集群架构,并且采用了计算和存储分离以及读写分离的机制,所以其存储容量最高能够支持100TB,用户无需因为单机容量的天花板而去购买多个MySQL实例做分片,并且也不需要考虑分库分表,因此就简化应用的开发,同时也降低了运维的负担。
其次,轻松筹还看中了POLARDB强大的读写分离能力。当应用程序使用集群地址时,POLARDB通过内部的代理层对外提供服务,应用程序的请求都先经过代理,然后才访问到数据库节点。Proxy不仅可以做安全认证和保护,还可以解析SQL,把写操作发送到主节点,把读操作均衡地分发到多个只读节点,实现自动的读写分离。对于轻松筹的小程序而言,在后台使用POLARDB集群就像使用一个单点的MySQL数据库一样简单。
此外,在性能方面,阿里云POLARDB利用基于Redo的物理复制代替基于Binlog的逻辑复制,提升主备复制的效率和稳定性,即使对大表进行加索引、加字段等DDL操作,也不会造成数据库的延迟,能够实现毫秒级延迟。此外,POLARDB内置并行查询引擎,对执行时长超过1分钟的复杂分析类SQL加速效果明显。这样的性能优势能够很好地满足轻松筹的需求。
POLARDB助力“大病救助”平台
数据管理效率提升
在阿里云POLARDB的强大能力的基础之上,轻松筹的“大病救助”平台的数据管理效率有了非常大的提升,其主要体现在以下三个方面:
自适应数据增长
轻松筹的大病筹款项目随着时间的累积,每年以上T以上的结构化数据进行新增进行存储。每年新增数据表达到数百个,单表数据量更是达到亿级别。由于POLARDB采用分布式存储服务,能够根据数据增长自适应增加存储空间,按照实际数据使用量进行计费,不必为数据容量的限制和升级所担忧。
7*24 高可用服务
阿里云POLARDB采用自带读写分离的Active-Active多活高可用集群架构 ,能够更好的监测故障和进行快速故障自动恢复,确保99.95%的高可用服务的同时,集群自带只读节点,使得系统的聚合读取性能成倍提升。
即时数据检索和查询
大病筹款的数据需要周期性批量写入到POLARDB,而同时又需要支持即时的检索查询和分析处理,POLARDB的读写分离架构,很好的支撑了这类场景。同时,POLARDB还能够在几分钟以内在线增加只读节点,进一步提升系统的吞吐处理能力,结合读写分离连接地址,自动进行请求的识别转发,通过自适应负载均衡处理,让集群的计算力能够发挥到最大,消除了计算瓶颈。
作者:桐碧2018
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-04-24 11:47:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
原文: https://blog.csdn.net/u010185262/article/details/53158786 ,文章转载过程中进行了重新排版。
名词解释
在具体的讲解influxdb的相关操作之前先说说influxdb的一些专有名词,这些名词代表什么。
influxDB名词 database:数据库; measurement:数据库中的表; points:表里面的一行数据。
influxDB中独有的一些概念
Point由时间戳(time)、数据(field)和标签(tags)组成。 time:每条数据记录的时间,也是数据库自动生成的主索引; fields:各种记录的值; tags:各种有索引的属性。
还有一个重要的名词:series
所有在数据库中的数据,都需要通过图表来表示,series表示这个表里面的所有的数据可以在图标上画成几条线(注:线条的个数由tags排列组合计算出来)
举个简单的小例子:
有以下数据:
test的series如下:
influxDB基本操作
数据库与表的操作
命令行指令: #创建数据库 create database "db_name" #显示所有的数据库 show databases #删除数据库 drop database "db_name" #使用数据库 use db_name #显示该数据库中所有的表 show measurements #创建表,直接在插入数据的时候指定表名 insert test,host=127.0.0.1,monitor_name=test count=1 #删除表 drop measurement "measurement_name"
数据库用户管理
命令行指令: #显示用户 show users #创建用户 create user "username" with password 'password' #创建管理员权限用户 create user "username" with password 'password' with all privileges #删除用户 drop user "username"
数据保存策略(Retention Policies)
influxDB是没有提供直接删除数据记录的方法,但是提供数据保存策略,主要用于指定数据保留时间,超过指定时间,就删除这部分数据。
查看当前数据库Retention Policies show retention policies on "db_name"
创建新的Retention Policies create retention policy "rp_name" on "db_name" duration 3w replication 1 default
参数说明: rp_name:策略名 db_name:具体的数据库名 3w:保存3周,3周之前的数据将被删除,influxdb具有各种事件参数,比如:h(小时),d(天),w(星期) replication 1:副本个数,一般为1就可以了 default:设置为默认策略
修改Retention Policies alter retention policy "rp_name" on "db_name" duration 30d default
删除Retention Policies drop retention policy "rp_name"
连续查询(Continous Queries)
当数据超过保存策略里指定的时间之后就会被删除,但是这时候可能并不想数据被完全删掉,怎么办?influxdb提供了联系查询,可以做数据统计采样。
查看数据库的Continous Queries show continuous queries
创建新的Continous Queries create continous query cq_name on db_name begin select sum(count) into new_table_name from table_name group by time(30m) end
参数说明: cq_name:连续查询名字 db_name:数据库名字 sum(count):计算总和 table_name:当前表名 new_table_name:存新的数据的表名 30m:时间间隔为30分钟
删除Continous Queries drop continous query cp_name on db_name
数据的插入和查询
InfluxDB数据插入
SQL方式 insert test,host=127.0.0.1,monitor_name=test count=1
说明:influxDB存储数据采用的是Line Protocol格式。那么何谓Line Protoco格式?
Line Protocol格式: 写入数据库的Point的固定格式,如: test,host=127.0.0.1,monitor_name=test count=1 HTTP方式 curl -i -XPOST 'http://127.0.0.1:8086/write?db=metrics' --data-binary 'test,host=127.0.0.1,monitor_name=test count=1'
其中:
test:表名; host=127.0.0.1,monitor_name=test:tag; count=1:field 相对此格式有详细的了解参见 官方文档
InfluxDB数据查询
SQL方式 select * from test order by time desc
HTTP方式 curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=metrics" --data-urlencode "q=select * from test order by time desc"
influxDB是支持类sql语句的,具体的查询语法都差不多,这里就不再做详细的赘述了。
---------------------
作者:miaomiaoLoveCode
来源:CSDN
原文:https://blog.csdn.net/u010185262/article/details/53158786
版权声明:本文为博主原创文章,转载请附上博文链接!
数据库
2019-04-24 11:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
《人是如何学习的》的读后感范文4000字:
本书分为四个部分,第一部分为导论,包含第一章学习:从猜测到科学,第二部分学习者与学习,包含第二章专家与新手的差异,第三章学习与迁移,第四章儿童是怎样学习的,第五章心理与大脑,第三部分教师与教学,包含第六章学习环境设计,第七章有效教学:历史、数学、科学示例,第八章教师的学习,第九章技术支持下的学习,第四部分为学习科学未来发展走向,包含第十章结论,第十一章后续研究。
从书名和目录可以看出,这本书覆盖了心理学、脑科学、信息技术等多个学科领域,借助各个领域的研究成果,来指导或者说帮助人们更有效地学习。现在大都提倡终身学习,因为这个时代的知识和信息量太大了,而且还在不断地增加,一段时间不学习,就感觉被社会淘汰了。一个人的终身学习,大概可以分为上学前的学习,学校里的学习,工作以后的学习,当然这中间都会穿插社会上的学习。
关于上学前的学习,第四章里面有提到婴幼儿的学习,主要是通过与周围环境的交互,看护人的引导或者指导来学习。现在的脑科学已经证实,婴儿生下来时候大脑并不是白板,里面是有内容的。婴儿来到这个世界后,就开始不断地学习,按照书上的说法是什么特惠领域学习,之前也看到过一个名词叫贝叶斯学习法,他们能够快速调整自己的学习。关于婴幼儿的学习,我对自己的两个儿子都稍微观察了一下,大儿子今年7岁,他的观察能力很强,能够发现大人不留意的事情,他从小喜欢坐公交车,记路线,背站名,现在县城大部分公交线路他都装到脑子里了,他每天都会问我很多问题,比如有个问题他问过好几次,傍晚看见月亮,为什么“我走到哪里,月亮也跟着走。”,我用地理学上的解释他不太理解,所以问了好几次。另外一个问题是他在斜坡上往下走为什么停不下来,我跟他说了些万有引力、重力以及重力加速度的知识,估计讲的也不够通俗,难以理解。他还喜欢数字计算,总是问数字问题,包括超市里商品的价格。小儿子2岁,说话还不太会,大人讲的大部分都能听懂,喜欢到处翻箱倒柜,探索,还喜欢和哥哥做一样的事情。
学校里的学习,根据我个人的经验,小学五年级以前稀里糊涂,也没有特别强烈的学习动机。五年级开始有强烈的学习欲望或者说动机,脑袋瓜似乎也开窍了一点。初中高中一直维持着这种状态。按照这本书里的说法,就是学习需要主动,需要兴趣、意志,还有灵性?这个不太懂。给教师的建议读书笔记(http://www.simayi.net/dushubiji/5440.html)关于兴趣,我并不是对所有学科都感兴趣,比较有兴趣的就是以前所谓的主课语数外,所以物理、化学什么的主要靠意志。因为选了理科,政治历史就基本废弃。到了大学,基本上就没有多少学习动机了,唯一比较上心的就是英语。
工作以后基本上没怎么学习,除了玩电脑,如果玩电脑也算学习的话。中间有几年领导让我兼职计算机基础教学。再就是2013年到2018年给一家北京的小公司翻译计算机方面的英语新闻,很遗憾,公司倒闭了,我的翻译也停止了。现在,我加入大鱼共读,“似乎又开始学习了”。
下面围绕这本书简单说说,第一部分导论主要讲了学习科学的进展情况,这是一个跨学科的领域,能扯上关系的都可以拉进来。
第二部分,学习者与学习。人是学习的主体,作者首先来了一章专家与新手的差异,给我来个下马威。好吧,学习这么多年,教书十几年,我还是停留在新手的阶段。虽然这个世界伪专家很多,但我觉得书里描述的那种专家还是挺多的,有的达到了艺术家水准。学习光有广度,没有深度是不行的,会成为所谓的煎饼人。要成为专家,在专业领域就要达到一英里厚。
学习的目的是什么?我们常常说学以致用,但很多时候感觉学了没啥用,这其实是因为很多知识是惰性的,提取不出来。我们把知识生吞活剥,割裂了他们的联系,没有形成网状结构,因而没法条件性地触发。现在很多人已经意识到,我们的知识分成了各个学科,彼此孤立,也是导致我们无法融会贯通并加以运用的原因。
其实就学习本身而言,大脑是基础,现在的脑科学以及神经科学知识部分地揭开了有关学习的一些秘密,但未知的仍然占大部分。这里要避开两个误区,一是左脑与右脑必须分别培养以获得最大的学习效果,二是人仅仅使用了20%的大脑功能。这部分主要围绕3个方面,一是学习改变大脑的物质结构,好比生成新的大脑硬件,二是学习组织和重组大脑,三是大脑的不同部位适合于不同时段的学习。
本书也吸收了认知心理学、发展心理学的一些重要观点和例证,从心理学角度来阐述人类的学习。在心理上,我的很多学生把自己的学习能力固化了,认为自己只能学到这个程度,只能考这点分,而没有运用成长型思维。当然,这只是一方面,就算他们认同这样的观念,他们的学习动机仍然难以激发,很多学生还是处在被动学习状态,停留在学习的舒适区,畏难心理让他们的成绩难以提升。一些老师批评他们懒惰,但我始终坚持认为懒惰只是表象,这里面有深层次的原因,职业学校学生的学习动机似乎被深深掩埋,难以激活。
第三部分教师与学习当然是针对教师的。陶行知说过教学做一体,教与学之间的关系并不是单向传输那么简单,它们是一个有机地整体。第六章学习环境设计围绕四个方面阐述,包括学习者中心环境,知识中心环境,评价中心环境和共同体中心环境,4个方面也是相互联系甚至部分重叠,有机整合的。最近几年的教育教学改革也是围绕这几个方面进行的,比如学习者中心环境,也就是我们常说的以学生为主体,传统课堂以教师为主体,教师填鸭式满堂灌,少量提问,没有小组讨论或学生之间的合作。当然,教育改革并不是全盘否定传统课堂模式,采用何种教学方式,要看内容而定。全盘否定则教育教学改革根本无法进行。知识中心环境方面,如果仅仅以记忆陈述性知识或者程序性知识为目标,那么很多的教学方法看起来并没有什么区别。
评价是教学中很重要的环节,它包括形成性评价和终结性评价等等。前者是在教学过程中,后者是在某些学习结束后的评价,比如通过作业,考试。评价是反馈信息的来源,教师用来进行反思并调整或者设计下一步的教学,学生用以反思改进自己的学习。共同体中心环境一般指的是班级,当然也可以是学校甚至更大范围,也可以是家庭、行业。这里摘录一段话:比如在一些班级里有一条不成文的规矩,那就是不要出错或不要回答不懂的问题,这个标准使得学生不愿意提问他们不明白的学习材料,阻碍他们探索新问题,建立假设。共同体,按我的粗浅理解,就是一些潜移默化的规则或者文化氛围,它们可能是正面的,也可能是负面的。当然,我们要积极设计和营造积极正面的共同体中心环境。比如,把家庭营造为成功的学习环境,为孩子提供鼓励,指导孩子朝着学校要求的方向转变。假如父母整天刷手机,或者其中一方天天晚上打麻将,我想这些都是非常不利的影响,我大儿子就常常批评我每天看手机时间太长,小儿子不会说话,抓住我的手机往我口袋里塞,让我陪他玩。
再说说看电视的问题。作者的意思是看某些电视对学习也是有帮助的,即使在孩子所看的电视里占的比重很少。儿子最近迷上了俄罗斯动画片螺丝钉,我看了下觉得还不错。当然,还可以看看纪录片。我儿子除了动画片,最喜欢的就是看中央五套体育频道,看到国际性的比赛还认识了很多国家的国旗,我就顺便给他补充点,给个地球仪,给张世界地图,电脑下个地理学习app。当然,王阳明老先生说了,孩子心里要留点空,他一天能做200题算术,你只要给他100题就行了,王阳明的教育理念我觉得比较像西方的释放天性观念:大抵童子之情,乐嬉游而惮拘检,如草木之始萌芽,舒畅之则条达,催挠之则衰萎。今教童子,必使其趋向鼓舞,中心喜悦,则其进自不能已。
第七章主要是一些有效教学的例子,你设计好一堂课,自我感觉不错,但是能达到什么样的效果,上完才知道,也可能考试了才知道,不同的学科其实也对应不同的教育教学知识,很少有老师能做到教全科,或者说会学不一定会教。教育教学知识仍然是教师需要学习的主要知识。刚开始参加工作的时候经常会听到一些话,比如半桶水(学科知识)的老师未必教起来比满通水(学科知识)的老师差。当然,作为某一门课的老师,学科知识也是需要不断积累的。
第八章教师的学习这里不再评论了。
第九章技术支持下的学习。自从有了教师360学时培训以来,已经看了许多技术表演。个人认为,各种技术如果没有有效的与教育教学知识整合,那就是单纯地停留在炫技层面,未必能够改进教学的效果。当然,我并不是反对使用新技术,新技术有很多好处,它们可以使学生的思维可视化,可以把课堂与校外专家联系起来,可以提升课堂练习的评价速度,快速汇集数据,获取反馈,及时调整或者改进教学,也可以在课后让学生看视频复习,或者说前几年疯狂炒作的翻转课堂、微课。这几年似乎也有游戏化教学的尝试,学习真的可以像游戏那样轻松吗?人们设想游戏总是很轻松的,当然也有一些游戏难度匪夷所思。
第十章去年读了《终身幼儿园》这本书,书中提到一个观点,学习是艰难的乐趣,这一点我非常认同。采用游戏化的学习方式“似乎”降低了学习的难度,让学生更有兴趣学习,但学生在具体学习中依然会感到困难,他们的原有概念可能和新知识格格不入。所以,在短暂的游戏形式刺激以后,学生是否仍有动力去学习,这就要看学生的兴趣、意志等多方面因素了。此外,游戏化学习也有造成形式大于内容的风险,让学生误以为这节课主要就是来玩的。总之,游戏化的学习比较适合幼儿园和小学,再往上就不适合用的太多,看具体教学内容而定。
最后总结性的来几句,这本书从题目来看,野心很大,因此你一看到这题目就会向看到了高深的武林秘籍《九阴真经》那样期望很大。当然,这本书里面,干货还是很多的,包括一些我认为是金句的段落。人类要学习什么?怎么学?其实教师群体常常说的是授之以鱼不如授之以渔,教是为了不教。我个人认为,学习不光是学知识,学技能,也是要获得学习的能力,书上说的是自我教育能力,这样才能达成所谓的终身学习。
这本书可惜的几个方面,一是原稿像论文,过于学术,缺乏二次整理,通俗化改编,二是翻译,三个人翻译,这是忌讳,风格不统一,语言像机器翻译,当然,比机器翻译还是要好,总之读起来像卡顿的磁带,这也是因为我的学术阅读能力不行。总之,短短一篇总结也不能全部提取书中精华,或许,哪天想起来,会再翻一翻。作者:飞哥
数据库
2019-04-23 21:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
OSC Markdown Editer 真香,笔记一篇,懒得转中文,如有表述问题,请不吝指出,先谢为敬!
OS: Ubuntu 19.04
When the MySQL server is installed, it automatically started with the OS. It slows the speed of OS loading. But the computer is for personal, not a DB server. I just want to start it when I need.
Check the MySQL server's status and stop it. $ service mysql status $ service mysql stop
Disable autostart: $ sudo systemctl disable mysql
When you need it, it can be started by: $ service mysql start
References: https://askubuntu.com/questions/57381/how-to-stop-mysql-from-running-at-boot-time https://askubuntu.com/questions/833094/how-can-i-disable-autostart-of-mysql-server
数据库
2019-04-23 20:11:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言:提供一个InfluxDB Java API 封装的工具类,方便大家直接上手使用。
1.InfluxDB工具类
先奉上工具类,接下来介绍使用方法。 package com.common.utils.influxdb; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.influxdb.InfluxDB; import org.influxdb.InfluxDB.ConsistencyLevel; import org.influxdb.InfluxDBFactory; import org.influxdb.dto.BatchPoints; import org.influxdb.dto.Point; import org.influxdb.dto.Point.Builder; import org.influxdb.dto.Pong; import org.influxdb.dto.Query; import org.influxdb.dto.QueryResult; /** * InfluxDB数据库连接操作类 * * @author 大脑补丁 */ public class InfluxDBConnection { // 用户名 private String username; // 密码 private String password; // 连接地址 private String openurl; // 数据库 private String database; // 保留策略 private String retentionPolicy; private InfluxDB influxDB; public InfluxDBConnection(String username, String password, String openurl, String database, String retentionPolicy) { this.username = username; this.password = password; this.openurl = openurl; this.database = database; this.retentionPolicy = retentionPolicy == null || retentionPolicy.equals("") ? "autogen" : retentionPolicy; influxDbBuild(); } /** * 创建数据库 * * @param dbName */ @SuppressWarnings("deprecation") public void createDB(String dbName) { influxDB.createDatabase(dbName); } /** * 删除数据库 * * @param dbName */ @SuppressWarnings("deprecation") public void deleteDB(String dbName) { influxDB.deleteDatabase(dbName); } /** * 测试连接是否正常 * * @return true 正常 */ public boolean ping() { boolean isConnected = false; Pong pong; try { pong = influxDB.ping(); if (pong != null) { isConnected = true; } } catch (Exception e) { e.printStackTrace(); } return isConnected; } /** * 连接时序数据库 ,若不存在则创建 * * @return */ public InfluxDB influxDbBuild() { if (influxDB == null) { influxDB = InfluxDBFactory.connect(openurl, username, password); } try { // if (!influxDB.databaseExists(database)) { // influxDB.createDatabase(database); // } } catch (Exception e) { // 该数据库可能设置动态代理,不支持创建数据库 // e.printStackTrace(); } finally { influxDB.setRetentionPolicy(retentionPolicy); } influxDB.setLogLevel(InfluxDB.LogLevel.NONE); return influxDB; } /** * 创建自定义保留策略 * * @param policyName * 策略名 * @param duration * 保存天数 * @param replication * 保存副本数量 * @param isDefault * 是否设为默认保留策略 */ public void createRetentionPolicy(String policyName, String duration, int replication, Boolean isDefault) { String sql = String.format("CREATE RETENTION POLICY \"%s\" ON \"%s\" DURATION %s REPLICATION %s ", policyName, database, duration, replication); if (isDefault) { sql = sql + " DEFAULT"; } this.query(sql); } /** * 创建默认的保留策略 * * @param 策略名:default,保存天数:30天,保存副本数量:1 * 设为默认保留策略 */ public void createDefaultRetentionPolicy() { String command = String.format("CREATE RETENTION POLICY \"%s\" ON \"%s\" DURATION %s REPLICATION %s DEFAULT", "default", database, "30d", 1); this.query(command); } /** * 查询 * * @param command * 查询语句 * @return */ public QueryResult query(String command) { return influxDB.query(new Query(command, database)); } /** * 插入 * * @param measurement * 表 * @param tags * 标签 * @param fields * 字段 */ public void insert(String measurement, Map tags, Map fields, long time, TimeUnit timeUnit) { Builder builder = Point.measurement(measurement); builder.tag(tags); builder.fields(fields); if (0 != time) { builder.time(time, timeUnit); } influxDB.write(database, retentionPolicy, builder.build()); } /** * 批量写入测点 * * @param batchPoints */ public void batchInsert(BatchPoints batchPoints) { influxDB.write(batchPoints); // influxDB.enableGzip(); // influxDB.enableBatch(2000,100,TimeUnit.MILLISECONDS); // influxDB.disableGzip(); // influxDB.disableBatch(); } /** * 批量写入数据 * * @param database * 数据库 * @param retentionPolicy * 保存策略 * @param consistency * 一致性 * @param records * 要保存的数据(调用BatchPoints.lineProtocol()可得到一条record) */ public void batchInsert(final String database, final String retentionPolicy, final ConsistencyLevel consistency, final List records) { influxDB.write(database, retentionPolicy, consistency, records); } /** * 删除 * * @param command * 删除语句 * @return 返回错误信息 */ public String deleteMeasurementData(String command) { QueryResult result = influxDB.query(new Query(command, database)); return result.getError(); } /** * 关闭数据库 */ public void close() { influxDB.close(); } /** * 构建Point * * @param measurement * @param time * @param fields * @return */ public Point pointBuilder(String measurement, long time, Map tags, Map fields) { Point point = Point.measurement(measurement).time(time, TimeUnit.MILLISECONDS).tag(tags).fields(fields).build(); return point; } }

pom依赖的Jar包: org.influxdb influxdb-java 2.10

2.使用工具类查询数据
InfluxDB支持一次查询多个SQL,SQL之间用逗号隔开即可。下面只演示下,只有一条SQL的情况下,怎么解析查询返回的结果集。 public static void main(String[] args) { InfluxDBConnection influxDBConnection = new InfluxDBConnection("admin", "admin", "1.1.1.1", "db-test", "hour"); QueryResult results = influxDBConnection .query("SELECT * FROM measurement where name = '大脑补丁' order by time desc limit 1000"); //results.getResults()是同时查询多条SQL语句的返回值,此处我们只有一条SQL,所以只取第一个结果集即可。 Result oneResult = results.getResults().get(0); if (oneResult.getSeries() != null) { List> valueList = oneResult.getSeries().stream().map(Series::getValues) .collect(Collectors.toList()).get(0); if (valueList != null && valueList.size() > 0) { for (List value : valueList) { Map map = new HashMap(); // 数据库中字段1取值 String field1 = value.get(0) == null ? null : value.get(0).toString(); // 数据库中字段2取值 String field2 = value.get(1) == null ? null : value.get(1).toString(); // TODO 用取出的字段做你自己的业务逻辑…… } } } }
取数据的时候,注意空值判断,本例将返回数据先进行判空oneResult.getSeries() != null,然后调用oneResult.getSeries().getValues().get(0)获取到第一条SQL的返回结果集,然后遍历valueList,取出每条记录中的目标字段值。
InfluxDB封装的结果集有点深,主要是由于支持多条SQL一次性查询,可以提高查询速度,这个地方有别于关系型数据库的使用。
3.使用InfluxDB工具类,插入单条数据
InfluxDB的字段类型,由第一条插入的值得类型决定;tags的类型只能是String型,可以作为索引,提高检索速度。 public static void main(String[] args) { InfluxDBConnection influxDBConnection = new InfluxDBConnection("admin", "admin", "1.1.1.1", "db-test", "hour"); Map tags = new HashMap(); tags.put("tag1", "标签值"); Map fields = new HashMap(); fields.put("field1", "String类型"); // 数值型,InfluxDB的字段类型,由第一天插入的值得类型决定 fields.put("field2", 3.141592657); // 时间使用毫秒为单位 influxDBConnection.insert("表名", tags, fields, System.currentTimeMillis(), TimeUnit.MILLISECONDS); }

4.使用InfluxDB工具类,批量写入数据的两种方式
注:使用这两种种方式,要就这两条数据都写入到同一数据库下且tag相同,若tag不相同,需将它们放到不同的BatchPoint对象中,否则会出现数据写入错乱问题。
方式一:通过BatchPoints组装数据后,循环插入数据库。 public static void main(String[] args) { InfluxDBConnection influxDBConnection = new InfluxDBConnection("admin", "admin", "1.1.1.1", "db-test", "hour"); Map tags = new HashMap(); tags.put("tag1", "标签值"); Map fields1 = new HashMap(); fields1.put("field1", "abc"); // 数值型,InfluxDB的字段类型,由第一天插入的值得类型决定 fields1.put("field2", 123456); Map fields2 = new HashMap(); fields2.put("field1", "String类型"); fields2.put("field2", 3.141592657); // 一条记录值 Point point1 = influxDBConnection.pointBuilder("表名", System.currentTimeMillis(), tags, fields1); Point point2 = influxDBConnection.pointBuilder("表名", System.currentTimeMillis(), tags, fields2); // 将两条记录添加到batchPoints中 BatchPoints batchPoints1 = BatchPoints.database("db-test").tag("tag1", "标签值1").retentionPolicy("hour") .consistency(ConsistencyLevel.ALL).build(); BatchPoints batchPoints2 = BatchPoints.database("db-test").tag("tag2", "标签值2").retentionPolicy("hour") .consistency(ConsistencyLevel.ALL).build(); batchPoints1.point(point1); batchPoints2.point(point2); // 将两条数据批量插入到数据库中 influxDBConnection.batchInsert(batchPoints1); influxDBConnection.batchInsert(batchPoints2); }
方式二:通过BatchPoints组装数据,序列化后,一次性插入数据库。 public static void main(String[] args) { InfluxDBConnection influxDBConnection = new InfluxDBConnection("admin", "admin", "1.1.1.1", "db-test", "hour"); Map tags1 = new HashMap(); tags1.put("tag1", "标签值"); Map tags2 = new HashMap(); tags2.put("tag2", "标签值"); Map fields1 = new HashMap(); fields1.put("field1", "abc"); // 数值型,InfluxDB的字段类型,由第一天插入的值得类型决定 fields1.put("field2", 123456); Map fields2 = new HashMap(); fields2.put("field1", "String类型"); fields2.put("field2", 3.141592657); // 一条记录值 Point point1 = influxDBConnection.pointBuilder("表名", System.currentTimeMillis(), tags1, fields1); Point point2 = influxDBConnection.pointBuilder("表名", System.currentTimeMillis(), tags2, fields2); BatchPoints batchPoints1 = BatchPoints.database("db-test").tag("tag1", "标签值1") .retentionPolicy("hour").consistency(ConsistencyLevel.ALL).build(); // 将两条记录添加到batchPoints中 batchPoints1.point(point1); BatchPoints batchPoints2 = BatchPoints.database("db-test").tag("tag2", "标签值2") .retentionPolicy("hour").consistency(ConsistencyLevel.ALL).build(); // 将两条记录添加到batchPoints中 batchPoints2.point(point2); // 将不同的batchPoints序列化后,一次性写入数据库,提高写入速度 List records = new ArrayList(); records.add(batchPoints1.lineProtocol()); records.add(batchPoints2.lineProtocol()); // 将两条数据批量插入到数据库中 influxDBConnection.batchInsert("db-test", "hour", ConsistencyLevel.ALL, records); }
推荐使用第二种方式,属于一个数据库的数据,可以一次性批量写入,写入速度最快。
注:Influx在Java中的读取和写入,还有批量写入数据的两种方式,今天都给大家介绍了,希望本工具类和案例可以给你带来帮助。
数据库
2019-04-23 17:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
建表 CREATE TABLE `user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `sex` tinyint(1) DEFAULT NULL, `age` tinyint(2) DEFAULT NULL, PRIMARY KEY (`id`), KEY `Index_user` (`name`,`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
测试sql
第一种 mysql> explain SELECT * FROM `user` where name="tom" \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user partitions: NULL type: ref possible_keys: Index_user key: Index_user key_len: 43 ref: const rows: 1 filtered: 100.00 Extra: NULL
第二种 mysql> explain SELECT * FROM `user` where age=18 and name="tom" \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user partitions: NULL type: ref possible_keys: Index_user key: Index_user key_len: 45 ref: const,const rows: 1 filtered: 100.00 Extra: NULL
第三种 mysql> explain SELECT * FROM `user` where age=18 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 filtered: 33.33 Extra: Using where 1 row in set, 1 warning (0.00 sec)
第四种 mysql> explain SELECT * FROM `user` where name="tom" and age=18 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user partitions: NULL type: ref possible_keys: Index_user key: Index_user key_len: 45 ref: const,const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
总结
由此可见,只有sql中where包含联合索引的首个字段的查询才能命中索引,这个叫索引的最左匹配特性。 联合索引的使用在写where条件的顺序无关,mysql查询分析会进行优化而使用索引。但是减轻查询分析器的压力,最好和索引的从左到右的顺序一致。
b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+树是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道第一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。
欢迎扫描下方二维码,持续关注:
互联网工程师(id:phpstcn),我们一起学习,一起进步
数据库
2019-04-23 16:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问题:SQL查询慢怎么办?
优化手段,加索引。
索引是 帮助MYSQL高效的获取数据的 排好序 的 数据结构。
问题:索引结构为什么使用Btree而不使用二叉树,红黑树或者HASH结构?
二叉树的特性,右边子节点的值大于父节点,且左边子节点的值小于父节点
二叉树:使用二叉树来做索引的数据结构,当索引值递增的时候,二叉树也会不断地增加右子节点 ,那么在查找索引的时候,所做的IO操作,等同于没有索引逐行查找数据的IO操作。
红黑树:平衡二叉树。随着数据量增大,树的高度也会变高。2的N次方=数量量(假设100万),那么N至少也是50,那么树的高度也是50,如果数据在子叶节点上,那么至少要经过50次的IO操作,才能找到数据。
HASH:散列表结构,key不适合排序,而且数据是散列的分布的,不利于IO读写。
BTree:一个节点上,横向扩展。即一个节点上,可以存储多个元素(Degree),且每个元素,都可以有子节点,叫做多叉树。 度(Degree)-节点的数据存储个数 叶节点具有相同深度 叶节点的指针为空 节点中的数据KEY从左至右递增排列
以5阶B树举例
B+Tree(Btree变种) 非叶子节点不存储data,只存储key,可以增大度(Degree)-节点的数据存储个数 叶子节点不存储指针 叶子节点增加了顺序访问指针,提高区间访问的性能
以5阶B+树举例
问题:Btree和B+Tree的区别? Btree每个节点,都包含了key和data,而B+tree只有叶子节点保存了key和data,其他节点,只保存了key,方便增大度的个数 B+tree的叶子节点之间, 增加了指针,提高了查询区间的性能,不需要像Btree一样每次都从根节点开始查找 Btree中,父节点和叶子节点,关键字的字段值不会冗余,而B+tree会冗余的存储关键的索引值,即父节点和叶子结点都存在相同的关键值
MyISAM索引实现(非聚集)
MyISAM索引文件和数据文件是分离的。它是针对建表的,而不是针对数据库的,既同一个数据库可以多种类型的表,myISAM和innoDB。
在mysql的文件件中,会发现一个表有三个文件: test_table_myisam.frm // 存储表的结构,表的定义 test_table_myisam.MYD // 存储表的数据 test_table_myisam.MYI // 存储表的索引文件
myisam存储引擎是非聚集索引,所以在MYI的文件中,以B+tree的数据结构存储了表的索引信息,而索引内容中的data,是指向数据的在MYD文件中的地址。
在myisam存储引擎中,主键索引使用了B+tree,而辅助索引,非主键索引,也同样根据字段的值,使用了B+tree结构来存储。
innoDB索引实现(聚集) 表数据文件本身就是按B+Tree组织的一个索引结构文件
聚集索引 定义:叶节点包含了完整的数据记录 为什么innoDB表必须有主键,并且推荐使用整形的自增主键? 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
在mysql的文件件中,会发现一个表有两个文件: test_table_myisam.frm // 存储表的结构,表的定义 test_table_myisam.ibd // 存储表的索引+数据
在innoDB存储引擎中,辅助索引、非主键索引都不是聚集索引。
问题:为什么B+tree比Btree快?
树的高度h,影响了查找效率,因为从根节点开始,每次查找下一个节点,都可能需要一次IO读写。所以增加非叶子节点的度,可以有效提高查询效率。而CPU从磁盘上读取数据到内存中,最小单元是页,一页读取的最小数据是4KB,而CPU一次IO,读取的数据是一页的整数倍,且最大值也有限制。而mysql在底层,设置的大小是16KB。所以B+TREE要把数据放在叶子节点,所以非叶子几点,就可以增加更多的节点数。而Btree节点和数据是保存在一起的,所以非叶节点的节点数,要比B+tree少,树的高度就比B+tree高。
问题:为什么MYSQL页文件默认的配置是16KB?
我们假设一行的数据是1K,那我们一页的数据,就能存储16行数据,也就是一个叶节点可以存储16条记录;再来看非叶节点,假设ID是bigint类型,那么长度为8B,指针大小在InnoDB源码中为64(6B),一共就是14B,那么一页里面就可以存储16K/14=1170个(主键+指针)
那么一颗高度为2的B+树能存储的数据为:1170*16=18720条,一颗高度为3的B+树可以存储1170*1170*16=21902400(千万条)
原因就是16KB就足够应对千万条表记录了。
问题:为什么innoDB表必须有主键,并且推荐使用 整形的自增 主键?
使用innoBD存储引擎时,要建立主键索引,如果我们没有主动创建索引,那么innoDB会自动帮我们建立主键索引。innoDB会在我们创建的表里找一列唯一的,可以代表主键的字段来创建主键索引,如果没有这样的字段,innoDB会默认加一列字段,来创建主键索引。有点类似oracle中的rowId。为什么要推荐整形而且自增的字段当做主键?为什么建议使用整形类型,因为如果使用uuid作为主键索引,uuid类型比整数类型大,为了方便IO操作一次读取4个页的数据(mysql默认16KB,一页是4KB),意味索引的非叶子节点上的关键字要比使用整数类型的关键字数量小,那么B+tree的高度就可能增加,那么读取叶子节点上的数据的IO操作次数就增加了。那么查找的效率就低了。为什么要自增,因为当新纪录插入时,主键自增的话,在B+tree中插入节点比较方便,而使用uuid为主键,新纪录的uuid不一定比前面的uuid大,可能会将新纪录插在靠前面的叶子节点中,叶子节点满了,会取中间的关键字向上存放到父节点中,可能会造成树的分裂,微观上性能就比自增的差。
数据库
2019-04-23 16:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
搭建环境 虚拟机:VMware 10.0.2 Linux系统:CentOS-6.5 SSH客户端:SecureCRT 7.3,SecureFX 7.3
Redis安装自行百度
数据库
2019-04-17 22:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Redis的由来: 它其实是用 C 语言开发的一个高性能的键值对的数据库
Redis概述:
高性能键值对数据库,支持的键值数据类型: 字符串类型 列表类型 有序集合类型 散列类型 集合类型
Redis的应用场景 缓存 任务队列(秒杀、抢购) 网站访问统计 数据过期处理 应用排行榜 分布式集群架构中的session分离












数据库
2019-04-17 22:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Redis入门(上)
学习一个东西之前我们需要知道什么是这个东西,为什么需要这个东西
第一章 NoSQL概述:
什么是NoSQL? NoSQL = Not Only SQL 意思是不仅仅是使用SQL 非关系型的数据库
为什么需要NoSQL? High performance - 高并发读写 关系型数据库对上万次读勉强可以应对,对上万次写则有些力不从心了 Huge Storage - 海量数据的高效率存储和访问 High Scalability && High Availability- 高可扩展性和高可用性
主流的NoSQL的产品有:CouchDB,Redis,MongoDB,riak...
NoSQL 数据库的四大分类: 键值(Key-Value)存储 优势是快速查询,劣势是缺少结构化 列存储 优势是查找速度快,劣势是功能比较局限 文档数据库 对应的产品是MongoDB,优势是数据结构要求不是特别严格,劣势是查询性能不是特别的高,缺少统一的语法 图形数据库 相关的产品有inforgread,典型的应用是社交网络上面,优势是图结构相关的算法,劣势是需要对整个图进行计算才能得出结果,不容易做分布式的集群方案
四类NoSQL数据库比较:
NoSQL的特点: 易扩展 灵活的数据模型 大数据量,高性能 高可用














数据库
2019-04-17 22:02:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.登陆到 oracle
su - oracle
2.启动 监听
$ lsnrctl start
3. 启动数据库
$ sql plus /nolog
sql>conn / as sysdba
sql> startup
4.解锁hr用户
sql> alter user hr identified by hr account unlock ;
5.登陆到 hr用户
sql>conn hr/hr
6.查看有哪些表
sql>select table_name from user_tables;
7.查看表结构
sql> desc departments
8.看具体内容
sql >select * from departments;
数据库
2019-04-17 17:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>> “如果大家当时能看见原来十年后OceanBase能长成这样,可能十年前OceanBase得到的支持会好很多。但是这种如果是不存在的,很多时候你要先证明自己。”
根据工信部数据显示,1998年,中国软件企业5000家,市场规模325亿;到了2018年底,中国软件企业3.78万家,收入规模超过6.3万亿元,营收增长了193.8倍。可在最核心的基础设施三大件芯片、操作系统和数据库上,过去我们并未取得商用意义上的重大突破。
不过,相比芯片和操作系统,国内数据库领域的局面要略微乐观一些。除了传统的数据库厂商、数据服务商,互联网巨头、云计算厂商、硬件厂商、新兴的创业公司也越来越多地投入到数据库的研发中。而谈及国产自研数据库,就不得不提OceanBase。OceanBase是完全由阿里巴巴和蚂蚁金服自主研发、全球首个应用于金融核心业务的分布式关系数据库。OceanBase的研发始于2010年6月,因为选择从零开始,研发之路从一开始就磨难重重,中途因为找不到愿意使用的业务,团队曾经濒临解散。
最终OceanBase还是跨越了死亡之谷,在蚂蚁金服实现了全面替代Oracle,成功支撑了过去5年“双11”蚂蚁金服全部核心业务的重压,创造了25.6万笔/秒支付峰值和4200万笔/秒请求数处理峰值这一业内全新的纪录。自2017年开始,OceanBase开始走向外部商用,目前已经在数十家商业银行落地,其中包括南京银行、浙商银行、苏州银行、人保健康险等。OceanBase帮助南京银行共同打造“鑫云+”互金开放平台,实现贷款交易处理能力10倍提升,轻资产模式显著降低成本,从原有的30~50元/账户降低到上线后的4元/账户。日处理百万笔放款,平均处理时间小于1 秒,让老百姓借钱更方便,真正实现了普惠金融。
站在现在这个时间点上顾盼今昔,蚂蚁金服高级研究员、OceanBase创始人阳振坤认为,OceanBase的成功其实有行业和时代的必然性。
这是最坏的时代,也是最好的时代
2009年开始,大量新的非关系型数据库如雨后春笋般涌出,在整个数据库行业掀起了一场空前盛大的NoSQL革命。这时候的关系数据库早已过了而立之年,在此期间虽然曾短暂爆发过一些所谓终结关系数据库的革命,但丝毫没有动摇到关系数据库的主导地位。
但这一次似乎与以往不同,火热发展的云计算带来了对更大规模数据库的需求,而关系数据库的缺点则相应地被越来越多人诟病:不能够扩展、容量小、处理能力不够、成本又非常高。在当时的很多人看来,关系数据库的末日是真的要来了。
那时阳振坤已经做了两年多的自研分布式系统,他十分看好云计算系统的发展机会。同一年,阳振坤加入阿里巴巴,开始了分布式关系数据库OceanBase的研发。
数据库从诞生起已经有几十年的时间了,但基本上它的市场格局就没有多少变化,最早起来的几家厂商今天还是占据着统治地位。因为数据库非常难被替换,它处在整个产品或者产业链最底层的位置,替换风险很大,但收益相比起来却小得多。这也是为什么像IBM、微软这样的后来者也无法取代Oracle。这就导致了数据库变成了一个门槛极高、强者恒强的领域,后来者很难居上。前有Oracle挡道、后有NoSQL数据库追赶,在大部分人看来,那时候怎么也不会是自研关系数据库的好时机,但阳振坤却不这么想。
加入阿里之后,阳振坤发现无论对淘宝还是支付宝,关系数据库都扮演着十分关键的角色,在使用上根本不可能摆脱。但已有的数据库,无论是商业数据库还是开源数据库,都有非常多的局限,远远无法满足如淘宝、支付宝这样的互联网和金融业务对高扩展、高并发、高可用和低成本的需求。单机数据库已经走到了尽头,下一步只能走向分布式,而分布式恰好是阳振坤所擅长的。如果能将分布式技术揉到数据库里面,解决单机数据库存在的各种问题,对当时整个互联网的基础设施都会是一个巨大的帮助和进步。阳振坤认为他们赶上了一个“天时地利人和”的好机会。
“天时”指的是互联网的爆发式增长对数据库的高并发、大数据量提出了很大的需求,有了需求去推动就会容易得多;“地利”指的是阿里内部从淘宝到蚂蚁金服拥有大量需要使用数据库的场景,OceanBase可以从不是特别重要的应用场景开始尝试,一步步地将数据库做成关键系统;“人和”指的是当时单机数据库已经走到了尽头,下一步一定是走向分布式,而当时团队成员大多是研究分布式出身,做的就是自己最擅长的工作。用阳振坤的原话就是:“这是千载难逢的机会,我们一定要做,而且一定能做成。”
一个不断“破格”的人
“一个不断破格的人”,这是早前某次采访中记者对阳振坤的评价。1984年阳振坤考入北京大学数学系,硕士师从本系的张恭庆院士,后又转向计算机领域,博士师从计算机系的王选院士。需要强调的是,他修完大学课程只用了3年,硕士只用了一年多,成为王选院士博士生的时候他只有24岁。1995年其所在团队研究成果获国家科技进步一等奖(排名第四),1997年也就是他32岁那年被破格晋升为教授。
在他人或许都安于现状之时,他却毅然选择了离校。个中原因也不复杂,他的工作更偏于工程,而在工业界有更多的机会,也能发挥更大的作用。2002年离开北大/方正的时候,阳振坤内心很清楚自己必须要做点不一样的事情。他先是加入联想研究院担任首席研究员,负责无线通信领域的研究;后来接触到分布式系统并看好其前景,在微软亚洲研究院、百度所从事的工作都属于分布式这个范畴,前者侧重研究,后者偏重工程实践。
回想在北大的那些年,阳振坤觉得特别感激的是,学数学让他有了一个很好的数学基础,后来转到计算机系以后,碰到了王选老师,又打下了一个比较牢靠的计算机基础,这才有了他后来的今天。作为对阳振坤影响最大的人,恩师王选有两点让他至今受益:一是如何判断一件事情是否有价值,二是“顶天立地”的技术理念,“顶天”就是技术上要不断追求新突破,“立地”就是要把技术做成通用产品,让整个社会都能普遍使用。
其实2010年去淘宝的时候,阳振坤根本不知道自己会做什么事情。加入淘宝之后,摆在他面前的有两个选择,一个是加入正在快速发展的淘宝业务团队,去主管技术,这是一条已经能看到很大的发展机会、相对轻松的道路;另一条是阳振坤后来自己选的,从头组建团队做一个技术平台,也就是今天我们看到的OceanBase数据库。从加入淘宝到选择做自研数据库,一共只花了两个星期的时间。
这不是一个容易的选择,但阳振坤相信自己的判断:“2010年选这个项目的时候,我是觉得这件事情需要做。当时互联网迅速发展带来了对大数据量、高并发的需求,大家对传统单机数据库有很大的抱怨,觉得它既没有扩展能力,又没有高并发的能力,成本还非常高,但是互联网根本就离不开关系数据库。这件事情怎么看都是一件应该要做、需要做的事情。”阳振坤没有说出来的是,这件事到底有多难。
那时候阿里巴巴刚开始要“去IOE”,几乎没人想着说要自己从头做一个数据库。传统关系数据库都是通过外部硬件来保证可用性,用便宜的PC机替换高端服务器之后,硬件更容易出故障了,如何保证数据库高可用?高可用和数据一致性如何同时保证?分布式系统怎么同时实现CAP的要求?几十年来这么多做数据库的厂商,国内国外基本没有人成功过。而且从公司的业务发展的角度,也不可能等你几年把数据库做出来,再去发展业务,更可行的做法是基于开源做出一些东西,让业务先往前走。因此OceanBase立项之初,除了阳振坤和他当时的直属领导,其他人对这个项目要么不关心,要么不赞成。从零开始自研分布式关系数据库并全面替换Oracle,在当时有多少人会相信这真的能做成呢?当时整个淘宝一共只有两三千人,而Oracle有十几万人,就算整个淘宝的人全部去做数据库,跟Oracle比起来也只是很小很小的一个比例。
在阳振坤看来,如果一件事情几乎所有的人都认为它很重要、需要做,这件事情就已经不是创新了。当所有人都认为这件事情要做的时候,其实做这件事情的时机已经过去了一大半。作为最底层的基础软件设施,数据库需要很长时间的积累,不可能今年做,明年就能真正大规模地用起来。 虽然在2010年选择做数据库的时候,没有太多人看重和支持,对于团队来说这可能反而是一件好事。无人关注,反倒给了团队几年积累发展的时间。
阳振坤不只要自研,还要把OceanBase定位成恩师王选所说的“顶天立地”的技术产品——走标准化的路,做一个通用的关系数据库产品,而不是一个仅仅在公司内部使用的产品。每个公司使用任何产品其实都只用了其中很小的一部分功能,如果只做满足公司自用需求的数据库,可能只需要投入十分之一、五分之一的人力物力时间。而要做成通用产品就意味着必须实现所有功能,这要困难得多,团队的投入、花费的精力和时间也要大好多倍。但也因为阳振坤最初的坚持,今天的OceanBase才得以走出蚂蚁金服,走进众多银行系统。不过这都是后话了。
做数据库就像在黑暗中前行,守得住寂寞、担得了压力,甚至要有近乎偏执的性格才可能跨越死亡之谷,到达最终目的地。阳振坤团队中一位新人曾经向他表达过自己的困惑,当时这位新人入职三个月了,因为有太多东西要学,什么也没做出来,而跟他同时入职天猫的新员工才来了一个月,做的系统就已经在线上使用了。阳振坤当时给新人讲了一个故事,他说:“你过三年再看,没有人还记得那个同学三年前在天猫上把网页做了什么改版,可是三年以后你今天做的东西还会在生产系统中使用。”
十年蛰伏,一飞冲天
OceanBase的第一个客户来自淘宝收藏夹。当时的淘宝收藏夹正处于业务高速发展期,数据库的访问量飞快增长,面临着第二年服务器数量需要翻一倍甚至几倍的局面。业务方忙于寻找解决方案的时候,阳振坤主动找上门去提出了可以用OceanBase帮他们解决问题,把服务器数量降低一个数量级。四个月出Demo,八个月出试用版,一年后系统正式上线,淘宝收藏夹就这样成了第一个吃OceanBase螃蟹的业务,新数据库取得了非常好的效果。这时候是2011年,收藏夹项目成为了OceanBase第一个小小的里程碑。
但在后续一年多的时间里,OceanBase团队一直在寻找更多业务,也确实有一些业务用了,却再也没有找到像淘宝收藏夹效果这么显著的业务。做数据库难度大、周期长,前几年的投入也许有那么一点点产出,但其实跟投入比几乎微不足道,团队面临的压力可想而知。数据库少不了人力投入,OceanBase团队从最早只有阳振坤一个人,后来发展到2012年已经有30多个人了。占了这么多人头,但在公司里却没有足够多、足够重要的业务,没能产生足够大的价值和效益。团队陷入了一个比较困难的时期,甚至数度濒临解散。
当被问及“中间有没有想过这事如果没做成,怎么办?”,阳振坤回答得云淡风轻:“不是每件事都能做成,那太难了。如果每件事在做之前都想着它能不能做成,那最后做成的事就会很少。”
在最困难也最危险的时候,团队迎来了一丝转机。2012年底,公司把OceanBase整个团队调到了支付宝。支付宝属于金融领域,面临的数据库挑战会比其他业务更大,这相当于给了OceanBase团队一次从头开始的机会。
2013年夏天,支付宝也开始启动“去IOE”,并希望能够把Oracle数据库替换掉。阳振坤又一次主动出击,向当时的主管、也是现在蚂蚁金服的CTO程立自荐了OceanBase的解决方案。
金融行业数据库,最怕的就是突发故障导致数据丢失,涉及到钱的事,多了少了都是不可接受的。为了解决高可用与主备库数据一致的矛盾,OceanBase将可用性做到了数据库系统内部,用一主两备或一主多备代替一主一备。主库到备库同步的时候不要求同步到每个备库,而是同步到包括主库在内的多数库(超过半数),也就是说总共三个库中如果有两个成功了,这个事务就成功了。如果任何一台机器出了问题,这个系统的可用性和数据一致性都是可以保证的。
程立认可了阳振坤提出的方案,OceanBase团队开始埋头开发,第一个要攻克的目标是支付宝交易库。2014年双11,OceanBase迎来了第一次大考。
大促开始前的凌晨,各个团队都在自己的作战室里热火朝天地准备。当时任蚂蚁金服董事长的彭蕾去了OceanBase团队的作战室,问大家:“有没有信心?”阳振坤跟彭蕾开了个玩笑说:“你看我们窗子都已经打开了,如果等会出问题,我们就准备从这跳下去。”
在一开始的计划里,双11交易流量的1%会切给OceanBase,但因为当时的Oracle数据库系统支撑不了汹涌而来的巨大流量,最后OceanBase成功支撑了2014年双11中10%的交易流量。经过了双11的考验之后,OceanBase得到了更多的认可和支持。后来OceanBase团队获得了2015年蚂蚁金服的CEO大奖,这也是第一次由技术团队拿到这个奖。彭蕾希望借这个奖鼓励那些能够沉下心来、扎扎实实地把一项技术做好做扎实的技术人们。
OceanBase团队获得2015年蚂蚁金服CEO大奖
2015年春夏,支付宝交易库和支付库都换成了OceanBase;2016年,支付宝账务系统上线,这也标记着OceanBase真正在金融系统最核心最关键的领域站住了脚。
从2017年开始,OceanBase开始走出支付宝、走出蚂蚁金服,在商业银行推广使用,最早的两家客户是浙商银行和南京银行。仅仅用了两年多的时间,OceanBase已经在人保健康险、常熟农商行、苏州银行、广东农信等数十家商业银行和保险机构上线。
2017年10月,南京银行“鑫云+”互金开放平台正式发布,这是阿里云、蚂蚁金服合作整体输出的第一次尝试,通过“鑫云”+平台的建设,南京银行互金核心系统在交易处理能力、成本控制和对接效率都得到了极大的提升。
南京银行传统的线下消费金融业务开展10年,余额100亿,而与互联网平台合作开展线上业务仅一年时间业务量已达到100亿。南京银行“鑫云+”平台上线后,业务快速增长,贷款交易处理能力全面升级,从原有的10万笔/天到上线后实现100万笔/天,对普惠金融起到了更有利的支撑。轻资产模式使得单账户管理成本约为传统IOE架构的1/5至1/10,从原有的30~50元/账户降到了上线后的4元/账户。“鑫云+”平台的维护人员较传统银行业务系统约为1/5左右。以往合作时银行需要分别与各个互联网平台进行对接,自项目上线后,只需对接鑫云+一家平台即可实现多家互联网平台的对接,大大减少了重复建设,提高对接效率,同时也降低了中小银行以及互联网平台的对接成本。
从濒临解散到浴火重生,OceanBase已经走了快十年,但在自研关系数据库这条漫漫长路上,OceanBase才仅仅走出了一小步。在阳振坤看来,OceanBase现在“开了很大的一朵花,但是结了很小的一个果”,虽然它已经向所有人证明了通用的分布式关系数据库是能够做成的,而且能真正应用在生产系统中,但今天OceanBase的应用还很有限,远远没有充分发挥它的价值。
阳振坤告诉我们,OceanBase当初没有选择基于开源或已有的技术思路开发,而是选择走分布式自研这条路,虽然走得艰难,但做成之后就会成为不可替代的优势。过去这十来年正好是分布式系统发展的十来年,转型到分布式已经成为所有人都认可的一个选择。如今,以蚂蚁金服的OceanBase为代表的分布式关系数据库,不仅解决了关系数据库的扩展性问题,也极大地降低了关系数据库的成本,还提升了可用性。
现在,兼容Oracle的工作是OceanBase的重中之重。OceanBase团队的目标是,用两年时间做到Oracle业务的平滑迁移,不需要修改一行代码、不需要业务做任何调整就能够将数据库迁移过来。
在阳振坤看来,能够把最早的一些想法一些创新变成产品,真的是非常艰难甚至说过程中充满痛苦的一条道路。但是OceanBase做的所有事情其实还是从业务、从客户中出发,只有技术真的能够落到生产中去,落到用户中去才是真正有价值的,否则做得再好也只是一个空中楼阁。
相信未来,OceanBase还会走得更快、更远。
作者:华蒙
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-04-17 12:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果where后面有or,这样不会命中索引如: select * from student where userName='小明' or age=19;
如果想要or后面的也走索引的话,应该是创建两个单列索引 like是以%开头的不会命中索引如: select * from student where userName like %明 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引如: select * from student where userName='小明' and age=19 and phone=1887821; 没有查询条件,或者查询条件没有建立索引 查询条件中,在索引列上使用函数(+/-*/) select * from student where userName='小明' and age-1=19 错误 select * from student where userName='小明' and age=20 正确 采用 not in, not exist B-tree 索引 is null 不会走, is not null 会走 负向条件查询不能使用索引。 select * from order where status != 0; 数据区分度不大的字段不宜使用索引 例如:性别,状态等。
数据库
2019-04-20 17:26:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
使用data pump 驱动的外部表移动数据
比如我们有一个报表的数据,准备从一个数据库A中移动到另一个数据库B中,如何实现?
这个问题,我们使用带data pump驱动的外部表方式,很容易实现,具体方法如下:
1.在A库中,使用data pump驱动创建一个外部表,把报表的数据全部卸载到指定数据文件中:
1.1 创建一个目录
create directory exp_dir as '/home/ oracle /';
grant read,write on directory exp_dir to hr;
1.2 把报表数据通过外部表方式卸载到exp_dir目录下面的两个文件下面:
create table extab_emp_dp
(employee_id number(4),
last_name varchar2(30),
hire_date date,
salary number)
organization external
(type oracle_datapump
default directory exp_dir
location ('empdp.exp','empdp1.exp')
)
parallel
as
select employee_id,last_name,hire_date,salary from hr.employees;
1.3 查看/home/oracle/ 下,应该产生两个二精致文件:
ls /home/oracle/empdp*.exp
2.把 /home/oracle/empdp.exp,/home/oracle/empdp1.exp拷贝到B 库中
3.在B库上通过外部表方式,装载数据
3.1 创建一个目录
create directory exp_dir as '/home/oracle/';
grant read,write on directory exp_dir to hr;
3.2 装载数据
create table extab_emp_dp
(employee_id number(4),
last_name varchar2(30),
hire_date date,
salary number)
organization external
(type oracle _data pump
default directory exp_dir
location ('empdp.exp','empdp1.exp')
)
parallel
3.3 查看数据
select * from extab_emp_dp;
数据库
2019-04-20 17:17:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
主从配置
change master to master_host='192.168.1.132’,master_port=3306,master_user=‘slave’,master_password=‘a123456’,master_log_file='mysql-bin.000001',master_log_pos=1;
赞助我
限于个人经验、知识面和技术栈体系的限制,目前的帖子尚不完美,但至少,我在努力,我会争取把所有的博客做成体系化的课程,方便初学者使用,缩短与成熟文库系统的差距。
所以,给出你的意见和建议,给出你的打赏与鼓励,让我知道,我不是一个人在孤独地战斗。 赞助的时候,麻烦备注一下你的称呼,以便公开并表示感谢
数据库
2019-04-19 22:31:04
「深度学习福利」大神带你进阶工程师,立即查看>>>
#!/bin/bash

fullDate=`date +"%F_%H.%M.%S"`
backdir=/root/backup

mysqldump smartcampus -u root -pWanda-2009 --hex-blob --add-drop-table > ${backdir}/backup${fullDate}.sql;
cd /root/backup
tar zcvf backup${fullDate}.tar.gz backup${fullDate}.sql --remove-files;

# 等到时间到了再过来试试看 删除
find ${backdir?no path} -mtime +2 -name "backup*.tar.gz" -exec rm -rf {} \;


mysqldump donut -u root -pa123456 --hex-blob --add-drop-table > donut.sql;
cd /root/backup
赞助我
限于个人经验、知识面和技术栈体系的限制,目前的帖子尚不完美,但至少,我在努力,我会争取把所有的博客做成体系化的课程,方便初学者使用,缩短与成熟文库系统的差距。
所以,给出你的意见和建议,给出你的打赏与鼓励,让我知道,我不是一个人在孤独地战斗。 赞助的时候,麻烦备注一下你的称呼,以便公开并表示感谢
数据库
2019-04-19 22:28:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
SELECT (sum(DATA_LENGTH)+sum(INDEX_LENGTH))/1048576 FROM information_schema.TABLES where TABLE_SCHEMA='infantedu';
赞助我
限于个人经验、知识面和技术栈体系的限制,目前的帖子尚不完美,但至少,我在努力,我会争取把所有的博客做成体系化的课程,方便初学者使用,缩短与成熟文库系统的差距。
所以,给出你的意见和建议,给出你的打赏与鼓励,让我知道,我不是一个人在孤独地战斗。 赞助的时候,麻烦备注一下你的称呼,以便公开并表示感谢
数据库
2019-04-19 22:27:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
嵌套表 使用 集合操作符 -单列或多列均可
嵌套表 使用multiset union,multiset except,multiset intersect等使用集合操作符,往往一般都只是对一列进行比较,但是多列实际上也是可以的:
以下介绍中,以multiset except 操作符 为例。
一、只有一列情况下使用集合操作符
由以下1,),2),3),4)说明只有一列的情况下使用集合操作符时既可以直接赋值也可以通过sql语句的形式赋值,但是 2)编译出错,原因是 必须将变量类型定义到schma级别,即要么像3)和4)那样直接在外部定义了一个变量,或者直接在包中的包头进行定义
1)执行成功
DECLARE
TYPE typ_id_table1 IS TABLE OF NUMBER;
tab_fids1 typ_id_table1 := typ_id_table1;
tab_fids2 typ_id_table1 := typ_id_table1;
v_result typ_id_table1;
BEGIN
v_result := tab_fids1 MULTISET except tab_fids2;
END ;
2)执行提示:在SQL语句中不允许使用本地收集类型
DECLARE
TYPE typ_id_table1 IS TABLE OF NUMBER;
tab_fids1 typ_id_table1 := typ_id_table1;
tab_fids2 typ_id_table1 := typ_id_table1;
v_result typ_id_table1;
BEGIN
SELECT tab_fids1 MULTISET EXCEPT tab_fids2 INTO v_result FROM DUAL;
END ;
3)编译成功
CREATE OR REPLACE TYPE typ_id_table3 AS TABLE OF NUMBER(10); --在外部定义了变量
DECLARE
tab_fids1 typ_id_table3 := typ_id_table3;
tab_fids2 typ_id_table3 := typ_id_table3;
v_result typ_id_table3;
BEGIN
v_result := tab_fids1 MULTISET except tab_fids2;
END ;
4)编译成功
CREATE OR REPLACE TYPE typ_id_table3 AS TABLE OF NUMBER(10);--在外部定义变量
DECLARE
tab_fids1 typ_id_table3 := typ_id_table3;
tab_fids2 typ_id_table3 := typ_id_table3;
v_result typ_id_table3;
BEGIN
SELECT tab_fids1 MULTISET EXCEPT tab_fids2 INTO v_result FROM DUAL;
END ;
二、多列情况下使用集合操作符
本以为多列的情况下无法使用 集合操作符 ,再次要感谢itpub newkid版主的解答。
即在使用多列进行比较的时候不能直接比较,而必须改成sql语句的形式进行比较,如下:
CREATE OR REPLACE TYPE typ_id_object AS OBJECT (gid NUMBER(10),
gno NUMBER(5),
co NUMBER(5));
CREATE OR REPLACE TYPE typ_id_table AS TABLE OF typ_id_object;
1)编译成功
DECLARE
v1 typ_id_table := typ_id_table;
v2 typ_id_table := typ_id_table;
v3 typ_id_table;
BEGIN
SELECT v1 MULTISET EXCEPT v2 INTO V3 FROM DUAL; --如果是多列的情况下就必须是这种sql语句的形式
END;
2)
DECLARE
v1 typ_id_table := typ_id_table;
v2 typ_id_table := typ_id_table;
v3 typ_id_table;
BEGIN
V3 := v1 MULTISET EXCEPT v2; --如果采用此种形式则会提示:PLS-00306:调用 'MULTISET_EXCEPT_ALL' 时参数个数或类型错误
END;
数据库
2019-04-19 16:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
阿里妹导读 :2006 年10 月Google 发布三架马车之一的《Bigtable:A Distributed Storage System for Strctured Data》论文之后,Powerset 公司就宣布 HBase 在 Hadoop 项目中成立,作为子项目存在。后来,在2010 年左右逐渐成为 Apache 旗下的一个顶级项目。可能是实际应用中包装得太好,很多人对于 HBase 的认识止步于 NoSQL 。今天,蚂蚁金服的南俊从基础开始讲起,希望有助于增强大家在实际业务中对 HBase 的理解。
一、 HBase 简介

HBase 名称的由来是由于其作为 Hadoop Database 存在的,用来存储非结构化、半结构化数据。

要想知道 HBase 的用途,就需要看一看其在 Apache 的 Hadoop 生态系统中的位置,可以看到 HBase 是构建在 HDFS 之上的,这是由于 HBase 内部管理的文件全部都是存储在 HDFS 当中的。同时,MapReduce 这个计算框架在 HBase 之上又提供了高性能的计算能力来处理海量数据。此外还有一些像 Pig、Hive 用来提供高层语言的支持。还有 Sqoop 用来完成传统数据库到 HBase 之间的数据迁移。类似衍生出来的新技术还有很多,有兴趣的同学可以自己去了解一下。

原文链接
数据库
2019-04-19 15:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Redis实现了不定长压缩前缀的radix tree,用在集群模式下存储slot对应的的所有key信息。本文将详述在Redis中如何实现radix tree。
核心数据结构
raxNode是radix tree的核心数据结构,其结构体如下代码所示: typedef struct raxNode { uint32_t iskey:1; uint32_t isnull:1; uint32_t iscompr:1; uint32_t size:29; unsigned char data[]; } raxNode; iskey:表示这个节点是否包含key 0:没有key 1:表示从头部到其父节点的路径完整的存储了key,查找的时候按子节点iskey=1来判断key是否存在 isnull:是否有存储value值,比如存储元数据就只有key,没有value值。value值也是存储在data中 iscompr:是否有前缀压缩,决定了data存储的数据结构 size:该节点存储的字符个数 data:存储子节点的信息 iscompr=0:非压缩模式下,数据格式是: [header strlen=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?) ,有size个字符,紧跟着是size个指针,指向每个字符对应的下一个节点。size个字符之间互相没有路径联系。 iscompr=1:压缩模式下,数据格式是: [header strlen=3][xyz][z-ptr](value-ptr?) ,只有一个指针,指向下一个节点。size个字符是压缩字符片段
Rax Insert
以下用几个示例来详解rax tree插入的流程。假设j是遍历已有节点的游标,i是遍历新增节点的游标。
场景一:只插入abcd
z-ptr指向的叶子节点iskey=1,使用了压缩前缀。
场景二:在abcd之后插入abcdef
从abcd父节点的每个压缩前缀字符比较,遍历完所有abcd节点后指向了其空子节点,j = 0, i < len(abcded)。
查找到abcd的空子节点,直接将ef赋值到子节点上,成为abcd的子节点。ef节点被标记为iskey=1,用来标识abcd这个key。ef节点下再创建一个空子节点,iskey=1来表示abcdef这个key。
场景三:在abcd之后插入ab
ab在abcd能找到前两位的前缀,也就是i=len(ab),j < len(abcd)。
将abcd分割成ab和cd两个子节点,cd也是一个压缩前缀节点,cd同时被标记为iskey=1,来表示ab这个key。
cd下挂着一个空子节点,来标记abcd这个key。
场景四:在abcd之后插入abABC
abcABC在abcd中只找到了ab这个前缀,即i < len(abcABC),j < len(abcd)。这个步骤有点复杂,分解一下: step 1:将abcd从ab之后拆分,拆分成ab、c、d 三个节点。 step 2:c节点是一个非压缩的节点,c挂在ab子节点上。 step 3:d节点只有一个字符,所以也是一个非压缩节点,挂在c子节点上。 step 4:将ABC 拆分成了A和BC, A挂在ab子节点上,和c节点属于同一个节点,这样A就和c同属于父节点ab。 step 5:将BC作为一个压缩前缀的节点,挂在A子节点下。 step 6:d节点和BC节点都挂一个空子节点分别标识abcd和abcABC这两个key。
场景五:在abcd之后插入Aabc
abcd和Aabc没有前缀匹配,i = 0,j = 0。
将abcd拆分成a、bcd两个节点,a节点是一个非压缩前缀节点。
将Aabc拆分成A、abc两个节点,A节点也是一个非压缩前缀节点。
将A节点挂在和a相同的父节点上。
同上,在bcd和abc这两个节点下挂空子节点来分别表示两个key。
Rax Remove
删除
删除一个key的流程比较简单,找到iskey的节点后,向上遍历父节点删除非iskey的节点。如果是非压缩的父节点并且size > 1,表示还有其他非相关的路径存在,则需要按删除子节点的模式去处理这个父节点,主要是做memove和realloc。
合并
删除一个key之后需要尝试做一些合并,以收敛树的高度。
合并的条件是: iskey=1的节点不能合并 子节点只有一个字符 父节点只有一个子节点(如果父节点是压缩前缀的节点,那么只有一个子节点,满足条件。如果父节点是非压缩前缀的节点,那么只能有一个字符路径才能满足条件)
结束语
云数据库Redis版(ApsaraDB for Redis)是一种稳定可靠、性能卓越、可弹性伸缩的数据库服务。基于飞天分布式系统和全SSD盘高性能存储,支持主备版和集群版两套高可用架构。提供了全套的容灾切换、故障迁移、在线扩容、性能优化的数据库解决方案。欢迎各位购买使用:云数据库 Redis 版
作者:羽洵
原文链接
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-04-19 12:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
全面云化的时代已经到来,面对一系列的新技术和挑战,数据库市场将面临怎样的变革?作为云服务提供商,如何帮助更多的企业级用户把握“云”潮,提供最高效、最具价值的数据库解决方案?
日前,在阿里云峰会·北京站的数据库专场上, 阿里巴巴集团副总裁、达摩院首席数据库科学家、阿⾥云智能事业群数据库产品事业部总负责⼈李飞飞 针对下一代云原生数据库的技术与挑战进行了精彩分享。
数据库发展与技术演进之路
根据DB-Engine 2019年1月份的数据库市场趋势分析,关系型数据库依旧占据着最核心的市场份额。与此同时,数据库市场也在不断细分,图数据库、文档数据库以及NoSQL等数据库细分市场正在崛起。
另一大趋势则是:以Oracle、DB2和Microsoft SQL Server三大巨头为代表的传统商业数据库所占据的市场份额不断下降,而开源和第三方数据库市场持续增长。
数据库技术诞生至今,虽然已经历40多年的发展历程,但仍旧处于蓬勃发展的时期。如今,各大云计算厂商也达成了共识:数据库是连接IaaS和云上智能化应用的重要组成部分,因此从数据的产生、存储以及消费等各个环节,云产商都需要提升全链路的能力,进而满足用户连接IaaS和智能化应用的需求。
随着数据库技术的不断发展,不仅出现了OLTP系统来实现事务的处理,可以实现对交易数据的实时记录;还出现了OLAP系统,借助OLAP系统可实现对于海量数据的实时分析。除此之外,还需要各类的数据库服务和管控工具支持核心的OLTP和OLAP系统。在此基础之上,针对半结构化数据和非结构化数据,NoSQL数据库解决方案应运而生。
上世纪70年代末到80年代初,诞生了关系型数据库,产生了SQL查询语言以及OLTP交易处理系统。随着数据的爆发式增长以及复杂分析需求的出现,诞生了数据仓库,以及OLAP在线分析处理系统以及ETL等数据处理技术。
技术发展到今天,图、文档以及时空、时序等多元异构数据量持续增长,因此也对应地出现了关系型数据库之外的NoSQL和NewSQL数据库系统。
云原生时代 我们需要怎样的数据库?
传统数据库往往采用单节点架构,而到了如今的云原生时代,云原生数据库通常会采用共享存储的架构方式。阿里云POLARDB所采用的就是共享存储架构,通过高速网络构建共享存储,在此之上实现存储与计算的分离,进而可以快速弹性扩容出多个计算节点。
与此同时,POLARDB还可以根据用户的具体需求在存储和计算两个维度迅速地实现扩缩容。而对于用户而言,无需修改任何业务逻辑,就可以使用基于共享存储的POLARDB数据库,可以实现无侵入式迁移。
除了云原生的共享存储技术之外,面对高并发、海量数据访问的挑战,也需要借助分布式架构来解决,比如为了应对每年的双11大促,阿里巴巴自身就需要去探索分布式架构。
此外,针对数据的多模多态需求,阿里云也希望在用户侧提供不同查询接口,比如SQL。在存储侧,阿里云希望能够支持用户将数据存储到不同地方,并通过像SQL这样的统一接口实现对不同数据类型的统一查询访问。目前,阿里云所提供数据湖服务就是针对上述场景所演进来的云原生技术。
正如前面所提到的OLTP和OLAP系统,传统的解决方案希望将读写冲突隔离开,让OLTP负责事务处理,让OLAP负责海量数据的分析任务。而在云原生时代,阿里云会借助新硬件所带来的技术红利,尽可能降低数据迁移的成本,将事务处理和数据分析整合在同一个引擎中,通过一套系统为用户无缝解决两种问题。
阿里云目前已经服务了大量企业级客户,这些客户通过虚拟化、存储与计算分离来使用阿里云所提供的云资源池。因此,我们需要对于云上全部资源进行智能化监控和调配,做到快速响应,并且为用户提供最高质量的服务。而智能化的背后需要利用机器学习以及人工智能技术,从数据迁移、数据保护、弹性调度等各个维度实现自感知、自决策、自恢复以及自优化。
在云原生时代,另外一个重要技术就是软硬件一体化设计。新硬件的发展为数据库系统带来了许多可不断利用的技术红利,比如RDMA网络、SSD、NVM、GPU、IPG加速等。阿里云的POLARDB共享存储就利用了RDMA网络,因此可以做到像本地节点一样快速访问远程数据库节点。
对于很多云上的客户而言,可能也有金融级高可用的需求。利用高可用协议,阿里云数据库可以采用三副本架构,在本地可以实现数据库之间的无缝实时切换,在异地也可以满足不同用户对于灾备的需求,借助Binlog技术实现异地数据同步,实现金融级高可用。此外,云上用户对数据的安全性尤为看重,阿里云数据库服务从数据落盘开始,就提供了加密技术,确保全链路的数据安全。
阿里云数据库服务:全域布局自主可控 兼具创新与商用价值
阿里云数据库所提供的工具类产品包括数据备份、数据迁移、数据管理、混合云数据管理以及智能化诊断与优化系统等,可以帮助客户实现快速上云并且打造混合云解决方案。
我们提供的核心引擎产品中,既有自主可控的自研产品,也有第三方以及开源的产品。希望通过商业数据库以及开源产品为用户提供丰富的选择,同时也希望将云计算的技术红利整合到自研数据库产品中去,进一步做深做透,真正地帮助客户解决应用第三方或者开源数据库所无法解决的痛点和问题。
在OLTP方向,阿里云数据库所提供的核心产品是POLARDB以及其分布式版本POLARDB X。除此之外,阿里云也提供主流的MySQL、PostgreSQL、SQLServer以及针对于Oracle兼容的PPAS等一系列服务。
针对OLAP系统,我们的核心产品是AnalyticDB、针对多源异构数据所提供的数据湖服务DLA,以及针对IoT场景的时序时空数据库TSDB。而在NoSQL领域,阿里云则提供了丰富的第三方数据库产品供客户选择,比如HBase、Redis、MongoDB以及阿里云自研的图数据库GDB等。
阿里云数据库的管控平台与全链路监控服务为用户提供了智能化的全链路检测与分析,保障阿里云数据库能够为用户提供最高级别的Service Level。
阿里云帮助客户打造了一条线上线下混合云数据存储的链路,从客户迁移上云开始,其可以选择阿里云DTS服务进行实时的数据上传和同步。数据上云之后,客户可选择POLARDB等云原生数据库产品进行存储,也可以借助DLA或者AnalyticDB等产品进行数据分析。
而针对特定场景进行数据分析可以选用文档数据库、图数据库或者时序时空数据库等解决方案,可以借助DTS系统实现线上线下的数据同步备份,并且还可以借助HDM来进行混合云数据库管理。此外,阿里云数据库服务还提供了数据库管理套件,可以支撑用户对数据库进行管理和开发,使得管理和开发流程更为高效。
这里主要为大家介绍两种云原生数据库产品, POLARDB和AnalyticDB 。
POLARDB
POLARDB利用RDMA网络实现了高效的共享分布式存储,借助于共享存储技术,多个计算节点中可以实现“一写多读”,并且可以根据客户的工作量需求,快速弹出多个只读节点来满足客户在高峰时刻对于计算的需求,同时也可以在存储节点上实现快速缩扩容。
针对客户的应用场景以及其业务峰值峰谷的波动情况,POLARDB可以做到按量按需使用和计费,进而大大地提升了客户的数据库使用效率,节省了所需要成本。总体而言,POLARDB就是一个超级MySQL,后续POLARDB还会陆续推出兼容PostgreSQL和Oracle的版本。
在一些场景下,用户需要面对高并发和海量数据访问的挑战,因此需要突破共享存储的容量上限。分布式架构的POLARDB X则利用Sharding Partition解决方案实现了存储容量的无限水平扩展。POLARDB X分布式版本也会在后续进行公测,欢迎大家试用。
AnalyticDB
对海量数据进行分析的时候,读写会产生一定的冲突。如果需要读取大量数据并进行分析将会极为复杂,因此推荐大家使用阿里云的实时交互式分析数据库系统AnalyticDB。
AnalyticDB最核心的特点是具有支持高吞吐写入的能力,并且具有针对于行列存储所研发的存储引擎,因此可以实现实时的交互式分析。在海量数据以及高并发场景下,AnalyticDB在响应时间方面的表现都非常优秀。AnalyticDB兼容了MySQL生态,因此可以将MySQL里面的数据直接导入AnalyticDB中,可以实现对上百亿级别的数据的毫秒级查询以及百万TPS级别的写入。
数据传输云服务DTS
除了核心的云原生数据库产品,我们还有多款数据库工具型产品,比如数据传输云服务DTS。DTS所解决的痛点是云下客户上云所需要进行的数据传输问题,以及其在上云之后,云上与云下数据库或者从TP到AP系统之间进行实时的数据同步问题。
利用DTS,用户可以实现快速、高效地增量数据同步,保证了实时数据一致。DTS还提供了数据订阅能力,可以通过不同的协议和接口来接入更多不同的数据源。
数据库家族迎来新成员:图数据库GDB
这里为大家介绍一款阿里云新的数据库产品——阿里云图数据库GDB,该产品目前正在阿里云官网进行公测。GDB是一种支持属性图模型,用于处理高度连接数据查询与存储的实时可靠的在线数据库,其利用了大量的云原生技术,比如存储与计算分离等。GDB支持标准的图查询语言,兼容Gremlin语法,这一点与市场主流的图数据库保持一致。
GDB另外一个最核心的特点就是支持实时更新、支持OLTP级别的数据一致性,能够帮助大家在对海量的属性图进行分析存储时保证数据的一致性。GDB具有服务高可用、易于维护等云原生数据库产品的特性。比较典型的应用场景有社交网络、金融欺诈检测、实施推荐等,同时GDB还支持知识图谱等形态以及神经网络等。
以赋能客户为本:降本提效 无忧发展
阿里云数据库团队的目标是为客户提供企业级的云原生数据库服务,利用自身全域布局、自主可控的技术为企业客户提供快速数据上云、云上云下统一数据管理以及数据安全等服务。
举例而言,阿里云数据库服务目前在杭州等城市支撑了像城市大脑这样复杂的应用,其既需要存储结构化数据也需要存储非结构数据,并且对于OLTP、OLAP、工具类产品等都提出了巨大的挑战,而借助阿里云AnalyticDB、POLARDB、DTS等利用了云原生技术的产品可以无缝地支撑城市大脑这样的复杂应用场景。
以POLARDB为例,该产品于2018年8月份进行公测,2018年的年底进行商业化,到目前为止实现了在公有云平台上的快速增长。POLARDB呈现快速增长背后的核心原因就是阿里云真正地帮助客户解决自身的痛点问题,不是利用新技术来“造完锤子找钉子”,而是真正地“看到钉子再去造锤子”。
POLARDB最为核心的特点就是云原生、分钟级别的弹性存储和计算、高性价比、灵活弹性的使用计费方式、高并发能力,可快速扩容多个只读节点、大容量支持、通过共享分布式存储做到了类似单机数据库的体验,对用户的业务逻辑无侵入,并且高度兼容MySQL。
AnalyticDB则是一个实时交互式分析系统,无论是自制数据还是从大数据系统中获取的存储数据,都可以借助DTS工具将其迁移到AnalyticDB集群上,进行深入的商业分析、可视化以及交互式查询等。
AnalyticDB能够支持上百个表的连接查询,可为客户提供毫秒级的查询服务。如今,无数用户正在阿里云上使用POLARDB和AnalyticDB这样的云原生数据库,云原生数据库也正在真正地改变客户在应用中所遇到的痛点,为他们带来更多的业务价值。
总结而言,云原生时代出现了一系列的新技术和新挑战。面对这些挑战,需要对于数据库内核产品、管控平台以及数据库工具进行有机整合,才能够为客户提供最高效、最具有价值的解决方案。阿里云诚挚地邀请大家体验自身的产品和技术,希望能够与更多的客户一起合作,解决问题,也希望更多的开发者和生态合作伙伴能够基于阿里云的数据库服务和产品打造针对特定行业和领域的深度解决方案,使得云原生时代的数据库市场更加繁荣。
作者:七幕
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-04-19 10:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
https://github.com/alibaba/canal
概述
canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB)。
起源:早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变更,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务,从此开启了一段新纪元。
基于日志增量订阅&消费支持的业务: 数据库镜像 数据库实时备份 多级索引 (卖家和买家各自分库索引) search build 业务cache刷新 价格变化等重要业务消息
工作原理
mysql主备复制实现:
从上层来看,复制分成三步: master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看); slave将master的binary log events拷贝到它的中继日志(relay log); slave重做中继日志中的事件,将改变反映它自己的数据。
canal的工作原理
原理相对比较简单: canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议 mysql master收到dump请求,开始推送binary log给slave(也就是canal) canal解析binary log对象(原始为byte流)
架构设计
个人理解,数据增量订阅与消费应当有如下几个点: 增量订阅和消费模块应当包括binlog日志抓取,binlog日志解析,事件分发过滤(EventSink),存储(EventStore)等主要模块。 如果需要确保HA可以采用Zookeeper保存各个子模块的状态,让整个增量订阅和消费模块实现无状态化,当然作为consumer(客户端)的状态也可以保存在zk之中。 整体上通过一个Manager System进行集中管理,分配资源。
可以参考下图:
canal架构设计
说明: server代表一个canal运行实例,对应于一个jvm instance对应于一个数据队列 (1个server对应1..n个instance)
instance模块: eventParser (数据源接入,模拟slave协议和master进行交互,协议解析) eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作) eventStore (数据存储) metaManager (增量订阅&消费信息管理器)
EventParser
整个parser过程大致可分为几部: Connection获取上一次解析成功的位置(如果第一次启动,则获取初始制定的位置或者是当前数据库的binlog位点) Connection建立连接,发生BINLOG_DUMP命令 Mysql开始推送Binary Log 接收到的Binary Log通过Binlog parser进行协议解析,补充一些特定信息 传递给EventSink模块进行数据存储,是一个阻塞操作,直到存储成功 存储成功后,定时记录Binary Log位置
EventSink设计
说明: 数据过滤:支持通配符的过滤模式,表名,字段内容等 数据路由/分发:解决1:n (1个parser对应多个store的模式) 数据归并:解决n:1 (多个parser对应1个store) 数据加工:在进入store之前进行额外的处理,比如join
1 数据1:n业务 :
为了合理的利用数据库资源, 一般常见的业务都是按照schema进行隔离,然后在mysql上层或者dao这一层面上,进行一个数据源路由,屏蔽数据库物理位置对开发的影响,阿里系主要是通过cobar/tddl来解决数据源路由问题。 所以,一般一个数据库实例上,会部署多个schema,每个schema会有由1个或者多个业务方关注。
2 数据n:1业务:
同样,当一个业务的数据规模达到一定的量级后,必然会涉及到水平拆分和垂直拆分的问题,针对这些拆分的数据需要处理时,就需要链接多个store进行处理,消费的位点就会变成多份,而且数据消费的进度无法得到尽可能有序的保证。 所以,在一定业务场景下,需要将拆分后的增量数据进行归并处理,比如按照时间戳/全局id进行排序归并.
EventStore设计
目前实现了Memory内存、本地file存储以及持久化到zookeeper以保障数据集群共享。
Memory内存的RingBuffer设计:
定义了3个cursor Put : Sink模块进行数据存储的最后一次写入位置 Get : 数据订阅获取的最后一次提取位置 Ack : 数据消费成功的最后一次消费位置
借鉴Disruptor的RingBuffer的实现,将RingBuffer拉直来看:
实现说明: Put/Get/Ack cursor用于递增,采用long型存储 buffer的get操作,通过取余或者与操作。(与操作: cusor & (size – 1) , size需要为2的指数,效率比较高)
Instance设计
instance代表了一个实际运行的数据队列,包括了EventPaser,EventSink,EventStore等组件。
抽象了CanalInstanceGenerator,主要是考虑配置的管理方式:
1. manager方式: 和你自己的内部web console/manager系统进行对接。(alibaba内部使用方式)
2. spring方式:基于spring xml + properties进行定义,构建spring配置. spring/memory-instance.xml 所有的组件(parser , sink , store)都选择了内存版模式,记录位点的都选择了memory模式,重启后又会回到初始位点进行解析。特点:速度最快,依赖最少 spring/file-instance.xml 所有的组件(parser , sink , store)都选择了基于file持久化模式,注意,不支持HA机制.支持单机持久化 spring/default-instance.xml 所有的组件(parser , sink , store)都选择了持久化模式,目前持久化的方式主要是写入zookeeper,保证数据集群共享. 支持HA spring/group-instance.xml 主要针对需要进行多库合并时,可以将多个物理instance合并为一个逻辑instance,提供客户端访问。场景:分库业务。 比如产品数据拆分了4个库,每个库会有一个instance,如果不用group,业务上要消费数据时,需要启动4个客户端,分别链接4个instance实例。使用group后,可以在canal server上合并为一个逻辑instance,只需要启动1个客户端,链接这个逻辑instance即可.
Server设计
server代表了一个canal的运行实例,为了方便组件化使用,特意抽象了Embeded(嵌入式) / Netty(网络访问)的两种实现: Embeded : 对latency和可用性都有比较高的要求,自己又能hold住分布式的相关技术(比如failover) Netty : 基于netty封装了一层网络协议,由canal server保证其可用性,采用的pull模型,当然latency会稍微打点折扣,不过这个也视情况而定。
增量订阅/消费设计

具体的协议格式,可参见:CanalProtocol.proto
get/ack/rollback协议介绍: Message getWithoutAck(int batchSize),允许指定batchSize,一次可以获取多条,每次返回的对象为Message,包含的内容为: a. batch id 唯一标识 b. entries 具体的数据对象,对应的数据对象格式:EntryProtocol.proto void rollback(long batchId),顾命思议,回滚上次的get请求,重新获取数据。基于get获取的batchId进行提交,避免误操作 void ack(long batchId),顾命思议,确认已经消费成功,通知server删除数据。基于get获取的batchId进行提交,避免误操作 canal的get/ack/rollback协议和常规的jms协议有所不同,允许get/ack异步处理,比如可以连续调用get多次,后续异步按顺序提交ack/rollback,项目中称之为流式api. 流式api设计的好处: get/ack异步化,减少因ack带来的网络延迟和操作成本 (99%的状态都是处于正常状态,异常的rollback属于个别情况,没必要为个别的case牺牲整个性能) get获取数据后,业务消费存在瓶颈或者需要多进程/多线程消费时,可以不停的轮询get数据,不停的往后发送任务,提高并行化. (作者在实际业务中的一个case:业务数据消费需要跨中美网络,所以一次操作基本在200ms以上,为了减少延迟,所以需要实施并行化)
流式api设计:
每次get操作都会在meta中产生一个mark,mark标记会递增,保证运行过程中mark的唯一性 每次的get操作,都会在上一次的mark操作记录的cursor继续往后取,如果mark不存在,则在last ack cursor继续往后取 进行ack时,需要按照mark的顺序进行数序ack,不能跳跃ack. ack会删除当前的mark标记,并将对应的mark位置更新为last ack cusor 一旦出现异常情况,客户端可发起rollback情况,重新置位:删除所有的mark, 清理get请求位置,下次请求会从last ack cursor继续往后取
数据格式
canal采用protobuff:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Entry Header logfileName [binlog文件名] logfileOffset [binlog position] executeTime [发生的变更] schemaName tableName eventType [insert/update/delete类型] entryType [事务头BEGIN/事务尾END/数据ROWDATA] storeValue [ byte 数据,可展开,对应的类型为RowChange] RowChange isDdl [是否是ddl变更操作,比如create table/drop table] sql [具体的ddl sql] rowDatas [具体insert/update/delete的变更数据,可为多条, 1 个binlog event事件可对应多条变更,比如批处理] beforeColumns [Column类型的数组] afterColumns [Column类型的数组] Column index sqlType [jdbc type] name [column name] isKey [是否为主键] updated [是否发生过变更] isNull [值是否为 null ] value [具体的内容,注意为文本]
canal-message example:
比如数据库中的表:
1
2
3
4
5
6
7
8
9
mysql> select * from person; +----+------+------+------+ | id | name | age | sex | +----+------+------+------+ | 1 | zzh | 10 | m | | 3 | zzh3 | 12 | f | | 4 | zzh4 | 5 | m | +----+------+------+------+ 3 rows in set ( 0.00 sec)
更新一条数据(update person set age=15 where id=4):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
**************************************************** * Batch Id: [ 2 ] ,count : [ 3 ] , memsize : [ 165 ] , Time : 2016 - 09 - 07 15 : 54 : 18 * Start : [mysql-bin. 000003 : 6354 : 1473234846000 ( 2016 - 09 - 07 15 : 54 : 06 )] * End : [mysql-bin. 000003 : 6550 : 1473234846000 ( 2016 - 09 - 07 15 : 54 : 06 )] ****************************************************
================> binlog[mysql-bin. 000003 : 6354 ] , executeTime : 1473234846000 , delay : 12225ms BEGIN ----> Thread id: 67 ----------------> binlog[mysql-bin. 000003 : 6486 ] , name[canal_test,person] , eventType : UPDATE , executeTime : 1473234846000 , delay : 12225ms id : 4 type= int ( 11 ) name : zzh4 type=varchar( 100 ) age : 15 type= int ( 11 ) update= true sex : m type= char ( 1 ) ---------------- END ----> transaction id: 308 ================> binlog[mysql-bin. 000003 : 6550 ] , executeTime : 1473234846000 , delay : 12240ms
HA机制设计
canal的HA分为两部分,canal server和canal client分别有对应的ha实现: canal server: 为了减少对mysql dump的请求,不同server上的instance要求同一时间只能有一个处于running,其他的处于standby状态. canal client: 为了保证有序性,一份instance同一时间只能由一个canal client进行get/ack/rollback操作,否则客户端接收无法保证有序。
整个HA机制的控制主要是依赖了zookeeper的几个特性,watcher和EPHEMERAL节点(和session生命周期绑定),可以看下我之前zookeeper的相关文章。
Canal Server:
大致步骤: canal server要启动某个canal instance时都先向zookeeper进行一次尝试启动判断 (实现:创建EPHEMERAL节点,谁创建成功就允许谁启动) 创建zookeeper节点成功后,对应的canal server就启动对应的canal instance,没有创建成功的canal instance就会处于standby状态 一旦zookeeper发现canal server A创建的节点消失后,立即通知其他的canal server再次进行步骤1的操作,重新选出一个canal server启动instance. canal client每次进行connect时,会首先向zookeeper询问当前是谁启动了canal instance,然后和其建立链接,一旦链接不可用,会重新尝试connect. Canal Client的方式和canal server方式类似,也是利用zokeeper的抢占EPHEMERAL节点的方式进行控制.
HA配置架构图(举例)如下所示:
canal其他链接方式
canal还有几种连接方式:
1. 单连
2. 两个client+两个instance+1个mysql
当mysql变动时,两个client都能获取到变动
3. 一个server+两个instance+两个mysql+两个client
4. instance的standby配置
整体架构
从整体架构上来说canal是这种架构的(canal中没有包含一个运维的console web来对接,但要运用于分布式环境中肯定需要一个Manager来管理):
一个总体的manager system对应于n个Canal Server(物理上来说是一台服务器), 那么一个Canal Server对应于n个Canal Instance(destinations). 大体上是三层结构,第二层也需要Manager统筹运维管理。
那么随着Docker技术的兴起,是否可以试一下下面的架构呢?
一个docker中跑一个instance服务,相当于略去server这一层的概念。 Manager System中配置一个instance,直接调取一个docker发布这个instance,其中包括向这个instance发送配置信息,启动instance服务. instance在运行过程中,定时刷新binlog filename+ binlog position的信息至zk。 如果一个instance出现故障,instance本身报错或者zk感知此node消失,则根据相应的信息,比如上一步保存的binlog filename+binlog position重新开启一个docker服务,当然这里可以适当的加一些重试机制。 当要更新时,类似AB test, 先关闭一个docker,然后开启新的已更新的替换,循序渐进的进行。 当涉及到分表分库时,多个物理表对应于一个逻辑表,可以将结果存于一个公共的模块(比如MQ),或者单独存取也可以,具体情况具体分析 存储可以参考canal的多样化:内存,文件,zk,或者加入至MQ中 docker由此之外的工具管理,比如kubernetes 也可以进一步添加HA的功能,两个docker对应一个mysql,互为主备,类似Canal的HA架构。如果时效性不是贴别强的场景,考虑到成本,此功能可以不采用。
总结
这里总结了一下Canal的一些点,仅供参考: 原理:模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议;mysql master收到dump请求,开始推送binary log给slave(也就是canal);解析binary log对象(原始为byte流) 重复消费问题:在消费端解决。 采用开源的open-replicator来解析binlog canal需要维护EventStore,可以存取在Memory, File, zk canal需要维护客户端的状态,同一时刻一个instance只能有一个消费端消费 数据传输格式:protobuff 支持binlog format 类型:statement, row, mixed. 多次附加功能只能在row下使用,比如otter binlog position可以支持保存在内存,文件,zk中 instance启动方式:rpc/http; 内嵌 有ACK机制 无告警,无监控,这两个功能都需要对接外部系统 方便快速部署。
数据库
2019-04-19 09:37:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Redis的数据结构之字符串
五种数据类型: 字符串 (String) 字符串列表 (list) 有序字符串集合 (sorted set) 哈希 (hash) 字符串集合 (set)
Key定义的注意点: 不要过长 不要过短,会降低Key的可读性 统一的命名规范
存储 String 二进制安全的,存入和获取的数据相同 Value最多可以容纳的数据长度是512M
存储String常用命令 赋值 set key name 取值 get key 删除 del key 数值递增扩展命令 incrby key 5,将该key的值加5,如果key不为int类型,则出现error,如果该key没有创建过,则先将该key创建,默认为 0 然后加5 数值递减扩展命令 decrby key 5,将该key的值减5,如果key不为int类型,则出现error,如果该key没有创建过,则先将该key创建,默认为 0 然后减5 数值递增 incr key ,如果key不为int类型,则出现error,如果该key没有创建过,则先将该key创建,默认为 0 然后加一 数值递减 decr key,如果key不为int类型,则出现error,如果该key没有创建过,则先将该key创建,默认为 0 然后减一得 -1 追加字符串 append key 5,在该key 的后面追加5这个字符串。如果该key 没有创建过,则先将该key创建,值为5

存储Hash String Key和String Value的map容器 每一个Hash可以存储4294967295个键值对
存储Hash常用命令: 赋值 hset key field value 如:hset myhash username rose。hmset key field value[field value ...] 如:hmset myhash username rose age 21 取值 hget key field 如:hget myhash username。hmget key field value[field value ...] 如:hmget myhash username age 获取key的属性和属性值 hgetall key 删除 hdel key field field [field value...], 增加数字 hincrby key field 5 如:hincrby myhash age 5 将age在原有的数字上加5 查看该属性在key中是否存在 hexists key field 如:hexists myhash username有的话会返回1,没有的话返回0 获取所有属性和值 hgetall key 获取所有属性长度 hlen key 返回Integer 获取所有Key: hkeys key 获取所有Value: hvals key
存储list:
基本概述,在Redis中,list类型是按照插入顺序排序的一个字符串的链表和数据结构中的普通链表是一样的,可以在左侧头部和右侧尾部添加新的元素,在插入的时候如果该键不存在,Redis则为该键创建一个新的链表,于此相反,如果Redis中的所有元素都被移除了,那么该键也被从数据库中删除
从元素的插入和删除的效率来看,如果我们是在链表的两头插入和删除,那么这是非常高效的操作
如果链表中存在百万条数据,这个操作也可以在一定时间内完成,重点是如果插入和删除的操作是在链表的中间的,那么这个时候效率就会比较低了 ArrayList 使用数组方式存储数据(根据索引去查询速度是非常快的,新增和删除的时候需要涉及到位移操作,所以比较慢) LinkedList 使用双向链接方式存储数据
以上两个在头尾删除或添加数据的时候只是改变了每个元素所指向的指针 双向链表中添加数据 双向链表中删除数据
存储list常用命令: 从左侧添加 lpush key value [value...] 如:lpush mylist a b c 从右侧添加 rpush key value [value...] 如:rpush mylist c b a 查看列表 lrange key start stop 如:lrange mylist 0 5。stop为-1 则从start开始到最后一个结束,stop为-2则从start开始到倒数第二个结束,以此类推 左边弹出 lpop key 如:lpop mylist 弹出mylist的左边第一个(弹出的数就此从列表中删除) 右边弹出 rpop key 如:rpop mylist 弹出mylist的右边第一个弹出的数就此从列表中删除) 获取列表元素个数 llen key 如:llen mylist 头部插入一个值 lpushx key value(在该key的头部插入一个值,如果该key不存在,则不插入) 如:lpushx mylist x 尾部插入一个值 rpushx key value(在该key的尾部追加一个值,如果该key不存在,则不插入) 如:rpushx mylist x 删除列表中相同value的个数 lrem key count value 如:lrem mylist 2 x(在mylist中从前面删除两个x,如果count为0则代表删除所有的x) 如:lrem mylist -2 x(在mylist中从后面删除两个x,如果count为0则代表删除所有的x) 在指定的位置添加某个元素 lset key index value 如:lset mylist 3 mmm(在mylist集合中的第三个位置添加mmm,lset从0开始数到3) 在某个元素前插入某个元素 linsert key BEFORE|AFTER pivot value 如:linsert mylist before b 11(在mylist集合中的b元素前插入11) 在某个元素后插入某个元素 linsert key BEFORE|AFTER pivot value 如:linsert mylist after b 11(在mylist集合中的b元素后插入11) 将链表中的尾部元素弹出并添加到头部 rpoplpush source destination如:rpoplpush mylist5 mylist6(将mylist5列表的右边弹出,插入到左边mylist6列表的左边)
rpoplpush 经常用于多个消息队列,来完成多个程序之间消息的交互
假设一个应用程序正在执行lpush向链表中添加新的元素,我们通常称这样的程序为生产者,另外一个程序正在执行rpop操作从链表中取出元素,我们称之为是消费者
与此同时,消费者程序在取出元素后立刻崩溃,由于该消息已经被取出且没有被正常处理,那么我们就可以认为这个消息已经丢失了,由此可能回导致业务数据的丢失,或者是业务状态的不一致等现象的发生,然而可以通过 rpoplpush这个命令消费者程序在主消息队列中取出元素后再将它插入到备份的队列当中知道我们的消费者程序完成正常的逻辑处理后再将消息从备份队列中删除,这样的话我们可以提供一个守护的线程。当发现备份队列中的消息过期的时候可以重新将它放回到主消息队列中以便其他消费者可以继续去处理
rpoplpush使用场景
存储Set 和List类型不同的是, Set集合中不允许出现重复的元素 Set可包含的最大元素数量是4294967295
存储set常用命令: 添加元素 sadd key member [member ...] 如:sadd myset a b c 删除set中制定的元素 srem key member [member ...] 如:srem myset 1 2 获得集合中的元素 smembers key 如:smembers myset 查看该集合中是否有指定元素 sismember key member 如:sismember myset a (返回1代表存在0为不存在) 集合中的差集运算 sdiff key [key ...] 如:sdiff mya1 myb1(获取这两个集合中不同的元素) 集合中的交集运算 sinter key [key ...] 如:sinter mya2 myb2(获取这两个元素中相同的元素) 集合中的并集运算 sunion key [key ...] 如:sunion mya3 myb3(将这两个元素合并到一起,如元素相同则只留一个) 获取Set中成员的数量 scard key 如:scard myset 随机返回Set中成员 srandmember key 如:srandmember myset 将两个集合中的差集存到另外一个集合中 sdiffstore destination key [key ...] 如:sdiffstore my1 mya1 myb1 (将mya1和myb1不同的值存到my1中) 将两个集合中的交集存到另外一个集合中 sinterstore destination key [key ...] 如:sinterstore my2 mya1 myb1(将mya1和myb1相同的值存到my1中) 将两个集合中的并集存到另外一个集合中 sunionstore destination key [key ...] 如:sunionstore my3 mya1 myb1(将mya1和myb1的值存到my1中,重复的只存一个)
存储Set的使用场景 我们可以用Set数据类型跟踪一些 唯一性的数据 ,比如访问者的IP地址,只需在访问的时候将IP添加进Redis中Set就会自动保证这些数据的唯一性 维护服务器之间数据的关联关系,比如所有购买电子设备的客户ID和购买另外一个电子设备的客户ID,而我们想知道同时购买这两种电子设备的客户,就可以将他们交集存入到一个Set集合中
存储Sorted-Set Sorted-Set 和 Set是很相似的,他们都是字符串的集合,都不允许重复的成员出现在一个Set当中 Sorted-Set 和 Set的主要区别是:Sorted-Set 中每一个成员都会有一个分数与之关联,Redis正是用这个分数将这个集合进行从小到大的排序, 尽管Sorted-Set 中的元素是唯一的,但是分数是可以重复的 在Sorted-Set 中删除和更新一个成员都是非常快速的,时间复杂度为集合中成员的个数的一个对数,由于Sorted-Set 集合中成员位置是有序的,因此就算访问集合中部的成员它也是非常高效的
Sorted-Set应用场景 在游戏的排名上 微博的热点话题等
存储Sorted-set常用命令: 添加元素 zadd key score member [score member ...] 如:zadd mysort 70 zs 80 ls 90 ww (返回存到里面的元素个数,如果添加的元素已存在,则将里面相同的元素替换) 获得元素分数 zscore key member 如:zscore mysort zs 获得集合长度 zcard key 如:zcard mysort 删除元素 zrem key member [member ...] 如:zrem mysort tom ww 范围查询 zrange key start stop 如:zrange mysort 0 -1 (stop 为-1则查询全部,返回该集合的所有元素) 范围查询并显示分数 zrange key start stop 如:zrange mysort 0 -1 withscores (返回该集合的所有元素和分数,默认是有小到大的顺序) 范围查询显示分数并从大到小 zrevrange key start stop 如:zrevrange mysort 0 -1 withscores(返回该集合的所有元素和分数,由大到小) 按照范围进行删除 zremrangebyrank key start stop 如:zremrangebyrank mysort 0 4 (删除集合前五个,从0开始算) 按照分数范围进行删除 zremrangebyscore key start stop 如:zremrangebyscore mysort 80 100 (删除分数在 80 到 100 的元素) 根据分数范围进行查询 zrangebyscore key start stop 如:zrangebyscore mysort 0 100 根据分数范围进行查询并显示分数 zrangebyscore key start stop 如:zrangebyscore mysort 0 100 withscores 根据分数范围进行查询显示分数并进行分页 zrangebyscore key start stop withscores limit 0 2 如:zrangebyscore mysort 0 100 withscores limit 0 2(查询分数在0到100 之间的元素,并进行每页两个元素的分页,查看第一页) 给某个元素的分数加分 zincrby key increment member 如:zincrby mysort 3 ls 显示分数范围内的元素个数 zcount mysort 80 90
Sorted-Set 使用场景 用于大型游戏的积分排行榜,每当玩家的分数变化时可以用 zadd 这个命令更新他的分数 zrang 获取积分,用户信息等 构建索引数据

数据库
2019-04-19 09:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、编辑 /etc/my.cnf
在[mysqld]下添加:
skip-grant-table

2、重启Mysql
# systemctl restart mysqld

3、进入mysql操作
# mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.15 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> flush privileges;
Query OK, 0 rows affected (0.44 sec)
mysql> alter user 'root'@'localhost'IDENTIFIED BY 'newpassword';
Query OK, 0 rows affected (0.02 sec)
mysql>
4、删除掉配置文件里的skip-grant-table
5、重启
异常:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''Aaa+123456'' at line 1
密码必须包含大写、小写、数字和特殊字符。不能有+号。
数据库
2019-04-18 21:26:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Jedis入门
Jedis介绍: Jedis是Redis官网首选的Java客户但开发包 https://github.com/xetorthio/jedis
/** * 单实例的测试 */ public void demo1(){ // 1.设置IP地址和端口 Jedis jedis = new Jedis("localhost",6379); // 2.保存数据: jedis.set("name","vincent"); // 3.获取数据 String value = jedis.get("name"); System.out.println("value"); // 4.释放资源 jedis.close(); } /** * 连接池方式链接 */ public void demo2(){ // 获得链接池的配置对象: JedisPoolConfig config = new JedisPoolConfig(); // 设置最大链接数: config.setMaxTotal(100); // 设置最大空闲连接数: config.setMaxIdle(10); // 获得连接池: JedisPool jedisPool = new JedisPool(config,"localhost",6379); // 获得核心对象 Jedis jedis = null; try{ // 通过连接池获得链接 jedis = jedisPool.getResource(); // 设置数据 jedis.set("name","LuLu"); // 获得数据: String name = jedis.get("name"); System.out.println(name); }catch (Exception e){ e.printStackTrace(); }finally { // 释放资源 if (jedis != null){ jedis.close(); } if (jedisPool != null){ jedisPool.close(); } } }
数据库
2019-04-18 11:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
《红楼梦》的读后感范文2300字:
《红楼梦》一开始,就写贾宝玉的小厮各种顽劣,“恋风流情友入家塾,起嫌疑顽童闹学堂”,文中说“这茗烟无故就要欺压人的”,又说“宝玉还有三个小厮:一名锄药,一名扫红,一名墨雨。这三个岂有不淘气的,一齐乱嚷……”四个顽童把学堂闹了个天翻地覆,再对照他们那文绉绉的名字,总不免让人哑然失笑。
“茗烟”指的是茶上的雾气。古人有诗曰“白云堆里茗烟青”,宝玉亦曾写道,“宝鼎茶闲烟尚绿”,可见无论是作者,还是宝玉,都恋恋于茶雾之美,只是这种极静态的雅致,实在安不到活蹦乱跳的茗烟身上啊。
锄药、扫红等等,自然更不能人如其名,但宝玉给他们起这些名字时,大概也没想着要名副其实,在那时,下人的名字由主子随意指定,主要用来体现主子的趣味与审美。
贾宝玉的小厮是这等锄药扫红的风雅,丫鬟的名字,就起得更为用心。袭人姓花,名字里有个“花气袭人知昼暖”的典故,贾政听了都摇头,骂宝玉专会这些“精致的淘气”。而晴雯、麝月、秋纹、碧痕等等,更是透着富贵小闲人的文青气质,一听就知道,背后有个专爱吟风弄月的主子。
就像琼瑶喜欢让主人公叫什么“梦竹”“慕天”“雨薇”“吟霜”之类,宝玉的小厮和丫鬟的名字,看似大雅,实则大俗,当曹公写下当初那精致又无聊的用心,脸上应有自嘲的笑容。千帆过尽,沧桑阅遍,再看那“锦衣纨袴之时,饫甘餍肥之日”的所谓风雅,真是不忍卒读,又令人感慨万千。
黛玉屋里的丫鬟出现得不多,除了一带而过的小丫鬟“春纤”,大丫鬟就是“紫鹃”和“雪雁”。两个名字都是偏正词组,紫色的杜鹃,与白色的大雁,这两种颜色皆与黛玉正相宜,这两种鸟,在古代,也都有着悲情的色彩。
杜鹃不用说了,杜鹃啼血,黛玉自己也有诗句,“洒上空枝见血痕”。“雪雁”的名字,我总怀疑是出自元好问的那首《摸鱼儿·雁丘词》:问世间、情是何物,直教生死相许,天南地北双飞客,老翅几回寒暑。欢乐趣,离别苦,就中更有痴儿女。君应有语,渺万里层云,千山暮雪,只影向谁去?”
丫鬟的名字,暗示了黛玉恋情的结局,另一方面,是否也展示了黛玉的性情?老婆子们看不上宝玉,觉得他痴,会对着天上的鸟、地上的鱼喃喃自语,却不知,真性情的人,与动物有着更好的交流,豆瓣网同人人手一猫或者可以算是一个证明。
《红楼梦》里,最爱小动物的,除了宝玉,就是黛玉,她养的鹦鹉会学着她的口气念诗,她跟宝玉吵架,还不忘嘱咐紫鹃:“把屋子收拾了,撂下一扇纱屉;社会契约论读后感(http://m.simayi.net/duhougan/6076.html)看那大燕子回来,把帘子放下来,拿狮子倚住;烧了香就把炉罩上。”那只无情的“梁间燕子”,俨然已经成为她生活中的一部分,这是在怜惜落花之外,黛玉与宝玉又一心神相通之处。
据说紫鹃来到黛玉身边之前原本叫鹦哥,那时她是老太太屋里的一个二等丫鬟,其实不看介绍,单看这名字,就知道她在老太太屋里并不被看重。老太太最得力的几个丫鬟,名字全是“单纯词”——必须两个音节放在一起才能有意义,像鸳鸯、琥珀,袭人从前的名字珍珠,还有玻璃、翡翠,等等。这种“单纯词”念在口中,有琳琅之感,前后音节敲叩,似有金石声,也显示出贾母生命里华丽鲜明的色彩,配得上她在审美方面一贯的自信——她曾对宝钗说:“我最会收拾屋子的……如今让我替你收拾,包管又大方又素静。”她看不得宝钗房间里像雪洞一般,而她自己的屋子,则珠围翠绕,花枝招展。
贾母华丽,却华丽得不俗,她有一份自信撑着,相形之下,王夫人给她的丫鬟起的那些名字,就多了点笨拙与力不从心。
王夫人的丫鬟的名字,走的也是华丽路线,比如金钏、玉钏,再有彩霞,等等。与贾母的丫鬟的名字都是单纯词不同,王夫人的丫鬟的名字都是偏正词组,却又不能像黛玉的丫鬟的名字那样来得浑然天成,金啊玉啊彩啊,暴露了王夫人审美的极度贫乏,以及在起名字上面的敷衍潦草。最起码,她不像贾母那样,坚持过有审美的生活。
凤姐的手下人名字都俗,小厮就叫什么兴儿、旺儿,丫鬟就叫平儿、丰儿。我小时候看《红楼梦》,是从中间部分看起,不知道平儿是谁,单看这名字,实在引发不了丝毫美好想象。凤姐屋里人的名字,不求优美,但求顺嘴,外加意思吉利,这都是当家人的起名诉求。
所以她听见“红玉”这个名字就皱起眉头,张嘴就改成了“小红”。你别说,“小红”这名字比“红玉”顺嘴多了,透出凤姐的实用主义。只是,兴儿、旺儿、平儿、丰儿,这些名字虽俗,却像她作的那句诗“一夜北风紧”,俗得坦率,俗得利索,俗成一种无色无味的空白。一旦对书中人有所认知,倒可以填充进去更多的想象,曹公为她起这些名字,也算是煞费苦心了,可见得对她的厚爱。
薛姨妈家里人的名字也俗,却不像王熙凤的手下人俗得有味道,是一种市井草根的俗法,比如同喜、同贵,一看就是薛姨妈自己起的。《红楼梦》里,就这个薛姨妈最有烟火气,虽然与王夫人是亲姐妹,她却不像姐姐老是端着,她也慈祥,也嘴碎,丫鬟们都能在她面前撒个娇,叫一声“好亲亲的姨太太,姨祖宗”,她像所有的老太太那样,见不得家里人糟践东西,这与王夫人的爱面子死撑着的大家闺秀气质迥然不同。
这样一个俗气又可爱的姨妈,喜欢同喜、同贵这种名字不足为奇,但宝钗的名字不见得是她起的,也俗得可以,还有薛蟠,这名字明显起得太大,若命小福薄压不住,就会给自己带来麻烦。薛家族人的名字也不漂亮,比如薛蝌,比如薛宝琴,都显得小气,我总怀疑这无形中暴露了曹公对这个富而不贵的皇商之家的不屑。
只是薛蟠字“文起”,跟他们家起名整体风格不搭,虽然这俩字没啥意义,但真不像那个会把“唐寅”念成“庚黄”的半文盲薛蟠的字号。前两天在网上看到有人说,薛蟠,字文起,“文起八代之衰”,才发现这里面埋伏着一个小小的狭促,曹公这算是欺负薛呆子吗?有文化,真可怕。
《红楼梦》里的名字,大多都内中有文章,但相对于贾雨村、甄士隐、詹光、单聘仁这些巧用谐音的名字,我更喜欢丫鬟们名字里藏着的那份委婉用意,前者能立即让人会心一笑,后者却让人在许多时日后,恍然大悟。
数据库
2019-04-17 23:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
sql server 2008 r2 安装文件+图解安装过程+注意事项

sql server 2008 r2下载地址:【 http://yunpan.cn/QNZJnrdjfdBP9 】
安装步骤:
一、解压完压缩包之后,双击【setup】:




左侧选择【安装】-->右侧选择【全新安装或向现有安装添加新功能】


二、全部通过就点击【确定】,否则可点击【重新运行】,解决问题之后再点击【确定】


三、选择【输入产品密钥】-->输入密钥-->【下一步】(可用的密钥:PTTFM-X467G-P7RH2-3Q6CG-4DMYB)




四、勾选【我接受许可条款】-->【下一步】



五、点击【安装】



六、通过了之后,点【下一步】

这里经常会出现一个关于“windows防火墙设置”的警告,千万不要忽略这个警告。这个警告的意思是要让你打开windows防火墙中sql server 预留的tcp端口1433,打开的方法可以参考一下博文: http://www.cnblogs.com/hewenwu/p/3661059.html 。

七、勾选【SQL Server功能安装】-->【下一步】



八、点击【全选】按钮-->【下一步】



九、点击【下一步】



十、点击【默认实例】-->【下一步】


十一、点击【下一步】

十二、弹出【服务器配置】对话框,指定服务账户和排序规则配置,点击【对所有SQL Server服务使用相同的账户】

在出现的对话框中,点击帐户名后面的倒三角形--> 选择【NT AUTHORITY\SYSTEM】 -->【确定】




十三、【下一步】-->选择【混合模式】-->输入用户名和密码(自己设置,登陆时用)-->点击【添加当前用户】-->【下一步】

十四、连续点击【下一步】,直到点击【安装】:


等待安装完成即可


十五、运行:开始菜单-->所有程序-->sql server 2008 r2 文件夹-->点击sql server management studio
-->身份验证选择“SQL SERVER 身份验证”-->输入账号密码-->点击【连接】。

到此就大功告成了!!
数据库
2019-04-15 17:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
mysql 数据库我如何优化慢的SQL语句
1.根据慢日志定位到慢sql语句,使用 show variables like ‘%quer%’ 查看与查询相关的变量,在慢sql查询中我们主要关注一下三个。
开启 slow_query_log set global slow_query_log = on
设置 long_query_time 时间 set global long_query_time = 1
找到 slow_query_log_file 指定的文件位置
2. 使用 explain 工具分析SQL语句
3.修改SQL或者尽量让SQL走索引
增加索引 alter table <表明> and index 索引名(字段名)
在语句后面增加 force idnex(索引)表示使用强制索引
番外篇:符合索引的 最左匹配原则
1.最左前缀匹配原则,非常重要的原则,mysql会一直想右匹配直到遇到范围查询(>,<,between,like)就停止匹配。比如 a=3 and b=4 and c > 5 and d=5 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以则都可以用得到。并且a,b,d 的顺序可以任意调整。
2.= 和 in 可以乱序,例如 a=1 and b =2 and c= 3 建立 (a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
数据库
2019-04-15 16:31:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问题: mysql无法停止
> sudo /usr/local/mysql/support-files/mysql.server stop
(参考: https://blog.csdn.net/sheqianweilong/article/details/88925349 )

问题: mysql -uroot -p 时报错:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
> sudo /usr/local/mysql/bin/mysqld_safe --skip-grant-tables
(若出现“2019-06-03T06:33:53.6NZ mysqld_safe Logging to '/usr/local/mysql/data/wykaydeMacBook-Pro.local.err'.
2019-06-03T06:33:53.6NZ mysqld_safe Starting mysqld daemon with databases from /usr/local/mysql/data”,不要关闭当前的终端窗口,新建一个终端窗口,输入如下命令,回车登录mysql /usr/local/mysql/bin/mysql

(参考: https://blog.csdn.net/sheqianweilong/article/details/88925349 )

问题: MAC连接上数据库spider后,查看表结构时:desc user 报如下错误
error: 'File './spider/user.MYD' not found (Errcode: 13 - Permission denied)'
解决方法:更改该数据库权限 (参考: https://blog.csdn.net/wozaixiaoximen/article/details/49391631 ) cd /usr/local/mysql/data sudo chown -R mysql spider
数据库
2019-04-15 15:49:09
「深度学习福利」大神带你进阶工程师,立即查看>>>
查看redis的状态(以aws上面的redis实例为例子):
redis-cli -h 主机 -a 密码 -p 端口 ‘info’
# Server
redis_version:3.2.10 ##redis版本
redis_git_sha1:0 ##git上版本
redis_git_dirty:0 ##git的代码是否修改
redis_build_id:0 ##编译时ID
redis_mode:standalone ##redis运行模式
os:Amazon ElastiCache ##服务器的宿主操作系统
arch_bits:64 ##架构(32或64位)
multiplexing_api:epoll ##Redis基于epoll模型
gcc_version:0.0.0 ##编译redis的GCC版本
process_id:1 ##服务器进程的pid
run_id:3802b560256fc3702fd858ebdf877b8ace8d6630 ##Redis 服务器的随机标识符(用于 Sentinel 和集群)
tcp_port:6379 ##redis的tcp端口
uptime_in_seconds:77788 ##redis自启动以来,一共运行了多少秒
uptime_in_days:0 ##redis自启动以来,一共运行了多少天
hz:10
lru_clock:11803518 ##以分钟为单位进行自增的时钟,用于 LRU 管理
executable:- ##redis的服务
config_file:- ##redis的配置文件
# Clients
connected_clients:394 ##已连接客户端的数量(不包括通过从属服务器连接的客户端)
client_longest_output_list:0 ##当前连接的客户端当中,最长的输出列表
client_biggest_input_buf:0 ##当前连接的客户端当中,最大输入缓存
blocked_clients:0 ##正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量
# Memory
used_memory:5238452648 ##由 Redis 分配器分配的内存总量,以字节(byte)为单位
used_memory_human:4.88G ##由Redis分配的内存的总量,单位G给人看的
used_memory_rss:5469782016 ## Redis进程从OS角度分配的物理内存,如key被删除后,malloc不一定把内存归还给OS,但可以Redis进程复用,代表redis使用的总内存,除两次1024,换算成M
used_memory_rss_human:5.09G ##Redis进程从OS角度分配的物理内存,如key被删除后,malloc不一定把内存归还给OS,但可以Redis进程复用,代表redis使用的总内存,给人看的
used_memory_peak:5883554584 ##Redis使用内存的峰值,字节数 给机器看的
used_memory_peak_human:5.48G ##Redis使用内存的峰值,单位G 给人看的
used_memory_lua:37888 ##lua引擎使用的内存总量,字节数;有使用lua脚本的注意监控
used_memory_lua_human:37.00K ##lua引擎使用的内存总量,给人看的
maxmemory:11496376320 ##redis可以使用的最大的内存,字节数,给机器看的
maxmemory_human:10.71G ##redis可以使用的最大的内存,给人看的
maxmemory_policy:volatile-lru ##设置删除key的规则(有6种, volatile-lru:只对设置了过期时间的key进行LRU(默认值) allkeys-lru : 删除lru算法的key volatile-random:随机删除即将过期key allkeys-random:随机删除 volatile-ttl : 删除即将过期的 noeviction : 永不过期,返回错误

mem_fragmentation_ratio:1.04 ##内存碎片过高(如果实例比较小,这个指标可能比较大,不实用)实用的内存碎片换算:used_memory_peak-used_memory 多出来的就是内丰碎片,重启后此碎片消失。
mem_allocator:jemalloc-4.0.3 ##Redis内存管理器
# Persistence
loading:0 ##标志位,是否在载入数据文件,0代表没有,1代表正在载入
rdb_changes_since_last_save:23910414 ##从最近一次dump快照后,未被dump的变更次数(和save里变更计数器类似)
rdb_bgsave_in_progress:0 ##标志位,记录当前是否在创建RDB快照
rdb_last_save_time:1555246900 ##最近一次创建RDB快照文件的Unix时间戳
rdb_last_bgsave_status:ok ##标志位,记录最近一次bgsave操作是否创建成功
rdb_last_bgsave_time_sec:53 ##最近一次bgsave操作耗时秒数
rdb_current_bgsave_time_sec:-1 ##当前bgsave执行耗时秒数(-1 还没有执行)
aof_enabled:0 ##appenonly是否开启,appendonly为yes则为1,no则为0
aof_rewrite_in_progress:0 ##AOF重写是否正在进行
aof_rewrite_scheduled:0 ##AOF重写是否被RDB save操作阻塞等待
aof_last_rewrite_time_sec:-1 ##最近一次AOF重写操作耗时
aof_current_rewrite_time_sec:-1 ##当前AOF重写持续的耗时
aof_last_bgrewrite_status:ok ##最近一次AOF重写操作是否成功
aof_last_write_status:ok ##最近一次AOF写入操作是否成功
aof_current_size:9835262755 AOF 文件目前的大小 字节
aof_base_size:5503440342 服务器启动时或者 AOF 重写最近一次执行之后,AOF 文件的大小
aof_pending_rewrite:0 是否有 AOF 重写操作在等待 RDB 文件创建完毕之后执行
aof_buffer_length:0 AOF 缓冲区的大小
aof_rewrite_buffer_length:0 AOF 重写缓冲区的大小
aof_pending_bio_fsync:0
aof_delayed_fsync:0
后台 I/O 队列里面,等待执行的 fsync 调用数量
被延迟的 fsync 调用数量
# Stats
total_connections_received:6379828 ##服务器已接受的连接请求数量
total_commands_processed:81224444 ##服务器已执行的命令数量
instantaneous_ops_per_sec:1127 ##服务器每秒钟执行的命令数量
total_net_input_bytes:18584424305 ##Redis每秒网络输入的字节数
total_net_output_bytes:77018034738 ##Redis每秒网络输出的字节数
instantaneous_input_kbps:254.88 ##瞬间的Redis输入网络流量(kbps)
instantaneous_output_kbps:1013.06 ##瞬间的Redis输出网络流量(kbps)
rejected_connections:0 ##因连接数达到maxclients上限后,被拒绝的连接个数
sync_full:1 ##累计Master full sync的次数;如果值比较大,说明常常出现全量复制,就得分析原因,或调整repl-backlog-size
sync_partial_ok:0 ##累计Master psync成功的次数
sync_partial_err:0 ##累计Master pysync 出错失败的次数
expired_keys:3220662 ##因为过期而被自动删除的数据库键数量
evicted_keys:0 ##因内存used_memory达到maxmemory后,每秒被驱逐的key个数
keyspace_hits:19703662 ##查找键命中的次数
keyspace_misses:6473188 ##查找键未命中的次数
pubsub_channels:84 ##目前被订阅的频道数量
pubsub_patterns:0 ##目前被订阅的模式数量
latest_fork_usec:0 ##最近一次fork操作的耗时的微秒数(BGREWRITEAOF,BGSAVE,SYNC等都会触发fork),当并发场景fork耗时过长对服务影响较大
migrate_cached_sockets:0 ##迁移缓存的套接字
# Replication
role:master ##当前Redis的主从状态
connected_slaves:1 ##下面有几个slave
slave0:ip=172.16.3.209,port=6379,state=online,offset=7556814795556,lag=1 ##state=online Redis状态,offset=841328220554 Redis偏移量,lag=1 延时的秒数
master_repl_offset:7556814892028 ##master复制的偏移量、
repl_backlog_active:1 标志位,master是否开启了repl_backlog,有效地psync(2.8+)
repl_backlog_size:1048576 ##repl_backlog的长度(repl-backlog-size),网络环境不稳定的,建议调整大些。(主从之间如何网络延时过大可以调整此参数,避免重复的触发全量同步)
repl_backlog_first_byte_offset:7556813843453 ##repl_backlog中首字节的复制偏移位
repl_backlog_histlen:1048576 ##repl_backlog当前使用的字节数
# CPU
used_cpu_sys:815.33 ##Redis进程消耗的sys cpu
used_cpu_user:6904.00 ##Redis进程消耗的user cpu
used_cpu_sys_children:0.00 ##后台进程耗费的系统 CPU
used_cpu_user_children:0.00 ##后台进程耗费的用户 CPU
# Cluster
cluster_enabled:0 ##从服务
# Keyspace
db0:keys=34432,expires=27982,avg_ttl=166891313 ## keys=29831410 key的总数,expires=0 过期的key的数量,avg_ttl=0平均key过期的时间
数据库
2019-04-15 14:36:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
最近线上执行备份的从库时出现复制卡死现象,分析以后发现是两个死锁,show full processlist的状态如图1所示,其中,数据库版本是官方5.7.18版本,我们内部做了些许修改,但与此次死锁无关。
先说一下结论,图一中: 162线程是执行innobackup执行的flush tables with read lock; 144是SQL线程,并行复制中的Coordinator线程; 145/146是并行复制的worker线程,145/146worker线程队列中的事务可以并行执行。
144Coordinator线程分发relaylog中事务时发现这个事务不能执行,要等待前面的事务完成提交,所以处于waiting for dependent transaction to commit的状态。145/146线程和备份线程162形成死锁,145线程等待162线程 global read lock 释放,162线程占有MDL::global read lock 全局读锁,申请全局commit lock的时候阻塞等待146线程,146线程占有MDL:: commit lock,因为从库设置slave_preserve_commit_order=1,保证从库binlog提交顺序,而146线程执行事务对应的binlog靠后面,所以等待145的事务提交。最终形成了145->162->146->145的死循环,形成死锁。

同样的,图二中: 183是备份程序执行的flush tables with read lock; 165是SQL线程,并行复制的Coordinator线程; 166/167是并行复制的worker线程。
165Coordinator线程分发的事务还不能执行,进入waiting for dependent transaction to commit的状态,183、166、167三个线程形成死锁,183占有全局读锁,获取全局commit锁的时候进入阻塞,等待167释放事务涉及到表的commit锁;166,167的事务可以并行复制,167占有表级commit锁,但是事务对应的binlog在后面,阻塞等待166先提交进入waiting for preceding transaction to commit的状态;166线程事务执行时提交要获得表级commit锁,但已经被183占有,所以阻塞等待。这样形成了183->167->166->183的死锁。

三个线程相互形成死锁,在我的经验中还是很少见的,又因为涉及的MDL锁是服务层的锁,死锁检测也不会起作用。
一、死锁原因分析
1、MDL锁
参考:http://mysql.taobao.org/monthly/2015/11/04/
2 、flush tables with read lock获取两个锁
MDL::global read lock 和MDL::global commit lock,而且是显示的MDL_SHARED锁。 //Global_read_lock::lock_global_read_lock MDL_REQUEST_INIT(&mdl_request,MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT); //Global_read_lock::make_global_read_lock_block_commit MDL_REQUEST_INIT(&mdl_request,MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT);
3 、事务执行中涉及两个锁
在所有更新数据的代码路径里,除了必须的锁外,还会额外请求MDL_key::GLOBAL锁的MDL_INTENTION_EXCLUSIVE锁;在事务提交前,会先请求MDL_key::COMMIT锁的MDL_INTENTION_EXCLUSIVE锁。对于scope锁来说,IX锁和S锁是不兼容的。
4 、--slave_preserve_commit_order For multi-threaded slaves, enabling this variable ensures that transactions are externalized on theslave in the same order as they appear in the slave's relay log.
slave_preserve_commit_order=1时,relay-log中事务的提交顺序会严格按照在relay-log中出现的顺序提交。
所以,事务的执行和flush tables with read lock语句获得两个锁都不是原子的,并行复制时模式下按以下的顺序就会出现死锁。 事务A、B可以并行复制,relay-log中A在前,slave_preserve_commit_order=1 从库回放时B事务执行较快,先执行到commit,获得commit锁,并进入waiting for preceding transaction to commit的状态 执行flush tables with read lock,进入waiting for commit的状态 事务A执行。事务A如果在FTWRL语句获得global read lock锁之后执行,那么事务A就进入waiting for global read lock的状态,即第一种死锁;如果事务A在FTWRL获得global read lock之前执行,同时FTWRL获得global commit锁之后应用Xid_event提交事务,则进入 waiting for the commit lock的状态,即第二种死锁。
二、复现
理解了死锁出现的原因后,重现就简单多了。重现这个死锁步骤主要是2步:
1 、在主库构造并行复制的事务,利用debug_sync session 1 SET DEBUG_SYNC='waiting_in_the_middle_of_flush_stage SIGNAL s1 WAIT_FOR f'; insert into test.test values(13);//事务A //session 2 SET DEBUG_SYNC= 'now WAIT_FOR s1'; SET DEBUG_SYNC= 'bgc_after_enrolling_for_flush_stage SIGNAL f'; insert into test.test values(16);//事务B
2、从库执行,修改源代码,在关键地方sleep若干时间,控制并行复制的worker的执行并留出足够时间执行flush tables with read lock
修改点如下: //Xid_apply_log_event::do_apply_event_worker if(w->id==0) { std::cout<<"before commit"< sleep(20); } //pop_jobs_item if(worker->id==0) sleep(20);
开启slave以后,观察show full processlist和输出日志,在其中一个worker出现wait for preceding transaction to commit以后,执行 ftwrl,出现图1的死锁;wait for preceding transaction to commit以后,出现日志before commit之后,执行 ftwrl,出现图2的死锁。
三、如何解决
出现死锁以后如果不人工干预,IO线程正常,但是SQL线程一直卡住,一般需要等待lock-wait-timeout时间,这个值我们线上设置1800秒,所以这个死锁会产生很大影响。
那么如何解决呢?kill !kill哪个线程呢? 对图1的死锁,146处于wait for preceding transaction状态的worker线程实际处于mysql_cond_wait的状态,kill不起作用,所以只能kill 145线程或者备份线程,如果kill145worker线程,整个并行复制就报错结束,show slave status显示SQL异常退出,之后需要手动重新开启sql线程,所以最好的办法就是kill执行flush tables with read lock的线程,代价最小。 至于图2的死锁,则只能kill掉执行flush tables with read lock的线程。所以出现上述死锁时,kill执行flush tables with read lock的备份线程就恢复正常,之后择机重新执行备份即可。
四、如何避免
设置xtrabackup的kill-long-queries-timeout参数可以避免第一种死锁的出现,其实不算避免,只是出现以后xtrabackup会杀掉阻塞的执行语句的线程;但是这个参数对第二种死锁状态则无能为力了,因为xtrabackup选择杀掉的线程时,会过滤Info!=NULL。
另外还有个参数safe-slave-backup,执行备份的时候加上这个参数会停掉SQL线程,这样也肯定不会出现这个死锁,只是停掉SQL未免太暴力了,个人不提倡这样做。
可以设置slave_preserve_commit_order=0关闭从库binlog的顺序提交,关闭这个参数只是影响并行复制的事务在从库的提交顺序,对最终的数据一致性并无影响,所以如果无特别要求从库的binlog顺序必须与主库保持一致,可以设置slave_preserve_commit_order=0避免这个死锁的出现。

关于xtrabackup kill-long-query-type参数
首先说下```kill-long-queries-timeout,kill-long-query-type```参数,文档介绍如下
--KILL-LONG-QUERY-TYPE=ALL|SELECT This option specifies which types of queries should be killed to unblock the global lock. Default is “all”.
--KILL-LONG-QUERIES-TIMEOUT=SECONDS** This option specifies the number of seconds innobackupex waits between starting FLUSH TABLES WITH READ LOCK and killing those queries that block it. Default is 0 seconds, which means innobackupex will not attempt to kill any queries. In order to use this option xtrabackup user should have PROCESS and SUPER privileges.Where supported (Percona Server 5.6+) xtrabackup will automatically use Backup Locks as a lightweight alternative to FLUSH TABLES WITH READ LOCK to copy non- InnoDB data to avoid blocking DML queries that modify InnoDB tables.
参数的作用的就是在Xtrabackup执行FLUSH TABLES WITH READ LOCK以后,获得全局读锁时,如果有正在执行的事务会阻塞等待,kill-long-queries-timeout参数不为0时,xtrabackup内部创建一个线程,连接到数据库执行show full processlist,如果TIME超过kill-long-queries-timeout,会kill掉线程,kill-long-query-type设置可以kill掉的SQL类型。
官方文档介绍kill-long-query-type默认值时all,也就是所有语句都会kill掉。但在使用中发现,只设置kill-long-queries-timeout,未设置kill-long-query-type时,参数没起作用!最后查阅xtrabackup代码,如下: {"kill-long-query-type", OPT_KILL_LONG_QUERY_TYPE, "This option specifies which types of queries should be killed to " "unblock the global lock. Default is \"all\".", (uchar*) &opt_ibx_kill_long_query_type, (uchar*) &opt_ibx_kill_long_query_type, &query_type_typelib, GET_ENUM, REQUIRED_ARG, QUERY_TYPE_SELECT, 0, 0, 0, 0, 0}
心中一万头草泥马,也许只是笔误,但也太坑爹了!所以使用kill-long-query-type时一定要自己指定好类型!
五、总结
回顾这次执行备份的从库复制卡死故障,根本原因在于flush tables with read lock语句和事务执行的过程都涉及到连个锁,而且不是原子的,再加上并行复制以及设置了从库binlog的顺序提交,最终导致三个线程形成死锁。在寻找问题的解决方案中,意外发现了Xtrabackup kill-long-query-type的“秘密”,告诫我们在使用中尽量显示指定参数,一方面更准确,另一方面也便于查看。

另外,我们知道set global read_only=1语句执行中涉及到的锁和flush tables with read lock涉及的锁时一样的,也是两个MDL锁,所以理论上在并行复制的从库执行set global read_only=1语句也可能会出现上述的两个死锁,有兴趣的可以验证下
数据库
2019-04-15 11:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在MySQL中,不存在类似于SQL Server或Orcal等中的rank()函数来得到排名;所以我们需要手动地写这个rank功能。 基本知识 : sql语句中,使用 @ 来定义一个变量。如: @abc sql语句中,使用 := 来给变量赋值,: @abc :=123,则变量abc的值为123 sql语句中, if(A,B,C)表示,如果A条件成立,那么执行B,否则执行C ,如:
@abc := if(2>1,100,200)的结果是,abc的值为100。 ◎case...when...then语句
case...when...then语句有两种情况:
case情况一 (CASE 后面不带表达式): CASE WHEN expression THEN 操作1
WHEN expression THEN 操作2
.......
ELSE 操作n
END
注:自上而下,凡是走了其中一个when或者是走了else了,其他的都不再走了。
case情况二 (CASE 后面带表达式,此时WHEN 后面的则是该表达式可能的值): CASE expression
WHEN expression的值1 THEN 操作1
WHEN expression的值2 THEN 操作2
.......
ELSE 操作n
END
注:自上而下,凡是走了其中一个when或者是走了else了,其他的都不再走了。 MySQL中,手写rank示例 :
先创建一个tablle,并放入一些数据,如:
age升序排列(age相同时,排名继续增加) ,示例:
注:这里的(SELECT @curRank := 0) q 的作用是:在 同一个select语句中给变量curRank赋初始值 。效果等
同于,两个sql语句,第一个先赋值,第二个再select:
age降序排列(age相同时,排名继续增加) ,示例:
age升序排列(age相同时,排名相同;但是到下一个age不同时,排名不跳级,继续+1) ,示例一( case...when...then ):
age升序排列(age相同时,排名相同;但是到下一个age不同时,排名不跳级,继续+1) ,示例二 if(a,b,c) :
age升序排列(age相同时,排名相同;但是到下一个age不同时,排名跳级+n) ,示例:
注:如果嫌查出来的列太多了,可以再对此结果进行select,如:
^_^ 如有不当之处,欢迎指正 ^_^ 参考链接:
https://www.jianshu.com/p/bb1b72a1623e 、
https://blog.csdn.net/tjuyanming/article/details/77825875
https://zhidao.baidu.com/question/336737352.html ^_^ 本文已经被收录进《程序员成长笔记(二)》,作者JustryDeng
数据库
2019-04-15 10:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MySQL 排名、分组后组内排名、取各组的前几名

一、排名 /* 普通排名:从1开始,顺序往下排 */ SELECT cs. * , @r : = @r + 1 AS rank FROM cs,( SELECT @r : = 0 ) r ORDER BY score;
/* 并列排名:相同的值是相同的排名 */ SELECT cs. * , CASE WHEN @p = score THEN @r WHEN @p : = score THEN @r : = @r + 1 END rank FROM cs,( SELECT @r : = 0 , @p : = NULL )r ORDER BY score;

/* 并列排名:相同的值名次相同,与上例中的并列排名不同 */ SELECT city,score,rank FROM ( SELECT cs. * , @c : = IF ( @p = score, @c , @r ) AS rank, @p : = score, @r : = @r + 1 FROM cs ,( SELECT @p : = NULL , @r : = 1 , @c : = 0 )r ORDER BY score )c



二、分组后组内排名 /* 分组普通排名:顺序排名 */ SELECT city,score,rank FROM ( SELECT cs. * , IF ( @p = city, @r : = @r + 1 , @r : = 1 ) AS rank, @p : = city FROM cs,( SELECT @p : = NULL , @r : = 0 )r ORDER BY city,score )s;
/* 分组后并列排名:组内相同数值排名相同 */ SELECT city,score,rank FROM ( SELECT * , IF ( @p = city, CASE WHEN @s = score THEN @r WHEN @s : = score THEN @r : = @r + 1 END , @r : = 1 ) AS rank, @p : = city, @s : = score FROM cs,( SELECT @p : = NULL , @s : = NULL , @r : = 0 )r ORDER BY city,score )s;


三、分组后取各组的前两名 /* 取每组分数高的前两个,法一 */ SELECT city,score,rank FROM ( SELECT * , IF ( @p = city, CASE WHEN @s = score THEN @r WHEN @s : = score THEN @r : = @r + 1 END , @r : = 1 ) AS rank, @p : = city, @s : = score FROM cs,( SELECT @p : = NULL , @s : = NULL , @r : = 0 )r ORDER BY city,score DESC )s WHERE rank < 3 ;

/* 分组后取前两个,法二 */ SELECT * FROM cs c WHERE ( SELECT count ( * ) FROM cs WHERE city = c.city AND score > c.score ) < 2 ORDER BY city,score DESC

参考:
https://www.jianshu.com/p/bb1b72a1623e
http://blog.sina.com.cn/s/blog_4c197d420101e408.html



数据库
2019-04-15 09:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
地区表
通过当前节点编号查询父级所有编号集合,通过逗号进行分隔(倒序,包含自身编号) -- 通过当前节点编号查询父级所有编号集合,通过逗号进行分隔(倒序,包含自身编号) -- DROP FUNCTION `getDistrictParentIds`; CREATE FUNCTION `getDistrictParentIds`(nodeId varchar(100)) RETURNS varchar(1000) BEGIN DECLARE pid varchar(100) default ''; DECLARE ids varchar(1000) default nodeId; WHILE nodeId is not null do SET pid =(SELECT district_pid FROM district WHERE district_id = nodeId); IF pid is not null THEN SET ids = concat(ids, ',', pid); SET nodeId = pid; ELSE SET nodeId = pid; END IF; END WHILE; return ids; END
通过当前节点编号查询父级所有名称集合,通过逗号进行分隔(正序,包含自身名称) -- 通过当前节点编号查询父级所有名称集合,通过逗号进行分隔(正序,包含自身名称) -- DROP FUNCTION `getDistrictParentNames`; CREATE FUNCTION `getDistrictParentNames`(nodeId varchar(100)) RETURNS varchar(1000) BEGIN DECLARE pid varchar(100) default ''; DECLARE ids varchar(1000) default nodeId; WHILE nodeId is not null do SET pid =(SELECT district_pid FROM district WHERE district_id = nodeId); IF pid is not null THEN SET ids = concat(ids, ',', pid); SET nodeId = pid; ELSE SET nodeId = pid; END IF; END WHILE; return (select group_concat(district_name order by district_id) from district where FIND_IN_SET(district_id,ids) ); END
行业表
通过当前节点编号查询父级所有编号集合,通过逗号进行分隔(倒序,包含自身编号) -- 通过当前节点编号查询父级所有编号集合,通过逗号进行分隔(倒序,包含自身编号) -- DROP FUNCTION `getIndustryParentIds`; CREATE FUNCTION `getIndustryParentIds`(nodeId varchar(100)) RETURNS varchar(1000) BEGIN DECLARE pid varchar(100) default ''; DECLARE ids varchar(1000) default nodeId; WHILE nodeId is not null do SET pid =(SELECT industry_pid FROM industry WHERE industry_id = nodeId); IF pid is not null THEN SET ids = concat(ids, ',', pid); SET nodeId = pid; ELSE SET nodeId = pid; END IF; END WHILE; return ids; END
通过当前节点编号查询父级所有名称集合,通过逗号进行分隔(正序,包含自身名称) -- 通过当前节点编号查询父级所有名称集合,通过逗号进行分隔(正序,包含自身名称) DROP FUNCTION `getIndustryParentNames`; CREATE FUNCTION `getIndustryParentNames`(nodeId varchar(100)) RETURNS varchar(1000) BEGIN DECLARE pid varchar(100) default ''; DECLARE ids varchar(1000) default nodeId; WHILE nodeId is not null do SET pid =(SELECT industry_pid FROM industry WHERE industry_id = nodeId); IF pid is not null THEN SET ids = concat(ids, ',', pid); SET nodeId = pid; ELSE SET nodeId = pid; END IF; END WHILE; return (select group_concat(industry_name order by industry_level) from industry where FIND_IN_SET(industry_id,ids)); END
数据库
2019-04-14 22:55:08
「深度学习福利」大神带你进阶工程师,立即查看>>>
作为一款金融级分布式关系型数据库,SequoiaDB 巨杉数据库 的分布式数据库架构和面向微服务的云化产品形态,已经帮助包括民生银行、恒丰银行在内的多家大型金融客户实现了大量业务系统的底层数据库云化转型升级。

如今,大型企业的应用平台正在向微服务架构进行转型。在微服务架构下,应用程序和数据库等底层平台的关系将会被重构。 巨杉数据库,作为新一代分布式数据库,为多家大型金融客户的云化架构升级提供了极为重要的助力。
作为新一代分布式数据库,SequoiaDB巨杉数据库,其架构与功能特性需要保证在与传统数据库全兼容的基础上,拥抱微服务与云计算框架。因此,分布式数据库对于分布式交易与ACID必须保证与传统技术完全兼容。同时,在面向微服务应用开发与云计算基础架构时,新一代分布式数据库必须支持弹性扩张、资源隔离、多租户、可配置一致性、多模式(支持各类SQL协议)、集群内可配置容灾策略等一系列功能。
传统单点数据库的容量瓶颈,仅仅是分布式数据库所解决的问题之一。更重要的是在未来微服务化应用开发以及云化平台的趋势下,应用不再以“烟囱式”的中间件加数据库模式进行构建,而是采用数千甚至上万的微服务程序构建成的复杂网状模型。因此,分布式数据库需要满足以下能力,才能够满足上层应用的弹性扩展、高并发、高吞吐量、与灵活敏捷的需求。
在这些技术需求驱动下,分布式数据库核心技术能力分为两个方面,一方面是对传统技术的兼容,包括: 完整的ACID支持,事务和一致性保证; SQL的完整支持,传统数据库如MySQL/PostgreSQL的语法完全兼容。
另一方面,则是技术创新,包括: 分布式与扩展性,应对数据量的变化,实现存储层和计算层的弹性扩展; 多模式访问接口,支持多类型数据管理和多种模式的访问接口; HTAP交易/分析混合处理能力,复杂业务需求下,实现数据的物理隔离,互不干扰。
作为一款金融级分布式关系型数据库,SequoiaDB巨杉数据库的分布式数据库架构和面向微服务的云化产品形态,已经帮助包括民生银行、恒丰银行在内的多家大型金融客户实现了大量业务系统的底层数据库云化转型升级。
目前,巨杉数据库在银行生产系统单机群最大物理节点数达到135个,单集群最大存储容量超过2.1 PB,单集群最大管理数据条数1318亿条。
SequoiaDB 巨杉数据库作为一款金融级的分布式关系型数据库,在企业客户云化架构转型过程中,提供了多种重要技术能力。

数据存储资源池化
SequoiaDB 数据存储引擎采用原生分布式架构,数据完全打散在分布式节点间存储,自动化数据分布和管理,数据可以按需灵活扩展。
SequoiaDB 采用分片技术为系统提供了横向扩展机制,其分片过程对于应用程序来说完全透明。该机制解决了单台服务器硬件资源(如内存、CPU、磁盘 I/O)受限的问题,并不会增加应用程序开发的复杂性。巨杉数据库通过原生分布式架构,可以轻松实现PB级别数据管理,目前生产环境最大支持超过1500个节点集群。
SequoiaDB 巨杉数据库存储引擎也实现了multi-model多模数据管理,支持非结构化、结构化和半结构化数据全覆盖并统一管理。SequoiaDB的多模引擎设计让数据库平台场景更多样,也能符合云数据架构下对于多样化业务数据的统一管理与运维要求。
同时,在一个大型集群中,SequoiaDB提供了多维度、多层级的逻辑与物理隔离能力。在一个典型的数据资源池类型基础数据服务平台(DBaas,DataBase As A Service)中,SequoiaDB巨杉数据库能够同时服务于成百上千个不同SLA服务级别、优先级、业务特性、与数据量的应用程序,并保证应用程序之间的数据逻辑与物理隔离。
SequoiaDB 提供的“数据域(Domain)”特性,能够将整个集群在物理设备层面进行隔离,确保不同的表、表空间、与数据库实例坐落于独立的硬件设备上,保证高优先级的联机交易应用与后台统计分析相互隔离互不干扰。

数据库实例化
SequoiaDB 巨杉数据库支持数据库服务实例化。
针对微服务应用架构,用户可以在同一个集群中创建成百上千个不同的关系型数据库实例。数据库实例的访问与使用方式和传统关系型数据库100%兼容,同时其底层所使用的数据从逻辑上完全独立,每个实例拥有自己独立的权限管理、数据管控、甚至可以选择部署在独立的硬件环境或共享设备中。
目前SequoiaDB巨杉数据库支持用户创建 MySQL、PostgreSQL 与 SparkSQL 实例,同时还提供了JSON、S3对象存储以及Posix文件系统实例,充分满足用户对于结构化、半结构化、以及非结构化数据的需求。
从应用程序开发者与DBA的角度看,SequoiaDB巨杉数据库所提供的关系型数据库实例,与传统MySQL、PostgreSQL和SparkSQL保持全兼容。例如,在SequoiaDB巨杉数据库中的MySQL实例中,其所有的增删改查语法、视图、触发器、事务、甚至访问计划都与传统MySQL保持一致。
作为分布式数据库,SequoiaDB巨杉数据库的SQL实例用户不需要关心底层的数据到底被分散在一台还是多台设备中。用户可以简单创建一个分区表,向其中写入上亿条记录,其数据将会被自动分散在不同的物理设备中,对于应用程序根本无需关注分库分表,数据库自动提供分布式事务以及分布式访问等能力。

双活容灾与数据安全
双活容灾即灾备系统中使主生产端数据库和备机端数据库同时在线运行,处于可读可写状态的技术。在银行的交易系统中,双活容灾能力不仅保证数据不丢失,也保证系统在遭遇事故时能够短时间内重新上线。在正常情况下,双活架构的两个数据中心都能够同时提供业务的读写服务,而当一个中心宕机后,所有前端应用可以立刻切换至依然存活的数据中心继续使用。
SequoiaDB 巨杉数据库在内核层面实现了多种容灾方式,包括同城双活、同城双中心、同城三中心、两地三中心、与三地五中心等容灾策略。通过使用SequoiaDB巨杉数据库的容灾与高可用机制,数据中心内的服务器故障可以保证RTO与RPO均为零,而整个数据中心或同城网络故障也可以做到秒级RTO、RPO=0。

关于SequoiaDB巨杉数据库
巨杉数据库 专注新一代分布式数据库技术研发,自2011年成立以来,坚持从零开始打造分布式开源数据库引擎,是中国首家连续两年入选 Gartner 数据库报告的数据库厂商。
巨杉数据库的主要产品包括 SequoiaDB 分布式关系型数据库与 SequoiaCM 企业内容管理软件,企业级应用场景包括分布式在线交易、数据中台、分布式内容管理等。
目前巨杉数据库已在超过50家500强级别的大型商业银行核心生产业务上线,企业用户总数超过1000家。
数据库
2019-04-17 11:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以前找的,链接已经找不到啦。。。。。。。。。。。。。
数据库常用日期格式获取格式如下:
------------------------------------------------------------------------------------------------------------
Style(2位表示年份) | Style(4位表示年份) | 输入输出格式
------------------------------------------------------------------------------------------------------------
0 | 100 | mon dd yyyy hh:miAM(或PM)
------------------------------------------------------------------------------------------------------------
1 | 101 美国 | mm/dd/yy
------------------------------------------------------------------------------------------------------------
2 | 102 ANSI | yy-mm-dd
------------------------------------------------------------------------------------------------------------
3 | 103 英法 | dd/mm/yy
------------------------------------------------------------------------------------------------------------
4 | 104 德国 | dd.mm.yy
------------------------------------------------------------------------------------------------------------
5 | 105 意大利 | dd-mm-yy
------------------------------------------------------------------------------------------------------------
6 | 106 | dd mon yy
------------------------------------------------------------------------------------------------------------
7 | 107 | mon dd,yy
------------------------------------------------------------------------------------------------------------
8 | 108 | hh:mm:ss
------------------------------------------------------------------------------------------------------------
9 | 109 | mon dd yyyy hh:mi:ss:mmmmAM(或PM)
------------------------------------------------------------------------------------------------------------
10 | 110 美国 | mm-dd-yy
------------------------------------------------------------------------------------------------------------
11 | 111 日本 | yy/mm/dd
------------------------------------------------------------------------------------------------------------
12 | 112 ISO | yymmdd
------------------------------------------------------------------------------------------------------------
13 | 113 欧洲默认值 | dd mon yyyy hh:mi:ss:mmm(24小时制)
------------------------------------------------------------------------------------------------------------
14 | 114 | hh:mi:ss:mmm(24小时制)
------------------------------------------------------------------------------------------------------------
20 | 120 ODBC 规范 | yyyy-mm-dd hh:mi:ss(24小时制)
------------------------------------------------------------------------------------------------------------
21 | 121 | yyyy-mm-dd hh:mi:ss:mmm(24小时制)
------------------------------------------------------------------------------------------------------------
语句及查询结果:
SELECT CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AM
SELECT CONVERT(varchar(100), GETDATE(), 1): 05/16/06
SELECT CONVERT(varchar(100), GETDATE(), 2): 06.05.16
SELECT CONVERT(varchar(100), GETDATE(), 3): 16/05/06
SELECT CONVERT(varchar(100), GETDATE(), 4): 16.05.06
SELECT CONVERT(varchar(100), GETDATE(), 5): 16-05-06
SELECT CONVERT(varchar(100), GETDATE(), 6): 16 05 06
SELECT CONVERT(varchar(100), GETDATE(), 7): 05 16, 06
SELECT CONVERT(varchar(100), GETDATE(), 8): 10:57:46
SELECT CONVERT(varchar(100), GETDATE(), 9): 05 16 2006 10:57:46:827AM
SELECT CONVERT(varchar(100), GETDATE(), 10): 05-16-06
SELECT CONVERT(varchar(100), GETDATE(), 11): 06/05/16
SELECT CONVERT(varchar(100), GETDATE(), 12): 060516
SELECT CONVERT(varchar(100), GETDATE(), 13): 16 05 2006 10:57:46:937
SELECT CONVERT(varchar(100), GETDATE(), 14): 10:57:46:967
SELECT CONVERT(varchar(100), GETDATE(), 20): 2006-05-16 10:57:47
SELECT CONVERT(varchar(100), GETDATE(), 21): 2006-05-16 10:57:47.157
SELECT CONVERT(varchar(100), GETDATE(), 22): 05/16/06 10:57:47 AM
SELECT CONVERT(varchar(100), GETDATE(), 23): 2006-05-16
SELECT CONVERT(varchar(100), GETDATE(), 24): 10:57:47
SELECT CONVERT(varchar(100), GETDATE(), 25): 2006-05-16 10:57:47.250
SELECT CONVERT(varchar(100), GETDATE(), 100): 05 16 2006 10:57AM
SELECT CONVERT(varchar(100), GETDATE(), 101): 05/16/2006
SELECT CONVERT(varchar(100), GETDATE(), 102): 2006.05.16
SELECT CONVERT(varchar(100), GETDATE(), 103): 16/05/2006
SELECT CONVERT(varchar(100), GETDATE(), 104): 16.05.2006
SELECT CONVERT(varchar(100), GETDATE(), 105): 16-05-2006
SELECT CONVERT(varchar(100), GETDATE(), 106): 16 05 2006
SELECT CONVERT(varchar(100), GETDATE(), 107): 05 16, 2006
SELECT CONVERT(varchar(100), GETDATE(), 108): 10:57:49
SELECT CONVERT(varchar(100), GETDATE(), 109): 05 16 2006 10:57:49:437AM
SELECT CONVERT(varchar(100), GETDATE(), 110): 05-16-2006
SELECT CONVERT(varchar(100), GETDATE(), 111): 2006/05/16
SELECT CONVERT(varchar(100), GETDATE(), 112): 20060516
SELECT CONVERT(varchar(100), GETDATE(), 113): 16 05 2006 10:57:49:513
SELECT CONVERT(varchar(100), GETDATE(), 114): 10:57:49:547
SELECT CONVERT(varchar(100), GETDATE(), 120): 2006-05-16 10:57:49
SELECT CONVERT(varchar(100), GETDATE(), 121): 2006-05-16 10:57:49.700
SELECT CONVERT(varchar(100), GETDATE(), 126): 2006-05-16T10:57:49.827
SELECT CONVERT(varchar(100), GETDATE(), 130): 18 ???? ?????? 1427 10:57:49:907AM
SELECT CONVERT(varchar(100), GETDATE(), 131): 18/04/1427 10:57:49:920AM
说明:
使用 CONVERT:
CONVERT ( data_type [ ( length ) ] , expression [ , style ] )
参数
expression
是任何有效的 Microsoft? SQL Server? 表达式。。
data_type
目标系统所提供的数据类型,包括 bigint 和 sql_variant。不能使用用户定义的数据类型。
length
nchar、nvarchar、char、varchar、binary 或 varbinary 数据类型的可选参数。
style
日期格式样式,借以将 datetime 或 smalldatetime 数据转换为字符数据(nchar、nvarchar、char、varchar、nchar 或 nvarchar 数据类型);或者字符串格式样式,借以将 float、real、money 或 smallmoney 数据转换为字符数据(nchar、nvarchar、char、varchar、nchar 或 nvarchar 数据类型)。
SQL Server 支持使用科威特算法的阿拉伯样式中的数据格式。
在表中,左侧的两列表示将 datetime 或 smalldatetime 转换为字符数据的 style 值。给 style 值加 100,可获得包括世纪数位的四位年份 (yyyy)。
不带世纪数位 (yy) 带世纪数位 (yyyy)
标准
输入/输出**
- 0 或 100 (*) 默认值 mon dd yyyy hh:miAM(或 PM)
1 101 美国 mm/dd/yyyy
2 102 ANSI yy.mm.dd
3 103 英国/法国 dd/mm/yy
4 104 德国 dd.mm.yy
5 105 意大利 dd-mm-yy
6 106 - dd mon yy
7 107 - mon dd, yy
8 108 - hh:mm:ss
- 9 或 109 (*) 默认值 + 毫秒 mon dd yyyy hh:mi:ss:mmmAM(或 PM)
10 110 美国 mm-dd-yy
11 111 日本 yy/mm/dd
12 112 ISO yymmdd
- 13 或 113 (*) 欧洲默认值 + 毫秒 dd mon yyyy hh:mm:ss:mmm(24h)
14 114 - hh:mi:ss:mmm(24h)
- 20 或 120 (*) ODBC 规范 yyyy-mm-dd hh:mm:ss[.fff]
- 21 或 121 (*) ODBC 规范(带毫秒) yyyy-mm-dd hh:mm:ss[.fff]
- 126(***) ISO8601 yyyy-mm-dd Thh:mm:ss.mmm(不含空格)
- 130* Hijri**** dd mon yyyy hh:mi:ss:mmmAM
- 131* Hijri**** dd/mm/yy hh:mi:ss:mmmAM
* 默认值(style 0 或 100、9 或 109、13 或 113、20 或 120、21 或 121)始终返回世纪数位 (yyyy)。
** 当转换为 datetime时输入;当转换为字符数据时输出。
*** 专门用于 XML。对于从 datetime或 smalldatetime 到 character 数据的转换,输出格式如表中所示。对于从 float、money 或 smallmoney 到 character 数据的转换,输出等同于 style 2。对于从 real 到 character 数据的转换,输出等同于 style 1。
****Hijri 是具有几种变化形式的日历系统,Microsoft? SQL Server? 2000 使用其中的科威特算法。
重要 默认情况下,SQL Server 根据截止年份 2049 解释两位数字的年份。即,两位数字的年份 49 被解释为 2049,而两位数字的年份 50 被解释为 1950。许多客户端应用程序(例如那些基于 OLE 自动化对象的客户端应用程序)都使用 2030 作为截止年份。SQL Server 提供一个配置选项("两位数字的截止年份"),借以更改 SQL Server 所使用的截止年份并对日期进行一致性处理。然而最安全的办法是指定四位数字年份。
当从 smalldatetime 转换为字符数据时,包含秒或毫秒的样式将在这些位置上显示零。当从 datetime 或 smalldatetime 值进行转换时,可以通过使用适当的 char 或 varchar 数据类型长度来截断不需要的日期部分。
数据库
2019-04-17 11:20:00
「深度学习福利」大神带你进阶工程师,立即查看>>>


1. 索引类型(normal,unique,full text)和索引方法(Hash,BTree)的区别
转自:
https://blog.csdn.net/weixin_41172767/article/details/83012166
1.1 索引类型

1.2 索引方法


2. 索引性能分析
转自:
https://www.cnblogs.com/balala/p/5601308.html
数据库
2019-04-16 22:59:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Redis 简介
REmote DIctionary Server(Redis) 是一个由SalvatoreSanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是字符串(String), 哈希(Map), 列表(list), 集合(sets) 和有序集合(sorted sets)等类型。
Redis特点
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。 Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 Redis支持数据的备份,即master-slave模式的数据备份。
Redis 优势
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis 还支持 publish/subscribe, 队列,key 过期等等特性。
Redis对象类型简介 Redis是一种key/value型数据库,其中,每个key和value都是使用对象表示的。 比如,我们执行以下代码: redis> SET message "hello redis"
其中的key是message,是一个包含了字符串"message"的对象。而value是一个包含了"hello redis"的对象。 Redis共有五种对象的类型,分别是:
类型常量 对象的名称 REDIS_STRING 字符串对象
REDIS_LIST 列表对象
REDIS_HASH 哈希对象
REDIS_SET 集合对象
REDIS_ZSET
Redis中的一个对象的结构体表示如下:
有序集合对象

typedef struct redisObject { // 类型 unsigned type:4; // 编码方式 unsigned encoding: 4; // 引用计数 int refcount; // 指向对象的值 void *ptr; } robj;
type表示了该对象的对象类型,即上面五个中的一个。但为了提高存储效率与程序执行效率,每种对象的底层数据结构实现都可能不止一种。encoding就表示了对象底层所使用的编码。 Redis对象底层数据结构
编码常量 编码所对应的底层数据结构
REDIS_ENCODING_INT long 类型的整数
REDIS_ENCODING_EMBSTR embstr 编码的简单动态字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET
REDIS_ENCODING_SKIPLIST
整数集合
跳跃表和字典
字符串对象
字符串对象的编码可以是int、raw或者embstr 如果一个字符串的内容可以转换为long,那么该字符串就会被转换成为long类型,对象的ptr就会指向该long,并且对象类型也用int类型表示。 普通的字符串有两种,embstr和raw。embstr应该是Redis 3.0新增的数据结构,在2.8中是没有的。如果字符串对象的长度小于39字节,就用embstr对象。否则用传统的raw对象。 #define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 44 robj *createStringObject(char *ptr, size_t len) { if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) return createEmbeddedStringObject(ptr,len); else return createRawStringObject(ptr,len); }
embstr的好处有如下几点: embstr的创建只需分配一次内存,而raw为两次(一次为 sds 分配对象,另一次为objet分配对象,embstr省去了第一次)。 相对地,释放内存的次数也由两次变为一次。 embstr的objet和sds放在一起,更好地利用缓存带来的优势。
raw和embstr的区别可以用下面两幅图所示:
列表对象 列表对象的编码可以是ziplist或者linkedlist ziplist是一种压缩链表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。如下图所示,对象结构中ptr所指向的就是一个ziplist整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。
linkedlist是一种双向链表。它的结构比较简单,节点中存放pre和next两个指针,还有节点相关的信息。当每增加一个node的时候,就需要重新malloc一块内存。
哈希对象 哈希对象的底层实现可以是ziplist或者hashtable。 ziplist中的哈希对象是按照key1,value1,key2,value2这样的顺序存放来存储的。当对象数目不多且内容不大时,这种方式效率是很高的。
hashtable的是由dict这个结构来实现的, dict是一个字典,其中的指针dicht ht[2] 指向了两个哈希表 typedef struct dict { dictType *type; void *privdata; dictht ht[2]; long rehashidx; /* rehashing not in progress if rehashidx == -1 */ int iterators; /* number of iterators currently running */ } dict; typedef struct dictht { dictEntry **table; unsigned long size; unsigned long sizemask; unsigned long used; } dictht;
dicht[0] 是用于真正存放数据,dicht[1]一般在哈希表元素过多进行rehash的时候用于中转数据。 dictht中的table用语真正存放元素了,每个key/value对用一个dictEntry表示,放在dictEntry数组中。
集合对象 集合对象的编码可以是intset或者hashtable intset是一个整数集合,里面存的为某种同一类型的整数,支持如下三种长度的整数: #define INTSET_ENC_INT16 (sizeof(int16_t)) #define INTSET_ENC_INT32 (sizeof(int32_t)) #define INTSET_ENC_INT64 (sizeof(int64_t))
intset是一个有序集合,查找元素的复杂度为O(logN),但插入时不一定为O(logN),因为有可能涉及到升级操作。比如当集合里全是int16_t型的整数,这时要插入一个int32_t,那么为了维持集合中数据类型的一致,那么所有的数据都会被转换成int32_t类型,涉及到内存的重新分配,这时插入的复杂度就为O(N)了。 intset不支持降级操作。 有序集合对象 有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合。 ziplist作为集合和作为哈希对象是一样的,member和score顺序存放。按照score从小到大顺序排列 skiplist是一种跳跃表,它实现了有序集合中的快速查找,在大多数情况下它的速度都可以和平衡树差不多。但它的实现比较简单,可以作为平衡树的替代品。它的结构比较特殊。下面分别是跳跃表skiplist和它内部的节点skiplistNode的结构体: /* * 跳跃表 */ typedef struct zskiplist { // 头节点,尾节点 struct zskiplistNode *header, *tail; // 节点数量 unsigned long length; // 目前表内节点的最大层数 int level; } zskiplist; /* ZSETs use a specialized version of Skiplists */ /* * 跳跃表节点 */ typedef struct zskiplistNode { // member 对象 robj *obj; // 分值 double score; // 后退指针 struct zskiplistNode *backward; // 层 struct zskiplistLevel { // 前进指针 struct zskiplistNode *forward; // 这个层跨越的节点数量 unsigned int span; } level[]; } zskiplistNode;
head和tail分别指向头节点和尾节点,然后每个skiplistNode里面的结构又是分层的(即level数组) 用图表示,大概是下面这个样子:
总结
以上简单介绍了Redis的简介,特性以及五种对象类型和五种对象类型的底层实现。事实上,Redis的高效性和灵活性正是得益于同一个对象类型采用不同的底层结构,并且在必要的时候对二者进行转换,还有就是各种底层结构对内存的合理利用。
Worktile 官网: worktile.com
本文作者:Worktile高级工程师 龚林杰
文章首发于「 Worktile官方博客 」,转载请注明出处。
数据库
2019-04-16 17:57:00