数据专栏

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

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在Oracle8I的版本中,Oracle推出了一种全新的表空间管理方式:本地化管理的表空间。所谓本地化管理,就是指Oracle不再利用数据字典表
来记录Oracle表空间里面的区的使用状况,而是在每个表空间的数据文件的头部加入了一个位图区,在其中记录每个区的使用状况。每当一个
区被使用,或者被释放以供重新使用时,Oracle都会更新数据文件头部的这个记录,反映这个变化。本地化管理的表空间的创建过程:
语法:CREATE TABLESPACE 表空间名字
DATAFILE '数据文件详细信息'
[EXTENT MANAGEMENT { LOCAL
{AUTOALLOCATE | UNIFORM [SIZE INTETER [K|M] ] } } ]
关键字EXTENT MANAGEMENT LOCAL 指定这是一个本地化管理的表空间。对于系统表空间,只能在创建数据库的时候指定EXTENT MANGEMENT
LOCAL,因为它是数据库创建时建立的第一个表空间。
在8i中,字典管理还是默认的管理方式,当选择了LOCAL关键字,即表明这是一个本地管理的表空间。当然还可以继续选择更细的管理方式:是
AUTOALLOCATE 还是 UNIFORM.。若为AUTOALLOCATE,则表明让Oracle来决定区块的使用办法;若选择了UNIFORM,则还可以详细指定每个区块的
大小,若不加指定,则为每个区使用1M大小。
Oracle之所以推出了这种新的表空间管理方法,让我们来看一下这种表空间组织方法的优点:
1. 本地化管理的表空间避免了递归的空间管理操作。而这种情况在数据字典管理的表空间是经常出现的,当表空间里的区的使用状况发生改变
时,数据字典的表的信息发生改变,从而同时也使用了在系统表空间里的回滚段。
2. 本地化管理的表空间避免了在数据字典相应表里面写入空闲空间、已使用空间的信息,从而减少了数据字典表的竞争,提高了空间管理的并
发性。
3. 区的本地化管理自动跟踪表空间里的空闲块,减少了手工合并自由空间的需要。
4. 表空间里的区的大小可以选择由Oracle系统来决定,或者由数据库管理员指定一个统一的大小,避免了字典表空间一直头疼的碎片问题。
5. 从由数据字典来管理空闲块改为由数据文件的头部记录来管理空闲块,这样避免产生回滚信息,不再使用系统表空间里的回滚段。因为由数
据字典来管理的话,它会把相关信息记在数据字典的表里,从而产生回滚信息。
由于这种表空间的以上特性,所以它支持在一个表空间里边进行更多的并发操作,并减少了对数据字典的依赖。
数据库
2019-07-17 17:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
代码下载地址: https://github.com/tazhigang/big-data-github.git
一、项目结构
二、pom.xml mongodb-parent----->pom.xml 4.0.0 com.ittzg.mongodb mongodb-parent pom 1.0-SNAPSHOT mongodb-client springboot-mongodb com.alibaba fastjson 1.2.58 org.apache.maven.plugins maven-compiler-plugin 1.8 1.8 UTF-8 mongodb-client----->pom.xml mongodb-parent com.ittzg.mongodb 1.0-SNAPSHOT 4.0.0 mongodb-client org.mongodb mongodb-driver 3.11.0-beta4 junit junit 4.12
三、代码 MDBClientUtil.java package com.ittzg.mongodb; import com.alibaba.fastjson.JSON; import com.mongodb.MongoClient; import com.mongodb.client.FindIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.Filters; import org.bson.Document; import org.junit.Before; import org.junit.Test; /** * @email: tazhigang095@163.com * @author: ittzg * @date: 2019/7/11 23:13 */ public class MDBClientUtil { MongoDatabase db = null; MongoCollection usersDB =null; @Before public void init(){ //连接mongodb服务器 MongoClient mongoDBClient = new MongoClient("127.0.0.1", 27017); //建立客户端连接 db = mongoDBClient.getDatabase("test"); //获取数据库test usersDB = db.getCollection("users"); //获取users集合的操作 } @Test public void testInsert(){ Document doc = new Document("name", "汤姆"); doc.append("age",18); doc.append("shuxin","老鼠"); usersDB.insertOne(doc); //插入一条数据 } /** * 添加一个对象 */ @Test public void testInserObj(){ User user = new User("杰瑞", 21, "猫"); String userJsonStr = JSON.toJSONString(user); //将对象转为json格式字符串 Document doc = Document.parse(userJsonStr); //解析成文档对象 usersDB.insertOne(doc); } /** * 查询一个对象 */ @Test public void testFind(){ Document doc = usersDB.find().first();//查出users中的第一个文档 User user = JSON.parseObject(doc.toJson(), User.class);//将其反序列化成对象 System.out.println(user); } /** * 删除一个文档 */ @Test public void testDelete(){ usersDB.deleteOne(Filters.eq("name","杰瑞")); } /** * 更新一个文档 */ @Test public void testUpdate(){ FindIterable documents = usersDB.find(); for (Document document : documents) { System.out.println(document.toJson()); } usersDB.updateOne(Filters.eq("name","杰瑞"), new Document("$set" , new Document("like","play2")) ); System.out.println("==============="); FindIterable documents2 = usersDB.find(); for (Document document : documents2) { System.out.println(document.toJson()); } } } User.java package com.ittzg.mongodb; import org.bson.types.ObjectId; /** * @email: tazhigang095@163.com * @author: ittzg * @date: 2019/7/11 23:26 */ public class User { private ObjectId _id; private String name; private Integer age; private String shuxin; public User(String name, Integer age, String shuxin) { this.name = name; this.age = age; this.shuxin = shuxin; } public User() { } public ObjectId get_id() { return _id; } public void set_id(ObjectId _id) { this._id = _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getShuxin() { return shuxin; } public void setShuxin(String shuxin) { this.shuxin = shuxin; } @Override public String toString() { return "User{" + "_id=" + _id + ", name='" + name + '\'' + ", age=" + age + ", shuxin='" + shuxin + '\'' + '}'; } }
数据库
2019-07-13 22:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
原创作者: 钟悦 关于作者
钟 悦 - 资深DBLE用户
某宇宙行资深架构师,在大型重点项目中使用 DBLE。
常年与 MySQL 纠缠不清,经常运用技术处理大企业病的技术or非技术问题的一个挨踢从业者。

当分片索引不是纯整型的字符串时,只接受整型的内置 hash 算法是无法使用的。为此,stringhash 按照用户定义的起点和终点去截取分片索引字段中的部分字符,根据当中每个字符的二进制 unicode 值换算出一个长整型数值,然后就直接调用内置 hash 算法求解分片路由:先求模得到逻辑分片号,再根据逻辑分片号直接映射到物理分片。
用户需要在 rule.xml 中定义 partitionLength[] 和 partitionCount[] 两个数组和 hashSlice 二元组。 在 DBLE 的启动阶段,点乘两个数组得到模数,也是逻辑分片的数量 并且根据两个数组的叉乘,得到各个逻辑分片到物理分片的映射表(物理分片数量由 partitionCount[] 数组的元素值之和) 此外根据 hashSlice 二元组,约定把分片索引值中的第 4 字符到第 5 字符(字符串以 0 开始编号,编号 3 到编号 4 等于第 4 字符到第 5 字符)字符串用于 “字符串->整型” 的转换 在 DBLE 的运行过程中,用户访问使用这个算法的表时,WHERE 子句中的分片索引值会被提取出来,取当中的第 4 个字符到第 5 字符,送入下一步 设置一个初始值为 0 的累计值,逐个取字符,把累计值乘以 31,再把这个字符的 unicode 值当成长整型加入到累计值中,如此类推直至处理完截取出来的所有字符,此时的累计值就能够代表用户的分片索引值,完成了 “字符串->整型” 的转换 对上一步的累计值进行求模,得到逻辑分片号 再根据逻辑分片号,查映射表,直接得到物理分片号

与MyCat的类似分片算法对比
两种算法在string转化为int之后,和 hash 分区算法相同,区别也继承了 hash 算法的区别。

开发注意点
【分片索引】1. 必须是字符串
【分片索引】2. 最大物理分片配置方法是,让 partitionCount[] 数组和等于 2880
例如: 1 2880
1,1 1440,1440
【分片索引】3. 最小物理分片配置方法是,让 partitionCount[] 数组和等于 1
例如 2880 1
【分片索引】4. partitionLength 和 partitionCount 被当做两个逗号分隔的一维数组,它们之间的点乘必须在 [1, 2880] 范围内
【分片索引】5. partitionLength 和 partitionCount 的配置对顺序敏感 512,256 1,2
256,512 2,1
是不同的分片结果
【分片索引】6. 分片索引字段长度小于用户指定的截取长度时,截取长度会安全减少到符合分片索引字段长度
【数据分布】1. 分片索引字段截取越长则越有利于数据均匀分布
【数据分布】2. 分片索引字段的内容重复率越低则越有利于数据均匀分布

运维注意点
【扩容】1. 预先过量分片,并且不改变 partitionCount 和 partitionLength 点乘结果,也不改变截取设置 hashSlice 时,可以避免数据再平衡,只需进行涉及数据的迁移
【扩容】2. 若需要改变 partitionCount 和 partitionLength 点乘结果或改变截取设置 hashSlice 时,需要数据再平衡
【缩容】1. 预先过量分片,并且不改变 partitionCount 和 partitionLength 点乘结果,也不改变截取设置 hashSlice 时,可以避免数据再平衡,只需进行涉及数据的迁移
【缩容】2. 若需要改变 partitionCount 和 partitionLength 点乘结果或改变截取设置 hashSlice 时,需要数据再平衡

配置注意点
【配置项】1. 在 rule.xml 中,可配置项为
【配置项】2.在 rule.xml 中配置 标签
内容形式为: <物理分片持有的虚拟分片数>[,<物理分片持有的虚拟分片数>,...<物理分片持有的虚拟分片数>]
物理分片持有的虚拟分片数必须是整型,物理分片持有的虚拟分片数从左到右与同顺序的物理分片数对应,partitionLength 和partitionCount 的点乘结果必须在 [1, 2880] 范围内
【配置项】3. 在 rule.xml 中配置 标签 内容形式为: <物理分片数>[,<物理分片数>,...<物理分片数>]
其中物理分片数必须是整型,物理分片数按从左到右的顺序与同顺序的物理分片持有的虚拟分片数对应,物理分片的编号从左到右连续递进,partitionLength 和 partitionCount 的点乘结果必须在 [1, 2880] 范围内
【配置项】4. partitionLength 和 partitionCount 的语义是:持有partitionLength[i] 个虚拟分片的物理分片有 partitionCount[i] 个
例如 512,256 1,2
语义是持有 512 个逻辑分片的物理分片有 1 个,紧随其后,持有 256 个逻辑分片的物理分片有 2 个
【配置项】5.partitionLength 和 partitionCount 都对书写顺序敏感,
例如 512,256 1,2
分片结果是第一个物理分片持有头512个逻辑分片,第二个物理分片持有紧接着的256个逻辑分片,第三个物理分片持有最后256个逻辑分片,相对的 256,512 2,1
分片结果则是第一个物理分片持有头 256 个逻辑分片,第二个物理分片持有紧接着的 256 个逻辑分片,第三个物理分片持有最后 512 个逻辑分片
【配置项】6.partitionLength[] 的元素全部为 1 时,这时候partitionCount 数组和等于 partitionLength 和 partitionCount 的点乘,物理分片和逻辑分片就会一一对应,该分片算法等效于直接取余
【配置项】7.在 rule.xml 中配置标签,从分片索引字段的第几个字符开始截取到第几个字符: 若希望从首字符开始截取 k 个字符( k 为正整数),配置的内容形式可以为“ 0 : k ”、“ k ”或“ : k ”; 若希望从末字符开始截取 k 个字符( k 为正整数),则配置的内容形式可以为“ -k : 0 ”、“ -k ”或“ -k : ”; 若希望从头第 m 个字符起算截取 n 个字符( m 和 n 都是正整数),则先计算出 i = m - 1 和 j = i + n - 1,配置的内容形式为“ i : j ”; 若希望从尾第 m 个字符起算截取从尾算起的 n 个字符( m 和 n 都是正整数),则先计算出 i = -m + n - 1,配置的内容形式可以为“ -m : i ”; 若希望不截取,则配置的内容形式可以为“ 0 : 0 ”、“ 0 : ”、“ : 0 ”或 “ : ”
数据库
2019-07-09 14:01:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
直接调试,会报以下错误:
ORA-16224: Database Guard is enabled
我们可以通过设置
ALTER DATABASE GUARD none;
的方式来解决,
甚至可以针对 某些表进行设置,不同步,
exec dbms_logstdby.skip('DML','HR','DEPT',NULL);
后面又想同步了,执行以下语句即可.
EXECUTE DBMS_LOGSTDBY.UNSKIP (STMT => 'DML',SCHEMA_NAME => 'HR', OBJECT_NAME => 'DEPT');
数据库
2019-07-06 17:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
节点 (标签)
() (n) (:User) (n:User) (u:User{id:1,name:'may'})
关系 (类型)
-- -[r]- -[:ORG]- -[r:ORG]- -[r:ORG{type:'aaa'}]- //无向关系
--> -[r]-> -[:ORG]-> -[r:ORG]-> -[r:ORG{type:'aaa'}]-> //有向关系
模式变量
p = (:User) -[:ORG]-> (:User)
数据类型
数值型 如: 1,2,
字符串 如:‘a’ , “b”
布尔型 如: true ,false
节点 如: ()
关系 如: --> , -- ,<--
路径 如: ()--() , ()<--()
map 如: {id:1}
list 如:[1,2,3]
null 任何类型
运算符
数学: + - * / % ^
比较: = <> < > <= >= IS NULL IS NOT NULL
布尔: AND OR XOR NOT
字符: + =~
列表: IN
链式比较: mathc(n) where 21 < n.age < 30 return n

注意与null 比较都返回 null:
null = null 结果 null
null <> null 结果 null // 注释
// 语句
读语句: match / optional match / where / start / aggregation / load csv
写语句:create / merge / set / delete / remove / foreach / create unique
通用语句:return / order by / limit / skip / with / unwind / all union / call



数据库
2019-07-05 23:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
备份归档日志方式:
单独备份归档日志:backup archivelog all
在执行备库时一起备份归档日志:backup database plus archivelog;
这两种方式有什么区别呢?
运行backup archivelog all 命令时执行的步骤:
1.alter system archive log current; 归档当前日志
2.backup archivelog all ; 备份所有归档日志
而运行backup database plus archivelog,的运行步骤:
1.alter system archive log current; 归档当前日志
2.backup archivelog all; 备份所有归档日志
3.backup database; 备份数据库
4.alter system archive log current; 归档当前日志
5.backup archivelog recently generated ; 备份刚生成的归档日志
删除归档日志一般在备份归档日志同时加上一个参数:delete all input,
例如:backup database plus archivelog delete all input;执行此命令是会将所有的归档日志进行备份,并且在备份的同时删除已备份的归档日志。
所以如果在RMAN备份脚本中有plus archivelog 参数就无需在备份归档日志之前执行: sql 'alter system archive log current';
数据库
2019-07-05 17:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者:satoru
TiDB Binlog 架构简介
TiDB Binlog 主要由 Pump 和 Drainer 两部分组成,其中 Pump 负责存储 TiDB 产生的 binlog 并向 Drainer 提供按时间戳查询和读取 binlog 的服务,Drainer 负责将获取后的 binlog 合并排序再以合适的格式保存到对接的下游组件。
在《 TiDB Binlog 架构演进与实现原理 》一文中,我们对 TiDB Binlog 整体架构有更详细的说明,建议先行阅读该文。
相关源码仓库
TiDB Binlog 的实现主要分布在 tidb-tools 和 tidb-binlog 两个源码仓库中,我们先介绍一下这两个源码仓库中的关键目录。
1. tidb-tools
Repo: https://github.com/pingcap/tidb-tools/
这个仓库除了 TiDB Binlog 还有其他工具的组件,在这里与 TiDB Binlog 关系最密切的是 tidb-binlog/pump_client 这个 package。 pump_client 实现了 Pump 的客户端接口,当 binlog 功能开启时,TiDB 使用它来给 pump 发送 binlog 。
2. tidb-binlog
Repo: https://github.com/pingcap/tidb-binlog
TiDB-Binlog 的核心组件都在这个仓库,下面是各个关键目录: cmd :包含 pump , drainer , binlogctl , reparo , arbiter 等 5 个子目录,分别对应 5 个同名命令行工具。这些子目录下面的 main.go 是对应命令行工具的入口,而主要功能的实现则依赖下面将介绍到的各个同名 packages。 pump :Pump 源码,主要入口是 pump.NewServer 和 Server.Start ;服务启动后,主要的功能是 WriteBinlog (面向 TiDB/pump_client ) 和 PullBinlogs (面向 Drainer )。 drainer :Drainer 源码,主要入口是 drainer.NewServer 和 Server.Start ;服务启动后,Drainer 会先找到所有 Pump 节点,然后调用 Pump 节点的 PullBinlogs 接口同步 binlog 到下游。目前支持的下游有:mysql/tidb,file(文件增量备份),kafka 。 binlogctl :Binlogctl 源码,实现一些常用的 Binlog 运维操作,例如用 -cmd pumps 参数可以查看当前注册的各个 Pump 节点信息,相应的实现就是 QueryNodesByKind 。 reparo :Reparo 源码,实现从备份文件(Drainer 选择 file 下游时保存的文件)恢复数据到指定数据库的功能。 arbiter :Arbiter 源码,实现从 Kafka 消息队列中读取 binlog 同步到指定数据库的功能,binlog 在消息中以 Protobuf 格式编码。 pkg :各个工具公用的一些辅助类的 packages,例如 pkg/util 下面有用于重试函数执行的 RetryOnError ,pkg/version 下面有用于打印版本信息的 PrintVersionInfo 。 tests :集成测试。
启动测试集群
上个小节提到的 tests 目录里有一个名为 run.sh 脚本,我们一般会使用 make integration_test 命令,通过该脚本执行一次完整的集成测试,不过现在我们先介绍如何用它来启动一个测试集群。
启动测试集群前,需要在 bin 目录下准备好相关组件的可执行文件: pd-server:下载链接( Linux / macOS ) tikv-server:下载链接( Linux / macOS ) tidb-server:下载链接( Linux / macOS ) pump , drainer , binlogctl :在 tidb-binlog 目录执行 make build
脚本依赖 MySQL 命令行客户端来确定 TiDB 已经成功启动,所以我们还需要安装一个 MySQL 客户端。
准备好以上依赖,运行 tests/run.sh --debug ,就可以启动一个测试集群。启动过程中会输出一些进度信息,看到以下提示就说明亿成功启动: Starting Drainer... You may now debug from another terminal. Press [ENTER] to continue.
测试集群包含以下服务: 2 个作为上游的 TiDB 实例,分别使用端口 4000 和 4001 1 个作为下游的 TiDB 实例, 使用端口 3306 PD 实例,使用端口 2379 TiKV,使用端口 20160 Pump ,使用端口 8250 Drainer,使用端口 8249
使用 MySQL 客户端连接任意一个上游 TiDB,可以用 SHOW PUMP STATUS 和 SHOW DRAINER STATUS 查询对应工具的运行状态,例如:
通过 binlogctl 也可以查询到同样的信息,例如: $ bin/binlogctl -pd-urls=localhost:2379 -cmd pumps [2019/06/26 14:36:29.158 +08:00] [INFO] [nodes.go:49] ["query node"] [type=pump] [node="{NodeID: pump:8250, Addr: 127.0.0.1:8250, State: online, MaxCommitTS: 409345979065827329, UpdateTime: 2019-06-26 14:36:27 +0800 CST}"]
接下来我们可以用 MySQL 客户端连接上端口为 4000 或 4001 的 TiDB 数据库,插入一些测试数据。
上图的演示中创建了一个叫 hello_binlog 的 database,在里面新建了 user_info 表并插入了两行数据。完成上述操作后,就可以连接到端口为 3306 的下游数据库验证同步是否成功:
小结
本文简单介绍了 tidb-tools 和 tidb-binlog 及其中的目录,并且展示了如何启动测试集群。有了这些准备,大家就可以进一步了解各个功能的源码实现。下篇文章将会介绍 pump_client 如何将一条 binlog 发送往 Pump Server。
原文阅读 : https://www.pingcap.com/blog-cn/tidb-binlog-source-code-reading-2/
数据库
2019-07-05 10:53:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
原创: 杨奇龙 作者简介
杨奇龙,网名“北在南方”,7年DBA老兵,目前任职于杭州有赞科技DBA,主要负责数据库架构设计和运维平台开发工作,擅长数据库性能调优、故障诊断。
一 序言
在运维MySQL数据库时,DBA会接收到比较多关于主备延时的报警: check_ins_slave_lag (err_cnt:1)critical-slavelag on ins:3306=39438
相信slave延迟是MySQL DBA遇到的一个老生常谈的问题了。我们先来分析一下slave延迟带来的风险: 异常情况下,主从HA无法切换,HA 软件需要检查数据的一致性,延迟时,主备不一致 备库复制hang会导致备份失败(flush tables with read lock会900s超时) 以slave为基准进行的备份,数据不是最新的,而是延迟
本文主要探讨如何解决 ,如何规避slave延迟的问题,接下来我们要分析一下导致备库延迟的几种原因。
二 slave延迟的场景以及解决方法
1 无主键、无索引或索引区分度不高
有如下特征 a. show slave status 显示position一直没有变 b. show open tables 显示某个表一直是 in_use 为 1 c. show create table 查看表结构可以看到无主键,或者无任何索引,或者索引区分度很差。
解决方法: a. 找到表区分度比较高的几个字段, 可以使用这个方法判断: select count(*) from xx; select count(*) from (select distinct xx from xxx) t; 如果2个查询count(*)的结果差不多,说明可以对这些字段加索引 b. 备库stop slave; 可能会执行比较久,因为需要回滚事务。 c. 备库 set global slave_rows_search_algorithms='TABLE_SCAN,INDEX_SCAN,HASH_SCAN'; 或者 set sql_log_bin=0; alter table xx add key xx(xx); d. 备库start slave 如果是innodb,可以通过show innodb status来查看 rows_inserted,updated,deleted,selected这几个指标来判断。 如果每秒修改的记录数比较多,说明复制正在以比较快的速度执行。
其实针对无索引的表,可以直接调整从库上的参数 slave_rows_search_algorithms。
2 主库上有大事务,导致从库延时
现象解析binlog发现类似于下图的情况看:
**解决方法:**​​​​​​​ 事前防范,与开发沟通,增加缓存,异步写入数据库,减少业务直接对db的大事务写入。 事中补救,调整数据库中io相关的参数比如innodb_flush_log_at_trx_commit和sync_binlog 或者打开并行复制功能。
3 主库写入频繁,从库压力跟不上导致延时
此类原因的主要现象是数据库的IUD操作非常多,slave由于sql_thread单线程的原因追不上主库。
解决方法: a 升级从库的硬件配置,比如ssd,fio. b 使用@丁奇的预热工具-relay fetch 在备库sql线程执行更新之前,预先将相应的数据加载到内存中,并不能提高sql_thread线程执行sql的能力, 也不能加快io_thread线程读取日志的速度。 c 使用多线程复制 阿里MySQL团队实现的方案--基于行的并行复制。 该方案允许对同一张表进行修改的两个事务并行执行,只要这两个事务修改了表中的不同的行。 这个方案可以达到事务间更高的并发度,但是局限是必须使用 Row格式的binlog。 因为只有使用Row格式的binlog才可以知道一个事务所修改的行的范围,而使用Statement格式的binlog只能知道修改的表对象。
4 大量myisam表,在备份时导致slave延迟
由于xtrabackup工具备份到最后会执行flash tables with read lock ,对数据库进行锁表以便进行一致性备份,然后对于myisam表锁,会阻碍salve_sql_thread停滞运行进而导致hang。
该问题目前的比较好的解决方式是修改表结构为innodb存储引擎的表。
三 MySQL的改进
为了解决复制延迟的问题,MySQL也在不遗余力的解决主从复制的性能瓶颈,研发高效的复制算法。
1 基于组提交的并行复制
MySQL的复制机制大致原理是:slave通过io_thread 将主库的binlog拉到从库并写入relay log,由SQL thread读出来relay log并进行重放。当主库写入并发写入压力很大,也即N:1的情形,slave就可能会出现延迟。MySQL 5.6版本提供并行复制功能,slave复制相关的线程由io_thread,coordinator_thread,worker构成,其中: coordinator_thread 负责读取relay log,将读取的binlog event以事务为单位分发到各个 worker thread进行执行,并在必要时执行binlog event,比如是DDL或者跨库事务的时候。 worker_thread :执行分配到的binlog event,各个线程之间互不影响,具体worker_thread的个数由slave_parallel_workers决定。
需要注意的是dbname与worker线程的绑定信息在一个hash表中进行维护,hash表以entry为单位,entry中记录当前entry所代表的数据库名,有多少个事务相关的已被分发,执行这些事务的worker thread等信息。
分配线程是以数据库名进行分发的,当一个实例中只有一个数据库的时候,不会对性能有提高,相反,由于增加额外的操作,性能还会有一点回退。
MySQL 5.7版本提供基于组提交的并行复制,通过设置如下参数来启用并行复制。 slave_parallel_workers>0
global.slave_parallel_type='LOGICAL_CLOCK'
即主库在ordered_commit中的第二阶段, 将同一批commit的 binlog打上一个相同的seqno标签,同一时间戳的事务在备库是可以同时执行的,因此大大简化了并行复制的逻辑 ,并打破了相同DB不能并行执行的限制。备库在执行时,具有同一seqno的事务在备库可以并行的执行,互不干扰,也不需要绑定信息,后一批seqno的事务需要等待前一批相同seqno的事务执行完后才可以执行。这样的实现方式大大提高了slave应用relaylog的速度。 #默认是DATABASE模式的,需要调整或者在my.cnf中配置 mysql> show variables like 'slave_parallel%'; +------------------------+----------+ | Variable_name | Value | +------------------------+----------+ | slave_parallel_type | DATABASE | | slave_parallel_workers | 4 | +------------------------+----------+ 2 rows in set (0.00 sec) mysql> STOP SLAVE SQL_THREAD; Query OK, 0 rows affected (0.00 sec) mysql> set global slave_parallel_type='LOGICAL_CLOCK'; Query OK, 0 rows affected (0.00 sec) mysql> START SLAVE SQL_THREAD; Query OK, 0 rows affected (0.01 sec)
启用并行复制之后查看processlist,系统多了四个线程Waiting for an event from Coordinator (手机用户推荐横屏查看) mysql> show processlist; +----+-------------+-----------------+------+---------+------+--------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------------+-----------------+------+---------+------+--------------------------------------------------------+------------------+ | 9 | root | localhost:40270 | NULL | Query | 0 | starting | show processlist | | 10 | system user | | NULL | Connect | 1697 | Waiting for master to send event | NULL | | 31 | system user | | NULL | Connect | 5 | Slave has read all relay log; waiting for more updates | NULL | | 32 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL | | 33 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL | | 34 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL | | 35 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL | +----+-------------+-----------------+------+---------+------+--------------------------------------------------------+------------------+ 7 rows in set (0.00 sec)
核心参数: binlog_group_commit_sync_no_delay_count:一组里面有多少事物才提交,单位 (ms) binlog_group_commit_sync_delay:等待多少时间后才进行组提交
2 基于写集合的并行复制
其实从MySQL5.7.22就提供基于 write set 的复制优化策略。WriteSet并行复制的思想是: 不同事务的不同记录不重叠,则都可在从机上并行回放,可以看到并行的力度从组提交细化为记录级 。不想看官方文档的话,大家可以看看姜老师的文章: 速度提升5~10倍,基于WRITESET的MySQL并行复制
废话不多说,直接上性能压测图:
四 总结
slave延迟的原因可以归结为slave apply binlog的速度跟不上主库写入的速度,如何解决复制延迟呢?其实也是如何提高MySQL写速度的问题。从目前的硬件和软件的发展来看,硬件存储由之前的HDD机械硬盘发展到现在的SSD,PCI-E SSD,再到NVM Express(NVMe),IO性能一直在提升。MySQL的主从复制也从单线程复制到不同算法的并行复制(基于库,事务,行),应用binlog的速度也越来越快。
本文归纳从几个常见的复制延迟场景,有可能还不完整,也欢迎大家留言讨论。
五 拓展阅读
[1] 一种MySQL主从同步加速方案-改进 https://dinglin.iteye.com/blog/1187154
[2] MySQL多线程同步MySQL-Transfer介绍(已经不在维护) https://dinglin.iteye.com/blog/1581877
[3] MySQL并行复制演进及MySQL 8.0中基于WriteSet的优化 https://www.cnblogs.com/DataArt/p/10240093.html
[4] 速度提升5~10倍,基于WRITESET的MySQL并行复制
[5] https://mysqlhighavailability.com/improving-the-parallel-applier-with-writeset-based-dependency-tracking/
数据库
2019-07-04 11:08:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
redis
概念:redis是一款高性能的NOSQL系列的非关系型数据库
应用场景:
缓存(数据查询,短连接,新闻内容,商品内容)
聊天室的在线好友列表
任务队列(秒杀,抢购,12306等)
应用排行榜
网址访问统计
数据过期处理
分布式集群架构中的session分离

全局key操作:
flushdb :清空当前选择的数据库
del mykey :删除key
move mysetkey 1 :将当前数据库中的mysetkey键移入到id为1的数据库中
rename mykey mykey1 :将mykey改名为mykey1
renamex oldkey newkey:如果newkey已经存在,则无效
expire mykey 100:将该键的超时设置为100秒
persist mykey:将该key的超时去掉,变成持久化的键
keys my* :获取当前数据库中所有以my开头的key
exists mykey :若存在返回0,不存在返回1
select 0:打开id为0的数据库
ttl mykey:查看还有多少秒过期,-1表示永不过期,-2表示已经过期
type mykey:返回mykey对应的值的类型

命令操作:
1、redis的数据结构
redis存储的是:key-value格式的数据,其中key都是字符串,value有5种不同的数据结构
value的数据结构:1、字符串类型:String
2、哈希类型:hash:map格式
3、列表类型:list:LinkedList格式
4、集合类型:set
5、有序集合类型:sortedset
2、字符串类型:String ,redis最基本的类型,一个key对应一个value,String类型是二进制安全的,意思是String可以包含任何数据,比如jpg图片,或者序列化的对象,一个键最大能存储512MB
存储:set key value
获取:get key
删除:del key
例如:
set mykey “test” :为键设置新值,并覆盖原有值
getset mycounter 0 :设置值,取值同时进行
setex mykey 10 “hello” :设置指定key的过期时间为10秒,在存活时间可以获取value
setnx mykey “hello” :若该键不存在,则为键设置新值,如果key已经存在则插入无效
mset key3 “stephen” key4 “liu”:批量设置键
del mykey:删除已有键
append mykey “hello” :若该键并不存在,返回当前value的长度,该键已经存在,返回追加后的value的长度
incr mykey:值增加1.若该key不存在,创建key,初始值设为0,增加后结果为1
decrby mykey 5 :值减少5
setrange mykey 20 dd:把第21和22个字节替换为dd,超过value长度,自动补0
exists mykey:判断该键是否存在,存在返回1,否则返回0
get mykey:获取key对应的value
strlen mykey:获取指定key的字符长度
ttl mykey :查看指定key的剩余存活时间
getrange mykey 1 20 :获取第2到第20个字节,若20超过value长度,则截取第2个和后面所有的
mget key3 key4 :批量获取键

3、哈希类型 :hash,可以看成具有>,其中同一个key可以有多个不同的值,所以该类型非常适合于存储值对象信息
存储:hset key field value
获取:hget key field,获取指定的field对应的值
hgetall key:获取所有的field和vlaue
删除:hdel key field
例如:
hset key field1 “s”:若字段field1不存在,创建该键及其与其关联的Hash,Hash中key为field1,并设value为s,若字段field存在,则覆盖
hsetnx key field1 “s” :若字段field1不存在,创建该键及与其关联的hash,hash中,key为field1,并设value为s,若字段存在,则无效
hmset key field1 “hello” field2 “world” :一次性设置多个字段
hdel key field1 :删除key键中字段名为field1的字段
del key:删除键
hincrby key field 1 :给field的值加1
hget key field1 :获取键值为key ,字段为field的值
hlen key :获取key键的字段数量
hexists key field:判断key键中是否存在字段名field1的字段
hmget key field1 field2 field3 :一次性获取多个字段
hgetall key :返回key键的所有field值及value值
hkeys key :获取key键中所有字段的field值
hvals key :获取key键中所有字段的value值

4、列表类型:list,按照插入顺序排序的字符串链表
存储:lpush key value:将元素加入列表的左边
rpush key value :将元素加入列表的右边
获取:lrange key start end:范围获取
删除:lpop key:删除列表最左边的元素,并将元素返回
rpop key:删除列表最右边的元素,并将元素返回
例如:
lpush mykey a b :若key不存在,创建该键及其关联的List,依次插入a,b 若List类型的key存在,则插入value中
lpushx mykey e:若key不存在,则此命令无效,若key存在,则插入value中
linsert mykey before a a1 :在a的前面插入新元素
linsert mykey after a a1 :在a的后面插入新元素
rpush mykey a b:在链表的尾部先插入b在插入a
rpushx mykey e :若key存在,在尾部插入e,若不存在,则无效
del mykey :删除已有键
lrem mykey 2 a :从头部开始找,按先后顺序,值为a的元素,删除数量为2个,若存在第3个,则不删除
ltrim mykey 0 2:从头开始,索引为0,1,2的3个元素,其余全部删除
lset mykey 1 e :从头开始,将索引为1的元素值,设置为新值e,若索引越界,则返回错误信息
rpoplpush mykey mykey :将mykey中的尾部元素移到头部
lrange mykey 0 -1 :取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素
lrange mykey 0 2 :从头开始,取索引为0,1,2的元素
lpop mykey :获取头部元素,并且弹出头部元素,出栈
lindex mykey 6 :从头开始,获取索引为6的元素,若下标越界,则返回nil

5、集合类型 :set 不允许重复元素,如果多次添加相同元素,set中仅保留该元素的一份拷贝
存储:sadd key value
获取:smembers key:获取set集合中的所有元素
删除:srem key value:删除set集合中的某个元素
例如:
asdd myset a b c:若key不存在,创建该键及与其关联的set,依次插入a,b,c,若key存在,则插入value中,插入不存在的元素
spop myset :尾部的b被移出,事实上b并不是之前插入的第一个或最后一个成员
srem myset a d f:若f不存在,移除a,d并返回2
smove myset myset2 a:将a从myset移到myset2
sismember myset a :判断a是否存在,返回值1表示存在
smembers myset 查看set中的内容
scard myset :获取set集合中元素的数量
srandmember myset:随机的返回某一成员
sdiff myset myset2 :显示myset和myset2比较后myset1独有的值
sdiff myset myset2 myset3:显示myset和myset2,myset3比较后myset独有的值
sdiffstore diffkey myset myset2 myset3 :3个集的和比较,独有的元素存入diffkey关联的set中
sinter myset myset2 myset3 :获得3个集合中都有的元素
sinterstroe interkey myset myset2 myset3 :把交集存入interkey关联的set中
sunion myset myset2 myset3 :获取3个集合中的成员的并集
sunionstore unionkey myset myset2 myset3 :把并集存入unionkey关联的set中

6、有序集合类型:sortedset:不允许重复元素,且元素有顺序,每一个成员都会有一个分数与之关联,通过分数来为集合中的成员进行从小到大的排序。成员是唯一的,但是分数却是可以重复的
存储:zadd key score value
获取:zrange key start end
删除:zrem key value
例如:
zadd myzset 2 “two” 3 “three” :添加两个分数分别是2和3的两个成员
arem myzset one two :删除多个成员变量,返回删除的数量
zincrby myzset 2 one :将成员one 的分数增加2,并返回该成员更新后的分数
zrange myzset 0 -1 WITHCSORES :返回所有成员和分数,不加WITHSCORES,只返回成员
zrank myzset one :获取成员one在sorted-set中的位置的索引值。0表示第一个位置
zcard myzset:获取myzset键中成员的数量
zcount myzset 1 2 :获取分数在1-2之间的成员数量
zscore myzset three :获取成员three的分数
zraangebyscore myzset (1 2 :获取分数大于1,小于等于2 的成员
zrangebyscore myzset -inf +inf limit 2 3 :返回索引是2和3的成员
zremrangebyscore myzset 1 2 :删除分数1-2的成员,并返回实际删除的数量
zremrangebyrank myzset 0 1 :删除位置索引0-1的成员
zrevrange myzset 0 -1 WITHSCORE :按位置索引从高到低,获取所有成员和分数
zrevrange myzset 1 3 :获取位置索引为1,2,3的成员
zrevrangebyscore myzset 3 0 :获取分数0-3之间的成员并且以相反的顺序输出
zrevrangebyscore myzset 4 10 limit 1 2 :获取索引是1 和2 的成员,并反转位置索引


持久化:redis是一个内存数据库,当redis服务器重启或者电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中
机制:RDB:默认方式,不需要进行配置,默认使用的机制,在一定的间隔时间中,检测key的变化情况,然后持久化数据
AOF:日志记录的方式,可以记录每一条命令的操作,可以每一次命令操作后持久化数据
注意:redis的持久化是可以禁用的,两种方式的持久化是可以同时存在的,但是当redis重启时,AOF文件会被优先用于重构数据

RDB详解:
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,Redis会单独创建fork一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久华过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了如果需要进行大规模数据的恢复时还能有极高性能
默认情况下,redis会把快照文件存储为当前目录下一个名为dump.rdb的文件,要修改文件的存储路径和名称,可以通过修改配置文件redis.conf实现。
保存点可以设置多个,Redis的配置文件就默认设置了3个保存点
如果想禁用快照保存的功能,可以通过注释掉所有save配置到达,或者添加配置 save “”
启动快照的方式:
1、在配置文件中配置保存点
2、SAVE命令:SAVE命令会使用同步的方式生成RDB快照文件,这意味着在这个过程中会阻塞所有其他客户端的请求,因此不建议在生产环境使用这个命令
3、BGSAVE:BGSAVE命令使用后台的方式保存RDB文件,调用此命令后,会立刻返回OK返回码。redis会产生一个子进程进行处理并立刻恢复对客户端的服务。在客户端我们可以使用LASTSAVE命令查看操作是否成功
4、flushall:会产生一个dump.rdb文件,但里面是空的,无意义
注意:配置文件里禁用了快照生成功能,不影响SAVE和BGSAVE命令的效果

AOF详解:
以日志文件的形式来记录每个写操作,将rediscover执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据恢复到工作
启动:在redis.conf中修改默认的appendonly no 改为yes
同步策略:
1、appendfsync always:每修改同步,同步持久华,每次发生数据变更立即记录到磁盘,性能较差,但数据完整性比较好
2、appendsync everysec:每秒同步,异步操作,每秒记录,如果发生灾难,可能会丢失1秒数据
3、appendsync no:不同步
官方建议配置:每秒同步
AOF重写:AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阀值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写,遍历新进程的内存中数据,每条记录有一条set语句,重写AOF文件的操作,并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件,这点和快照有点类似
Redis会记录上次重写时点AOF大小,默认配置上当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发,实际生产环境至少要2G

RDB优缺点
优点:
1、比起AOF,在数据量较大的情况下,RDB的启动速度更快
2、RDB文件是一个很简洁的单文件,它保存了某个时间点点redis数据,很适合用于备份
3、RDB很适合用于灾备。单文件很方便就能传输到远程服务器上
4、RDB的性能很好,需要进行持久化时,主进程会fork一个子进程出来,然后把持久化的工作交给子进程,自己不会有相关的I/O操作
缺点:
1、RDB容易造成数据丢失,假设每5分钟保存一次快照,如果redis因为某种原因不能正常工作,那么从上次产生快照到redis出现问题这段时间的数据就会丢失
2、RDB使用fork产生子进程进行数据的持久化,如果数据比较大的话可能会花费点时间,造成redis停止服务几毫秒

AOF优缺点
优点:
1、该机制带来更高的数据安全性,即数据持久性,redis中提供了3种同步策略
2、AOF日志文件是一个纯追加的文件,就算服务器突然Crash,也不会出现日志的定位或者损坏问题,甚至如果因为某些原因命令只写到一半到日志文件里,我们也可以用redis-check-aof这个工具很简单的进行修复
3、当AOF文件太大时,redis会自动在后台进行重写,重写很安全,因为重新是在一个新文件上进行,同时热地说会继续往旧文件追加数据
缺点:
1、在相同的数据集下,AOF文件的大小一般会比RDB大
2、在某些同步策略下,AOF的速度会比RDB慢

关于RDB和AOF的建议
1、官方建议同时开启两种持久化策略,因为有时需要RDB快照时进行数据库备份,更快重启以及发生AOF引擎错误的解决办法
2、因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了
3、如果选择AOF,只要硬盘许可,将两减少AOF rewrite的频率,因为一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成是阻塞几乎是不可避免的,可以设置到5G以上

Redis的事务
Redis通过MULTI,EXEC,DSCARD和WATCH四个命令来实现事务功能
MULTI:标记一个事务块的开始
EXEC:执行所有事务块内的命令
DISCARD:取消事务,放弃执行事务块内的所有命令
WATCH key:监视一个或多个key,如果在事务执行之前key被其他命令所改动,那么事务将被打断
注意:当遇到错误时,redis放过这种错误,保证事务执行完成,需要自己保重逻辑符合预期
Jedis:
概念:java操作redis数据库的工具
使用步骤:
1、导入jedis的jar包
2、使用

操作value数据类型:
1、字符串类型 :set:设置key-value
get:获得value
setex:设置指定时间过期的key-value
2、哈希类型:hset :设置
hget:获取
hgetall:获取全部
3、列表类型:lpush/rpush:存储
lpop/rpop:删除
lrange:获取
4、集合类型:sadd:存储
smembers:获取
5、有序集合类型:zadd:存储
zrange :获取

Jedis连接池:JedisPool
使用:
1、创建连接池对象,JedisPool
2、调用方法,getResource()方法获取Jedis连接


数据库
2019-07-04 09:52:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> SQL 脚本: select database();
数据库
2019-07-03 14:36:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
本文介绍如何通过阿里云Quick BI连接AnalyticDB for PostgreSQL数据库。
在 Quick BI 中新建AnalyticDB for PostgreSQL数据源 登录Quick BI控制台。 单击 工作空间 > 数据源 ,进入 数据源 管理页面。 单击 新建数据源 > AnalyticDB for PostgreSQL 。 在 添加AnalyticDB for PostgreSQL数据源 页面进行参数配置。
配置项 说明 显示名称 数据源名称。
数据库地址 AnalyticDB for PostgreSQL的连接地址 。
端口 链接地址对应的端口号。
数据库 数据库名。
Schema 数据库Schema名。
用户名
密码
AccessKey ID。
Access Key Secret。
完成上述参数配置后,单击 连接测试 测试连通性,测试通过后,单击 添加 添加数据源。
使用Quick BI
成功连接AnalyticDB for PostgreSQL数据源后,用户可以参考以下步骤学习如何在Quick BI中完成报表分析等操作。 创建数据集 制作仪表板 制作电子表格 制作数据门户
关于Quick BI的更过功能,请参见 Quick BI相关文档 。
作者:陆封
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-07-03 12:02:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
数据库中现有两张表,一张客户表(customer),一张订单表(order)
客户表数据大概3K多条,订单表数据有100W条左右
当需要查询客户的订单状态在退款,付款,部分付款,支付完成的客户时,出现了页面加载缓慢问题,大概需要6s左右才加载完成
表结构如下 --客户表-- CREATE TABLE `customer` ( `customer_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '学员ID', `customer_name` varchar(64) NOT NULL DEFAULT '' COMMENT '姓名', `gender` tinyint(1) NOT NULL DEFAULT '0' COMMENT '性别,1为男,2为女', `birthday` date DEFAULT NULL COMMENT '生日', `email` varchar(30) NOT NULL DEFAULT '' COMMENT '邮箱', PRIMARY KEY (`customer_id`) ) ENGINE=InnoDB AUTO_INCREMENT=20374 DEFAULT CHARSET=utf8 COMMENT='后台学生信息表' --订单表-- CREATE TABLE `erp_order` ( `order_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID', `product_id` bigint(11) DEFAULT NULL COMMENT '产品ID', `order_num` varchar(50) DEFAULT NULL COMMENT '订单编号', `policy_id` bigint(11) DEFAULT NULL COMMENT '优惠政策', `policy_amount` bigint(11) NOT NULL DEFAULT '0' COMMENT '优惠金额', `sale_user_id` bigint(11) DEFAULT NULL COMMENT '销售顾问', `customer_id` bigint(11) DEFAULT NULL COMMENT '学员ID', `order_status` int(11) DEFAULT NULL COMMENT '订单状态 1 待客户激活 2 未支付 3 已部分支付 4 支付完成 5 已取消 6 退款中 7 已退款', `order_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间', PRIMARY KEY (`order_id`) USING BTREE, KEY `index_order_time` (`order_time`) USING BTREE, KEY `index_product_id` (`product_id`) USING BTREE, KEY `index_order_num` (`order_num`) USING HASH, KEY `index_order_service_status` (`service_status`) USING BTREE, KEY `index_order_status` (`order_status`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=998096 DEFAULT CHARSET=utf8 COMMENT='订单信息表'
原查询sql 如下 SELECT COUNT(0) FROM erp_customer ec WHERE ec.`customer_id` IN (SELECT DISTINCT eo.`customer_id` FROM erp_order eo WHERE eo.order_status IN (3, 4, 6, 7))
上述sql执行时长和explain如下


优化后的sql SELECT COUNT(0) FROM erp_customer ec WHERE EXISTS (SELECT DISTINCT eo.`customer_id` FROM erp_order eo WHERE eo.order_status IN (3, 4, 6, 7) AND eo.`customer_id` = ec.`customer_id`)
优化后sql执行时长和explain如下

可见,执行时间从3.1s缩短到0.137s,提示很明显
数据库
2019-07-02 16:54:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
由于频繁强制启停tomcat不清理连接等情况时可能导致oracle连接爆满,此时可以使用这个方式清理 --查询 select sess.sid,sess.serial#,sess.machine, lo.oracle_username,lo.os_user_name, ao.object_name,lo.locked_mode from v$locked_object lo, dba_objects ao,v$session sess where ao.object_id = lo.object_id and lo.session_id = sess.sid; --杀会话 alter system kill session '上面的sid,上面的serial#';
数据库
2019-12-13 16:16:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
MySQL的行锁是在引擎层由各个引擎自己实现的。但不是所有的引擎都支持行锁,又如MySAM引擎不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。InnoDB是支持行锁的,这也是MySAM被InnoDB替代的原因之一。下面我们就来说说InnoDB的行锁:
行锁就是针对数据库表中行记录的锁,比如事务A更新了一行,而这个时候事务B也要更新同一行,则必须等事务A的操作完成后才能进行更新。
从两阶段锁说起
下面的操作序列中,事务B的update语句执行时会是什么现象呢?假设字段id是表t的主键。
这个问题的结论取决于事务A在执行完两条update语句后,持有哪些锁,以及在什么时候释放。你可以验证一下:实际上事务B的update语句会被阻塞,直到事务A执行commit之后,事务B才能继续执行。知道了这个答案,你一定知道了事务A持有的两个记录的行锁,都是在commit的时候才释放的。
在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要的时候就立刻释放,而是要等到事务结束时才释放,这个就是两阶段锁协议。
那么, 如果你的事务需要锁多行,要把最有可能造成锁冲突、最可能影响并发度的锁尽量往后放。
死锁和死锁检测
当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。下面举一个死锁的例子:
这时候,事务A在等待事务B释放id=2的行锁,而事务B在等待事务A释放id=1的行锁。事务A和事务B在互相等待对方的资源释放,就是进行了死锁状态。当出现死锁状态后,有两种策略: 一种策略是,直接进行等待状态,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。 另一种策略,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其它事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。
在InnoDB中,innodb_lock_wait_timeout的默认值是50s,如果采用第一个策略,当发生死锁后,第一个锁住的线程要过50s才会超时退出,然后其它线程才有可能继续执行。对于在线服务来说,这个等待时间往往是无法接受的。但我们又不可能把这个时间设置成一个很小的值,比如1s。这样当出现死锁时,确实很快就可以解锁,但如果不是死锁,而是简单的锁等待呢?所以超时时间设置太短的话,会出现很多误杀。
所以,正常情况下,我们还是要采用第二种策略,即:主动死锁检测,而且innodb_deadlock_detect的默认值本身就是on。主动死锁检测在发生死锁的时候,是能快速发现并进行处理的,但是它也是有额外负担的。
如果有很多个事务都要更新同一行的场景呢?
每一个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是O(n)的操作。假设有1000个并发线程要同时更新同一行记录时,那么死锁检测操作就是100W这个量级的,虽然最终检测的结果是没有死锁,但这个期间消耗了大量的CPU资源,因此,会看到CPU利用率很高,但每秒却执行不了几个事务。
那么,怎么解决由于这种热点行更新导致的性能问题呢?问题的症结在于,死锁检测要消耗大量的CPU资源。 一种头痛医头的方法就是如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉。但是这种操作本身带有一定的风险,因为业务设计的时候一般不会把死锁当做一个严重错误,毕竟出现死锁了,就回滚,然后通过业务重试一般就能解决问题,这是业务无损的。而关了死锁检测意味着可能会出现大量的超时,这个是业务有损的。 另一个思路是控制并发度。根据上面的分析,你会发现如果并发能控制住,比如同一行同时最多只有10个线程在更新那么死锁检测的成功就很低,就不会出现这个问题。我们就可以把一行改成逻辑上的多行来减少锁冲突。还是以影院账户为例,可以考虑在多行记录上,比如10个记录,影院的账户总额等于这10个记录的值总和。这样每次要给影院账户加金额的时候,随机选其中一条记录来加。这样每次冲突概率变成原来的1/10,可以减少锁等待个数,也就减少了死锁检测的CPU消耗。
问题:
如果你要删除一个表里面的前10000行记录,以下三种方法,你选哪一种: 第一种,直接执行delete from T limit 10000; 第二种,在一个连接中循环执行20次delete from T limit 500; 第三种,在20个连接中同时执行delete from T limit 500。
答案是:第二种方式较好。
第一种方式,单个语句占用时间长,锁的时间也比较长;而且 大事务还会导致主从延迟 。
第三种方式,会人为造成锁冲突。


数据库
2019-12-12 22:58:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
  
  昨天看了红米K30的发布会,感触颇多啊,“卢本伟”就是雷军请来恶心荣耀的吧,恶心的还淋漓尽致,发布会全程diss荣耀V30,结果跑分出来的那一刻,网友们笑了。红米K30 26W+的跑分充其量是个中端机,面对5G时代肯定要优先选择性能更强悍的准旗舰荣耀V30这种5G手机了。下面来分析一波。
 
  (左红米K30 右荣耀9X)
  别的咱不说,红米K30的跑分连麒麟810处理器的荣耀9X都拼不过,亦不是麒麟980的对手了,谈何与麒麟990抗衡?手机处理器一直都是关键因素,毕竟大脑不给力,四肢也跑不起来。所以手机最关键的核心就是处理器,处理器决定了一款手机的上限和下限。在5G时代,对于处理器的性能可比4G时代大多了,麒麟990无论是玩游戏还是日常使用都是没问题的,红米K30的性能可能会招架不住。卢姥爷在发布会上的疯狂diss我觉得差点意思,红米K30并不会稳赢荣耀V30,甚至可以说在目前的5G手机中,红米K30的性能是最弱的…
 
  来一波荣耀V30的跑分摩擦一下红米K30吧,44W+的跑分轻轻松松,完全是旗舰处理器的性能。没想到红米K30的发布会上,荣耀V30成了全场最佳,懂手机的人都知道,处理器是多么的关键,所以这也不言而喻了,所以荣耀V30才是值得买的5G手机。
  
  目前有购买5G手机需求的小伙伴,我感觉是可以入手荣耀V30的,处理器性能和红米K30都不是一个档次,麒麟990运行大型游戏或者是每天高强度的刷手机都足以应付过来,不会出现小马拉大车的问题,手机处理器和配置都是严丝合缝的高度配合。至于外观来说,荣耀V30的AG磨砂手感不错,红米K30的投币机我没办法审美了,看完红米的发布会,昨天下单的荣耀V30 5G手机,算是给红米K30烧高香了送它一程吧…
数据库
2019-12-12 14:17:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
如果启动前不对 Linux 内核做任何更改,那么 Redis 启动会报出警告,共三个:如下图所示

第一个警告 :The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
意思是:TCP backlog设置值,511没有成功,因为 /proc/sys/net/core/somaxconn这个设置的是更小的128.
临时解决方法:(即下次启动还需要修改此值)
echo 511 > /proc/sys/net/core/somaxconn
永久解决方法:(即以后启动还需要修改此值)
将其写入/etc/rc.local文件中。
baklog参数实际控制的是已经3次握手成功的还在accept queue的大小。
参考 linux里的backlog详解

第二个警告 :overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to/etc/sysctl.conf andthen reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
意思是:overcommit_memory参数设置为0!在内存不足的情况下,后台程序save可能失败。建议在文件 /etc/sysctl.conf 中将overcommit_memory修改为1。
临时解决方法:echo "vm.overcommit_memory=1" > /etc/sysctl.conf
永久解决方法:将其写入/etc/sysctl.conf文件中。
参考: 有关linux下redis overcommit_memory的问题

第三个警告 :you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix thisissue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain thesetting after a reboot. Redis must be restarted after THP is disabled.
意思是:你使用的是透明大页,可能导致redis延迟和内存使用问题。执行 echo never > /sys/kernel/mm/transparent_hugepage/enabled 修复该问题。
临时解决方法:
echo never > /sys/kernel/mm/transparent_hugepage/enabled。
永久解决方法:
将其写入/etc/rc.local文件中。

如果第一个警告不能再宿主机中解决,可以在docker容器中尝试解决,可以参考此文章。
http://weeklyalgo.codes/2017/03/06/some%20docker%20security%20options/#2-
数据库
2019-12-06 13:44:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Sentinel是Redis的高可用解决方案,有一个或者多个实例组成Sentinel系统,可以监视任意多个master以及其下的slave,并在master下线时,自动将该master下单某个slave升级为新的master,并可以做重新选主之后的故障迁移操作;


Sentinel是为一组一主多从的服务器服务,主从服务器上存储的键值对是一样的,slave上只能执行读操作;
sentinel服务本质上是一个运行在特殊模式下的Redis服务器,不加载RDB、AOF文件,运行的命令列表也不一样;
命令列表:ping、sentinel、info、subscribe、unsubscribe、psubscribe、punsubscribe;
启动方式:
redis-sentinel /path/to/user/sentinel.conf
或者
redis-server /path/to/user/sentinel's --sentinel


对于每个被sentinel监视的服务器来说,sentinel会创建两个连向服务器的异步网络; 命令连接,这个连接专门用作向主服务器发送命令,并接受回复; 订阅连接,专门用于订阅主服务器的__sentinel__:hello频道;
这里sentinel可以是多台,一台发送,所有的sentinel都可以监听到订阅的消息;


sentinel通过向master发送info命令(10s一次),可以获取到master本身的详细信息、master下的slave简要信息(ip、port、state、offset、lag);
sentinel通过向slave发送info命令,获取slave的详细信息(run_id、role、master_host、moster_port、master_link_status、slave_priority、slave_repl_offset);
检测主观下线状态:
sentinel以每秒一次的频率向所有与他创建了命令连接到实例(master、slave、其他sentinel) 发送ping命令 ,并通过实例返回的回复来判断实例是否在线 ;
检测客观下线状态:
当一个sentinel检测到一个master主观下线以后,会 向其他 sentinel询问 (发送命令:sentinel is-master-down-by addr )该master是否下线,当认为下线的数量达到足够数量(配置指定)以后,sentinel会将master判定为客观下线,并对master执行故障迁移操作;
选举领头sentinel:
当一个master被判断为客观下线以后,监视这个下线服务器的各个sentinel会进行协商,选出一个领头sentinel,有他来对下线主服务器做故障迁移操作;
当一个sentinel发现master客观下线以后,会向其他sentinel发送一个请求将自己选为领头sentinel的命令,只要有 1/2以上的sentinel同意 这个请求,这个sentinel就会正式成为领头sentinel;
一轮请求中, 一个sentinel只有一次选举的机会 ,哪个请求先到,就答应哪个,如果一轮选举没有领头sentinel,就再来一轮,直到选出为止;
故障迁移:
领头sentinel会完成对下线的master的故障迁移操作; 从下线master的slave中选出一个,作为新的master(发送命令:slaveof no one); 将其他slave的master指向新的master(发送命令:slaveof ); 将已下线的master转为slave,当其上线之后,会成为新master的slave;



数据库
2019-12-03 16:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
使用mysql -uroot -p,然后输入密码登录mysql时,出现了如下错误:
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
一般这个错误是由密码错误引起,解决的办法自然就是重置密码
解决方案如下:
1.停止mysql数据库:systemctl stop mysqld
2.用以下命令启动MySQL,以不检查权限的方式启动:
mysqld --skip-grant-tables &
此时又报了一个错误:2018-02-01T02:52:55.093724Z 0 [ERROR] Fatal error: Please read "Security" section of the manual to find out how to run mysqld as root!
执行命令:mysqld --user=root --skip-grant-tables &
3.登录mysql:mysql -uroot或mysql
4.更新root密码
mysql5.7以下版本:UPDATE mysql.user SET Password=PASSWORD('123456') where USER='root';
mysql5.7版本:UPDATE mysql.user SET authentication_string=PASSWORD('123456') where USER='root';
5.刷新权限:flush privileges;
6.退出mysql:exit或quit
7.使用root用户重新登录mysql
mysql -uroot -p
Enter password:<输入新设的密码123456>
数据库
2019-11-26 11:12:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1、Oracle用户没有权限执行crontab
引用
$ crontab -e
crontab: you are not authorized to use cron. Sorry.
$ exit
2、在 /var/adm/cron/cron.allow文件下用root用户添加oracle即可
引用
root@oradb1:/tmp/dbcheck # more /var/adm/cron/cron.allow
root
adm
uucp
oracle
3、crontab调度执行hotbackup.sh,意为每天凌晨1点03分调度一次备份脚本
引用
$ crontab -l
03 01 * * * /u05/backup/hotbackup.sh
重庆思庄oracle OCP认证培训班火热报名中!循环开课,欢迎联系试听!
数据库
2019-11-22 17:07:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在开发过程中,经常会由用户自主创建模型,然后添加模块内容,这种情况在cms中居多,所以我把几张常规的表给大家列出,帮助大家在以后的开发中可以参考该表来实现该功能。
一, Module表展示视图
module表数据字典
id module主键id title 模块名称 name:模块表名 description 模块 Issearch 列表页是否开启搜索 listfields 列表页调用字段 Isdel 是否允许删除 Isadd 是否是否允许添加数据 status 是否启用模型 sortid 排序 is_category 是否启用栏目 Category_max_level 最大栏目级别 is_add 允许添加栏目 Is_del 允许删除栏目 Is_ext 启用多栏目 query_field 数据列表搜索字段 list_pagesize 列表页显示条数 is_hidden 是否隐藏
二.Module category栏目表
Category 表数据字典
Id 栏目主键 typeid 上级栏目id Title 栏目名称 Moduleid 所属模块 Url 地址链接 Isshow 是否显示 Sortid 显示排序 Alias 栏目别名
三.字段表
Field 表数据字典
Id 字段id Moduleid 所属模块id Title 字段 Name 字典名称 Required 字段是否必填 Minlength 字段最小长度 Maxlength 字段最大长度 Pattern 字段验证规则 Defaultmsg 默认提示语 Errormsg 验证失败错误信息 Type 字段类型 Setup 字段相关设置 Sortid 字段排序 Status 字段启用状态 Issystem Memo 备注信息 Readonly 是否只读*如果是只读字段,则该字段内容修改无效 Issearch 搜索栏是否显示
数据库
2019-11-12 10:19:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Choose three
Which three actions can you perfom only with system privileges?
A) Truncate a table in another schema.
B) Access flat files via a database, which are stored in an operating system directory.
C) Log in to a database.
D) Query any table in a database.
E) Use the WITH GRANT OPTION clause.
F) Execute a procedure in another schema.
Answer:CDF
(解析:这道题考的就是可以授权哪些系统权限去做哪些活儿。
C:create session
D:select any table
F:EXECUTE ANY PROCEDURE
原来以为 AAnswer:正确,结构查询发现没有 truncate any table 的权限。
)
数据库
2019-11-11 10:57:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> use mysql select * from user where user='root'; update user set password=password('mysql@2020') where user='root'; 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 '('mysql@2020') where user='root'' at line 1 解决: 在MySQL 8.04前,执行:SET PASSWORD=PASSWORD('[新密码]');但是MySQL8.0.4开始,这样默认是不行的。因为之前,MySQL的密码认证插件是“mysql_native_password”,而现在使用的是“caching_sha2_password”。 alter user 'root'@'localhost'identified with mysql_native_password by 'root'; flush privileges; https://blog.csdn.net/u012232730/article/details/82417245 alter user 'root'@'localhost'identified with mysql_native_password by 'mysql@2020'; alter user 'root'@'%'identified with mysql_native_password by 'mysql@2020'; 也有文章说不用flush 成功!不用flush。 改完不重启mycat没事 重启就不行了。 mysql> select count(*) from td_b_nodecode; ERROR 1184 (HY000): Invalid DataSource:0
数据库
2019-11-04 08:27:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
es 与mysql

