数据专栏

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

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
选好可视化
“一图胜千言”,是最直观的数据可视化魅力。以图表来传达和沟通信息,其效率远超枯燥乏味的数据表达。
有需求就有市场。数据可视化崭露头角后,各个厂商出备的产品、解决方案,开发者自研的可视化工具、操作平台都如雨后春笋般冒了出来。
受众不同,个人的选择就会不同;需求不同,特色的选择就会不同。但选择繁多,很多开发者和企业就会头疼:有数据可视化的需求,但工具到底该如何选择?
AntV-G2是阿里巴巴2018年推出的开源项目,是一套基于可视化编码的图形语法,具有高度的易用性和扩展性。无需关注繁琐的实现细节,一条语句即可构建出各种各样的可交互统计图表。它具备以下特性: 简单、易用:从数据出发,仅需几行代码就能轻松获得想要的图表展示效果 完备的可视化编码:以数据驱动,提供从数据到图形的完整映射 强大的扩展能力:任何图表,都可以基于图形语法灵活绘制,满足无限创意
作为一个非常全面的图表库,AntV G2库有折线图、柱状图、条形图、雷达图、箱体图、面积图、饼图、热力图、仪表盘… …几乎满足了所有基本的图表类需求。
另外,G2还是一个使用WebGL/canvas技术实现的基础图表库,因此既可以在原生js环境下使用,也可以使用任意的js框架。基于G2封装的组件框架有BizCharts和Viser,所以如果使用angular、react、vue的话可以直接使用其封装的组件,和自行动手封装G2组件是一样的效果。
G2的构成
一个可视化框架需要四部分: 数据处理模块,对数据进行加工的模块,包括一些数据处理方法。例如:合并、分组、排序、过滤、计算统计信息等 图形映射模块,将数据映射到图形视觉通道的过程。例如:将数据映射成颜色、位置、大小等 图形展示模块,决定使用何种图形来展示数据,点、线、面等图形标记 辅助信息模块,用于说明视觉通道跟数据的映射关系,例如:坐标轴、图例、辅助文本等
在 数据处理 模块上,dataSet主要通过state状态管理多个dataview视图,实现多图联动,或者关联视图。dataView则是对应的是每一个数据源,通过connector来接入不同类型的数据,通过tranform进行数据的转换或者过滤。最后输出我们理想的数据,dataSet是与g2分离的,需要用到的时候可以加载;
在 图形映射 模块上,度量 Scale,是数据空间到图形空间的转换桥梁,负责原始数据到 [0, 1] 区间数值的相互转换工作,从原始数据到 [0, 1] 区间的转换我们称之为归一化操作。我们可以通过chart.source或者chart.scale('field', defs)来实现列定义,我们可以在这对数据进行起别名,更换显示类型(time,cat类型等);
辅助信息 ,就是标记数据,方便理解数据;
图形展示 chart图表是一个大画布,可以有多个view视图,geom则是数据映射的图形标识,就是指的点,线,面,通过对其操作,从而展示图形。
大体步骤如下:
G2 经典新生
目前AntV-G2已更新到3.4版本。通过这次升级,G2往经典的“图形语法”理论注入了新的生命,为大家带来“交互语法” — 一套简洁高效的交互式可视化解决方案。同时,G2的底层渲染进行了升级,实现 SVG 和 Canvas 自由切换。
简洁灵活的交互语法
G2将经典的图形语法理论扩展为“交互语法”,一方面开放 220+ 种交互事件,支持定制最小粒度的图表元素交互,另一方面封装了各类复杂的、常用的交互场景,使丰富灵活的图表交互仅需一行代码实现。
渲染引擎自由切换
G2的绘图引擎开始支持 SVG 和 Canvas 双引擎,以适应更多业务场景。并在拾取、动画管线、碰撞检测等方面进行了优化,G2的绘图能力变得更自由、更流畅。
两种引擎在不同场景的性能对比
256+58的试炼
通过256 plots计划和58+业务模板计划,来向用户提供更丰富的场景,也由此检验G2图表的数据表达能力。 通过256 plots计划,G2挑战了d3.js、R语言社区等经典图表绘制,检验并刺激了G2框架图形能力的更新。
58+业务模板源自真实的业务,由基础的线、柱、饼图表改造而起,进而辐射到分面、迷你图等更复杂的场景,能更好的帮助用户找到理想的可视化解决方案。
DataV数据可视化
AntV-G2功能虽然强大,但对于需要开箱即用、直接适用业务的企业而言,距离可视化还缺少一个成熟的产品。幸运的是, 阿里云.DataV数据可视化 完美承担了这样的一个角色。DataV只需通过拖拽式的操作,使用数据连接、可视化组件库、行业设计模板库、多终端适配与发布运维于等功能,就能让非专业的人员快速地将数据价值通过视觉来传达。
DataV具有丰富的图表库,并外接有国内两大第三方图表组件库——Echarts和今日的主角:AntV-G2。在强大的图表库支持下,DataV可以制作出丰富多样的可视化页面,随心所欲自由搭配图表来做组合。
作者:数据智能小二
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-06-06 12:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
作者:王相
本文为 DM 源码阅读系列文章的第七篇,在 上篇文章 中我们介绍了 relay log 的实现,主要包括 relay log 目录结构定义、relay log 数据的处理流程、主从切换支持、relay log 的读取等逻辑。 本篇文章我们将会对 DM 的定制化数据同步功能进行详细的讲解。
在一般的数据同步中,上下游的数据是一一对应的,即上下游的库名、表名、列名以及每一列的值都是相同的,但是很多用户因为业务的原因希望 DM 在同步数据到 TiDB 时进行一些定制化的转化。下面我们将主要介绍数据同步定制化中的库表路由(Table routing)、黑白名单(Black & white table lists)、列值转化(Column mapping)、binlog 过滤(Binlog event filter)四个主要功能的实现。值得注意的是,由于其他一些工具(例如 TiDB Lightning 和 TiDB Binlog)也需要类似的功能,所以这四个功能都以 package 的形式维护在 tidb-tools 项目下,这样方便使用和维护。
库表路由( Table routing )
库表路由顾名思义就是对库名和表名根据一定的路由规则进行转换。比如用户在上游多个 MySQL 实例或者 schema 有多个逻辑上相同的表,需要把这些表的数据同步到 TiDB 集群的同一个表中,这个时候就可以使用 table-router 功能,如下图所示:
该功能实现在 pkg/table-router 中,库表路由的规则定义在结构 TableRule 中,其中的属性 SchemaPattern 和 TablePattern 用于配置原库名和表名的模式, TargetSchema 和 TargetTable 用于配置目标库和表名,即符合指定 pattern 的库和表名都将转化成目标库名和表名。
使用结构 Table 对路由规则进行维护,Table 提供了如下方法:
方法 说明 AddRule 增加规则
UpdateRule 修改规则
RemoveRule
Route
删除规则
获取路由后的结果
Table 结构中组合了 Selector , Selector 用于管理指定模式的库、表的规则,提供如下方法:
方法 说明 Insert 增加规则
Match 查找指定的库、表匹配到的规则
Remove
AllRules
删除规则
返回所有的规则
Selector 的底层实现是 trieSelector ,使用了单词查找树的结构来维护库、表与规则的对应关系,感兴趣的同学可以阅读代码深入了解一下。 trieSelector 中使用 cache 缓存了库、表到规则的映射关系,这样可以减少相同库、表匹配规则的资源消耗。除了 table routing,以下的列值转化和 binlog 过滤功能也都使用了 Selector,在下面的介绍中就不再赘述。
黑白名单( black & white table lists )
黑白名单功能用来选择同步哪些库和表,以及不同步哪些库和表,这部分代码维护在 pkg/filter 中。
黑白名单规则配置在 Rules 结构中,该结构包括 DoTables 、 DoDBs 、 IgnoreTables 和 IgnoreDBs 四个属性,下面以判断表 test.t 是否应该被过滤的例子说明配置的作用: 首先 schema 过滤判断。 如果 do-dbs 不为空,则判断 do-dbs 中是否存在一个匹配的 schema。 如果存在,则进入 table 过滤判断。 如果不存在,则过滤 test.t 。 如果 do-dbs 为空并且 ignore-dbs 不为空,则判断 ignore-dbs 中是否存在一个匹配的 schema。 如果存在,则过滤 test.t 。 如果不存在,则进入 table 过滤判断。 如果 do-dbs 和 ignore-dbs 都为空,则进入 table 过滤判断。 进行 table 过滤判断。 如果 do-tables 不为空,则判断 do-tables 中是否存在一个匹配的 table。 如果存在,则同步 test.t 。 如果不存在,则过滤 test.t 。 如果 ignore-tables 不为空,则判断 ignore-tables 中是否存在一个匹配的 table。 如果存在,则过滤 test.t 。 如果不存在,则同步 test.t 。 如果 do-tables 和 ignore-tables 都为空,则同步 test.t 。
使用 Filter 对黑白名单进行管理,Filter 提供了 ApplyOn 方法来判断一组 table 中哪些表可以同步。
列值转化( Column mapping )
列值转化功能用于对指定列的值做一些转化,主要用于分库分表的同步场景。比较典型的场景是:在上游分表中使用自增列作为主键,这样数据在同步到 TiDB 的一个表时会出现主键冲突,因此我们需要根据一定规则对主键做转化,保证每个主键在全局仍然是唯一的。
该功能实现在 pkg/column-mapping 中的 PartitionID :修改列的值的最高几位为 PartitionID 的值(只能作用于 Int64 类型的列)。
代码中使用 Rule 来设置 column mapping 的规则,Rule 的属性及说明如下表所示:
属性 说明 值 PatternSchema 匹配规则的库的模式 可以设置为指定的库名,也可以使用通配符 “*” 和 “?”
PatternTable 匹配规则的表的模式 可以设置为指定的表名,也可以使用通配符 “*” 和 “?”
SourceColumn 需要转化的列 列名
TargetColumn 转化后的值保存到哪个列 列名
Expression
Arguments
转化表达式
转化所需要的参数
目前只支持 PartitionID
Expression 为 PartitionID ,参数为 InstanceID 、schema 名称前缀、table 名称前缀以及前缀与 ID 的分割符号
Expression 为 PartitionID 的配置和转化的计算方式都较为复杂,下面举个例子说明。
例如 Arguments 为 [1, “test”, “t”, “_”] , 1 表示数据库实例的 InstanceID , “test” 为库名称的前缀, “t” 为表名称的前缀, “_” 为前缀与 ID 的分隔符,则表 test_1.t_2 的 SchemaID 为 1 , TableID 为 2 。转化列值时需要对 InstanceID 、 SchemaID 、 TableID 进行一定的位移计算,然后与原始的值进行或运算得出一个新的值。对于具体的计算方式,可以查看代码 partitionID 和 computePartitionID 。下面是一个 PartitionID 逻辑简化后的示意图:
使用 Mapping 结构对 column mapping 的规则进行管理,Mapping 提供列如下方法:
方法 说明 AddRole 增加规则
UpdateRule 修改规则
RemoveRule
HandleRowValue
删除规则
获取转化结果
binlog 过滤( binlog event filter )
binlog 过滤功能支持过滤指定类型的 binlog,或者指定模式的 query,该功能维护在 pkg/binlog-filter 中。某些用户不希望同步一些指定类型的 binlog,例如 drop table 和 truncate table,这样就可以在下游仍然保存这些表的数据作为备份,或者某些 SQL 语句在 TiDB 中不兼容,希望可以在同步中过滤掉,都可以通过配置 binlog event filter 功能来实现。
首先需要对 binlog 进行分类,可以查看代码 Event Type List 。然后再定义过滤规则 BinlogEventRule ,包括以下属性:
属性 说明 值 SchemaPattern 匹配规则的库的模式 可以设置为指定的库名,也可以使用通配符 “*” 和 “?”
TablePattern 匹配规则的表的模式 可以设置为指定的表名,也可以使用通配符 “*” 和 “?”
Events 规则适用于哪些类型的 binlog binlog event 的类型
SQLPattern
Action
匹配的 SQL 的模式
是否对符合上面要求的 binlog 进行过滤
SQL 语句的模式,支持适用正则表达式
Ignore 或者 Do
例如,TiDB 对 ADD PARTITION 和 DROP PARTITION 语句不兼容,在同步时需要过滤掉相关的 SQL 语句,就可以在 DM 中使用如下配置: filter-partition-rule: schema-pattern: "*" sql-pattern: ["ALTER\\s+TABLE[\\s\\S]*ADD\\s+PARTITION", "ALTER\\s+TABLE[\\s\\S]*DROP\\s+PARTITION"] action: Ignore
如果需要过滤掉所有的 DROP DATABASE 语句,则可以在 DM 中使用如下配置: filter-schema-rule: schema-pattern: "*" events: ["drop database"] action: Ignore
代码中通过 BinlogEvent 结构对 binlog event 过滤规则做统一的管理, BinlogEvent 提供了如下的方法:
方法 说明 AddRule 增加规则
UpdateRule 修改规则
RemoveRule
Filter
删除规则
判断指定的 binlog 是否应该过滤
小结
以上就是定制化数据同步功能中库表路由(Table routing)、黑白名单(Black & white table lists)、列值转化(Column mapping)、binlog 过滤(Binlog event filter)的实现介绍。欢迎大家阅读相关代码深入了解,也欢迎给我们提 pr 优化代码。下一篇我们将介绍 DM 是如何支持上游 online DDL 工具(pt-osc,gh-ost)的 DDL 同步场景的。
原文阅读 : https://www.pingcap.com/blog-cn/dm-source-code-reading-7/
数据库
2019-06-06 10:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
介绍
很多互联网应用程序开发人员第一个接触到的网站项目就是博客系统。而全球使用最广的Wordpress常常被用户用来快速搭建个人博客网站。默认情况下,Wordpress一般在后台使用MySQL关系型数据库存储所有的博文及回复。本文将展示如何使用 SequoiaDB 巨杉分布式数据库替换MySQL,成为Wordpress博客系统的后台关系型数据库。

通过阅读本文,用户可以了解到如何使用SequoiaDB巨杉数据库的MySQL实例无缝替换标准MySQL数据库。SequoiaDB巨杉数据库允许用户在不更改一行代码的情况下直接对已有应用进行后台MySQL数据库迁移。

通过使用SequoiaDB巨杉数据库,用户可以得到: 水平弹性扩张 100%全兼容MySQL 优秀的交易性能

WordPress是使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站,也可以把 WordPress当作一个内容管理系统(CMS)来使用。

WordPress有许多第三方开发的免费模板,安装方式简单易用。同时,WordPress官方支持中文版,并拥有成千上万个各式插件和不计其数的主题模板样式。
安装SequoiaDB
本文使用Linux Ubuntu Server 18.10作为服务器,SequoiaDB巨杉数据库版本为3.2.1。

本教程默认使用sudo用户名密码为“sequoiadb:sequoiadb”,默认home路径为/home/sequoiadb。

对于使用CentOS等其他Linux版本的用户,本文所描述的流程可能略有不同,需要根据实际情况自行调整。

1)下载并安装SequoiaDB巨杉数据库
$ wget http://cdn.sequoiadb.com/images/sequoiadb/x86_64/sequoiadb-3.2.1-linux_x86_64.tar.gz
$ tar -zxvf sequoiadb-3.2.1-linux_x86_64.tar.gz
$ cd sequoiadb-3.2.1/
$ sudo ./setup.sh
之后一直回车确认各个默认参数即可。
使用数据库实例用户创建默认实例
$ sudo su sdbadmin
$ /opt/sequoiadb/tools/deploy/quickDeploy.sh

3)连接数据库并开启事务功能并设置默认隔离级别RC
$ /opt/sequoiadb/bin/sdb
> db = new Sdb() ;
> db.updateConf ( { transactionon: true, transisolation: 1 } ) ;
> quit ;
$ /opt/sequoiadb/bin/sdbstop
$ /opt/sequoiadb/bin/sdbstart
安装Apache与PHP
更新系统包并安装Apache与PHP
$ sudo apt-get update
$ sudo apt-get install apache2 php libapache2-mod-php php-mysql unzip php-xml

安装Wordpress
本教程使用Wordpress 5.2.1。

1)登录Wordpress官网下载页面 https://wordpress.org/download/releases/

或登录sequoiadb用户,使用wget下载安装包
$ wget https://wordpress.org/wordpress-5.2.1.tar.gz

