数据专栏

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

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
环境:主库 WinServer2008R2 数据库:SqlServer2008r2 服务器名:Sql-master
从库 WinServer2008R2 数据库:SqlServer2008r2 服务器名:Sql-slave
先检查服务名称和主机名是否相同
select @ @servername
select serverproperty('servername')
不同可以通过执行一下SQL查询是否相同
if serverproperty('servername') <> @ @servername
begin
declare @server sysname
set @server = @ @servername
exec sp_dropserver @server = @server
set @server = cast(serverproperty('servername') as sysname)
exec sp_addserver @server = @server , @local = 'LOCAL'
End
执行后一定要重启SQL服务才能生效!
一. 主从服务器上新建sqlbackup用户,隶属于administrators组 ,删除users组

二. 主从上设置:打开控制面板-->网络和 Internet-->网络和共享中心-->高级共享设置
在主从库上分别创建用于存放主从备份日志文件的共享文件夹db_backup

删除everyone用户,添加sqlback

分别在主从数据库服务器上将SQLServer服务和SQLServer代理服务的“登录身为”sqladmin用户且启动模式为:自动

连接到本地的主数据库服务器上,此时应确保下面的“用户名”中的用户具有控制该SQL Server服务器的权限


到从库上测试是否能访问主库的网络共享文件夹DB_Backup文件夹
主库上设置
确认“计划类型”为重复执行,为测试效果明显,设置为15秒执行一次作业计划。最后确认“持续时间”,根据自己4需要设置,如果一直备份的话,可以设置为“无结束日期” 然后确认保存

右击数据库属性界面的“辅助数据库”中的“添加”按钮,打开“辅助数据库设置”窗口

要保证主库从库的sqlbackup密码一致
如果浏览不到从库,需要从库启动SQL Server Browser服务



设置完之后点击确定按钮,在数据库属性配置界面将配置好的脚本保存到本地

从库上查询同步作业是否成功

验证主从同步
主库新建个test表,从库查询此表是否复制过来