es 用途 与 索引构建
1. es 用于检索
当 es 用于 搜索的时候,那么就需要将 对应的 目标属性 写在一个 索引的 字段里面进行对应。
比如 搜索 对应的房屋。 那么 构建 es 的房屋索引的时候, 就需要 将 数据库房屋表id , 房屋名称, 房屋 描述 等等 其他属性可能
用到的 检索条件和属性 构建在 es 的房屋 索引里面。
当用户搜索比如 房屋名称的时候, 就会先用房屋名称去 es 里面去搜索 相关数据, 返回 房屋 id ,房屋名称 等其他属性,
之后 就可以 根据 房屋id 和其他数据再去 查询 mysql 拿到 对应的更多,更真实的 房屋数据给用户了。
也就是 真实数据 在mysql 里面, 查询的时候,从es 取到 相关的数据,这时候可以直接返回给用户,也可以根据es数据再次去mysql 里面获取。
es的索引数据,可以使用定时器去定时 从mysql 里面 取,或者 将 新增 改动的数据放入 mq里面,之后异步写入 es 中。

2. es 用于 数据分析, 数据报表
目前 公司就在 用es 做这块工作。
因为有数据分析,与数据报表,因此 建立 es 索引就有 对应的 指标(新增客户数 等 报表字段),维度(时间,组织等查询条件) 的字段属性要求了。
使用定时器或者其他方式 从mysql 中 取基础数据,或者 将mysql 的数据汇总之后 存入 es 里面,
查询报表或者数据分析 展示数据的时候,就直接从 es 里面取数展示了,或者将 es 中的数据 聚合 之后再展示。
这种用途,es 的聚合 查询就会用得比较多。