2)安装Wordpress并配置
$ tar -zxvf wordpress-5.2.1.tar.gz
$ cd wordpress
$ sudo rm /var/www/html/*
$ sudo cp -R * /var/www/html/
$ sudo cp /var/www/html/wp-config-sample.php /var/www/html/wp-config.php
$ sudo chown www-data:www-data /var/www/html/*

3)更改配置文件
$ sudo vi /var/www/html/wp-config.php
define( 'DB_NAME', 'database_name_here' ); 变为 define( 'DB_NAME', 'wordpress’ );
define( 'DB_USER', 'username_here' ); 变为 define( 'DB_USER', ‘sequoiadb’ );
define( 'DB_PASSWORD', 'password_here' ); 变为 define( 'DB_PASSWORD', 'sequoiadb' );
define( 'DB_HOST', 'localhost' ); 变为 define( 'DB_HOST', ‘<服务器IP地址>’ );
创建Wordpress数据库
$ sudo su sdbadmin
$ /opt/sequoiasql/mysql/bin/mysql -S /opt/sequoiasql/mysql/database/3306/mysqld.sock -u root
mysql> create user 'sequoiadb'@'localhost' identified by 'sequoiadb';
mysql> create database wordpress;
mysql> grant all on wordpress.* to ‘sequoiadb'@’localhost';
mysql> grant all privileges on *.* to 'sequoiadb'@'%' identified by 'sequoiadb' with grant option;
mysql> exit
确认表被分散在多个分区
$ /opt/sequoiadb/bin/sdb
> db=new Sdb() ;
> db.snapshot(SDB_SNAP_CATALOG) ;
……
{
"_id": {
"$oid": "5cecf121116eae6117df17dc"
},
"Name": "wordpress.wp_posts",
"UniqueID": 4294967308,
"Version": 1,
"ReplSize": -1,
"Attribute": 1,
"AttributeDesc": "Compressed",
"CompressionType": 1,
"CompressionTypeDesc": "lzw",
"ShardingKey": {
"ID": 1
},
"EnsureShardingIndex": false,
"ShardingType": "hash",
"Partition": 4096,
"InternalV": 3,
"CataInfo": [
{
"ID": 0,
"GroupID": 1000,
"GroupName": "group1",
"LowBound": {
"": 0
},
"UpBound": {
"": 1365
}
},
{
"ID": 1,
"GroupID": 1001,
"GroupName": "group2",
"LowBound": {
"": 1365
},
"UpBound": {
"": 2730
}
},
{
"ID": 2,
"GroupID": 1002,
"GroupName": "group3",
"LowBound": {
"": 2730
},
"UpBound": {
"": 4096
}
}
],
"AutoSplit": true,
"AutoIncrement": [
{
"SequenceName": "SYS_4294967308_ID_SEQ",
"Field": "ID",
"Generated": "default",
"SequenceID": 11
}
]
}
……

其中针对每个表的CataInfo字段为该表分散在不同分区的一致性散列范围,而分区键则为ShardingKey字段。对于wp_posts来说,其表结构显示数据根据ID字段进行散列切分,数据被打散至集群的三个分区中。
配置Wordpress 通过浏览器登录服务器IP地址
Site Title: SDBWordpress Username: sequoiadb Password: sequoiadb 选择Confirm use of weak password Your Email: test@test.com 点击Install WordPress按键,得到安装成功界面
使用sequoiadb:sequoiadb作为用户名密码登录
更换桌面主题


回到Wordpress博客首页,可以尝试更改博客内容或添加评论

简单编辑文章后
结论
SequoiaDB巨杉数据库作为一款分布式数据库,提供包括结构化SQL、非结构化文件系统和对象存储的机制。

通过SequoiaDB创建的MySQL实例,能够提供与标准MySQL全兼容的SQL与DDL能力,用户无需调整DDL或SQL即可实现无缝透明地访问分布式表结构。

本文向读者展示了如何通过SequoiaDB的MySQL实例,实现与标准MySQL的无缝迁移。通过使用SequoiaDB巨杉数据库,用户可以在满足标准ACID与MySQL协议的基础上,实现近无限的弹性扩展能力。
数据库
2019-06-05 21:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.什么是redis?
Redis 是一个基于内存的高性能key-value数据库。
2.Reids的特点  
Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。
因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能。
比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
3.使用redis有哪些好处?   
速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
支持丰富数据类型,支持string,list,set,sorted set,hash
支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
4.redis相比memcached有哪些优势?   
memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
redis的速度比memcached快很多
redis可以持久化其数据
5.Memcache与Redis的区别都有哪些?
存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,这样能保证数据的持久性。
数据支持类型 Memcache对数据类型支持相对简单。Redis有复杂的数据类型。
使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
6.redis常见性能问题和解决方案:  
Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内
7. mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。
redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
8.请用Redis和任意语言实现一段恶意登录保护的代码,限制1小时内每用户Id最多只能登录5次。具体登录函数或功能用空函数即可,不用详细写出。
用列表实现:列表中每个元素代表登陆时间,只要最后的第5次登陆时间和现在时间差不超过1小时就禁止登陆.用Python写的代码如下:
#!/usr/bin/env python3
import redis
import sys
import time
r = redis.StrictRedis(host=’127.0.0.1′, port=6379, db=0)
try:
id = sys.argv[1]
except:
print(‘input argument error’)
sys.exit(0)
if r.llen(id) >= 5 and time.time() – float(r.lindex(id, 4)) <= 3600:
print(“you are forbidden logining”)
else:
print(‘you are allowed to login’)
r.lpush(id, time.time())
# login_func()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
9.为什么redis需要把所有数据放到内存中? 
Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。
如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
10.Redis是单进程单线程的
redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销
11.redis的并发竞争问题如何解决?
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。
对此有2种解决方法:
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
2.服务器角度,利用setnx实现锁。
注:对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。
12.redis事物的了解CAS(check-and-set 操作实现乐观锁 )?
和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。
相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出Redis中事务的实现特征:
在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为”BEGIN TRANSACTION”语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。
当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。
Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。
此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。
13.WATCH命令和基于CAS的乐观锁: 
在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务
执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:
val = GET mykey
val = val + 1
SET mykey $val
1
2
3
以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景–竞态争用(race condition)。
比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
1
2
3
4
5
6
和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。
14.redis持久化的几种方式
1、快照(snapshots)
缺省情况情况下,Redis把数据快照存放在磁盘上的二进制文件中,文件名为dump.rdb。你可以配置Redis的持久化策略,例如数据集中每N秒钟有超过M次更新,就将数据写入磁盘;或者你可以手工调用命令SAVE或BGSAVE。
工作原理
Redis forks.
子进程开始将数据写到临时RDB文件中。
当子进程完成写RDB文件,用新文件替换老文件。
这种方式可以使Redis使用copy-on-write技术。
2、AOF
快照模式并不十分健壮,当系统停止,或者无意中Redis被kill掉,最后写入Redis的数据就会丢失。
这对某些应用也许不是大问题,但对于要求高可靠性的应用来说,Redis就不是一个合适的选择。Append-only文件模式是另一种选择。你可以在配置文件中打开AOF模式
3、虚拟内存方式
当你的key很小而value很大时,使用VM的效果会比较好.因为这样节约的内存比较大.
当你的key不小时,可以考虑使用一些非常方法将很大的key变成很大的value,比如你可以考虑将key,value组合成一个新的value.
  
vm-max-threads这个参数,可以设置访问swap文件的线程数,设置最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的.可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.
自己测试的时候发现用虚拟内存性能也不错。如果数据量很大,可以考虑分布式或者其他数据库。
15.redis的缓存失效策略和主键失效机制
作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略.
  
在Redis当中,有生存期的key被称为volatile。在创建缓存时,要为给定的key设置生存期,当key过期的时候(生存期为0),它可能会被删除。
  
1、影响生存时间的一些操作
生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆盖原来的数据,也就是说,修改key对应的value和使用另外相同的key和value来覆盖以后,当前数据的生存时间不同。
  
比如说,对一个 key 执行INCR命令,对一个列表进行LPUSH命令,或者对一个哈希表执行HSET命令,这类操作都不会修改 key 本身的生存时间。另一方面,如果使用RENAME对一个 key 进行改名,那么改名后的 key的生存时间和改名前一样。
  
RENAME命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的 another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。使用PERSIST命令可以在不删除 key 的情况下,移除 key 的生存时间,让 key 重新成为一个persistent key 。
  
2、如何更新生存时间
可以对一个已经带有生存时间的 key 执行EXPIRE命令,新指定的生存时间会取代旧的生存时间。过期时间的精度已经被控制在1ms之内,主键失效的时间复杂度是O(1),EXPIRE和TTL命令搭配使用,TTL可以查看key的当前生存时间。设置成功返回 1;当 key 不存在或者不能为 key 设置生存时间时,返回 0 。
  
最大缓存配置:
在 redis 中,允许用户设置最大使用内存大小,server.maxmemory默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。
redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
  
注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。
  
使用策略规则:
如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru
如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random
  
三种数据淘汰策略:
ttl和random比较容易理解,实现也会比较简单。主要是Lru最近最少使用淘汰策略,设计上会对key 按失效时间排序,然后取最先失效的key进行淘汰
16.redis 最适合的场景  
Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?
如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:
Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
1、会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
  
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
2、全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
3、队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。
4、排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。
所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
1
Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。
5、发布/订阅
最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
  
Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
17、Redis集群方案什么情况下会导致整个集群不可用?
有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。
18、Redis支持的Java客户端都有哪些?官方推荐用哪个?
Redisson、Jedis、lettuce等等,官方推荐使用Redisson。
19、Redis和Redisson有什么关系?
Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。
20、Jedis与Redisson对比有什么优缺点?
Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;
Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
21、Redis如何设置密码及验证密码?
设置密码:config set requirepass 123456
授权密码:auth 123456
22、说说Redis哈希槽的概念?
Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
23、Redis集群的主从复制模型是怎样的?
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.
24、Redis集群会有写操作丢失吗?为什么?
Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。
25、Redis集群之间是如何复制的?
异步复制
26、Redis集群最大节点个数是多少?
16384个。
27、Redis集群如何选择数据库?
Redis集群目前无法做数据库选择,默认在0数据库。
28、怎么测试Redis的连通性?
ping
29、Redis中的管道有什么用?
一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。
这就是管道(pipelining),是一种几十年来广泛使用的技术。例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。
30、怎么理解Redis事务?
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
31、Redis事务相关的命令有哪几个?
MULTI、EXEC、DISCARD、WATCH
32、Redis key的过期时间和永久有效分别怎么设置?
EXPIRE和PERSIST命令。
33、Redis如何做内存优化?
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。
比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。
34、Redis回收进程如何工作的?
一个客户端运行了新的命令,添加了新的数据。
Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。
一个新的命令被执行,等等。
所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。
如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。
35、Redis集群方案应该怎么做?都有哪些方案?
1.codis。
目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新hash节点。
2.redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。
3.在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key 进行hash计算,然后去对应的redis实例操作数据。 这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。
数据库
2019-06-05 15:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1,redis是什么
redis 是一种支持Key-Value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。该数据库使用ANSI C语言编写,支持网络,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。

2,支持的语言

3,redis的应用场景有哪些
1,会话缓存(最常用)
2,消息队列,
比如支付3,活动排行榜或计数
4,发布,订阅消息(消息通知)
5,商品列表,评论列表等

4,redis数据类型
Redis 一共支持五种数据类:string(字符串),hash(哈希),list(列表),set(集合)和zset(sorted set有序集合)。
(1)字符串(字符串)
它是redis的最基本的数据类型,一个键对应一个值,需要注意是一个键值最大存储512MB。
(2)hash(哈希)
redis hash是一个键值对的集合,是一个string类型的field和value的映射表,适合用于存储对象
(3)表(列表)
是redis的简单的字符串列表,它按插入顺序排序
(4)组(集合)
是字符串类型的无序集合,也不可重复
(5)zset(sorted set有序集合)
是string类型的有序集合,也不可重复
有序集合中的每个元素都需要指定一个分数,根据分数对元素进行升序排序,如果多个元素有相同的分数,则以字典序进行升序排序,sorted set因此非常适合实现排名

5,redis的服务相关的命令
slect#选择数据库(数据库编号0-15)
退出#退出连接
信息#获得服务的信息与统计
monitor#实时监控
config get#获得服务配置
flushdb#删除当前选择的数据库中的key
flushall#删除所有数据库中的键

6,redis的发布与订阅
redis的发布与订阅(发布/订阅)是它的一种消息通信模式,一方发送信息,一方接收信息。
下图是三个客户端同时订阅同一个频道
下图是有新信息发送给频道1时,就会将消息发送给订阅它的三个客户端


7,redis的持久化
redis持久有两种方式:快照(快照),仅附加文件(AOF)
快照(快照)
1,将存储在内存的数据以快照的方式写入二进制文件中,如默认dump.rdb中
2,保存900 1
#900秒内如果超过1个Key被修改,则启动快照保存
3,保存300 10
#300秒内如果超过10个Key被修改,则启动快照保存
4,保存60 10000
#60秒内如果超过10000个重点被修改,则启动快照保存

仅附加文件(AOF)
1,使用AOF持久时,服务会将每个收到的写命令通过写函数追加到文件中(appendonly.aof)
2,AOF持久化存储方式参数说明
appendonly yes
#开启AOF持久化存储方式
appendfsync always
#收到写命令后就立即写入磁盘,效率最差,效果最好
appendfsync everysec
#每秒写入磁盘一次,效率与效果居中
appendfsync no
#完全依赖操作系统,效率最佳,效果没法保证

8,redis的性能测试
自带相关测试工具
实际测试同时执行100万的请求
数据库
2019-06-05 15:11:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
表格存储是阿里云自研分布式存储系统,可以用来存储海量结构化、半结构化的数据。表格存储支持高性能和容量型两种实例类型。高性能使用SSD的存储介质,针对读多写多的场景都有较好的访问延时。容量型使用的是SSD和SATA混合的存储介质。对写多的场景,性能接近高性能,读方面,如果遇到冷数据产生读SATA盘的话,延时会比高性能上涨一个量级。在海量数据存储场景下,例如时序场景,我们会希望最新的数据可以支持高性能查询,较早的数据的读写频次都会低很多。这时候一个基于表格存储高性能和容量型存储分层的需求就产生了。
方案细节
表格存储近期对外正式发布的全增量一体的通道服务( 参考文档 ),通道服务基于表格存储数据接口之上的全增量一体化服务。通道服务为用户提供了增量、全量、增量加全量三种类型的分布式数据实时消费通道。有了通道服务,我们可以很方便的构建从高性能实例下的表到容量型表之间的实时数据同步,进而可以在高性能表上使用表格存储的特性数据生命周期( 参考文档 ),根据业务需求设置一个合理的TTL。
总体来说就可以构建一个如下图所示的架构:
整个数据的流动过程如下: 业务写入端直接写入高性能实例 高性能实例中的数据通过通道服务同步至容量型 高性能实例中的老数据自动过期,减少存储量占用 用户查询请求根据时序查询条件,判断是否是近期数据 近期数据查询进入高性能,毫秒级别返回 较早数据查询进入容量型,几十毫秒后返回
代码和操作流程:
在高性能实例上根据业务主键需求创建数据表,并设置合理的数据TTL,然后在容量型下创建相同的schema的表用来持久化存储所有数据。
然后在通道页面创建一个全增量类型的通道:
通过控制台可以简单清晰的查看到同步的状态,并发,进度等信息:
下面贴一下通过Tunnel进行复制同样schema表TableStore表的Sample代码: func main () { //高性能实例的信息 tunnelClient := tunnel.NewTunnelClient("", "", "", "") //容量型实例的信息 client := tablestore.NewClient("", "", "", "") //配置callback到SimpleProcessFactory,配置消费端TunnelWorkerConfig workConfig := &tunnel.TunnelWorkerConfig{ ProcessorFactory: &tunnel.SimpleProcessFactory{ ProcessFunc: replicateDataFunc, CustomValue: client, }, } //使用TunnelDaemon持续消费指定tunnel daemon := tunnel.NewTunnelDaemon(tunnelClient, "", workConfig) err := daemon.Run() if err != nil { fmt.Println("failed to start tunnel daemon with error:", err) } } func replicateDataFunc(ctx *tunnel.ChannelContext, records []*tunnel.Record) error { client := ctx.CustomValue.(*tablestore.TableStoreClient) fmt.Println(client) for _, rec := range records { fmt.Println("tunnel record detail:", rec.String()) updateRowRequest := new(tablestore.UpdateRowRequest) updateRowRequest.UpdateRowChange = new(tablestore.UpdateRowChange) updateRowRequest.UpdateRowChange.TableName = "coldtable" updateRowRequest.UpdateRowChange.PrimaryKey = new(tablestore.PrimaryKey) updateRowRequest.UpdateRowChange.SetCondition(tablestore.RowExistenceExpectation_IGNORE) for _, pk := range rec.PrimaryKey.PrimaryKeys { updateRowRequest.UpdateRowChange.PrimaryKey.AddPrimaryKeyColumn(pk.ColumnName, pk.Value) } for _, col := range rec.Columns { if col.Type == tunnel.RCT_Put { updateRowRequest.UpdateRowChange.PutColumn(*col.Name, col.Value) } else if col.Type == tunnel.RCT_DeleteOneVersion { updateRowRequest.UpdateRowChange.DeleteColumnWithTimestamp(*col.Name, *col.Timestamp) } else { updateRowRequest.UpdateRowChange.DeleteColumn(*col.Name) } } _, err := client.UpdateRow(updateRowRequest) if err != nil { fmt.Println("hit error when put record to cold data", err) } } fmt.Println("a round of records consumption finished") return nil }
总结
通过通道服务,存储在表格存储中的结构化,半结构化数据可以实时流出,进行加工,萃取,计算或进行同步。如果是想进一步降低冷数据的存储成本,可以参考 这篇文章 把表格存储的数据备份到OSS归档存储。
作者:宇珩
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-06-05 12:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、创建组、用户、安装目录、分配用户权限
$su - root
#groupadd dba //数据库系统管理理组
#groupadd oinstall //Oracle文件所有者的组
#useradd -g oinstall -G dba -m -s /bin/bash oracle
#passwd oracle
#chown -R oracle.oinstall /home/oracle
mkdir -p /data/oracle  #oracle数据库安装目录
mkdir -p /data/oraInventory  #oracle数据库配置文件目录
mkdir -p /data/database  #oracle数据库软件包解压目录
cd /data
ls  #创建完毕检查一下
chown -R oracle:oinstall /data/oracle  #设置目录所有者为oinstall用户组的oracle用户
chown -R oracle:oinstall /data/oraInventory
chown -R oracle:oinstall /data/database
2、安装xclock
参考地址: https://www.cnblogs.com/tibit/p/6134150.html
3、修改OS系统标识
oracle默认不支持CentOS系统安装, 修改文件 /etc/RedHat-release 内容为RedHat-7
vi /etc/redhat-release#修改成红色部分文字
redhat-7
4、安装oracle数据库所需要的软件包
使用下面指令,检查依赖软件包
yum install binutils-2.* compat-libstdc++-33* elfutils-libelf-0.* elfutils-libelf-devel-* gcc-4.* gcc-c++-4.* glibc-2.* glibc-common-2.* glibc-devel-2.* glibc-headers-2.* ksh-2* libaio-0.* libaio-devel-0.* libgcc-4.* libstdc++-4.* libstdc++-devel-4.* make-3.* sysstat-7.* unixODBC-2.* unixODBC-devel-2.* pdksh*
5、关闭防火墙
systemctl disable firewalld
6、关闭SELINUX
vi /etc/selinux/config
#SELINUX=enforcing #注释掉
#SELINUXTYPE=targeted #注释掉
SELINUX=disabled #增加
:wq! #保存退出
setenforce 0 #使配置立即生效
7、修改内核参数
vi /etc/sysctl.conf #以下是要添加sysctl.conf内容
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.rp_filter = 1
fs.file-max = 6815744 #设置最大打开文件数
fs.aio-max-nr = 1048576
kernel.shmall = 2097152 #共享内存的总量,8G内存设置:2097152*4k/1024/1024
kernel.shmmax = 2147483648 #最大共享内存的段大小
kernel.shmmni = 4096 #整个系统共享内存端的最大数
kernel.sem = 250 32000 100 128
net.ipv4.ip_local_port_range = 9000 65500 #可使用的IPv4端口范围
net.core.rmem_default = 262144
net.core.rmem_max= 4194304
net.core.wmem_default= 262144
net.core.wmem_max= 1048576
8、对oracle用户设置限制,提高软件运行性能
vi /etc/security/limits.conf #以下部分要添加到Limits.conf内容
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft nofile 1024
oracle hard nofile 65536
9、配置用户的环境变量
vi /home/oracle/.bash_profile #以下部分是要追加bash_profile内容部分
export ORACLE_BASE=/data/oracle #oracle数据库安装目录
export ORACLE_HOME=$ORACLE_BASE/product/11.2.0/db_1 #oracle数据库路径
export ORACLE_SID=orcl #oracle启动数据库实例名
export ORACLE_TERM=xterm #xterm窗口模式安装
export PATH=$ORACLE_HOME/bin:/usr/sbin:$PATH #添加系统环境变量
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib #添加系统环境变量
export LANG=C #防止安装过程出现乱码
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK #设置Oracle客户端字符集,必须与Oracle安装时设置的字符集保持一致
安装包拷贝到/data/database文件夹,分配权限
chown -R oracle:oinstall /data/database/database/
chmod 775 data/database/database/
10、在xshell窗口分别执行
export DISPLAY=本地IP:0.0
xclock
命令,xmanager出现图形化界面,左上角显示一个时钟
在xshell中对应目录下执行./runInstaller命令,安装数据库,xmanager会弹出oracle数据库安装的图形化界面
备注:
Oracle安装INS-30131解决办法
chmod -R 775 /tmp
数据库
2019-06-05 10:54:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MySQL索引?这玩意儿还能简单聊?明显是在挖坑,幸好老夫早有准备,切听我一一道来。
一、索引是什么?
索引是帮助MySQL高效获取数据的数据结构。
二、索能干什么?
索引非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。 索引能够轻易将查询性能提高好几个数量级,总的来说就是可以明显的提高查询效率。
三、索引的分类?
1、从存储结构上来划分:BTree索引(B-Tree或B+Tree索引),Hash索引,full-index全文索引,R-Tree索引。这里所描述的是索引存储时保存的形式,
2、从应用层次来分:普通索引,唯一索引,复合索引
3、根据中数据的物理顺序与键值的逻辑(索引)顺序关系:聚集索引,非聚集索引。
平时讲的索引类型一般是指在应用层次的划分。
就像手机分类:安卓手机,IOS手机 与 华为手机,苹果手机,OPPO手机一样。
普通索引 : 即一个索引只包含单个列,一个表可以有多个单列索引
唯一索引 : 索引列的值必须唯一,但允许有空值
复合索引 : 多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
聚簇索引(聚集索引) : 并不是一种单独的索引类型,而是一种数据存储方式。具体细节取决于不同的实现,InnoDB的聚簇索引其实就是在同一个结构中保存了B-Tree索引(技术上来说是B+Tree)和数据行。
非聚簇索引 : 不是聚簇索引,就是非聚簇索引
四、索引的底层实现
mysql默认存储引擎innodb只显式支持B-Tree( 从技术上来说是B+Tree)索引,对于频繁访问的表,innodb会透明建立自适应hash索引,即在B树索引基础上建立hash索引,可以显著提高查找效率,对于客户端是透明的,不可控制的,隐式的。 不谈存储引擎,只讨论实现(抽象)
Hash索引
基于哈希表实现,只有精确匹配索引所有列的查询才有效,对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),并且Hash索引将所有的哈希码存储在索引中,同时在索引表中保存指向每个数据行的指针。
B-Tree索引(MySQL使用B+Tree)
B-Tree能加快数据的访问速度,因为存储引擎不再需要进行全表扫描来获取数据,数据分布在各个节点之中。
B+Tree索引
是B-Tree的改进版本,同时也是数据库索引索引所采用的存储结构。数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来说,进行范围查找时只需要查找两个节点,进行遍历即可。而B-Tree需要获取所有节点,相比之下B+Tree效率更高。
结合存储引擎来讨论(一般默认使用B+Tree)
案例:假设有一张学生表,id为主键
id name birthday 1 Tom 1996-01-01
2 Jann 1996-01-04
3 Ray 1996-01-08
4 Michael 1996-01-10
5 Jack 1996-01-13
6
7
Steven
Lily
1996-01-23
1996-01-25
在MyISAM引擎中的实现 (二级索引也是这样实现的)
在InnoDB中的实现


五、为什么索引结构默认使用B+Tree,而不是Hash,二叉树,红黑树?

B-tree:因为B树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低;
Hash:虽然可以快速定位,但是没有顺序,IO复杂度高。
二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。
红黑树:树的高度随着数据量增加而增加,IO代价高。

六、为什么官方建议使用自增长主键作为索引?
结合B+Tree的特点,自增主键是连续的,在插入过程中尽量减少页分裂,即使要进行页分裂,也只会分裂很少一部分。并且能减少数据的移动,每次插入都是插入到最后。总之就是减少分裂和移动的频率。
插入连续的数据:
插入非连续的数据
七、简单总结下
1、MySQL使用B+Tree作为索引数据结构。
2、B+Tree在新增数据时,会根据索引指定列的值对旧的B+Tree做调整。
4、从物理存储结构上说,B-Tree和B+Tree都以页(4K)来划分节点的大小,但是由于B+Tree中中间节点不存储数据,因此B+Tree能够在同样大小的节点中,存储更多的key,提高查找效率。
5、影响MySQL查找性能的主要还是磁盘IO次数,大部分是磁头移动到指定磁道的时间花费。
6、MyISAM存储引擎下索引和数据存储是分离的,InnoDB索引和数据存储在一起。
7、InnoDB存储引擎下索引的实现,(辅助索引)全部是依赖于主索引建立的(辅助索引中叶子结点存储的并不是数据的地址,还是主索引的值,因此,所有依赖于辅助索引的都是先根据辅助索引查到主索引,再根据主索引查数据的地址)。
8、由于InnoDB索引的特性,因此如果主索引不是自增的(id作主键),那么每次插入新的数据,都很可能对B+Tree的主索引进行重整,影响性能。因此,尽量以自增id作为InnoDB的主索引。
数据库
2019-06-04 21:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
根据MySql文档,我们知道在修改表内某一列的属性的时候,MySql支持3中语法结构: ALTER [ONLINE|OFFLINE] [IGNORE] TABLE tbl_name ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} ALTER [ONLINE|OFFLINE] [IGNORE] TABLE tbl_name CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name] ALTER [ONLINE|OFFLINE] [IGNORE] TABLE tbl_name MODIFY [COLUMN] col_name column_definition [FIRST | AFTER col_name]
这里比较一下这三种语法的不同之处,以及什么情况下应该选用什么语法
语法 功能 说明 ALTER 只能更改列的默认值
CHANGE 可以重命名列或者修改列的定义 标准SQL的扩展
MODIFY
通过文档介绍的功能,我们就基本能够判断处该使用使用哪种语法,CHANGE功能最强大,什么情况下都可以使用(达到预期的效果)。但是还有一个区别:
可以更改列的定义,但不能更改列的名称

兼容Oracle的扩展

ALTER 语法只是修改 .frm 文件,不会去更新表中的数据; MODIFY和CHANGE在更新表结构的时候重新插入表中的数据,因此比较耗费时间。
所以,当只需要修改某一列的默认值的时候,优先选择用ALTER,需要修改列的名称用CHANGE,只修改列的定义用MIODIFY 如果修改的列上有索引,修改完后最好重建一下索引
数据库
2019-06-04 21:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.1 基础概念
MVCC,Multi-Version Concurrency Control,多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。
如果有人从数据库中读数据的同时,有另外的人写入数据,有可能读数据的人会看到『半写』或者不一致的数据。有很多种方法来解决这个问题,叫做并发控制方法。最简单的方法,通过加锁,让所有的读者等待写者工作完成,但是这样效率会很差。MVCC 使用了一种不同的手段,每个连接到数据库的读者, 在某个瞬间看到的是数据库的一个快照 ,写者写操作造成的变化在写操作完成之前(或者数据库事务提交之前)对于其他的读者来说是不可见的。
当一个 MVCC 数据库需要更一个一条数据记录的时候,它不会直接用新数据覆盖旧数据,而是将旧数据标记为过时(obsolete)并在别处增加新版本的数据。这样就会有存储多个版本的数据,但是只有一个是最新的。这种方式允许读者读取在他读之前已经存在的数据,即使这些在读的过程中半路被别人修改、删除了,也对先前正在读的用户没有影响。这种多版本的方式避免了填充删除操作在内存和磁盘存储结构造成的空洞的开销,但是需要系统周期性整理(sweep through)以真实删除老的、过时的数据。对于面向文档的数据库(Document-oriented database,也即半结构化数据库)来说,这种方式允许系统将整个文档写到磁盘的一块连续区域上,当需要更新的时候,直接重写一个版本,而不是对文档的某些比特位、分片切除,或者维护一个链式的、非连续的数据库结构。
MVCC 提供了时点(point in time)一致性视图。MVCC 并发控制下的读事务一般使用 时间戳 或者 事务 ID 去标记当前读的数据库的状态(版本),读取这个版本的数据。读、写事务相互隔离,不需要加锁。读写并存的时候,写操作会根据目前数据库的状态,创建一个新版本,并发的读则依旧访问旧版本的数据。 一句话讲,MVCC就是用 同一份数据临时保留多版本的方式 的方式,实现并发控制。
这里留意到 MVCC 关键的两个点: 在读写并发的过程中如何实现多版本; 在读写并发之后,如何实现旧版本的删除(毕竟很多时候只需要一份最新版的数据就够了);
1.2 实现
MVCC 使用时间戳(TS)、递增的事务 ID(T)实现事务一致性。
MVCC 通过维护多版本数据,保证一个读事务永远不会被阻塞。对象 P 维护有多个版本,每个版本会有一个读时间戳(Read TimeStamp, RTS)和 写时间戳(Write TimeStamp, WTS),事务 Ti 读对象 P 的最新版本,该版本早于事务 Ti 的读时间戳 RTS(Ti)。
事务 Ti 要对 P 执行写操作,如果有其他事务 Tk 同时对 P 操作,则 RTS(Ti)必须要早于 RTS(Tk),即有 RTS(Ti) < RTS(Tk),这样对 Ti 对 P 的写操作才能完成。一般地,如果其他事务拥有 P 的一个更早的读时间戳的情况下,写操作是不能完成的。打个比方就是在存储前面有一道线,只有等你前面的人的完成了他们的事务,你的修改事务才可以提交完成。
重复说一下:每个对象 P 有一个时间戳 TS,如果事务 Ti 想要对 P 执行写操作,(写要先读)事务的读时间戳是 RTS(Ti),如果有其他事务拥有一个比较早的时间戳,有 TS(P) < RTS(Ti),这时事务 Ti 会退出并重新开始。否则,事务 Ti 创建一个 P 的新版本,并设置新版本 P 的时间戳,似的 TS = TS(Ti)。
MVCC 系统明显的缺点是会存储多个版本数据的冗余开销。但同时,读操作永不会被阻塞,这对那些以读操作为主的数据库来说非常重要。MVCC 实现了真的快照隔离(snapshot isolation),然后其他的并发控制方法要么是不完整的快照隔离方式,要么需要较高的性能损耗。 Wikipedia 中的内容有点繁琐,简单地,上面的描述,阐明了在同一数据版本下写操作的限制,已经通过多版本实现快照隔离的优越性。
1.3 示例
Time Object1 Object2
0
1
"Foo" by T0
"Hello" by T1
"Bar" by T0

Time=1 的时候数据库的状态如上:
T0 写 Object1 为 "Foo",写 Object2 为 "Bar";之后 T1 写 Object1 为 "Hello",保留 Object2 为原始值。 Object1 的新值将取代 Time=0 时刻的旧值,并提供给 T1提交之后的发生的所有事务。Object1的版本号为0的旧数据会被 GC 掉。
如果有一个长事务 T2,在 T1之后对 Object1和 Object2 进行读操作,同时并行地,有事务 T3 做更新:删除 Object2、增加 Object3="Foo-Bar" ,在 Time=2 数据的状态如下所示:
Time Object1 Object2 Object3
0
1 2
"Foo" by T0
"Hello" by T1
"Bar" by T0
(delete)by T3
"Foo-Bar" by T3
在 Time=2 Object2有一个新版本:标记删除,同时增加了新对象 Object3 。T2 和 T3 并发执行,T2 看到的是数据在 Time=2 且 T3提交前的版本,这样 T2读到了 Object2="Bar"" 且 Object1="Hello" 。
以上就是 MVCC 在不加锁的情况下实现的快照隔离的读的原理。
1.4 历史
最早于1978年,论文『Naming and Synchronization in a Decentralized Computer System』清晰地介绍了 MVCC,这是公认关于 MVCC 最早的工作。
在1981年,论文『Concurrency Control in Distributed Database System』介绍MVCC的一些细节。
目前支持 MVCC 的数据库,包括 DB2、Oracle、Sybase、SQL Server、MySQL、PG 等所有主流数据库,以及 HBase、Couchbase、Berkeley DB 等 NoSQL 数据库
数据库
2019-06-04 20:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Oracle中INSTR和SUBSTR的用法
Oracle中INSTR的用法:
INSTR方法的格式为
INSTR(源字符串, 要查找的字符串, 从第几个字符开始, 要找到第几个匹配的序号)
返回找到的位置,如果找不到则返回0.
例如:INSTR('CORPORATE FLOOR','OR', 3, 2)中,源字符串为'CORPORATE FLOOR', 在字符串中查找'OR',从第三个字符位置开始查找"OR",取第三个字后第2个匹配项的位置。
默认查找顺序为从左到右。当起始位置为负数的时候,从右边开始查找。
所以SELECT INSTR('CORPORATE FLOOR', 'OR', -1, 1) "aaa" FROM DUAL的显示结果是
Instring
——————
14
oracle的substr函数的用法:
取得字符串中指定起始位置和长度的字符串 substr( string, start_position, [ length ] )
如:
substr('This is a test', 6, 2) would return 'is'
substr('This is a test', 6) would return 'is a test'
substr('TechOnTheNet', -3, 3) would return 'Net'
substr('TechOnTheNet', -6, 3) would return 'The'

select substr('Thisisatest', -4, 2) value from dual
综合应用:
SELECT INSTR('CORPORATE FLOOR', 'OR', -1, 1) "Instring" FROM DUAL
--INSTR(源字符串, 目标字符串, 起始位置, 匹配序号)
SELECT INSTR('CORPORATE FLOOR','OR', 3, 2) "Instring" FROM DUAL
SELECT INSTR('32.8,63.5',',', 1, 1) "Instring" FROM DUAL
SELECT SUBSTR('32.8,63.5',INSTR('32.8,63.5',',', 1, 1)+1) "INSTRING" FROM DUAL
SELECT SUBSTR('32.8,63.5',1,INSTR('32.8,63.5',',', 1, 1)-1) "INSTRING" FROM DUAL
-- CREATED ON 2008-9-26 BY ADMINISTRATOR
DECLARE
-- LOCAL VARIABLES HERE
T VARCHAR2(2000);
S VARCHAR2(2000);
NUM INTEGER;
I INTEGER;
POS INTEGER;
BEGIN
-- TEST STATEMENTS HERE
T := '12.3,23.0;45.6,54.2;32.8,63.5;';
SELECT LENGTH(T) - LENGTH(REPLACE(T, ';', '')) INTO NUM FROM DUAL;
DBMS_OUTPUT.PUT_LINE('NUM:' || NUM);
POS := 0;
FOR I IN 1 .. NUM LOOP
DBMS_OUTPUT.PUT_LINE('I:' || I);
DBMS_OUTPUT.PUT_LINE('POS:' || POS);
DBMS_OUTPUT.PUT_LINE('==:' || INSTR(T, ';', 1, I));
DBMS_OUTPUT.PUT_LINE('INSTR:' || SUBSTR(T, POS + 1, INSTR(T, ';', 1, I) - 1));
POS := INSTR(T, ';', 1, I);
END LOOP;
END;
-- Created on 2008-9-26 by ADMINISTRATOR
declare
-- Local variables here
i integer;
T VARCHAR2(2000);
S VARCHAR2(2000);
begin
-- Test statements here
--历史状态
T := '12.3,23.0;45.6,54.2;32.8,63.5;';
IF (T IS NOT NULL) AND (LENGTH(T) > 0) THEN
--T := T || ',';
WHILE LENGTH(T) > 0 LOOP
--ISTATUSID := 0;
S := TRIM(SUBSTR(T, 1, INSTR(T, ';') - 1));
IF LENGTH(S) > 0 THEN
DBMS_OUTPUT.PUT_LINE('LAT:'||SUBSTR('32.8,63.5',1,INSTR('32.8,63.5',',', 1, 1)-1));
DBMS_OUTPUT.PUT_LINE('LON:'||SUBSTR('32.8,63.5',INSTR('32.8,63.5',',', 1, 1)+1));
-- COMMIT;
END IF;
T := SUBSTR(T, INSTR(T, ';') + 1);
END LOOP;
END IF;
end;
数据库
2019-06-04 20:34:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
SELECT *
FROM table_name AS OF TIMESTAMP TO_TIMESTAMP('2016-06-17 16:00:00', 'YYYY-MM-DD HH24:MI:SS')
WHERE XQID='20160219-133933-03263723'
and XSID='440106200012311810'




SELECT *
FROM table_name AS OF TIMESTAMP sysdate -62/1440//62分钟前的数据
WHERE XQID='20160219-133933-03263723'
and XSID='440106200012311810' order by rqxx desc
数据库
2019-06-04 20:34:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
SELECT sum(fzxx) FROM table_name VERSIONS BETWEEN TIMESTAMP MINVALUE AND
MAXVALUE WHERE XQID='20160219-133933-03263723'
and XSID='440106200012311810'
order by
RQXX desc ;
数据库
2019-06-04 20:34:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一、配置web.xml

struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

actionPackages
com.test.action




struts2
/*


二、加入注解
@Namespace(value="/test")
@Action(value = "file-manager",
interceptorRefs = {
@InterceptorRef(value = "fileUpload",params={"maximumSize","1024000","allowedTypes","image/pjpeg"}),
@InterceptorRef(value = "basicStack")},
results = {@Result(name = ActionSupport.SUCCESS, location = "/view/file-manager-sucess.jsp"),
@Result(name = ActionSupport.ERROR, location = "/view/file-manager-error.jsp") },
exceptionMappings = {@ExceptionMapping(exception = "java.lang.Exception", result = ActionSupport.ERROR)}
)

验证注解:
@Validations(
requiredStrings={
@RequiredStringValidator(fieldName="username",message="用户名不能为空!"),
@RequiredStringValidator(fieldName="telNum",message="电话号码不能为空!")
},
regexFields={@RegexFieldValidator(fieldName="telNum",expression="^(//+86|0|1)//d{10,11}$",
message="电话号码格式不正确!")}
)

跳过验证注解:
@SkipValidation

三、Convention的Annotation
1)与Action相关的两个Annotation是@Action 和@Actions
2)@Action中可指定一个value属性。类似于指定属性值
3)@Action中还可以指定一个params属性,该属性是一个字符串数组,用于该Acion指定的参数名和参数值。params属性应遵守如下格式:{“name1”,”value1”,”name2”,”value2”}
4)@Actions 也用于修饰Action类里的方法,用于将该方法映射到多个URL.@Actions用于组织多个@Action.因此它可将一个方法映射成多个逻辑Action。

四、与Result配置相关的Annotation
1)@ResultPath @Result 和Results
2)@Results用于组织多个@Result因此它只需指定一个value属性值,该value属性值为多个@Result
3)@Result相当于struts.xml文件中的元素。使用@Result必须指定一个name属性,相当于另外,它还有几个可选的属性。
☆ type 相当于指定返回视图资源的类型
☆ location 相当于…..中间部分,用于指定实际视图位置
☆ params:该属性相当于元素里多个子元素的作用,用于为该Result指定参数值。该属性应满足{“name1”,”value1”,”name2”,”value2”}格式
4)@Result有以下两种用法
1.Action级的Result映射:以@Actions组合多个@Action后修饰的Action类。这种Result映射对该Action里的所有方法都有效。
2.方法级Result映射:将多个@Result组成数组后作为@Action的results属性值。这种Result映射仅对被修饰的方法有效。
5)@ResultPath则用于修饰包和Action类,用于改变被修饰Action所对应的物理视图资源的根路径。举例说:默认情况下,Convention都会到WEB-INF/content路径下找物理视图资源,一旦我们使用@ResultPath("/view")修饰该Action,系统将回到view目录下寻找物理视图资源。

五、与包和命名空间相关的Annotation:
@Namespace:修饰Action类或其所在的包。该Annotation中指定一个value属性值,用于指定被修饰的Action所在的命名空间
@Namespaces:修饰Action类或其所在的包,用于组合多个@Namespace
@ParentPackage: 用于指定被修饰的Action所在包的父包。

六、与异常处理相关的Annotation
@ExceptionMappings 用于组织多个@ExceptionMapping,因此它只需指定一个value属性值,该value属性值为多个@ExceptionMapping。
@ExceptionMapping 用于定义异常类和物理视图之间的对应关系,也相当于struts.xml文件里元素的作用 使用时,必须注意以下两个属性:
exception: 用于指定异常类
result:用于指定逻辑视图
@ExceptionMpping有如下两种用法
Action级的异常定义:以@ExceptionMappings组合多个@ExceptionMapping后修饰的Action类。这种异常定义对Action中的所有方法有效
方法级的异常定义:将多个@ExceptionMapping组成数组后作为@Action的exceptionMappings属性值,这种异常定义仅对被修饰的方法有效。

七、与拦截器配置相关的Annotation
与拦截器配置的Annotation有@InterceptorRef、@InterceptorRefs和@DefaultInterceptorRef
@InterceptorRefs用于组织多个@InterceptorRef,因此它只需要指定一个value属性值,该value属性值为多个@InterceptorRef
@InterceptorRef用于为指定Action引用lanjieq或者是拦截器栈。也就相当于strut.xml中位于元素内部的子元素的作用。使用@InterceptorRefAnnotation时,必须制定一个value属性,用于指定所引用的拦截器或拦截器栈的名字。相当于子元素里name属性的作用。

八、查看struts2配置
为了看到struts2应用里的Action等各种资源的影射情况,struts2提供了Config Browser插件。
使用方法:将struts2-config-browser-plugin-2.1.6.jar文件复制到struts2应用的WEB-INF/lib目录中。
打开首页地址:http://localhost:8080/应用名字/config-browser/actionNames.action 这里可以看到Config Browser插件的首页。











struts2注解实例:

使用注解完成一个Action的流程必须要如下的7个jar包
1.commons-fileupload-1.2.1.jar
2.commons-io-1.3.2.jar
3.freemarker-2.3.15.jar
4.ognl-2.7.3.jar
5.struts2-convention-plugin-2.1.8.1.jar
6.struts2-core-2.1.8.1.jar
7.xwork-core-2.1.6.jar



如下用户登录的Action通过注解的方式验证通过:

package com.huawei.action;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ExceptionMapping;
import org.apache.struts2.convention.annotation.ExceptionMappings;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.ActionSupport;
/**
* @name
* @date 2011-3-24
* @action LoginAction.java
* @time 下午11:23:58
* @package_name com.huaweiaction
* @project_name steutsAction
*/
/*
* 这个小Demo的主要作用就是温故一下Struts2 Action的注解
* 一般在一个项目实施开发中是不会配置struts.xml进行Action的转发或重定向的,其都是通过注解的方式来配置Action的
*/
///////////使用注解来配置Action///////////////////////////

@ParentPackage("struts-default")
// 父包
@Namespace("")
@Results( {
@Result(name = com.opensymphony.xwork2.Action.SUCCESS, location = "/msg.jsp"),
@Result(name = com.opensymphony.xwork2.Action.ERROR, location = "/erlogin.jsp") })