到此同步已成功
数据库
2019-03-04 17:20:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
在MySQL8.0之前的版本中,由于架构的原因,mysql在server层使用统一的frm文件来存储表元数据信息,这个信息能够被不同的存储引擎识别。而实际上innodb本身也存储有元数据信息。这给ddl带来了一定的挑战,因为这种架构无法做到ddl的原子化,我们在线上经常能够看到数据目录下遗留的临时文件,或者类似server层和innodb层列个数不一致之类的错误。甚至某些ddl可能还遗留元数据在innodb内,而丢失了frm,导致无法重建表…..(我们为了解决这个问题,实现了一个叫drop table force的功能,去强制做清理….)
(以下所有的讨论都假定使用InnoDB存储引擎)
到了8.0版本,我们知道所有的元数据已经统一用InnoDB来进行管理,这就给实现原子ddl带来了可能,几乎所有的对innodb表,存储过程,触发器,视图或者UDF的操作,都能做到原子化: - 元数据修改,binlog以及innodb的操作都放在一个事务中 - 增加了一个内部隐藏的系统表`mysql.innodb_ddl_log`,ddl操作被记录到这个表中,注意对该表的操作产生的redo会fsync到磁盘上,而不会考虑innodb_flush_log_at_trx_commit的配置。当崩溃重启时,会根据事务是否提交来决定通过这张表的记录去回滚或者执行ddl操作 - 增加了一个post-ddl的阶段,这也是ddl的最后一个阶段,会去:1. 真正的物理删除或重命名文件; 2. 删除innodb_ddl_log中的记录项; 3.对于一些ddl操作还会去更新其动态元数据信息(存储在`mysql.innodb_dynamic_metadata`,例如corrupt flag, auto_inc值等) - 一个正常运行的ddl结束后,其ddl log也应该被清理,如果这中间崩溃了,重启时会去尝试重放:1.如果已经走到最后一个ddl阶段的(commit之后),就replay ddl log,把ddl完成掉;2. 如果处于某个中间态,则回滚ddl
由于引入了atomic ddl, 有些ddl操作的行为也发生了变化: - DROP TABLE: 在之前的版本中,一个drop table语句中如果要删多个表,比如t1,t2, t2不存在时,t1会被删除。但在8.0中,t1和t2都不会被删除,而是抛出错误。因此要注意5.7->8.0的复制问题 (DROP VIEW, CREATE USER也有类似的问题) - DROP DATABASE: 修改元数据和ddl_log先提交事务,而真正的物理删除数据文件放在最后,因此如果在删除文件时崩溃,重启时会根据ddl_log继续执行drop database
测试:
MySQL很贴心的加了一个选项 innodb_print_ddl_logs ,打开后我们可以从错误日志看到对应的ddl log,下面我们通过这个来看下一些典型ddl的过程 root@(none) 11:12:19>SET GLOBAL innodb_print_ddl_logs = 1; Query OK, 0 rows affected (0.00 sec) root@(none) 11:12:22>SET GLOBAL log_error_verbosity = 3; Query OK, 0 rows affected (0.00 sec)
CREATE DATABASE mysql> CREATE DATABASE test; Query OK, 1 row affected (0.02 sec)
创建数据库语句没有写log_ddl,可能觉得这不是高频操作,如果创建database的过程中失败了,重启后可能需要手动删除目录。
CREATE TABLE mysql> USE test; Database changed mysql> CREATE TABLE t1 (a INT PRIMARY KEY, b INT); Query OK, 0 rows affected (0.06 sec) [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=428, thread_id=7, space_id=76, old_file_path=./test/t1.ibd] [InnoDB] DDL log delete : by id 428 [InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=429, thread_id=7, table_id=1102, new_file_path=test/t1] [InnoDB] DDL log delete : by id 429 [InnoDB] DDL log insert : [DDL record: FREE, id=430, thread_id=7, space_id=76, index_id=190, page_no=4] [InnoDB] DDL log delete : by id 430 [InnoDB] DDL log post ddl : begin for thread id : 7 InnoDB] DDL log post ddl : end for thread id : 7
从日志来看有三类操作,实际上描述了如果操作失败需要进行的三项逆向操作:删除数据文件,释放内存中的数据词典信息,删除索引btree。在创建表之前,这些数据被写入到ddl_log中,在创建完表并commit后,再从ddl log中删除这些记录。
另外上述日志中还有 DDL log delete 日志,其实在每次写入ddl log时是单独事务提交的,但在提交之后,会使用当前事务执行一条delete操作,直到操作结束了才会提交。
加列(instant) mysql> ALTER TABLE t1 ADD COLUMN c INT; Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0 [InnoDB] DDL log post ddl : begin for thread id : 7 [InnoDB] DDL log post ddl : end for thread id : 7
注意这里执行的是Instant ddl, 这是8.0.13新支持的特性,加列操作可以只修改元数据,因此从ddl log中无需记录数据
删列 mysql> ALTER TABLE t1 DROP COLUMN c; Query OK, 0 rows affected (2.77 sec) Records: 0 Duplicates: 0 Warnings: 0 [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=487, thread_id=7, space_id=83, old_file_path=./test/#sql-ib1108-1917598001.ibd] [InnoDB] DDL log delete : by id 487 [InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=488, thread_id=7, table_id=1109, new_file_path=test/#sql-ib1108-1917598001] [InnoDB] DDL log delete : by id 488 [InnoDB] DDL log insert : [DDL record: FREE, id=489, thread_id=7, space_id=83, index_id=200, page_no=4] [InnoDB] DDL log delete : by id 489 [InnoDB] DDL log insert : [DDL record: DROP, id=490, thread_id=7, table_id=1108] [InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=491, thread_id=7, space_id=82, old_file_path=./test/#sql-ib1109-1917598002.ibd, new_file_path=./test/t1.ibd] [InnoDB] DDL log delete : by id 491 [InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=492, thread_id=7, table_id=1108, old_file_path=test/#sql-ib1109-1917598002, new_file_path=test/t1] [InnoDB] DDL log delete : by id 492 [InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=493, thread_id=7, space_id=83, old_file_path=./test/t1.ibd, new_file_path=./test/#sql-ib1108-1917598001.ibd] [InnoDB] DDL log delete : by id 493 [InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=494, thread_id=7, table_id=1109, old_file_path=test/t1, new_file_path=test/#sql-ib1108-1917598001] [InnoDB] DDL log delete : by id 494 [InnoDB] DDL log insert : [DDL record: DROP, id=495, thread_id=7, table_id=1108] [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=496, thread_id=7, space_id=82, old_file_path=./test/#sql-ib1109-1917598002.ibd] [InnoDB] DDL log post ddl : begin for thread id : 7 [InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=496, thread_id=7, space_id=82, old_file_path=./test/#sql-ib1109-1917598002.ibd] [InnoDB] DDL log replay : [DDL record: DROP, id=495, thread_id=7, table_id=1108] [InnoDB] DDL log replay : [DDL record: DROP, id=490, thread_id=7, table_id=1108] [InnoDB] DDL log post ddl : end for thread id : 7
这是个典型的三阶段ddl的过程:分为prepare, perform 以及commit三个阶段: Prepare: 这个阶段会修改元数据,创建临时ibd文件#sql-ib1108-1917598001.ibd, 如果发生异常崩溃,我们需要能把这个临时文件删除掉, 因此和create table类似,也为这个idb写了三条日志:delete space, remove cache,以及free btree Perform: 执行操作,将数据拷贝到上述ibd文件中,(同时处理online dmllog), 这部分不涉及log ddl操作 Commit: 更新数据词典信息并提交事务, 这里会写几条日志: DROP : table_id=1108 RENAME SPACE: #sql-ib1109-1917598002.ibd文件被rename成t1.ibd RENAME TABLE: #sql-ib1109-1917598002被rename成t1 RENAME SPACE: t1.ibd 被rename成#sql-ib1108-1917598001.ibd RENAME TABLE: t1表被rename成#sql-ib1108-1917598001 DROP TABLE: table_id=1108 DELETE SPACE: 删除#sql-ib1109-1917598002.ibd
实际上这一步写的ddl log描述了commit阶段操作的逆向过程:将t1.ibd rename成#sql-ib1109-1917598002, 并将sql-ib1108-1917598001 rename成t1表,最后删除旧表。其中删除旧表的操作这里不执行,而是到post-ddl阶段执行 Post-ddl: 在事务提交后,执行最后的操作:replay ddl log, 删除旧文件,清理mysql.innodb_dynamic_metadata中相关信息 DELETE SPACE: #sql-ib1109-1917598002.ibd DROP: table_id=1108 DROP: table_id=1108
加索引 mysql> ALTER TABLE t1 ADD KEY(b); Query OK, 0 rows affected (0.14 sec) Records: 0 Duplicates: 0 Warnings: 0 [InnoDB] DDL log insert : [DDL record: FREE, id=431, thread_id=7, space_id=76, index_id=191, page_no=5] [InnoDB] DDL log delete : by id 431 [InnoDB] DDL log post ddl : begin for thread id : 7 [InnoDB] DDL log post ddl : end for thread id : 7
创建索引采用inplace创建的方式,没有临时文件,但如果异常发生的话,依然需要在发生异常时清理临时索引, 因此增加了一条FREE log,用于异常发生时能够删除临时索引.
TRUNCATE TABLE mysql> TRUNCATE TABLE t1; Query OK, 0 rows affected (0.13 sec) [InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=439, thread_id=7, space_id=77, old_file_path=./test/#sql-ib1103-1917597994.ibd, new_file_path=./test/t1.ibd] [InnoDB] DDL log delete : by id 439 [InnoDB] DDL log insert : [DDL record: DROP, id=440, thread_id=7, table_id=1103] [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=441, thread_id=7, space_id=77, old_file_path=./test/#sql-ib1103-1917597994.ibd] [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=442, thread_id=7, space_id=78, old_file_path=./test/t1.ibd] [InnoDB] DDL log delete : by id 442 [InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=443, thread_id=7, table_id=1104, new_file_path=test/t1] [InnoDB] DDL log delete : by id 443 [InnoDB] DDL log insert : [DDL record: FREE, id=444, thread_id=7, space_id=78, index_id=194, page_no=4] [InnoDB] DDL log delete : by id 444 [InnoDB] DDL log insert : [DDL record: FREE, id=445, thread_id=7, space_id=78, index_id=195, page_no=5] [InnoDB] DDL log delete : by id 445 [InnoDB] DDL log post ddl : begin for thread id : 7 [InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=441, thread_id=7, space_id=77, old_file_path=./test/#sql-ib1103-1917597994.ibd] [InnoDB] DDL log replay : [DDL record: DROP, id=440, thread_id=7, table_id=1103] [InnoDB] DDL log post ddl : end for thread id : 7
Truncate table是个比较有意思的话题,在早期5.6及之前的版本中, 是通过删除旧表创建新表的方式来进行的,5.7之后为了保证原子性,改成了原地truncate文件,同时增加了一个truncate log文件,如果在truncate过程中崩溃,可以通过这个文件在崩溃恢复时重新truncate。到了8.0版本,又恢复成了删除旧表,创建新表的方式,与之前不同的是,8.0版本在崩溃时可以回滚到旧数据,而不是再次执行。以上述为例,主要包括几个步骤: 将表t1.ibd rename成#sql-ib1103-1917597994.ibd 创建新文件t1.ibd post-ddl: 将老文件#sql-ib1103-1917597994.ibd删除
RENAME TABLE mysql> RENAME TABLE t1 TO t2; Query OK, 0 rows affected (0.06 sec)
DDL LOG: [InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=450, thread_id=7, space_id=78, old_file_path=./test/t2.ibd, new_file_path=./test/t1.ibd] [InnoDB] DDL log delete : by id 450 [InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=451, thread_id=7, table_id=1104, old_file_path=test/t2, new_file_path=test/t1] [InnoDB] DDL log delete : by id 451 [InnoDB] DDL log post ddl : begin for thread id : 7 [InnoDB] DDL log post ddl : end for thread id : 7
这个就比较简单了,只需要记录rename space 和rename table的逆操作即可. post-ddl不需要做实际的操作
DROP TABLE DROP TABLE t2 [InnoDB] DDL log insert : [DDL record: DROP, id=595, thread_id=7, table_id=1119] [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=596, thread_id=7, space_id=93, old_file_path=./test/t2.ibd] [InnoDB] DDL log post ddl : begin for thread id : 7 [InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=596, thread_id=7, space_id=93, old_file_path=./test/t2.ibd] [InnoDB] DDL log replay : [DDL record: DROP, id=595, thread_id=7, table_id=1119] [InnoDB] DDL log post ddl : end for thread id : 7
先在ddl log中记录下需要删除的数据,再提交后,再最后post-ddl阶段执行真正的删除表对象和文件操作
代码实现:
主要实现代码集中在文件storage/innobase/log/log0ddl.cc中,包含了向log_ddl表中插入记录以及replay的逻辑。
隐藏的innodb_log_ddl表结构如下 def->add_field(0, "id", "id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT"); def->add_field(1, "thread_id", "thread_id BIGINT UNSIGNED NOT NULL"); def->add_field(2, "type", "type INT UNSIGNED NOT NULL"); def->add_field(3, "space_id", "space_id INT UNSIGNED"); def->add_field(4, "page_no", "page_no INT UNSIGNED"); def->add_field(5, "index_id", "index_id BIGINT UNSIGNED"); def->add_field(6, "table_id", "table_id BIGINT UNSIGNED"); def->add_field(7, "old_file_path", "old_file_path VARCHAR(512) COLLATE UTF8_BIN"); def->add_field(8, "new_file_path", "new_file_path VARCHAR(512) COLLATE UTF8_BIN"); def->add_index(0, "index_pk", "PRIMARY KEY(id)"); def->add_index(1, "index_k_thread_id", "KEY(thread_id)");
记录类型
根据不同的操作类型,可以分为如下几类: FREE_TREE_LOG
目的是释放索引btree,入口函数 log_DDL::write_free_tree_log ,在创建索引和删除表时会调用到
对于drop table中涉及的删索引操作,log ddl的插入操作放到父事务中,一起要么提交要么回滚
对于创建索引的case, log ddl就需要单独提交,父事务将记录标记删除,这样后面如果ddl回滚了,也能将残留的index删掉。 DELETE_SPACE_LOG
入口函数: Log_DDL::write_delete_space_log
用于记录删除tablespace操作,同样分为两种情况: drop table/tablespace, 写入的记录随父事务一起提交,并在post-ddl阶段replay 创建tablespace, 写入的记录单独提交,并被父事务标记删除,如果父事务回滚,就通过replay删除参与的tablespace RENAME_SPACE_LOG
入口函数: Log_DDL::write_rename_space_log
用于记录rename操作,例如如果我们把表t1 rename成t2,在其中就记录了逆向操作t2 rename to t1.
在函数 Fil_shard::space_rename() 中,总是先写ddl log, 再做真正的rename操作. 写日志的过程同样是独立事务提交,父事务做未提交的删除操作 DROP_LOG
入口函数: Log_DDL::write_drop_log
用于记录删除表对象操作,这里不涉及文件层操作,写ddl log在父事务中执行 RENAME_TABLE_LOG
入口函数: Log_DDL::write_rename_table_log
用于记录rename table对象的逆操作,和rename space类似,也是独立事务提交ddl log, 父事务标记删除 REMOVE_CACHE_LOG
入口函数: Log_DDL::write_remove_cache_log
用于处理内存表对象的清理,独立事务提交,父事务标记删除 ALTER_ENCRYPT_TABLESPACE_LOG
入口函数: Log_DDL::write_alter_encrypt_space_log
用于记录对tablespace加密属性的修改,独立事务提交. 在写完ddl log后修改tablespace page0 中的加密标记
综上,在ddl的过程中可能会提交多次事务,大概分为三类: 独立事务写ddl log并提交,父事务标记删除, 如果父事务提交了,ddl log也被顺便删除了,如果父事务回滚了,那就要根据ddl log做逆操作来回滚ddl 独立事务写ddl log 并提交, (目前只有ALTER_ENCRYPT_TABLESPACE_LOG) 使用父事务写ddl log,在ddl结束时提交。需要在post-ddl阶段处理
post_ddl
如上所述,有些ddl log是随着父事务一起提交的,有些则在post-ddl阶段再执行, post_ddl发生在父事提交或回滚之后: 若事务回滚,根据ddl log做逆操作,若事务提交,在post-ddl阶段做最后真正不可逆操作(例如删除文件)
入口函数: Log_DDL::post_ddl -->Log_DDL::replay_by_thread_id
根据执行ddl的线程thread id通过innodb_log_ddl表上的二级索引,找到log id,再到聚集索引上找到其对应的记录项,然后再replay这些操作,完成ddl后,清理对应记录
崩溃恢复
在崩溃恢复结束后,会调用 ha_post_recover 接口函数,进而调用innodb内的函数 Log_DDL::recover() , 同样的replay其中的记录,并在结束后删除记录。但ALTER_ENCRYPT_TABLESPACE_LOG类型并不是在这一步删除,而是加入到一个数组ts_encrypt_ddl_records中,在之后调用 resume_alter_encrypt_tablespace 来恢复操作,
参考文档
1. 官方文档
2. wl#9536: support crash safe ddl
原文链接
数据库
2019-03-04 16:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
假设有个字段记录多个员工id并以逗号隔开,如“1,2,3,44,55,66” 在统计报表的时候,我们需要统计这个字段内存了多少个员工id,可以使用这个
select (LENGTH(“1,2,3,44,55,66” )-LENGTH(REPLACE(“1,2,3,44,55,66” ,',','')) +1) ,完整的字符串减去去除了逗号的字符串,得到逗号的个数再加1即是我们要求的数量
数据库
2019-03-04 08:58:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
概述
事务(Transaction)是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元。 ACID
事务具有4个基本特征,分别是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Duration),简称ACID。 隔离级别
ACID这4个特征中,最难理解的是隔离性。在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。4个隔离级别分别是:读未提及(READ_UNCOMMITTED)、读已提交(READ_COMMITTED)、可重复读(REPEATABLE_READ)、顺序读(SERIALIZABLE)。 事务并发引起的问题
数据库在不同的隔离性级别下并发访问可能会出现以下几种问题:脏读(Dirty Read)、不可重复读(Unrepeatable Read)、幻读(Phantom Read)。
事务的思维导图
ACID
1. 原子性(Atomicity)
事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只允许出现两种状态之一。 全部执行成功 全部执行失败
任何一项操作都会导致整个事务的失败,同时其它已经被执行的操作都将被撤销并回滚,只有所有的操作全部成功,整个事务才算是成功完成。
2. 一致性(Consistency)
事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。
比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱。
3. 隔离性(Isolation)
事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。
一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。
隔离性分4个级别,下面会介绍。
4. 持久性(Duration)
事务的持久性是指事务一旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态。
事务隔离级别
1. 读未提及(READ_UNCOMMITTED)
读未提及,该隔离级别允许脏读取,其隔离级别是最低的。换句话说,如果一个事务正在处理某一数据,并对其进行了更新,但同时尚未完成事务,因此还没有提交事务;而以此同时,允许另一个事务也能够访问该数据。
脏读示例:
在事务A和事务B同时执行时可能会出现如下场景:
时间 事务A(存款) 事务B(取款) T1 开始事务 ——
T2 —— 开始事务
T3 —— 查询余额(1000元)
T4 —— 取出1000元(余额0元)
T5 查询余额(0元) ——
T6 —— 撤销事务(余额恢复1000元)
T7
T8
存入500元(余额500元)
提交事务
——
——
余额应该为1500元才对。请看T5时间点,事务A此时查询的余额为0,这个数据就是 脏数据 ,他是事务B造成的,很明显是事务没有进行隔离造成的。
2. 读已提交(READ_COMMITTED)
读已提交是不同的时候执行的时候只能获取到已经提交的数据。
这样就不会出现上面的脏读的情况了。
不可重复读示例
可是解决了脏读问题,但是还是解决不了可重复读问题。
时间 事务A(存款) 事务B(取款) T1 开始事务 ——
T2 —— 开始事务
T3 —— 查询余额(1000元)
T4 查询余额(1000元) ——
T5 —— 取出1000元(余额0元)
T6 —— 提交事务
T7
T8
查询余额(0元)
提交事务
——
——
事务A其实除了查询两次以外,其它什么事情都没做,结果钱就从1000编程0了,这就是不可重复读的问题。
3. 可重复读(REPEATABLE_READ)
可重复读就是保证在事务处理过程中,多次读取同一个数据时,该数据的值和事务开始时刻是一致的。因此该事务级别进制了不可重复读取和脏读,但是有可能出现幻读的数据。
幻读
幻读就是指同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。
时间 事务A(统计总存款) 事务B(存款) T1 开始事务 ——
T2 —— 开始事务
T3 统计总存款(1000元) ——
T4 —— 存入100元
T5 —— 提交事务
T6
T7
提交总存款(10100)
提交事务
——
——
银行工作人员在一个事务中多次统计总存款时看到结果不一样。如果要解决幻读,那只能使用顺序读了。
4. 顺序读(SERIALIZABLE)
顺序读是最严格的事务隔离级别。它要求所有的事务排队顺序执行,即事务只能一个接一个地处理,不能并发。
事务隔离级别对比
事务隔离级别 脏 读 不可重复读 幻 读 读未提及(READ_UNCOMMITTED) 允许 允许 允许
读已提交(READ_COMMITTED) 可重复读(REPEATABLE_READ) 顺序读(SERIALIZABLE)
禁止 禁止 禁止
允许 禁止 禁止
允许 允许 禁止
4种事务隔离级别从上往下,级别越高,并发性越差,安全性就越来越高。
一般数据默认级别是读以提交或可重复读。

作者:jijs
链接:https://www.jianshu.com/p/aa35c8703d61
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
数据库
2019-03-04 14:53:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 过去一年在 PingCAP 全力奔跑的同时,越来越多的小伙伴开始关注我们、了解我们,我们的团队也愈加庞大,我们也期待更多对我们感兴趣的小伙伴加入我们,跟我们一起做点有意义的事情。可能有些小伙伴对我司「神秘的招聘职位」感到茫然,对我们在做的事情也没有深入的了解,于是我们准备推出「PingCAP 招聘职位深度解读」系列文章,介绍 PingCAP 各个团队的小伙伴们现在在做什么、接下来的规划是什么、不同团队吸纳成员的核心需求是什么等等。 本篇将带大家速览我司各个研发团队的定位和分工,并回答一个热门问题「在 PingCAP 工作是什么样的体验?」
作为开源的新型分布式数据库公司,PingCAP 一直致力于探索并逐步解决分布式数据库领域的诸多问题 ,比如: 如何设计和实现世界前沿的分布式 SQL 优化器,让一个复杂的 SQL 查询变的无比轻快智能; 如何实现一致性同步的行列格式混合的 HTAP 架构,且 AP 业务对 TP 业务几乎无干扰; 如何在成千上万台集群规模的情况下,实现无阻塞的表结构变更操作,而不影响任何在线的业务; 如何实现高效的分布式事务算法,让 ACID 事务在大规模并发的分布式存场景下依然可以高效可靠; 如何基于 Raft 协议实现快速稳定的数据强一致复制和自动故障恢复,确保数据安全; 如何设计一个高效智能的调度器,负责对上百 TB 的数据进行调度,保证系统平稳运行; 如何在一个 PR 提交之后,让千万级的测试 cases 在三分钟内跑完,并立即看到对数据库性能有没有显著的提升,以及混沌工程的具体实践; 如何在 AWS,GCP,Aliyun 等公有云上一键启动 TiDB 集群,一键伸缩上百个数据库节点,理解有状态服务在 K8s 上调度的最佳实践。
我们研发团队的定位和分工与以上问题息息相关,或者说,是围绕着 TiDB 产品展开的 。
TiDB 产品架构

从上图可以看到,TiDB 集群主要包括三个核心组件:TiDB Server,TiKV Server 和 PD Server,分别用于解决计算、存储、调度这三个核心问题。此外,还有用于解决用户复杂 OLAP 需求的 TiSpark / TiFlash 组件。与之对应的,我们的内核研发团队分别是: TiDB 团队、 TiKV 团队和 AP (Analytical Product)团队,此外还有 Cloud 团队、 EE (Efficiency Engineering)团队和很重要的 QA (Quality Assurance)团队。
所以很多对 TiDB 不太了解的小伙伴看完我们的招聘页面,可能会觉得那些五(没)花(听)八(说)门(过)的研发类职位是特别神秘的存在……吧……
招聘页面上一小部分神秘部队

那么这些「神秘」团队到底是做什么的?
下面就简单的介绍一下这些研发团队是做什么的吧。
TiDB 团队
TiDB 团队负责所有和 SQL 计算相关的工作以及和客户端(业务)之间的交互,包括协议解析、语法解析、查询优化、执行计算等等,这是一个承上启下的核心模块。除此之外还包括与其他数据库之间的数据迁移和同步组件,比如 TiDB 自身的 Binlog 模块以及读取 MySQL 之类数据源 Binlog 的组件。
TiKV 团队
TiKV 是一个支持事务的,数据强一致的分布式 Key-Value 存储引擎。 从产品架构图中可以看出:无论是 TiDB Server 还是 TiSpark 组件,都是从 TiKV 存取数据的,所以我们一定要保证 TiKV 的稳定和高效。TiKV 团队主要负责的就是分布式 Key-Value 存储引擎的设计和开发,分布式调度系统的设计与研发,构建分布式压力测试框架,稳定性测试框架等工作。
AP 团队
这个是一个比较新的团队,主要负责 OLAP 业务相关的产品,包括之前已经有的 TiSpark 和正在研发中的 AP 扩展引擎 TiFlash 产品。TiDB 是一款 HTAP 的产品,而加强和补齐 HTAP 中的 AP 环节主要就这个组的责任,这里包含了基于 Raft 的一致性同步列存引擎,MPP 计算引擎开发以及大数据相关产品的整合等工作。
Cloud 团队
TiDB 是一个 Cloud Native 的数据库,Cloud 团队的职责就是让 TiDB 更平滑、以更大的规模跑在云上。他们将 TiDB 的组件容器化,并借助 Kubernetes 进行编排与调度。其核心是 TiDB-Operator,实现了云上的快速部署、一键伸缩和故障自治愈。编排有状态的分布式服务是 Kubernetes 最有挑战的事情之一,也是这个团队最擅长解决的问题。Cloud 团队正在努力将 TiDB 构建成为一个云上的服务,即一个 Multi-tenant, Across-cloud, Fully-managed 的 DBaaS(Database as a Service)产品。
EE 团队
这是一个非常 Hack 的团队,致力于解决研发、测试、交付、甚至公司运营中的各种效率问题。他们信仰自动化,摒弃重复性的人工劳动,发明各种 bot 帮助提高 DevOps 的效率;他们创造了强大的“薛定谔”测试平台,将混沌工程变成现实,不断挑战分布式数据库的极限;他们深入系统内核,改造 bcc/eBPF 这些最酷的工具,将操作系统的秘密暴露无遗;他们高效率定位线上的各种疑难杂症,还第一手玩到 Optane Memory 硬件——他们就是神秘的 EE 团队。
QA 团队
每个发布的 TiDB 版本,都有数千万的测试用例来保障产品在客户生产环境下的完美工作。QA 团队开发测试工具和自动化测试框架,并引入混沌工程、人工智能技术来保障 TiDB 的数据一致性和稳定性。 后续我们将每周更新 1-2 篇文章为大家详细介绍以上团队和相关职位。如果大家对文章有意见或建议,欢迎在微信后台留言或者发邮件到 hire@pingcap.com 告诉我们~
在 PingCAP 工作是什么样的体验?
这可能是很多小伙伴们最最关心的 Part。弹性工作制、零食水果、六险一金这些就不多说了,应该已经成为很多公司的标配,我们来说点有特色的:
工作内容
选择一份工作,工作内容是否有意义、有价值,你是否有兴趣投入其中,这两点至关重要。
在 PingCAP,你可以亲自参与打造一款代表未来数据库产品,接触核心的分布式关系数据库技术,你的每一个想法都会被重视,每一次提交都有可能给整个产品带来意想不到的变化 。
工作伙伴
他们大多来自于国内外一线互联网公司,有非常出色的技术实力,作为聪明人的你一定也想和聪明的人一起工作。 团队成员整体比较年轻,氛围相对轻松、自在。在这里,你可以保留自己的个性和兴趣爱好 。无论你是爱好桌游、喜欢摇滚、热爱运动,都能找到与你志同道合的小伙伴,在从事喜欢的工作的同时也可以做你自己,是不是很 Cool?
开源文化
我们有着 活跃的开源社区 。截止到 2019 年 3 月 1 日,TiDB+TiKV 项目在 GitHub 上的 Star 数已经达到了 21000+,拥有 350+ Contributor,社区的力量在不断壮大。TiDB-Operator、TiDB-DM、TiDB-Lightning 等生态工具陆续开源;24 篇  TiDB 源码阅读系列文章  已经完结, TiKV 源码解析系列文章  已经启动 ;除了开放的线下 Infra Meetup,我们也将内部的 Paper Reading 活动放到了线上直播平台(Bilibili ID: TiDB_Robot)…… 想要了解 2018 年 TiDB 社区的成长足迹可以查看这篇文章—— 《2018 TiDB 社区成长足迹与小红花 | TiDB DevCon 2019》
工作地点
目前除北京总部之外,我们在 上海 、 杭州 、 广州 、 深圳 、 成都 、 硅谷 都设立了 Office。你可以去体验北上广深的快节奏,感受经济、文化、思想的强烈碰撞,也可以去杭州、成都,在下班或午后享受片刻的宁静与悠闲,还可以去硅谷体验前沿的技术氛围;如果你喜欢美食,可以去魔都的人民广场吃炸鸡,也可以去广州品味一下正宗的粤式茶点,还可以去硅谷 Office 尝一尝正宗的西餐,当然还有成都的火锅、小酒馆等着你;甚至你还有机会 Remote 在家,事业家庭两相宜。
需要特别说明的是,我们并不会按照工作地点来划分工作模块 ,每一个 Office 的小伙伴都在我们的核心研发模块中承担着重要角色,而且内部的跨团队和跨地域 Transfer 都非常透明,PingCAP 的整个项目协作也都是分布式的。
全方面的成长 入职之后,Mentor 会为你定制化培养方案,你对于所从事模块的认知会日渐深入,公司内部小伙伴的分享以及 Paper Reading、Meetup 等活动也能够帮助你对于其他知识领域有更加深刻的认识; 公司为每一位小伙伴提供了分享平台,支持并鼓励大家积极分享自己的想法和见解,在这个过程中,你的语言表达能力、逻辑思维能力也能得到一定程度的提升; 当然,如果你具备了作为 Mentor 的能力并有意向尝试 Mentor 的角色,在 PingCAP,都有机会实现。
我们一直以来的理念是希望每个 PingCAP 的小伙伴都先得到个人成长,再反哺给团队和公司,每一个小伙伴都能参与到公司发展的过程中来。我们完全不担心「把你锻炼出来,却被其他公司高价挖走了」这类事情。且不说我们的薪酬本身就很有竞争力,更重要的是,我们相信一旦你喜欢上我们的理念和工作模式,你是不会舍得离开的~
加入我们吧!
我们认为优秀的工程师或多或少有以下共同特质: A Quick Learner An Earnest Curiosity Faith in Open Source Self-driven     Get Things Done
如果你符合以上特质,欢迎进入招聘页面查看目前开放的工作机会:
https://www.pingcap.com/recruit-cn/join/#positions
简历投递通道:hire@pingcap.com
实习生 :公司的各项福利和学习资源对实习生全面开放,更重要的是实习生还未毕业就有机会接触工业级项目,而且实习期间表现优异者将有机会获得校招绿色通道特权。如果小伙伴们时间不够充裕,也可以先从社区 Contributor 做起,或许下一期  Talent Plan  的主角就是你!
伯乐推荐 :如果你身边有符合以上要求的小伙伴,也可以找我们聊一聊,推荐成功就有机会获得伯乐推荐奖励(iPad、iPhone、MacBook Pro 等等)。伯乐推荐邮件格式:[伯乐推荐] 候选人姓名-职位名称-推荐人姓名-推荐人手机号。
数据库
2019-03-04 13:44:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
搭建过dg的同学肯定非常清楚归档日志和备份的重要性,这些归档日志从主库传输到备库,在备库被apply。
思考一个问题:主库的归档日志是怎么管理呢,备库的归档又该怎么管理呢?
在11g里面,随着ASM、RAC、Data Guard(包括Active Data Guard)的成熟,使用RAC+ASM+Data Guard越来越成为一种可靠的、维护简单、稳定的高可用性和容灾保护方案。这篇文章谈谈如何管理Oracle 11g Data Guard环境中的归档日志。
归档日志是重要的,不然就不必提到这篇文章, 备份恢复 需要它,而Data Guard也需要它。在早期版本的Data Guard环境中,常常面临着归档日志管理问题。在Data Guard环境里面,对归档日志管理需要达到以下几个方面的要求或者说是需求: 不能够随意删除掉归档日志,归档日志丢失会导致Data Guard需要重新搭建。 不能随意使用RMAN删除归档日志,否则同样会导致Data Guard需要重新搭建。 在使用RMAN备份后,如果归档没有被传送或应用到备库上,那么RMAN不应该删除归档日志,否则Data Guard需要的归档就必须从备份里面还原出来,增加了维护工作量。 对RMAN的备份脚本没有特别的要求,否则脚本意外改动,可能会导致Data Guard需要的归档日志被删除。 归档应尽量保存在磁盘上,以避免Data Guard长时间维护时归档被删除。 备库的归档日志不需要花精力去维护,自动删除已经应用过的归档日志。
幸运的是,在11g环境里面,上述的几点很容易就满足,那就是只需要做到以下几点。 使用快速恢复区(fast recovery area),在10g版本的文档中称为闪回恢复区(flash recovery area),老实说,一直不太明白为什么取名叫闪回恢复区,难道是因为10g有了数据库闪回功能?在RAC中,毫无疑问快速恢复区最好是置放在ASM上。 为快速恢复区指定合适的空间。首先我们需要预估一个合理的归档保留时间长。比如由于备份系统问题或Data Guard备库问题、维护等,需要归档保留的时间长度。假设是24小时,再评估一下在归档量最大的24小时之内,会有多少量的归档?一般来说是在批量数据处理的时候归档量最大,假设这24小时之内归档最大为200G。注意对于RAC来说是所有节点在这24小时的归档量之和。最后为快速恢复区指定需要的空间量,比通过参数db_recovery_file_dest_size指定快速恢复区的大小。这里同样假设快速恢复区们存放归档日志。 在备库上指定快速恢复区以及为快速恢复区指定合适的大小,在备库上指定快速恢复区的大小主要考虑的是:切换成为主库后归档日志容量;如果主库归档容量压力大,备库能否存储更多的归档日志以便可以通过备库来备份归档日志。 对主库和备份使用RMAN配置归档删除策略: CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY; 在主备库 需要指定归档路径为 快速恢复区,这样才好让rman来管理闪回区,log_archive_dest_size_1='location=USE_DB_RECOVERY_FILE_DEST'
完成了上述几个步骤,那么归档管理的要求基本上就达到了。通过这样的设置,实现的效果如下: 归档日志如果没有应用到备库,那么在RMAN中使用backup .... delete inputs all和delete archivelog all不会将归档日志删除。但但是请注意如果是使用delete force命令则会删除掉归档,不管归档有没有被应用到备库。 如果归档日志已经应用到了备库,那么在RMAN中使用backup .... delete inputs all和delete archivelog all可以删除归档日志,在正常情况下,由于归档日志可能很快应用到Data Guard,所以在RMAN备份之后可以正常删除归档日志。RMAN也不需要使用特别的备份脚本,也不必担心人为不小心使用。delete archivelog all命令删除了归档。 备库的归档日志存储到快速恢复区中,备库的快速恢复区空间紧张时,会自动删除已经应用过的较早的归档日志以释放空间,这样便可以实现备库的归档日志完全自动管理。 如果由于备份异常或Data Guard异常,在快速恢复区空间紧张时,Oracle在切换日志时,会自动删除掉已经应用过的归档日志,以释放空间。但是如果归档日志没有应用到Data Guard,那么归档日志不会被删除。这种情况下,快速恢复区的归档可能会增加到空间耗尽,最后就会出现数据库不能归档,数据库挂起的问题。
注意上面最后一点,当快速恢复区空间紧张时,Oracle开始删除归档日志,删除的条件还包括归档日志已经应用到备库,这种情况下如果归档日志还没有备份,也会被删除掉。这里的问题是,文档中描述的快速恢复区空间紧张,具体是指什么时间?也就是快速恢复区的空间消耗多少百分比的时候才算是空间紧张?在MOS文章《Files being deleted in the flash recovery area, messages in the alert log Deleted Oracle managed file (Doc ID 1369341.1)》里面有提到,空间使用率达到80%以后就开始删除文件(归档日志)。
Oracle在往快速恢复区存储文件时,其步骤大概是这样的:Oracle估计需要的空间大小(切换日志时就是归档日志大小),然后将这个大小与当前的占用空间大小相加,看是否超过了80%,如果超过了,那么就回收空间(回收的空间应大于等于新建文件需要的空间大小,也就是回收的空间以够用为原则)。如果不能回收空间(比如归档日志没有被应用到备库),那就只能继续占用新的空间,直到空间耗尽。
这里的问题是,假设快速恢复区设定了200G空间,那么在使用到80%,也就是160G的时候就开始回收空间。那么我们在估算空间时,就应该上浮20%。比如我们要求保留24小时归档,这24小时之内归档量最大是200G,那么我们应该为快速恢复区设置240G左右的容量。
那么,这个80%的比率能够更改吗以便延迟Oracle删除归档日志的时间吗?答案是肯定的。没有相应的数据库参数来设定,但是可以通过事件来设置,事件号是19823: oerr ora 19823 19823, 00000, "soft limit recovery area space pressure percentage" // *Document: NO // *Cause: Set on all instances to alter recovery area space pressure // trigger percentage. // *Action: level 1 to 100 indicates the percentage when the space // pressure has to be triggered.
下在是一个测试:
测试环境:主库是Oracle 11.2.0.3 for Linux两节点RAC,备库是Oracle 11.2.0.3 for linux单实例库。测试是在主库的节点1上进行的,其在线日志大小为512MB,快速恢复区指定的大小为16GB。
当前主库的FRA(快速恢复区)的使用率已经接近于80%: select * from V$RECOVERY_AREA_USAGE; FILE_TYPE PERCENT_SPACE_USED PERCENT_SPACE_RECLAIMABLE NUMBER_OF_FILES -------------------- ------------------ ------------------------- --------------- CONTROL FILE 0 0 0 REDO LOG 15.33 0 13 ARCHIVED LOG 64.04 63.81 45 BACKUP PIECE .24 0 1 IMAGE COPY 0 0 0 FLASHBACK LOG 0 0 0 FOREIGN ARCHIVED LOG 0 0 0
发现FRA(快速恢复区)的空间使用率基本上在80%左右。alert日志也有相应的删除较早的归档日志的信息: Thu Jan 02 12:28:50 2014 Thread 1 advanced to log sequence 981 (LGWR switch) Current log# 12 seq# 981 mem# 0: +DATA1/ractest/onlinelog/group_12.299.835542549 Current log# 12 seq# 981 mem# 1: +DG_FLA/ractest/onlinelog/group_12.298.835542551 Thu Jan 02 12:28:50 2014 LNS: Standby redo logfile selected for thread 1 sequence 981 for destination LOG_ARCHIVE_DEST_2 Thu Jan 02 12:28:50 2014 Deleted Oracle managed file +DG_FLA/ractest/archivelog/2014_01_02/thread_2_seq_309.424.835783855 Deleted Oracle managed file +DG_FLA/ractest/archivelog/2014_01_02/thread_1_seq_947.426.835783855 Deleted Oracle managed file +DG_FLA/ractest/archivelog/2014_01_02/thread_1_seq_948.437.835784237 Archived Log entry 2645 added for thread 1 sequence 980 ID 0xc8804744 dest 1:
上面的日志也可以看到其过程是:切换日志;删除不需要的最老的归档日志;生成新的归档日志。
现在我们利用事件19823将这个比率调到95%看看会是什么样子: SQL> alter system set event='19823 trace name context forever,level 95' scope=spfile sid='*';
然后重启主库。再运行上面的测试代码,发现Oracle不再删除归档日志,而是到接近95%的空间使用率时再开始删除归档日志: FILE_TYPE PERCENT_SPACE_USED PERCENT_SPACE_RECLAIMABLE NUMBER_OF_FILES -------------------- ------------------ ------------------------- --------------- CONTROL FILE 0 0 0 REDO LOG 15.33 0 13 ARCHIVED LOG 68.99 65.72 49 BACKUP PIECE .24 0 1 IMAGE COPY 0 0 0 FLASHBACK LOG 0 0 0 FOREIGN ARCHIVED LOG 0 0 0 ............. FILE_TYPE PERCENT_SPACE_USED PERCENT_SPACE_RECLAIMABLE NUMBER_OF_FILES -------------------- ------------------ ------------------------- --------------- CONTROL FILE 0 0 0 REDO LOG 15.33 0 13 ARCHIVED LOG 78.62 59.9 55 BACKUP PIECE .24 0 1 IMAGE COPY 0 0 0 FLASHBACK LOG 0 0 0 FOREIGN ARCHIVED LOG 0 0 0
从上面的最后一次对v$recovery_area_usage的查询数据可以看到,此时空间利用率达到了94.19%,离95%已经很接近(在线日志的大小是512MB,占快速恢复区的3.1%,如果在快速恢复区里面多一个文件就会超过95%)。
接下来我们将这个比率调整到50%,看看是什么结果: SQL> alter system set event='19823 trace name context forever,level 50' scope=spfile sid='*';
然后重启主库。再运行上面的测试代码,发现Oracle在删除归档日志,但是每次均删除的日志只需要容纳要新增的文件即可,不会一下子删除到使利用率到50%以下: FILE_TYPE PERCENT_SPACE_USED PERCENT_SPACE_RECLAIMABLE NUMBER_OF_FILES -------------------- ------------------ ------------------------- --------------- CONTROL FILE 0 0 0 REDO LOG 15.33 0 13 ARCHIVED LOG 72.47 48.57 54 BACKUP PIECE .24 0 1 IMAGE COPY 0 0 0 FLASHBACK LOG 0 0 0 FOREIGN ARCHIVED LOG 0 0 0
然后一直使用alter system switch logfile命令,每执行一次,Oracle会删除一个归档日志,到最后快速恢复区的空间利用率到接近于50%。 Thu Jan 02 12:56:29 2014 Thread 1 advanced to log sequence 1004 (LGWR switch) Current log# 12 seq# 1004 mem# 0: +DATA1/ractest/onlinelog/group_12.299.835542549 Current log# 12 seq# 1004 mem# 1: +DG_FLA/ractest/onlinelog/group_12.298.835542551 Thu Jan 02 12:56:30 2014 Deleted Oracle managed file +DG_FLA/ractest/archivelog/2014_01_02/thread_1_seq_963.317.835788195 Thu Jan 02 12:56:30 2014 LNS: Standby redo logfile selected for thread 1 sequence 1004 for destination LOG_ARCHIVE_DEST_2 Archived Log entry 2703 added for thread 1 sequence 1003 ID 0xc8804744 dest 1: FILE_TYPE PERCENT_SPACE_USED PERCENT_SPACE_RECLAIMABLE NUMBER_OF_FILES -------------------- ------------------ ------------------------- --------------- CONTROL FILE 0 0 0 REDO LOG 15.33 0 13 ARCHIVED LOG 33.29 28.86 65 BACKUP PIECE .24 0 1 IMAGE COPY 0 0 0 FLASHBACK LOG 0 0 0 FOREIGN ARCHIVED LOG 0 0 0
因此,我们可以了解event 19823的用途。对于空间容量比较小的主机,但是希望归档能够尽量保留在快速恢复区,以便留有足够的备份时间窗口,那么可以考虑把这个百分比调整到更大,比如90%,95%等。



数据库
2019-03-04 08:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
项目需要实现使用Kettle向神通数据库中写入数据,Kettle官方标准的数据库插件里面并没有对神通数据库的支持,因此需要自己写一个数据库插件。下面我们开始写一个数据库插件
1.在eclipse中创建一个maven项目,然后修改pom.xml的文件内容,最终内容如下 4.0.0 org.pentaho pentaho-ce-jar-parent-pom 8.1.0.0-365 kettle-plugins kettle-database-osrcar-plugin 0.0.1-SNAPSHOT jar kettle-database-osrcar-plugin http://maven.apache.org UTF-8 1.10.19 8.1.0.0-365 8.1.0.0-365 4.12 xerces xercesImpl 2.8.1 pentaho-kettle kettle-core ${pdi.version} provided junit junit ${junit.version} test maven-assembly-plugin distro-assembly package single false src/main/assembly/assembly.xml
2.在src/main下面创建一个assembly文件夹,然后在创建一个assembly.xml,文件内容如下: bin zip ${project.build.directory} / *.jar lib/ false
此文件的作用是在target目录下生成一个zip格式的插件包,将插件包解压到kettle的plugins目下即可。项目若依赖到了其他jar包,会自动将依赖的jar包打包到lib目录下。
3.创建数据库插件类,在eclipse里面新建一个OscarDatabaseMeta,集成BaseDatabaseMeta,实现接口DatabaseInterface,并实现抽象类BaseDatabaseMeta的抽象方法,最终的类如下: package org.pentaho.di.core.database; import java.sql.ResultSet; import org.pentaho.di.core.Const; import org.pentaho.di.core.exception.KettleDatabaseException; import org.pentaho.di.core.plugins.DatabaseMetaPlugin; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.util.Utils; /** * DatabaseMeta数据库插件-神通数据库 */ @DatabaseMetaPlugin(type = "OSCAR", typeDescription = "神通数据库") public class OscarDatabaseMeta extends BaseDatabaseMeta implements DatabaseInterface { private static final String STRICT_BIGNUMBER_INTERPRETATION = "STRICT_NUMBER_38_INTERPRETATION"; @Override public int[] getAccessTypeList() { return new int[] { DatabaseMeta.TYPE_ACCESS_NATIVE, DatabaseMeta.TYPE_ACCESS_JNDI }; } @Override public int getDefaultDatabasePort() { if (getAccessType() == DatabaseMeta.TYPE_ACCESS_NATIVE) { return 2003; } return -1; } /** * 当前数据库是否支持自增类型的字段 */ @Override public boolean supportsAutoInc() { return false; } /** * 获取限制读取条数的数据,追加再select语句后实现限制返回的结果数 * @see org.pentaho.di.core.database.DatabaseInterface#getLimitClause(int) */ @Override public String getLimitClause(int nrRows) { return " WHERE ROWNUM <= " + nrRows; } /** * 返回获取表所有字段信息的语句 * @param tableName * @return The SQL to launch. */ @Override public String getSQLQueryFields(String tableName) { return "SELECT * FROM " + tableName + " WHERE 1=0"; } @Override public String getSQLTableExists(String tablename) { return getSQLQueryFields(tablename); } @Override public String getSQLColumnExists(String columnname, String tablename) { return getSQLQueryColumnFields(columnname, tablename); } public String getSQLQueryColumnFields(String columnname, String tableName) { return "SELECT " + columnname + " FROM " + tableName + " WHERE 1=0"; } @Override public boolean needsToLockAllTables() { return false; } @Override public String getDriverClass() { if (getAccessType() == DatabaseMeta.TYPE_ACCESS_ODBC) { return "sun.jdbc.odbc.JdbcOdbcDriver"; } else { return "com.oscar.Driver"; } } @Override public String getURL(String hostname, String port, String databaseName) throws KettleDatabaseException { if (getAccessType() == DatabaseMeta.TYPE_ACCESS_ODBC) { return "jdbc:odbc:" + databaseName; } else if (getAccessType() == DatabaseMeta.TYPE_ACCESS_NATIVE) { // / // :/ String _hostname = hostname; String _port = port; String _databaseName = databaseName; if (Utils.isEmpty(hostname)) { _hostname = "localhost"; } if (Utils.isEmpty(port) || port.equals("-1")) { _port = ""; } if (Utils.isEmpty(databaseName)) { throw new KettleDatabaseException("必须指定数据库名称"); } if (!databaseName.startsWith("/")) { _databaseName = "/" + databaseName; } return "jdbc:oscar://" + _hostname + (Utils.isEmpty(_port) ? "" : ":" + _port) + _databaseName; } else { throw new KettleDatabaseException("不支持的数据库连接方式[" + getAccessType() + "]"); } } /** * Oracle doesn't support options in the URL, we need to put these in a * Properties object at connection time... */ @Override public boolean supportsOptionsInURL() { return false; } /** * @return true if the database supports sequences */ @Override public boolean supportsSequences() { return true; } /** * Check if a sequence exists. * * @param sequenceName * The sequence to check * @return The SQL to get the name of the sequence back from the databases data * dictionary */ @Override public String getSQLSequenceExists(String sequenceName) { int dotPos = sequenceName.indexOf('.'); String sql = ""; if (dotPos == -1) { // if schema is not specified try to get sequence which belongs to current user sql = "SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '" + sequenceName.toUpperCase() + "'"; } else { String schemaName = sequenceName.substring(0, dotPos); String seqName = sequenceName.substring(dotPos + 1); sql = "SELECT * FROM ALL_SEQUENCES WHERE SEQUENCE_NAME = '" + seqName.toUpperCase() + "' AND SEQUENCE_OWNER = '" + schemaName.toUpperCase() + "'"; } return sql; } /** * Get the current value of a database sequence * * @param sequenceName * The sequence to check * @return The current value of a database sequence */ @Override public String getSQLCurrentSequenceValue(String sequenceName) { return "SELECT " + sequenceName + ".currval FROM DUAL"; } /** * Get the SQL to get the next value of a sequence. (Oracle only) * * @param sequenceName * The sequence name * @return the SQL to get the next value of a sequence. (Oracle only) */ @Override public String getSQLNextSequenceValue(String sequenceName) { return "SELECT " + sequenceName + ".nextval FROM dual"; } @Override public boolean supportsSequenceNoMaxValueOption() { return true; } /** * @return true if we need to supply the schema-name to getTables in order to * get a correct list of items. */ @Override public boolean useSchemaNameForTableList() { return true; } /** * @return true if the database supports synonyms */ @Override public boolean supportsSynonyms() { return true; } /** * Generates the SQL statement to add a column to the specified table * * @param tablename * The table to add * @param v * The column defined as a value * @param tk * the name of the technical key field * @param use_autoinc * whether or not this field uses auto increment * @param pk * the name of the primary key field * @param semicolon * whether or not to add a semi-colon behind the statement. * @return the SQL statement to add a column to the specified table */ @Override public String getAddColumnStatement(String tablename, ValueMetaInterface v, String tk, boolean use_autoinc, String pk, boolean semicolon) { return "ALTER TABLE " + tablename + " ADD " + getFieldDefinition(v, tk, pk, use_autoinc, true, false); } /** * Generates the SQL statement to drop a column from the specified table * * @param tablename * The table to add * @param v * The column defined as a value * @param tk * the name of the technical key field * @param use_autoinc * whether or not this field uses auto increment * @param pk * the name of the primary key field * @param semicolon * whether or not to add a semi-colon behind the statement. * @return the SQL statement to drop a column from the specified table */ @Override public String getDropColumnStatement(String tablename, ValueMetaInterface v, String tk, boolean use_autoinc, String pk, boolean semicolon) { return "ALTER TABLE " + tablename + " DROP COLUMN " + v.getName() + Const.CR; } /** * Generates the SQL statement to modify a column in the specified table * * @param tablename * The table to add * @param v * The column defined as a value * @param tk * the name of the technical key field * @param use_autoinc * whether or not this field uses auto increment * @param pk * the name of the primary key field * @param semicolon * whether or not to add a semi-colon behind the statement. * @return the SQL statement to modify a column in the specified table */ @Override public String getModifyColumnStatement(String tablename, ValueMetaInterface v, String tk, boolean use_autoinc, String pk, boolean semicolon) { ValueMetaInterface tmpColumn = v.clone(); String tmpName = v.getName(); boolean isQuoted = tmpName.startsWith("\"") && tmpName.endsWith("\""); if (isQuoted) { // remove the quotes first. // tmpName = tmpName.substring(1, tmpName.length() - 1); } int threeoh = tmpName.length() >= 30 ? 30 : tmpName.length(); tmpName = tmpName.substring(0, threeoh); tmpName += "_KTL"; // should always be shorter than 35 positions // put the quotes back if needed. // if (isQuoted) { tmpName = "\"" + tmpName + "\""; } tmpColumn.setName(tmpName); // Read to go. // String sql = ""; // Create a new tmp column sql += getAddColumnStatement(tablename, tmpColumn, tk, use_autoinc, pk, semicolon) + ";" + Const.CR; // copy the old data over to the tmp column sql += "UPDATE " + tablename + " SET " + tmpColumn.getName() + "=" + v.getName() + ";" + Const.CR; // drop the old column sql += getDropColumnStatement(tablename, v, tk, use_autoinc, pk, semicolon) + ";" + Const.CR; // create the wanted column sql += getAddColumnStatement(tablename, v, tk, use_autoinc, pk, semicolon) + ";" + Const.CR; // copy the data from the tmp column to the wanted column (again) // All this to avoid the rename clause as this is not supported on all Oracle // versions sql += "UPDATE " + tablename + " SET " + v.getName() + "=" + tmpColumn.getName() + ";" + Const.CR; // drop the temp column sql += getDropColumnStatement(tablename, tmpColumn, tk, use_autoinc, pk, semicolon); return sql; } @Override public String getFieldDefinition(ValueMetaInterface v, String tk, String pk, boolean use_autoinc, boolean add_fieldname, boolean add_cr) { StringBuilder retval = new StringBuilder(128); String fieldname = v.getName(); int length = v.getLength(); int precision = v.getPrecision(); if (add_fieldname) { retval.append(fieldname).append(" "); } int type = v.getType(); switch (type) { case ValueMetaInterface.TYPE_TIMESTAMP: case ValueMetaInterface.TYPE_DATE: retval.append("TIMESTAMP"); break; case ValueMetaInterface.TYPE_BOOLEAN: if (supportsBooleanDataType()) { retval.append("BOOLEAN"); } else { retval.append("CHAR(1)"); } break; case ValueMetaInterface.TYPE_NUMBER: case ValueMetaInterface.TYPE_INTEGER: case ValueMetaInterface.TYPE_BIGNUMBER: if (fieldname.equalsIgnoreCase(tk) || // Technical key fieldname.equalsIgnoreCase(pk) // Primary key ) { retval.append("BIGSERIAL"); } else { if (length > 0) { if (precision > 0 || length > 18) { // Numeric(Precision, Scale): Precision = total length; Scale = decimal places retval.append("NUMERIC(").append(length + precision).append(", ").append(precision).append(")"); } else if (precision == 0) { if (length > 9) { retval.append("BIGINT"); } else { if (length < 5) { retval.append("SMALLINT"); } else { retval.append("INT"); } } } else { retval.append("FLOAT(53)"); } } else { retval.append("DOUBLE PRECISION"); } } break; case ValueMetaInterface.TYPE_STRING: if (length < 1 || length >= DatabaseMeta.CLOB_LENGTH) { retval.append("TEXT"); } else { retval.append("VARCHAR(").append(length).append(")"); } break; case ValueMetaInterface.TYPE_BINARY: retval.append("BLOB"); break; default: retval.append(" UNKNOWN"); break; } if (add_cr) { retval.append(Const.CR); } return retval.toString(); } /* * (non-Javadoc) * * @see com.ibridge.kettle.core.database.DatabaseInterface#getReservedWords() */ @Override public String[] getReservedWords() { return new String[] { "ALIAS", "AND", "AS", "AT", "BEGIN", "BETWEEN", "BIGINT", "BIT", "BY", "BOOLEAN", "BOTH", "CALL", "CASE", "CAST", "CHAR", "CHARACTER", "COMMIT", "CONSTANT", "CURSOR", "COALESCE", "CONTINUE", "CONVERT", "CURRENT_DATE", "CURRENT_TIMESTAMP", "CURRENT_USER", "DATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DECODE", "DELETE", "ELSE", "ELSIF", "END", "EXCEPTION", "EXECUTE", "EXIT", "EXTRACT", "FALSE", "FETCH", "FLOAT", "FOR", "FROM", "FUNCTION", "GOTO", "IF", "IN", "INT", "INTO", "IS", "INTEGER", "IMMEDIATE", "INDEX", "INOUT", "INSERT", "LEADING", "LIKE", "LIMIT", "LOCALTIME", "LOCALTIMESTAMP", "LOOP", "NCHAR", "NEXT", "NOCOPY", "NOT", "NULLIF", "NULL", "NUMBER", "NUMERIC", "OPTION", "OF", "OR", "OUT", "OVERLAY", "PERFORM", "POSITION", "PRAGMA", "PROCEDURE", "QUERY", "RAISE", "RECORD", "RENAME", "RETURN", "REVERSE", "ROLLBACK", "REAL", "SELECT", "SAVEPOINT", "SETOF", "SMALLINT", "SUBSTRING", "SQL", "SYSDATE", "SESSION_USER", "THEN", "TO", "TYPE", "TABLE", "TIME", "TIMESTAMP", "TINYINT", "TRAILING", "TREAT", "TRIM", "TRUE", "TYPE", "UID", "UPDATE", "USER", "USING", "VARCHAR", "VARCHAR2", "VALUES", "WITH", "WHEN", "WHILE", "LEVEL" }; } /** * @return The SQL on this database to get a list of stored procedures. */ @Override public String getSQLListOfProcedures() { /* * return * "SELECT DISTINCT DECODE(package_name, NULL, '', package_name||'.') || object_name " * + "FROM user_arguments " + "ORDER BY 1"; */ return "SELECT name FROM ORM_FUNCTIONS union SELECT name FROM ORM_PROCEDURES"; } @Override public String getSQLLockTables(String[] tableNames) { StringBuilder sql = new StringBuilder(128); for (int i = 0; i < tableNames.length; i++) { sql.append("LOCK TABLE ").append(tableNames[i]).append(" IN EXCLUSIVE MODE;").append(Const.CR); } return sql.toString(); } @Override public String getSQLUnlockTables(String[] tableNames) { return null; // commit handles the unlocking! } /** * @return extra help text on the supported options on the selected database * platform. */ @Override public String getExtraOptionsHelpText() { return "http://www.shentongdata.com/?bid=3&eid=249"; } @Override public String[] getUsedLibraries() { return new String[] { "oscarJDBC.jar", "oscarJDBC14.jar", "oscarJDBC16.jar" }; } /** * Verifies on the specified database connection if an index exists on the * fields with the specified name. * * @param database * a connected database * @param schemaName * @param tableName * @param idx_fields * @return true if the index exists, false if it doesn't. * @throws KettleDatabaseException */ @Override public boolean checkIndexExists(Database database, String schemaName, String tableName, String[] idx_fields) throws KettleDatabaseException { String tablename = database.getDatabaseMeta().getQuotedSchemaTableCombination(schemaName, tableName); boolean[] exists = new boolean[idx_fields.length]; for (int i = 0; i < exists.length; i++) { exists[i] = false; } try { // // Get the info from the data dictionary... // String sql = "SELECT * FROM USER_IND_COLUMNS WHERE TABLE_NAME = '" + tableName + "'"; ResultSet res = null; try { res = database.openQuery(sql); if (res != null) { Object[] row = database.getRow(res); while (row != null) { String column = database.getReturnRowMeta().getString(row, "COLUMN_NAME", ""); int idx = Const.indexOfString(column, idx_fields); if (idx >= 0) { exists[idx] = true; } row = database.getRow(res); } } else { return false; } } finally { if (res != null) { database.closeQuery(res); } } // See if all the fields are indexed... boolean all = true; for (int i = 0; i < exists.length && all; i++) { if (!exists[i]) { all = false; } } return all; } catch (Exception e) { throw new KettleDatabaseException("Unable to determine if indexes exists on table [" + tablename + "]", e); } } @Override public boolean requiresCreateTablePrimaryKeyAppend() { return true; } /** * Most databases allow you to retrieve result metadata by preparing a SELECT * statement. * * @return true if the database supports retrieval of query metadata from a * prepared statement. False if the query needs to be executed first. */ @Override public boolean supportsPreparedStatementMetadataRetrieval() { return false; } /** * @return The maximum number of columns in a database, <=0 means: no known * limit */ @Override public int getMaxColumnsInIndex() { return 32; } /** * @return The SQL on this database to get a list of sequences. */ @Override public String getSQLListOfSequences() { return "SELECT SEQUENCE_NAME FROM all_sequences"; } /** * @param string * @return A string that is properly quoted for use in an Oracle SQL statement * (insert, update, delete, etc) */ @Override public String quoteSQLString(String string) { string = string.replaceAll("'", "''"); string = string.replaceAll("\\n", "'||chr(13)||'"); string = string.replaceAll("\\r", "'||chr(10)||'"); return "'" + string + "'"; } /** * Returns a false as Oracle does not allow for the releasing of savepoints. */ @Override public boolean releaseSavepoint() { return false; } @Override public boolean supportsErrorHandlingOnBatchUpdates() { return false; } /** * @return true if Kettle can create a repository on this type of database. */ @Override public boolean supportsRepository() { return true; } @Override public int getMaxVARCHARLength() { return 2000; } /** * Oracle does not support a construct like 'drop table if exists', which is * apparently legal syntax in many other RDBMSs. So we need to implement the * same behavior and avoid throwing 'table does not exist' exception. * * @param tableName * Name of the table to drop * @return 'drop table if exists'-like statement for Oracle */ @Override public String getDropTableIfExistsStatement(String tableName) { return "DROP TABLE IF EXISTS " + tableName; } @Override public SqlScriptParser createSqlScriptParser() { return new SqlScriptParser(false); } /** * @return true if using strict number(38) interpretation */ public boolean strictBigNumberInterpretation() { return "Y".equalsIgnoreCase(getAttributes().getProperty(STRICT_BIGNUMBER_INTERPRETATION, "N")); } /** * @param strictBigNumberInterpretation * true if use strict number(38) interpretation */ public void setStrictBigNumberInterpretation(boolean strictBigNumberInterpretation) { getAttributes().setProperty(STRICT_BIGNUMBER_INTERPRETATION, strictBigNumberInterpretation ? "Y" : "N"); } }
这是一个完整的代码,这个类里面不仅实现父类的抽象方法,还重写的一部分。这个类只需要getFieldDefinition、getDriverClass、getURL、getAddColumnStatement、getModifyColumnStatement、getUsedLibraries、getAccessTypeList实现这些方法就可以编译通过。其他的方法要根据实际的数据库来决定是否需要实现,抽象类里面已经实现了一部分,可以到BaseDatabaseMeta中看一其实现的方式能否满足自己使用数据库的需求,如果不满足则需要重写。方法getFieldDefinition()的实现尤其重要,这个是实现数据类型转换所需要,编写不好会导致插入数据的时候出现问题。
4.在pom.xml右键执行打包,会在target目录下生成一个jar包和一个zip包,由于本插件没有其他依赖,直接将jar包放到plugins目录下或者自己再创建一个文件夹。依赖其他jar包时,可以将zip解压到plugins目录下
5.重新启动kettle,新建DB连接,就可以看到里面已经有神通数据库了。
附件: 点击下载插件源代码




数据库
2019-03-03 19:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
写了两个kettle插件,这两个插件是两个插件项目,且在两个分类,开发完成打包到放到kettle的plugins目录下,启动kettle,结果在kettle设计器中只显示了一个插件。后来改了插件名称,插件在plugins文件夹中的文件夹的名字,发现始终是先扫描到谁就可以注册进来,后续的不管有几个都进不来,后果跟了一下kettle的源代码,发现是kettle里面存储插件TreeSet的Comparator写的有问题。
负责注册插件的类org.pentaho.di.core.plugins.PluginRegistry中声明了一个Map存放所有的插件类型及插件。key是插件类型如作业项插件、步骤插件类型等,value是一个TreeSet,TreeSet中存储的是改插件类型所有的插件。(Kettle8.1.0.0-365版本在PluginRegistry的91行) private final Map, Set> categoryMap = new HashMap<>();
下面看一下注册插件的方法 public void registerPlugin( Class pluginType, PluginInterface plugin ) throws KettlePluginException { boolean changed = false; // Is this an add or an update? lock.writeLock().lock(); try { if ( plugin.getIds()[0] == null ) { throw new KettlePluginException( "Not a valid id specified in plugin :" + plugin ); } // Keep the list of plugins sorted by name... // Set list = pluginMap.computeIfAbsent( pluginType, k -> new TreeSet<>( Plugin.nullStringComparator ) ); if ( !list.add( plugin ) ) { list.remove( plugin ); list.add( plugin ); changed = true; } if ( !Utils.isEmpty( plugin.getCategory() ) ) { // Keep categories sorted in the natural order here too! // categoryMap.computeIfAbsent( pluginType, k -> new TreeSet<>(getNaturalCategoriesOrderComparator(pluginType ))) .add( plugin.getCategory() ); } } finally { lock.writeLock().unlock(); Set listeners = this.listeners.get( pluginType ); if ( listeners != null ) { for ( PluginTypeListener listener : listeners ) { // Changed or added? if ( changed ) { listener.pluginChanged( plugin ); } else { listener.pluginAdded( plugin ); } } } synchronized ( this ) { notifyAll(); } } }
重点看方法中的这一行代码: categoryMap.computeIfAbsent( pluginType, k -> new TreeSet<>(getNaturalCategoriesOrderComparator(pluginType ))) .add( plugin.getCategory() );
这一行是当map中不存在指定的ke时,返回一个空的TreeSet.关键是TreeSet指定了一个Comparator。
我们接下来看一下getNaturalCategoriesOrderComparator这个方法是怎么创建Comparator的。这个方法的目的是对Kettle自身的插件进行排序按顺序显示。 private static Comparator getNaturalCategoriesOrderComparator( Class pluginType ) { PluginTypeCategoriesOrder naturalOrderAnnotation = pluginType.getAnnotation( PluginTypeCategoriesOrder.class ); final String[] naturalOrder; if ( naturalOrderAnnotation != null ) { String[] naturalOrderKeys = naturalOrderAnnotation.getNaturalCategoriesOrder(); Class i18nClass = naturalOrderAnnotation.i18nPackageClass(); naturalOrder = Arrays.stream( naturalOrderKeys ) .map( key -> BaseMessages.getString( i18nClass, key ) ) .toArray( String[]::new ); } else { naturalOrder = null; } return ( s1, s2 ) -> { if ( naturalOrder != null ) { int idx1 = Const.indexOfString( s1, naturalOrder ); int idx2 = Const.indexOfString( s2, naturalOrder ); return idx1 - idx2; } return 0; }; }
按照这个方式的实现,自定了两个插件且插件在两个分类中且分类不是Kettle原有的分类,那么idx1和idx2的值均是-1,如此compare就会返回0,0表示连个分类是相同的就添加不到set中,所以第二个及以后的插件都注册不进来。修改的代码如下: private static Comparator getNaturalCategoriesOrderComparator( Class pluginType ) { PluginTypeCategoriesOrder naturalOrderAnnotation = pluginType.getAnnotation( PluginTypeCategoriesOrder.class ); ........ return ( s1, s2 ) -> { if ( naturalOrder != null ) { int idx1 = Const.indexOfString( s1, naturalOrder ); int idx2 = Const.indexOfString( s2, naturalOrder ); // 两个插件分类都不是Kettle自己已有的分类是,则让两个分类进行进行比较 if (-1 == idx1 && -1 == idx2) return s1.compareTo(s2); // 自定义的插件显示在末尾 if (-1 == idx1 || -1 == idx2) return 1; return idx1 - idx2; } return 0; }; }
数据库
2019-03-03 17:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
第一步:准备低成本存储的业务数据和DLA表 OSS(https://www.aliyun.com/product/oss)是云上低成本数据存储的优选方案 DLA(https://www.aliyun.com/product/datalakeanalytics)是云上低成本、无服务器化的支持OSS数据查询、分析的优选方案
参考如下文档使用案例,准备OSS上存储的业务数据和DLA表:
https://yq.aliyun.com/articles/623282
此步骤预计耗时:5分钟。
第二步:使用DataV访问DLA制作数据大屏 DataV(https://data.aliyun.com/visual/datav)是云上大数据可视化大屏的优选方案
1. 准备DataV
以第一步中的业务数据为例,构建企业销售数据大屏,本大屏主要涉及三张表: orders表,销售订单数据; customer表,客户记录数据; nation表,国家记录数据;
登录控制台DataV控制台:http://datav.aliyun.com/data,购买基础版:
使用“兼容MySQL”的方式,就能连接DLA服务,本例中基础版就能满足。
2. 准备DLA数据源
点击 “我的数据”,“添加数据”
编辑数据源: 选择“兼容MySQL数据库”类型; 名称按需进行命名; 根据在DLA控制台https://datalakeanalytics.console.aliyun.com/overview上的链接信息(经典网络)和在阿里云站内信收到用户名、密码信息,填入相应栏目,选择目标的tpch数据库,确定保存。
3. 准备大屏模板
点击 “我的可视化”,“新建可视化”
选择“销售实时监控模板”,点“创建”
本示例大屏中,目标显示各个国家的销售数据情况,把下面标红的框内的组件删除。
然后为了布局,美观,重新调整一个组件在画布上的位置。
为了显示各个国家的销售数据情况,需要世界地图,删除现有的中国地图组件,然后在导航栏的“地图”中选择“3D平面世界地图”。
4. 给大屏中的组件配置数据
4.1 给地图配置数据
根据如下操作,给地图render数据: 选择地图,在数据tab页面中,“数据源类型数据库选择已有数据源我的数据**”中配置的DLA数据源; 在SQL中,填入如下SQL,计算按国家销售额排序的数据;
字段id和value,分别填入上述SQL返回的n_id列和total_price列; 根据业务数据更新需求(见附一:架构示意),选择大屏数据“自动更新请求”,比如60秒一次; 然后点击“刷新数据”。
4.2 给总销售额配置数据
根据如下操作,给总销售额配置数据: 选择销售总额组件,在数据tab页面中,“数据源类型数据库选择已有数据源我的数据**”中配置的DLA数据源; 在SQL中,填入如下SQL,计算销售总额的数据;
select sum(o_totalprice) total_price
from orders; 字段value,填入上述SQL返回的total_price列; 根据业务数据更新需求(见附一:架构示意),选择大屏数据“自动更新请求”,比如60秒一次; 然后点击“刷新数据”。
4.3 给按国家销售排名配置数据
根据如下操作,给总销售额配置数据: 选择销售额国家排名组件,在数据tab页面中,“数据源类型数据库选择已有数据源我的数据**”中配置的DLA数据源; 在SQL中,填入如下SQL,计算按国家销售额排序的数据;

字段value和content,填入上述SQL返回的total_price列和n_name列; 根据业务数据更新需求(见附一:架构示意),选择大屏数据“自动更新请求”,比如60秒一次; 然后点击“刷新数据”。
5. 预览和发布大屏
点击右上角的“预览”,可以看到大屏发布后的效果。
确认无误后,可以进行发布:
此步骤预计耗时:10分钟。
附:架构示意
和使用传统数据库加DataV构建大屏相比,DataV + DLA + OSS的方案是另外一种低成本选择,在绝大部分频率较低的数据刷新的业务大屏场景下,DataV + DLA + OSS的方式远比DataV + 传统数据库的成本低。结合业务数据的产生,整体架构示意如下:
可能的业务数据产出的大屏数据刷新链路: 业务应用程序产出的增量数据,直接上传到OSS中,通过定时查询,刷新到业务数据大屏中; 业务应用程序产出的业务日志,采集到日志服务中,通过定时日志投递(最小5分钟延迟),投递到OSS中,再通过定时查询,刷新到业务数据大屏中。
原文链接
数据库
2019-03-01 18:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在阿里云上,很多客户的应用都是多地域部署的, 比如在北京(cn-beijing)的地域部署一个应用让北方的客户访问快一点,同时在杭州(cn-hangzhou)地域部署一份让南方的客户访问快一点。多地域部署之后,业务数据被拆成了多份,而各个地域的数据库都是独立的,网络又不通,给总体业务数据的分析造成了困难。今天我给大家介绍一套基于 DataLakeAnalytics , OSS , DataX 等几个阿里云产品的跨地域数据分析的解决方案。 其实云产品本身(比如我们 DataLakeAnalytics 自己)也有跨地域数据分析的需求,这个方案也同样适用。这个方案本来就是为了分析 DataLakeAnalytics 自己的业务数据而探索出来的。
方案概览
我们知道各个地域的RDS是不通的,除非你开公网访问权限(有很大的安全风险,不推荐), 而且即使你开公网,要对多个数据库里面的数据进行联合分析也不是一件容易的事情;而且这种数据分析的需求我们不希望它占用太多的预算。
我们的方案是把各个地域的数据都同步到同一个地域的OSS上面去,然后用 DataLakeAnalytics 进行联合分析。这个方案的优点在于 OSS 存储收费非常便宜, DataLakeAnalytics 也是按查询量收费的,你平时不查询的时候一分钱都不用花。总体方案如下图:
汇聚各个地域的数据
我们方案的第一步是把各个地域的RDS数据同步到同一个地域的OSS里面去。阿里巴巴集团开源了一个很棒的数据搬运的工具: DataX , 可以把数据在各种不同的数据源之间进行搬运,它支持的数据源类型非常丰富: 从关系型的 MySQL, SQLServer, 到各种文件系统如 HDFS, OSS等等,其中我们需要的是从 MySQL 读数据的 mysqlreader 插件以及往 OSS 写数据的 osswriter 插件。
假定我们有下面这么一个记录人员信息的表 person 需要同步: create table person ( id int primary key auto_increment, name varchar(1023), age int );
我们写一个类似下面这样的DataX任务描述文件 person.json : { "job": { "setting": { "speed": { "channel": 1, "byte": 104857600 }, "errorLimit": { "record": 10 } }, "content": [ { "reader": { "name": "mysqlreader", "parameter": { "username": "your-user-name", "password": "your-password", "column": [ "id", "name", "age", ], "connection": [ { "table": [ "person" ], "jdbcUrl": [ "jdbc:mysql://your-rds.mysql.rds.aliyuncs.com:3306/dbname" ] } ] } }, "writer": { "name": "osswriter", "parameter": { "endpoint": "http://oss.aliyuncs.com", "accessId": "your-access-id", "accessKey": "your-access-secret", "bucket": "mydb-bucket", "object": "mydb/person/region=cn-hangzhou/person.csv", "encoding": "UTF-8", "fieldDelimiter": "|", "writeMode": "truncate" } } } ] } }
这里 MySQL 相关的信息填你的业务库的信息,而 OSS 相关的信息选择一个我们同步到的OSS的地址。注意 OSS 配置部分的 object 字段, mydb 保存你所有的数据, person 这个目录保存你的 person 表的数据, region=cn-hangzhou 这个目录就有意思了,它保存的是你的应用在 cn-hangzhou 这个region里面的数据,同样的,你可能还会有 cn-beijing , cn-shangahi 的数据等等。
然后执行如下命令: // 执行前确保你已经下载并正确配置好 DataX 了。 python datax/bin/datax.py person.json
正确执行的话你会看到下面的输出: .....省略N行...... 2018-09-06 19:53:19.900 [job-0] INFO JobContainer - PerfTrace not enable! 2018-09-06 19:53:19.901 [job-0] INFO StandAloneJobContainerCommunicator - Total 251 records, 54067 bytes | Speed 5.28KB/s, 25 records/s | Error 0 records, 0 bytes | All Task WaitWriterTime 0.001s | All Task WaitReaderTime 0.026s | Percentage 100.00% 2018-09-06 19:53:19.902 [job-0] INFO JobContainer - 任务启动时刻 : 2018-09-06 19:53:09 任务结束时刻 : 2018-09-06 19:53:19 任务总计耗时 : 10s 任务平均流量 : 5.28KB/s 记录写入速度 : 25rec/s 读出记录总数 : 251 读写失败总数 : 0
这样数据就自动同步到 OSS 上去了,你可以下载一个 oss-browser 去查看oss上面的数据:
文件里面数据大概是这样的: 9|ethan|10 10|julian|20 11|train|30 12|wally|40
完成了一个地域的数据搬运之后,其它地域都可以照葫芦画瓢,唯一需要注意的地方是,虽然 MySQL 数据是各个 地域 的数据,但是 OSS 要用同一个根目录 person ,因为我们要做数据汇集嘛,把几个地域的数据汇集完成之后, person 目录的结构大概是这样的:
使用 DataLakeAnalytics 分析汇聚后的OSS数据
下面的分析就可以交给 DataLakeAnalytics 了,分析OSS上的数据是 DataLakeAnalytics 的拿手好戏,在开始之前我们要有一个 DataLakeAnalytics 的账号,目前 DataLakeAnalytics 正在公测,直接申请试用就好了。试用审批成功之后,你会获得一个用户名和密码, 然后在控制台登录就可以使用:
或者如果你是极客,更偏爱命令行,你也可以使用普通的 MySQL 客户端就可以连接 DLA 了: mysql -hservice.cn-shanghai.datalakeanalytics.aliyuncs.com -P10000 -u -p 在这篇文章里面,我会使用 MySQL 命令行给大家演示 DLA 的功能。
首先我们来建一个 DataLakeAnalytics 的数据库: CREATE DATABASE `mydb` WITH DBPROPERTIES ( catalog = oss, location = 'oss://your-bucket/mydb/' );
这里的 oss://mydb-bucket/mydb/ 就是前面我们数据汇聚的 person 目录的父目录。
建好库之后,我们再建一个表: CREATE EXTERNAL TABLE IF NOT EXISTS `person` ( `id` bigint, `name` varchar(128), `age` int ) PARTITIONED BY (region varchar(63)) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' STORED AS TEXTFILE LOCATION 'oss://mydb-bucket/mydb/person';
注意这是一个分区表,分区的key是我们的region,这样的好处一是各个地域在同步数据的时候比较简单,不用担心把别的地域的数据冲掉了;另外利用地域分区也使得我们在分析单个地域的时候扫描数据量会比较小,查询速度更快。
建好表之后,我们运行如下命令让 DataLakeAnalytics 去对OSS上的文件列表进行扫描以找到所有的 region 分区: mysql> msck repair table person; +-----------------------------------------------------------------------------------------------------------+ | Result | +-----------------------------------------------------------------------------------------------------------+ | Partitions not in metastore: person:region=cn-beijing person:region=cn-hangzhou person:region=cn-shanghai | | Repair: Added partition to metastore mydb.person:region=cn-beijing | | Repair: Added partition to metastore mydb.person:region=cn-hangzhou | | Repair: Added partition to metastore mydb.person:region=cn-shanghai | +-----------------------------------------------------------------------------------------------------------+
现在我们就可以开心的对所有地域的数据进行联合查询了 :) mysql> select * from person limit 5; +------+-------+------+-------------+ | id | name | age | region | +------+-------+------+-------------+ | 1 | james | 10 | cn-beijing | | 2 | bond | 20 | cn-beijing | | 3 | lucy | 30 | cn-beijing | | 4 | lily | 40 | cn-beijing | | 5 | trump | 10 | cn-hangzhou | +------+-------+------+-------------+ 5 rows in set (0.43 sec) mysql> select region, count(*) cnt from person group by region; +-------------+------+ | region | cnt | +-------------+------+ | cn-beijing | 4 | | cn-hangzhou | 4 | | cn-shanghai | 4 | +-------------+------+ 3 rows in set (0.18 sec)
总结
在这篇文章里面,我们介绍了一种通过 DataLakeAnalytics , OSS, DataX 进行跨地域数据分析的方法。限于篇幅的原因方案的很多细节没有进一步优化,比如我们其实可以对数据进行进一步按天分区,这样每天同步的数据可以更少,效率更高;再比如我们没有介绍如何周期性的进行数据同步,用crontab? 还是什么调度系统?这些就留给读者自己去探索了。
原文链接
数据库
2019-03-01 17:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>

一 总体描述
自己的实验虚拟数据库中的 alert 不停的报下面的错误.alert日志刷的闹心.
Sun Jun 10 23:44:42 2012
Error s in file /u01/app/ oracle /product/10.2.0/db_1/admin/testb/bdump/testb_j000_4944.trc:
ORA-00604: error occurred at recursive SQL level 1
ORA-08102: index key not found, obj# 239, file 1, block 1674 (2)
ORA-12012: error on auto execute of job 21
ORA-08102: index key not found, obj# 239, file 1, block 1674 (2)

二 操作环境
OS
$cat / etc /redhat-release
Red Hat Enterprise Linux Server release 5.6 (Tikanga)
$ uname -a
Linux stu00 2.6.18-238.el5 #1 SMP Tue Jan 4 15:24:05 EST 2011 i686 i686 i386 GNU/Linux
DB
SQL> set lines 150
COL PRODUCT FORMAT A55
COL VERSION FORMAT A15
COL STATUS FORMAT A15
SELECT * FROM PRODUCT_COMPONENT_VERSION;SQL> SQL> SQL> SQL>
PRODUCT VERSION STATUS

NLSRTL 10.2.0.4.0 Production
Oracle Database 10g Enterprise Edition 10.2.0.4.0 Prod
PL/SQL 10.2.0.4.0 Production
TNS for Linux : 10.2.0.4.0 Production
SQL>
other
三 排错思路概况
1.报错信息ORA-12012: error on auto execute of job 21,ORA-08102: index key not found, obj# 239, file 1, block 1674 (2) 是由于job 21执行的时候没有找到索引key.所以alert日志不停的刷.
2.查询job
3.查询索引
4.重建索引
四 详细步骤操作
1.查看 /u01/app/ oracle /product/10.2.0/db_1/admin/testb/bdump/testb_j000_4944.trc文件内容(汗,看不懂,这里跳过)
2.根据排错思路查看job 21的情况:
SQL> r
1* select job,log_user,interval,what from dba_jobs where job=21
JOB LOG_USER INTERVAL WHAT

21 SYS sysdate + 1 / (24 * 60) EMD_MAINTENANCE. EXECUTE _EM_DBMS_JOB_PROCS;
SQL>
原来是EM的维护操作,1分钟执行一次.
3.查询obj# 239号是什么obj:
SQL> r
1* select owner,object_name,object_id,object_type,status from DBA_OBJECTS where OBJECT_ID=239
OWNER OBJECT_NAM OBJECT_ID OBJECT_TYPE STATUS

SYS I_JOB_NEXT 239 INDEX VALID
SQL>
obj# 239真是一个索引哈,看status是valid的,这里竟然不一致.不管为什么不一致了,先解决这个alert再说. (嘿嘿,实验机有快照,有兴趣再研究的请留言)
4.重建问题索引:(使用在线重建方式)
SQL> ALTER INDEX I_JOB_NEXT REBUILD ONLINE;
Index altered.
SQL>
5.观察alert日志是否还有错误产生.
6.日志已无报错.
五 个人总结
面对报错不要心急,静心的去分析每一个报错提示.官方支持文档中有说使用索引重建方式依然解决不了 alert 日志报错的.唯有重建索引一种方式.这里附上使用dbms_metadata.get_ddl查找索引ddl的方式.
SQL> select DBMS_METADATA.GET_DDL('INDEX','I_JOB_NEXT','SYS') from dual;
DBMS_METADATA.GET_DDL('INDEX','I_JOB_NEXT','SYS')

CREATE INDEX "SYS"."I_JOB_NEXT" ON " SYS "."JOB$" ("NEXT_DATE")
PCTFREE 10 I
SQL >
六 资料参考引用
ORA-08102: TRYING TO MANIPULATE A JOB IN DBA_JOBS [ID 1036858.6]
数据库
2019-03-01 17:02:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
三者之间的区别(mysql 5.6+)
存储范围 占用空间(字节) 是否与时区相关 默认值 存储方式 编码方式 是否根据时区转换 date 1000-01-01

9999-12-31 3 无关 null 二进制 1bit用来存符号位,表示正负
1 bit sign (1= non-negative, 0= negative)
17bit用来存放年和月
17 bits year*13+month (year 0-9999, month 0-12)
5bit用来存放日
5 bits day (0-31)
---------------------------
一共23bit约为3个字节
23 bits = 3 bytes 否
datetime 1000-01-01 00:00:00

9999-12-31 23:59:59 5
当保存毫秒部分时使用额外的空间 (1到3 字节) 无关 null 二进制 1bit用来存符号位,表示正负
1 bit sign (1= non-negative, 0= negative)
17bit用来存放年和月
17 bits year*13+month (year 0-9999, month 0-12)
5bit用来存放日
5 bits day (0-31)
5bit用来存放时
5 bits hour (0-23)
5bit用来存放分
6 bits minute (0-59)
5bit用来存放秒
6 bits second (0-59)
---------------------------
一共40bit刚好5个字节
40 bits = 5 bytes 否
timestamp 1970-01-01 00:00:00

2038-01-09 03:14:07 4
当保存毫秒部分时使用额外的空间 (1到3 字节) 相关 CURRENT_TIMESTAMP(当前时间)
默认情况下,
insert、update 数据时,TIMESTAMP列会自动以当前时间填充/更新 二进制 存的是自【1970-01-01,UTC】以来的秒数。 是
PS:在5.6版本之后,datetime开始可以使用current time作为默认值,所以不再有timestamp比datetime更适合作为需要随插入和更新来变更时间的类型的说法
date这个主要是表示日期的,用途上面没有什么需要抉择的。
对于timestamp和datetime,道友们可能还有选择困难症,在这里我发表一下自己的看法:
在5.6+的mysql数据库中,timestamp这个基本可以弃用了,以datetime为主。如果遇到跨时区的需求时,建议使用long或者bigint进行时间戳的存储,然后在代码中进行转换,不建议用timestamp;
Mysql相关文档:https://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html
参考博文:https://juejin.im/entry/5b6fcb1b6fb9a009d15a2c5c
数据库
2019-03-01 15:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.安装memcache: http://www.runoob.com/memcached/window-install-memcached.html
2、下载php_memcache扩展 扩展php7下载地址: https://github.com/nono303/PHP7-memcache-dl http://pecl.php.net/package/memcache/3.0.8/windows 或 http://windows.php.net/downloads/pecl/releases/memcache/3.0.8/ 3、把php_memcached.dll扔进 php安装路径下的ext文件夹
4、修改php.ini  加一行(若存在直接#注释)  extension=php_memcache.dll
5、保存,重启apache,也就是重启(phpstudy或xamp或....)
6.然后 在php页面输出phpinfo();检查 memcache 是否成功加载了。
本章节重点难点(安装memcache时一定要用管理员身份安装哦,下载php_memcache扩展时注意选 TS/NTS 和x86/x64版本,可在phpinfo查到)
如果成功加载了 ,就可以 在一个php页面做 memcache测试了 public function testMemcache() { $memcache = new Memcache; $memcache->connect('127.0.0.1', 11211) or die('shit'); $memcache->set('key', 'hello memcache!'); $out = $memcache->get('key'); echo $out; }
成功的话会输出 hello memcache!
数据库
2019-03-01 11:31:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一、解压后配置
1,解压Redis-x64-3.2.100.zip,解压后的文件结构如下:
2,修改redis.windows.conf文件,设置maxmemory 大小
设置redis密码
redis启动命令:
D:\Redis-x64-3.2.100> redis-server redis.windows.conf
启动成功 就可以访问redis了
D:\Redis-x64-3.2.100> .\redis-cli.exe
二、设置Redis服务
1、由于上面虽然启动了redis,但是只要一关闭cmd窗口,redis就会消失。所以要把redis设置成windows下的服务。
也就是设置到这里,首先发现是没用这个Redis服务的。
2、设置服务命令
redis-server --service-install redis.windows-service.conf --loglevel verbose
输入命令之后没有报错,表示成功了,刷新服务,会看到多了一个redis服务。
3、常用的redis服务命令。
卸载服务:redis-server --service-uninstall
开启服务:redis-server --service-start
停止服务:redis-server --service-stop
4、启动服务
5、测试Redis
安装测试成功。
这里只是做简单的安装,部署服务使用,更深入的使用可以去redis中文网看看 https://www.redis.net.cn/博客园也有很多深入使用方法.在下载的解压包里面有一份文档,有详细的说明,
3.可视化窗口:
下载地址: https://redisdesktop.com/download

数据库
2019-03-01 10:54:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
linux安装参考: https://blog.csdn.net/gaokcl/article/details/83109684
一,Windows安装:
1,下载地址: https://github.com/MicrosoftArchive/redis/releases/tag/win-3.2.100    ( redis官网:  https://redis.io/   )
2,安装Redis-x64-3.2.100.msi ( 安装步骤,下一步下一步,有选择框选中即可), https://www.cnblogs.com/juncaoit/p/10122642.html 安装完成配置
3,设置 redis密码 : requirepass 123456 (我的是 123456) Ctrl+S
4,右击计算机(我的电脑) ===》管理
window + R 输入 cmd ===> cd D:\Redis-x64-3.2.100 ===> redis-cli =====> auth 123456
数据库
2019-03-01 10:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
事情是这样的。 这样的代码能行: $servername = "localhost"; $username = "username"; $password = "password"; // 创建连接 $conn = new mysqli($servername, $username, $password); // 检测连接 if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } echo "连接成功";
这样的代码能行: $servername = "localhost"; $username = "username"; $password = "password"; $link = mysqli_connect($servername, $username, $password, 'wxjs'); if (!$link) { echo "Error: Unable to connect to MySQL." . PHP_EOL; echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL; echo "Debugging error: " . mysqli_connect_error() . PHP_EOL; exit; } echo "Success: A proper connection to MySQL was made! The my_db database is great." . PHP_EOL; echo "Host information: " . mysqli_get_host_info($link) . PHP_EOL; mysqli_close($link);
输出: Success: A proper connection to MySQL was made! The my_db database is great. Host information: Localhost via UNIX socket
这样的代码不行: $servername = "127.0.0.1"; $username = "username"; $password = "password"; $link = mysqli_connect($servername, $username, $password, 'wxjs'); if (!$link) { echo "Error: Unable to connect to MySQL." . PHP_EOL; echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL; echo "Debugging error: " . mysqli_connect_error() . PHP_EOL; exit; } echo "Success: A proper connection to MySQL was made! The my_db database is great." . PHP_EOL; echo "Host information: " . mysqli_get_host_info($link) . PHP_EOL; mysqli_close($link);
输出: Error: Unable to connect to MySQL. Debugging errno: 2002 Debugging error: Permission denied
得出结论:用IP和用localhost是不一样的。
我还找到了一个很变态的方法解决 用 IP 不行的,居然是关掉防火墙…… $ sudo setenforce 0
MySQL 新手。幼稚了……
数据库
2019-03-01 10:13:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
【mysql】mysql5.7&CentOS6.8二进制方式安装 2018年05月18日 11:32:18 debimeng 阅读数:2726

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/debimeng/article/details/80362087 mysql5.7&CentOS6.8二进制方式安装
--环境描述
系统: CentOS6.8_x86_64
mysql: mysqlmysql-5.7.21
安装包:mysql-5.7.21-linux-glibc2.12-x86_64.tar.gz #注二进制安装包
安装包地址: https://dev.mysql.com/downloads/mysql/5.7.html#downloads
注:二进制包的话Select Operating System:这里勾选Linux-Generic --将mysql二进制包上传到服务器并解压
先创建/data,然后将安装包上传到这里
# mkdir /data
解压安装包到/data目录
# tar zxvf mysql-5.7.21-linux-glibc2.12-x86_64.tar.gz -C /data #注意如果需要解压到指定的目录需要加-C参数
更改文件夹名
# mv /data/mysql-5.7.21-linux-glibc2.12-x86_64 /data/mysql-5.7.21
--安装依赖包,使用yum安装,故前提条件是网络源的yum方式可用
# yum -y install libaio
--创建mysql用户和组
# groupadd mysql
# useradd -g mysql mysql
--配置数据库目录
数据目录: /data/mysql-5.7.21/data
参数文件my.cnf: /data/mysql-5.7.21/etc/my.cnf
错误日志log-error: /data/mysql-5.7.21/log/mysql_error.log
二进制日志log-bin: /data/mysql-5.7.21/log/mysql_bin.log
慢查询日志slow_query_log_file: /data/mysql-5.7.21/log/mysql_slow_query.log
套接字socket文件: /data/mysql-5.7.21/run/mysql.sock
pid文件: /data/mysql-5.7.21/run/mysql.pid
注:其中的mysql-5.7.21尽量和安装的mysql版本号一致,便宜管理。

# mkdir -p /data/mysql-5.7.21/{data,log,etc,run}
# touch /data/mysql-5.7.21/log/{mysql_error.log,mysql_bin.log,mysql_slow_query.log}
# chown -R mysql:mysql /data/mysql-5.7.21
# chmod 750 /data/mysql-5.7.21/{data,log,etc,run} --配置环境变量
# echo "export PATH=$PATH:/data/mysql-5.7.21/bin" >> /etc/profile
--配置my.cnf文件
# touch /data/mysql-5.7.21/etc/my.cnf
# chown mysql:mysql /data/mysql-5.7.21/etc/my.cnf

my.cnf文件内容如下:
[client]
port = 3306
socket = /data/mysql-5.7.21/run/mysql.sock

[mysqld]
port = 3306
socket = /data/mysql-5.7.21/run/mysql.sock
pid_file = /data/mysql-5.7.21/run/mysql.pid
datadir = /data/mysql-5.7.21/data
default_storage_engine = InnoDB
max_allowed_packet = 128M
max_connections = 2048
open_files_limit = 65535

skip-name-resolve
lower_case_table_names=1

character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'

innodb_buffer_pool_size = 128M
innodb_log_file_size = 128M
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 0

key_buffer_size = 16M

log-error = /data/mysql-5.7.21/log/mysql_error.log
log-bin = /data/mysql-5.7.21/log/mysql_bin.log
slow_query_log = 1
slow_query_log_file = /data/mysql-5.7.21/log/mysql_slow_query.log
long_query_time = 5

tmp_table_size = 16M
max_heap_table_size = 16M
query_cache_type = 0
query_cache_size = 0

server-id=1 --初始化
# mysqld --initialize --user=mysql --basedir=/data/mysql-5.7.21 --datadir=/data/mysql-5.7.21/data
此时会生成一个root临时登录密码,在mysql_error.log文件,
# grep 'temporary password' /data/mysql-5.7.21/log/mysql_error.log 注:有可能并没有写进文件,直接在执行后的 界面 显示
--配置服务
# cp /data/mysql-5.7.21/support-files/mysql.server /etc/init.d/
# mv /etc/init.d/mysql.server /etc/init.d/mysqld
修改mysqld文件
# vi /etc/init.d/mysqld
将basedir和datadir值修改如下,46和47行
basedir=/data/mysql-5.7.21
datadir=/data/mysql-5.7.21/data
加入服务
# chkconfig --add mysqld
设置开机自动启动和查看
# chkconfig mysqld on
# chkconfig mysqld --list
启动服务
# service mysqld start
查看mysql服务状态
# service mysqld status
--修改mysqlroot密码
# mysql -uroot -p临时密码
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';
mysql> flush privileges;
mysql> exit
重新使用修改后的密码登录
# mysql -uroot -p
然后输入临时密码
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
如果能查出如上的信息,表示安装成功。
数据库
2019-03-05 11:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
unsigned和zerofill unsigned是指的无符号,就是没有负数,只能用正数和0,此时unsigned修饰的字段的值可以达到的最大值是原来的最大值加1,即原来一个tinyint字段能表示的范围是:-128~127,而unsigned修饰的字段范围是:0~255。 zerofill是指的一个整型字段在允许的最大位数之前用0填充,比如一个字段设置为tinyint(4),需要用4个数字表示,而tinyint最大值为(假如是unsigned修饰)255,那么前面不足4位数都将用0填充,例如:0005, 0010, 0100, 0255。无论是int或是tinyint等整型数据,括号里面的数字只是代表这个数据最后的显示位数,不影响数据本身的存储大小。就是说int(1)和int(4)数据存储长度一样,int(1)还是会比tinyint(8)存储空间占用大。总结一下数据大小: tinyint(1)与tinyint(4)在占用空间大小上没区别,都是一个字节 int(1)与int(4)在占用空间大小上没区别,都是四个字节 int(1)比tinyint(4)占用空间大,但是对于zerofill修饰的字段,最终显示出来的tinyint(4)都是以4个数字(前面补0)呈现出来,而int(1)是不用0补齐来呈现的 tinyint一个字节 smallint 两个字节 MEDIUMINT三个字节 int 4个字节 BIGINT 8个字节,因此在选择字段类型的时候,在够用的情况下,能选择小的就尽量选择小的,节省更多的空间 int(6)与varchar(6)这两个6所表示的含义是不一样的,int(6)只会影响zerofill字段最终的显示长度,而varchar(6)会要求字符串存储的长度不超过6字节
字段类型说明
各种字段类型在数据库存放的空间占用大小如下表
数据类型 默认范围 unsigned范围
tinyint -2^7 ~ 2^7-1 0 ~ 2^8-1
smallint -2^15 ~ 2^15-1 0 ~ 2^16-1
MEDIUMINT -2^23 ~ 2^23-1 0 ~ 2^24-1
int
bigint
-2^31 ~ 2^31-1
-2^63 ~ 2^63-1
0 ~ 2^32-1
0 ~ 2^64-1
数据库
2019-03-05 08:50:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1. 首先确实是否已安装gcc yum search gcc-c++
2. 如果未安装,则先安装gcc yum install gcc-c++
3. 下载redis安装包,可以网络下载,也可以本地上传 wget http://download.redis.io/releases/redis-5.0.5.tar.gz
4. 解压压缩包 tar -zxvf redis-5.0.5.tar.gz
5. 移动文件夹到 /usr/local/ 目录下 mv ./redis-5.0.5 /usr/local/redis
6. 安装,切换到/usr/local/redis目录下 make
7. 修改配置文件 redis.conf,设置daemonize属性为yes,使得redis以守护进程的方式运行。 vi redis.conf
8. 启动redis,并指定配置文件。 /usr/local/redis/src/redis-server /usr/local/redis/redis.conf
数据库
2019-06-21 16:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.闲着无聊在客户一套较为重要的系统中提取了一个4天的AWR,发现下面这条SQL产生最多的物理读,四天运行了159次。平均每次物理读1G左右。SQL运行时间三分钟左右。
SQL:
SELECT DECODE(B.DH, 'MAIN', :1, B.DH) DH, COUNT(DISTINCT(A.PROCESSID)) VALUE,
B.MC COMPANYNAME FROM FMIS3000.WF_ACTIVITIES A, (SELECT DH, MC FROM FMIS3000.XTDW WHERE 1=1 )
B WHERE A.ACTTYPE = 1 AND TRIM(A.DH) = TRIM(B.DH) GROUP BY B.DH, B.MC;
2.我查看了执行计划:
PLAN_TABLE_OUTPUT

Plan hash value: 2953189885

| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | 191K| 18M| | 300K (1)| 01:10:12 |
| 1 | SORT GROUP BY | | 191K| 18M| 4006M| 300K (1)| 01:10:12 |
|* 2 | HASH JOIN | | 37M| 3516M| | 45650 (1)| 00:10:40 |
| 3 | TABLE ACCESS FULL| XTDW | 528 | 18480 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| WF_ACTIVITIES | 7054K| 430M| | 45478 (1)| 00:10:37 |

PLAN_TABLE_OUTPUT

Predicate Information (identified by operation id):

2 - access(TRIM("A"."DH")=TRIM("DH"))
4 - filter("A"."ACTTYPE"=1)
17 rows selected.
3.我注意到有两个全表扫描,于是我查找执行这个SQL时 两个表会返回的数据量
这是一个HASH连接 FMIS3000.XTDW 表为驱动表,可以看到他数据量不大,作为驱动表还是很合适的,走table access full,也比较合理。
SQL> select count(*) from FMIS3000.XTDW ;
COUNT(*)

530
4.这是被驱动表,数据量比较大。我发现这儿应是一个瓶颈。
SQL> select count(*) from FMIS3000.WF_ACTIVITIES where ACTTYPE=1;
COUNT(*)

12078901
5.这个查询返回1.2KW数据,整个表有1.7KW数据。走全表扫描其实并没有错,但是我在想如何解决这个瓶颈
6.接着我返回去看SQL,发现我不需要扫描整个表的数据:
经过查询这个表有26列,我可以在需要的列上面创建索引。
create index id_tex on FMIS3000.WF_ACTIVITIES(ACTTYPE,TRIM(DH),PROCESSID);
在这儿我没有考虑选择性(因为不需要,而且选择性肯定奇差),我推测这样,可以走INDEX FAST FULL SCAN,且不用回表。将会有效提升效率。
建立索引前:
PLAN_TABLE_OUTPUT

SQL_ID 4n6ajf2fsm2x9, child number 0

SELECT DECODE(B.DH, 'MAIN','MAIN', B.DH) DH, COUNT(DISTINCT(A.PROCESSID)) VALUE, B.MC COMPANYNAME FROM
FMIS3000.WF_ACTIVITIES A, (SELECT DH, MC FROM FMIS3000.XTDW WHERE 1=1 ) B WHERE A.ACTTYPE = 1 AND
TRIM(A.DH) = TRIM(B.DH) GROUP BY B.DH, B.MC
Plan hash value: 2953189885

| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes |

| 1 | SORT GROUP BY | | 1 | 191K| 475 |00:02:51.39 | 285K| 423K| 138K|
|* 2 | HASH JOIN | | 1 | 37M| 12M|00:01:12.56 | 285K| 285K| 0 |
| 3 | TABLE ACCESS FULL| XTDW | 1 | 528 | 530 |00:00:00.02 | 10 | 8 | 0 |
|* 4 | TABLE ACCESS FULL| WF_ACTIVITIES | 1 | 7054K| 12M|00:00:48.38 | 285K| 285K| 0 |

Statistics

1082 recursive calls
71 db block gets
285137 consistent gets
311743 physical reads
0 redo size
2208 bytes sent via SQL*Net to client
514 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
0 sorts (memory)
1 sorts (disk)
30 rows processed
建立索引后:
PLAN_TABLE_OUTPUT

SQL_ID g5pavm81dbpdt, child number 0

SELECT DECODE(B.DH, 'MAIN', 'MAIN', B.DH) DH, COUNT(DISTINCT(A.PROCESSID)) VALUE, B.MC COMPANYNAME
FROM FMIS3000.WF_ACTIVITIES A, (SELECT DH, MC FROM FMIS3000.XTDW WHERE 1=1 ) B WHERE A.ACTTYPE = 1
AND TRIM(A.DH) = TRIM(B.DH) GROUP BY B.DH, B.MC
Plan hash value: 513252325

| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | 191K| 18M| | 341K (2)| 01:08:17 |
| 1 | SORT GROUP BY | | 191K| 18M| 4062M| 341K (2)| 01:08:17 |
|* 2 | HASH JOIN | | 37M| 3516M| | 39892 (2)| 00:07:59 |
| 3 | TABLE ACCESS FULL | XTDW | 528 | 18480 | | 4 (0)| 00:00:01 |
|* 4 | INDEX FAST FULL SCAN| TE_DX | 7054K| 430M| | 39524 (2)| 00:07:55 |

Statistics

1074 recursive calls
51 db block gets
179736 consistent gets
124315 physical reads
0 redo size
2208 bytes sent via SQL*Net to client
514 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
0 sorts (memory)
1 sorts (disk)
30 rows processed
这个执行计划是真实的
可以很明显的看到,不管是逻辑读和物理读都少了一倍左右。(我在执行前都flush了share pool和buffer cache)
建立索引之后 CBO自动选择了INDEX FAST FULL SCAN ,而且确实没有回表。和预估的结果一样。
总的执行时间减少了一倍,在扫描WF_ACTIVITIES表阶段 时间从48秒减少到了8秒。性能提升了6倍。
但是这个SQL还有个瓶颈是SORT GROUP BY 也就是DISTINCT排序导致的。 我也试验了,如果不要distinct,执行时间还可以减少30多秒,这个和业务相关了,就没有太大办法,而且被驱动表返回结果1.2KW行,hash不可能不消耗时间,这个确实没办法。如果知道业务或许可以改写SQL。
数据库
2019-06-21 16:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在业务查询中经常会出现【过滤出最新一条记录】的需求,下面写一个用【NOT EXISTS】实现的小例子记录一下: SELECT t1.unit_id, IFNULL( COUNT(distinct t.person_phr_code ), 0 ) AS visit_person_num, IFNULL( COUNT(distinct CASE WHEN t.avg_sbp < 130 AND t.avg_dbp < 80 THEN t.person_phr_code ELSE NULL END ), 0 ) AS person_num_80_130, IFNULL( COUNT(distinct CASE WHEN t.avg_sbp < 140 AND t.avg_dbp < 90 THEN t.person_phr_code ELSE NULL END ), 0 ) AS person_num_90_140 FROM yida_historical_visit_info t, yida_person_basic_info t1 WHERE t.person_phr_code = t1.person_phr_code AND NOT EXISTS ( SELECT 1 FROM `yida_historical_visit_info` n WHERE t.person_phr_code = n.person_phr_code AND t.visit_date < n.visit_date) GROUP BY t1.unit_id
数据库
2019-06-21 15:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
NSERT IGNORE 与INSERT INTO的区别就是INSERT IGNORE会忽略数据库中已经存在 的数据,如果数据库没有数据,就插入新的数据,如果有数据的话就跳过这条数据。这样就可以保留数据库中已经存在数据,达到在间隙中插入数据的目的。
eg: insert ignore into table(name) select name from table2

mysql中常用的三种插入数据的语句:

insert into表示插入数据,数据库会检查主键(PrimaryKey),如果出现重复会报错;

replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引的话,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;
REPLACE语句会返回一个数,来指示受影响的行的数目。该数是被删除和被插入的行数的和。如果对于一个单行REPLACE该数为1,则一行被插入,同时没有行被删除。如果该数大于1,则在新行被插入前,有一个或多个旧行被删除。如果表包含多个唯一索引,并且新行复制了在不同的唯一索引中的不同旧行的值,则有可能是一个单一行替换了多个旧行。

insert ignore表示,如果中已经存在相同的记录,则忽略当前新数据;
数据库
2019-06-21 10:02:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
《伟大的博弈》读后感作文4100字:
五一小长假,作为工厂员工,没能跟上大家放假的步伐,窝家里两天没出门,逼着自己看完《伟大的博弈》,感触颇多。似乎不能消化,先记录第一遍作为幼稚的见证。
科技进步,人类发展,但人性永恒不变。那些刻在基因里的代码,引领我们走出了非洲丛林,从猿猴变成人。但是,也正是因为这些基因里的代码,让我们不断重复曾今的过错。每一次都会以为这次不一样,但是这次真的不一样吗?
这本书的书名,起得非常的经典,用一个词完美的概括了美国的金融史。博弈,并且是伟大的博弈。是的,人类的存活是关乎于选择的问题,在不同的选择面前,通过各种利益权衡,最终形成了这样的我们。这个博弈的过程,有自然的选择,也有人类的文明选择。资本市场也一样,在博弈的过程中,逐渐发展壮大并完善。从无序到有序,从自由到监管,从传统到创新,都是一系列博弈的过程。
看不见的手与看得见的手
从1653到2019,美国的资本市场从无到有,从路边交易到交易所,从纽约到全球。这个过程,最大的博弈方,就是看不见的手和看得见的手之间的博弈。市场这只看不见的手,具有自我调节作用,在人类社会进步中发挥着重要的作用,甚至是主要的作用。
因为有市场交换的存在,才使得分工与规模化生产得以实现。一个人再也不用掌握所有的生存技能,反而只要掌握自己擅长的技能,并使其专业化,便可以活得更好。
因为分工促使专业化形成,促使规模化生产得以实现。但是市场并不完美,调节具有滞后性,也有短视性,所以伴随着经济的发展,人口规模的扩大,总会有那只看得见的手产生,并积极实施影响,甚至想取代那只看不见的手。比如计划经济。
要自由还是要监管?仅仅看历史的发展,我们也许会发生监管更多了,从《梧桐树协议》到《证券法》在到后面的一系列法案,看似看得见的手更强大了。每次金融危机发生后,是任其自由还是积极救市,总是有不同的声音。甚至连总统层面都有这样的声音。
亚历山大.汉密尔顿将军的积极救市,成立中央银行的措施,良好的控制住恐慌。但是托马斯.杰斐逊总统称华尔街为人性的大阴沟,不仅关闭了中央银行,红楼梦读后感(http://www.simayi.net/duhougan/8051.html)还偿还了所有的债务。后来的安德鲁.杰克逊,执政党在促进金融市场的发展和抑制其发展,反复多次博弈,最终形成现在的纽约金融中心。
早年的胡佛总统在1929年的经济危机中,处理失当。在1000多名经济学联名家反对声中,签署了《斯幕特-霍利关税法案》。并且在危机刚刚发生时,在白宫召开会议,反而引起市场更大的恐慌。(P417)
当年不但没有缓解危机,反而带入更深的泥潭。最近得到包刚升老师的《政治学通识30讲》,讲这件事情是因为胡佛政府的那只看得见的手,想要发挥作用,但是忘记了市场那只看不见的手的反作用。说看得见的手,一旦存在,总是想要发挥作用,所以不仅仅是因为他有权利管,而是他的位置相让他管,甚至很多时候,这只手闲不住。胡佛总体也因此被认为是历史上最差的总体。
不管怎么样,我们总体可以看出,在自由与监管的博弈中,市场更加完善,功能更加齐全,更好的促进了经济的发展,把蛋糕做大,让更多人获得了更多的财富,并且能参与其中。
现今,金融不只是少数人的盛宴,而是只要你想,你有能力,一部手机就可以加入金融这个资本大市场。不过不要忘记了,只要有博弈,就会有输赢。不管监管多么的完善,不管市场多么的有效,只要存在不确定性,就会有波动,就是有机会,也就会有盈亏。
监管是为了更大的自由。
市场并不看重你是谁,仅仅看中你是否足够聪明及幸运。比如总体,能在南北战争中带领北方军队获胜,并且成为总统,但是最后在纽约的资本市场中,亏得只剩下200美元,最后只能靠写传记补贴家人,当然,也因此写成了最伟大的传记之一《格兰特将军回忆录》。
博弈,不仅仅是在自由市场与监管之间,还在于投资者与融资中之间,管理者与所有者之间,公共利益,国与国的博弈,等等。
货币可控性与金融危机
从全书可以看出,危机更多情况下,是由于市场的恐慌情绪引发的。如果在危机发生后,能积极救市,给予市场信心,那么危机的破坏就没那么大。
情绪是难以控制的。比如2019年的今天,A股下跌的势头非常的强劲,哪怕一大早就有定向降准的通知,市场依然下跌。事情的导火索仅仅是川普的一个推文,不见得会实锤。金融,是多方博弈的结果。是科学,也是艺术,有更多不确定性因素来自于参与者本身。
非纸币作为主要流通或法定货币的时期,具有供给与需求的双向不确定性,使市场处于被动的波动中。特别是当发现或者生产更多的贵金属,或者金属本身作为原材料需求上升,总有投机者试图操纵市场。
书里就有两次金属货币操纵风波,一次黄金,一次白银。贵金属作为货币的不确定性,以及携带计量的不方便性,在技术进步中,逐渐被纸币取代,也是必然之势。而今,电子货币的兴起,方便快捷,货币总量也有范围可行。一个强势但又有克制的央行,就显得非常有必要。
而更有意思的是,有一本《银线:19世纪的世界与中国》的书。讲清朝后期,中国闭关锁国,连历史书上也是这样讲。但是实际情况是,我们所用的银子不是中国生产的,而是靠西班牙从墨西哥进口。这样一来,虽然实体上的大门被封锁,但是货币靠外国的时候,中国早已是世界中的一部分,但又没主动参与,并弄清情况,导致了后面一系列的悲剧发生。
货币做为交易的媒介,是价值的体现形式。曾今货币的发现标的是其背后的贵金属,后来为石油。未来,是一个国家的信用、经济走势等等综合国力及大国担当力。
未来,货币将变得越来越复杂。因为资金的便利性、流动性,如果经济形势不好,定价体系混乱,钱会从各种渠道流进/流动,变得难以琢磨。当那只看不见的手,变得越来越强大的时候,看得见的手,如果不能一并成长,危机指日可待。
未来的社会,看得见的手,要变得更加聪明,更有担当,才能维持金融界的暂时稳定。经济社会,没有一劳永逸,总有一些不确定的因子,推动者社会的进步。
资本+科技,经济的腾飞
纽约的经济地位,不是一天造就的。良好的利用了资本+科技,通过科技的进步推进了社会的进步,做大蛋糕的总量,给资本家带来大量的财富和社会地位,也同时造福利大量的人类。迄今,中国贫困人口逐步下降。曾今生活在云南乡村的我,得以坐在广东省的一个三线城市,吹着空调看书,不得不承认是经济的繁荣与发展带来的结果,跟自身努力没多大关系。
15世纪文艺复兴,印刷术的进步,让印刷成本大大降低,从而使信息的传播变得低廉,知识也可以被大众获得并掌握。
16世纪,蒸汽机的改良,让铁路运输变得可行,西部的物资大量运输到东部,也造就了规模化生产的可行性。
纽约的伊利运河的开凿成功,连接了五大湖和纽约地区,让货运成本大幅降低,促进了纽约的繁荣。
后期的电器、汽车、通行、航天航空,更进一步推进了经济的发展和科技的进步。
纽约成为全球最大的金融中心,与紧跟科技技术发展分不开。从曾今的runner跑单交易,到后来的电报,再到大西洋电缆线,让纽约跟欧洲相连接,引入欧洲资本,促进了纽约的进一步繁荣。
21世纪,互联网的发展,以苹果、微软、亚马逊、google、facebook、airbnb为代表的科技巨头的崛起,就是极好的资本+科技的发展例子。资本让科技更快速的落地,更进一步的推动人类社会的进步。
如今,资本家跟以前一样贪婪,但是因为有了更快、更大、更全的载体,科技进步的速度超越我们的想象。资本和产业,也联系得更加的紧密,彼此难以分开。虽然资本家依然贪婪,参与者也一样恐惧,人性,其实也没多大的改变,但是因为科技的进步,让蛋糕变大。如果更好的利用资本,将会带来正向的发展。如果不敬畏市场,不好好利用资本和市场的力量,走不出大草原期的行为模式,苦难不断。
政治经济学是否为变为经济政治学
2016年特朗普当选美国总统。2017年马克龙当选法国总统。这两个人的共同特点,就是没很强的政治经验。一个是美国商界大亨,一个是法国的经济顾问。他们的当选,很多人觉得不可思议。但是现在回头看,通过书中的观点,他们至少都应该懂经济。参选的目的也是带领经济腾飞。对于普通百姓、中产阶级,这是一个利好的面,不当选都不可能。
2008年美国的次贷危机,如果简单粗暴的归咎于资本家的贪婪,真的有点不负责任。从该书作者的观点,我们至少可以追溯到罗斯福新政,然后到克林顿修正的《社区再投资法》。产生了房地美和房利美这样的奇拉美——-希腊神话中的狮头、羊身、蛇尾的怪物。不熟悉经济运作规律,但又有政治资本,能获大量的补贴。这样盈亏不需要自负,还可以不断获得补贴奖金的机构,没有KPI业绩考核,自然不会对市场产生敏锐的嗅觉,也不会主动防范风险,聚集大量的风险。
从2003年开始,小布什就要求国会对“两房”实施更加严格的监管和监督。美联储主席格林斯潘也持有相同观点。但是即提示有风险,但依然错过这个修正机会。
奥巴马能连任,但是他执政期间,经济疲软,一系列法案,明显的违背经济规律。特朗普执政的3年间,不断修正/废除一些法案。未来是否还有更多的法案存在潜在风险,不得而知。但是,未来,政治经济学也许应该改名为经济政治学?多点经济规律,让经济与政治完美结合,致力于做大蛋糕,促使人类文明进一步发展。
初读此书,买空卖空、对敲交易、期权期货,是早在大航海时代就开始的交易手段,至今仍然是主要的交易手段。一个完善的交易环境,一个有效的市场,总是能促进经济的繁荣。当然其中也有很多运气的成分,比如三次危机中,都发生了战争。为了生产足够的军需产品,筹措战争款项,各种金融产品及工具被开发、被创新,引领经济走出萧条。也慢慢奠定了纽约的金融中心的位置。未来,这样的运气,当然不会是战争。但也许是科技的进步,认知模型的升级,新能源的发现,等等,不得而知。从1653到2019,366年间,财富总体是正向飞速增长的。作为个体,跟上经济发展的节奏,一直进步,总能享受经济繁荣带来的果实。
中国股市从1990年到2019年,仅仅29年。很多知识和经验更多来源与西方。未来,通过自己的积累与完善,地位提升,影响加大,共享经济繁荣带来的果实。
未来,依然充满不确定性。自律+学习,提升认知,认清博弈的世界,并积极参与!
数据库
2019-06-20 23:43:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MySQL 存储过程简单实例
==============================
1、创建实例数据库
CREATE DATABASE procdb DEFAULT CHARSET utf8;
USE procdb;
==============================
2、创建要使用的表
CREATE TABLE tb
(
id INT PRIMARY KEY NOT NULL,
cname VARCHAR(20)
);
INSERT INTO tb VALUES
(1,'统一方便面'),
(2,'康师傅方便面'),
(3,'马老表方便面'),
(4,'王老吉方便面');
SELECT * FROM tb;
==============================
3、创建一个最简单的存储过程
DELIMITER $$
CREATE PROCEDURE p1()
BEGIN
SELECT id,cname FROM tb;
END$$
DELIMITER ;
#调用存储过程p1
CALL p1();
==============================
################
DELIMITER $$
CREATE PROCEDURE p2()
BEGIN
#declare @dt int; ##使用临时变量不用声明数据类型
SET @dt =CURDATE();
SELECT CONCAT(YEAR(@dt),'年',MONTH(@dt),'月',DAY(@dt),'日') ;
END$$
DELIMITER ;
#调用存储过程P2
CALL p2();
==============================
4、创建一个带传入参数的存储过程
DROP PROCEDURE IF EXISTS p3;
DELIMITER $$
CREATE PROCEDURE p3(IN aid INT)
COMMENT '向表中插入一条记录'
BEGIN
##定义一个整形变量
DECLARE v1 INT;
##将输入参数的值赋给变量
SET v1 = aid;
##执行插入操作
INSERT INTO tb(id,cname) VALUES(v1,CONCAT('第',v1,'条记录'));
END$$
DELIMITER ;
##调用存储过程,使用一个参数,就会在表中插入一条记录
CALL p3(99);
SELECT * FROM tb;
==============================
5、带输出参数的存储过程
DROP PROCEDURE IF EXISTS p4;
DELIMITER $$
CREATE PROCEDURE p4(OUT ret INT)
COMMENT '输出表中id的最大值'
BEGIN
DECLARE maxid INT;
SELECT MAX(id) INTO maxid FROM tb;
SET ret=maxid;
END$$
DELIMITER ;
#调用存储过程
CALL p4(@maxno);
SELECT @maxno; #查看临时变量@maxno
==============================
6、带输入输出参数的存储过程
DROP PROCEDURE p5;
DELIMITER $$
CREATE PROCEDURE p5(IN p1 INT , OUT p2 INT)
COMMENT '带输入输出参数的存储过程'
BEGIN
IF p1 = 1 THEN
#用@符号加变量名的方式定义一个变量,与declare类似
SET @v = 10;
ELSE
SET @v = 20;
END IF;
#语句体内可以执行多条sql,但必须以分号分隔
INSERT INTO tb(id,cname) VALUES(@v,CONCAT('第',@v,'条记录'));
SELECT MAX(id) INTO p2 FROM tb;
END$$
DELIMITER ;
#调用存储过程
CALL p5(2,@maxid);
SELECT * FROM tb;
SELECT @maxid;
==============================
7、既做输入又做输出参数的存储过程
DROP PROCEDURE IF EXISTS p6;
DELIMITER $$
CREATE PROCEDURE p6(INOUT argument INT)
COMMENT '既做输入又做输出参数的存储过程'
BEGIN
IF argument = 4 THEN
SET @pg = 400;
ELSE
SET @pg = 500;
END IF;
SELECT @pg;
SET argument=@pg;
END$$
DELIMITER ;
# 调用存储过程,这里需要先设置一个已赋值的变量,然后再作为参数传入,同时也作为输出参数
SET @pp = 5;
CALL p6(@pp);
SELECT @pp;
数据库
2019-06-20 17:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
回收站(Recycle Bin)从原理上来说就是一个数据字典表,放置用户删除(drop)掉的数据库对象信息。用户进行删除操作的对象并没有被数据库删除,仍然会占用空间。除非是由于用户手工进行Purge或者因为存储空间不够而被数据库清掉。
如果一个表被drop删除,那么与表关联的对象、约束条件、索引都会一并删除。
2种查看回收站的当前状态:(默认是打开的)
SQL> show parameter bin
NAME TYPE VALUE
cursor_bind_capture_destination string memory+disk
recyclebin string on
SQL> SELECT Value FROM V$parameter WHERE Name = 'recyclebin';
VALUE
ON
启动或者关闭回收站里的每个会话(session)和系统(system),代码如下:
ALTER SYSTEM SET recyclebin = ON;
ALTER SESSION SET recyclebin = ON;
ALTER SYSTEM SET recyclebin = OFF;
ALTER SESSION SET recyclebin = OFF;
注:执行ALTER SESSION SET recyclebin = OFF只适用于当前会话窗口,重新复制新窗口默认回收站依旧是打开的,测试如下:
SQL> ALTER SESSION SET recyclebin = OFF;
Session altered.
新开窗口:
[oracle @strong ~]$ sqlplus / as sysdba
SQL*Plus: Release 11.2.0.4.0 Production on Thu Jan 28 18:03:00 2018
Copyright (c) 1982, 2013, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> show parameter bin
NAME TYPE VALUE
cursor_bind_capture_destination string memory+disk
recyclebin string on
修改system系统级别的测试如下:
SQL> ALTER SYSTEM SET recyclebin = OFF;
ALTER SYSTEM SET recyclebin = OFF
*
ERROR at line 1:
ORA-02096: specified initialization parameter is not modifiable with this
option
报错:不允许这样操作
原因:回收站是数据文件里面的动态参数,需要添加spoce=spfile,重启数据库才能修改成功

测试如下:
SQL> alter system set recyclebin=off scope=spfile;
System altered.
[root @strong ~]# reboot
Broadcast message from root @strong
(/dev/pts/5) at 18:05 ...
The system is going down for reboot NOW!

[root @strong ~]# su - oracle
[oracle @strong ~]$ sqlplus / as sysdba
SQL*Plus: Release 11.2.0.4.0 Production on Thu Jan 18 18:10:29 2018
Copyright (c) 1982, 2013, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> show parameter bin
NAME TYPE VALUE
cursor_bind_capture_destination string memory+disk
recyclebin string OFF

注:生产线上建议把回收站设置为on打开,当我们有误操作删除表时,还可以到回收站找回(以上只是我本人的测试,不建议在生产线操作)。



数据库
2019-06-20 16:56:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、根据查询语句创建表
CREATE table <表名称> as