es 数据建模


mapping


一般 date_detection 设置为 false ,手动指定日期类型

何种类型

是否需要检索


实例

以上的意思是 因为 content 内容太多了,如果 每次查询
都取出来 content 那么性能太差了。 所有这样可以优化性能效率。
同时 不将 content 内容放入 _source 里面 .

关联关系处理

例子
也就是将 博客的 评论内容 也放入 blog 索引的内容里面。
查询



即将 type 设置 为 nested 即可


关联关系处理之Parent/Child



管理

防止字段过多




聚合分析 以下实例 数据 # aggregation POST test_search_index/doc/_bulk {"index":{"_id":"1"}} {"username":"alfred way","job":"java engineer","age":18,"birth":"1990-01-02","isMarried":false,"salary":10000} {"index":{"_id":"2"}} {"username":"tom","job":"java senior engineer","age":28,"birth":"1980-05-07","isMarried":true,"salary":30000} {"index":{"_id":"3"}} {"username":"lee","job":"ruby engineer","age":22,"birth":"1985-08-07","isMarried":false,"salary":15000} {"index":{"_id":"4"}} {"username":"Nick","job":"web engineer","age":23,"birth":"1989-08-07","isMarried":false,"salary":8000} {"index":{"_id":"5"}} {"username":"Niko","job":"web engineer","age":18,"birth":"1994-08-07","isMarried":false,"salary":5000} {"index":{"_id":"6"}} {"username":"Michell","job":"ruby engineer","age":26,"birth":"1987-08-07","isMarried":false,"salary":12000}


聚合分析-分类
metric
cardinality 就是 计算数目的 类似于 sql 里面的 distinc count(*)
percentile 就是百分位数的统计
top hits 取 前几个数
min
cardinality
extended stats
Percentile

比如 第一个 1.0 : 5150 也就是 百分之1 的数值 范围是 5150 以内
5% 的数值在 5750 之内 指定 百分比的对应的数值: 以下返回 百分比占比 95%, 99%, 99.9% 对应的数值范围 GET test_search_index/_search { "size": 0, "aggs": { "per_age": { "percentiles": { "field": "salary", "percents": [ 95, 99, 99.9 ] } } } }


Percentile Rank 百分位排名
以上 就是 求出 工资 为 11000 和 30000 在 数据里面的 占比 百分位数
结果是 11000 为 50% , 30000 为 75%
Top Hits
以上是 按照 job.keyword 分组,且取分组是 10个分组数据,
之后 每个分组 按照 年龄倒序取前10个详情数据返回
也就是 分组之后,再获取每个组里面的详情数据就可以 使用这个
Bucket

Terms
range
Date Range

Historgram

效果图

以上例子可以看出, 比如 有 5000 ,和 8000 的 那么都归属于 5000的分组。
10000 和 12000 归属于 1W分组。
范围分组是 [0,5000], [5000,10000], [10000,15000] ...
这样的
extended_bounds 可以补全

Date Historgram
效果图


Bucket + Metric 聚合分析
也就是分桶之后再分桶

效果图: 千层饼


Pipeline 聚合分析


Min bucket
实例 GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "min_salary_by_job":{ "min_bucket": { "buckets_path": "jobs>avg_salary" } } } }




Derivative
导数: 即看一个数的 发展趋势
Moving Average 移动平均值
移动平均值: 数据变化的趋势
作用范围
filter
注意 aggs 加上 filter 还得 额外加上 一个 aggs
也就是 jobs_salary_small 的聚合 不影响 同级的 jobs 的 聚合
post-filter
即聚合分析还是针多所有 文档, post_filter 只显示 满足条件的数据
当然也可以加上 query 语句 。
适合于 整体聚合数据与 返回部分数据
global

聚合排序
不指定 order 默认按照 doc_count 倒排
先分桶,然后按照 平均值进行 排序桶
多值

原理与精准度问题
min


terms 不准确
正常应该是 abc 的


上例中:
doc_count_error_upper_bound= 4 +2
即 node1 出来的最后一个 d(4) + node2 最后的 b(2)
因为剩下的 c(3) 与 d(1) 不可能超过 6
sum_other_doc_count = 相比总的 文档,漏了c 即 c(3+3) = 6
了解即可, 没有参考意义
比如
因为 node2 中没有 d ,所以去最新值 b(2) 即 doc_count_eror_upper_bound 为2
也就是 不指定 term 的 size ,全部返还即可,也可以避免这个问题
近似统计算法
聚合例子 # aggregation POST test_search_index/doc/_bulk {"index":{"_id":"1"}} {"username":"alfred way","job":"java engineer","age":18,"birth":"1990-01-02","isMarried":false,"salary":10000} {"index":{"_id":"2"}} {"username":"tom","job":"java senior engineer","age":28,"birth":"1980-05-07","isMarried":true,"salary":30000} {"index":{"_id":"3"}} {"username":"lee","job":"ruby engineer","age":22,"birth":"1985-08-07","isMarried":false,"salary":15000} {"index":{"_id":"4"}} {"username":"Nick","job":"web engineer","age":23,"birth":"1989-08-07","isMarried":false,"salary":8000} {"index":{"_id":"5"}} {"username":"Niko","job":"web engineer","age":18,"birth":"1994-08-07","isMarried":false,"salary":5000} {"index":{"_id":"6"}} {"username":"Michell","job":"ruby engineer","age":26,"birth":"1987-08-07","isMarried":false,"salary":12000} GET test_search_index/_search GET _cat/indices GET test_search_index/_search { "size":1, "from":2, "aggs": { "people_per_job": { "terms": { "field": "job.keyword" } } } } GET test_search_index/_search { "size":0, "aggs": { "people_per_job": { "terms": { "field": "job.keyword" } }, "avg_salary":{ "avg": { "field": "salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "min_age":{ "min": { "field": "age" } } } } GET test_search_index/_search { "size":0, "aggs":{ "max_age":{ "max": { "field": "age" } } } } GET test_search_index/_search { "size":0, "aggs":{ "avg_age":{ "avg": { "field": "age" } } } } GET test_search_index/_search { "size":0, "aggs":{ "sum_age":{ "sum": { "field": "age" } } } } GET test_search_index/_search { "size": 0, "aggs": { "min_age": { "min": { "field": "age" } }, "max_age": { "max": { "field": "age" } }, "avg_age": { "avg": { "field": "age" } }, "sum_age": { "sum": { "field": "age" } } } } GET test_search_index/_search { "size":0, "aggs":{ "count_of_job":{ "cardinality": { "field": "job.keyword" } } } } GET test_search_index/_search { "size":0, "aggs":{ "stats_age":{ "stats": { "field": "age" } } } } GET test_search_index/_search { "size":0, "aggs":{ "exstats_salary":{ "extended_stats": { "field": "salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "per_salary":{ "percentiles": { "field": "salary" } } } } GET test_search_index/_search { "size": 0, "aggs": { "per_age": { "percentiles": { "field": "salary", "percents": [ 95, 99, 99.9 ] } } } } GET test_search_index/_search { "size": 0, "aggs": { "per_salary": { "percentile_ranks": { "field": "salary", "values": [ 11000, 30000 ] } } } } GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 10 }, "aggs": { "top_employee": { "top_hits": { "size": 10, "sort": [ { "age": { "order": "desc" } } ] } } } } } } # bucket GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job", "size": 5 } } } } GET test_search_index GET test_search_index/_search { "size": 0, "aggs": { "salary_range": { "range": { "field": "salary", "ranges": [ { "key":"<10000", "to": 10000 }, { "from": 10000, "to": 20000 }, { "key":">20000", "from": 20000 } ] } } } } GET test_search_index/_search { "size": 0, "aggs": { "date_range": { "range": { "field": "birth", "format": "yyyy", "ranges": [ { "from":"1980", "to": "1990" }, { "from": "1990", "to": "2000" }, { "from": "2000" } ] } } } } GET test_search_index/_search { "size": 0, "aggs": { "date_range": { "range": { "field": "birth", "format":"yyyy", "ranges": [ { "to": "now-30y/y" }, { "from": "now-30y/y", "to": "now-20y/y" }, { "from": "now-20y/y" } ] } } } } GET test_search_index/_search { "size":0, "aggs":{ "salary_hist":{ "histogram": { "field": "salary", "interval": 5000, "extended_bounds": { "min": 0, "max": 40000 } } } } } GET test_search_index/_search { "size":0, "aggs":{ "salary_hist":{ "histogram": { "field": "salary", "interval": 5000, "extended_bounds": { "min": 0, "max": 40000 } } } } } GET test_search_index/_search { "size":0, "aggs":{ "by_year":{ "date_histogram": { "field": "birth", "interval": "year", "format":"yyyy" } } } } # bucket more GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 10 }, "aggs": { "age_range": { "range": { "field": "age", "ranges": [ { "to": 20 }, { "from": 20, "to": 30 }, { "from": 30 } ] } } } } } } GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 10 }, "aggs": { "salary": { "stats": { "field": "salary" } } } } } } # scope GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 } } } } GET test_search_index/_search { "size":0, "query":{ "match": { "username": "alfred" } }, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 } } } } # filter GET test_search_index/_search { "size": 0, "aggs": { "jobs_salary_small": { "filter": { "range": { "salary": { "to": 10000 } } }, "aggs": { "jobs": { "terms": { "field": "job.keyword" } } } }, "jobs": { "terms": { "field": "job.keyword" } } } } #post-filter GET test_search_index/_search { "aggs": { "jobs": { "terms": { "field": "job.keyword" } } }, "post_filter": { "match":{ "job.keyword":"java engineer" } } } #global GET test_search_index/_search { "query": { "match": { "job.keyword": "java engineer" } }, "aggs": { "java_avg_salary": { "avg": { "field": "salary" } }, "all": { "global": {}, "aggs": { "avg_salary": { "avg": { "field": "salary" } } } } } } #sort GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 10 } } } } GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 10, "order": [ { "avg_salary": "desc" } ] }, "aggs": { "avg_salary": { "avg": { "field": "salary" } } } } } } GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 10, "order": [ { "stats_salary.sum": "desc" } ] }, "aggs": { "stats_salary": { "stats": { "field": "salary" } } } } } } GET test_search_index/_search { "size": 0, "aggs": { "salary_hist": { "histogram": { "field": "salary", "interval": 5000, "order": { "age>avg_age": "desc" } }, "aggs": { "age": { "filter": { "range": { "age": { "gte": 10 } } }, "aggs": { "avg_age": { "avg": { "field": "age" } } } } } } } } # pipeline aggs GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "min_salary_by_job":{ "min_bucket": { "buckets_path": "jobs>avg_salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "max_salary_by_job":{ "max_bucket": { "buckets_path": "jobs>avg_salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "avg_salary_by_job":{ "avg_bucket": { "buckets_path": "jobs>avg_salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "sum_salary_by_job":{ "sum_bucket": { "buckets_path": "jobs>avg_salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "stats_salary_by_job":{ "stats_bucket": { "buckets_path": "jobs>avg_salary" } } } } GET test_search_index/_search { "size":0, "aggs":{ "jobs":{ "terms": { "field": "job.keyword", "size": 10 }, "aggs":{ "avg_salary":{ "avg": { "field": "salary" } } } }, "percentiles_salary_by_job":{ "percentiles_bucket": { "buckets_path": "jobs>avg_salary" } } } } #derivative GET test_search_index/_search { "size": 0, "aggs": { "birth": { "date_histogram": { "field": "birth", "interval": "year", "min_doc_count": 0 }, "aggs": { "avg_salary": { "avg": { "field": "salary" } }, "derivative_avg_salary": { "derivative": { "buckets_path": "avg_salary" } } } } } } GET test_search_index/_search { "size": 0, "aggs": { "birth": { "date_histogram": { "field": "birth", "interval": "year", "min_doc_count": 0 }, "aggs": { "avg_salary": { "avg": { "field": "salary" } }, "mavg_salary": { "moving_avg": { "buckets_path": "avg_salary" } } } } } } GET test_search_index/_search { "size": 0, "aggs": { "birth": { "date_histogram": { "field": "birth", "interval": "year", "min_doc_count": 0 }, "aggs": { "avg_salary": { "avg": { "field": "salary" } }, "cumulative_salary": { "cumulative_sum": { "buckets_path": "avg_salary" } } } } } } # shard_size GET test_search_index/_search { "size": 0, "aggs": { "jobs": { "terms": { "field": "job.keyword", "size": 2, "shard_size": 10, "show_term_doc_count_error": true } } } } GET test_search_index GET test_order/_search GET test_order GET test_order/_search { "size":0, "aggs":{ "customer_count":{ "cardinality": { "field": "CustomerID.keyword" } } } } GET test_order_one_shard/_search { "size":0, "aggs":{ "customer_count":{ "cardinality": { "field": "CustomerID.keyword" } } } } GET test_order_one_shard/_search { "size":0, "aggs":{ "states":{ "terms":{ "field":"CustomerID.keyword", "size":1, "show_term_doc_count_error": true } } } } GET test_order_one_shard GET test_order/_search { "size":0, "aggs":{ "states":{ "terms":{ "field":"CustomerID.keyword", "size":1, "shard_size": 10000, "show_term_doc_count_error": true } } } }
Search的运行机制
query 和 fetch