// @ExceptionMappings 一级声明异常的数组
// @ExceptionMapping 映射一个声明异常
@ExceptionMappings( {
@ExceptionMapping(exception = "java.lange.RuntimeException", result = "error") })
public class LoginAction extends ActionSupport {
private static final long serialVersionUID = -2554018432709689579L;
private String loginname;
private String pwd;

// @Action(value="login") 指定某个请求处理方法的请求URL。注意,它不能添加在Action类上,要添加到方法上。
@Action(value = "loginName")
public String login() throws Exception {
if ("HEFE".equalsIgnoreCase(loginname.trim())&&"123".equalsIgnoreCase(pwd.trim())) {
return SUCCESS;
}
else {
System.out.println("===========");
return ERROR;
}
}

@Action(value = "add", results = { @Result(name = "success", location = "/index.jsp") })
public String add() throws Exception {
return SUCCESS;
}

public String getLoginname() {
return loginname;
}

public void setLoginname(String loginname) {
this.loginname = loginname;
}

public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}

}
数据库
2019-06-04 20:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
那就从简单的标签说起吧!1.x中常用的标签只有4中html、bean、logic、tiles

而struts2.0里的标签却没有分类,只用在jsp头文件加上


<%@ taglib prefix="s" uri="/struts-tags" %> 就能使用struts2.0的标签库


下面就介绍每个标签的具体应用实例说明:按字母排列


A:

1.
2. -----超链接,类似于html里的
3. -----执行一个view里面的一个action
4. -----如果action的errors有值那么显示出来
5. -----如果action的message有值那么显示出来
6. -----添加一个值到list,类似于list.add();
7. -----自动完成标签的内容,这个是ajax


B:


1. -----类似于struts1.x中的,JavaBean的值


C:


1. -----复选框
2. -----多选框
3. -----下拉框
4. -----图像符号






D:


1. -----获取日期格式
2. -----日期输入框
3. -----显示错误信息
4. -----表示一个块,类似于html的

5. -----双下拉框






E:


1.
2.
3. -----这3个标签一起使用,表示条件判断






F:


1. -----显示文件错误信息
2. -----文件上传
3. -----获取相应form的值






G:


1. ----和标签一起使用






H:


1. -----在里使用,表示头文件结束
2. -----隐藏值






I:


1. -----加载资源包到值堆栈
2. -----包含一个输出,servlet或jsp页面
3. -----获取form的一个输入
4. -----用于遍历集合






L:


1. -----只读的标签






M:


1. -----合并遍历集合出来的值






O:


1. -----获取标签组
2. -----左右选择框






P:


1. -----为其他标签提供参数
struts2的s:param标签主要有两个属性name与value, 若想在value属性中输入直接量,则可以这样写:, 也可以这样写:user。但如果直接赋值,这个值不是由Action动态生成的,而是自己指定的一个字符串,则只能用后者



2. -----密码输入框
3. -----得到'value'的属性
4. -----value的值push到栈中,从而使property标签的能够获取value的属性






R:


1.
2. -----单选按钮
3. -----重置按钮






S:


1. -----单选框
2. -----赋予变量一个特定范围内的值
3. -----通过属性给list分类
4. -----提交按钮
5. -----为遍历集合输出子集






T:


1. -----表格框
2. -----表格
3. -----I18n文本信息
4. -----文本域输入框
5. -----文本输入框
6. -----拦截器
7. -----树
8. -----树的结构






U:


1. -----多选择框
2. -----创建url












___________________________________________________




struts2框架的标签库分为三类:


用户标签:生成HTML元素
非用户标签:数据访问、逻辑控制等。
AJAX标签:支持AJAX技术。
用户界面标签又分两类:


表单标签:生成HTML页面的from元素
非表单标签:生成页面上的Tab、Tree等
非界面标签又分为数据访问标签和流程控制标签。





控制标签:


if/elseif/else标签:控制流程分支。
iteratot标签:对集合属性迭代(属性类型:List、Map、数组)。
append标签:将多个集合对象拼接在一起,组成一个新的集合。将多个集合使用一个标签完成迭代。
generatot标签:将指定的字符串按照规定的分隔符分解成多个子字符串。
merge标签:将多个集合拼接在一起。
subset标签:获取某个集合的子集合。
sort标签:多指定的集合元素进行排序。
数据标签:


action:直接调用一个Action,根据executeResult参数,可以将Action的处理结果包含到页面中。
bean:创建一个JavaBean实例。。
date:格式化输出一个日期属性。
debug:生成一个调试链接,当单击该链接时,可以看到当前栈值中的内容。
i18n:指定国际化资源文件的baseName。
include:包含其他的页面资源。
param:设置参数。
property:输出某个值。可以输出值栈、StackContext 、ActionContext中的值。
push:将某个值放入值栈。
set:设置一个新的变量。
text:输出国际化信息。
url:生成一个URL地址。
表单标签:


checkbox标签:复选框。
checkboxlist标签:根据一个集合属性创建一系列的复选框。
combobox标签:生成一个单选文本框和一个下拉列表框的组合。
doubleselect标签:生成一个相互关联的列表框,该标签由两个下拉选择框组成。
datetimepicker标签:生成一个日期、时间下拉列表框。
head标签:生成HTML页面的HEAD部分。
file标签:在页面上生成一个上传文件元素。
hidder标签:生成一个不看见的用户输入元素。
select标签:生成下拉列表框。
optiontransferselect标签:创建两个选项以及转移下拉列表项,该标签生成两个下拉列表框,同时生成相应的按钮,这些按钮可以控制选项在两个下拉列表之间移动、排序。
radio标签:单选框。
optgroup标签:生成一个下拉列表框的选择组,下拉列表框中可以包含多个选择组。
token标签:防用户多次提交表单。
textarea标签:文本域。
updownselct标签:支持选项内容的上下移动。
password表单:密码表单域。
textfield标签:单行文本输入框。
非表单标签:


actionerror标签:输出Action中getActionErrors()方法返回的异常信息。
actionmessage标签:输出Action中getActionErrors()方法返回的信息。
component标签:生成一个自定义的组件。
div标签:AJAX标签,生成一个div片段。
fielderror标签:输出异常提示信息。
tabbedPanel:AJAX标签,生成HTML中的Tab页。
tree标签:生成一个树形结构。
treenode标签:生成树形结构的节点。


















____________________________________________________
Struts之LOGIC标签库详解




1. logic:empty
该标签是用来判断是否为空的。如果为空,该标签体中嵌入的内容就会被处理。该标签用于以下情况:
1)当Java对象为null时;
2)当String对象为""时;
3)当java.util.Collection对象中的isEmpty()返回true时;
4)当java.util.Map对象中的isEmpty()返回true时。
eg.


该句等同于:
if (userList.isEmpty()) {
...
}


2. logic:notEmpty
该标签的应用正好和logic:empty标签相反。


3. logic:equal
该标签为等于比较符。
eg1. 比较用户的状态属性是否1,若为1,输出"启用";
eg2. 如果上例中的value值是动态获得的,例如需要通过bean:write输出,因struts不支持标签嵌套,可采用EL来解决该问题。


4. logic:notEqual
该标签意义与logic:equal相反,使用方法类似,略。


5. logic:forward
该标签用于实现页面导向,查找配置文件的全局forward。struts-config.xml文件中全局转向
eg.


6. logic:greaterEqual
为大于等于比较符。
eg. 当某学生的成绩大于等于60时,输出“及格”:

及格

7. logic:greaterThan
此为大于比较符,使用方法同logic:greaterEqual;
8. logic:lessEqual
此为小于等于比较符,使用方法同logic:greaterEqual;
9. logic:lessThan
此为小于比较符,使用方法同logic:greaterEqual;
10. logic:match
此标签比较对象是否相等;
eg1. 检查在request范围内的name属性是否包含"hello"串:

中有一个“hello”串。

eg2. 检查在request范围内的name属性是否已“hello”作为起始字符串:

以“hello”作为起始字符串。

eg3.

你运行的是Windows系统

11. logic:notMatch
此标签用于比较对象是否不相同,与logic:match意义相反,使用方法类似


12. logic:messagePresent
该标签用于判断ActionMessages/ActionErrors对象是否存在;
eg. 如果存在error信息,将其全部输出:





13. logic:messagesNotPresent
该标签用于判断ActionMessages/ActionErrors对象是否不存在,使用方法与logic:messagePresent类似
14. logic:present
此标签用于判断request对象传递参数是否存在。
eg1. user对象和它的name属性在request中都存在时,输出相应字符串:

user对象和该对象的name属性都存在

eg2. 若有一个名字为“hello”的JavaBean,输出对应字符串:

有一个名字为“hello”的JavaBean。

eg3.

we got a user-agent header.

15. logic:notPresent
此标签用于判断request对象传递参数是否不存在,意义与了logic:present相反,使用方法类似。
16. logic:iterator
用于显示列表为collection的值(List ,ArrayList,HashMap等)。
eg1. 逐一输出用户列表(empList)中用户的姓名:




eg2. 从用户列表中输出从1开始的两个用户的姓名

.


eg3. logic:iterator标签的嵌套举例

.






17. logic:redirect
该标签用于实现页面转向,可传递参数。
eg.






logic:iterate
logic:iterate标签用来迭代集合,您可以使用如下方式来为这个标签指定其要叠代的集合:


使用一个运行时表达式,这个表达式的值是一个集合。
用name属性引用一个JSP Bean,这个JSP Bean本身就是一个集合。
用name属性引用一个JSP Bean,这个JSP Bean的一个属性是一个集合,这时可以联合使用property来指定这个集合。
上面所提到的集合可以是:


对象类型或原子类型的数组(Array)。
java.util.Collection的实现,包括ArrayList,Vector。
java.util.Enumeration的实现。
java.util.Iterator的实现。
java.util.Map的实现,包括HashMap,Hashtable和TreeMap。
如果您叠代的集合中含有null的值,这时需要采取一定的措施,因为这时logic:iterate不会在page作用域中创建对象。一般是使用标签或标签来判断一下。


下面是logic:iterate叠代ArrayList的示例的对象引用关系和部分代码:




图示 3. logic:iterate中对象的引用关系






图中的persons列表是在ListAction中填充的,在这里只是简单的加入了三个Person对象,在实际的应用中这些数据应该取自数据库。具体的代码如下:


public ActionForward execute(ActionMapping mapping,ActionForm. form,
HttpServletRequest request, HttpServletResponse response) {


ListForm. listForm. = (ListForm) form;

List persons = new ArrayList();

Person person1 = new Person();
person1.setId("00001");
person1.setName("赵辰");

Person person2 = new Person();
person2.setId("00002");
person2.setName("李为芳");

Person person3 = new Person();
person3.setId("00003");
person3.setName("王微");

persons.add(person1);
persons.add(person2);
persons.add(person3);

listForm.setPersons(persons);

return mapping.findForward("success");
}


标签输出的结果为:


00001-->赵辰
00002-->李为芳
00003-->王微


回页首
logic:present
如果指定的值出现该标签就会创建其标签体内的内容。该标签用于以下情况:


检查具有指定名称的cookie是否出现。
检查具有指定名称的header是否出现。
检查具有指定名称的JSP Bean是否出现 或 检查具有指定名称的JSP Bean中的property属性是否出现。
检查request中指定名称的参数是否出现。
检查当前的认证用户是否与指定的安全角色相关联。
检查当前认证的主体是否具有指定的名字。
下面的代码示例了logic:present标签检查具有指定名称User-Agent的header是否出现:



您的浏览器是





logic:notPresent标签的应用正好和logic:present标签相反。


回页首
logic:messagesPresent
logic:messagesPresent标签用于以下情况:


在request作用域中存在一个ActionMessages对象,标签的property属性和ActionMessages中的property对应。
在request作用域中存在一个ActionErrors对象,标签的property属性和ActionErrors中的property对应。
存在一个String对象,将其转换(构造)成ActionMessage然后在添加到ActionMessages中。
存在一个String Array对象,将数组中的每一个String转换成一个ActionMessage,在将其添加到ActionMessages中。
标签的message属性值为true时将以Globals.MESSAGE_KEY为key在request作用域中查找Message,其它情况下,将name的值作为key查找,如果name 没有出现,默认值为Globals.ERROR_KEY。


下面的代码示例了logic:messagesPresent标签的典型用法:











标签logic:messagesNotPresent的应用正好和logic:messagesPresent的应用相反。


回页首
logic:empty
logic:empty标签是用来判断是否为空的。如果为空,该标签体中嵌入的内容就会被处理。该标签用于以下情况:


当Java对象为null时
当String对象为""时
当java.util.Collection对象中的isEmpty()返回true时
当java.util.Map对象中的isEmpty()返回true时
下面的代码示例了logic:empty标签判断集合persons是否为空:



集合persons为空!




logic:notEmpty标签的应用正好和logic:empty标签相反。


回页首
logic:match
logic:match标签用来处理子串匹配问题。


如果指定的值匹配该标签就会创建其标签体内的内容。该标签用于以下情况:


检查具有指定名称的cookie是否与该标签的value匹配。
检查具有指定名称的header是否与该标签的value匹配。
检查具有指定名称的JSP Bean是否与该标签的value匹配 或 检查具有指定名称的JSP Bean中的property属性值是否与该标签的value匹配。
检查request中指定名称的参数值是否与该标签的value匹配。


下面的代码示例了logic:match标签的典型用法:




MS IE 6.0




logic:notMatch标签的应用正好和logic:match标签相反。


回页首
logic:equal
这里要介绍的不只是logic:equal(=)标签,而是要介绍一类标签,这类标签完成比较运算,包括:


logic:equal(=)
logic:notEqual(!=)
logic:greaterEqual(>=)
logic:lessEqual(<=)
logic:graterThan(>)
logic:lessThan(<)
该类标签的用法类似,我们只介绍logic:equal标签,其它的留给您自己去完成。


logic:equal是用来判断是否相等的。如果相等,该标签体中嵌入的内容就会被处理。该标签用于以下情况:


比较由该标签的cookie属性指定的cookie的值是否与该标签的value属性值相等。
比较由该标签的header属性指定的header的值是否与该标签的value属性值相等。
比较由该标签的name属性指定的JSP Bean是否与该标签的value属性值相等(property属性不出现)或比较由该标签的name属性指定的JSP Bean中的property属性值是否与该标签的value属性值相等。
比较由该标签的parameter属性指定的参数值(request中)是否与该标签的value属性值相等。
回页首
logic:forward
我觉得将forward和redirect这两个动作放到一起对比着介绍更加有利于理解,基于此原因也就将logic:forward和logic:redirect这两个标签也拿到这里一起介绍了。


让我们看看这两个动作的区别:


forward是在servlet内部执行,浏览器完全不会感知到这个动作,原始的url也不会改变,浏览器重新装载的话也只是对原始的请求进行简单的重复。
redirect则分成两个步骤:第一步是web应用程序告诉浏览器第二个url,然后浏览器向第二个url发送请求。


redirect比forward慢,因为浏览器要做二次请求。还有就是要注意,在第一次的请求作用域(request作用域)内的bean对于第二次请求是不可见的。


理解了上面描述的区别也就知道了什么时候该选用logic:forward标签什么时候该选用logic:redirect标签了。logic:forward标签完成PageContext.forward()或HttpServletResponse.sendRedirect(),如何选择由控制器决定。logic:redirect标签完成HttpServletResponse.sendRedirect()。


在使用logic:redirect标签时我们可以向使用html:link一样构造baseurl和query参数。如果您感兴趣可以参考html:link标签。






标签的用法
(2012-09-27 18:04:34)
转载▼
标签:
<saction>
struts2
杂谈
分类: struts2
使用action标签,可以允许在jsp页面中直接调用Action,在调用Action时候,可以指定需要被调用的Action的name和namespace.如果指定了executeResult参数的属性值为true,该标签会把Action的处理结果(视图资源)包含到本页面中. 使用action标签指定属性有:


id: 可选属性,作为该Action的引用ID

name:必选属性,指定调用Action

namespace:可选属性,指定该标签调用Action所属namespace

executeResult:可选属性,指定是否将Action的处理结果包含到本页面中.默认值为false,不包含.

ignoreContextParam:可选参数,指定该页面的请求参数是否需要传入调用的Action中,默认值是false,即传入参数.





传递参数







是以request方式的值的,而不是以参数传值


所以request.getParameter("")会获取不到值


通过request.getAttribute("")方式可以获取


另外:使用 value指代的是action定义的对象的名称,而不是一个值


中的name是一个意思,所以要要传递字符串的不能把值写value中而应该写到


写到这里
例如:
struts.xml如下配置




页面加载如下段
me="tab_gotoHeader" namespace="/reportList" executeResult="true">





xxx.action中只要set,get headerMap,scopeId就可以拿到值了
数据库
2019-06-04 20:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
(1): ---- 文本输入框?
使用:?
(2): ----- 文本域输入框?
使用:?
(3): ----- 密码输入框?
使用:?
前三个基本都是一样的。如果是要显示值的话可以这样:value = “实体Bean。Get***()”。?
(4): ---- 单选按钮?
使用:其中list 属性是必须要有的。?
<1>:第一种方式:list = “#{‘male’:’男’,’female’:’女’}”?
<2>:第二中方式:list = “#request/session.list”。 ---- 与action结合起来了。?
如果要默认选中的话后面加上value = “”.也可以通过javascript的方式来默认选中。?
(5): --- url连接?
--- 超链接?
这两个标签一般结合起来来使用。?
使用:?
(6): --- 获取相应form的值?
使用:?
<1>:?
<2>: -- 定义了一个userId的指向userActionde 路径?
?
(7): ---- 提交标签?
(8): ---- 重置标签?
(9): ---- 隐藏域标签?
使用:?
(10): ---- 下拉框配合输入框一起使用?
使用:?
<1>:list = “{18,20,30}”?
<2>:list = “#request/session.list”?
(11): ---- 多选框?
使用: 足球?
(12): ---- 复选框?
使用:?
<1>:list = “{‘hibernate’, ‘spring’, ‘strust2’}”?
<2>:list = “#request/session.list”.?
如果想默认选中的话,那么加上value=””?
(13):?
?
---- -这3个标签一起使用,表示条件判断?
使用:?
?
Will Not Be Executed
?
?
?
Will Be Executed
?
?
?
Will Not Be Executed
?
?
(14): --- 表示一个块,类似与html中的div?
(15): ---- 一般和一起使用。?
使用:?
?
?

?
?
?
?
?
?
Days表示:在action中存的值,status :表示起的别名。循环的一个个属性。?
(16): ---- 下拉框的使用?
使用:?
?
Value : 表示默认值。?
(17): ----- Bean标签,当然需要一个JavaBean。它的属性值的操作是经由Bean标签中的参数属性来进行赋值。当然,它还有一个id属性可以进行赋值,这样就可以在上下文中使用这个Bean.?
使用:?
jsf?
?
?
页面输出的结果:jsf。?
(18): ---- 方便在页面中进行格式化的输出。?
使用:?
?
页面中显示的结果:24/09/2008. 格式还有很多:MM/dd/yyyy、MM/dd/yyyy hh:mm:ss等。?
(19): ---- 包含标签,是把这个页面中的所有的内容都包含进来。?
使用:?
(20): --- param标签用于传递参数,如给标签传递参数,它有两个属性:?
<1>:name(String):参数名。?
<2>:value(Object):参数值。?
使用:?
?
?
?
?
">?
insert
?
(21): --- Set标签用户将某一值赋给某一变量,因此,任何对该项值的引用都可以通过该变量来得到该值.?
使用:?
?
教师职称:?
页面显示:教师职称:数学高级教师。?
(22): ---- 防止重复提交?
使用:在页面加载时,会产生一个GUID(Globally Unique Identifier,?
全局唯一标识符)值的隐藏输入框如:?
?
?
放在页面中随便的一个地方。
数据库
2019-06-04 20:31:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
echarts官网 提供了源码和说明文档,使用echarts需要先到官网下载需要的js源文件。
官网上的demo中夹杂着很多我们用不到的东西,想使用饼状图就得从demo中把不用的去掉,劈植斩叶留下最原始的功能实现。这样毕竟比较费时,我就在裁剪后的代码中加以总结于是乎新的使用教程如下所示:
echarts饼状图实现步骤:
1,在简单的html中引入js文件