相关性算法


search 排序




Fielddata

fielddata 只对 text 类型生效
Doc Values

docvalue_fields
分页与遍历
from size


scroll



参考 https://www.elastic.co/guide/cn/elasticsearch/guide/cn/scroll.html
启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。 设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。
只有设置 滚动的时间不要太长即可,一般不会存在占用大量内存的问题, 因为 es 会释放的。

Search_after



es 术语
因为 es 6.0 版本之后, 去掉了 index 下面的 type 类型,
只有一个 type 类型了,
因此把 index 看做 是 数据库中的一张 表即可

document

document MetaData
不推荐使用 _all

Index

index API

Document API


批量创建文档

update 当文档不存在就会报错。
如果是 index 那么文档存在就覆盖,不存在就创建
批量查询

倒排索引与分词

正排索引与倒排索引






es 倒排索引
分词
分词器

Analyze API
当发现查询 不到的时候 或者与预期不一样的时候,既可以去看看分词是怎样的,可以通过 分词 analyze 去分析排查


standard 是 es 默认的分词器

自带分词器




当不想对文本进行分词就可以使用这个

中文分词

自定义分词


自定义 Tokennizer
搜索的 自动提示 可以使用 NGram
自定义 Token Filters

自定义分词API



分词使用说明


es Mapping

自定义mapping


copy to
index
不索引,设置为 false 可以节省 磁盘和内存 空间
index_options


默认es 对 空值会忽略该值
数据类型
复杂数据类型
专用类型
多字段特性
Dynamic Mapping












数据库
2019-10-25 13:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1.登陆MariaDB
# mysql MariaDB [(none)]> select @@datadir;
当前数据目录是:/var/lib/mysql/
2.创建MariaDB新的数据目录
# mkdir /data/sdv1/mydata/ # chown -R mysql:mysql /data/sdv1/mydata/ # chmod 755 /data/sdv1/mydata/
3.停止MariaDB服务
# systemctl stop mariadb.service # systemctl status mariadb.service
4.修改配置文件
# vim /etc/my.cnf # cat /etc/my.cnf [mysqld] character-set-server=utf8 #datadir=/var/lib/mysql #socket=/var/lib/mysql/mysql.sock datadir=/data/sdv1/mydata socket=/data/sdv1/mydata/mysql.sock # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 # Settings user and group are ignored when systemd is used. # If you need to run mysqld under a different user or group, # customize your systemd unit file for mariadb according to the # instructions in http://fedoraproject.org/wiki/Systemd [mysqld_safe] log-error=/var/log/mariadb/mariadb.log pid-file=/var/run/mariadb/mariadb.pid # # include all files from the config directory # !includedir /etc/my.cnf.d [client] default-character-set=utf8 socket=/data/sdv1/mydata/mysql.sock [mysql] default-character-set=utf8
5.复制数据文件
# cp -a /var/lib/mysql/* /data/sdv1/mydata/
6.启动服务
# systemctl start mariadb # systemctl status mariadb
# ll /data/sdv1/mydata/
至此,MariaDB迁移数据目录完毕!
数据库
2019-10-21 14:54:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似面向对象的查询语句,几乎可以实现类型关系数据库表单查询的绝大部份功能,而且还支持对数据库建立索引。

下载和安装MongoDB与Studio 3T(mongoDB客户端):略

基础概念:
database:数据库
collection:集合,类似数据库的表
document:文档,类似数据库的一行数据
field:域,类似数据库的字段
index:索引
primary key:主键,MongoDB自动在每个集合中添加_id主键
注意:Mongo DB不支持表链接


链接MongoDB指令:mongodb://root:root@localhost:27017,连接本地数据库27017端口的数据库,用户名root:密码root

java程序连接MongoDB
一、添加依赖 org.mongodb mongo-java-driver 3.11.0
二、测试 @Test public void testConnction() { //创建mongodb客户端 MongoClient mongoClient = new com.mongodb.MongoClient("localhost", 27017); //连接数据库 MongoDatabase database = mongoClient.getDatabase("test"); //获取集合 MongoCollection collection = database.getCollection("student"); //查询第一个文档 Document myDoc = collection.find().first(); //将文档转换成json格式输出显示 String json = myDoc.toJson(); System.out.println(json); }

MongoDB指令:
数据库
1、查询数据库: show dbs
2、显示当前数据库 :db
3、创建使用数据库:use DATABASE_NAME
注意:如果存在这个数据库,则切换到此数据库,没有则创建
4、删除数据库:db.dropDatabase()

集合
1、创建集合:db.createCollection(NAME,OPTIONS)
NAME: 集合名称
OPTIONS:创建参数
2、删除集合:db.COLLECTION_NAME.drop()

文档
1、插入文档:db.COLLECTION_NAME.insert(json格式数据)
2、更新文档:db.COLLECTION_NAME.update(第一个json数据用来匹配,第二个json数据用来更新)
3、删除文档:db.COLLECTION_NAME.remove(json格式数据,用于匹配,为空则删除所有数据)
4、查询文档:db.COLLECTION_NAME.find(json格式数据用于匹配,不写查询全部)

用户:
1、创建用户
db.createUser(
{
user:"root",
pwd:"root",
roles:[{roles:"root",db:"admin"}]
}
)
Mongodb内置很多角色,常用的有read,readWrite,root(超级用户)
2、查询数据库:show users
3、删除用户:db.dropUser("用户名")
4、修改用户:db.updateUser("root",{roles:[role:"read",db:"admin"]})
5、修改用户密码:db.changeUserPassword("USER_NAME","PASSWORD")

Spring boot工程使用Mongodb
一、导入工程依赖 org.springframework.boot spring-boot-starter-data-mongodb
二、创建dao,继承MongoRepository,并指定实体类型和主键类型
三、dao接口继承MongoRepository,就可以使用save,delete等方法,也可以和spring Data Jpa一样,通过自定义方法等规则,如findByXXX,findByXXXAndXXX,countByXXXAndXXX等规则定义方法,实现查询操作。
数据库
2019-10-19 22:07:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
首先说一下数据库事务的四大特性
1 ACID
事务的四大特性是ACID(不是"酸"....)
(1) A:原子性(Atomicity)
原子性指的是事务要么完全执行,要么完全不执行.
(2) C:一致性(Consistency)
事务完成时,数据必须处于一致的状态.若事务执行途中出错,会回滚到之前的事务没有执行前的状态,这样数据就处于一致的状态.若事务出错后没有回滚,部分修改的内容写入到了数据库中,这时数据就是不一致的状态.
(3) I:隔离性(Isolation)
同时处理多个事务时,一个事务的执行不能被另一个事务所干扰,事务的内部操作与其他并发事务隔离.
(4) D:持久性(Durability)
事务提交后,对数据的修改是永久性的.
2 Mysql的锁
Mysql的锁其实可以按很多种形式分类: 按加锁机制分,可分为乐观锁与悲观锁. 按兼容性来分,可分为X锁与S锁. 按锁粒度分,可分为表锁,行锁,页锁. 按锁模式分,可分为记录锁,gap锁,next-key锁,意向锁,插入意向锁.
这里主要讨论S锁,X锁,乐观锁与悲观锁.
(1) S锁与X锁
S锁与X锁是InnoDB引擎实现的两种标准行锁机制 .查看默认引擎可使用 show variables like '%storage_engine%';
作者的mysql版本为8.0.17,结果如下:
先建好测试库与测试表,很简单,表就两个字段. create database test; use test; create table a ( id int primary key auto_increment, money int );
Ⅰ.S锁
S锁也叫共享锁,读锁,数据只能被读取不能被修改. 玩一下,上锁! lock table a read;
然后.....
只能读不能改,删,也不能增.
Ⅱ.X锁
X锁也叫排他锁,写锁,一个事务对表加锁后,其他事务就不能对其进行加锁与增删查改操作.
设置手动提交,开启事务,上X锁. set autocmmmit=0; start transaction; lock table a write;
在开启另一个事务,使用select语句. set autocommit=0; start transaction; select * from a;
这里是阻塞select操作,因为一直都没释放X锁.
同样也不能再加锁,也是阻塞中.
回到原来那个加锁的事务,嗯,什么事也没有,正常读写.
释放锁后: unlock table;

在另一个事务中可以看到中断时间.
(2) 乐观锁与悲观锁
Ⅰ.乐观锁
乐观锁就是总是假设是最好的情况,每次去操作的时候都不会上锁,但在更新时会判断有没有其他操作去更新这个数据,是一种宽松的加锁机制. mysql本身没有提供乐观锁的支持,需要自己来实现,常用的方法有版本控制和时间戳控制两种. 版本控制 版本控制就是为表增加一个version字段,读取数据时连同这个version字段一起读出来,之后进行更新操作,版本号加1,再将提交的数据的版本号与数据库中的版本号进行比较,若提交的数据的版本号大于数据库中的版本号才会进行更新.
举个例子,假设此时version=1,A进行操作,更新数据后version=2,与此同时B也进行操作,更新数据后version=2,A先完成操作,率先将数据库中的version设置为2,此时B提交,B的version与数据库中的version一样,不接受B的提交. 时间戳控制 时间戳控制与版本控制差不多,把version字段改为timestamp字段 还有一种实现方法叫CAS算法,这个作者不怎么了解,有兴趣可以自行搜索.
Ⅱ.悲观锁
悲观锁就是总是假设最坏的情况,在整个数据处理状态中数据处于锁定状态,悲观锁的实现往往依靠数据库的锁机制.每次在拿到数据前都会上锁. mysql在调用一些语句时会上悲观锁,如(先关闭自动提交,开启事务): set autocommit=0; start transaction;
两个事务都这样操作,然后其中一个事务输入: select * from a where xxx for update;
在另一事务也这样输入:
这时语句会被阻塞,直到上锁的那个事务commit(解开悲观锁).

在另一事务中可以看到这个事务被阻塞了2.81s. *** lock in share mode.
也会加上悲观锁.
4 脏读,幻读,不可重复读与两类丢失更新
(1) 脏读
脏读是指一个事务读取到了另一事务未提交的数据,造成select前后数据不一致.
比如事务A修改了一些数据,但没有提交,此时事务B却读取了,这时事务B就形成了脏读,一般事务A的后续操作是回滚,事务B读取到了临时数值.
事务A 事务B
开始事务 开始事务
更新X,旧值X=1,新值X=2  
  读取X,X=2(脏读)
回滚X=1
结束事务(X=1)
 
结束事务
(2) 幻读
幻读是指并不是指同一个事务执行两次相同的select语句得到的结果不同,而是指select时不存在某记录,但准备插入时发现此记录已存在,无法插入,这就产生了幻读.
事务A 事务B
开始事务 开始事务
select某个数据为空,准备插入一个新数据  
  插入一个新数据
  提交,结束事务
插入数据,发现插入失败,由于事务B已插入相同数据
结束事务
 
 
(3) 不可重复读
不可重复读指一个事务读取到了另一事务已提交的数据,造成select前后数据不一致. 比如事务A修改了一些数据并且提交了,此时事务B却读取了,这时事务B就形成了不可重复读.
事务A 事务B
开始事务 开始事务
读取X=1 读取X=1
更新X=2  
提交,结束事务  
 
 
读取X=2
结束事务
(4) 第一类丢失更新
第一类丢失更新就是两个事务同时更新一个数据,一个事务更新完毕并提交后,另一个事务回滚,造成提交的更新丢失.
事务A 事务B
开始事务 开始事务
读取X=1 读取X=1
修改X=2 修改X=3
  提交,结束事务
回滚
结束事务(X=1)
 
X=1,X本应为提交的3
(5) 第二类丢失更新
第二类丢失更新就是两个事务同时更新一个数据,先更新的事务提交的数据会被后更新的事务提交的数据覆盖,即先更新的事务提交的数据丢失.
事务A 事务B
开始事务 开始事务
读取X=1 读取X=1
更新X=2  
提交事务,X=2,结束  
 
 
更新X=3
提交事务,X=3,事务A的更新丢失,结束
5 封锁协议与隔离级别
封锁协议就是在用X锁或S锁时制定的一些规则,比如锁的持续时间,锁的加锁时间等.不同的封锁协议对应不同的隔离级别.事务的隔离级别一共有4种,由低到高分别是Read uncommitted,Read committed,Repeatable read,Serializable,分别对应的相应的封锁协议等级.
(1) 一级封锁协议
一级封锁协议对应的是Read uncommitted隔离级别,Read uncommitted,读未提交,一个事务可以读取另一个事务未提交的数据,这是最低的级别.一级封锁协议本质上是在事务修改数据之前加上X锁,直到事务结束后才释放,事务结束包括正常结束(commit)与非正常结束(rollback).
一级封锁协议不会造成更新丢失,但可能引发脏读,幻读,不可重复读. 设置手动提交与事务隔离等级为read uncommited,并开启事务(注意要先设置事务等级再开启事务). set autocommit=0; set session transaction isolation level read uncommitted; start transaction;
(中间有一行打多了一个t可以忽略.....)
a.引发脏读
在一个事务中修改表中的值,不提交,另一个事务可以select到未提交的值.

出现了脏读.
b.引发幻读
在一个事务中插入一条数据,提交.
另一事务中select时没有,准备insert,但是insert时却提示已经存在.引发幻读.
c.引发不可重复读
未操作提交前:
另一事务修改并提交:
再次读:
引发不可重复读.
(2) 二级封锁协议
二级封锁协议本质上在一级协议的基础上(在修改数据时加X锁),在读数据时加上S锁,读完后立即释放S锁,可以避免脏读.但有可能出现不可重复读与幻读.二级封锁协议对应的是Read committed与Repeatable Read隔离级别.
先设置隔离等级 set session transaction isolation level read committed;
Ⅰ.Read committed
Read committed,读提交,读提交可以避免脏读,但可能出现幻读与不可重复读.
a.避免脏读
开启一个事务并更新值,在这个事务中money=100(更新后)
另一事务中money为未更新前的值,这就避免了脏读.
注意,事实上脏读在read committed隔离级别下是不被允许的,但是mysql不会阻塞查询,而是返回未修改之前数据的备份,这种机制叫MVCC机制(多版本并发控制).
b.引发幻读
在一个事务中插入数据并提交.
另一事务中不能插入"不存在"的数据,出现幻读.
c.引发不可重复读
事务修改并提交前:
事务修改并提交:
出现不可重复读.
Ⅱ.Repeatable read
Repeatable read比Read committed严格一点,是Mysql的默认级别,读取过程更多地受到MVCC影响,可防止不可重复读与脏读,但仍有可能出现幻读.
a.避免脏读
在一个事务中修改数据,不提交.
另一事务中两次select的结果都不变,没有出现脏读.
b.避免不可重复读
一个事务修改数据并提交.
另一事务中select的结果没有发生改变,即没有出现不可重复读.
c.引发幻读
同理,一个事务插入一条数据并提交.
另一个事务插入时出现幻读.
(3) 三级封锁协议
三级封锁协议,在一级封锁协议的基础上(修改时加X锁),读数据时加上S锁(与二级类似),但是直到事务结束后才释放S锁,可以避免幻读,脏读与不可重复读.三级封锁协议对应的隔离级别是Serializable.
先设置Serializable隔离级别 set session transaction isolation level serializable
a.避免脏读
设置事务隔离等级后开启事务并update,发现堵塞.从而避免了脏读.
b.避免幻读
插入时直接阻塞,避免了幻读.
c.避免不可重复读
在脏读的例子中可以知道,update会被堵塞,都不能提交事务,因此也避免了不可重复读.
6 两段锁协议
事务必须分为两个阶段对数据进行加锁与解锁,两端锁协议叫2PL(不是2PC),所有的加锁都在解锁之前进行.
(1) 加锁
加锁会在更新或者 select *** for update *** lock in share mode
时进行
(2) 解锁
解锁在事务结束时进行,事务结束包括rollback与commit.
7.最后
这是作者的CSDN地址
最后,以下是作者的微信公众号,里面有更多精彩文章,欢迎关注.一起学习,一起成长.
参考链接 1: ACID1
2: ACID2
3: mysql的锁1
4: 乐观锁与悲观锁1
5: 乐观锁与悲观锁2
6: 乐观锁与悲观锁3
7: mysql修改事务隔离等级
8: mysql三级封锁与二段锁
9: 数据库封锁协议
10: mysql事务隔离机制1
11: mysql事务隔离机制2
12: mysql幻读
13: mysql脏读,不可重复读与幻读
14: mysql两段锁1
15: mysql两段锁2
数据库
2019-10-17 17:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
概念就不多说了,直接上操作了
1. 首先创建索引 PUT /test1 { "mappings": { "task": { "properties": { "user": { "type": "keyword" } } } } } PUT /test { "mappings": { "task": { "properties": { "user": { "type": "keyword" } } } } }
2. 创建别名一个可写,一个只读 POST /_aliases { "actions" : [ { "add" : { "index" : "test", "alias" : "alias1", "is_write_index" : true } }, { "add" : { "index" : "test1", "alias" : "alias1" } } ] }
3. 写入并查询结果 PUT /alias1/_doc/1 { "foo": "bar" } PUT /alias1/_doc/6 { "12222": "1111" } GET /test1/_search { "query": {"match_all": {}} } GET /test/_search { "query": {"match_all": {}} }
结果就不展示了 test1 没有数据 test里面有数据
数据库
2019-10-17 10:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 张晓宇(衷源) 阿里云容器平台技术专家
关注『阿里巴巴云原生』公众号,回复关键词“1010 ”,可获取本文 PPT。 导读: 资源利用率一直是很多平台管理和研发人员关心的话题。本文作者通过阿里巴巴容器平台团队在这一领域的工作实践,整理出了一套资源利用提升的方案,希望能够带给大家带来一些讨论和思考。
引言
不知道大家有没有过这样的经历:当我们拥有了一套 Kubernetes 集群,然后开始部署应用的时候,我们应该给容器分配多少资源呢?
这很难说。由于 Kubernetes 自己的机制,我们可以理解容器的资源实质上是一个静态的配置。 如果我发现资源不足,为了分配给容器更多资源,我们需要重建 Pod; 如果分配冗余的资源,那么我们的 worker node 节点似乎又部署不了多少容器。
试问,我们能做到容器资源的按需分配吗?接下来,我们将在本次分享中和大家一起进行探讨这个问题的答案。
生产环境中的真实挑战
首先请允许我们根据我们的实际情况抛出我们实际生产环境的挑战。或许大家还记得 2018 年的天猫双 11,这一天的总成交额达到了 2135 亿。由此一斑可窥全豹,能够支撑如此庞大规模的交易量背后的系统,其应用种类和数量应该是怎样的一种规模。
在这种规模下,我们常常听到的容器调度,如:容器编排,负载均衡,集群扩缩容,集群升级,应用发布,应用灰度等等这些词,在被“超大规模集群”这个词修饰后,都不再是件容易处理的事情。规模本身也就是我们最大的挑战。如何运营和管理好这么一个庞大的系统,并遵循业界 dev-ops 宣传的那样效果,犹如让大象去跳舞。但是马老师曾说过,大象就该干大象该干的事情,为什么要去跳舞呢。
Kubernetes 的帮助
大象是否可以跳舞,带着这个问题,我们需要从淘宝、天猫等 APP 背后系统说起。
这套互联网系统应用部署大致可分为三个阶段,传统部署,虚拟机部署和容器部署。相比于传统部署,虚拟机部署有了更好的隔离性和安全性,但是在性能上不可避免的产生了大量损耗。而容器部署又在虚拟机部署实现隔离和安全的背景下,提出了更轻量化的解决方案。我们的系统也是沿着这么一条主航道上运行的。假设底层系统好比一艘巨轮,面对巨量的集装箱---容器,我们需要一个优秀的船长,对它们进行调度编排,让系统这艘大船可以避开层层险阻,操作难度降低,且具备更多灵活性,最终达成航行的目的。
理想与现实
在开始之初,想到容器化和 Kubernetes 的各种美好场景,我们理想中的容器编排效果应该是这样的: 从容:我们的工程师更加从容的面对复杂的挑战,不再眉头紧锁而是更多笑容和自信; 优雅:每一次线上变更操作都可以像品着红酒一样气定神闲,优雅地按下执行的回车键; 有序:从开发到测试,再到灰度发布,一气呵成,行云流水; 稳定:系统健壮性良好,任尔东西南北风,我们系统岿然不动。全年系统可用性 N 多个 9; 高效:节约出更多人力,实现“快乐工作,认真生活”。
然而理想很丰满,现实很骨感。迎接我们的却是杂乱和形态各异的窘迫。
杂乱,是因为作为一个异军突起的新型技术栈,很多配套工具和工作流的建设处于初级阶段。Demo 版本中运行良好的工具,在真实场景下大规模铺开,各种隐藏的问题就会暴露无遗,层出不穷。从开发到运维,所有的工作人员都在各种被动地疲于奔命。另外,“大规模铺开”还意味着,要直接面对形态各异的生产环境:异构配置的机器、复杂的需求,甚至是适配用户的既往的使用习惯等等。
除了让人心力交瘁的混乱,系统还面临着应用容器的各种崩溃问题:内存不足导致的 OOM,CPU quota 分配太少,导致进程被 throttle,还有带宽不足,响应时延大幅上升...甚至是交易量在面对访问高峰时候由于系统不给力导致的断崖式下跌等等。这些都使我们在大规模商用 Kubernetes 场景中积累非常多的经验。
直面问题
稳定性
问题总要进行面对的。正如某位高人说过:如果感觉哪里不太对,那么肯定有些地方出问题了。于是我们就要剖析,问题究竟出在哪里。针对于内存的 OOM,CPU 资源被 throttle,我们可以推断我们给与容器分配的初始资源不足。
资源不足就势必造成整个应用服务稳定性下降。例如上图的场景:虽然是同一种应用的副本,或许是由于负载均衡不够强大,或者是由于应用自身的原因,甚至是由于机器本身是异构的,相同数值的资源,可能对于同一种应用的不同副本并具有相等的价值和意义。在数值上他们看似分配了相同的资源,然而在实际负载工作时,极有可能出现的现象是肥瘦不均的。
而在资源 overcommit 的场景下,应用在整个节点资源不足,或是在所在的 CPU share pool 资源不足时,也会出现严重的资源竞争关系。资源竞争是对应用稳定性最大的威胁之一。所以我们要尽力在生产环境中清除所有的威胁。
我们都知道稳定性是件很重要的事情,尤其对于掌控上百万容器生杀大权的一线研发人员。或许不经心的一个操作就有可能造成影响面巨大的生产事故。
因此,我们也按照一般流程做了系统预防和兜底工作。 在预防维度,我们可以进行全链路的压力测试,并且提前通过科学的手段预判应用需要的副本数和资源量。如果没法准确预算资源,那就只采用冗余分配资源的方式了。 在兜底维度,我们可以在大规模访问流量抵达后,对不紧要的业务做服务降级并同时对主要应用进行临时扩容。
但是对于陡然增加几分钟的突增流量,这么多组合拳的花费不菲,似乎有些不划算。或许我们可以提出一些解决方案,达到我们的预期。
资源利用率
回顾一下我们的应用部署情况:节点上的容器一般分属多种应用,这些应用本身不一定,也一般不会同时处于访问的高峰。对于混合部署应用的宿主机,如果能都错峰分配上面运行容器的资源或许更科学。
应用的资源需求可能就像月亮一样有阴晴圆缺,有周期变化。例如在线业务,尤其是交易业务,它们在资源使用上呈现一定的周期性,例如:在凌晨、上午时,它的使用量并不是很高,而在午间、下午时会比较高。
打个比方:对于 A 应用的重要时刻,对于 B 应用可能不那么重要,适当打压 B 应用,腾挪出资源给 A 应用,这是个不错的选择。这听起来有点像是分时复用的感觉。但是如果我们按照流量峰值时的需求配置资源就会产生大量的浪费。
除了对于实时性要求很高的在线应用外,我们还有离线应用和实时计算应用等:离线计算对于 CPU 、Memory 或网络资源的使用以及时间不那么敏感,所以在任何时间段它都可以运行;实时计算,可能对于时间敏感性就会很高。
早期,我们业务是在不同的节点按照应用的类型独立进行部署。从上面这张图来看,如果它们进行分时复用资源,针对实时性这个需求层面,我们会发现它实际的最大使用量不是 2+2+1=5,而是某一时刻重要紧急应用需求量的最大值,也就是 3 。如果我们能够数据监测到每个应用的真实使用量,给它分配合理值,那么就能产生资源利用率提升的实际效果。
对于电商应用,对于采用了重量级 Java 框架和相关技术栈的 Web 应用,短时间内 HPA 或者 VPA 都不是件容易的事情。
先说 HPA,我们或许可以秒级拉起了 Pod,创建新的容器,然而拉起的容器是否真的可用呢。从创建到可用,可能需要比较久的时间,对于大促和抢购秒杀-这种访问量“洪峰”可能仅维持几分钟或者十几分钟的实际场景,如果我们等到 HPA 的副本全部可用,可能市场活动早已经结束了。
至于社区目前的 VPA 场景,删掉旧 Pod,创建新 Pod,这样的逻辑更难接受。所以综合考虑,我们需要一个更实际的解决方案弥补 HPA 和 VPA 的在这一单机资源调度的空缺。
解决方案
交付标准
我们首先要对解决方案设定一个可以交付的标准那就是—— “既要稳定性,也要利用率,还要自动化实施,当然如果能够智能化那就更好”,然后再交付标准进行细化: 安全稳定:工具本身高可用。所用的算法和实施手段必须做到可控; 业务容器按需分配资源:可以及时根据业务实时资源消耗对不太久远的将来进行资源消耗预测,让用户明白业务接下来对于资源的真实需求; 工具本身资源开销小:工具本身资源的消耗要尽可能小,不要成为运维的负担; 操作方便,扩展性强:能做到无需接受培训即可玩转这个工具,当然工具还要具有良好扩展性,供用户 DIY; 快速发现 & 及时响应:实时性,也就是最重要的特质,这也是和HPA或者VPA在解决资源调度问题方式不同的地方。
设计与实现
上图是我们最初的工具流程设计:当一个应用面临很高的业务访问需求时,体现在 CPU、Memory 或其他资源类型需求量变大,我们根据 Data Collector 采集的实时基础数据,利用 Data Aggregator 生成某个容器或整个应用的画像,再将画像反馈给 Policy engine。 Policy engine 会瞬时快速修改容器 Cgroup 文件目录下的的参数。
我们最早的架构和我们的想法一样朴实,在 kubelet 进行了侵入式的修改。虽然我们只是加了几个接口,但是这种方式确实不够优雅。每次 kubenrnetes 升级,对于 Policy engine 相关组件升级也有一定的挑战。
为了做到快速迭代并和 Kubelet 解耦,我们对于实现方式进行了新的演进。那就是将关键应用容器化。这样可以达到以下功效: 不侵入修改 K8s 核心组件; 方便迭代&发布; 借助于 Kubernetes 相关的 QoS Class 机制,容器的资源配置,资源开销可控。
当然在后续演进中,我们也在尝试和 HPA,VPA 进行打通,毕竟这些和 Policy engine 存在着互补的关系。因此我们架构进一步演进成如下情形。当 Policy engine 在处理一些更多复杂场景搞到无力时,上报事件让中心端做出更全局的决策。水平扩容或是垂直增加资源。
下面我们具体讨论一下 Policy engine 的设计。Policy engine 是单机节点上进行智能调度并执行 Pod 资源调整的核心组件。它主要包括 api server,指挥中心 command center 和执行层 executor。 其中 api server 用于服务外界对于 policy engine 运行状态的查询和设置的请求; command center 根据实时的容器画像和物理机本身的负载以及资源使用情况,作出 Pod 资源调整的决策; Executor 再根据 command center 的决策,对容器的资源限制进行调整。同时,executor 也把每次调整的 revision info 持久化,以便发生故障时可以回滚。
指挥中心定期从 data aggregator 获取容器的实时画像,包括聚合的统计数据和预测数据,首先判断节点状态,例如节点磁盘异常,或者网络不通,表示该节点已经发生异常,需要保护现场,不再对Pod进行资源调整,以免造成系统震荡,影响运维和调试。如果节点状态正常,指挥中心会策略规则,对容器数据进行再次过滤。比如容器 CPU 率飙高,或者容器的响应时间超过安全阈值。如果条件满足,则对满足条件的容器集合给出资源调整建议,传递给executor。
在架构设计上,我们遵循了以下原则: 插件化:所有的规则和策略被设计为可以通过配置文件来修改,尽量与核心控制流程的代码解耦,与 data collector 和 data aggregator 等其他组件的更新和发布解耦,提升可扩展性; 稳定,这包括以下几个方面: 控制器稳定性。指挥中心的决策以不影响单机乃至全局稳定性为前提,包括容器的性能稳定和资源分配稳定。例如,目前每个控制器仅负责一种 cgroup 资源的控制,即在同一时间窗口内,Policy engine 不同时调整多种资源,以免造成资源分配震荡,干扰调整效果; 触发规则稳定性。例如,某一条规则的原始触发条件为容器的性能指标超出安全阈值,但是为避免控制动作被某一突发峰值触发而导致震荡,我们把触发规则定制为:过去一段时间窗口内性能指标的低百分位超出安全阈值;如果规则满足,说明这段时间内绝大部分的性能指标值都已经超出了安全阈值,就需要触发控制动作了; 另外,与社区版 Vertical-Pod-Autoscaler 不同,Policy engine 不主动驱逐腾挪容器,而是直接修改容器的 cgroup 文件; 自愈:资源调整等动作的执行可能会产生一些异常,我们在每个控制器内都加入了自愈回滚机制,保证整个系统的稳定性; 不依赖应用先验知识:为所有不同的应用分别进行压测、定制策略,或者提前对可能排部在一起的应用进行压测,会导致巨大开销,可扩展性降低。我们的策略在设计上尽可能通用,尽量采用不依赖于具体平台、操作系统、应用的指标和控制策略。
在资源调整方面,Cgroup 支持我们对各个容器的 CPU、内存、网络和磁盘 IO 带宽资源进行隔离和限制,目前我们主要对容器的 CPU 资源进行调整,同时在测试中探索在时分复用的场景下动态调整 memory limit 和 swap usage 而避免 OOM 的可行性;在未来我们将支持对容器的网络和磁盘 IO 的动态调整。
调整效果
上图展示了我们在测试集群得到的一些实验结果。我们把高优先级的在线应用和低优先级的离线应用混合部署在测试集群里。SLO 是 250ms,我们希望在线应用的 latency 的 95 百分位值低于阈值 250ms。
在实验结果中可以看到: 在大约90s前,在线应用的负载很低;latency 的均值和百分位都在 250ms 以下; 到了 90s后,我们给在线应用加压,流量增加,负载也升高,导致在线应用 latency 的 95 百分位值超过了 SLO; 在大约 150s 左右,我们的小步快跑控制策略被触发,渐进式地 throttle 与在线应用发生资源竞争的离线应用; 到了大约 200s 左右,在线应用的性能恢复正常,latency 的 95 百分位回落到 SLO 以下。
这说明了我们的控制策略的有效性。
经验和教训
下面我们总结一下在整个项目的进行过程中,我们收获的一些经验和教训,希望这些经验教训能够对遇到类似问题和场景的人有所帮助。 避开硬编码,组件微服务化,不仅便于快速演进和迭代,还有利于熔断异常服务。 尽可能不要调用类库中还是 alpha 或者 beta 特性的接口。 例如我们曾经直接调用 CRI 接口读取容器的一些信息,或者做一些更新操作,但是随着接口字段或者方法的修改,共建有些功能就会变得不可用,或许有时候,调用不稳定的接口还不如直接获取某个应用的打印信息可能更靠谱。 基于 QoS 的资源动态调整方面:如我们之前所讲,阿里集团内部有上万个应用,应用之间的调用链相当复杂。应用 A 的容器性能发生异常,不一定都是在单机节点上的资源不足或者资源竞争导致,而很有可能是它下游的应用 B、应用 C,或者数据库、cache 的访问延迟导致的。由于单机节点上这种信息的局限性,基于单机节点信息的资源调整,只能采用“尽力而为”,也就是 best effort 的策略了。在未来,我们计划打通单机节点和中心端的资源调控链路,由中心端综合单机节点上报的性能信息和资源调整请求,统一进行资源的重新分配,或者容器的重新编排,或者触发 HPA,从而形成一个集群级别的闭环的智能资源调控链路,这将会大大提高整个集群维度的稳定性和综合资源利用率。 资源v.s.性能模型:可能有人已经注意到,我们的调整策略里,并没有明显地提出为容器建立“资源v.s.性能”的模型。这种模型在学术论文里非常常见,一般是对被测的几种应用进行了离线压测或者在线压测,改变应用的资源分配,测量应用的性能指标,得到性能随资源变化的曲线,最终用在实时的资源调控算法中。在应用数量比较少,调用链比较简单,集群里的物理机硬件配置也比较少的情况下,这种基于压测的方法可以穷举到所有可能的情况,找到最优或者次优的资源调整方案,从而得到比较好的性能。但是在阿里集团的场景下,我们有上万个应用,很多重点应用的版本发布也非常频繁,往往新版本发布后,旧的压测数据,或者说资源性能模型,就不适用了。另外,我们的集群很多是异构集群,在某一种物理机上测试得到的性能数据,在另一台不同型号的物理机上就不会复现。这些都对我们直接应用学术论文里的资源调控算法带来了障碍。所以,针对阿里集团内部的场景,我们采用了这样的策略:不对应用进行离线压测,获取显示的资源性能模型。而是建立实时的动态容器画像,用过去一段时间窗口内容器资源使用情况的统计数据作为对未来一小段时间内的预测,并且动态更新;最后基于这个动态的容器画像,执行小步快跑的资源调整策略,边走边看,尽力而为。
总结与展望
总结起来,我们的工作主要实现了以下几方面的收益: 通过分时复用以及将不同优先级的容器(也就是在线和离线任务)混合部署,并且通过对容器资源限制的动态调整,保证了在线应用在不同负载情况下都能得到足够的资源,从而提高集群的综合资源利用率。 通过对单机节点上的容器资源的智能动态调整,降低了应用之间的性能干扰,保障高优先级应用的性能稳定性 各种资源调整策略通过 Daemonset 部署,可以自动地、智能地在节点上运行,减少人工干预,降低了运维的人力成本。
展望未来,我们希望在以下几个方面加强和扩展我们的工作: 闭环控制链路:前面已经提到,单机节点上由于缺乏全局信息,对于资源的调整有其局限性,只能尽力而为。未来,我们希望能够打通与 HPA 和 VPA 的通路,使单机节点和中心端联动进行资源调整,最大化弹性伸缩的收益。 容器重新编排:即使是同一个应用,不同容器的负载和所处的物理环境也是动态变化的,单机上调整 pod 的资源,不一定能够满足动态的需求。我们希望单机上实时容器画像,能够为中心端提供更多的有效信息,帮助中心端的调度器作出更加智能的容器重编排决策。 策略智能化:我们现在的资源调整策略仍然比较粗粒度,可以调整的资源也比较有限;后续我们希望让资源调整策略更加智能化,并且考虑到更多的资源,比如对磁盘和网络IO带宽的调整,提高资源调整的有效性。 容器画像精细化:目前的容器画像也比较粗糙,仅仅依靠统计数据和线性预测;刻画容器性能的指标种类也比较局限。我们希望找到更加精确的、通用的、反映容器性能的指标,以便更加精细地刻画容器当前的状态和对不同资源的需求程度。 查找干扰源:我们希望能找到在单机节点上找到行之有效的方案,来精准定位应用性能受损时的干扰源;这对策略智能化也有很大意义。
Q & A
Q1: 直接修改 cgroup 容器一定会获得资源吗?
A1: 容器技术隔离的技术基础就是 cgroup 层面。在宿主机腾出足够资源的情况下,给 cgroup 设置更大的值可以获取更多的资源。同理,对于一般优先级不高的应用,设置较低的 cgroup 资源值就会达到抑制容器运行的效果。
Q2: 底层是如何区分在线和离线优先级的?
A2: 底层是无法自动获取谁是在线,谁是离线,或者谁的优先级高,谁的优先级低的。这个我们可以通过各种 Kubernetes 提供的扩展实现。最简单的是通过 label,Annotation 标识。当然通过扩展 QoS class 也是一种思路。社区版本的 QoS class设置太过于保守,给予用户发挥的空间不大。我们通过这些方面也进行了增强。在合适的时候或许会推向社区。自动感知是个方向,感知谁是干扰源,感知谁是某种资源型应用,这块我们还在研发中。做到真正的动态,肯定是具备自动感知的智能系统。
Q3: “与社区版 Vertical-Pod-Autoscaler 不同,Policy engine 不主动驱逐腾挪容器,而是直接修改容器的 cgroup 文件”,想问一下,不主动驱逐的话,如果 Node 的资源达到上线了会怎么处理?
A3: 这是一个好问题。首先这里要先区分是哪种资源,如果是 CPU 型的,我们可以调整低优先级容器的 cgroup 下 cpu quota 的值,首先抑制低优先级的容器对于 CPU 的争抢。然后再适当上调高优先级容器的相关资源值。如果是内存型资源,这个不能直接去缩小低优先级容器的 cgroup 值,否则会造成 OOM,对于学习内存型资源的调整,我们会在其他分享中继续讨论。这个技术比较特殊。
Q4: 只修改 cgroup,怎么保证 K8s 对单个物理机能够分配更多的容器?
A4: 文字直播有了一定说明,容器的资源消耗并非是一成不变的,很多时候它们的资源消耗呈现潮汐现象,相同的资源条件下部署更多应用,完成更多作业就是达到资源利用的最大化的效果。资源出现超卖才是我们这个主题讨论的最大价值。
Q5: 也就是说,低优先级的容器,request 设置的比 limit 小很多,然后你们再动态的调整 cgroup?
A5: 在现有 QoS 场景下,你可以理解被调整的 Pod 都是 burstable 的。但是我们并不是直接调整 Pod 元数据的 limit 的值,而是调整 limit 在 cgroup 反映的值,这个值在资源竞争缓和的时候还会被调整回去的。我们并不建议单机的 cgroup 数据和 etcd 的中心数据割裂太久。如果长期偏离,我们会像 VPA 发出警报,联动 VPA 做调整。当然在容器运行的高峰期,任何重建容器的操作都是不明智的。
Q6: 整体的理解就是你们开始就让物理机超配了一定比例的 pod,然后通过策略动态调整容器的 cgroup 值?
A6: 如果资源完全是富足冗余的,这个动态调整也有一定意义。就是并非资源用满场景下,高优先级应用会被干扰,实际上,当主机的 CPU 达到一定比例,打个比方例如 50%,应用的时延就变大。为了完全确保高优先级应用的 SLO,牺牲低优先级的 CPU 正常运行也是有价值的。
Q7: Policy engine 有没有考虑开源?
A7: 有计划进行开源,Policy engine 更多的是和自身的应用属性相关,电商应用或者大数据处理应用的策略都是不相同的,我们开源会首先开源框架和附带一些简单的策略,更多的策略可以用户自定义。
Q8: 我之前遇到的大部分应用都无法正确感知 cgroup 的配置,因此很多情况都需要在启动参数里面根据 cpu 或者 mem 设置参数,那么也就是说即使改变了 cgroup 对于他们来说都无效,那么使用场景也就有限了
A8: 限制容器的资源使用这个还是有价值的。限制低优先级应用本身也可以提升高优先级应用的 SLO,虽然效果没有那么明显。稳定性的考量同样也很重要。
Q9: Policy engine 目前在阿里的使用如何?在生产上有多上的规模使用这种方式进行动态调整?是否和社区的 HPA VPA 配合使用?
A9: Policy engine 在阿里某些集群已经使用。至于规模暂时无法透漏。涉及到很多组件之间的联动,社区的 HPA 和 VPA 目前都不太能满足我们的需求。因此阿里的 HPA 和 VPA 都是我们自行开发的,但是和社区的原理是一致的。阿里 HPA 的开源可以关注 Openkruise 社区。VPA 开源计划我这里还没有确切消息。
Q10: 当单机节点资源不足以提供容器扩容时,目前是否可以进行 HPA 或 VPA 扩容呢?
A10: 单机节点不足的时候,应用可以通过 HPA 进行增加副本应对。但是 VPA 如果选择原节点进行更新的话,是失败的。只能调度到其他资源丰富的节点。在流量陡升的场景下,重建容器未必能满足需求,很可能导致雪崩,即重建过程中,整个应用其他未升级的副本接受更多流量,OOM 掉,新启动的容器再瞬间被 OOM,所以重启容器需要慎重。快速扩容(HPA)或者快速提升高优先级资源,抑制低优先级容器资源的方式效果更明显。
关注『阿里巴巴云原生』公众号,回复关键词“1010 ”,可获取本文 PPT。

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-10-12 15:18:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者:黄梦龙
众所周知, PD 是整个 TiDB 集群的核心,负责全局元信息的存储以及 TiKV 集群负载均衡调度,本文将详细介绍 PD 调度系统的原理,并通过几个典型场景的分析和处理方式,分享调度策略的最佳实践和调优方法,帮助大家在使用过程中快速定位问题。本文内容基于 3.0 版本,更早的版本(2.x)缺少部分功能的支持,但是基本原理类似,也可以以本文作为参考。
PD 调度原理
概念
首先我们介绍一下调度系统涉及到的相关概念,理解这些概念以及它们相互之间的关系,有助于在实践中快速定位问题并通过配置进行调整。 Store
PD 中的 Store 指的是集群中的存储节点,也就是 tikv-server 实例。注意 Store 与 TiKV 实例是严格一一对应的,即使在同一主机甚至同一块磁盘部署多个 TiKV 实例,这些实例也会对应不同的 Store。 Region / Peer / Raft Group
每个 Region 负责维护集群的一段连续数据(默认配置下平均约 96 MiB),每份数据会在不同的 Store 存储多个副本(默认配置是 3 副本),每个副本称为 Peer。同一个 Region 的多个 Peer 通过 raft 协议进行数据同步,所以 Peer 也用来指代 raft 实例中的成员。TiKV 使用 multi-raft 模式来管理数据,即每个 Region 都对应一个独立运行的 raft 实例,我们也把这样的一个 raft 实例叫做一个 Raft Group。 Leader / Follower / Learner
它们分别对应 Peer 的三种角色。其中 Leader 负责响应客户端的读写请求;Follower 被动地从 Leader 同步数据,当 Leader 失效时会进行选举产生新的 Leader;Learner 是一种特殊的角色,它只参与同步 raft log 而不参与投票,在目前的实现中只短暂存在于添加副本的中间步骤。 Region Split
TiKV 集群中的 Region 不是一开始就划分好的,而是随着数据写入逐渐分裂生成的,分裂的过程被称为 Region Split。
其机制是集群初始化时构建一个初始 Region 覆盖整个 key space,随后在运行过程中每当 Region 数据达到一定量之后就通过 Split 产生新的 Region。 Pending / Down
Pending 和 Down 是 Peer 可能出现的两种特殊状态。其中 Pending 表示 Follower 或 Learner 的 raft log 与 Leader 有较大差距,Pending 状态的 Follower 无法被选举成 Leader。Down 是指 Leader 长时间没有收到对应 Peer 的消息,通常意味着对应节点发生了宕机或者网络隔离。 Scheduler
Scheduler(调度器)是 PD 中生成调度的组件。PD 中每个调度器是独立运行的,分别服务于不同的调度目的。常用的调度器及其调用目标有: balance-leader-scheduler :保持不同节点的 Leader 均衡。 balance-region-scheduler :保持不同节点的 Peer 均衡。 hot-region-scheduler :保持不同节点的读写热点 Region 均衡。 evict-leader-{store-id} :驱逐某个节点的所有 Leader。(常用于滚动升级) Operator
Operator 是应用于一个 Region 的,服务于某个调度目的的一系列操作的集合。例如“将 Region 2 的 Leader 迁移至 Store 5”,“将 Region 2 的副本迁移到 Store 1, 4, 5” 等。
Operator 可以是由 Scheduler 通过计算生成的,也可以是由外部 API 创建的。 Operator Step
Operator Step 是 Operator 执行过程的一个步骤,一个 Operator 常常会包含多个 Operator Step。
目前 PD 可生成的 Step 包括: TransferLeader :将 Region Leader 迁移至指定 Peer AddPeer :在指定 Store 添加 Follower RemovePeer :删除一个 Region Peer AddLearner :在指定 Store 添加 Region Learner PromoteLearner :将指定 Learner 提升为 Follower SplitRegion :将指定 Region 一分为二
调度流程
宏观上来看,调度流程大体可划分为 3 个部分:
1. 信息收集
TiKV 节点周期性地向 PD 上报 StoreHeartbeat 和 RegionHeartbeat 两种心跳消息。其中 StoreHeartbeat 包含了 Store 的基本信息,容量,剩余空间,读写流量等数据, RegionHeartbeat 包含了 Region 的范围,副本分布,副本状态,数据量,读写流量等数据。PD 将这些信息梳理并转存供调度来决策。
2. 生成调度
不同的调度器从自身的逻辑和需求出发,考虑各种限制和约束后生成待执行的 Operator。这里所说的限制和约束包括但不限于: 不往断连中、下线中、繁忙、空间不足、在大量收发 snapshot 等各种异常状态的 Store 添加副本 Balance 时不选择状态异常的 Region 不尝试把 Leader 转移给 Pending Peer 不尝试直接移除 Leader 不破坏 Region 各种副本的物理隔离 不破坏 Label property 等约束
3. 执行调度
生成的 Operator 不会立即开始执行,而是首先会进入一个由 OperatorController 管理的一个等待队列。 OperatorController 会根据配置以一定的并发从等待队列中取出 Operator 进行执行,执行的过程就是依次把每个 Operator Step 下发给对应 Region 的 Leader。
最终 Operator 执行完毕会被标记为 finish 状态或者超时被标记为 timeout,并从执行列表中移除。
Balance
Region 负载均衡调度主要依赖 balance-leader 和 balance-region 这两个调度器,这二者的调度目标是将 Region 均匀地分散在集群中的所有 Store 上。它们的侧重点又有所不同: balance-leader 关注 Region 的 Leader,可以认为目的是分散处理客户端请求的压力; balance-region 关注 Region 的各个 Peer,目的是分散存储的压力,同时避免出现爆盘等状况。
balance-leader 与 balance-region 有着类似的调度流程,首先根据不同 Store 的对应资源量的情况分别打一个分,然后不断从得分较高的 Store 选择 Leader 或 Peer 迁移到得分较低的 Store 上。
这两者的分数计算上也有一定差异: balance-leader 比较简单,使用 Store 上所有 Leader 所对应的 Region Size 加和作为得分; balance-region 由于要考虑不同节点存储容量可能不一致的情况,会分三种情况,当空间富余时使用数据量计算得分(使不同节点数据量基本上均衡),当空间不足时由使用剩余空间计算得分(使不同节点剩余空间基本均衡),处于中间态时则同时考虑两个因素做加权和当作得分。
此外,为了应对不同节点可能在性能等方面存在差异的问题,我们还支持为 Store 设置 balance 权重。 leader-weight 和 region-weight 分别用于控制 leader 权重以及 region 权重,这两个配置的默认值都为 1 。假如把某个 Store 的 leader-weight 设为 2 ,调度稳定后,则该节点的 leader 数量约为普通节点的 2 倍;假如把某个 Store 的 region-weight 设为 0.5 ,那么调度稳定后该节点的 region 数量约为其他节点的一半。
热点调度
热点调度对应的调度器是 hot-region-scheduler 。目前 3.0 版本统计热点 Region 的方式比较单一,就是根据 Store 上报的信息,统计出持续一段时间读或写流量超过一定阈值的 Region,然后再用与 Balance 类似的方式把这些 Region 分散开来。
对于写热点,热点调度会同时尝试打散热点 Region 的 Peer 和 Leader;对于读热点,由于只有 Leader 承载读压力,热点调度会尝试将热点 Region 的 Leader 打散。
集群拓扑感知
让 PD 感知不同节点分布的拓扑是为了通过调度使不同 Region 的各个副本尽可能分散,保证高可用和容灾。例如集群有 3 个数据中心,最安全的调度方式就是把 Region 的 3 个 Peer 分别放置在不同的数据中心,这样任意一个数据中心故障时,都能继续提供服务。
PD 会在后台不断扫描所有 Region,当发现 Region 的分布不是当前的最优化状态时,会生成调度替换 Peer,将 Region 调整至最佳状态。
负责这个检查的组件叫 replicaChecker (跟 Scheduler 类似,但是不可关闭),它依赖于 location-labels 这个配置来进行调度。比如配置 [zone, rack, host] 定义了三层的拓扑结构:集群分为多个 zone(可用区),每个 zone 下有多个 rack(机架),每个 rack 下有多个 host(主机)。PD 在调度时首先会尝试将 Region 的 Peer 放置在不同的 zone,假如无法满足(比如配置 3 副本但总共只有 2 个 zone)则退而求其次保证放置在不同的 rack,假如 rack 的数量也不足以保证隔离,那么再尝试 host 级别的隔离,以此类推。
缩容及故障恢复
缩容是指预备将某个 Store 下线,通过命令将该 Store 标记为 Offline 状态,此时 PD 通过调度将待下线节点上的 Region 迁移至其他节点。故障恢复是指当有 Store 发生故障且无法恢复时,有 Peer 分布在对应 Store 上的 Region 会产生缺少副本的状况,此时 PD 需要在其他节点上为这些 Region 补副本。
这两种情况的处理过程基本上是一样的。由 replicaChecker 检查到 Region 存在异常状态的 Peer,然后生成调度在健康的 Store 创建新副本替换掉异常的。
Region merge
Region merge 指的是为了避免删除数据后大量小 Region 甚至空 Region 消耗系统资源,通过调度把相邻的小 Region 合并的过程。Region merge 由 mergeChecker 负责,其过程与 replicaChecker 类似,也是在后台遍历,发现连续的小 Region 后发起调度。
查询调度状态
查看调度系统的状态的手段主要包括:Metrics,pd-ctl,日志。本文简要介绍 Metrics 和 pd-ctl 两种方式,更具体的信息可以参考官方文档中 PD 监控 以及 PD Control 使用 的章节。
Operator 状态
Grafana PD / Operator 页面展示了 Operator 相关统计。其中比较重要的有: Schedule Operator Create :展示 Operator 的创建情况,从名称可以知道 Operator 是哪个调度器创建的以及创建的原因。 Operator finish duration :展示了 Operator 执行耗时的情况 Operator Step duration :展示不同 Operator Step 执行耗时的情况
查询 Operator 的 pd-ctl 命令有: operator show :查询当前调度生成的所有 Operator operator show [admin | leader | region] :按照类型查询 Operator
Balance 状态
Grafana PD / Statistics - Balance 页面展示了负载均衡相关统计,其中比较重要的有: Store Leader/Region score :展示每个 Store 的得分 Store Leader/Region count :展示每个 Store 的 Leader/Region 数量 Store available :展示每个 Store 的剩余空间
使用 pd-ctl 的 store 命令可以查询 Store 的得分,数量,剩余空间,weight 等信息。
热点调度状态
Grafana PD / Statistics - hotspot 页面展示了热点 Region 的相关统计,其中比较重要的有: Hot write Region’s leader/peer distribution :展示了写热点 Region 的 Leader/Peer 分布情况 Hot read Region’s leader distribution :展示了读热点 Region 的 Leader 分布情况
使用 pd-ctl 同样可以查询上述信息,可以使用的命令有: hot read :查询读热点 Region 信息 hot write :查询写热点 Region 信息 hot store :按 Store 统计热点分布情况 region topread [limit] :查询当前读流量最大的 Region region topwrite [limit] :查询当前写流量最大的 Region
Region 健康度
Grafana PD / Cluster / Region health 面板展示了异常状态 Region 数的统计,其中包括 Pending Peer,Down Peer,Offline Peer,以及副本数过多或过少的 Region。
通过 pd-ctl 的 region check 命令可以查看具体异常的 Region 列表: region check miss-peer :缺副本的 Region region check extra-peer :多副本的 Region region check down-peer :有副本状态为 Down 的 Region region check pending-peer :有副本状态为 Pending 的 Region
调度策略控制
在线调整调度策略主要使用 pd-ctl 工具来完成,可以通过以下 3 个方面来控制 PD 的调度行为。本文做一些简要介绍,更具体的信息可以参考官方文档中 PD Control 使用 的章节。
启停调度器
pd-ctl 支持动态创建和删除 Scheduler 的功能,我们可以通过这些操作来控制 PD 的调度行为,如下所示: scheduler show :显示当前系统中的 Scheduler scheduler remove balance-leader-scheduler :删除(停用)balance leader 调度器 scheduler add evict-leader-scheduler-1 :添加移除 Store 1 的所有 Leader 的调度器
手动添加 Operator
PD 还支持绕过调度器,直接通过 pd-ctl 来创建或删除 Operator,如下所示: operator add add-peer 2 5 :在 Store 5 上为 Region 2 添加 Peer operator add transfer-leader 2 5 :将 Region 2 的 Leader 迁移至 Store 5 operator add split-region 2 :将 Region 2 拆分为 2 个大小相当的 Region operator remove 2 :取消 Region 2 当前待执行的 Operator
调度参数调整
使用 pd-ctl 执行 config show 命令可以查看所有的调度参数,执行 config set {key} {value} 可以调整对应参数的值。这里举例说明常见的参数,更详情的说明请参考 PD 调度参数指南 : leader-schedule-limit :控制 Transfer Leader 调度的并发数 region-schedule-limit :控制增删 Peer 调度的并发数 disable-replace-offline-replica :停止处理节点下线的调度 disable-location-replacement :停止处理调整 Region 隔离级别相关的调度 max-snapshot-count :每个 Store 允许的最大收发 Snapshot 的并发数
典型场景分析与处理
1. Leader / Region 分布不均衡
需要说明的是,PD 的打分机制决定了一般情况下,不同 Store 的 Leader Count 和 Region Count 不一样多并不代表负载是不均衡的。需要从 TiKV 的实际负载或者存储空间占用来判断是否有 Balance 不均衡的状况。
确认存在 Leader / Region 分布不均衡的现象后,首先要观察不同 Store 的打分情况。
如果不同 Store 的打分是接近的 ,说明 PD 认为此时已经是均衡状态了,可能的原因有: 存在热点导致负载不均衡。需要根据热点调度相关的信息进一步分析,可以参考下文热点调度的部分。 存在大量的空 Region 或小 Region,导致不同 Store 的 Leader 数量差别特别大,导致 raftstore 负担过重。需要开启 Region Merge 并尽可能加速合并,可以参考下文关于 Region Merge 的部分。 不同 Store 的软硬件环境存在差异。可以酌情调整 leader-weight 和 region-weight 来控制 Leader / Region 的分布。 其他不明原因。也可以使用调整权重这个兜底的方法,通过调整 leader-weight 和 region-weight 来调整至用户觉得合理的分布。
如果不同 Store 的打分差异较大 ,需要进一步检查 Operator 相关 Metrics,特别关注 Operator 的生成和执行情况,这时大体上又分两种情况。
一种情况是生成的调度是正常的,但是调度的速度很慢 。可能的原因有: 调度速度受限于 limit 配置。PD 默认配置的 limit 比较保守,在不对正常业务造成显著影响的前提下,可以酌情将 leader-schedule-limit 或 region-schedule-limit 调大一些,此外, max-pending-peer-count 以及 max-snapshot-count 限制也可以放宽。 系统中同时运行有其它的调度任务产生竞争,导致 balance 速度上不去。这种情况下如果 balance 调度的优先级更高,可以先停掉其他的调度或者限制其他调度的速度。例如 Region 没均衡的情况下做下线节点操作,下线的调度与 Region Balance 会抢占 region-schedule-limit 配额,此时我们可以把 replica-schedule-limit 调小将下线调度的速度限制住,或者干脆设置 disable-replace-offline-replica = true 来暂时关闭下线流程。 调度执行得太慢。可以检查 Operator Step 的耗时来进行判断。通常不涉及到收发 Snapshot 的 Step(比如 TransferLeader , RemovePeer , PromoteLearner 等)的完成时间应该在毫秒级,涉及到 Snapshot 的 Step(如 AddLearner , AddPeer 等)的完成时间为数十秒。如果耗时明显过高,可能是 TiKV 压力过大或者网络等方面的瓶颈导致的,需要具体情况具体分析。
另一种情况是没能生成对应的 balance 调度 。可能的原因有: 调度器未被启用。比如对应的 Scheduler 被删除了,或者 limit 被设置为 0 。 由于其它约束无法进行调度。比如系统中有 evict-leader-scheduler ,此时无法把 Leader 迁移至对应的 Store。再比如设置了 Label property,也会导致部分 Store 不接受 Leader。 集群拓扑的限制导致无法均衡。比如 3 副本 3 数据中心的集群,由于副本隔离的限制,每个 Region 的 3 个副本都分别分布在不同的数据中心,假如这 3 个数据中心的 Store 数不一样,最后调度就会收敛在每个数据中心均衡,但是全局不均衡的状态。
2. 节点下线速度慢
这个场景还是从 Operator 相关 Metrics 入手,分析 Operator 的生成执行情况。
如果调度在正常生成,只是速度很慢。可能的原因有: 调度速度受限于 limit 配置。下线对应的 limit 参数是 replica-schedule-limit ,可以把它适当调大。与 Balance 类似, max-pending-peer-count 以及 max-snapshot-count 限制同样也可以放宽。 系统中同时运行有其它的调度任务产生竞争,或者调度执行得太慢了。处理方法在上一节已经介绍过了,不再赘述。 下线单个节点时,由于待操作的 Region 有很大一部分(3 副本配置下约 1/3)的 Leader 都集中在下线的节点上,下线速度会受限于这个单点生成 Snapshot 的速度。可以通过手动给这个节点添加一个 evict-leader 调度迁走 Leader 来加速。
如果没有对应的 Operator 调度生成,可能的原因有: 下线调度被关闭,或者 replica-schedule-limit 被设为 0。 找不到节点来转移 Region。例如相同 Label 的替代节点容量都大于 80%,PD 为了避免爆盘的风险会停止调度。这种情况需要添加更多节点,或者删除一些数据释放空间。
3. 节点上线速度慢
目前 PD 没有对节点上线特殊处理,节点上线实际上就是依靠 balance region 机制来调度的,所以参考前面 Region 分布不均衡的排查步骤即可。
4. 热点分布不均匀
热点调度的问题大体上可以分为以下几种情况。
一种是从 PD 的 metrics 能看出来有不少 hot Region,但是调度速度跟不上,不能及时地把热点 Region 分散开来。
解决方法是加大 hot-region-schedule-limit ,并减少其他调度器的 limit 配额,从而加快热点调度的速度。还有 hot-region-cache-hits-threshold 调小一些可以使 PD 对流量的变化更快做出反应。
第二种情况是单一 Region 形成热点的情况,比如大量请求频繁 scan 一个小表 。这个可以从业务角度或者 metrics 统计的热点信息看出来。由于单 Region 热点现阶段无法使用打散的手段来消除,需要确认热点 Region 后先手动添加 split-region 调度将这样的 Region 拆开。
还有一种情况是从 PD 的统计来看没有热点,但是从 TiKV 的相关 metrics 可以看出部分节点负载明显高于其他节点,成为整个系统的瓶颈。
这是因为目前 PD 统计热点 Region 的维度比较单一,仅针对流量进行分析,在某些场景下无法准备定位出热点。例如部分 Region 有大量的点查请求,从流量上来看并不显著,但是过高的 QPS 导致关键模块达到瓶颈。这个问题当前的处理方式是:首先从业务层面确定形成热点的 table,然后添加 scatter-range-scheduler 来使得这个 table 的所有 Region 均匀分布。TiDB 也在其 HTTP API 中提供了相关接口来简化这个操作,具体可以参考 TiDB HTTP API 文档 。
5. Region Merge 速度慢
与前面讨论过的所有调度慢的问题类似,Region Merge 速度慢也很有可能是受到 limit 限制(Region Merge 同时受限于 merge-schedule-limit 及 region-schedule-limit ),或者是与其他调度器产生了竞争,处理方法不再赘述了。
假如我们已经从统计得知系统中有大量的空 Region,这时可以通过把 max-merge-region-size 和 max-merge-region-keys 调整为较小值来加快 Merge 速度。这是因为 Merge 的过程涉及到副本迁移,于是 Merge 的 Region 越小,速度就越快。如果 Merge Operator 生成的速度已经有几百 opm,想进一步加快,还可以把 patrol-region-interval 调整为 "10ms" ,这个能加快巡检 Region 的速度,但是会消耗更多的 CPU。
还有一种特殊情况:曾经创建过大量 Table 然后又清空了(truncate 操作也算创建 Table),此时如果开启了 split table 特性,这些空 Region 是无法合并的,此时需要调整以下参数关闭这个特性: TiKV split-region-on-table 设为 false PD namespace-classifier 设为 “”
另外对于 3.0.4 和 2.1.16 以前的版本,Region 的统计 approximate_keys 在特定情况下(大部分发生在 drop table 之后)统计不准确,造成 keys 的统计值很大,无法满足 max-merge-region-keys 的约束,可以把 max-merge-region-keys 这个条件放开,调成很大的值来绕过这个问题。
6. TiKV 节点故障处理策略
没有人工介入时,PD 处理 TiKV 节点故障的默认行为是,等待半小时之后(可通过 max-store-down-time 配置调整),将此节点设置为 Down 状态,并开始为涉及到的 Region 补充副本。
实践中,如果能确定这个节点的故障是不可恢复的,可以立即做下线处理,这样 PD 能尽快补齐副本,降低数据丢失的风险。与之相对,如果确定这个节点是能恢复的,但可能半小时之内来不及,则可以把 max-store-down-time 临时调整为比较大的值,这样能避免超时之后产生不必要的补副本产生资源浪费。
总结
本文介绍了 PD 调度的概念,原理以及常见问题的处理方法,希望读者可以在理解调度系统的基础上,参考本文按图索骥解决生产中遇到的调度相关的问题。PD 的调度策略还在不断的演进和完善中,也期待大家踊跃提出宝贵的改进意见。
原文阅读 : https://pingcap.com/blog-cn/best-practice-pd/
数据库
2019-10-12 10:48:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
数据库存储引擎storage engine,又称数据表处理器,它是数据库底层软件的组织。数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。
数据表在硬盘上的存储方式
在文件系统中,MySQL将每个数据库 (也称schema)保存为数据目录下的一个子目录。创建表时,MySQL会在数据库子目录下创建一个与表同名的.frm文件保存表的定义。
MySQL使用文件系统的目录来保存数据库和表的定义,大小写敏感性与具体的操作系统的文件系统相关。在Windows中,大小写不敏感;而在Uinux/Linux中则是大小写敏感。
不同的储存引擎保存数据和索引的方式是不同的,但表的定义在MySQL服务器层是统一处理的。
查看有哪些存储引擎可用 mysql> SHOW ENGINES\G;
查询默认存储引擎 mysql> SHOW VARIABLES LIKE 'default_storage_engine%';
查询数据表的相关信息 mysql> SHOW TABLE STATUS LIKE 'user'\G;
下面简单介绍每一行的含义:
Name : 表名。
Engine :表的存储引擎类型。
Row_format :行的格式。可选值有 Dynamic、Fixed或者Compressed。 Dynamic: 行的长度可变,一般包含可变长度的字段,如VARCHAR或者BLOB。 Fixed:行的长度是固定,只包含固定长度的列,如CHAR、INTEGER。 Compressed:行存在压缩表中。
Rows :表中的行数。
Avg_row_length :平均每行包含的字节数。
Data_length :整个表的大小(单位:字节)。
Max_data_length : 表可以容纳的最大数据量
Index_length :索引的大小(单位:字节)。
Data_free : 对于MyISAM表,表示已经分配但目前没有使用的空间。这部分空间包括之前删除的行,以及后续可 以被INSERT利用到的空间。
Auto_increment :下一个Auto_increment的值。
Create_time : 表的创建时间。
Update_time :表数据的最后修改时间。
Check_time : 使用 CHECK TABLE 或 myisamchk 工具最后一次检查表的时间。
Collation : 表的默认字符集和字符排序规则。
Checksum : 如果启用,则对整个表的内容计算时的校验和。
Create_options :指表创建时的其他所有选项。
Comment :包含了其他额外信息,对于MyISAM引擎,保存的是表创建时带的注释;如果表使用的是innoDB引 擎 ,则保存的是表空间的剩余空间信息;如果是一个视图,则包含了“VIEW”的文本字样。
InnoDB存储引擎
InnoDB是MySQL的默认事务引擎,也是目前最重要、使用最广泛的存储引擎。它被设计用来处理大量的短期事务(大部分情况能正常提交,很少被回滚)。同时,InnoDB的性能和崩溃自动恢复特性使得它在非事务型储存的需求中也被广泛使用。若非有特别原因需要使用其他的储存引擎,建议优先考虑InnoDB引擎。
概述
InnoDB的数据存储在表空间(tablespace)中,表空间是由一系列的数据文件构成,类似一个虚拟的文件系统,它存储和管理所有InnoDB数据表内容。
InnoDB 采用MVCC支持高并发,且实现了四个标准的隔离级别。其默认级别是 REPEATABLE READ ,且通过间隙锁策略防止幻读的出现。间隙锁使得InnoDB不仅锁定查询涉及的行,还会对索引中的间隙进行锁定,防止幻影行的插入。
InnoDB表基于聚蔟索引建立的,其索引结构和MySQL的其他存储引擎有很大不同,聚蔟索引对主键查询有很高的性能。它的二级索引必须包含主键列,所以主键很大的话,其他索引也会很大。因此若表上的索引比较多,主键应当尽可能小。
四种隔离级别说明
隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed)
可重复读(Repeatable read) 可串行化(SERIALIZABLE)
不可能
不可能 不可能
可能
不可能 不可能
可能
可能 不可能
脏读 :一个事务读取到另一事务未提交的更新数据。 不可重复读 : 在同一事务中,多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据. 相反, “可重复读”在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据。。 幻读 :一个事务读到另一个事务已提交的insert数据。
提供的功能 支持提交和回滚操作,还可以创建保存点实现部分回滚。 在系统奔溃后可自动恢复。 外键和引用完整性支持,包括递归式删除和更新。 数据行级别的锁定和多版本共存,使得InnoDB数据表在同时检索和更新操作的复杂查询中表现出非常好的并发性能。 默认情况下,InnoDB储存引擎会把数据表存储在一个共享的表空间里,表空间可由多个文件构成,类似一个虚拟的文件系统,它存储和管理所有InnoDB数据表内容。
MyISAM 存储引擎
MyISAM是MySQL 5.1 以及之前版本的默认存储引擎,它提供了全文检索、压缩、空间压缩函数等特性。MyISAM不支持事务和行级锁,崩溃后无法安全恢复。如果对于只读数据,或者表比较小,可以忍受修复操作,也可以考虑存储引擎选用MyISAM。
存储
MyISAM将表存储在两个文件中:数据文件和索引文件,分别以.MYD和.MYI为扩展名。
MyISAM表可以包含动态或者静态(长度固定)行。
MyISAM表的存储记录数受限于可用的磁盘空间或者操作系统中单个文件的最大尺寸。
特性
加锁与并发
MyISAM对整表加锁,而不是针对行。读取时会对需要读到的所有表加共享锁,写入时会对表加排他锁。若表有读取查询时,可以往表中插入新的记录,称之为并发插入。
修复
MySQL可手工或者自动执行检查和修复的操作。执行表的修复可能会导致一些数据的丢失,而且过程非常 慢。
可通过 CHECK TABLE mytable 检查表的错误,如果有错误可通过 REPAIR TABLE mytable 命令修复。如果MySQL服务器已经关闭,可以使用 myisamchk 命令进行检查和修复的操作。
索引特性
支持全文检索,这是一种基于分词创建的索引,可以支持复杂的查询。
延迟更新索引键
创建MyISAM表时若指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立刻将修改的索引写入磁盘,而是写到内存中的键缓冲区,在清理缓冲区或者关闭表的时候才会把对应的索引块写入到磁盘。此方式极大提升了写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。延迟更新索引键可以在全局设置,也可以为单表设置。
MyISAM压缩表
MyISAM压缩表适合那些创建并导入数据后,不再进行修改操作的数据表。可以使用 myisampack 对MyISAM表进行压缩,压缩后不能修改,除非先将表解压后修改数据,然后再压缩。压缩后,减少了磁盘空间占用,因此减少了磁盘I/O,从而提升查询性能。压缩表支持可读索引。
MyISAM 性能
MyISAM最典型的性能问题是表锁的问题,如果发现所有查询都长期处于“Locked”状态,那么就表锁的问题。
MySQL内建的其他存储引擎简介
Archive 引擎
Archive只支持INSERT和SELECT操作,它会缓存所有的写并对插入的行进行行压缩,因此它比MyISAM表的磁盘I/O 更少。Archive引擎每次SELECT查询需要执行全表扫描, 它适合日志和数据采集类应用 ,因为这类应用做数据分析时候需要全表扫描。同时也适合需要更快INSERT操作的场合下使用。
Archive支持行级锁和专用缓冲区,可以实现高并发插入。在一个查询开始直到返回表中存在的所有行数之前,它会阻止其他SELECT执行,以实现读一致性。Archive在操作批量插入时,在完成前对读操作是不可见,此机制模仿了事务和MVCC的一些特性,但是它不是一个事务型引擎,而是一个针对高速插入和压缩做了优化的简单引擎。
Blackhole引擎
Blackhole引擎没有实现任何存储机制,它会丢弃所有插入的数据,不做任何保存。但是服务器会记录Blackhole表的日志,所以它适合用于复制数据到备库,或简单地记录到日志。
CSV引擎
CSV引擎可以将CSV文件作为MySQL的表来处理,但它不支持索引。CSV引擎可以在数据库运行适合拷入或拷出文件,可以把Excel等电子表格中的数据存储为CSV文件,然后复制到M一SQL数据目录下,就能在MySQL中打开。
Federated存储引擎
Federated引擎是访问其他MySQL服务器饿一个代理,它创建一个到远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,然后提取或者发送需要的数据。
MERGE储存引擎 该引擎提供了把多个MyISAM数据表合并为一个虚拟数据表的手段,查询一个MEGRE数据表相当于查询所有成员数据表,这种做法的好处可以绕开文件系统对各个MyISAM数据表的最大长长大限制。 构成MERGE数据表的所有数据表必须具有同样的结构。 该引擎适用于日志或数据仓库类应用。
Memory储存引擎
该引擎把数据表保存在内存中,数据表具有固定不变的数据行,因此它的检索非常快,但是服务器断电时,表的内容也随之消失。如果需要快速访问数据,而且这些数据不会被修改,重启后丢失也没关系,可以考虑使用Memory存储引擎。
特点: 使用散列索引,因此进行“相等比较”速度非常快,但是进行“范围比较”速度比较慢。散列索引适合使用“=”和“<=>”,不适合使用“<”和“>”,同样散列索引也不适合用在ORDER BY字句里。 该引擎的数据表的数据行里使用长度固定不变的格式,以此加快处理速度。不能使用BLOB和TEXT这样长度可变的数据类型。VARCHAR是一种长度可变的类型,但是在MySQL内部被当作一种固定不变的CHAR类型。
适合使用的场景: 用于查找(lookup)或者映射(mapping)表。 用于缓存周期性的聚合数据的结果。 用于保存数据分析中产生的中间数据。
Falcon储存引擎 支持提交和回滚的操作。 在系统奔溃后可自动恢复。 灵活的锁定级别和多版本共存,在同时检索和更新操作的复杂查询中表现出非常好的并发性能。 在储存时对数据行进行压缩,在检索时对数据行进行解压缩以节省空间。 日常管理和维护开销低。
NDB储存引擎
该引擎是MySQL的集群储存引擎。
命令总结
查看有哪些存储引擎可用 mysql> SHOW ENGINES\G;
查询默认存储引擎 mysql> SHOW VARIABLES LIKE 'default_storage_engine%';
查询数据表的相关信息 mysql> SHOW TABLE STATUS LIKE 'user'\G;
数据库
2019-10-11 14:10:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在Ubuntu18.04 中安装MySQL 过程中没有出现提示输入密码环节,可以使用以下方法重设MySQL的root密码。
使用安装时的默认密码登录MySQL 安装时默认生成的默认账户在 /etc/mysql/debian.cnf:
 $ mysql -udebian-sys-maint -p   密码是debian.cnf中的password,每个人的密码不一样,根据自己系统中debian.cnf的密码输入。