[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body >
2,为图形准备容器


[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body > < div id = "picturePlace" >
就是在html中添加一个div给定id,图表就会显示在div中。


3,模块导入js

[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body > < div id = "picturePlace" > < script type = "text/javascript" > // 路径配置 require.config({ paths:{ 'echarts' : 'js/echarts', 'echarts/chart/pie' : 'js/echarts' } });


4,添加显示数据

[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body > < div id = "picturePlace" > < script type = "text/javascript" > // 路径配置 require.config({ paths:{ 'echarts' : 'js/echarts', 'echarts/chart/pie' : 'js/echarts' } }); // 使用 require( [ 'echarts', 'echarts/chart/pie' // 使用柱状图就加载bar模块,按需加载 ], function (ec) { // 基于准备好的dom,初始化echarts图表 var myChart = ec .init(document.getElementById(' < span style = "font-family:SimSun;" > picturePlace < span style = "font-family:FangSong_GB2312;" > ' )); option = { title : { text: '某站点用户访问来源', subtext: '纯属虚构', x:'center' }, tooltip : { trigger: 'item', formatter: "{a} < br /> {b} : {c} ({d}%)" }, legend: { orient : 'vertical', x : 'left', data:['直接访问','邮件营销','联盟广告','视频广告','搜索引擎'] }, toolbox: { show : true, feature : { mark : {show: true}, dataView : {show: true, readOnly: false}, restore : {show: true}, saveAsImage : {show: true} } }, calculable : true, series : [ { name:'访问来源', type:'pie', radius : '55%', center: ['50%', '60%'], data:[ {value:335, name:'直接访问'}, {value:310, name:'邮件营销'}, {value:234, name:'联盟广告'}, {value:135, name:'视频广告'}, {value:1548, name:'搜索引擎'} ] } ] }; // 为echarts对象加载数据 myChart.setOption(option); } );


5,运行程序显示结果

以上是饼状图的实现步骤,柱状图散点图跟这个类似就是引用js时饼状图是pie,柱状图是bar,对应的option里面的数据不同,程序的架子是一样的。。

出处:
数据库
2019-06-04 20:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
echarts官网 提供了源码和说明文档,使用echarts需要先到官网下载需要的js源文件。
官网上的demo中夹杂着很多我们用不到的东西,想使用饼状图就得从demo中把不用的去掉,劈植斩叶留下最原始的功能实现。这样毕竟比较费时,我就在裁剪后的代码中加以总结于是乎新的使用教程如下所示:
echarts饼状图实现步骤:
1,在简单的html中引入js文件

[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body >
2,为图形准备容器


[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body > < div id = "picturePlace" >
就是在html中添加一个div给定id,图表就会显示在div中。


3,模块导入js

[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body > < div id = "picturePlace" > < script type = "text/javascript" > // 路径配置 require.config({ paths:{ 'echarts' : 'js/echarts', 'echarts/chart/pie' : 'js/echarts' } });


4,添加显示数据

[html] view plain copy print ? < head > < meta charset = "utf-8" > < title > Charts demo < script src = "js/esl.js" > < body > < div id = "picturePlace" > < script type = "text/javascript" > // 路径配置 require.config({ paths:{ 'echarts' : 'js/echarts', 'echarts/chart/pie' : 'js/echarts' } }); // 使用 require( [ 'echarts', 'echarts/chart/pie' // 使用柱状图就加载bar模块,按需加载 ], function (ec) { // 基于准备好的dom,初始化echarts图表 var myChart = ec .init(document.getElementById(' < span style = "font-family:SimSun;" > picturePlace < span style = "font-family:FangSong_GB2312;" > ' )); option = { title : { text: '某站点用户访问来源', subtext: '纯属虚构', x:'center' }, tooltip : { trigger: 'item', formatter: "{a} < br /> {b} : {c} ({d}%)" }, legend: { orient : 'vertical', x : 'left', data:['直接访问','邮件营销','联盟广告','视频广告','搜索引擎'] }, toolbox: { show : true, feature : { mark : {show: true}, dataView : {show: true, readOnly: false}, restore : {show: true}, saveAsImage : {show: true} } }, calculable : true, series : [ { name:'访问来源', type:'pie', radius : '55%', center: ['50%', '60%'], data:[ {value:335, name:'直接访问'}, {value:310, name:'邮件营销'}, {value:234, name:'联盟广告'}, {value:135, name:'视频广告'}, {value:1548, name:'搜索引擎'} ] } ] }; // 为echarts对象加载数据 myChart.setOption(option); } );


5,运行程序显示结果

以上是饼状图的实现步骤,柱状图散点图跟这个类似就是引用js时饼状图是pie,柱状图是bar,对应的option里面的数据不同,程序的架子是一样的。。


数据库
2019-06-04 20:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
首先介绍Oracle 9i新增加的一个系统自带的排序函数
1、按首字母排序
在oracle9i中新增了按照拼音、部首、笔画排序功能。设置NLS_SORT值
SCHINESE_RADICAL_M 按照部首(第一顺序)、笔划(第二顺序)排序
SCHINESE_STROKE_M 按照笔划(第一顺序)、部首(第二顺序)排序
SCHINESE_PINYIN_M 按照拼音排序
oracle9i中新增了按照拼音、部首、笔画排序功能
用法示例:
拼音
SELECT * FROM TEAM ORDER BY NLSSORT(排序字段,'NLS_SORT = SCHINESE_PINYIN_M')
笔划
SELECT * FROM TEAM ORDER BY NLSSORT(排序字段,'NLS_SORT = SCHINESE_STROKE_M')
部首
SELECT * FROM TEAM ORDER BY NLSSORT(排序字段,'NLS_SORT = SCHINESE_RADICAL_M')

2、按首字母拼音查询
首先我们要创建一个可以查询汉字首字母的函数,函数代码如下:
CREATE OR REPLACE FUNCTION F_TRANS_PINYIN_CAPITAL(P_NAME IN VARCHAR2) RETURN VARCHAR2 AS
V_COMPARE VARCHAR2(100);
V_RETURN VARCHAR2(4000);

FUNCTION F_NLSSORT(P_WORD IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN NLSSORT(P_WORD, 'NLS_SORT=SCHINESE_PINYIN_M');
END;
BEGIN
FOR I IN 1..LENGTH(P_NAME) LOOP
V_COMPARE := F_NLSSORT(SUBSTR(P_NAME, I, 1));
IF V_COMPARE >= F_NLSSORT(' 吖 ') AND V_COMPARE <= F_NLSSORT('驁 ') THEN
V_RETURN := V_RETURN || 'a';
ELSIF V_COMPARE >= F_NLSSORT('八 ') AND V_COMPARE <= F_NLSSORT('簿 ') THEN
V_RETURN := V_RETURN || 'b';
ELSIF V_COMPARE >= F_NLSSORT('嚓 ') AND V_COMPARE <= F_NLSSORT('錯 ') THEN
V_RETURN := V_RETURN || 'c';
ELSIF V_COMPARE >= F_NLSSORT('咑 ') AND V_COMPARE <= F_NLSSORT('鵽 ') THEN
V_RETURN := V_RETURN || 'd';
ELSIF V_COMPARE >= F_NLSSORT('妸 ') AND V_COMPARE <= F_NLSSORT('樲 ') THEN
V_RETURN := V_RETURN || 'e';
ELSIF V_COMPARE >= F_NLSSORT('发 ') AND V_COMPARE <= F_NLSSORT('猤 ') THEN
V_RETURN := V_RETURN || 'f';
ELSIF V_COMPARE >= F_NLSSORT('旮 ') AND V_COMPARE <= F_NLSSORT('腂 ') THEN
V_RETURN := V_RETURN || 'g';
ELSIF V_COMPARE >= F_NLSSORT('妎 ') AND V_COMPARE <= F_NLSSORT('夻 ') THEN
V_RETURN := V_RETURN || 'h';
ELSIF V_COMPARE >= F_NLSSORT('丌 ') AND V_COMPARE <= F_NLSSORT('攈 ') THEN
V_RETURN := V_RETURN || 'j';
ELSIF V_COMPARE >= F_NLSSORT('咔 ') AND V_COMPARE <= F_NLSSORT('穒 ') THEN
V_RETURN := V_RETURN || 'k';
ELSIF V_COMPARE >= F_NLSSORT('垃 ') AND V_COMPARE <= F_NLSSORT('擽 ') THEN
V_RETURN := V_RETURN || 'l';
ELSIF V_COMPARE >= F_NLSSORT('嘸 ') AND V_COMPARE <= F_NLSSORT('椧 ') THEN
V_RETURN := V_RETURN || 'm';
ELSIF V_COMPARE >= F_NLSSORT('拏 ') AND V_COMPARE <= F_NLSSORT('瘧 ') THEN
V_RETURN := V_RETURN || 'n';
ELSIF V_COMPARE >= F_NLSSORT('筽 ') AND V_COMPARE <= F_NLSSORT('漚 ') THEN
V_RETURN := V_RETURN || 'o';
ELSIF V_COMPARE >= F_NLSSORT('妑 ') AND V_COMPARE <= F_NLSSORT('曝 ') THEN
V_RETURN := V_RETURN || 'p';
ELSIF V_COMPARE >= F_NLSSORT('七 ') AND V_COMPARE <= F_NLSSORT('裠 ') THEN
V_RETURN := V_RETURN || 'q';
ELSIF V_COMPARE >= F_NLSSORT('亽 ') AND V_COMPARE <= F_NLSSORT('鶸 ') THEN
V_RETURN := V_RETURN || 'r';
ELSIF V_COMPARE >= F_NLSSORT('仨 ') AND V_COMPARE <= F_NLSSORT('蜶 ') THEN
V_RETURN := V_RETURN || 's';
ELSIF V_COMPARE >= F_NLSSORT('侤 ') AND V_COMPARE <= F_NLSSORT('籜 ') THEN
V_RETURN := V_RETURN || 't';
ELSIF V_COMPARE >= F_NLSSORT('屲 ') AND V_COMPARE <= F_NLSSORT('鶩 ') THEN
V_RETURN := V_RETURN || 'w';
ELSIF V_COMPARE >= F_NLSSORT('夕 ') AND V_COMPARE <= F_NLSSORT('鑂 ') THEN
V_RETURN := V_RETURN || 'x';
ELSIF V_COMPARE >= F_NLSSORT('丫 ') AND V_COMPARE <= F_NLSSORT('韻 ') THEN
V_RETURN := V_RETURN || 'y';
ELSIF V_COMPARE >= F_NLSSORT('帀 ') AND V_COMPARE <= F_NLSSORT('咗 ') THEN
V_RETURN := V_RETURN || 'z';
END IF;
END LOOP;
RETURN V_RETURN;
END;


随后,我们可以先取出姓名,然后截取第一个汉字,最后取汉字的首字母,即可用来作为查询条件
select e.fullname,e.expert_id from expert e where f_trans_pinyin_capital(substr(e.fullname,1,1)) = 'z'



最后展示一个将两个结合起来运用的例子:
select e.expert_id,e.eperson_id,e.fullname,
sum(b2i.browse_num) browsecount,
count(o.object_id),wmsys.wm_concat(distinct d.name)
from expert e
left join expert2disciplinetype e2d on e2d.expert_id = e.expert_id
left join disciplinetype d on d.discipline_type_id = e2d.discipline_type_id
and d.upid = '0'
left join community2expert c2e on c2e.expert_id = e.expert_id
left join item i on i.item_type_id = 'AcademicRes' and i.withdrawn = 'N'
join metadatavalue m on m.item_id = i.item_id and m.metadata_field_id = '64'
join objectpublishinfo o on o.object_id = i.item_id
and o.object_type = 'item' and o.viewobjecttype = 'eperson'
and o.viewobjectid = e.eperson_id and o.state = '1'
and o.publishstate_id = '3'
left join browse2item b2i on b2i.item_id = o.object_id
where e.state = '1' and f_trans_pinyin_capital(substr(e.fullname,1,1)) = 'z'
group by e.expert_id,e.eperson_id,e.fullname
order by nlssort(e.fullname,'NLS_SORT=SCHINESE_PINYIN_M')

转自:http://zheng12tian.iteye.com/blog/817571
数据库
2019-06-04 20:26:00
「深度学习福利」大神带你进阶工程师,立即查看>>>

MySQL数据库的优化技术:
对数据库的优化是一个综合性的技术,主要包括:
1.表的设计是否合理(符合三范式,3NF)
2.添加适当索引(常见索引有四种:普通索引,主键索引,唯一索引,全文索引,(空间索引,复合索引)
3.分表技术(水平分割,垂直分割)
4.读写分离
5.存储过程(SQL语句每次执行都需要编译,存储过程只编译一次,模块化编程)
6.对MySQL的配置优化(配置最大并发数 my.ini,调整缓存大小)
7.MySQL服务器的硬件升级
8.定时清除不需要的数据,定时进行碎片整理
9.SQL语句优化
l 需求:为什么要调优
l 分析:怎么找到软肋
l 实战:如何调优
l 总结

为什么要调优
§ 老板要求 § 客户投诉 § 机器发飙 § 自己主动

机器发飙 网站或客户端打开非常慢,而 webserver 负载低,或打开静态页面很快,那就很可能是数据库的问题 load average >= 5 Iowait >= 10 vmstat procs(r b) 值较高 top 中 CPU 的 idle 很小, sys 或 wait 较高 服务器的 swap 严重 mysql 的内存命中率很低,例如 myisam_key_read_hit_ratio 或 innodb_buffer_hit_ratio 较低

瓶颈定位 vmstat,iostat,top 等系统级别的工具 explain slow query show status/show processlist /show engine innodb status 其他,如 mysqlreport , profiling

瓶颈定位 – vmstat / iostat


瓶颈定位 - Explain
Explain 都能提供什么信息呢? 表的读取顺序 每个表都是如何读取的 可能用到哪些索引,实际使用了哪些索引 表是如何引用的 查询优化器从每个表中预计读取的记录数 其他额外信息,例如是否使用了内存表,是否引发排序等
瓶颈定位 – 续 将 LONG_QUERY_TIME 设为最小值;建议打补丁,这样单位可以设成微秒,并可查看详细执行计划 执行 SHOW [GLOBAL] STATUS/PROCESSLIST 查看当前运行状态,从结果中发现可能的问题 执行 SHOW ENGINE INNODB STATUS 查看 INNODB 的状态 另外 , 要定期检查多余的索引以及没有使用索引的慢查询 利用 mysqlreport 产生可读性更强的报告 利用 Profiling 剖析一次查询瓶颈所在 其他工具,包括监控工具, linux 自带工具等

MySQL 调优的几种途径 硬件、网络、软件 MySQL 参数设置 应用程序、架构优化 查询优化、索引

硬件、网络、软件 通常硬件是优化的最佳入口,主要是 CPU 、内存、磁盘、网络 客户端和服务器在一个高速的局域网内 通常,新版本的效率不如旧版本,但是可以利用新版本的新功能来从另一方面得到性能上的提升 编译优化,采用静态编译等 使用更稳定高效的内核 使用合适的文件系统,推荐使用 xfs ( 高级文件系统实现者指南 )

MySQL 参数设置
参数名 说明
Key Buffer MyISAM 索引缓冲
Query Cache 查询结果缓存
Sort Buffer 排序缓冲
Read Buffer 全表扫描缓冲
Join Buffer 连接查询缓冲
Slow Query 设置慢查询 , 打上 msl 补丁
Tmp Table
Innodb Buffer
内存 表 ,还需要注意 max_heap_table_size
InnoDB 最重要的设置 , 包括日志缓冲


应用程序、架构优化 垂直 / 水平切分服务器 / 数据库、表 开启 MySQL 复制,实现读、写分离 在复制的基础上,增加负载均衡 采用集群 + 复制 (MySQL 6.0+) 频繁更新的表,可以分离成父表和子表 ( 内存表 ) 用统计表保存定时统计结果,而不是在大表上直接统计 编写存储过程 / 函数来代替大量的外部应用程序交互

查询优化、索引 确保索引合理利用,尽量使用联合索引 适当加大查询缓存 (query cache) 尽量减少交互次数 尽量使用固定格式的 SQL 语句,查询语句中少用运算或函数 缩短每个事务 使用适当的字段类型;适当的长度,有需要的时候再扩充 分解复杂查询为多个小查询 字符型字段采用前缀索引 其他 ……

调优方法
一、选择合适的引擎
MyISAM
这个是默认类型 , 基于传统的 ISAM 类型,它是存储记录和文件的标准方法。与其他存储引擎比较, MyISAM 具有检查和修复表格的大多数工具。 MyISAM 表格可以被压缩 , 而且它们支持全文搜索。它们不是事务安全的,而且也不支持外键。
InnoDB
ACID 、外键、日志修复。 InnoDB 表格速度很快。如果需要一个事务安全的存储引擎或者是需要大量并发的 INSERT 或 UPDATE ,应该使用 InnoDB 表。
NDB
支持事务,用于 cluster ,实现高可用,但性能仍欠佳。

调优方法 – 续 ( 其他 ) 不直接执行 COUNT(*) – innodb 多个操作放在一起提交,但要注意事务不能太大 日志文件并非越大越好,需要考虑恢复和检查点 左连接时把数据量小的表放在前面 innodb_flush_log_at_trx_commit 可以尝试设置为 2 ,甚至是 0 导入数据时关闭 AUTOCOMMIT 以及 UNIQUE_CHECKS 、 FOREIGN_KEY_CHECKS 复杂的查询总是先用 EXPLAIN 来分析一下 定期执行 OPTIMIZE TABLE 整理碎片 用 char 来代替 varchar , MyISAM 是这样, InnoDB 则相反


数据库
2019-06-04 20:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
触发器 TRIGGER
1、触发器的定义
2、使用的场景
3、掌握触发器的创建语法
4、理解触发器的触发机制
什么叫做触发器?
当一个表中的数据发生改变的时候,会引起其他表中相关数据改变,
编制一个小程序附着在表上,把这种改变自动化执行,成为触发器。
触发器的类型?
在进行insert、update、delete操作时,触发相关的insert、update、delete
触发器触发。分为:insert、update、delete触发器。
USE wlw;
CREATE TABLE goods
(
gid INT NOT NULL PRIMARY KEY COMMENT '商品编号',
gname VARCHAR(20) COMMENT '商品名称',
kcnum DECIMAL(4,2) COMMENT '库存数量'
);
INSERT INTO goods VALUES(112,'肉猪',25);
INSERT INTO goods VALUES(113,'臭狗',56);
INSERT INTO goods VALUES(114,'瞎马',78);
SELECT * FROM goods;
CREATE TABLE dindans
(
ddh INT NOT NULL PRIMARY KEY COMMENT '订单号',
gid INT COMMENT '商品编号',
gname VARCHAR(20) COMMENT '商品名称',
ddnum DECIMAL(4,2) COMMENT '订单数量'
);
SELECT * FROM dindans;
=====================================
#insert触发器
DELIMITER $$
CREATE TRIGGER tri_insert
AFTER INSERT #在插入之后
ON dindans #在某个表上创建触发器
FOR EACH ROW #insert的每一行
BEGIN
UPDATE goods
SET kcnum = kcnum - new.ddnum #new代表着新插入的这一行,new是一个行对象,相当于dindans表的一行,new.id就可以取得新插入行的id
WHERE gid=new.gid;
END$$
DELIMITER ;
DROP TRIGGER tri_insert;
SELECT * FROM goods;
SELECT * FROM dindans;
#插入数据后,就会触发触发器,在goods表中减少相应的数量
INSERT INTO dindans VALUES(1,114,'瞎马',5);
SELECT * FROM goods;
SELECT * FROM dindans;
INSERT INTO dindans VALUES(2,113,'臭狗',25);
============================
#delete触发器
DELIMITER $$
CREATE TRIGGER tri_del
AFTER DELETE
ON dindans
FOR EACH ROW
BEGIN
UPDATE goods SET kcnum=kcnum + old.ddnum #old代表着刚刚删除的这一行,old是一个行对象,old.id可以取得新删除这一行的id
WHERE gid=old.gid;
END$$
DELIMITER ;
SELECT * FROM dindans;
SELECT * FROM goods;
#用delete语句触发delete触发器,在删除之后,增加goods表的数量
DELETE FROM dindans WHERE gid=113;
========================
SELECT * FROM goods;
SELECT * FROM dindans;
INSERT INTO dindans VALUES(2,112,'肉猪',20);
DROP TRIGGER tri_insert;
#当订单的数量大于库存量的时候,我们在触发器中要有解决问题的
#办法,处理此类情况
DELIMITER $$
CREATE TRIGGER tri_insert
AFTER INSERT
ON dindans
FOR EACH ROW
BEGIN
DECLARE sl INT;
SELECT kcnum INTO sl FROM goods WHERE gid=new.gid;
IF sl<=new.ddnum THEN
UPDATE goods SET kcnum=0 WHERE gid=new.gid;
ELSE
UPDATE goods SET kcnum=kcnum-new.ddnum WHERE gid=new.gid;
END IF;
END$$
DELIMITER ;
SELECT * FROM goods;
SELECT * FROM dindans;
DROP TRIGGER tri_insert;
INSERT INTO dindans VALUES(3,112,'肉猪',5);
================================
#当更改订单表中的数据的时候,会触发订单表的数据,从而导致
#goods表的数据发生改变
SELECT * FROM goods;
SELECT * FROM dindans;
DELIMITER $$
CREATE TRIGGER tri_update
AFTER UPDATE
ON dindans
FOR EACH ROW
BEGIN
IF old.ddnum >= new.ddnum THEN
UPDATE goods SET kcnum=kcnum+old.ddnum - new.ddnum WHERE gid=new.gid;
ELSE
UPDATE goods SET kcnum =0;
END IF;
END$$
DELIMITER ;
SELECT * FROM goods;
SELECT * FROM dindans;
UPDATE dindans SET ddnum=8 WHERE ddh=2;
====================================
做一个日志应用,使用触发器
SELECT * FROM goods;
DROP TABLE klog;
CREATE TABLE klog
(
id INT PRIMARY KEY AUTO_INCREMENT,
kusr VARCHAR(50),
kcz VARCHAR(20),
kgoodname VARCHAR(60),
ktime DATETIME
);
#当对goods插入数据时,即可在klog中生成一条记录
DELIMITER $$
CREATE TRIGGER g_insert
AFTER INSERT
ON goods
FOR EACH ROW
BEGIN
INSERT INTO klog(kusr,kcz,kgoodname,ktime) VALUES(USER(),'正在insert...',new.gname,NOW());
END$$
DELIMITER ;
#当对goods删除数据时,即可在klog中生成一条记录
DELIMITER $$
CREATE TRIGGER g_delete
AFTER DELETE
ON goods
FOR EACH ROW
BEGIN
INSERT INTO klog(kusr,kcz,kgoodname,ktime) VALUES(USER(),'正在delete...',old.gname,NOW());
END$$
DELIMITER ;
#当对goods更新数据时,即可在klog中生成一条记录
DELIMITER $$
CREATE TRIGGER g_udpate
AFTER UPDATE
ON goods
FOR EACH ROW
BEGIN
INSERT INTO klog(kusr,kcz,kgoodname,ktime) VALUES(USER(),'正在update...',old.gname,NOW());
END$$
DELIMITER ;
SELECT * FROM goods;
INSERT INTO goods VALUES(115,'骆驼',13);
SELECT * FROM goods;
SELECT * FROM klog;
INSERT INTO goods VALUES(116,'孔雀',63);
INSERT INTO goods VALUES(117,'羊驼',234);
DELETE FROM goods WHERE gid=114;
UPDATE goods
SET kcnum=400
WHERE gid=117;
SELECT * FROM goods;
==================================
用户自定义函数
1、系统函数
SELECT NOW();
SELECT USER();
2、数据库函数
SELECT COUNT(*) FROM klog;
3、用户自定义函数
(1)定义
CREATE FUNCTION 函数名()
RETURNS 返回值类型
BEGIN
RETURN 类型;
END;
定义函数
DELIMITER $$
CREATE FUNCTION hello()
RETURNS VARCHAR(20)
BEGIN
RETURN 'hello 你好!';
END$$
DELIMITER ;
调用函数
SELECT kusr,kcz,hello() FROM klog;
============================
DELIMITER $$
CREATE FUNCTION hellohaha(xm VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
RETURN CONCAT('你好!',xm);
END$$
DELIMITER ;
SELECT kusr,kcz,hellohaha(kusr) FROM klog;
SELECT xy,hellohaha(xm) FROM test.xuesheng;
SELECT * FROM emp;
SELECT empid,hellohaha(empname),income FROM emp;
==============================
DELIMITER $$
CREATE FUNCTION find_xh(arga VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
DECLARE xxh VARCHAR(20);
SELECT xh INTO xxh FROM test.xuesheng WHERE xm=arga;
IF ISNULL(xxh) THEN
RETURN '没找到';
ELSE
RETURN xxh;
END IF;

END$$
DELIMITER ;
DROP FUNCTION find_xh;
SELECT find_xh('王余昌');
SELECT find_xh('宋有国');
数据库
2019-06-04 17:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Cursor的应用场景:
查询结果集 list ,使用 cursor 的情况,肯定会有下边这两种情况,才可能使用到 cursor : 需要依据查询到的结果集,作为条件,进行下一步的查询。 需要在结果集某个位置上,满足某种条件时,对数据进行不同的修改。
上边的两种情况,都会涉及到遍历数据,所以游标的使用也需要注意:
比如,你的查询结果集很大,首先占了很多的内存,其次,遍历这么庞大的结果集,效率也肯定高不了。
另外,如果还有其他方式解决问题的话,那就不要选择使用游标,但是游标在使用中,的确有,定义游标、打开游标、关闭游标,这些操作,这样也肯定快不了多少。

Cursor的类别:
1.隐式游标。
DML操作和单行SELECT语句会使用隐式游标,如:INSERST,UPDATE,DELETE , SELECT ... INTO ..
另一种方式 : create or replace procedure test_class_procedure as begin for class_dec in ( select * from t_class ) loop dbms_output.put_line('姓名:'||class_dec.name||' 年龄:'||class_dec.age); end loop; end;
执行: set serveroutput on; begin test_class_procedure; end;
隐式游标其实默认已经打开游标,并在执行完成后关闭游标释放资源。
2.显示游标。
显示游标分为静态游标与动态游标,显示游标的写法会在存储过程中定义 Cursor ,并且一般都有固定的四个步骤: 声明游标 、 打开游标 、 提取数据 、 关闭游标 。显式游标打开后,必须显式地关闭。游标一旦关闭,游标占用的资源就被释放,游标变成无效,必须重新打开才能使用。
示例: CREATE OR REPLACE NONEDITIONABLE PROCEDURE PRC_MDS1_TO_MDS2 (parameter IN VARCHAR2, exitcode OUT INT) AS -- 定义一个变量用来保存此存储过程的名字 v_logic_proc_name VARCHAR2(30) := 'PRC_MDS1_TO_MDS2'; -- 声明显式游标, 查询udt_mds1_input中item不为null的数据 CURSOR c_udt_mds1_input_item_notnull IS SELECT * FROM scpomgr.udt_mds1_input WHERE item IS NOT NULL AND TRIM(item) IS NOT NULL; -- 定义游标变量,该变量的类型为基于游标c_udt_mds1_input_item_notnull的记录 c_mds1_item_notnull_row c_udt_mds1_input_item_notnull%ROWTYPE; -- 声明一个游标,查询udt_mds1_input中item为null的数据 -- MARKET、itemgroup、package、config不为NULL CURSOR c_udt_mds1_input_item_null IS SELECT * FROM scpomgr.udt_mds1_input WHERE (item IS NULL OR TRIM(item) IS NULL) AND market IS NOT NULL AND TRIM(market) IS NOT NULL AND itemgroup IS NOT NULL AND TRIM(itemgroup) IS NOT NULL AND package IS NOT NULL AND TRIM(package) IS NOT NULL AND config IS NOT NULL AND TRIM(config) IS NOT NULL; -- 定义游标变量 c_mds1_item_null_row c_udt_mds1_input_item_null%ROWTYPE; -- 再声明一个游标,查询udt_mds1_input中item为null -- MARKET、ITEMGROUP、PACKAGE、CONFIG有为NULL的 CURSOR c_udt_mds1_item_null_other IS SELECT * FROM scpomgr.udt_mds1_input WHERE (item IS NULL OR TRIM(item) IS NULL) AND (market IS NULL OR TRIM(market) IS NULL OR itemgroup IS NULL OR TRIM(itemgroup) IS NULL OR package IS NULL OR TRIM(package) IS NULL OR config IS NULL OR TRIM(config) IS NULL); -- 定义游标变量 c_mds1_item_null_other_row c_udt_mds1_item_null_other%ROWTYPE; BEGIN -- 设置要返回的变量值 exitcode := -20999; -- 如果传入的参数是BYPASS, 则表示不运行 IF parameter = 'BYPASS' THEN exitcode := 0; -- 返回0表示执行成功 RETURN; END IF; -- 记录具体逻辑开始执行 logger.info (v_logic_proc_name || ':' || parameter || ' ,Start'); ------------------- 具体的业务逻辑 ------------------- -- 1. 将UDT_MDS1_INPUT所有字段输入到MDS1_BCKUP对应同名字段中 pkg_log.info(v_logic_proc_name || ':' || '步骤1,将UDT_MDS1_INPUT的数据备份到MDS1_BCKUP表中'); INSERT INTO scpomgr.MDS1_BCKUP SELECT * FROM scpomgr.UDT_MDS1_INPUT; -- 2. 是否要先清空中间表和UDT_MDS2_REVIEW表中的数据 pkg_log.info(v_logic_proc_name || ':' || '步骤2,删除mid_mds1_mds2和udt_mds2_review表中的数据'); DELETE FROM scpomgr.mid_mds1_mds2; -- 删除中间表数据 DELETE FROM scpomgr.udt_mds2_review; -- 删除UDT_MDS2_REVIEW表数据 -- 3. 循环处理ITEM不为NULL的数据 pkg_log.info(v_logic_proc_name || ':' || '步骤3,循环处理udt_mds1_input中item不为null的数据'); FOR c_mds1_item_notnull_row IN c_udt_mds1_input_item_notnull LOOP -- 使用嵌套内部块 declare -- 定义变量 v_country scpomgr.udt_country_ratio.country%TYPE; -- 定义一个国家变量 v_color scpomgr.udt_basic_info.color%TYPE; -- 定义一个颜色变量 begin -- 3.1 查询udt_country_ratio.country信息,并赋值给v_country BEGIN SELECT country INTO v_country FROM ( SELECT ucr.country FROM scpomgr.udt_country_ratio ucr WHERE ucr.market=c_mds1_item_notnull_row.market AND ucr.itemgroup=c_mds1_item_notnull_row.itemgroup ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_notnull_row.market || '和' || c_mds1_item_notnull_row.itemgroup || '没有在scpomgr.udt_country_ratio查询到对应的国家信息'); prc_udt_error_log(c_mds1_item_notnull_row.itemgroup, c_mds1_item_notnull_row.market, '', '没有在scpomgr.udt_country_ratio查询到对应的国家信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 3.2 查询udt_basic_info.color信息,并赋值给v_color BEGIN SELECT color INTO v_color FROM ( SELECT ubi.color FROM scpomgr.udt_basic_info ubi WHERE ubi.item = c_mds1_item_notnull_row.item ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_notnull_row.item || '没有在scpomgr.udt_basic_info查询到对应的颜色信息'); prc_udt_error_log(c_mds1_item_notnull_row.item, '', '', '没有在scpomgr.udt_basic_info查询到对应的颜色信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 3.3 将数据保存到中间表中 INSERT INTO scpomgr.mid_mds1_mds2(market,brand,itemgroup,item,descr,status,loc,package,config,dmdtype,version_no,country,color, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27) VALUES (c_mds1_item_notnull_row.market, c_mds1_item_notnull_row.brand, c_mds1_item_notnull_row.itemgroup, c_mds1_item_notnull_row.item, c_mds1_item_notnull_row.descr, c_mds1_item_notnull_row.status, c_mds1_item_notnull_row.loc, c_mds1_item_notnull_row.package, c_mds1_item_notnull_row.config, c_mds1_item_notnull_row.dmdtype, c_mds1_item_notnull_row.version_no, v_country, v_color, c_mds1_item_notnull_row.w0, c_mds1_item_notnull_row.w1, c_mds1_item_notnull_row.w2, c_mds1_item_notnull_row.w3, c_mds1_item_notnull_row.w4, c_mds1_item_notnull_row.w5, c_mds1_item_notnull_row.w6, c_mds1_item_notnull_row.w7, c_mds1_item_notnull_row.w8, c_mds1_item_notnull_row.w9, c_mds1_item_notnull_row.w10, c_mds1_item_notnull_row.w11, c_mds1_item_notnull_row.w12, c_mds1_item_notnull_row.w13, c_mds1_item_notnull_row.w14, c_mds1_item_notnull_row.w15, c_mds1_item_notnull_row.w16, c_mds1_item_notnull_row.w17, c_mds1_item_notnull_row.w18, c_mds1_item_notnull_row.w19, c_mds1_item_notnull_row.w20, c_mds1_item_notnull_row.w21, c_mds1_item_notnull_row.w22, c_mds1_item_notnull_row.w23, c_mds1_item_notnull_row.w24, c_mds1_item_notnull_row.w25, c_mds1_item_notnull_row.w26, c_mds1_item_notnull_row.w27); end; END LOOP; -- 4. 循环处理ITEM为NULL的数据 pkg_log.info(v_logic_proc_name || ':' || '步骤4,循环处理udt_mds1_input中item为null,但MARKET/itemgroup/package/config不为NULL的数据'); FOR c_mds1_item_null_row IN c_udt_mds1_input_item_null LOOP -- 使用嵌套内部块 declare -- 定义变量 v_item scpomgr.udt_basic_info.item%TYPE; -- 定义item v_descr scpomgr.udt_basic_info.descr%TYPE; -- 定义descr -- 定义国家和国家占比 v_country scpomgr.udt_country_ratio.country%TYPE; -- 国家 v_country_ratio scpomgr.udt_country_ratio.country_ratio%TYPE; -- 国家占比 -- 使用数组定义15种颜色及占比 type v_color_varray is varray(15) of scpomgr.udt_country_ratio.color1%TYPE; -- 颜色 v_color v_color_varray := v_color_varray('', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); type v_color_ratio_varray is varray(15) of scpomgr.udt_country_ratio.color1_ratio%TYPE; -- 颜色占比 v_color_ratio v_color_ratio_varray := v_color_ratio_varray(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); -- 使用数组定义28周的数据量变量 type v_w_varray is varray(28) of scpomgr.udt_mds1_input.w0%TYPE; v_w v_w_varray := v_w_varray(c_mds1_item_null_row.w0,c_mds1_item_null_row.w1,c_mds1_item_null_row.w2,c_mds1_item_null_row.w3,c_mds1_item_null_row.w4,c_mds1_item_null_row.w5, c_mds1_item_null_row.w6,c_mds1_item_null_row.w7,c_mds1_item_null_row.w8,c_mds1_item_null_row.w9,c_mds1_item_null_row.w10,c_mds1_item_null_row.w11, c_mds1_item_null_row.w12,c_mds1_item_null_row.w13,c_mds1_item_null_row.w14,c_mds1_item_null_row.w15,c_mds1_item_null_row.w16,c_mds1_item_null_row.w17, c_mds1_item_null_row.w18,c_mds1_item_null_row.w19,c_mds1_item_null_row.w20,c_mds1_item_null_row.w21,c_mds1_item_null_row.w22,c_mds1_item_null_row.w23, c_mds1_item_null_row.w24,c_mds1_item_null_row.w25,c_mds1_item_null_row.w26,c_mds1_item_null_row.w27); begin -- 4.1 查询udt_country_ratio中抓取对应的country、 country_ratio、color1..15、color1..15_ratio,并赋值给相应的变量 BEGIN SELECT country,country_ratio,color1,color1_ratio,color2,color2_ratio,color3,color3_ratio,color4,color4_ratio,color5,color5_ratio,color6,color6_ratio, color7,color7_ratio,color8,color8_ratio,color9,color9_ratio,color10,color10_ratio,color11,color11_ratio,color12,color12_ratio,color13,color13_ratio, color14,color14_ratio,color15,color15_ratio INTO v_country,v_country_ratio,v_color(1),v_color_ratio(1),v_color(2),v_color_ratio(2),v_color(3),v_color_ratio(3),v_color(4),v_color_ratio(4), v_color(5),v_color_ratio(5),v_color(6),v_color_ratio(6),v_color(7),v_color_ratio(7),v_color(8),v_color_ratio(8),v_color(9),v_color_ratio(9), v_color(10),v_color_ratio(10),v_color(11),v_color_ratio(11),v_color(12),v_color_ratio(12),v_color(13),v_color_ratio(13),v_color(14),v_color_ratio(14), v_color(15),v_color_ratio(15) FROM ( SELECT ucr.country,ucr.country_ratio,ucr.color1,ucr.color1_ratio,ucr.color2,ucr.color2_ratio,ucr.color3,ucr.color3_ratio,ucr.color4,ucr.color4_ratio, ucr.color5,ucr.color5_ratio,ucr.color6,ucr.color6_ratio,ucr.color7,ucr.color7_ratio,ucr.color8,ucr.color8_ratio,ucr.color9,ucr.color9_ratio, ucr.color10,ucr.color10_ratio,ucr.color11,ucr.color11_ratio,ucr.color12,ucr.color12_ratio,ucr.color13,ucr.color13_ratio,ucr.color14,ucr.color14_ratio, ucr.color15,ucr.color15_ratio FROM scpomgr.udt_country_ratio ucr WHERE ucr.market = c_mds1_item_null_row.market AND ucr.itemgroup = c_mds1_item_null_row.itemgroup ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_null_row.market || '和' || c_mds1_item_null_row.itemgroup || '没有在scpomgr.udt_country_ratio查询到对应国家和颜色及占比信息'); prc_udt_error_log(c_mds1_item_null_row.itemgroup, c_mds1_item_null_row.market, '', '没有在scpomgr.udt_country_ratio查询到对应国家和颜色及占比信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 4.2对于country_ratio、color_ratio不为NULL的记录 更新W0-W27的数据 FOR i in 1 .. 15 LOOP -- 先判断有没有颜色,如果没有则不处理 IF(v_color(i) IS NOT NULL AND TRIM(v_color(i)) IS NOT NULL) THEN -- 4.2.1 获取item,descr v_item := ''; v_descr := ''; -- 先设置为默认值 BEGIN SELECT item,descr INTO v_item,v_descr FROM ( SELECT ubi.item,ubi.descr FROM scpomgr.udt_basic_info ubi WHERE ubi.country = v_country AND ubi.itemgroup = c_mds1_item_null_row.itemgroup AND ubi.package = c_mds1_item_null_row.package AND ubi.color = v_color(i) AND ubi.config = c_mds1_item_null_row.config ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_null_row.itemgroup || ',' || c_mds1_item_null_row.package || ',' || v_color(i) || ',' || c_mds1_item_null_row.config || '没有在scpomgr.udt_basic_info查询到对应的item和descr信息'); prc_udt_error_log(c_mds1_item_null_row.itemgroup, c_mds1_item_null_row.package|| '+' || c_mds1_item_null_row.config, '', '没有在scpomgr.udt_basic_info查询到对应的item和descr信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 4.2.2 如果不为空,则要计算v_w的数据 IF(v_country_ratio IS NOT NULL AND v_color_ratio(i) IS NOT NULL) THEN --循环处理v_w0...v_w27的数据 FOR j in 1 .. 28 LOOP v_w(j) := v_w(j) * v_country_ratio * v_color_ratio(i); END LOOP; -- 将处理完的数据插入到中间表中 INSERT INTO scpomgr.mid_mds1_mds2(market,itemgroup,package,config,country,country_ratio,color,color_ratio,item, descr, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27) VALUES (c_mds1_item_null_row.market, c_mds1_item_null_row.itemgroup, c_mds1_item_null_row.package, c_mds1_item_null_row.config, v_country, v_country_ratio, v_color(i),v_color_ratio(i), v_item, v_descr, v_w(1), v_w(2), v_w(3), v_w(4), v_w(5), v_w(6), v_w(7), v_w(8), v_w(9), v_w(10), v_w(11), v_w(12), v_w(13), v_w(14), v_w(15), v_w(16),v_w(17), v_w(18), v_w(19), v_w(20), v_w(21), v_w(22), v_w(23), v_w(24), v_w(25), v_w(26), v_w(27), v_w(28)); END IF; END IF; END LOOP; end; END LOOP; -- 5. 对于若UDT_MDS1_INPUT.ITEM为NULL,MARKET、ITEMGROUP、PACKAGE、CONFIG有为NULL的 pkg_log.info(v_logic_proc_name || ':' || '步骤5,循环处理udt_mds1_input中item为null,并且MARKET/itemgroup/package/config有为NULL字段的数据'); FOR c_mds1_item_null_other_row IN c_udt_mds1_item_null_other LOOP -- 使用嵌套内部块 declare -- 定义一个变量保存数据 v_null_filed varchar2(500); begin if(c_mds1_item_null_other_row.market is null or trim(c_mds1_item_null_other_row.market) is null) then v_null_filed := v_null_filed || 'market,'; elsif(c_mds1_item_null_other_row.itemgroup is null or trim(c_mds1_item_null_other_row.itemgroup) is null) then v_null_filed := v_null_filed || 'itemgroup,'; elsif(c_mds1_item_null_other_row.package is null or trim(c_mds1_item_null_other_row.package) is null) then v_null_filed := v_null_filed || 'package,'; elsif(c_mds1_item_null_other_row.config is null or trim(c_mds1_item_null_other_row.config) is null) then v_null_filed := v_null_filed || 'config'; end if; -- 找出哪个字段为空,并记录下来 pkg_log.error(v_logic_proc_name || ':' || 'scpomgr.UDT_MDS1_INPUT表中下面字段为空: ' || v_null_filed); prc_udt_error_log('', '', '', 'scpomgr.UDT_MDS1_INPUT表中下面字段为空: ' || v_null_filed, 1, 'PRC_MDS1_TO_MDS2'); END; END LOOP; -- 6. 将中间表的数据保存到UDT_MDS2_REVIEW表中 pkg_log.info(v_logic_proc_name || ':' || '步骤6,将中间表mid_mds1_mds2的数据保存到udt_mds2_review表中'); INSERT INTO scpomgr.udt_mds2_review(market,brand,itemgroup,item,descr,status,loc,package,color,config,dmdtype,version_no, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27) SELECT market,brand,itemgroup,item,descr,status,loc,package,color,config,dmdtype,version_no, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27 FROM scpomgr.mid_mds1_mds2; -- 说明没有错误, 执行成功 exitcode := 0; -- 记录完成 logger.info (v_logic_proc_name || ':' || parameter || ' ,Success'); -- 成功的话, 提交 COMMIT; EXCEPTION WHEN OTHERS THEN exitcode := SQLCODE; -- 返回错误代码 pkg_log.error('Failure:' || v_logic_proc_name || ':' || SQLCODE || ':' || SQLERRM || ':' || substr(dbms_utility.format_error_backtrace, 1, 1024)); COMMIT; END PRC_MDS1_TO_MDS2;

数据库
2019-06-04 16:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在MySql中有两种方法可以清除表数据
DELETE DELETE FROM tablename
delete语句可以携带where条件进行删除,所以使用起来很灵活。但是delete语句在删除的过程中会产生事务日志,可以回滚数据,所以性能上面会有所降低!
值得注意的是,当表中有自增字段的时候,清除完数据之后,自增的值将会从1开始,如果不希望如此,可以在delete的时候加上一个永远为真的条件,比如where 1 = 1或者where true等。
TRUNCATE TRUNCATE TABLE tablename
truncate是清除表内所有的数据,灵活性不是很好。但是由于不创建事务日志,truncate删除的数据将无法恢复,性能上面将提升很多!
两种方式都可以选择,根据不同需求选择不同的方式!
数据库
2019-06-04 16:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
数据仓库的定义之一是反映历史变化,数据或多或少都会包含时间特征,因此日期维度就成了数据仓库中不可或缺的维度之一,可以说在任何一个事实表中都会有一个或者多个日期维度的外键。日期维度可以尽可能多的包含日期的详细信息,比如周几、农历、年、月、日、节假日、季度,甚至是生肖年、干支纪年、星座等信息。
生成日期维度表的方式有很多,可以通过数据库例如MySQL直接生成日期维度,也可以通过编程语言JAVA、PYTHON等来生成。本文采用的是通过PYTHON来实现日期维度表的生成,该方式的优点是生成的日期维度相对详实。先来看一下最终的结果
每一列的含义如下
日期 id 2018-01-01
year 2018
month 1
day 1
季度 quarter 1
星期几 day_name Monday
一年的第几周 weekofyear 1
一年的第几天 dayofyear 1
该月有几天 daysinmonth 31
这周第几天 dayofweek 1
是否闰年 is_leap_year FALSE
是否月末最后一天 is_month_end FALSE
是否月初第一天 is_month_start TRUE
是否季度末最后一天 is_quarter_end FALSE
是否季度初第一天 is_quarter_start TRUE
是否年末最后一天 is_year_end FALSE
是否年初第一天 is_year_start TRUE
农历 lunar_date 冬月十五
干支纪年 gz_year 丁酉年
生肖年 sx_year 鸡年
干支纪日 gz_day 癸巳日
节气 solar_terms
星座
节假日
zodiac
holiday
摩羯座
元旦
实现方式: from util.lunar import Lunar import pandas as pd def getLunar(ct=None): ln = Lunar(ct) return (ln.ln_date_str(), ln.gz_year(), ln.sx_year(), ln.gz_day(),ln.ln_jie()) def holiday(ln_date): n = ('春节','春节','春节','端午节','中秋节','元旦','劳动节','国庆节','国庆节','国庆节') d = ('腊月三十','正月初一','正月初二','五月初五','八月十五',(1,1),(5,1),(10,1),(10,2),(10,3)) dic = dict(zip(d,n)) if ln_date in d: return dic[ln_date] def zodiac(month, day): n = ('摩羯座','水瓶座','双鱼座','白羊座','金牛座','双子座','巨蟹座','狮子座','处女座','天秤座','天蝎座','射手座') d = ((1,20),(2,19),(3,21),(4,21),(5,21),(6,22),(7,23),(8,23),(9,23),(10,23),(11,23),(12,23)) return n[len(list(filter(lambda y:y<=(month,day), d)))%12] def generateData(startDate='2019-1-01', endDate='2019-1-31'): d = {'id':pd.date_range(start=startDate, end=endDate)} data = pd.DataFrame(d) data['year'] = data['id'].apply(lambda x:x.year) data['month'] = data['id'].apply(lambda x:x.month) data['day'] = data['id'].apply(lambda x:x.day) data['quarter'] = data['id'].apply(lambda x:x.quarter) data['day_name'] = data['id'].apply(lambda x:x.day_name()) data['weekofyear'] = data['id'].apply(lambda x:x.weekofyear) data['dayofyear'] = data['id'].apply(lambda x:x.dayofyear) data['daysinmonth'] = data['id'].apply(lambda x:x.daysinmonth) data['dayofweek'] = data['id'].apply(lambda x:x.dayofweek) data['is_leap_year'] = data['id'].apply(lambda x:x.is_leap_year) data['is_month_end'] = data['id'].apply(lambda x:x.is_month_end) data['is_month_start'] = data['id'].apply(lambda x:x.is_month_start) data['is_quarter_end'] = data['id'].apply(lambda x:x.is_quarter_end) data['is_quarter_start'] = data['id'].apply(lambda x:x.is_quarter_start) data['is_year_end'] = data['id'].apply(lambda x:x.is_year_end) data['is_year_start'] = data['id'].apply(lambda x:x.is_year_start) data['lunar'] = data['id'].apply(lambda x:getLunar(x)) data['lunar_date'] = data['lunar'].apply(lambda x:x[0]) data['gz_year'] = data['lunar'].apply(lambda x:x[1]) data['sx_year'] = data['lunar'].apply(lambda x:x[2]) data['gz_day'] = data['lunar'].apply(lambda x:x[3]) data['solar_terms'] = data['lunar'].apply(lambda x:x[4]) data['zodiac'] = data['id'].apply(lambda x:(x.month,x.day)) data['holiday0']= data['zodiac'].apply(lambda x:holiday(x)) data['holiday1']= data['lunar_date'].apply(lambda x:holiday(x)) data['zodiac'] = data['zodiac'].apply(lambda x:zodiac(x[0],x[1])) del data['lunar'] return data data =generateData(startDate='2018-1-01', endDate='2018-12-31') data.to_csv('DIM_TIME.csv', index = False,index_label = False)
最后将生成的csv文件导入数据库即可
数据库
2019-06-04 14:47:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'userinfo.t_long.user_name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

原因:
MySQL 5.7.5和up实现了对功能依赖的检测。如果启用了only_full_group_by SQL模式(在默认情况下是这样),那么MySQL就会拒绝选择列表、条件或顺序列表引用的查询,这些查询将引用组中未命名的非聚合列,而不是在功能上依赖于它们。(在5.7.5之前,MySQL没有检测到功能依赖项,only_full_group_by在默认情况下是不启用的。关于前5.7.5行为的描述,请参阅MySQL 5.6参考手册。)
执行以下个命令,可以查看 sql_mode 的内容。
mysql> SHOW SESSION VARIABLES;
1
mysql> SHOW GLOBAL VARIABLES;
1
mysql> select @@sql_mode;
1
可见session和global 的sql_mode的值都为:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
only_full_group_by说明:
only_full_group_by :使用这个就是使用和oracle一样的group 规则, select的列都要在group中,或者本身是聚合列(SUM,AVG,MAX,MIN) 才行,其实这个配置目前个人感觉和distinct差不多的,所以去掉就好
官网摘抄:
官网:ONLY_FULL_GROUP_BY
Reject queries for which the select list, HAVING condition, or ORDER BY list refer to nonaggregated columns that are neither named in the GROUP BY clause nor are functionally dependent on (uniquely determined by) GROUP BY columns.
As of MySQL 5.7.5, the default SQL mode includes ONLY_FULL_GROUP_BY. (Before 5.7.5, MySQL does not detect functional dependency and ONLY_FULL_GROUP_BY is not enabled by default. For a description of pre-5.7.5 behavior, see the MySQL 5.6 Reference Manual.)
A MySQL extension to standard SQL permits references in the HAVING clause to aliased expressions in the select list. Before MySQL 5.7.5, enabling ONLY_FULL_GROUP_BY disables this extension, thus requiring the HAVING clause to be written using unaliased expressions. As of MySQL 5.7.5, this restriction is lifted so that the HAVING clause can refer to aliases regardless of whether ONLY_FULL_GROUP_BY is enabled.
解决:
执行以下两个命令:
mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
1
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
1
这两个命令,去掉 sql_mode 的 ONLY_FULL_GROUP_BY
见其他文章有说:
直接修改mysql配置文件(我的系统是Ubuntu16.04的,在/etc/mysql/mysql.conf.d/mysqld.cnf 中并没有sql_mode这个配置,所以直接加上就好,如果是其他系统有得修改就不用添加了)
这个方法暂时没有式。
mysql 配置信息读取顺序。
①ps aux|grep mysql|grep ‘my.cnf’
②mysql –help|grep ‘my.cnf’
/etc/my.cnf, /etc/mysql/my.cnf, /usr/local/etc/my.cnf, ~/.my.cnf 这些就是mysql默认会搜寻my.cnf的目录,顺序排前的优先。mysql按照上面的顺序加载配置文件,后面的配置项会覆盖前面的。
如果没有该文件可以自定义一个文件。然后回默认读取配置中的内容)
查看你需要修改的是哪个配置文件。我只有/etc/my.cnf 只修改这个文件即可
配置文件my.cnf通常会分成好几部分,如[client],[mysqld], [mysql]等等。MySQL程序通常是读取与它同名的分段部分,例如服务器mysqld通常读取[mysqld]分段下的相关配置项。如果配置项位置不正确,该配置是不会生效的
参考:https://stackoverflow.com/questions/37951742/1055-expression-of-select-list-is-not-in-group-by-clause-and-contains-nonaggr
这个语句没试过,先记录:
set @@sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
1
去掉ONLY_FULL_GROUP_BY即可正常执行sql.
数据库
2019-06-04 13:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
2019年5月8日-5月10日,由国内知名IT技术社区主办的数据库技术交流盛会——DTCC 2019在北京新云南皇冠假日大酒店召开。数据风云,十年变迁,DTCC见证并铭记了国内数据库技术的关键成长历程。作为DTCC的老朋友和全球领先的云计算厂商,阿里云数据库团队受邀参加本次技术盛会,不仅派出了重量级嘉宾阵容,还为广大数据库业内人士和行业用户奉上了 8场精彩议题 。
洞见前沿技术趋势,见证阿里云数据库成长历程。在这场数据库领域从业人士难得的年度盛会和交流平台上,来看看阿里云技术大牛们都带来了怎样的饕餮盛宴。
8场议题的直播回顾、干货PPT下载及分享实录 汇总如下:
1、阿里云李飞飞:云原生新战场,我们如何把握先机?
直播回顾: https://yq.aliyun.com/live/1043?utm_content=g_1000061144
分享实录文字版: https://yq.aliyun.com/articles/703885?utm_content=g_1000061145
会上, 阿里云POLARDB荣膺2019中国数据库年度最佳创新产品
相关阅读见: https://yq.aliyun.com/articles/702310?utm_content=g_1000061146
2、深度解码阿里数据库实现 数据库内核——基于HLC的分布式事务实现深度剖析
直播回顾: https://yq.aliyun.com/live/1045?utm_content=g_1000061147
分享实录文字版: https://yq.aliyun.com/articles/703552?utm_content=g_1000061148
PPT下载: https://yq.aliyun.com/download/3566?utm_content=g_1000061149
3、海量数据毫秒级分析的背后——《阿里超大规模实时数仓架构挑战与实践解析》
直播回顾: https://yq.aliyun.com/live/1047?utm_content=g_1000061150
分享实录文字版: https://yq.aliyun.com/articles/702765?utm_content=g_1000061151
PPT下载: https://yq.aliyun.com/download/3553?utm_content=g_1000061152
4、前沿技术应用知多少? 阿里云图数据库GDB带你探索互联数据的奥秘
直播回顾: https://yq.aliyun.com/live/1050?utm_content=g_1000061153
分享实录文字版: https://yq.aliyun.com/articles/703444?utm_content=g_1000061154
PPT下载: https://yq.aliyun.com/download/3555?utm_content=g_1000061155
5、把握数据库发展趋势 DBA应如何避免“踩坑”?
直播回顾: https://yq.aliyun.com/live/1046?utm_content=g_1000061156
分享实录文字版: https://yq.aliyun.com/articles/703521?utm_content=g_1000061157
PPT下载: https://yq.aliyun.com/download/3562?utm_content=g_1000061158
6、阿里云TSDB: 教你解锁时序时空数据库的种种黑科技
直播回顾: https://yq.aliyun.com/live/1044?utm_content=g_1000061159
分享实录文字版: https://yq.aliyun.com/articles/703542?utm_content=g_1000061160
PPT下载: https://yq.aliyun.com/download/3563?utm_content=g_1000061161
7、云时代数据库迁移 & 容灾技术新进展与应用
直播回顾: https://yq.aliyun.com/live/1048?utm_content=g_1000061162
分享实录文字版: https://yq.aliyun.com/articles/703544?utm_content=g_1000061163
PPT下载: https://yq.aliyun.com/download/3564?utm_content=g_1000061164
8、NoSQL数据库最新发展趋势 如何拥有居家必备的企业级能力?
直播回顾: https://yq.aliyun.com/live/1049?utm_content=g_1000061165
分享实录文字版: https://yq.aliyun.com/articles/703549?utm_content=g_1000061166
PPT下载: https://yq.aliyun.com/download/3565?utm_content=g_1000061167

作者:七幕
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-06-04 13:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.基本结构: CREATE OR REPLACE PROCEDURE 存储过程名字 ( 参数1 IN NUMBER, 参数2 IN NUMBER ) IS 变量1 INTEGER :=0; 变量2 DATE; BEGIN END 存储过程名字
2.SELECT INTO STATEMENT
将select查询的结果存入到变量中,可以同时将多个列存储多个变量中,必须有一条
记录,否则抛出异常(如果没有记录抛出NO_DATA_FOUND)
例子:
BEGIN SELECT col1,col2 into 变量1,变量2 FROM typestruct where xxx; EXCEPTION WHEN NO_DATA_FOUND THEN xxxx; END; ...

3.IF 判断
IF V_TEST=1 THEN BEGIN do something END; END IF;

4.while 循环
WHILE V_TEST=1 LOOP BEGIN XXXX END; END LOOP;

5.变量赋值
V_TEST := 123;
6.用for in 使用cursor
... IS CURSOR cur IS SELECT * FROM xxx; BEGIN FOR cur_result in cur LOOP BEGIN V_SUM :=cur_result.列名1+cur_result.列名2 END; END LOOP; END;

7.带参数的cursor
CURSOR C_USER(C_ID NUMBER) IS SELECT NAME FROM USER WHERE TYPEID=C_ID; OPEN C_USER(变量值); LOOP FETCH C_USER INTO V_NAME; EXIT FETCH C_USER%NOTFOUND; do something END LOOP; CLOSE C_USER;

简单示例: CREATE OR REPLACE NONEDITIONABLE PROCEDURE PRC_MDS1_TO_MDS2 (parameter IN VARCHAR2, exitcode OUT INT) AS -- 定义一个变量用来保存此存储过程的名字 v_logic_proc_name VARCHAR2(30) := 'PRC_MDS1_TO_MDS2'; -- 声明显式游标, 查询udt_mds1_input中item不为null的数据 CURSOR c_udt_mds1_input_item_notnull IS SELECT * FROM scpomgr.udt_mds1_input WHERE item IS NOT NULL AND TRIM(item) IS NOT NULL; -- 定义游标变量,该变量的类型为基于游标c_udt_mds1_input_item_notnull的记录 c_mds1_item_notnull_row c_udt_mds1_input_item_notnull%ROWTYPE; -- 声明一个游标,查询udt_mds1_input中item为null的数据 -- MARKET、itemgroup、package、config不为NULL CURSOR c_udt_mds1_input_item_null IS SELECT * FROM scpomgr.udt_mds1_input WHERE (item IS NULL OR TRIM(item) IS NULL) AND market IS NOT NULL AND TRIM(market) IS NOT NULL AND itemgroup IS NOT NULL AND TRIM(itemgroup) IS NOT NULL AND package IS NOT NULL AND TRIM(package) IS NOT NULL AND config IS NOT NULL AND TRIM(config) IS NOT NULL; -- 定义游标变量 c_mds1_item_null_row c_udt_mds1_input_item_null%ROWTYPE; -- 再声明一个游标,查询udt_mds1_input中item为null -- MARKET、ITEMGROUP、PACKAGE、CONFIG有为NULL的 CURSOR c_udt_mds1_item_null_other IS SELECT * FROM scpomgr.udt_mds1_input WHERE (item IS NULL OR TRIM(item) IS NULL) AND (market IS NULL OR TRIM(market) IS NULL OR itemgroup IS NULL OR TRIM(itemgroup) IS NULL OR package IS NULL OR TRIM(package) IS NULL OR config IS NULL OR TRIM(config) IS NULL); -- 定义游标变量 c_mds1_item_null_other_row c_udt_mds1_item_null_other%ROWTYPE; BEGIN -- 设置要返回的变量值 exitcode := -20999; -- 如果传入的参数是BYPASS, 则表示不运行 IF parameter = 'BYPASS' THEN exitcode := 0; -- 返回0表示执行成功 RETURN; END IF; -- 记录具体逻辑开始执行 logger.info (v_logic_proc_name || ':' || parameter || ' ,Start'); ------------------- 具体的业务逻辑 ------------------- -- 1. 将UDT_MDS1_INPUT所有字段输入到MDS1_BCKUP对应同名字段中 pkg_log.info(v_logic_proc_name || ':' || '步骤1,将UDT_MDS1_INPUT的数据备份到MDS1_BCKUP表中'); INSERT INTO scpomgr.MDS1_BCKUP SELECT * FROM scpomgr.UDT_MDS1_INPUT; -- 2. 是否要先清空中间表和UDT_MDS2_REVIEW表中的数据 pkg_log.info(v_logic_proc_name || ':' || '步骤2,删除mid_mds1_mds2和udt_mds2_review表中的数据'); DELETE FROM scpomgr.mid_mds1_mds2; -- 删除中间表数据 DELETE FROM scpomgr.udt_mds2_review; -- 删除UDT_MDS2_REVIEW表数据 -- 3. 循环处理ITEM不为NULL的数据 pkg_log.info(v_logic_proc_name || ':' || '步骤3,循环处理udt_mds1_input中item不为null的数据'); FOR c_mds1_item_notnull_row IN c_udt_mds1_input_item_notnull LOOP -- 使用嵌套内部块 declare -- 定义变量 v_country scpomgr.udt_country_ratio.country%TYPE; -- 定义一个国家变量 v_color scpomgr.udt_basic_info.color%TYPE; -- 定义一个颜色变量 begin -- 3.1 查询udt_country_ratio.country信息,并赋值给v_country BEGIN SELECT country INTO v_country FROM ( SELECT ucr.country FROM scpomgr.udt_country_ratio ucr WHERE ucr.market=c_mds1_item_notnull_row.market AND ucr.itemgroup=c_mds1_item_notnull_row.itemgroup ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_notnull_row.market || '和' || c_mds1_item_notnull_row.itemgroup || '没有在scpomgr.udt_country_ratio查询到对应的国家信息'); prc_udt_error_log(c_mds1_item_notnull_row.itemgroup, c_mds1_item_notnull_row.market, '', '没有在scpomgr.udt_country_ratio查询到对应的国家信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 3.2 查询udt_basic_info.color信息,并赋值给v_color BEGIN SELECT color INTO v_color FROM ( SELECT ubi.color FROM scpomgr.udt_basic_info ubi WHERE ubi.item = c_mds1_item_notnull_row.item ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_notnull_row.item || '没有在scpomgr.udt_basic_info查询到对应的颜色信息'); prc_udt_error_log(c_mds1_item_notnull_row.item, '', '', '没有在scpomgr.udt_basic_info查询到对应的颜色信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 3.3 将数据保存到中间表中 INSERT INTO scpomgr.mid_mds1_mds2(market,brand,itemgroup,item,descr,status,loc,package,config,dmdtype,version_no,country,color, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27) VALUES (c_mds1_item_notnull_row.market, c_mds1_item_notnull_row.brand, c_mds1_item_notnull_row.itemgroup, c_mds1_item_notnull_row.item, c_mds1_item_notnull_row.descr, c_mds1_item_notnull_row.status, c_mds1_item_notnull_row.loc, c_mds1_item_notnull_row.package, c_mds1_item_notnull_row.config, c_mds1_item_notnull_row.dmdtype, c_mds1_item_notnull_row.version_no, v_country, v_color, c_mds1_item_notnull_row.w0, c_mds1_item_notnull_row.w1, c_mds1_item_notnull_row.w2, c_mds1_item_notnull_row.w3, c_mds1_item_notnull_row.w4, c_mds1_item_notnull_row.w5, c_mds1_item_notnull_row.w6, c_mds1_item_notnull_row.w7, c_mds1_item_notnull_row.w8, c_mds1_item_notnull_row.w9, c_mds1_item_notnull_row.w10, c_mds1_item_notnull_row.w11, c_mds1_item_notnull_row.w12, c_mds1_item_notnull_row.w13, c_mds1_item_notnull_row.w14, c_mds1_item_notnull_row.w15, c_mds1_item_notnull_row.w16, c_mds1_item_notnull_row.w17, c_mds1_item_notnull_row.w18, c_mds1_item_notnull_row.w19, c_mds1_item_notnull_row.w20, c_mds1_item_notnull_row.w21, c_mds1_item_notnull_row.w22, c_mds1_item_notnull_row.w23, c_mds1_item_notnull_row.w24, c_mds1_item_notnull_row.w25, c_mds1_item_notnull_row.w26, c_mds1_item_notnull_row.w27); end; END LOOP; -- 4. 循环处理ITEM为NULL的数据 pkg_log.info(v_logic_proc_name || ':' || '步骤4,循环处理udt_mds1_input中item为null,但MARKET/itemgroup/package/config不为NULL的数据'); FOR c_mds1_item_null_row IN c_udt_mds1_input_item_null LOOP -- 使用嵌套内部块 declare -- 定义变量 v_item scpomgr.udt_basic_info.item%TYPE; -- 定义item v_descr scpomgr.udt_basic_info.descr%TYPE; -- 定义descr -- 定义国家和国家占比 v_country scpomgr.udt_country_ratio.country%TYPE; -- 国家 v_country_ratio scpomgr.udt_country_ratio.country_ratio%TYPE; -- 国家占比 -- 使用数组定义15种颜色及占比 type v_color_varray is varray(15) of scpomgr.udt_country_ratio.color1%TYPE; -- 颜色 v_color v_color_varray := v_color_varray('', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); type v_color_ratio_varray is varray(15) of scpomgr.udt_country_ratio.color1_ratio%TYPE; -- 颜色占比 v_color_ratio v_color_ratio_varray := v_color_ratio_varray(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); -- 使用数组定义28周的数据量变量 type v_w_varray is varray(28) of scpomgr.udt_mds1_input.w0%TYPE; v_w v_w_varray := v_w_varray(c_mds1_item_null_row.w0,c_mds1_item_null_row.w1,c_mds1_item_null_row.w2,c_mds1_item_null_row.w3,c_mds1_item_null_row.w4,c_mds1_item_null_row.w5, c_mds1_item_null_row.w6,c_mds1_item_null_row.w7,c_mds1_item_null_row.w8,c_mds1_item_null_row.w9,c_mds1_item_null_row.w10,c_mds1_item_null_row.w11, c_mds1_item_null_row.w12,c_mds1_item_null_row.w13,c_mds1_item_null_row.w14,c_mds1_item_null_row.w15,c_mds1_item_null_row.w16,c_mds1_item_null_row.w17, c_mds1_item_null_row.w18,c_mds1_item_null_row.w19,c_mds1_item_null_row.w20,c_mds1_item_null_row.w21,c_mds1_item_null_row.w22,c_mds1_item_null_row.w23, c_mds1_item_null_row.w24,c_mds1_item_null_row.w25,c_mds1_item_null_row.w26,c_mds1_item_null_row.w27); begin -- 4.1 查询udt_country_ratio中抓取对应的country、 country_ratio、color1..15、color1..15_ratio,并赋值给相应的变量 BEGIN SELECT country,country_ratio,color1,color1_ratio,color2,color2_ratio,color3,color3_ratio,color4,color4_ratio,color5,color5_ratio,color6,color6_ratio, color7,color7_ratio,color8,color8_ratio,color9,color9_ratio,color10,color10_ratio,color11,color11_ratio,color12,color12_ratio,color13,color13_ratio, color14,color14_ratio,color15,color15_ratio INTO v_country,v_country_ratio,v_color(1),v_color_ratio(1),v_color(2),v_color_ratio(2),v_color(3),v_color_ratio(3),v_color(4),v_color_ratio(4), v_color(5),v_color_ratio(5),v_color(6),v_color_ratio(6),v_color(7),v_color_ratio(7),v_color(8),v_color_ratio(8),v_color(9),v_color_ratio(9), v_color(10),v_color_ratio(10),v_color(11),v_color_ratio(11),v_color(12),v_color_ratio(12),v_color(13),v_color_ratio(13),v_color(14),v_color_ratio(14), v_color(15),v_color_ratio(15) FROM ( SELECT ucr.country,ucr.country_ratio,ucr.color1,ucr.color1_ratio,ucr.color2,ucr.color2_ratio,ucr.color3,ucr.color3_ratio,ucr.color4,ucr.color4_ratio, ucr.color5,ucr.color5_ratio,ucr.color6,ucr.color6_ratio,ucr.color7,ucr.color7_ratio,ucr.color8,ucr.color8_ratio,ucr.color9,ucr.color9_ratio, ucr.color10,ucr.color10_ratio,ucr.color11,ucr.color11_ratio,ucr.color12,ucr.color12_ratio,ucr.color13,ucr.color13_ratio,ucr.color14,ucr.color14_ratio, ucr.color15,ucr.color15_ratio FROM scpomgr.udt_country_ratio ucr WHERE ucr.market = c_mds1_item_null_row.market AND ucr.itemgroup = c_mds1_item_null_row.itemgroup ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_null_row.market || '和' || c_mds1_item_null_row.itemgroup || '没有在scpomgr.udt_country_ratio查询到对应国家和颜色及占比信息'); prc_udt_error_log(c_mds1_item_null_row.itemgroup, c_mds1_item_null_row.market, '', '没有在scpomgr.udt_country_ratio查询到对应国家和颜色及占比信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 4.2对于country_ratio、color_ratio不为NULL的记录 更新W0-W27的数据 FOR i in 1 .. 15 LOOP -- 先判断有没有颜色,如果没有则不处理 IF(v_color(i) IS NOT NULL AND TRIM(v_color(i)) IS NOT NULL) THEN -- 4.2.1 获取item,descr v_item := ''; v_descr := ''; -- 先设置为默认值 BEGIN SELECT item,descr INTO v_item,v_descr FROM ( SELECT ubi.item,ubi.descr FROM scpomgr.udt_basic_info ubi WHERE ubi.country = v_country AND ubi.itemgroup = c_mds1_item_null_row.itemgroup AND ubi.package = c_mds1_item_null_row.package AND ubi.color = v_color(i) AND ubi.config = c_mds1_item_null_row.config ) WHERE ROWNUM=1; EXCEPTION WHEN no_data_found THEN pkg_log.error(v_logic_proc_name || ':' || '根据' || c_mds1_item_null_row.itemgroup || ',' || c_mds1_item_null_row.package || ',' || v_color(i) || ',' || c_mds1_item_null_row.config || '没有在scpomgr.udt_basic_info查询到对应的item和descr信息'); prc_udt_error_log(c_mds1_item_null_row.itemgroup, c_mds1_item_null_row.package|| '+' || c_mds1_item_null_row.config, '', '没有在scpomgr.udt_basic_info查询到对应的item和descr信息', 1, 'PRC_MDS1_TO_MDS2'); END; -- 4.2.2 如果不为空,则要计算v_w的数据 IF(v_country_ratio IS NOT NULL AND v_color_ratio(i) IS NOT NULL) THEN --循环处理v_w0...v_w27的数据 FOR j in 1 .. 28 LOOP v_w(j) := v_w(j) * v_country_ratio * v_color_ratio(i); END LOOP; -- 将处理完的数据插入到中间表中 INSERT INTO scpomgr.mid_mds1_mds2(market,itemgroup,package,config,country,country_ratio,color,color_ratio,item, descr, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27) VALUES (c_mds1_item_null_row.market, c_mds1_item_null_row.itemgroup, c_mds1_item_null_row.package, c_mds1_item_null_row.config, v_country, v_country_ratio, v_color(i),v_color_ratio(i), v_item, v_descr, v_w(1), v_w(2), v_w(3), v_w(4), v_w(5), v_w(6), v_w(7), v_w(8), v_w(9), v_w(10), v_w(11), v_w(12), v_w(13), v_w(14), v_w(15), v_w(16),v_w(17), v_w(18), v_w(19), v_w(20), v_w(21), v_w(22), v_w(23), v_w(24), v_w(25), v_w(26), v_w(27), v_w(28)); END IF; END IF; END LOOP; end; END LOOP; -- 5. 对于若UDT_MDS1_INPUT.ITEM为NULL,MARKET、ITEMGROUP、PACKAGE、CONFIG有为NULL的 pkg_log.info(v_logic_proc_name || ':' || '步骤5,循环处理udt_mds1_input中item为null,并且MARKET/itemgroup/package/config有为NULL字段的数据'); FOR c_mds1_item_null_other_row IN c_udt_mds1_item_null_other LOOP -- 使用嵌套内部块 declare -- 定义一个变量保存数据 v_null_filed varchar2(500); begin if(c_mds1_item_null_other_row.market is null or trim(c_mds1_item_null_other_row.market) is null) then v_null_filed := v_null_filed || 'market,'; elsif(c_mds1_item_null_other_row.itemgroup is null or trim(c_mds1_item_null_other_row.itemgroup) is null) then v_null_filed := v_null_filed || 'itemgroup,'; elsif(c_mds1_item_null_other_row.package is null or trim(c_mds1_item_null_other_row.package) is null) then v_null_filed := v_null_filed || 'package,'; elsif(c_mds1_item_null_other_row.config is null or trim(c_mds1_item_null_other_row.config) is null) then v_null_filed := v_null_filed || 'config'; end if; -- 找出哪个字段为空,并记录下来 pkg_log.error(v_logic_proc_name || ':' || 'scpomgr.UDT_MDS1_INPUT表中下面字段为空: ' || v_null_filed); prc_udt_error_log('', '', '', 'scpomgr.UDT_MDS1_INPUT表中下面字段为空: ' || v_null_filed, 1, 'PRC_MDS1_TO_MDS2'); END; END LOOP; -- 6. 将中间表的数据保存到UDT_MDS2_REVIEW表中 pkg_log.info(v_logic_proc_name || ':' || '步骤6,将中间表mid_mds1_mds2的数据保存到udt_mds2_review表中'); INSERT INTO scpomgr.udt_mds2_review(market,brand,itemgroup,item,descr,status,loc,package,color,config,dmdtype,version_no, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27) SELECT market,brand,itemgroup,item,descr,status,loc,package,color,config,dmdtype,version_no, w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,w21,w22,w23,w24,w25,w26,w27 FROM scpomgr.mid_mds1_mds2; -- 说明没有错误, 执行成功 exitcode := 0; -- 记录完成 logger.info (v_logic_proc_name || ':' || parameter || ' ,Success'); -- 成功的话, 提交 COMMIT; EXCEPTION WHEN OTHERS THEN exitcode := SQLCODE; -- 返回错误代码 pkg_log.error('Failure:' || v_logic_proc_name || ':' || SQLCODE || ':' || SQLERRM || ':' || substr(dbms_utility.format_error_backtrace, 1, 1024)); COMMIT; END PRC_MDS1_TO_MDS2;
数据库
2019-06-04 11:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
摘要:阿里云TSDB是阿里自研的一种高性能,低成本,稳定可靠的在线时序时空数据库产品。该产品统一了阿里巴巴集团90%以上的APM数据和事件型数据的存储和计算,并在广泛应用于外部的物联网,工业制造,电力,化工以及IT运维等行业。本文中,阿里云智能数据库产品事业部技术专家伊翼就为大家介绍了阿里云TSDB的种种黑科技。
专家简介:伊翼(花名:老滚)。阿里云智能数据库产品事业部技术专家,主要从事TSDB核心引擎的研发工作。
直播回放
链接 : https://yq.aliyun.com/live/1044
议题PPT下载,戳这里!
https://yq.aliyun.com/download/3563
本次分享的内容主要包括以下四个方面: 走进时序数据库 认识阿里云TSDB 阿里云TSDB技术内幕 未来与展望
一、走进时序数据库
熟悉而又陌生的时序数据
时序数据库本身是一个比较新的概念,直到5年前,DB-Engine才将时序数据库列为一个独立的分类。虽然时序数据库的概念比较新,但是时序数据却由来已久。从古至今,在我们的日常生活中,时序数据从未缺席。古代记录灾害与祥瑞出现时间的县志也能够发挥类似今天时序数据库的作用,帮助决策者指定相关的决策,地方官员可以根据县志中的记录判断是否需要进行祭祀,也可以决策是否需要向中央朝廷报告祥瑞以谋取升迁等,因此当时的县志也发挥了类似于OLAP的功能。但由于理念和技术的限制,当时所记录的时序数据信息是有限的,精度也是有限的。
技术发展到今天,时序数据所能记录的信息和精度都有了极大的提升。如下图所示的是杭州市空气监测时序数据片段。由此可以看出,时序数据有一些共同的特征,比如多样的指标值、比较稳定的采集频率以及任何一个数据点都有时间戳。在技术飞速发展的今天,时序数据的规模越来越大,增长速度也越来越快。因此,我们需要面对一些问题,比如面对如此大规模的时序数据,应该将其存放在哪里。
时序数据库的概念
在十几年前,时序数据只能选择存放在关系型数据库中,但是随着通信技术的发展,特别是互联网技术的发展,时序数据的增长速度呈现指数级别,使用关系型数据库来存储时序数据显然跟不上时代的节奏了,所以时序数据库应运而生。时序数据库就是一类专门为处理时间序列数据而设计并优化的数据库管理系统。
相较传统的关系型数据库,时序数据库的特点如下:
 存储的任何一条数据记录都必然带一个时间戳
 通常高频访问热数据
 数据写入频率相对稳定,且远大于数据读取的频率
 通常按照时间窗口查询数据
 基本不提供单点数据的更新或删除功能
 无需提供类似关系型数据库事务级别的数据强一致性
目前,使用时序数据库的行业应用越来越广泛。
 电力行业:智能电表、电网、发电设备的集中监测
 交通行业:实时路况,路口流量监测,卡口数据的采集与分析
 石油石化:油井、运输管线、运输车队的实时监测
 物流行业:车辆、集装箱的追踪监测
 环境监测:天气、空气、水文、地质环境等监测
 物联网:电梯、锅炉、机械、水表、气表等各种联网设备的数据采集、分析与检测
 军工行业:军事装备的数据采集、存储与分析
 制造业:生产过程管控,流程数据、供应链数据采集与分析
 互联网:互联网应用的PV/UV数据,基础设施的性能监控
时序数据库的迅猛发展
由于时序数据库的适用性非常广泛,因此其在DB-Engine上的受关注度一直处于增长态势。面对这样的关注度增长态势,时序数据库技术的发展也作出了积极的响应。无论是在开源领域还是商用领域,都推出了大量的时序数据库产品,比如InfluxDB、OpenTSDB、TimescaleDB以及阿里云时序时空TSDB等。
二、认识阿里云TSDB
阿里云时序时空TSDB架构
如下图所示的是阿里云时序时空TSDB的整体架构,从左到右依次是采集端、TSDB服务端以及靠近最终用户和开发者的实例端。在采集端,阿里云时序时空TSDB采用了边缘计算的解决方案,其可以应用在资源受限或者网络状况不稳定的场景下。采集端可以和服务端进行打通,服务端可以向边缘下发各种各样的规则,使得边缘端能够直接进行数据清洗和计算,这就实现了“边云一体化”。图中的中间部分是TSDB的服务端,它也分为几个组件,TS计算引擎主要负责预聚合、降精度以及持续查询,TSQL引擎主要负责处理SQL查询,此外还有一个基于已经训练好的模型算法库,提供各行业定制化解决方案的智能引擎。在这三个引擎下面就是TSDB的时序引擎。
接下来为大家介绍阿里云时序时空TSDB在功能层面的一些特性。
特性1:强力的数据模型支持
阿里云TSDB支持多样的数据模型,同时支持了多值模型和单值模型。举例而言,温度监控设备需要每间隔一段时间向数据库上报温度数据,其上报的数据中必然带有一个时间戳以及温度值,这样最基础的数据形式称之为单值模型。而如果上报的数据中不仅仅包含了一个时间戳和室内温度,还包含了室外温度以及空气湿度等,这样的数据就可以称之为多值模型。其实,时序数据库对于多值模型的支持并不是行业要求,因此即便是在开源领域,各种数据库对于多值模型的支持也不同。支持多值模型的好处在于可以提升数据的写入效率,另外一方面就是对于业务应用的开发者而言可以使得设计更加直观。
除了对于多值模型的支持之外,阿里云TSDB还支持多种的数据类型,不仅支持传统数据类型,还能够支持字符串类型数据,并且能够支持精确到毫秒的时间戳。
特性2:降采样&数据聚合
对于时序数据库而言,降采样和数据聚合也是非常重要的特性。依旧以温度采集为例,温度采集设备可能上报数据的频率非常高,比如每秒钟上传一次数据,但是在做数据查询的时候并不需要按照原始的数据采集频率进行分析和展示,因此就需要对于上报的数据进行降采样操作,比如将按秒采样的数据降采样为按小时或者按天进行分析和展示。
与之相对的,数据聚合在分析和展示中也非常重要。通常情况下,有很多个数据采集设备,不同设备每隔一段时间上报数据的时候就认为这些数据属于不同的时间序列,而随着设备的增多,必然使得时间序列变得非常多,而在做分析和查询的时候并不需要对多个时间序列进行分析,只需要将其进行汇总,比如使用汇总后的平均值进行分析。这种情况下就是对于一个数据的指标值按照时间维度将多个时间序列聚合成一条,这就是数据聚合。无论是降采样还是数据聚合,阿里云TSDB都提供了非常丰富的聚合算子,有了这样的能力,就可以仅凭借阿里云原生能力来满足各种复杂的查询分析场景。
特性3:SQL查询能力
由于时序数据库本身属于比较新的概念,为了降低开发人员以及数据分析人员使用时序数据库的门槛和学习成本,阿里云TSDB也提供了基于SQL的查询接口。有了SQL的查询接口,用户就可以非常方便地使用SQL来操作时序模型。而阿里云TSDB的SQL接口也基于时序场景进行了算法上的优化,可以将SQL中的过滤、聚合等操作全部下推到TSDB的内核中,这样就可以最优化的方式来处理时序数据的分析和查询。
特性4:内置对接Prometheus
在最新版的阿里云TSDB中,已经实现了内置对接Prometheus的能力。Prometheus是一个非常适用于监控Kubernetes集群的工具,但是其对于监控数据的存储能力比较薄弱,虽然社区也考虑到这一点并且提供了Prometheus Adapter的第三方组件来将Prometheus的数据对接到各种各样的数据源上,但是当数据链路中增加一个组件就意味着查询性能的降低。为了在阿里云TSDB对接Prometheus的同时保持较高的查询效率,TSDB内置了对接Prometheus的能力。经过测试,内置对接Prometheus的方式相对于经由Prometheus Adapter中转方式的查询性能要高很多。
特性5:边缘计算能力
阿里云TSDB的边缘端计算能力处于行业内的领先地位。因为在物联网应用和工业大数据的应用场景中,无法保证数据的采集端是实时在线的,这样的场景就是边缘计算的用武之地。考虑到用户数据的可用性,TSDB边缘端再设计的时候也采用了高可用架构。当网络状况恢复稳定的时候,边缘段会将数据同步给阿里云TSDB服务端,这样可以方便用户在服务端进行统一的数据分析和查询。
与其他时序数据库的功能对比
下图中的表格列出了目前主流的时序数据库在功能特性上的支持情况对比。
接下来为大家介绍几个阿里云TSDB实际的应用案例。
案例1: 某互联网餐饮系统研发企业
该企业在自己的解决方案中将阿里云TSDB整合了进去,利用阿里云TSDB高性能写入将整个链路中的所有时序数据以及业务指标全部写入了TSDB中,借助TSDB优越的查询性能以及将监控系统整合在一起,从而支持了对于整个解决方案中所有链路节点的实时监控,与此同时提高了系统的整体稳定性。
案例2:某直播平台运维监控APM
该直播平台原来的APM系统中将所有采集到的时序数据全部通过消息队列存储到OpenTSDB集群中,但是很快就发现OpenTSDB的写入存在瓶颈,而且OpenTSDB在时序索引方面天生存在薄弱点,因此在面向较为复杂的查询的时候,几乎处于不可用的状态。在经过比较之后,该直播平台选择使用阿里云TSDB来替换所有的OpenTSDB,并且加大了写入规模,从实际效果来看,阿里云TSDB达到了所期望的效果。
案例3: 阿里巴巴集团内部全业务对接
最后的一个案例是阿里巴巴集团内部的案例。从上图可以看出,无论是底层的资源调控、整体监控还是上层应用,阿里云TSDB已经覆盖了阿里集团内部的130余个线上业务。而在2018年双11大促期间,阿里云TSDB承接的来自于阿里集团内部的各个业务的时序数据,写入TPS峰值达到了4000万TPS,查询峰值达到了2万QPS,累计时间线数量超过了100亿。
三、阿里云TSDB技术内幕
时序时空TSDB引擎的核心技术
阿里云时序时空TSDB引擎具有很多的核心技术,在本次分享中主要为大家介绍数据压缩、时序索引以及聚合引擎三个方面的核心技术。
数据压缩
时序数据的规模增长速度很快,而用户往往出于日后需要进行查询或者分析的考虑,希望所能够存储的时序数据越多越好。但是通常情况下,对于大规模时序数据的查询而言,往往非常困难。一方面需要满足用户对于查询的需求,另外一方面需要有效地降低用户存储的成本。针对于以上两方面的诉求,阿里云TSDB研发了一套数据压缩技术。下图中左侧是一张示意图,其每一行代表一个时间序列,其列代表数据点。在没有进行数据压缩的情况下,如果想要将其数据调整到毫秒级别,就会发现其列数会增加到360万,这样的数据量是非常可观的,所以必须要进行压缩。阿里云TSDB所采用的压缩思路借鉴了Facebook Gorilla的实现思路,会将时间戳和数据两块压缩成两个大数据块,对时间戳采用了delta-delta的压缩方法,而对于不同的数据类型则采用了相应的数据压缩算法。在压缩成两个大数据块基础之上,再对其进行通用的块压缩。经过两部分的压缩就使得数据压缩比达到15:1的效果。
如下图所示的是真实场景下的数据压缩效果。原始情况下数据大约6TB,一开始尝试最普通的块压缩,将数据压缩到了715G,但此时的数据压缩比不到10:1,而采用先进行时序压缩再追加一次块压缩后使得最终数据压缩为413G,压缩比达到了15:1。那么,追求如此之高的数据压缩比有什么好处呢?其实主要有两个好处,第一个好处就是能够帮助用户降低存储成本;另外一个好处就是因为数据压缩比很大,因此当在进行大范围的时序数据查询的时候,IO效率会非常高,在这个例子中可以将查询延时降低约50%。
时序索引
TSDB的整体查询流程非常简单,当用户指定了一个查询条件,阿里云TSDB首先会解析这个查询条件,同时做一定程度的优化。接下来会做两件事情,一件是将查询条件扔给时序索引模块,时序索引模块会根据查询条件计算命中的时间线数量以及相关信息,拿到时间线信息之后再将时间线集合扔给聚合索引,聚合索引再到底层存储上面获取相应的时间数据并进行降采样、聚合等操作。虽然这一过程看上去比较简单,但是却存在很多值得研究的点。
如下图所示的是时间线的生命周期,如果用户想要查询T2-T3时间范围内的数据,肯定不希望数据中包含T0-T2已经消亡或者说不再有新的数据进来的时间线,所以这部分也是时序索引可以进一步研究的地方。
对于时序索引而言,还需要支持模糊查询,所谓模糊查询就是给出的并不是一个完整的时间序列定义,而可能是Tag的全量匹配,或者基于Tag或者Tag Value的模糊查询,需要能够找到相应的时间线,此时就需要基于Tag Key或者Tag Value做一个倒排索引。在时间序列生命周期的启发下,在倒排索引技术基础之上,TSDB增加了时间序列生命周期的倒排索引。同时加上对于生命周期的进一步过滤,最终得到相对较少的时间线。将这些相对较少的时间线扔给下一层进行计算的时候就会带来一个好处就是减少了IO,提供了极致的查询性能。除了上述优化工作之外,TSDB还将整个倒排索引持久化到存储层,这使得索引节点可以处于无状态的结构,并且使得水平扩展变得非常容易,对于时间线数据实现TTL。
在时序索引模块中还实现了一个评估器, 评估器会以自己认为的最合适的方式计算时间线,因为当查询条件非常复杂的时候,计算时间线的方式有很多种,就好比在关系型数据库中做Join也有很多种方法。评估器会根据几个相应的来源做评估,分别是HHL技术器、BloomFilter以及时序索引缓存。HHL计数器所记录的就是倒排索引中命中的时间线数量,BloomFilter记录的是时间线是否还真实存在的情况,这两部分数据并不是非常精确的,它们是在过去写入查询的过程中所得到的粗略统计数据。但是当时间线本身出现量级差异的时候,这样的评估就会变得非常有意义,其能够以最优化的代价来获取时间线。因此,评估器的作用就类似于关系型数据库中的CBO (Cost-based Optimizer)。
聚合引擎
时序索引的下个模块就是聚合引擎,时序索引将查询条件所命中的时间线集合获取之后交给聚合索引。而聚合索引就是按照传统关系型数据库的执行器的火山模型模型进行设计的,我们为其设计了很多的聚合算子和插值算子,这些算子都是以Pipeline方式进行一轮轮迭代的。目前,一共实现了10多个核心聚合算子,20多个填充策略以及10多个插值算法,并且这些算子的数量还在不断地增加中。
借助聚合引擎,可以减少内存开销以及对于底层存储的查询压力,这是因为有了算子的支持之后,只需要每次抓取少批量数据进行计算即可。此外,聚合引擎和预聚合、降采样也进行了无缝对接,当数据写入的时候已经实施了采样过程,在实际查询的时候就可以很容易地实现采样,聚合引擎就不会从存储层捞取原始数据,而是直接捞取预降采样数据,从而进行进一步的数据计算,这就减少了底层存储的IO操作。
四、未来与展望
最后为大家介绍一下,阿里云数据库技术团队目前在时序时空领域所做的工作和尝试。
阿里云TSDB自研引擎的未来像
阿里云TSDB自研引擎的未来四个发展方向主要包含四点: 冷热数据异构存储;对于用户而言,往往希望将时序数据全部保存下来,因此需要考虑写入、存储以及查询成本等。而对于时序数据而言,用户往往对于热数据更感兴趣,因此可能需要考虑冷热数据异构存储。目前,TSDB正在尝试与阿里云OSS存储解决方案进行打通。 Serverless读写能力;希望能够赋予阿里云TSDB以Serverless读写能力,拥有该项能力就可以进一步降低存储和计算成本。 拥抱时序生态;未来,TSDB还将更加紧密地拥抱时序生态。TSDB目前已经对接了Prometheus的生态,而在未来会进一步对接更多开源生态,希望能够通过与生态的对接使得阿里云TSDB中一些比较有意义的特性能够发挥更大的价值。 时序智能分析;阿里云TSDB也希望能够在时序智能方面训练更多的模型,并且深入到各个具体的行业中,为行业解决特定的问题。
时序时空领域的新尝试
阿里数据库团队除了在自研时序数据库方面做了大量的工作之外,还在其他方面进行大量的尝试。比如开源版时序数据库InfluxDB的云端产品化——TSDB for InfluxDB,其瞄准的痛点就是如果用户使用开源版本的自建InfluxDB时,会感觉到内存管理不稳定,在进行一些稍微复杂的查询时就会触发OOM。TSDB for InfluxDB会优化和重构InfluxDB的内存管理模块,提高稳定性。
TSDB for InfluxDB
因为时序数据库在整个业界而言都是比较新的技术,可以想象如果使用自建的InfluxDB,运维成本就会非常高,而如果使用了TSDB for InfluxDB的基于阿里云服务,运维成本就会极大地降低,除了能够得到99.9%的SLA承诺,并能够得到InfluxDB专家的在线技术支持。虽然阿里云对于InfluxDB做了改动和产品化,但是不影响两者生态的兼容。TSDB for InfluxDB可以无缝地对接到用户的Telegraf、Chronograf以及Kapacitor工具链中。TSDB for InfluxDB在上月月底正式上线进行公测,公测期间免费使用,因此大家如果感兴趣可以尝试,并且也提供了数据的零感知迁移工具,能够帮助用户一键式实现数据迁移。
TSDB for Spatial Temporal
阿里云数据库团队还在做的另外一项尝试就是TSDB for Spatial Temporal,其属于存储时空数据的数据库。TSDB for Spatial Tempora为基于时空数据构建大规模应用提供了两大利器——自研的S3时空索引和高性能电子围栏。S3 时空索引实现千万级时空数据的秒级查询能力,助力用户实现时空数据的低延迟查询分析,满足实时交互查询及实时数据分析场景。而且TSDB for Spatial Temporal还能够支持海量围栏,同时检测大规模移动目标。
最后,欢迎大家关注阿里云时序时空数据库公众号: https://yq.aliyun.com/teams/395 。
作者:七幕
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-06-04 11:40:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
为什么会突然写这个blog呢?因为之前有只青蛙小姐姐问我,能不能教她join,当时上大学老师怎么教她也不会。然后本来想面对面交流给她说明,后面阴错阳差,就延误到了现在。
所以我想,我可以提前准备好我想说的东西,记录下来,顺便自己也回忆下join(ps:为什么我需要回忆?因为之前的公司都是面向互联网的、高并发的业务,用join的话,很容易导致数据库出现异常问题,我已经很久没用过了)。
当然有机会的话,我觉得还是要当面给她讲。
join是什么,为什么要有join
这个就很重要了,学一个东西你都不知道它是什么,有什么应用场景的话,基本是学完就忘。
SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
举个🌰:
栗子当然是要用学校的栗子啊,在学校的时候吃栗子还是蛮有感觉的,现在吃个🌰,都习以为常了。 学生表(student) uid,name 成绩表(achievement) uid,score
这个时候我们要怎么去查询学生的成绩呢?
如果不用连表,我们可能就需要2条sql. select * from student where uid = x select * from achievement where uid = x
这样是不是很繁琐。
假如我们有join了,那就很好操作了。 select * from student join achievement on student.uid = achievement.uid where uid = x
join的分类
join分为3种情况,分别是inner、left、right。从字面上,我们就能理解为全、左、右
inner join
inner join(等值连接) 只返回两个表中联结字段相等的行。
怎么理解呢?还是拿学生来举例。 select * from student inner join achievement on student.uid = achievement.uid where uid = x
比如一个班有30个学生,但是只有28个人参加的考试。
如果使用inner会展示出参加考试的学生及其成绩
left join
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
怎么理解呢?还是拿学生来举例。 select * from student left join achievement on student.uid = achievement.uid where uid = x
比如一个班有30个学生,但是只有28个人参加的考试。
如果使用left会展示出所有的学生,没有成绩的会补充为Null。
right join
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
right join一般用于右表数据比较多的情况。
还是拿学生来举例吧。 select * from student right join achievement on student.uid = achievement.uid where uid = x
比如一个班去年有33个学生,但是有3个人休学了。如果查询去年的成绩的话。
就会得到33条记录,而休学的3个同学的姓名是无法获知了,会和left join一样补充为null。
总结
因此很明显的,可以看出来
inner返回是完全匹配
left和right返回的数据是部分匹配但是会将其中一张表数据匹配到的完全返回,而将不完全的表的值填充为空。
至于left和right表,就这么理解,如果是left,那left左边的表名就是完全返回的,如果是right,则反之。
数据库
2019-06-04 11:28:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
InnoDB在设计lock-free的log system时,除了已有的参数外,还通过宏控制隐藏了一些参数,如果你使用源码编译时,打开cmake选项-DENABLE_EXPERIMENT_SYSVARS=1, 就可以看到这些参数了。本文主要简单的过一下这些隐藏的参数所代表的含义
A.
innodb_log_write_events
innodb_log_flush_events
两者的含义类似,表示用来唤醒等待log write/flush的event的个数,默认值都是2048
比如你要等待的位置在lsnA,那么计算的slot为:
slot = (lsnA - 1) /OS_FILE_LOG_BLOCK_SIZE & (innodb_log_write/flush_events - 1)
这意味着:如果事务的commit log的end lsn落在相同block里,他们可能产生event的竞争
当然如果不在同一个block的时候,如果调大参数,就可以减少竞争,但也会有无效的唤醒
唤醒操作通常由后台线程log_write_notifier 或者log_flush_notifier异步来做,但如果推进的log write/flush还不足一个block的话,那就log_writter/flusher
自己去唤醒了。
B.
innodb_log_recent_written_size, 默认1MB
表示recent_written这个link_buf的大小,其实控制了并发往log buffer中同时拷贝的事务日志量,向前由新的日志加入,后面由log writer通过写日志向前推进,如果写的慢的话,那这个link_buf很可能用满,用户线程就得spin等待。再慢io的系统上,我们可以稍微调大这个参数
innodb_Log_recent_closed_size, 默认2MB
表示recent closed这个link_buf的大小,也是维护可以并发往flush list上插入脏页的并罚度,如果插入脏页速度慢,或者lin_buf没有及时合并推进,就会spin wait 简单说下link_buf, 这本质上是一个数组,但使用无锁的使用方式来维护lsn的推进,比如获得一个lsn开始和结束,那就 通过设置buf[start_lsn] = end_lsn的类似方式来维护lsn链,基于lsn是连续值的事实,最终必然不会出现空洞,所以在演化的过程中,可以从尾部 推进连续的lsn,头部插入新的值. 如果新插入的值超过了尾部,表示buf满了,就需要spin wait了
C.
innodb_log_wait_for_write_spin_delay,
innodb_log_wait_for_write_timeout
从8.0版本开始用户线程不再自己去写redo,而是等待后台线程去写,这两个变量控制了spin以及condition wait的timeout时间,当spin一段时间还没推进到某个想要的lsn点时,就会进入condition wait
另外两个变量
innodb_log_wait_for_flush_spin_delay
innodb_log_wait_for_flush_timeout
含义类似,但是是等待log flush到某个指定lsn
注意在实际计算过程中,最大spin次数,会考虑到cpu利用率,以及另外两个参数:
innodb_log_spin_cpu_abs_lwm
innodb_log_spin_cpu_pct_hwm
如果是等待flush操作的话,还收到参数innodb_log_wait_for_flush_spin_hwm限制,该参数控制了等待flush的时间上限,如果平均等待flush的时间超过了这个上限的话, 就没必要去spin,而是直接进入condition wait
关于spin次数的计算方式在函数 log_max_spins_when_waiting_in_user_thread 中":
函数的参数即为配置项innodb_log_wait_for_write_spin_delay或innodb_log_wait_for_flush_spin_delay值 static inline uint64_t log_max_spins_when_waiting_in_user_thread( uint64_t min_non_zero_value) { uint64_t max_spins; /* Get current cpu usage. */ const double cpu = srv_cpu_usage.utime_pct; /* Get high-watermark - when cpu usage is higher, don't spin! */ const uint32_t hwm = srv_log_spin_cpu_pct_hwm; if (srv_cpu_usage.utime_abs < srv_log_spin_cpu_abs_lwm || cpu >= hwm) { /* Don't spin because either cpu usage is too high or it's almost idle so no reason to bother. */ max_spins = 0; } else if (cpu >= hwm / 2) { /* When cpu usage is more than 50% of the hwm, use the minimum allowed number of spin rounds, not to increase cpu usage too much (risky). */ max_spins = min_non_zero_value; } else { /* When cpu usage is less than 50% of the hwm, choose maximum spin rounds in range [minimum, 10*minimum]. Smaller usage of cpu is, more spin rounds might be used. */ const double r = 1.0 * (hwm / 2 - cpu) / (hwm / 2); max_spins = static_cast(min_non_zero_value + r * min_non_zero_value * 9); } return (max_spins); }
D. 以下几个参数是后台线程等待任务时spin及condition wait timeout的值
log_writer线程:
innodb_log_writer_spin_delay,
innodb_log_writer_timeout
log_flusher线程:
innodb_ log_flusher_spin_delay
innodb_log_flusher_timeout
log_write_notifier线程:
innodb_ log_write_notifier_spin_delay
innodb_log_write_notifier_timeout
log_flush_notifier线程
innodb_log_flush_notifier_spin_delay
innodb_log_flush_notifier_timeout
log_closer线程(用于推进recent_closed这个link_buf的专用线程)
innodb_log_closer_spin_delay
innodb_log_closer_timeout
E
innodb_ log_write_max_size
表示允许一个write操作最大的字节数,默认为4kb, 这个是在推进recent_written这个link buf时计算的,个人认为这个限制太小了,可以适当调大这个参数。(然而8.0的最大写入限制还受到innodb_log_write_ahead_size限制,两者得综合起来看)
F
innodb_log_checkpoint_every
默认1000毫秒(1秒),表示至少每隔这么长时间log_checkpointer线程会去尝试做一次checkpoint. 当然是否做checkpoint还受到其他因素的影响,具体见函数 log_should_checkpoint : a) more than 1s elapsed since last checkpoint b) checkpoint age is greater than max_checkpoint_age_async c) it was requested to have greater checkpoint_lsn, and oldest_lsn allows to satisfy the request
G. 参考:
MySQL8.0.16源代码
作者:zhaiwx_yinfeng
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-06-04 11:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在Oracle中,自定义函数格式如下:
CREATE OR REPLACE FUNCTION 函数名 (  argu1 [mode1] datatype1) --  定义参数变量 RETURN 返回值类型 IS 声明部分; BEGIN 函数体; RETURN 变量; END;
函数解析:
函数的参数有3种类型:
(1)in参数类型:表示输入给函数的参数,该参数只能用于传值,不能被赋值。
(2)out参数类型:表示参数在函数中被赋值,可以传给函数调用程序,该参数只能用于赋值,不能用于传值。
(3)in out参数类型:表示参数既可以传值,也可以被赋值。

示例: create or replace function sf_score_pm( p_in_stuid in varchar2,--学号 p_in_courseid in varchar2 --课程ID ) return number is ls_pm number:=0; ls_score number:=0; begin --获取该学生的成绩 select t.score into ls_score from score t where t.stuid = p_in_stuid and t.courseid = p_in_courseid; --获取成绩比该学生高的人数 select count(1) into ls_pm from score t where t.courseid = p_in_courseid and t.score>ls_score; --得到该学生的成绩排名 ls_pm:=ls_pm+1; return ls_pm; exception when no_data_found then dbms_output.put_line('该学生的课程:'||p_in_courseid|| '的成绩在成绩表中找不到'); end;

函数查看语法: select * from user_source t where t.name='SF_SCORE_PM';
删除自定义函数语法: drop function 函数名;
数据库
2019-06-04 11:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
生物学
门 Divisio (Phylum)
纲 Classis (class)
目 Ordo (order)
科 Familia (Family)
族 Tribus (Tribe)
属 Genus (Genus)
组 Sectio (Section)
系 Series (Series)
种 Species (Species)
变种 Varietas(Variety)
变型 Forma (Form)
分类学
数据库
2019-06-04 00:31:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
今天从社区邮件看到一个这样的问题,感觉很有意思,在这分享给大家~具体如下:
问题现象:
作者有一个很老的Java应用,当时后端采用的PostgreSQL数据库版本为8.x,该系统除了正常的数据增删改查违,有一项功能是把图片存储到pgsql中,存储类行为bytea,然后再读取使用。
作者近期做了数据库的迁移,将pgsql8.x中的数据迁移到pgsql11版本上,迁移完成后,Java应用端的其他功能都正常,只有图片无法正常读取显示了,但是写入是没问题的。这个问题困扰了作者很久。
解决办法:
经过社区大神指点后,得知bytea的默认输出格式从pgsql9.x版本开始改变了,在9.x版本之前,bytea_output的默认值为escape,9.x之后,bytea_out的默认值就变成了hex。所以如果要与老版本的前端应用适配,可以修改数据库参数为escape即可解决这个问题。
数据库
2019-05-24 21:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
substring_index(str,delim,count)
str:要处理的字符串
delim:分隔符
count:计数
例子:str=www.google.com
1.count是正数,那么就是从左往右数,第N个分隔符的左边的全部内容 SELECT SUBSTRING_INDEX('www.google.com','.',1);
结果是:www
SELECT SUBSTRING_INDEX('www.google.com','.',2);
结果是:www.google

2.count是负数,那么就是从右边开始数,第N个分隔符右边的所有内容,如: SELECT SUBSTRING_INDEX('www.google.com','.',-2);
结果为:google.com
如果我呀中间的的google怎么办?
很简单的,两个方向:
从右数第二个分隔符的右边全部,再从左数的第一个分隔符的左边: SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('www.google.com','.',-2),'.',1);
结果为:google
总结:SUBSTRING_INDEX可以理解为java里面按照分隔符使用split进行分组,从所得数组的下标+1处一直到开始算起的位置的所有内容即为最终结果,如果count 不等于0,那么即使 str中没有找到delim分隔,那么返回的结果就是str本身,即只要count!=0,那么返回的最差结果也是str本身(没有找到一个delim分隔符...的情况)
例如:
(1) SELECT SUBSTRING_INDEX('www.google.com','AAA',-1);
和 SELECT SUBSTRING_INDEX('www.google.com','AAA',-99);
两者的结果都为:www.google.com
(2)在项目中利用CONCAT来做字符串合并,发现一个一个有意思的事情, SELECT CONCAT(SUBSTRING_INDEX('www.google.com','.',1),'-','AAAABB')
结果为:www-AAAABB
(3) SELECT CONCAT(SUBSTRING_INDEX('www.google.com','AAA',1),'-','AAAABB')
结果为:www.google.com-AAAABB
(4) SELECT CONCAT(SUBSTRING_INDEX('www.google.com','AAA',1)-1,'-','AAAABB')
结果为:-1-AAAABB
结果咋这样呢?发现Mysql的字符串在参与运算的时候很有意思:
以下内容摘自: https://www.jianshu.com/p/2ab2c0dc3cb5
MYSQL截取函数:
1、从左开始截取字符串
left(str, length)
说明:left(被截取字段,截取长度)
例:select left(content,200) as abstract from my_content_t
2、从右开始截取字符串
right(str, length)
说明:right(被截取字段,截取长度)
例:select right(content,200) as abstract from my_content_t
3、截取字符串
substring(str, pos)
substring(str, pos, length)
说明:substring(被截取字段,从第几位开始截取)
substring(被截取字段,从第几位开始截取,截取长度)
例:select substring(content,5) as abstract from my_content_t
select substring(content,5,200) as abstract from my_content_t
(注:如果位数是负数 如-5 则是从后倒数位数,到字符串结束或截取的长度)
4、按关键字截取字符串
substring_index(str,delim,count)
说明:substring_index(被截取字段,关键字,关键字出现的次数)
例:select substring_index("blog.jb51.net","。",2) as abstract from my_content_t
结果:blog.jb51
(注:如果关键字出现的次数是负数 如-2 则是从后倒数,到字符串结束)
函数简介:
SUBSTRING( str , pos ) , SUBSTRING( str FROM pos ) SUBSTRING( str , pos , len ) , SUBSTRING( str FROM pos FOR len )
不带有 len 参数的格式从字符串 str 返回一个子字符串,起始于位置 pos 。带有 len 参数的格式从字符串 str 返回一个长度同 len 字符相同的子字符串,起始于位置 pos 。 使用 FROM的格式为标准 SQL 语法。也可能对 pos 使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的 pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对 pos 使用一个负值。
mysql之字符串进行运算或大小比较
在mysql当中,字符串类型间进行加减乘除运算的时候,如果字符串以数字开头,则会截取字符串以数字开头的那一部分数字进行运算,如果字符串没将诶有以数字开头数字,那么就只能截取的数值为0,那么进行加减的时候结果都是0,进行乘除的时候结果都是NULL(即如果以字符串开头,则会从数字一直截取到非数字为止;如果不以数字开头,那么取0)
如下几个结果说明问题: mysql> select '1a'+'1b';//字符串分别以1开头,所以运算的时候,分别为1,结果为1+1=2 +-----------+ | '1a'+'1b' | +-----------+ | 2 | +-----------+ 1 row in set, 2 warnings (0.00 sec) mysql> select '2a'-'1b';//字符串分别以2和1开头,所以结果为2-1=1 +-----------+ | '2a'-'1b' | +-----------+ | 1 | +-----------+ 1 row in set, 2 warnings (0.00 sec) mysql> select '1a'/'2b';//字符串分别以1和2开头,所以结果为:1/2=0.5 +-----------+ | '1a'/'2b' | +-----------+ | 0.5 | +-----------+ 1 row in set, 2 warnings (0.00 sec) mysql> select 'a'/'b';//字符串分别以'a'和'b'开头,所以结果为0/0=NULL +---------+ | 'a'/'b' | +---------+ | NULL | +---------+ 1 row in set, 3 warnings (0.00 sec) mysql> select 'a' - 'b';//字符串分别以'a'和'b'开头,结果为0-0=0 +-----------+ | 'a' - 'b' | +-----------+ | 0 | +-----------+ 1 row in set, 2 warnings (0.00 sec) mysql> select 'a' + 'b';//字符串分别以'a'和'b'开头,结果为0+0=0 +-----------+ | 'a' + 'b' | +-----------+ | 0 | +-----------+ 1 row in set, 2 warnings (0.00 sec)
如果一个字符串以数字开头,后面有非数字和数字组合的话,在进行运算的时候,会省略掉非数字和数字组合那一段,也就是只会截取开头的数字 mysql> select '2015-2-1' - '2015-1-1';////字符串分别以'2015'和'2015'开头,结果为2015-2015=2015 +-------------------------+ | '2015-2-1' - '2015-1-1' | +-------------------------+ | 0 | +-------------------------+ 1 row in set, 2 warnings (0.00 sec)
以上相当于2015-2015=0
mysql当字符串进行大小比较的时候 mysql> select '2015-2-1' > '2015-1-1'; +-------------------------+ | '2015-2-1' > '2015-1-1' | +-------------------------+ | 1 | +-------------------------+ 1 row in set (0.00 sec) mysql> select '2015-2-1' < '2015-1-1'; +-------------------------+ | '2015-2-1' < '2015-1-1' | +-------------------------+ | 0 | +-------------------------+ 1 row in set (0.00 sec)
在这里非常奇怪, '2015-2-1' - '2015-1-1' = 0 ,为什么大小比较的时候会有大小之分呢?
原来对于数字与非数字混合的字符串,在进行大小比较的时候,如果两字符串长度相等,那么两字符串就会比较相同位置的字符,比较时若字符是数字,则直接比较,若字符是非数字那么会转换为ascii码进行比较,若在某位置上已经有大小之分,那么就不会再进行比较。 mysql> select '2017-03-20 15:27:49' > '2017-03-20 15:27:48'; +-----------------------------------------------+ | '2017-03-20 15:27:49' > '2017-03-20 15:27:48' | +-----------------------------------------------+ | 1 | +-----------------------------------------------+ 1 row in set (0.00 sec)
看起来像日期的字符串可以用date_format函数提取当中的年月日,看如下: mysql> select date_format('2017/03/20 15:27:49','%Y') 年,date_format('2017/03/20 15:27:49','%c') 月,date_format('2017/03/20 15:27:49','%d') 日; +------+------+------+ | 年 | 月 | 日 | +------+------+------+ | 2017 | 3 | 20 | +------+------+------+ 1 row in set (0.00 sec)
非数字字符在比较大小的时候,就例如: mysql> select 'a' < 'b'; +-----------+ | 'a' < 'b' | +-----------+ | 1 | +-----------+ 1 row in set (0.00 sec)
当中的字母会转成ascii码,再进行比较,以上是单字母字符串比较,如果是多字母数字混合字符串比较呢? mysql> select '1c' > 'bc'; +-------------+ | '1c' > 'bc' | +-------------+ | 0 | +-------------+ 1 row in set (0.00 sec) mysql> select '1yz' > 'abc999'; +------------------+ | '1yz' > 'abc999' | +------------------+ | 0 | +------------------+ 1 row in set (0.00 sec)
其实从上面的结果大概可以猜测得到,为让解释更清晰更有说服力,再看下图: mysql> select ascii('1c'),ascii('bc'); +-------------+-------------+ | ascii('1c') | ascii('bc') | +-------------+-------------+ | 49 | 98 | +-------------+-------------+ 1 row in set (0.00 sec) mysql> select ascii('1yz'),ascii('abc999'); +--------------+-----------------+ | ascii('1yz') | ascii('abc999') | +--------------+-----------------+ | 49 | 97 | +--------------+-----------------+ 1 row in set (0.00 sec)
字符串大小比较的时候,会从左向右将两个字符串第一个不相等的两个字符的ascii码的比较结果作为最终结果
有时候我们需要直接用MySQL的字符串函数截取字符,毕竟用程序截取(如PHP)还得先写个脚本连接数据库之类的,所以在这里做一个记录,希望对大家有用。
MySQL截取字符串函数
1、从左开始截取字符串
left(str, length)
说明:left(被截取字段,截取长度)
例: select left(content,200) as abstract from my_content_t
2、从右开始截取字符串
right(str, length)
说明:right(被截取字段,截取长度)
例: select right(content,200) as abstract from my_content_t
3、截取字符串
substring(str, pos)
substring(str, pos, length)
说明:substring(被截取字段,从第几位开始截取)
substring(被截取字段,从第几位开始截取,截取长度)
例: select substring(content,5) as abstract from my_content_t select substring(content,5,200) as abstract from my_content_t
(注:如果位数是负数 如-5 则是从后倒数位数,到字符串结束或截取的长度)
4、按关键字截取字符串
substring_index(str,delim,count)
说明:substring_index(被截取字段,关键字,关键字出现的次数)
例: select substring_index("blog.jb51.net",".",2) as abstract from my_content_t
结果:blog.jb51
数据库
2019-05-29 11:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 阿里妹导读:云计算大潮来袭,传统数据库市场正面临重新洗牌的情境,包括云数据库在内的一批新生力量崛起,动摇了传统数据库的垄断地位,而由云厂商主导的云原生数据库则将这种“改变”推向了高潮。
云时代的数据库将面临怎样的变革?云原生数据库有哪些独特优势?在 DTCC 2019大会上,阿里巴巴副总裁 李飞飞博士就《下一代云原生数据库技术与趋势》进行了精彩分享。
李飞飞(花名:飞刀),阿里巴巴集团副总裁,高级研究员,达摩院首席数据库科学家,阿里云智能事业群数据库产品事业部负责人,ACM 杰出科学家。
大势所趋:云数据库市场份额增速迅猛
如下图所示的是 Gartner 关于全球数据库市场份额的报告,该报告指出目前全球数据库市场份额大约为400亿美金,其中,中国数据库市场份额占比为3.7%,大约为14亿美金。
具体到数据库市场分布,传统五大数据库厂商 Oracle、Microsoft、IBM、SAP、Teradata 占比达到了80%,云数据库的份额占比接近10%,并且云数据库市场份额占比每年也在快速增长,因此, Oracle、MongoDB 等也在大力布局其在云数据库市场的竞争态势。
根据 DB-Engines 数据库市场分析显示,数据库系统正朝着多样化、多元化的方向发展,从传统的 TP 关系型数据库发展到今天的多源异构的数据库形态。目前,处于主流位置的还是大家耳熟能详的数据库系统,比如商业数据库 Oracle、SQL Server以及开源的 MySQL、PostgreSQL 等。而一些比较新的数据库系统,比如MongoDB、Redis 则开辟了一个新的赛道。数据库 License 的传统销售方式在逐渐走下坡路,而开源以及云上数据库 License 的流行程度却在不断提升。
数据库:云上应用关键的一环
正如 AWS 创始人 Jeff Bezos 所说:“The real battle will be in databases”。因为云最早是从 IaaS 做起来的,从虚拟机、存储、网络,到现在如火如荼的语音识别、计算机视觉以及机器人等智能化应用,都是基于 IaaS 的,而数据库就是连接 IaaS 与智能化应用 SaaS 最为关键的一环。从数据产生、存储到消费的各个环节,数据库都至关重要。
数据库主要包括四大板块,即 OLTP、OLAP、NoSQL 以及数据库服务和管理类工具,也是云数据库厂商发力的四个方向。对于 OLTP 而言,技术发展已经历经了40年,而如今大家还在做的一件事情就是“加10元和减10元”,也就是所谓的事务处理。当数据量变得越来越大和读写冲突的原因,对数据进行在线实时分析的需求衍生出了 OLAP。由于需要 Scale out,而数据强一致性不能够得到保证,就有了NoSQL 。而最近又出现了一个新名词—— NewSQL,这是因为 NoSQL 也有所不足,故将传统 OLTP 的 ACID 保证与 NoSQL 的 Scale out 能力进行了整合,变成了NewSQL。
数据库系统架构演进:All depends on what is shared
纵观数据库40年来的发展历史,从最早的关系型数据库时期,衍生出了 SQL、OLTP 等技术;到数据量急剧增长,需要避免读写冲突,通过 ETL、数据仓库以及 Data Cube 等技术实现了 OLAP;再到今天,面对异构多源的数据结构,从图到时序、时空到向量等,也就诞生了 NoSQL、NewSQL 等数据库,同时也出现了一些新的技术,比如 Multi-Model 和 HTAP 等。
数据库系统最为主流的架构是 Shared Memory:共享处理器内核,共享内存并且具有共享的本地磁盘,这样的单机架构属于非常主流的架构,传统的数据库厂商基本采用的也是这样的架构。
而随着互联网企业的大规模发展,如 Google、Amazon 以及阿里巴巴,大家发现原来的单机架构有很多限制,其可扩展性以及吞吐量无法满足业务发展需求,于是就衍生出了 Shared Disk/Storage 架构,即共享存储架构。也就是说数据库底层可能是分布式存储,通过利用 RDMA 这样的快速网络让上层的数据库内核看起来像是在使用本地的磁盘,但实际上是分布式存储。上面可以有多个独立计算节点,一般是一写多读,但是也可以做多写多读,这就是共享存储架构,其中比较典型的代表就是阿里云的 POLARDB 数据库。
另外一种架构是 Shared Nothing 。共享存储虽然有诸多优点,解决了很多问题,但是 RDMA 网络也存在很多的限制,比如其跨越 Switch 甚至是跨 AZ 和 Region 的时候性能都会有所损失。分布式的共享存储达到一定的节点数量之后,性能会出现一定的损耗,所以不能保证访问远程数据和访问本地数据的性能完全相同,所以共享存储的架构当扩展到十几个节点之后就达到了 scale out 扩展的上限了。此时,如果应用需要继续扩展怎么办呢?那就需要实现分布式架构了,比较典型的就是 Google Spanner,其利用原子钟技术能够实现跨数据中心的数据一致性和事务一致性。而在阿里云,基于 POLARDB 实现的分布式版本 POLARDB-X 采用的也是 Shared Nothing 架构。
这里需要注意的一点就是:Shared Nothing 和 Shared Storage 可以结合。可以在上层做 Shared Nothing,而对于下层的 Shard 分片采用 Shared Storage 架构。这样混合架构的好处在于能够减轻分出太多 Shard 的痛点问题,减少分布式事务distributed commit的 概率,因为 distributed commit 的代价非常昂贵。
总结三种架构设计,如果在 Shared Storage 架构上做到多写多读而不是一写多读,实际上也就实现了 SharedEverything 。将 Shared nothing 和 Sharedstorage 架构进行结合的 hybrid 架构应该是后续数据库系统发展方向的一个重要突破点。
云原生数据库核心四要素
上面从架构方面分析了云时代的主流数据库架构。从技术上来讲,除了架构上的不同,云原生时代还有一些不同点。
多模(Multi-model)
其一是多模(Multi-model),多模主要有两种,即北向和南向。南向表示存储结构是多种多样的,数据结构可以是结构化的也可以是非结构化的,可以是图、向量、文档等,但对于用户只提供一个 SQL 的查询接口或者 SQL-Like 的接口,这部分业界比较典型的就是各种各样的数据湖服务。而北向的多模就是存储只有一种,一般是通过 KV 存储数据形态来支持结构化、半结构化以及非结构化数据,但希望能够提供不同的查询接口,比如 SPARQL、SQL、GQL 等。业界典型的代表是微软 Azure 的CosmosDB。
数据库智能化+自动化管控平台
数据库的自治化也是非常重要的发展方向,从数据库的内核以及管控平台两个角度都有很多技术点可以做。在数据库自治化部分,阿里巴巴认为,需要做到自感知、自决策、自恢复以及自优化。自优化比较简单,就是在内核中利用机器学习的方法来进行优化。而自感知、自决策、自恢复更多的是针对管控平台的,比如如何保证实例的巡检,当出现问题后如何能够自动快速修复或者自动切换等。
新硬件: 软硬件一体化设计
云原生数据库的第三大核心点是软硬件一体化设计。数据库首先是一个系统,而系统就需要能够安全高效地使用有限的硬件资源。所以数据库系统的设计和发展一定是和硬件性能和发展紧密相关的,我们不能够面对硬件的变化而坚持旧有数据库设计不改变,比如 NVM 出来之后就可能对传统的数据库设计有一些冲击。而新硬件所带来的变化也是数据库系统设计需要考虑的。
RDMA、NVM 以及 GPU/FPGA 等新硬件或者架构的出现,对于数据库的设计都会提供新的思路。
高可用
高可用是云原生最基本的要求之一,上云的用户势必不希望业务出现中断。高可用最简单的解决方案就是冗余,可以做 Table 级别的冗余,也可以做 Partition 级别的冗余。无论是使用哪一种,基本上都是三副本,甚至更多的时候需要做四副本或者五副本,比如金融级别的高可用可能需要做两地三中心或者两地四中心。
对于高可用的多副本而言,如何保证副本之间的数据一致性?在数据库里面有一个经典的CAP理论,其理论结果是在 Consistency、Availability 和 Partition Tolerant 三者之间只能选择两个。现在大家的一般选择都是 C+P,同时对于 A 而言,通过三副本技术和分布式一致性协议,使得 A 达到6个9或者7个9,这样基本上就做到了100%的 CAP。
云原生数据库 POLARDB:极致弹性+兼容性 为海量数据和海量并发而生
前面介绍了数据库市场背景和云原生数据库的基本要素,接下来我将结合阿里云 POLARDB 以及 AnalyticDB 两款数据库系统,分享以上技术的具体落地情况。POLARDB 是阿里云的云原生数据库,目前已有非常深厚的技术积累。我们在VLDB 2018,SIGMOD 2019等国际学术会议上发表了相关论文,主要介绍存储引擎等方面的技术创新。
POLARDB 采用共享存储架构,一写多读。共享存储架构有多个优势,首先是计算和存储分离,计算节点和存储节点可以分开实现弹性缩扩容;其次,POLARDB 突破了 MySQL、PG 等数据库对于单节点规格和可扩展性的限定,能够实现 100TB 存储容量以及每个节点100万 QPS 的性能;此外,POLARDB 能够提供极致的弹性能力,备份恢复能力也有很大提升。在存储层,每个数据块都采用三副本高可用技术,同时对于 Raft 协议进行了修改,通过实现并行式的 Raft 协议保证了三副本数据块之间的数据一致性,提供了金融级高可用。POLARDB 还能做到100%兼容 MySQL 以及 PG 等数据库生态,可以帮助用户实现无感知的应用迁移。

由于底层是共享的分布式存储,PolarDB 属于 Active-Active 的架构,主节点负责写入数据,从节点负责读取数据,因此,对于进入数据库的事务而言,主备节点都处于Active 状态,其好处在于通过一份物理存储避免了在主从之间不停地做数据同步。
具体而言,POLARDB 有一个 PolarProxy,也就是前面的网关代理,下面有 POLARDB 的内核以及 PolarFS,最下面对接的是 PolarStore,利用 RDMA 网络管理底层的分布式共享存储。PolarProxy 会对客户需求做分发,将写请求分配到主节点,而对于读请求而言,则会根据负载均衡以及读节点的状态实现对于读请求的分配,这样就能够尽可能地实现资源的最大化利用以及性能的提升。
POLARDB 共享存储采用分布式+三副本。其中 Primary 节点负责写,其他节点负责读,其下层是 PolarStore,每部分都会有三副本的备份,通过分布式一致性协议保证数据一致性。这样设计的优势在于能够实现存储与计算分离,同时能够做到无锁备份,所以备份可做到秒级。
在一写多读的情况下,POLARDB 能够实现快速伸缩。举例而言,从2核 vCPU 升级到32核或者从两个节点扩展到4个节点,都能够在5分钟之内生效。存储和计算分离能够带来的另一大好处是降低成本,因为存储和计算节点可以独立地进行弹性伸缩,充分体现成本优势。
下图展示了 POLARDB 如何利用物理日志实现持续恢复。左侧是传统数据库的架构,而在 POLARDB 里面,由于采用了共享存储,因此可基本保留类似传统数据库利用物理日志进行恢复的过程,通过共享存储实现持续恢复,做事务的 Snapshot 恢复。
对比一下,如果 MySQL 做主备架构,首先需要在主库里面有一个逻辑日志和物理日志,在备库里面要重放主库的逻辑日志,然后再按照主库的方式做逻辑日志和物理日志。而在 POLARDB 里面,因为是共享存储,可直接通过一份日志实现数据恢复,备库能够直接将所需要的数据恢复出来,而不需要去重放主库的逻辑日志。
POLARDB一写多读集群的另一大优势是动态 DDL 的支持。在 MySQL 架构下,如要对数据的 Schema 进行修改,需要通过 Binlog 去 Replay 到备库,因此备库会存在Blocking 的阶段,需要一定时间 Replay 动态的 DDL。而在 POLARDB共享存储架构下,所有 Schema 信息以及 metadata 均以表的形式直接存储在存储引擎里面,只要主库改完了,那么备库的元信息也实时同步更新,因此不会存在 Blocking 的过程。
POLARDB 的 Proxy 最主要的作用就是做读写分离、负载均衡、高可用切换以及安全防护等。POLARDB 是一写多读架构,当请求进来之后,需要进行读写的判断,将写请求分发到写节点,将读请求分发到读节点上去,并且对于读请求做一定的负载均衡。这样就能保证会话的一致性,并且彻底解决了读不到最新数据的问题。
无损弹性是 POLARDB 监控的模块之一。分布式存储需要知道分配多少磁盘量 /Chunk,POLARDB 会监控未使用的 Chunk 量。比如当可用量低于30%的时候,就会在后台自动地对其进行扩容,这使得应用基本不受影响,可连续写数据。
对于云数据库 POLARDB 而言,以上技术带来的最大优势是极致的弹性。这里我们以一个具体的客户案例进行说明。如下图所示,红线部分指离线资源的消耗情况,这些成本是客户无论如何都需要付出的,而其上面的部分则是计算资源的需求。
比如客户在3、4月有新品上市,5月还有促销活动,这两个时期计算需求会非常大。如按照传统架构方式,可能需要在新品上市之前就将容量弹到更大的规模,并且保持这样的水位,到了后面的促销阶段又需要弹到更高的规格,成本非常高昂。但如果能够做到极致弹性,比如 POLARDB 的存储与计算分离,实现快速弹性扩容,那么用户就只需在蓝色方块出现之前将容量弹上去,之后再弹下来即可,这样就能大幅降低成本。
除了云原生数据库 POLARDB ,阿里云数据库团队在其他方向还有众多探索。
分布式版本 POLARDB-X : 高并发+跨域高可用 支持水平拓展
如果企业需要极致的 Scale out 能力,像阿里巴巴以及传统行业中的银行、电力等对高并发、海量数据支撑要求极高的用户,共享存储架构只能支持弹至十几个节点,肯定是不够的。因此,阿里云数据库团队也采用 Shared Nothing 做水平拓展,将Shared Nothing 与 Shared Storage 相结合,形成 POLARDB-X 。POLARDB-X 支持金融级跨可用区数据强一致, 对支持海量数据下的高并发事务处理有着极好的性能表现。目前,POLARDB-X 在阿里内部已上线应用,利用存储计算分离、硬件加速、分布式事务处理和分布式查询优化等技术,成功支持了在双11这样的场景下阿里巴巴所有业务核心链路数据库洪峰的挑战,我们后续将推出商业化版本,敬请期待。
OLAP 数据库标杆—— AnalyticDB:海量数据 实时高并发在线分析
此外在 OLAP 分析型数据库方向,阿里云数据库团队自主研发了数据库产品——AnalyticDB,在阿里云的公有云和专有云上均有售卖。AnalyticDB 拥有几大核心架构特点: 行列混存引擎,能够支持高吞吐写入和高并发查询; 支持海量数据处理,对于海量数据能实现秒级分析,完美支持多表、中文以及复杂分析; 利用向量化技术,支持结构化数据和非结构化数据的融合处理。
近日,AnalyticDB 打榜 TPC-DS,在性价比方面达到了全球第一,通过了 TPC 官方的严苛认证。同时,介绍 AnalyticDB 系统的论文即将在 VLDB 2019 会议上展现。AnalyticDB 的常用应用场景是从 OLTP 应用我们的数据传输与同步工具 DTS 至AnalyticDB 进行实时的数据分析。
自治数据库平台:智能调参上线 iBTune (individualized Buffer Tuning)
云原生数据库的特点之一是自治化,阿里云内部有个平台叫 SDDP(Self-Driving Database Platform——自治化数据库平台),SDDP 会对各个数据库实例进行实时的性能数据采集,并使用机器学习方法建模进行实时调配。
iBTune 的基本思想是,每个数据库实例都包含一个 Buffer Size,传统数据库里面的Buffer Size 是提前分配好的,不能变化。而在大型企业里,Buffer 是一个资源池,需要消耗内存,因此希望做到弹性自动调配每个实例里的 BufferSize。比如淘宝商品库的数据库实例晚上不需要那么大的 Buffer,那么就可以自动将其 Buffer Size 弹下来,到早上再自动弹上去,同时要求不影响其 RT。为了满足上述需求并进行自动Buffer 优化,阿里云数据库团队构建了 iBTune 系统,目前监控近 7000个数据库实例,通过长期运营,可平均节省20TB 内存。介绍 iBTune 项目的核心技术论文也发表在了今年的 VLDB 2019大会上。
安全上云是关键 多重加密护航数据安全
云上的数据安全是非常重要的内容,阿里云数据库团队在数据安全方面也做了大量的工作。首先,数据落盘加密,在数据存储的时候就进行加密。此外,阿里云数据库也支持 BYOK,用户可以将自己的密钥拿到云上来实现落盘加密以及传输级别的加密。未来,阿里云数据库还将在内存处理时实现全程加密,对日志实现可信验证等。
阿里云企业级数据库云服务:全方位运维 全链路布局
阿里云数据库按照工具产品、引擎产品以及运营管控的全程数据库产品分类提供服务。下图展现的是阿里云——云数据库常用链路,通过 DTS 工具将线下数据库迁移到线上,基于数据需求/分类,分发至关系型数据库、图数据库以及 AnalyticDB 等。
阿里云数据库:客户第一,一切价值来自于服务用户
目前 POLARDB 数据库的增势迅猛,已经服务于通用行业、互联网金融、游戏、教育、新零售、多媒体等多个领域的龙头企业。
而 AnalyticDB 在分析型数据库市场也有非常出众的表现,支持实时分析以及可视化应用。
基于阿里云数据库技术,阿里巴巴支持了城市大脑等一系列关键项目及云上云下的大量客户。截止目前为止,阿里云数据库已经累计支持了近40万数据库实例成功上云。
云原生是数据库的新战场,它为发展了40多年的数据库行业带来了许多令人激动的新挑战和新机遇,阿里巴巴希望与国内外数据库行业的各位技术同仁一起,将数据库技术推向更高的境界。
作者:李飞飞
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-05-29 11:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
作者:何志勇 本文转载自公众号「平安科技数据库产品团队」。 2019 年 5 月 9 日,平安科技数据库产品资深工程师何志勇在第十届数据库技术大会 DTCC 上分享了《TiDB 在平安核心系统的引入及应用》,通过对 TiDB 进行 POC 测试,详细解析如何选择适用于金融行业级别的开源分布式数据库,以及平安“财神节”活动中引入 TiDB 的全流程应用实践案例分享。本文根据演讲内容整理。
何志勇 平安科技数据库产品团队 资深工程师

一、TiDB 引入的 POC 测试
作为一名运维人员,引入一个新的数据库产品前必须要明确几点: 从业务的角度,引入的产品能否满足业务基本需求和使用场景。 从运维管理角度看,这产品必须是可运维、可管理的,并且我们需要对其相应的功能与特性,要有一个很好的了解。 产品性能稳定。
所以在我们引入前从以下六个方面分别对 TiDB 进行测试验证,其中功能与架构、配置与管理、备份与恢复都是针对我们运维管理,SQL 特性、基准测试、应用场景测试则是应对业务需求和业务场景的。
1. 功能与架构
TiDB 事务隔级别为 SI,支持 Spark 生态,支持动态扩容,跨数据中心部署。
这是 TiDB 官网最新的架构图:
从左至右看,可以通过 MySQL 或 MySQL 客户端接入 TiDB,TiDB 有 TiDB、PD、TiKV 三个组件,组件之间功能相互独立,需独立部署,分别负责计算、调度、存储功能;同时又相互协作,共同完成用户请求处理。在 TiKV 层各节点是使用 Raft 协议保证节点间数据的一致性,同时它还提供 Spark 接口供大数据分析。
从上往下看,可通过 Data Miaration 工具从 MySQL 迁移到 TiDB,同时提供备份恢复功能、内部性能监控监测及诊断、支持容器化部署。
TiDB 从架构及生态上基本上具备了传统数据库应有的功能。
2. SQL 特性
兼容 mysql 语法,2.0 版本不支持窗口函数、分区表、视图、trigger 等。

3. 配置与管理
支持在线 DDL,2.0 只支持串行的 DDL、不支持并发,在优化器上支持 RBO 与 CBO,能对单会话进行管理,可以支持复杂的 SQL。
4. 备份与恢复
备份恢复工具均为开源,支持多线程备份恢复,当前版本不支持物理备份,loader 恢复时间偏长。
5. 基准测试
TiDB 在单条 SQL 的性能较好,高并发场景下性能较稳定,但 DML 事务大小有限制。


6. 应用场景测试
支持标量子查询,能支持非常复杂的查询,查询引擎可朔性强。

这个应用场景是我们的产险的实际分析场景,表数据量不大但是 SQL 较为复杂,是典型的星型查询。在 Oracle 用了 134 秒,但是 TiDB 用了 50 分钟,我们觉得很诧异,与 TiDB 的同事咨询后,他们通过现场支持我们优化底层代码后 34 秒可以跑出来。
二、“财神节”活动中 TiDB 的应用实战
“财神节”是中国平安综合性年度线上金融狂欢节。2019 年平安集团“财神节”活动于 1 月 8 日正式启动,涉及寿险、产险、银行、养老险、健康险、普惠、证券、基金、健康互联、陆金所、壹钱包、互娱、不动产等多个领域,活动参与的 BU 数量与推广的力度是历年之最。单日成交额超过 1000 亿,在单日交易额破千亿背后是几百个后台数据库实例的运维保障。
我们看下活动业务场景的特点: 参与门槛低 :暖宝保这个业务保费价格低至 19.9,所以人人都可以参与。 我们的推广力度很大 :以微服务的方式对接如平安健康、好福利、平安银行、陆金所等所有 APP 端,同时配合各种合作伙伴的宣传。 典型的互联网活动形式 : 如秒杀、红包雨,所以对数据库的要求是高并发、低延迟、高响应、高可用,2-5 年在线数据存储量预计达到 20~50TB,而这些只是预估,有可能远远大于以上评估值。
平安在用的开源数据库有很多,那在这么多数据库中,我们选择什么数据库呢?
综合对比考量最终我们选择 TiDB,在选择的同时也面临着挑战: 时间紧迫
2018 年 12 月 17 日~2019 年 1 月 7 日,20 天时间内完成开发测试到生产上线,时间短,风险大 开发零使用经验
现有开发大都是基于传统 Oracle 保险业务,对于 TiDB 没有使用经验 并发量与扩容
互联网业务并发需求前期不可完全需求,前期不能很好的以实际压力进行测试,与资源准备 DB 运维管理
TiDB 还处于生产落地阶段,一类系统尚未使用过 TiDB,没有大规模应用运维经验
基于以上挑战,我们在 9 台 PC 服务器上做了验证测试,测试工具是 jmeter,TiKV 节点数我们是逐步增加的,具体的测试过程如下:



总结一下,就是: TiDB 吞吐 :在 select 中即 point select,TiDB 的吞吐比较好。 弹性扩容 :在 insert 场景下随着节点数的增加,TPS 也会相应的增加,每增加 3 个节点 TPS 可提升 12%~20% 左右,同时在相同 TiKV 节点数下,TPS 与响应时间,此消彼长。 批量提交性能尤佳 :业务中一个保单需要同时写 7 个表,7 个表同时 commit 比单表 commit TPS 高,相同 TPS 场景下延迟更小。 初始化 region 分裂耗时长 :因在测试时没有预热数据(表为空表),对空表写入前几分钟,响应时间会比较大,约 5~8 分钟后响应时间趋于稳定。在前几分钟内响应时间大,是因为每个表初始化完都是一个 region,大量 insert 进来后需要进行分裂,消耗时间比较大。 Raftstore cpu 高问题 :由于 Raftstore 还是单线程,测试中从监控指标看到 CPU 达到瓶颈是raftrestore 线程。 TiKV 性能中的“木桶原理” :TiKV 中一个节点的写入性能变慢会影响到整个集群的 TPS 与响应时间。
上线时我们做了以下两方面改善:
1. 优化表的定义与索引
表定义:不使用自增长列(自增长的 rowid)作为主键,避免大量 INSERT 时把数据集中写入单个 Region,造成写入热点。
索引:使用有实际含义的列作为主键,同时减少表不必要的索引,以加快写入的速度。
2. 对表的 region 进行强制分裂
查找表对应的 region:curl http://$tidb_ip:$status_port /tables/$schema/$table_name/regions
使用 pd-ctl 工具 split 对应表的 region:operator add split-region $region_id
打散表的隐式 id,打散表的数据分布:alter table $table_name shard_row_id_bits=6;
我们使用了 25 台机器,后面还临时准备了 10 台机器去应对高并发的不时之需。
在使用过程中遇到如下问题:
(1) 2.0.10 版本下 in 不能下推到表过渡问题
大家看到我们两个相同的表结构,同时写入一些数据,在两个表进行关联的时候,发现过滤条件 t1.id=1 时,上面那个执行计划可以下推到两个表进行过滤,两个表可以完全精准的把数据取出来,但是下面把等号后改成 in 的时候,对 t2 表进行全表扫描,如果 t2 表数据量很大时就会很慢,这是 TiDB 的一个 bug,解决方案就是不要用 in, 在 2.1 版本修复了这个 bug。
(2) 2.0.10 下时区设置导致客户端不能连
我们在跑命令的时候没有问题,并且结果是可以的,但是跑完后就断掉了,从后台看也是有问题的,重启 TiDB 组件也不行,后来找到代码我们发现这是一个 bug。
原因 :这个 bug 会在你连接时 check 这个时区,导致用户不能连接。
解决办法 :我们找研发同事重新编译一个 tidb-server 登入服务器,把时区设置为正确的,然后使用最初的 TiDB 组件登录, 2.1 版本后这个 bug 修复。
(3) Spring 框架下 TiDB 事务
这个问题是比较重要的问题,有个产品需要生成一个唯一的保单号,业务是批量生成的,当时在 TiDB 中我们建了一个表,表中只有一条数据,但是我们发现会有重复保单号出来。
原因 :TiDB 使用乐观事务模型,在高并发执行 Update 语句对同一条记录更新时,不同事务拿的版本值可能是相同的,由于不同事务只有在提交时,才会检查冲突,而不是像 Oracle、MySQL、PG 那样,使用锁机制来实现对一记录的串行化更改。
解决办法 :Spring 开发框架下,对事务的管理是使用注解式的,无法捕获到 TiDB commit 时的返回状态。因此需要将 spring 注解式事务改成编程式事务,并对 commit 状态进行捕获,根据状态来决定是重试机制,具体步骤: 利用 redis 实现分布式锁,执行 SQL。 捕获事务 commit 状态,并判断更新成功还是失败: 失败:影响行数为 0 || 影响行数为 1 && commit 时出现异常。 成功:影响行数为 1 && commit 时无异常。
数据库
2019-05-29 10:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
加法 select sysdate,add_months(sysdate,12) from dual; --加1年
select sysdate,add_months(sysdate,1) from dual; --加1月
select sysdate,to_char(sysdate+7,'yyyy-mm-dd HH24:MI:SS') from dual; --加1星期
select sysdate,to_char(sysdate+1,'yyyy-mm-dd HH24:MI:SS') from dual; --加1天
select sysdate,to_char(sysdate+1/24,'yyyy-mm-dd HH24:MI:SS') from dual; --加1小时
select sysdate,to_char(sysdate+1/24/60,'yyyy-mm-dd HH24:MI:SS') from dual; --加1分钟
select sysdate,to_char(sysdate+1/24/60/60,'yyyy-mm-dd HH24:MI:SS') from dual; --加1秒
减法 select sysdate,add_months(sysdate,-12) from dual; --减1年 select sysdate,add_months(sysdate,-1) from dual; --减1月 select sysdate,to_char(sysdate-7,'yyyy-mm-dd HH24:MI:SS') from dual; --减1星期 select sysdate,to_char(sysdate-1,'yyyy-mm-dd HH24:MI:SS') from dual; --减1天 select sysdate,to_char(sysdate-1/24,'yyyy-mm-dd HH24:MI:SS') from dual; --减1小时 select sysdate,to_char(sysdate-1/24/60,'yyyy-mm-dd HH24:MI:SS') from dual; --减1分钟 select sysdate,to_char(sysdate-1/24/60/60,'yyyy-mm-dd HH24:MI:SS') from dual; --减1秒
ORACLE时间函数(SYSDATE)简析 1:取得当前日期是本月的第几周 SQL> select to_char(sysdate,'YYYYMMDD W HH24:MI:SS') from dual; TO_CHAR(SYSDATE,'YY ------------------- 20030327 4 18:16:09 SQL> select to_char(sysdate,'W') from dual; T - 4
2:取得当前日期是一个星期中的第几天,注意星期日是第一天 SQL> select sysdate,to_char(sysdate,'D') from dual; SYSDATE T --------- - 27-MAR-03 5
完整请参考: https://blog.csdn.net/wulex/article/details/79294363
数据库
2019-05-28 09:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
介绍
如今使用移动互联网的年轻人开始越来越多使用短视频展示自我,而流媒体则是支撑在线视频播放的核心技术。当我们开始构建流媒体站点时,往往面临最大的难题在于大量媒体音视频文件所占用的海量磁盘空间。譬如说,一个标准高清短视频可能需要30-50MB的存储空间,那么存储百万短视频的系统就需要几十TB的存储。而如果我们更进一步需要播放高清电影等视频内容,每个电影需要大概2GB左右的空间,十万部各种电影剧集则需要200TB的存储容量。

为了满足如此海量的音视频文件存储需求,大部分流媒体服务器使用外接NAS甚至分布式文件系统进行PB级海量数据存储。

通过阅读本文,用户可以了解到如何使用SequoiaDB巨杉数据库的分布式文件系统功能,为开源流媒体服务器Emby构建后端可弹性扩展的分布式存储。

Emby是一款被广泛使用的流媒体服务器,允许用户简单一键式部署自己的视频播放系统,将家庭内部或企业存储的图片、音视频文件进行统一管理,并能够在几乎全部设备上在线播放观看。

用户可以在Emby的服务端设置服务器路径指定其所访问的文件系统。一般来说,使用PC机或NAS设备内置的磁盘存储容量有限,因此如果希望将Emby后端的存储使用分布式文件系统替换传统内置盘,可以使用SequoiaDB所提供的分布式文件系统SequoiaFS,提供用户近乎无限可弹性扩展存储容量。
安装SequoiaDB
本文使用Linux Ubuntu Server 18.10作为服务器,SequoiaDB巨杉数据库版本为3.2。

本教程默认使用sudo用户名密码为“sequoiadb:sequoiadb”,默认home路径为/home/sequoiadb。

对于使用CentOS等其他Linux版本的用户,本文所描述的流程可能略有不同,需要根据实际情况自行调整。

更多安装细节可以查看SequoiaDB信息中心
下载并安装SequoiaDB巨杉数据库
$ wget http://cdn.sequoiadb.com/images/sequoiadb/x86_64/sequoiadb-3.2-linux_x86_64.tar.gz
$ tar -zxvf sequoiadb-3.2-linux_x86_64.tar.gz
$ cd sequoiadb-3.2/
$ sudo ./setup.sh
之后一直回车确认各个默认参数即可。
使用数据库实例用户创建默认实例
# 默认密码使用“sdbadmin”
$ sudo su sdbadmin
$ /opt/sequoiadb/tools/deploy/quickDeploy.sh
使用root用户创建SequoiaFS文件系统
# 确认fuse内核组件被安装,且版本大于2.9.4。
$ which fusermount
/bin/fusermount
$ fusermount -V
fusermount version: 2.9.8

# 创建名为Nas.Storage的集合容器
$ /opt/sequoiadb/bin/sdb "db=new Sdb(); db.createCS('Nas'); db.Nas.createCL('Storage');"

# 创建mountpoint挂载点目录
$ mkdir -p /opt/sequoiadb/mountpoint

# 退出sdbadmin用户,回到默认sequoiadb用户
$ exit

# 密码默认为“sequoiadb”
$ sudo ln -s /opt/sequoiadb/bin/sequoiafs /usr/local/bin/sequoiafs

# 为fuse内核开启多用户访问权限
$ sudo sed -i "s/\#user_allow_other/user_allow_other/g" /etc/fuse.conf

# 创建sequoiafs所需的目录与空配置文件
$ sudo mkdir -p /opt/sequoiafs/conf/NasStorage/
$ sudo mkdir -p /opt/sequoiafs/log/NasStorage/
$ sudo touch /opt/sequoiafs/conf/NasStorage/sequoiafs.conf
$ sudo chown -R sdbadmin:sdbadmin_group /opt/sequoiafs

# 切换回数据库实例用户,密码默认为“sdbadmin”
$ sudo su sdbadmin

# 创建sequoiafs文件系统
$ sequoiafs /opt/sequoiadb/mountpoint -i localhost:11810 -l Nas.Storage --autocreate -c /opt/sequoiafs/conf/NasStorage/ --diagpath /opt/sequoiafs/log/NasStorage/ -o big_writes -o auto_unmount -o max_write=131072 -o max_read=131072 -o allow_other

# 检查文件系统加载正确
$ mount
sequoiafs on /opt/sequoiadb/mountpoint type fuse.sequoiafs (rw,nosuid,nodev,relatime,user_id=1001,group_id=1001,allow_other,max_read=131072)
安装Emby
本教程使用Emby版本为4.1.1。

1)登录Emby官网 https://emby.media 下载安装包



或登录sequoiadb用户,使用wget下载安装包
$ wget https://github.com/MediaBrowser/Emby.Releases/releases/download/4.1.1.0/emby-server-deb_4.1.1.0_amd64.deb

2)安装Emby Server
# 密码默认使用“sequoiadb”
$ sudo dpkg -i emby-server-deb_4.1.1.0_amd64.deb
打开浏览器,登录Linux服务器8096端口

4)按照向导进行配置,用户名密码建议均使用sdbadmin

5)在添加媒体页面添加SequoiaFS挂载点作为外接分布式文件系统

6)在添加媒体库页面,内容类型选择Movies,输入显示名后点击目录旁边的“+”号

7)使用 /opt/sequoiadb/mountpoint 作为分布式文件系统挂载点路径,并点击OK

8)点击OK进入下一步配置

9)按照向导完成后续配置

上传并使用流媒体服务器
1)将电影视频拷贝入 /opt/sequoiadb/mountpoint 目录
sftp> put YourName.mp4
Uploading YourName.mp4 to /opt/sequoiadb/mountpoint/YourName.mp4
sftp> put 教父BD双语双字.mkv
Uploading 教父BD双语双字.mkv to /opt/sequoiadb/mountpoint/教父BD双语双字.mkv

# 文件系统中通过ls可以看到文件大小
$ pwd
/opt/sequoiadb/mountpoint
$ ls -la
total 3978136
drwxr-xr-x 2 sdbadmin sdbadmin_group 4096 Jan 1 1970 .
drwxr-xr-x 23 sdbadmin sdbadmin_group 4096 May 15 10:13 ..
-rw-r--r-- 1 sdbadmin sdbadmin_group 761962089 May 15 11:03 YourName.mp4
-rw-r--r-- 1 sdbadmin sdbadmin_group 3311639424 May 15 11:12 教父BD双语双字.mkv

2)在Emby首页的媒体库点击“刷新元数据”进行后台元数据同步

进入电影库可以看到上传的电影

3)点击电影可以在线播放