修改root 密码 登入MySQL后,执行以下命令:
 use mysql; update mysql.user set authentication_string=password('root') where user='root' and Host ='localhost'; update user set plugin="mysql_native_password"; flush privileges; quit; 重启MySQL服务
数据库
2019-10-10 12:01:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
阿里妹导读:TPC-C是TPC组织(国际事务性能委员会)制定的关于商品销售的订单创建和订单支付等的基准测试标准,是数据库联机交易处理系统的权威基准测试标准。
蚂蚁金服自研的分布式关系数据库OceanBase获得TPC-C测试第一名后,引起了大量关注,今天,我们邀请了OceanBase的核心研发人员对本次测试做专业的技术解读。
一、OceanBase如何做TPC-C测试
有机会挑战TPC-C测试相信是所有数据库内核开发人员的梦想,但TPC-C测试标准非常复杂。由于这是国产数据库同时也是分布式数据库第一次冲击这个榜单,为了完成这次挑战,OceanBase团队前后准备时间超过一年。
前期准备
TPC-C测试首先需要找到官方唯一认证的审计员来对测试进行审计监察,他们对这次OceanBase的审计也相当重视,全世界仅有的三个审计员这次就有两个参与到测试审计工作中。
测试系统
目前市面上基本找不到一个能够开箱即用的符合TPC-C标准的测试工具。以目前各个厂商PoC环境最常遇到的benchmarksql为例,可以说只是模拟TPC-C压力模型的压测工具,连最基本的数据导入都不合规,大量的字符串生成未保证全局随机,缺乏压测阶段最基本的think time、keying time这些基本配置导致极小的数据量就能跑出很高的tpmC,最关键的是它将测试模型大大简化为工具直连DB测试,完全没有遵守TPC-C测试标准规范。
在标准定义中,测试系统可以分为RTE(Remote Terminal Emulator)和SUT两部分,但实际上从角色上看SUT可以进一步拆分为两部分:WAS(web application server)和DB Server。
这其中DB Server是每个测试厂商的数据库服务;RTE扮演的角色是测试模型中的客户终端,事务的触发、RT的统计等都在这里完成;标准明确要求每一个用户terminal都必须保持一个长连接,显然在海量Warehouse时DB是无法承受这么多连接的,WAS就是RTE和DB之间桥梁,标准定义可以使用连接池,在保证对应用透明的情况下它可以做所有请求的管理。
这三个角色中,WAS和DB是必须商业化可购买且提供支付服务的,OceanBase这次是使用了OpenResty作为WAS供应商。而RTE则一般由各个参测厂商自行根据标准实现,但所有实现代码必须经过审计的严格审计,OceanBase这次完整的实现了一整套完全合规的RTE,并且支持在大规模测试系统中部署。要知道在实际的TPC-C测试流程中,不只是对DB端能力的考验,RTE端同样存在极大的资源消耗和压力。以这次6088w tpmC测试结果看,我们一共在64台64c128G的云服务器上运行了960个RTE客户端,来模拟总计47942400个用户terminal,最后还需要基于这么多RTE统计结果进行一致性和持久化审计验证。
虽然只是测试客户端,但RTE的中同样有大量的可能导致最后测试失败的小细节,比如大家可能注意不到的所有事务都因为用了web端模拟终端后需要增加的100毫秒rt,又比如为了模拟用户终端输出显示的100毫秒延时。
测试规划
TPC-C从来都不是一个简单的测试,它很科学并没有给出强制的软硬件配置,只是给出测试规范和各种审计检查限制标准,所有数据库厂商可以根据自己的特性充分调优来拿到一个最好的性能或性价比。但这同时也对所有参测厂商提出了一个巨大的难题,如何对已有的资源进行合理规划来保证顺利完成一次对TPC-C榜单的冲击。 硬件选型,这里不仅要对数据库服务器选型,还需要对RTE以及WAS服务器选型。这之前需要先期进行大量的测试和调优,来摸出在保证性价比的前提下每个角色服务器的资源配置是多少刚好。这次OceanBase测试最大的优势就是全部使用了云化资源,我们不需要再关注最底层机房、机柜、布线这些细节,只需要通过快速的规格调整来拿到我们需要的机型。 参数选择,如何选择合适的配置参数是一个非常令人头疼的问题。举个例子,一个最典型的问题就是我们最终要跑多少个Warehouse,每个数据库服务器上承载多少Warehouse。TPC-C标准为了尽可能模拟真实业务场景,通过每个事务限定不同的think time和keying time保证了一个warehouse的数据最多能够提供12.86tpmC值,因此数据库厂商想要拿到更高的成绩就必须装载更多的warehouse,但是另一方面单机的存储空间又有预计80%(经验值)需要预留给60天增量存储。在分布式数据库架构下,为了能让每个数据库服务器跑满又能充分利用本地存储空间,让每个服务器的CPU、内存、IO能力、存储空间的资源最大化利用,我们前后调整优化了近一个月时间。
性能压测
最受关注的性能压测部分在TPC-C标准中规定了以下三个状态: Ramp-up,标准允许每个数据库进行一定时间的预热来达到稳定状态,但是ramp-up阶段的所有配置必须和最终报告配置保持一致。 Steady state,保证ACID及可串行化隔离级别前提下,数据库需要能够以最终报告tpmC值在稳定状态无任何人工干预前提下保持运行8小时以上,每隔半小时需要完成一次checkpoint。 Measurement Interval,标准规定了需要能够支持8小时稳定运行,但性能采集阶段只需要保设置2小时以上即可。这个阶段还需要保证累计tpmC波动不能超过2%,并且必须完成至少4个以上的checkpoint。
所以之前一般数据库进行性能压测一般是以下的流程,先进行一段时间的预热到达稳态,等待稳定运行一段时间并完成一个checkpoint后开始进入2小时的性能采集阶段。
而OceanBase这次是使用了TPC-C测试迄今以来最严苛的一个流程来完成这个性能测试的,我们首先使用了10分钟进行预热,然后在6088w tpmC稳态保持运行25分钟并完成一个检查点,再继续跑了完整的8小时性能压测采集阶段,总耗时超过8个半小时,中间性能最大波动不到0.5%,最终结果也让审计员异常兴奋。
整个性能测试前后,审计员还需要进行数据及事务随机分布检查,简单说来就是大量全表扫描和统计sql,最大的一条sql需要访问超过万亿行的order_line表结果,可以算是TPC-C里的“TPC-H测试”。现场审计第一次遇到这些sql时我们也大量的出现sql执行超时情况,但后续基于OceanBase2.2版本最新的并行执行框架我们还是很快搞定了这些大sql,所以要顺利完成TPC-C测试并不能只是一个偏科生,保持自身没有短板才是真正意义上的通用关系数据库,从这点上说Oracle仍是OceanBase学习的榜样。
ACID A测试,通过提交和回滚payment事务来确认数据库对原子性支持,和I测试一样,OceanBase的A测试跑了两遍,分别应对分布式事务和本地事务。 C测试,标准里的C测试一共包含12个case,前四个是必须要完成验证的,每个case其实都可以认为是一个复杂的大sql,而对于分布式数据库来说重点是需要始终保证全局一致。 I测试,标准要求TPC-C模型里5个事务除了StockLevel事务都需要满足最高的可串行化隔离级别,并构造了9个case来验证隔离性。对于分布式数据库而言,这个要求并没有那么容易实现,所幸OceanBase在2.x版本中提供了全局时间戳的支持,所以的I测试都在审计员的特别要求下跑完了全本地和全分布式两种模式的两轮测试,这也应该是TPC-C测试中首次有数据库厂商需要做两轮I测试跑18个case的,也许在不久后TPC-C标准定义也会因为OceanBase的这次测试而带来针对分布式数据库的相关更新。 D测试,OceanBase在这个场景其实相对传统数据库是有较大天生优势的,OceanBase每个Warehouse数据有两份数据三份日志,通过paxos强同步,保证RPO=0的前提下只有秒级RTO。
面对D测试标准中最严格的一项-部分存储介质永久故障,OceanBase还使用了最严苛的测试场景,在使用超出标准要求的全量6000W tpmC压力下,我们强行销毁了一个云服务器节点,整体tpmC在两分钟内恢复到6000w并持续跑到测试时间结束,这些表现都是远远超过TPC-C规范要求的,相比较而言其它传统数据库基本面对有日志的存储介质故障D测试场景都是依赖磁盘RAID来恢复,OceanBase应该算是首个没有raid完全依赖数据库自身恢复机制完成全部D测试的数据库厂商了。
同时我们在D测试中是连续杀掉了两台服务器节点,首先杀掉一个数据节点,等待tpmC恢复且稳定5分钟后,再次杀掉了rootserver leader节点,tpmC仍然快速恢复。
二、TPC-C基准测试之SQL优化
对TPC-C有所了解人都知道,TPC-C是一个典型的OLTP (On-Line Transaction Processing) 场景测试,考察的是数据库在高并发压力场景下的事务处理能力,最终的性能指标以tpmC(transaction per minute,也即每分钟系统处理TPC-C模型中的new order事务的数量)和平均到每tpmC的系统成本作为衡量标准。在OLTP场景中,每条请求的响应时间都是极短的。因此,各个数据库厂商在进行TPC-C测试时,都会尽一切可能将每一个操作时间压缩到最短,不夸张的说,在TPC-C的测试中,一些关键操作的优化往往需要细化到CPU指令级。
在进入我们的主题前,我们先来谈谈TPC-C中的事务模型,主要分为五种事务,订单创建、订单支付、订单查询、订单发货以及库存查询,这五种事务按照一定的比例发生,测试最终衡量的是每分钟订单创建事务的执行个数。大家知道,每一个数据库的事务,其实就是由一定逻辑关系关联的若干条SQL语句组成,他们在一个事务中,要么全部成功,要么全部失败,这个在数据库中称为“原子性”,也就是ACID中的“A”。那么TPC-C中的一个事务的耗时大约是多久呢?看一下报告就很清楚了——只有十几个毫秒。考虑到一个事务由多条SQL构成,那么每一条SQL的平均耗时都不到1毫秒!
在C/S(client-server)模型中,一条SQL语句从发起到执行完成需要经历从客户端输入、网络传输、SQL优化、执行、结果返回到客户端这样一个流程。而具体每一条SQL的执行可能只是做一个字段的更新,所需要的执行时间是非常短暂的,从整个链路的角度来看,大量的时间会花费在与客户端的交互过程中,造成资源的浪费和耗时的增加。那么如何解决这个问题的呢?答案就是使用存储过程。
存储过程
所谓“存储过程”就是数据库为用户提供的一种面向过程的编程语言。基于这种语言,用户可以将应用程序的逻辑封装为一个可调用的过程(procedure)存放在数据库中并随时进行调用。通过这种方式,用户可以将本来需要与数据库进行多次交互才能完成的工作通过一次交互完成,省去了中间网络的传输和等待时间(参见图1)。假如一条事务的网络开销平均是30%,也就是说30%的CPU都花在了网络的收发和解析上。那么在6千万规模tpmC测试中节省下来30%的CPU资源换算成系统处理能力是惊人的。使用存储过程还可以带来事务响应时间的下降,导致数据库内核中事务锁的临界区缩短,间接的提升了系统CPU利用率,整个吞吐量也随之提高。存储过程在缩短应用端的等待耗时上同样有很大作用。
图1 传统的C/S模型与使用存储过程的执行方式对比
在TPC-C中,存储过程对于整个系统的执行效率提升是至关重要的。OceanBase 的2.2版本不仅全面支持了存储过程,而且对存储过程的执行效率做了大量极致的优化。
编译执行
存储过程作为一种面向过程的高级语言,需要转换成机器码才能够执行。这个过程一般可以分为“编译执行”和“解释执行”两种,一般来说,编译执行相比解释执行有代码优化充分、执行效率高等特点。OceanBase利用近两年逐渐成熟的LLVM编译器框架实现了一个支持存储过程的编译器,通过动态编译(Just-in-Time Compilation)的方式将存储过程翻译成高效的二进制可执行代码,在执行效率上获得了数量级的提升。同时,过程中LLVM框架将存储过程转换为与机器无关的中间代码,使得存储过程也自然而然地获得了跨平台的编译执行能力,LLVM内置的优化过程确保我们在各种不同的硬件平台上都可以获得正确、高效的可执行代码。
Array Binding
另外一个在TPC-C测试中发挥了重要作用的功能就是对DML语句进行批量处理的能力,在Oracle中该功能也称为“Array Binding”。一条SQL在数据库中的执行过程大致上可以分为“计划生成”和“执行”两个阶段。尽管我们对SQL的执行计划做了高速缓存,但找到一个合适的执行计划在整个执行过程中仍然是比较耗时的一个部分。那有没有办法省去这个时间呢?当一组SQL的执行计划完全一样而只有执行参数不同时,在存储过程中我们可以通过特定的语法将他们的执行做成一个批量处理的过程,此时“计划生成”只需要做一次即可,这就是所谓的“Array Binding”。
在Array Binding中,数据库会首先找到需要使用的计划,然后执行该计划,并在每次执行完毕后,重新执行参数绑定(binding)的过程。打个比方,这就像是在一个C语言的for循环中,反复赋值而不是重新定义一个数据结构。Array Binding的使用受用户控制,需要在存储过程中使用FORALL关键字来触发这一功能,在TPC-C的测试过程中,我们多次使用了Array Binding来提升系统的处理能力,效果非常明显。
Prepared Statement与执行计划缓存
Prepared Statement是一种二进制的请求交互协议,可以大大降低系统的交互成本。OceanBase不仅支持用户程序与数据库间使用Prepared Statement, 也支持在存储过程引擎调用SQL引擎执行时使用这种交互方式。存储过程在对SQL进行一次Prepare操作并获取唯一id后, 后续的每次执行仅需要传入该id和对应的参数,系统可以通过高速缓存找到对应的存储过程或SQL计划开始执行。该过程相比使用SQL文本的交互方式,省去了大量请求文本解析的CPU开销。
OceanBase内部实现了高速缓存来缓存存储过程的可执行代码及SQL执行计划,不同参数的存储过程和SQL可以通过这一高速缓存快速获取需要的执行对象, 耗时一般在几十微秒以内, 有效避免了重新编译带来的毫秒级的延迟和CPU消耗。
可更新视图
在OLTP场景中,通过减少应用与数据库的交互次数来实现性能提升的例子很多,可更新视图就是其中之一。我们常见的数据库视图通常是只读的,通过定义视图,用户可以定义自己感兴趣的数据以及其获取接口,但视图同时也可以作为更新操作的入口,比如在TPC-C的new order创建场景中,应用需要得到商品信息,更新库存并得到更新后的值。一般可以通过两条SQL实现这一过程: select i_price,i_name, i_data from item where i_id = ?; UPDATE stock SET s_order_cnt = s_order_cnt + 1, s_ytd = s_ytd + ?, s_remote_cnt = s_remote_cnt + ?, s_quantity = (CASE WHEN s_quantity< ? + 10 THEN s_quantity + 91 ELSE s_quantity END) - ? WHERE s_i_id = ? AND s_w_id = ? RETURNING s_quantity, s_dist_01, CASE WHEN i_data NOT LIKE'%ORIGINAL%' THEN 'G' ELSE (CASE WHEN s_data NOT LIKE '%ORIGINAL%' THEN 'G'ELSE 'B' END) END BULK COLLECT INTO ...;
但通过建立一个可更新视图: CREATE VIEW stock_item AS SELECT i_price, i_name, i_data, s_i_id,s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01 FROM stock s, item i WHERE s.s_i_id =i.i_id;
我们就可以通过一条语句更新库存并得到商品和库存信息: UPDATE stock_item SET s_order_cnt = s_order_cnt + 1, s_ytd = s_ytd + ?, s_remote_cnt = s_remote_cnt + ?, s_quantity = (CASE WHEN s_quantity< ? + 10 THEN s_quantity + 91 ELSE s_quantity END) - ? WHERE s_i_id = ? AND s_w_id = ? RETURNING i_price, i_name, s_quantity,s_dist_01, CASE WHEN i_data NOT LIKE'%ORIGINAL%' THEN 'G' ELSE (CASE WHEN s_data NOT LIKE '%ORIGINAL%' THEN 'G'ELSE 'B' END) END BULK COLLECT INTO ...;
这样就省去了一条语句的交互,并且更新逻辑更加直观。可更新视图允许用户可以像普通表一样操作视图,但不是所有视图都可以定义为可更新视图。比如带distinct, group by的视图,具体更新哪些行语义是不明确的,因此不能允许更新。具体到上面的stock_item两表join的视图,需要满足所更新表的unique key在join之后保持unique(key-preserved table),即item.i_id必须是唯一的这个前提。
需要强调,TPC-C规范禁止使用物化视图,而可更新视图并没有改变底层数据表格的存储形式,是符合规范的。
因为TPC-C的设计原则是尽可能的“真实”反应一个OLTP系统的运行场景,我们所做的很多优化都具有广泛的适用性。例如,对于一个高并发的OLTP系统来说,大部分的SQL请求的耗时是非常短的,采用纯粹的C/S交互模型的后果必然使系统的时间浪费在应用与数据库的频繁交互中,而使用存储过程可以大大缓解这种交互的耗时,并且增强系统对于网络抖动的免疫力,这种核心能力对于一个分布式OLTP数据库是不可或缺的。
在这次的TPC-C测试中,我们采用了OceanBase 2.0版本开始支持的Oracle兼容模式,存储过程和SQL全部使用了兼容Oracle的数据类型和语法,这样做也是为了在追求极致优化的同时,确保产品迭代可以沿着通用和正规的方向发展。
三、TPC-C基准测试之数据库事务引擎的挑战
OceanBase这次TPC-C测试与榜单上Oracle和DB2等其他数据库在硬件使用上有非常大的不同,OceanBase的数据库服务器使用的是204+3台型号是ecs.i2.16xlarge阿里云ECS服务器,其中204台作为data node,还有3台作为root node,每位读者都可以在阿里云网站上轻松按需购买。如果读者翻看Oracle和DB2的TPC-C测试报告会发现,这些数据库都会使用专用的存储设备,例如前最高记录保持者Oracle在2010年的测试,使用了97台COMSTAR专用的存储设备,其中28台用来存储数据库的重做日志(Redo Log)。
硬件的差异给软件架构提出了完全不同的挑战,专用的存储设备其内部通过硬件冗余实现了设备自身的可靠保证,数据库软件在使用这样的存储设备时就天然的预设了数据不会丢失。但是,这种方式带来了成本的极大消耗,专用的存储设备的价格都是特别昂贵的。
OceanBase使用通用的ECS服务器提供数据库服务,并且只使用ECS机器自带的本地硬盘做数据存储,这是最通用的硬件条件。但是这种方式对软件架构提出了很大的挑战,因为单个ECS服务器的不如专用的存储设备可靠性高。这也对OceanBase的事务引擎提出了很大的挑战,OceanBase是在普通的ECS服务器上就可以实现ACID特性。
TPC-C测试是对事务ACID特性有完整并且严格的要求。下面分别介绍OceanBase针对事务ACID的特性的解决方案。
Paxos日志同步保证持久性(Durability)
OceanBase数据库的事务持久性(Durability)保证是依赖事务重做日志(Redo Log)的持久性来达成的。所有的 Redo Log 会实时强同步到另外两台数据库服务机器上,包含产生 Redo Log 的机器在内,总共会有三台机器在硬盘中持久化 Redo Log。
OceanBase 采用了 Paxos 一致性同步协议来协调这三台机器上 Redo Log 的持久化,Paxos协议采用超过半数(也叫“多数派”)成功即算成功的算法(三个副本时,两个成功即超过半数),当其中两台机器完成持久化后,事务即可完成提交,剩下的一台机器的 Redo Log 在通常情况下,也是立即就持久化完成了。但如果这台机器碰巧出现异常,也不会影响事务的提交,系统会在其恢复后自动补齐所缺失的 Redo Log。如果机器永久故障,系统会将故障机器所应负责同步的数据分散给集群内的其他机器,这些机器会自动补齐所缺失内容,并跟上最新的 Redo Log 写入。
使用Paxos一致性协议的最大优势是数据持久化和数据库服务可用性的完美平衡。当使用三个副本时,任何时候坏掉一个副本时至少还有另一个副本有数据,并且写入还可以持续,因为还剩下两个副本,后续的写入也不受影响。
所以,OceanBase 在保证了事务持久性的同时,也大大提升了数据库的连续服务能力。TPC组织的审计员在现场审计OceanBase持久性能力时,在客户端持续产生压力的情况下,从OceanBase集群中随意挑选了一台机器做了强制断电操作,发现数据库的数据不仅没丢,数据库不需要任何人工干预还能持续的提供服务,审计员们都很吃惊,并且对OceanBase大为赞赏。
依靠自动两阶段提交解决原子性(Atomicity)
TPC-C测试模型的五种事务中的“订单创建”和“订单支付”两个事务分别会对很多数据做修改,是其中相对复杂的两个事务。TPC-C标准对事务的原子性(Atomicity)是强制性的要求,要求一个事务内部对仓库、订单、用户等表格的修改一定要原子的生效,不允许出现只有一半成功的情况。
OceanBase的数据是按照仓库ID(Warehouse_ID)拆分到多台机器上的,如果所有的事务都是发生在同一个仓库内部,那么无论数据量有多大,事务的修改都只会涉及一台机器的数据,也就是在一台机器上完成事务提交,这是一种完美的线形扩展的场景。但是这不符合实际的业务场景,大多数的实际业务都会有很多不同维度之间的数据交互。TPC-C测试标准也是对此认真考虑,所以对于事务操作数据的随机性规则提出了要求,最终要保证产生10%的“订单创建”事务和15%的“订单支付”事务要操作两个及以上的仓库。在OceanBase数据库内,这样就产生了跨机器的事务操作,而这必须使用两阶段提交协议来保证原子性。
OceanBase会自动跟踪一个事务内所有SQL语句操作的数据,根据实际数据修改的位置自动确定两阶段提交的参与者,事务开始提交时,OceanBase自动选择第一个参与者作为协调者,协调者会给所有参与者发送Prepare消息,每个参与者都需要写各自的Redo Log和Prepare Log(也意味着每个参与者各自做自己的Paxos同步),等协调者确认所有参与者的Redo Log和Prepare Log完成后,然后再给所有参与者发送Commit消息,再等所有参与者的Commit工作完成。整个协议是在事务提交过程中自动完成,对用户完全透明。OceanBase为每一个两阶段提交事务自动选择一个协调者,整个系统任何机器都可以分担协调者工作,所以OceanBase可以将事务处理能力进行线形扩展。
多版本并发控制保证事务的隔离性(Isolation)
TPC-C标准里要求“订单创建”、“订单支付”、“订单配送”、“订单支付”事务之间都是串行化隔离级别(Serializable)。OceanBase采用的方法是基于多版本的并发控制机制。事务提交时会申请一个事务的提交时间戳,事务内的修改以新的版本写入存储引擎,并且保证之前版本的数据不受影响。事务开始时会获取一个读取时间戳,整个事务内数据的读取操作只会看到基于读取时间戳的已提交数据。所以,事务的读取不会遇到脏数据、不可重复读数据以及幻读数据。同时,事务的修改会在修改的数据行上持有行锁,保证两个并发的修改相同行的事务会互斥。
OceanBase的全局时间戳生成器也是由多副本组成,可以独立部署在三台机器上,也可以像这次TPC-C评测中一样部署在root node机器上,与root node共享资源。全局时间戳的三副本是一种极高可用的架构,任何一次时间戳的获取操作都至少在三台机器上的两台获得了确认,所以任意一台机器出现故障,获取时间戳的操作不会有一点影响。
按照TPC-C标准,OceanBase准备了9种不同的场景测试有读-读、读-写冲突时事务的隔离性,最终都完美通过了审计员的审计。
一致性保证(Consistency)
在有了上述的事务能力后,OceanBase可以完美的保证各种数据的一致性的约束。TPC-C标准里提出了12种不同的一致性测试场景在各种测试运行前后对数据库内的数据进行一致性校验。因为OceanBase此次测试数据规模庞大,一致性校验的SQL需要核对大量的数据,所以一致性校验的挑战在于校验的SQL本身运行的效率。基于OceanBase的并行查询能力,发挥整个集群所有的计算资源,校验SQL的运行时间均缩短了几个数量级,很好的完成一致性功能的审计工作。
复制表
TPC-C测试模型中有一张商品(ITEM)表,这张表的内容是测试所模拟的销售公司所有售卖的商品信息,包含了商品的名字、价格等信息。“订单创建”事务执行中需要请求这张表内的数据来确定订单的价格信息,如果商品表的数据只存放在一台机器上,那么所有机器上发生的“订单创建”事务都会请求包含商品表的机器,这台机器就会成为瓶颈。OceanBase支持复制表功能,将商品表设置为复制表后,商品表的数据会自动复制到集群中的每一台机器上。
TPC-C标准不限制数据的副本数,但是不管数据的组织形式,标准里要求事务的ACID一定要保证。OceanBase使用特殊的广播协议保证复制表的所有副本的ACID特性,当复制表发生修改时,所有的副本会同时修改。并且,当有机器出现故障时,复制表的逻辑会自动剔除无效的副本,保证数据修改过程中不会因为机器故障出现无谓的等待。复制表在很多业务场景中都有使用,例如很多业务中存储关键信息的字典表,还有金融业务中存储汇率信息的表。
四、TPC-C基准测试之存储优化
TPC-C规范要求被测数据库的性能(tpmC)与数据量成正比。TPC-C的基本数据单元是仓库(warehouse),每个仓库的数据量通常在70MB左右(与具体实现有关)。TPC-C规定每个仓库所获得的tpmC上限是12.86(假设数据库响应时间为0)。
假设某系统获得150万tpmC,大约对应12万个仓库,按照70MB/仓库计算,数据量约为8.4TB。某些厂商采用修改过的不符合审计要求的TPC-C测试,不限制单个warehouse的tpmC上限,测试几百到几千个warehouse全部装载到内存的性能,这是没有意义的,也不可能通过审计。在真实的TPC-C测试中,存储的消耗占了很大一部分。OceanBase作为第一款基于shared nothing架构登上TPC-C榜首的数据库,同时也作为第一款使用LSM Tree存储引擎架构登上TPC-C榜首的数据库,在存储架构上有如下关键点: 为了保证可靠性,OceanBase存储了两个数据副本和三个日志副本,而传统的集中式数据库测试TPC-C只存储一份数据; 由于OceanBase存储两个数据副本,再加上OceanBase TPC-C测试采用了和生产系统完全一样的阿里云服务器i2机型,SSD硬盘的存储容量成为瓶颈。OceanBase采用在线压缩的方式缓解这个问题,进一步增加了CPU使用;相应地,集中式数据库测试存储一份数据,不需要打开压缩; OceanBase LSM引擎定期需要在后台做compaction操作,而TPC-C要求测试至少运行8小时且2小时之内抖动小于2%,因此,OceanBase存储需要解决LSM引擎后台操作导致的抖动问题;
两份数据
为了保证可靠性和不丢数据(RPO=0),有两种不同的方案:一种方案是在硬件层面容错,另一种方案是在软件层面容错。OceanBase选择在软件层面容错,优势是硬件成本更低,带来的问题是需要冗余存储多个副本的数据。OceanBase使用Paxos协议保证在单机故障下数据的强一致。在Paxos协议中,一份数据需要被同步到多数派(超过一半),才被认为是写入成功,所以一般来说副本个数总是奇数,出于成本考虑最常见的部署规格是三副本。
三副本带来的首要问题就是存储成本的上升,之前商业数据库的TPC-C测试大多基于磁盘阵列,而TPC-C规范中明确对磁盘阵列不做容灾要求,使用相对于传统数据库三倍的存储空间进行TPC-C测试显然难以接受。
我们注意到这样一个事实,通过Paxos协议同步的只是日志,日志需要写三份,但数据不是,数据只需要有两份就可以完成单机故障的容灾了,当一份数据由于服务器宕机不可用时,另一份数据只要通过日志把数据补齐,就可以继续对外提供访问。
和数据存储相比,日志的存储量比较小。我们将数据与日志分开,定义了三种不同的副本类型:F副本既包含数据又同步日志,并对外提供读写服务;D副本既包含数据又同步日志,但对外不提供读写服务;L副本只同步日志,不存储数据。当F副本出现故障时,D副本可以转换为F副本,补齐数据后对外提供服务。在TPC-C测试中我们使用FDL模式进行部署(一个F副本,一个D副本,一个L副本),使用了两倍数据副本的存储空间。无论是D副本还是L副本,都需要回放日志,D副本还需要同步数据,这些都是都会消耗网络和CPU。
在线压缩
在sharednothing架构下,OceanBase至少需要存储两份数据才可以满足容灾的要求,这意味着OceanBase需要比传统数据库多耗费一倍的存储空间。
为了缓解这个问题,OceanBaseTPC-C测试选择对数据进行在线压缩,Oracle数据库中一个warehouse的存储容量接近70MB,而OceanBase压缩后存储容量只有50MB左右,大幅降低了存储空间。TPC-C规范要求磁盘空间能够满足60天数据量的存储,对于OceanBase,由于需要保存两份数据,虽然可靠性更好,但需要保存相当于120天的数据量,这些存储成本都要计入总体价格。
OceanBase使用了204台ECS i2云服务器存储数据,服务器规格和线上真实业务应用保持一致。每台服务器的日志盘1TB,数据盘接近13TB。计算两份压缩后的数据60天的存储空间之后,服务器的数据盘基本没有太多余量,从服务器的资源成本消耗来看,已经达到了比较好的平衡。如果OceanBase的单机性能tpmC进一步提升,磁盘容量将成为瓶颈。OceanBase LSM引擎是append-only的,它的优势是没有随机修改,能够在线压缩。无论是TPC-C测试,还是最核心的OLTP生产系统(例如支付宝交易支付),OceanBase都会打开在线压缩,通过CPU换存储空间。
存储性能平滑
TPC-C测试很大的挑战在于在整个压测过程中性能曲线要求是绝对平滑的,曲线上的波动幅度不能超过2%,这对于传统数据库来说都是一件困难的事情,因为这要求对于所有后台任务的精细控制,不能由于某个后台任务的资源过度使用导致前台请求的阻塞积压。而对于OceanBase而言,事情变得更为困难,因为OceanBase的存储引擎是基于LSM Tree的,在LSM Tree要定期执行compaction操作。Compaction是个非常重的后台操作,会占用大量CPU和磁盘IO资源,这对前台的用户查询和写入天然就会造成影响。我们做了一些优化,来平滑后台任务对性能的影响,从最终的测试结果来看,性能曲线在整个8小时压测过程中的抖动小于0.5%。
| 分层转储
在LSMTree中,数据首先被写入内存中的MemTable,在一定时候为了释放内存,MemTable中的数据需要与磁盘中的SSTable进行合并,这个过程被称为compaction。在很多基于LSM Tree的存储系统中,为了解决写入的性能问题,通常会将SSTable分为多层,当一层的SSTable个数或者大小达到某个阈值时,合并入下一层SSTable。多层SSTable解决了写入的问题,但是SSTable的个数过多,会极大拖慢查询的性能。OceanBase同样借鉴了分层的思路,但同时使用了更加灵活的compaction策略,确保SSTable总数不会太多,从而在读取和写入性能之间做了更好的平衡。
| 资源隔离
Compaction等后台任务需要消耗大量的服务器资源,为了减少后台任务对用户查询和写入的影响,我们在CPU、内存、磁盘IO和网络IO四个方面对前后台任务做了资源隔离。在CPU方面,我们将后台任务和用户请求分为不同的线程池,并按照CPU亲和性做了隔离。在内存方面,对前后台请求做了不同的内存管理。在磁盘IO方面,我们控制后台任务IO请求的IOPS,使用deadline算法进行流控。在网络IO方面,我们将后台任务RPC和用户请求RPC分为不同队列,并对后台任务RPC的带宽使用进行流控。
存储CPU占用
TPC-C基准测试主要考察整体性能tpmC,很多人也会关注单核的tpmC。然而,这个指标只有在相同架构下才有意义。对于存储模块的CPU占用,有如下三点: 对于集中式架构,除了数据库使用CPU之外,专用存储设备也需要使用CPU。例如,第二名Oracle 3000多万tpmC的测试中,数据库使用了108颗T3SPARC处理器,共有1728个物理核心和13824个执行线程,同时存储设备使用的是Intel服务器作为机头,总共使用了97台服务器,194颗Intel X5670 CPU,2328个物理核心。 集中式数据库使用高可靠硬件,只需要存储一个副本,而OceanBase通过软件层面容错,虽然硬件成本更低但需要两个数据副本和三个日志副本,维护多个副本需要耗费大量CPU; OceanBase在TPC-C测试和生产系统中都打开了在线压缩,进一步增加了CPU使用;
因此,简单地对比OceanBase和Oracle的CPU核是不科学的,还需要算上共享存储设备的CPU核,以及OceanBase存储多副本和在线压缩带来的CPU开销。TPC-C推荐的方案是不关注具体的软件架构和硬件架构,关注硬件总体成本。在OceanBase的测试中,硬件成本只占整体成本的18%左右,只考虑硬件的性价比大幅优于集中式数据库。
后续发展
OceanBase的优势在于采用分布式架构,硬件成本更低,可用性更好且能够做到线性扩展,但是,OceanBase单机的性能离Oracle、DB2还有不小的差距,后续需要重点优化单机存储性能。另外,OceanBase的定位是在同一套引擎同时支持OLTP业务和OLAP业务,而目前OceanBase的OLAP处理能力还不如Oracle,后续需要加强存储模块对大查询的处理能力,支持将OLAP算子下压到存储层甚至在压缩后的数据上直接做OLAP计算。
作者:
阳振坤(OceanBase创始人)
曹晖(OceanBase技术专家)
陈萌萌(OceanBase资深技术专家)
潘毅(OceanBase资深技术专家)
韩富晟(OceanBase资深技术专家)
赵裕众(OceanBase高级技术专家)

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-10-09 11:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
说明: 最有可能成为瓶颈的是网络带宽,接下去是内存,最后才是CPU; Redis服务器是一个事件驱动程序; Redis服务器通过套接字(SOCKET)和客户端(或者其他Redis服务器)进行连接; 套接字和客户端的通信产生一些操作,这些操作对应的产生文件事件; Redis服务器有专门的服务程序(多路复用程序)去监听对应的套接字来完成客户端的网络请求操作; 这里的多路服务程序中的多路是指多个网络请求、多个套接字,处理这些网络请求实际操作的程序线程只有一个,但是不影响请求的接受,可以同时接受多个请求,但是某一时刻实际在处理的任务,只有一个; 如果一个操作卡住了,后续的也会卡住,怎么办?答案: k-v操作 ,卡住的几率几乎没有 ; Redis基于Reactor模式开发了自己的网络事件处理器,这个处理器使用了I /O多路复用技术 ,多路复用程序同时监听多个套接字(套接字产生并发时,I/O多路复用程序会将这些套接字放到一个队列中,顺序、同步的往下传递),并根据套接字目前的任务,通过事件分派器,为该套接字关联一组事件处理器(事件处理器有两种:文件事件、时间事件,针对客户端请求,只有文件事件),一个套接字可能会产生多个文件事件,这些事件是关联的;当一个套接字产生的事件被处理完成之后,I/O多路复用程序才会继续向文件事件分派器传送下一个套接字;这些事件处理器是一个个函数,他们定义了事件发生时,服务器应该执行的动作; Redis的多路复用程序的所有功能,都是通过包装select、epoll、evport、kqueue这些I/O多路复用库实现的,Redis在每个函数库上面包装了一层,实现了相同的API,所以,可以任意选择一个库,而不做其他修改;库的选择,是在编译时,自动选择系统性能最高的I/O多路复用函数库来作为Redis的多路复用底层实现; 文件事件处理器的组成和流程
数据库
2019-10-06 15:46:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
SCAN是为所有客户端连接到集群所提供的地址。
在11.2之前,client链接数据库的时候要用vip,假如你的cluster有4个节点,那么客户端的tnsnames.ora中就对应有四个主机vip的一个连接串,如果cluster增加了一个节点,那么对于每个连接数据库的客户端都需要修改这个tnsnames.ora。
引入了scan以后,就方便了客户端连接的一个接口,顾名思义 single client access name ,简单客户端连接名,这是一个唯一的名称,在整个公司网络内部唯一,并且在DNS中可以解析为三个ip地址,客户端连接的时候只需要知道这个名称,并连接即可, 每个SCAN VIP对应一个scan listener,cluster内部的service在每个scan listener上都有注册,scan listener接受客户端的请求,并foward到不同的Local listener中去,还是由local 的listener提供服务给客户端。
数据库
2019-09-28 17:39:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
概述
在疫情期间,发生微盟删库事件,让企业损失惨重。由此可见,数据库备份的重要性可见一斑。
当数据文件发生损坏、MySQL服务出现错误、系统内核崩溃、计算机硬件损坏或者数据被误删等事件时,使用一种有效的数据备份方案,尽量挽回损失。
MySQL备份方案 逻辑备份 物理备份 全备份 增量备份 ...
逻辑备份
逻辑备份通过保存代表数据库结构及数据内容的描述信息实现,如,保存创建数据结构以及添加数据内容的SQL语句,这种备份方式 适用于少量数据的备份与还原 。
逻辑备份需要查询MySQL服务器获得数据结构及内容信息,因为需要查询数据库信息并将这些信息转换为逻辑格式,所以相对于物理备份而言比较慢。
逻辑备份不会备份日志、配置文件等不属于数据库内容的资料。
逻辑备份的 优势 在于不管是服务层面、数据库层面还是数据表层面的备份都可以实现,由于是以逻辑格式存储的,所以这种备份与系统、硬件无关。
物理备份
物理备份通过直接复制包含有数据库内容的目录与文件实现,这种备份方式 适用于对重要的大规模数据进行备份,并且要求实现快速还原的生产环境 。
典型的物理备份就是复制 MySQL数据库的部分或全部目录,物理备份还可以备份相关的配置文件。但采用物理备份需要MySQL处于关闭状态或者对数据库进行锁操作,防止在备份的过程中改变发送数据。
物理备份可以使用mysqlbackup对InnoDB数据进行备份,使用mysqlhotcopy对MyISAM数据进行备份。另外,也可以使用文件系统级别的cp、scp、tar、rsync等命令。
全备份
全备份将备份某一时刻所有的数据。通过物理或逻辑备份工具就可以完成完全备份。
增量备份
增量备份仅备份某一段时间内发生过改变的数据。需要开启MySQL二进制日志,通过日志记录数据的改变,从而实现增量差异备份。
MySQL备份命令
使用MySQL提供的工具命令进行逻辑备份。 #备份所有的数据库 mysqldump -u root -p --all-databases > backup.sql #备份指定的数据库db1、db2以及db3 mysqldump -u root -p --databases db1 db2 db3 > backup.sql #备份db数据库,当仅备份一个数据库时,--databases可以省略 mysqldump -u root -p db4 > backup.sql mysqldump -u root -p --databases db4 > backup.sql
MySQL还原命令 mysql -u root -p mydb < backup.sql
参考资料
1.mysql数据库的备份与还原
2.MySQL数据库备份与还原
数据库
2020-03-14 00:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
数据库表字段有一个格式是:1|2|3.以竖线分割的,现在想取其中的第2个,pg提供这样的函数,记录如下,以下是我的sql SELECT COUNT (*) AS "count", split_part(tree_t.node_path, '|', 2) AS "id", name_t.node_name AS "type" FROM datsvc_data_standard_t sd_t INNER JOIN datsvc_tree_t tree_t ON tree_t."id" = sd_t.tree_category INNER JOIN datsvc_tree_t name_t ON name_t."id" :: VARCHAR = split_part(tree_t.node_path, '|', 2) WHERE tree_t."type" = 2 GROUP BY split_part(tree_t.node_path, '|', 2), name_t.node_name
split_part(string text, delimiter text2, field int)
text要切割的字段; text2按照什么形式切割 int截取的位置
ps:
text=“a.b.c” split_part(text,’.’,1) 结果: a
text=“a.b.c” split_part(text,’.’,2) 结果: b
text=“a.b.c” split_part(text,’.’,3) 结果: c
数据库
2020-03-12 15:05:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1、select count(*) exists...,如果exists中的子查询返回为空,在不同表有时返回1,有时返回0. select count(1) from dual where exists (select 1 from users where user_id = '100205');
原因:返回为0的有主键,返回为1的没有主键
数据库
2020-03-05 22:56:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在日常工作中,我们会有时会开慢查询去记录一些执行时间比较久的SQL语句,找出这些SQL语句并不意味着完事了,些时我们常常用到explain这个命令来查看一个这些SQL语句的执行计划,查看该SQL语句有没有使用上了索引,有没有做全表扫描,这都可以通过explain命令来查看。所以我们深入了解MySQL的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪种策略预计会被优化器采用。 -- 实际SQL,查找用户名为Jefabc的员工 select * from emp where name = 'Jefabc'; -- 查看SQL是否使用索引,前面加上explain即可 explain select * from emp where name = 'Jefabc';
expain出来的信息有10列,分别是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra
概要描述:
id:选择标识符
select_type:表示查询的类型。
table:输出结果集的表
partitions:匹配的分区
type:表示表的连接类型
possible_keys:表示查询时,可能使用的索引
key:表示实际使用的索引
key_len:索引字段的长度
ref:列与索引的比较
rows:扫描出的行数(估算的行数)
filtered:按表条件过滤的行百分比
Extra:执行情况的描述和说明
下面对这些字段出现的可能进行解释:
一、  id
SELECT识别符。这是SELECT的查询序列号
我的理解是SQL执行的顺序的标识,SQL从大到小的执行
1. id相同时,执行顺序由上至下
2. 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
3. id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行 -- 查看在研发部并且名字以Jef开头的员工,经典查询 explain select e.no, e.name from emp e left join dept d on e.dept_no = d.no where e.name like 'Jef%' and d.name = '研发部';
二、select_type
表示查询中每个select子句的类型
(1) SIMPLE(简单SELECT,不使用UNION或子查询等)
(2) PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)
(3) UNION(UNION中的第二个或后面的SELECT语句)
(4) DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
(5) UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)
(6) SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)
(7) DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)
(8) DERIVED(派生表的SELECT, FROM子句的子查询)
(9) UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
三、table
显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名字,可能是简称,例如上面的e,d,也可能是第几步执行的结果的简称
四、type
对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。
常用的类型有:** ALL、index、range、 ref、eq_ref、const、system、** NULL(从左到右,性能从差到好)
ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
index: Full Index Scan,index与ALL区别为index类型只遍历索引树
range:只检索给定范围的行,使用一个索引来选择行
ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
五、possible_keys
指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)
该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询
六、Key
key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中
如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
七、key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)
不损失精确性的情况下,长度越短越好
八、ref
列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
九、rows
  估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
十、Extra
该列包含MySQL解决查询的详细信息,有以下几种情况:
Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by
Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序” -- 测试Extra的filesort explain select * from emp order by name;
Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。
Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
No tables used:Query语句中使用from dual 或不含任何from子句 -- explain select now() from dual;
总结:
• EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
• EXPLAIN不考虑各种Cache
• EXPLAIN不能显示MySQL在执行查询时所作的优化工作
• 部分统计信息是估算的,并非精确值
• EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。
通过收集统计信息不可能存在结果
原文地址: MySQL Explain详解
数据库
2020-03-05 17:22:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
达梦数据库号称国产数据库第一品牌,在某些方面还是有它的优势的,作为IT攻城狮,我从事软件行业已经有10多年了,oracle、mysq、sqlserver都已经用的很熟了,这2年做了几个国产化相关的项目,从硬件到软件都用国产的来进行替代,数据库就用到了达梦,看到中国人自己研发的数据库,多少还是有一点情结的。
达梦实现了很多当前主流数据库oracle、mysq、sqlserver的功能特性,在兼容性方面做得很不错。开发人员从上述3种数据库切换到达梦数据库还是很轻松的,我们来看一下达梦数据库的兼容模式。
先进入控制台工具:
选中一个实例:
找到兼容性相关参数,打开后可以看到有一个COMPATIBLE_MODE属性:
对于该属性的说明为:是否兼容其他数据库模式。0:不兼容,1:兼容SQL92标准,2:兼容ORACLE,3:兼容MS SQL SERVER,4:兼容MYSQL;我们将其设置成2,变成兼容oracle模式,保存后重启实例。
进入到达梦管理工具,执行如下SQL,可以看到已经 COMPATIBLE_MODE已经变成2了,也就是oracle兼容模式。
数据库
2020-03-05 16:02:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1、innodb_ buffer_ pool _size默认128M, 建议物理内存的50%-70%;
2、1og_ timestamps默认UTC, 建议SYSTEM;
3、time_ zone默认SYSTEM, 建议" +8: 00";
4、join_ _buffer_ size默认2K,建议4M;
5、sort_ _buffer_ size默认2K,建议4M;
6、read_ rnd_ _buffer_ size默认2K,建议4M;
7、max_ connections默认151, 建议根据实际情况设定;
8、max_ connect_ errors默认100, 建议100000;
9、max_ allowed_ packet默认4M,建议32M;
10、interactive. timeout默认28800,建议300秒;
11、wait_ timeout默认28800, 建议300秒;
12、lock_ wait_timeout默认-一年,建议6秒;
13、long_ query_time默认10秒, 建议0.1~0.5;
14、binlog_cache_ size默认32K, 建议32M;
15、max_binlog_ cache_ size默认非常非常大,建议1G;
16、innodb_ log_ file_ size默认48M, 建议1~2G;
17、innodb_ log_ files_ in_ group默认2,建议3组以上;
18、innodb_ io_capacity默认200 ,根据实际磁盘io能力设定;
19、innodb_ open_ files默认-1,不限制 ,建议65535;
20、innodb_max_ dirty_ pages_ pct默认75,建议50;
21、innodb_ online_ alter_ log_ max_ _size默认128M,建议4G;
22、innodb_undo_ log truncate默认关闭, 建议打开;
更多靠谱的MySQL参数建议值见: https://imysql.com/my-cnf-wizard.html
数据库
2020-03-03 15:57:00