结论
SequoiaDB巨杉数据库作为一款分布式数据库,提供包括结构化SQL、与非结构化文件系统和对象存储的机制。
使用SequoiaDB的非结构化存储能力,用户可以轻松基于分布式文件系统接口构建多媒体应用程序,并保持与传统Posix文件系统的完整兼容。
本文向读者展示了如何通过SequoiaDB的文件系统接口能力构建与Posix文件系统兼容的分布式文件系统,同时搭建Emby流媒体服务器。通过使用基于SequoiaDB的分布式文件系统能力,流媒体服务器能够使后台的存储空间得到近无限的弹性扩展能力。
数据库
2019-05-27 17:58:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在项目中,遇到了一个存储文本的字段, 页面上展示的时候, 是一个查询字段
之前用的 的是 ilike 这样的查询方法, 在数据量过100万的时候, 查询基本没有效率.
例如 " where content ilike '%more%';
通过学习pg的手册, 12 章节中的内容,
尝试了如下2中方法:
SELECT 'a fat cat sat on a more and ate a fat rat' :: tsvector @@ 'more' :: tsquery; 这种返回的结果为 t, ①
SELECT to_tsvector('dfsjalfjsl more dflasjfl top') @@ plainto_tsquery(' more '); 这种返回的结果为f, ②
特别注意如上的写法,经过大量数据的测试下, 使用了① 方法,
如果要写在 where 条件中,
例如 : wher content::tsvector@@'more'::tsquery
说明 : content 这个是你的文本存储的字段, 大概意思就是, 将字段做一次数据上的处理, tsvector 是那个做数据处理的函数,
'more' 是你要搜索的关键词, 这个没啥好解释的

使用的场景 :
1 ilike 比较适合模糊的匹配, 而本文提供的方法是一种比较精确的, 欢迎大家去测试
2 效率的问题, ilike 在数据量大的情况, 查询速度不是一般慢,数据量过100万的时候, 而本文提供的方法则速度很快,

暂时还没有测试出有没有其他未知的隐患, 欢迎大家来信交流学习.
由于没有经过大量的测试,导致本文中以上的结论失效了,
在实际的项目中, 我用了 where to_tsvector(content)@@to_tsquery('more');
content 为要搜索 的表字段, more 为搜索的关键词,
数据库
2019-05-27 16:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
作者:张文博
Kubernetes (K8s)是一个开源容器编排系统,可自动执行应用程序部署、扩展和管理。它是云原生世界的操作系统。 K8s 或操作系统中的任何缺陷都可能使用户进程存在风险。作为 PingCAP EE(效率工程)团队,我们在 K8s 中测试 TiDB Operator (一个创建和管理 TiDB 集群的工具)时,发现了两个 Linux 内核错误。这些错误已经困扰我们很长一段时间,并没有在整个 K8s 社区中彻底修复。
经过广泛的调查和诊断,我们已经确定了处理这些问题的方法。在这篇文章中,我们将与大家分享这些解决方法。不过,尽管这些方法很有用,但我们认为这只是权宜之策,相信未来会有更优雅的解决方案,也期望 K8s 社区、RHEL 和 CentOS 可以在不久的将来彻底修复这些问题。
Bug #1: 诊断修复不稳定的 Kmem Accounting
关键词:SLUB: Unable to allocate memory on node -1
社区相关 Issue: https://github.com/kubernetes/kubernetes/issues/61937 https://github.com/opencontainers/runc/issues/1725 https://support.mesosphere.com/s/article/Critical-Issue-KMEM-MSPH-2018-0006
问题起源
薛定谔平台是我司开发的基于 K8s 建立的一套自动化测试框架,提供各种 Chaos 能力,同时也提供自动化的 Bench 测试,各类异常监控、告警以及自动输出测试报告等功能。我们发现 TiKV 在薛定谔平台上做 OLTP 测试时偶尔会发生 I/O 性能抖动,但从下面几项来看未发现异常: TiKV 和 RocksDB 的日志 CPU 使用率 内存和磁盘等负载信息
只能偶尔看到 dmesg 命令执行的结果中包含一些 “SLUB: Unable to allocate memory on node -1” 信息。
问题分析
我们使用 perf-tools 中的 funcslower trace 来执行较慢的内核函数并调整内核参数 hung_task_timeout_secs 阈值,抓取到了一些 TiKV 执行写操作时的内核路径信息:

从上图的信息中可以看到 I/O 抖动和文件系统执行 writepage 有关。同时捕获到性能抖动的前后,在 node 内存资源充足的情况下, dmesg 返回的结果也会出现大量 “SLUB: Unable to allocate memory on node -1” 的信息。
从 hung_task 输出的 call stack 信息结合内核代码发现,内核在执行 bvec_alloc 函数分配 bio_vec 对象时,会先尝试通过 kmem_cache_alloc 进行分配, kmem_cache_alloc 失败后,再进行 fallback 尝试从 mempool 中进行分配,而在 mempool 内部会先尝试执行 pool->alloc 回调进行分配, pool->alloc 分配失败后,内核会将进程设置为不可中断状态并放入等待队列中进行等待,当其他进程向 mempool 归还内存或定时器超时(5s) 后,进程调度器会唤醒该进程进行重试 ,这个等待时间和我们业务监控的抖动延迟相符。
但是我们在创建 Docker 容器时,并没有设置 kmem limit,为什么还会有 kmem 不足的问题呢?为了确定 kmem limit 是否被设置,我们进入 cgroup memory controller 对容器的 kmem 信息进行查看,发现 kmem 的统计信息被开启了, 但 limit 值设置的非常大。
我们已知 kmem accounting 在 RHEL 3.10 版本内核上是不稳定的,因此怀疑 SLUB 分配失败是由内核 bug 引起的,搜索 kernel patch 信息我们发现确实是内核 bug, 在社区高版本内核中已修复:
slub: make dead caches discard free slabs immediately
同时还有一个 namespace 泄漏问题也和 kmem accounting 有关:
mm: memcontrol: fix cgroup creation failure after many small jobs
那么是谁开启了 kmem accounting 功能呢?我们使用 bcc 中的 opensnoop 工具对 kmem 配置文件进行监控,捕获到修改者 runc 。从 K8s 代码上可以确认是 K8s 依赖的 runc 项目默认开启了 kmem accounting。
解决方案
通过上述分析,我们要么升级到高版本内核,要么在启动容器的时候禁用 kmem accounting 功能,目前 runc 已提供条件编译选项,可以通过 Build Tags 来禁用 kmem accounting,关闭后我们测试发现抖动情况消失了,namespace 泄漏问题和 SLUB 分配失败的问题也消失了。
操作步骤
我们需要在 kubelet 和 docker 上都将 kmem account 功能关闭。 kubelet 需要重新编译,不同的版本有不同的方式。
如果 kubelet 版本是 v1.14 及以上,则可以通过在编译 kubelet 的时候加上 Build Tags 来关闭 kmem account: $ git clone --branch v1.14.1 --single-branch --depth 1 [https://github.com/kubernetes/kubernetes](https://github.com/kubernetes/kubernetes) $ cd kubernetes $ KUBE_GIT_VERSION=v1.14.1 ./build/run.sh make kubelet GOFLAGS="-tags=nokmem"
但如果 kubelet 版本是 v1.13 及以下,则无法通过在编译 kubelet 的时候加 Build Tags 来关闭,需要重新编译 kubelet,步骤如下。
首先下载 Kubernetes 代码: $ git clone --branch v1.12.8 --single-branch --depth 1 https://github.com/kubernetes/kubernetes $ cd kubernetes
然后手动将开启 kmem account 功能的 两个函数 替换成 下面这样 : func EnableKernelMemoryAccounting(path string) error { return nil } func setKernelMemory(path string, kernelMemoryLimit int64) error { return nil }
之后重新编译 kubelet: $ KUBE_GIT_VERSION=v1.12.8 ./build/run.sh make kubelet
编译好的 kubelet 在 ./_output/dockerized/bin/$GOOS/$GOARCH/kubelet 中。 同时需要升级 docker-ce 到 18.09.1 以上,此版本 docker 已经将 runc 的 kmem account 功能关闭。 最后需要重启机器。
验证方法是查看新创建的 pod 的所有 container 已关闭 kmem,如果为下面结果则已关闭: $ cat /sys/fs/cgroup/memory/kubepods/burstable/pod//memory.kmem.slabinfo cat: memory.kmem.slabinfo: Input/output error
Bug #2:诊断修复网络设备引用计数泄漏问题
关键词:kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1
社区相关 Issue: https://github.com/kubernetes/kubernetes/issues/64743 https://github.com/projectcalico/calico/issues/1109 https://github.com/moby/moby/issues/5618
问题起源
我们的薛定谔分布式测试集群运行一段时间后,经常会持续出现“kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1” 问题,并会导致多个进程进入不可中断状态,只能通过重启服务器来解决。
问题分析
通过使用 crash 工具对 vmcore 进行分析,我们发现内核线程阻塞在 netdev_wait_allrefs 函数,无限循环等待 dev->refcnt 降为 0。由于 pod 已经释放了,因此怀疑是引用计数泄漏问题。我们查找 K8s issue 后发现问题出在内核上,但这个问题没有简单的稳定可靠复现方法,且在社区高版本内核上依然会出现这个问题。
为避免每次出现问题都需要重启服务器,我们开发一个内核模块,当发现 net_device 引用计数已泄漏时,将引用计数清 0 后移除此内核模块(避免误删除其他非引用计数泄漏的网卡)。为了避免每次手动清理,我们写了一个监控脚本,周期性自动执行这个操作。但此方案仍然存在缺陷: 引用计数的泄漏和监控发现之间存在一定的延迟,在这段延迟中 K8s 系统可能会出现其他问题; 在内核模块中很难判断是否是引用计数泄漏, netdev_wait_allrefs 会通过 Notification Chains 向所有的消息订阅者不断重试发布 NETDEV_UNREGISTER 和 NETDEV_UNREGISTER_FINAL 消息,而经过 trace 发现消息的订阅者多达 22 个,而去弄清这 22 个订阅者注册的每个回调函数的处理逻辑来判断是否有办法避免误判也不是一件简单的事。
解决方案
在我们准备深入到每个订阅者注册的回调函数逻辑的同时,我们也在持续关注 kernel patch 和 RHEL 的进展,发现 RHEL 的 solutions:3659011 有了一个更新,提到 upstream 提交的一个 patch:
route: set the deleted fnhe fnhe_daddr to 0 in ip_del_fnhe to fix a race
在尝试以 hotfix 的方式为内核打上此补丁后,我们持续测试了 1 周,问题没有再复现。我们向 RHEL 反馈测试信息,得知他们已经开始对此 patch 进行 backport。
操作步骤
推荐内核版本 Centos 7.6 kernel-3.10.0-957 及以上。 安装 kpatch 及 kpatch-build 依赖: UNAME=$(uname -r) sudo yum install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel sudo yum install pesign yum-utils zlib-devel \ binutils-devel newt-devel python-devel perl-ExtUtils-Embed \ audit-libs audit-libs-devel numactl-devel pciutils-devel bison # enable CentOS 7 debug repo sudo yum-config-manager --enable debug sudo yum-builddep kernel-${UNAME%.*} sudo debuginfo-install kernel-${UNAME%.*} # optional, but highly recommended - enable EPEL 7 sudo yum install ccache ccache --max-size=5G 安装 kpatch 及 kpatch-build: git clone https://github.com/dynup/kpatch && cd kpatch make sudo make install systemctl enable kpatch 下载并构建热补丁内核模块: curl -SOL https://raw.githubusercontent.com/pingcap/kdt/master/kpatchs/route.patch kpatch-build -t vmlinux route.patch (编译生成内核模块) mkdir -p /var/lib/kpatch/${UNAME} cp -a livepatch-route.ko /var/lib/kpatch/${UNAME} systemctl restart kpatch (Loads the kernel module) kpatch list (Checks the loaded module)
总结
虽然我们修复了这些内核错误,但是未来应该会有更好的解决方案。对于 Bug#1,我们希望 K8s 社区可以为 kubelet 提供一个参数,以允许用户禁用或启用 kmem account 功能。对于 Bug#2,最佳解决方案是由 RHEL 和 CentOS 修复内核错误,希望 TiDB 用户将 CentOS 升级到新版后,不必再担心这个问题。
数据库
2019-05-27 13:38:00