数据专栏

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

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
如需转载请注明出处 https://my.oschina.net/feistel/blog/3052024
目的:迅速激活Oracle SQL
参考:《Oracle从入门到精通》
----------------------------------------------------------------------------------------------------------------
DDL
create/drop/alter。
--创建表 产地
create table origin (
OriginId varchar2(10),
OriginName varchar2(20),
constraint pk_origin primary key(OriginId)
);
-- 创建表 商品
create table productinfo (
ProductId varchar2(10),
ProductName varchar2(20) not null,
ProductPrice number(8,2) not null,
OriginId varchar2(10)
-- 主键约束
-- constraint pk_productinfo primary key(ProductId)
--外键约束
/*constraint fk_productinfo foreign key(OriginId)
references origin(OriginId)
-- 级联删除(主键所在表的记录删除时,该表记录也被删除)
on delete cascade*/
);
-- 删除字段
alter table productinfo
drop column ProductPrice;
-- 添加字段
alter table productinfo
add ProductPrice number(8,2);
-- 修改字段类型
alter table productinfo
modify ProductPrice number(10,2);
-- 添加主键约束
alter table productinfo
add constraint pk_productinfo primary key(ProductId);
-- 移除主键约束
alter table productinfo
drop constraint pk_productinfo;
-- 添加外键约束
alter table productinfo
add constraint fk_productinfo foreign key(OriginId)
references origin(OriginId)
-- 级联删除(主键所在表的记录删除时,该表记录也被删除)
on delete cascade;
-- 删除外键约束
alter table productinfo
drop constraint fk_productinfo;
-- 添加check约束
alter table productinfo
add constraint ch_productinfo check(ProductPrice>0);
-- 删除check约束
alter table productinfo
drop constraint ch_productinfo;
-- 添加unique约束
alter table productinfo
add constraint uni_productinfo unique(ProductPrice);
-- 删除unique约束
alter table productinfo
drop constraint uni_productinfo;
-- 设置not null约束
alter table productinfo
modify OriginId not null;
-- 取消not null约束
alter table productinfo
modify OriginId null;
-- 删除表 商品
drop table productinfo;
-- 删除表 产地
drop table origin;
----------------------------------------------------------------------------------------------------------------
DML
对数据库中的数据进行操作,增、删、改,查作为DQL。
-- 增加数据
insert into origin(OriginId, OriginName)
values('1', '广东省深圳市');
insert into origin(OriginId, OriginName)
values('2', '海南省万宁市');
insert into origin
values('3', '广东省深圳市');
insert into origin
values(null, '广东省深圳市');
insert into productinfo
values('1', '牙刷', 9.9, '1');
insert into productinfo
values('2', '牙膏', 16, '2');
insert into productinfo
values('3', '牙刷', 12, '2');
insert into productinfo
values('4', '牙刷', 16, '2');
insert into productinfo
values('5', '牙刷', 100, '100');
/*-- 通过表快速创建子表
create table origin2 as
select OriginId, OriginName
from origin;
drop table origin2;*/
-- 修改数据
update origin set OriginName = '广东省广州市' where OriginId = '3';
-- 删除数据
delete from origin where OriginId = '3';
-- 截断表(比 delete from origin 快)
truncate table origin;
----------------------------------------------------------------------------------------------------------------
DQL
-- 降序查询desc(默认升序asc)
-- 多列排序
-- ull值默认为最大(可以修改nulls last)
select OriginId 产品ID, OriginName 产品名称
from origin
order by 2 asc, OriginId desc
nulls first;
--Oracle架构与MySQL不同:
--1)Oracle,一个用户对应一个模式,这里的模式对应MySQL中的数据库。
----对于Oracle来说没有数据库这个说法。
----Oracle实例下有多个模式,模式下有多个表。
--2)MySQL,一个用户可以管理多个数据库。
----MySQL下有多个数据库,数据库下有多个表。
----MySQL中新建数据库,可以看作Oracle实例下新建模式,即创建新用户,因为一个用户对应一个模式。
-- 查询其他模式下的表
select system.help.info from system.help;
-- ||表示连接字符串
select ProductName, ProductPrice || '*0.8=' || ProductPrice*0.8 as DiscountPrice
from productinfo;
-- 除去重复
select distinct(OriginName) from origin;
-- 条件查询
-- 不等于也可以用!=
-- 模糊查询(_匹配单字符,%匹配多个字符)
-- in在某个范围之内
-- is null查询空
select *
from origin
where OriginName <> '海南省万宁市'
and OriginId between '1' and '3'
and OriginName like '%广东%'
and OriginId in('1','2')
and OriginId is not null;
-- group by / having子句
-- 先where筛选再分组
-- 根据产品名分组后再根据产地ID分组,计算平均价格,最后having筛选
select ProductName, avg(ProductPrice) 平均价格, OriginId
from productinfo
where OriginId <= 2
group by ProductName, OriginId
having avg(ProductPrice) <= 10;
-- 子查询
-- >=any表示大于它的最小值
-- <=any表示小于它的最大值
-- >=all表示大于它的最大值
-- <=all表示小于它的最小值
select *
from productinfo
where ProductPrice >=
any (select ProductPrice from productinfo where ProductId != '1')
and ProductPrice <=
all (select ProductPrice from productinfo where ProductId != '1');
-- 连接查询 内连接=简单连接
select p.ProductId, p.ProductName, o.OriginName
from productinfo p, origin o
where p.OriginId = o.OriginId;
-- 另一种写法(内连接)
-- select p.ProductId, p.ProductName, o.OriginName
-- from productinfo p inner join origin o on p.OriginId = o.OriginId;
-- 外连接
-- left join左外连接
-- right join右外连接
-- full join全连接
select p.ProductId, p.ProductName, o.OriginName
from productinfo p left join origin o on p.OriginId = o.OriginId;
--另一种方法(左外连接)
select p.ProductId, p.ProductName, o.OriginName
from productinfo p, origin o
where p.OriginId = o.OriginId(+);
-- 自连接
-- 查询自身价格相同的商品
select p1.ProductId, p1.ProductName
from productinfo p1, productinfo p2
where p1.ProductPrice = p2.ProductPrice
and p1.ProductName <> p2.ProductName;
----------------------------------------------------------------------------------------------------------------
DCL
-- 创建默认用户
create user kangkang
identified by 123456;
grant create session to kangkang;
-- 创建用户
-- 验证方式为口令验证,口令123456
-- 设置默认表空间
-- 设置默认临时表空间
-- 设置该用户能使用的表空间大小(unlimited不受限制)
-- 设置概要文件
-- 设置口令过渡状态,第一次登陆成功需要修改口令
-- 设置账户解锁
create user lilei
identified by 123456
default tablespace USERS
temporary tablespace TEMP
quota 10M on USERS
--profile DEFAULT
--password expire
account unlock;
-- 赋予用户创建会话的权限
grant create session to lilei;
-- 修改用户
alter user lilei
identified by 888888
password expire;
-- 删除用户
drop user lilei cascade;
-- 给用户授权 系统权限
-- 用户也具有授予all privileges的权限
grant all privileges to lilei
with admin option;
-- 给用户授权 对象权限(可以是all)
-- 用户也具有授予对象权限的权限
grant insert,delete on origin to lilei
with grant option;
-- 撤销权限 系统权限
revoke all privileges from lilei;
-- 撤销权限 对象权限
revoke insert,delete on origin from lilei;
-- 查询用户权限 数据字典
-- 系统权限 DBA_SYS_PRIVS
-- 对象权限 DBA_TAB_PRIVS
select * from DBA_SYS_PRIVS
where grantee = 'lilei';
-- 创建角色
create role all_privileges
--not identified
identified by 123456;
-- 给角色授权
grant all privileges to all_privileges;
-- 设置角色
grant all_privileges to lilei;
-- 在当前用户设置角色
set role all_privileges identified by 123456;
-- 修改角色
alter role all_privileges
not identified;
-- 删除角色
drop role all_privileges;
-- 查询角色
select GRANTED_ROLE.DEFAULT_ROLE from DBA_ROLE_PRIVS
where grantee = 'LILEI';
----------------------------------------------------------------------------------------------------------------
如需转载请注明出处 https://my.oschina.net/feistel/blog/3052024
参考:《Oracle从入门到精通》
数据库
2019-05-21 01:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
https://serverfault.com/questions/628610/increasing-nproc-for-processes-launched-by-systemd-on-centos-7
要在systemd的配置里加才行
数据库
2019-05-21 01:01:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
需求:
已有的mysql数据表,希望增加一个自增的字段,并设置新数据的初始值。 实际上不复杂,只是做个备忘。
测试表 CREATE TABLE `t_abc` ( `name` varchar(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
测试数据: INSERT INTO `t_abc` (`name`) VALUES ('mike'), ('tom'), ('jack');
添加自增字段并设置新数据的起始值 /*增加一个自增主键字段,分两步操作*/ /*首先增加自增字段*/ alter table t_abc add column id int auto_increment primary key; /*执行完上面这一条,字段增加,但值为空*/ /*执行这一条,它会自动为已存在的数据的自增字段赋初值,从1开始,同时将后续新增的数据从100开始*/ alter table t_abc auto_increment=100;
修改已有数据初始值 /*如果希望所有的数据都从10001 开始,我们可以这么做*/ alter table t_abc add column id int auto_increment primary key; /*这里没有指定任何数值,执行完后只为自增字段赋从1开始的初始值,其实隐含的设置当前表自增字段从1开始*/ alter table t_abc auto_increment; /*将所有数据增加10000*/ update t_abc set id=id+10000; /* 前面的100 我们是任意指定的,现在我们应该指定数据库中的maxId+1作为下一个数据的起始值*/ set @maxId=1; select max(id) into @maxId from t_abc; /*表中有3条数据,那么maxId 现在是10003*/ select @maxId+1 from dual; /* 10004 */ alter table t_abc auto_increment=10004; /*这里不能直接引用变量,因此手动挪移过来*/
验证一下 insert into t_abc(name) values('Marry'); select * from t_abc order by id desc;
数据库
2019-05-20 18:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MySQL version: 5.5
MySQL报错: You can't specify target table 'document_basic' for update in FROM clause
原因:MySQL不支持对同表同时更新+查询
解决方案:查询结果使用中间表接收,或者使用表连接
# 错误: UPDATE table SET column = #{newValue} WHERE id IN (SELECT id FROM table WHERE #{condition}) # 使用中间表: UPDATE table SET column = #{newValue} WHERE id IN (SELECT * FROM (SELECT id FROM table WHERE #{condition})temp) # 使用表连接: UPDATE table JOIN table temp ON temp.id = table.id SET column = #{newValue} WHERE #{condition}
数据库
2019-05-20 17:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
完整代码如下:拼凑的代码,源代码不知道从哪来的了。见谅!
使用的话直接在自己的service或dao注入 package com.javafast.util; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.alibaba.druid.pool.DruidPooledConnection; import com.javafast.common.config.Global; import com.javafast.common.utils.PropertiesLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; /* * @Desc druid连接池 * @Author lisha 2019/5/6 0006 13:37 */ @Component public class DBPoolConnection { private static final Logger log = LoggerFactory.getLogger(DBPoolConnection.class); private static DBPoolConnection dbPoolConnection = null; private static DruidDataSource druidDataSource = null; private static String DB_PROPERTIES = "db_server.properties"; static { // 属性文件加载对象 PropertiesLoader loader = new PropertiesLoader(DB_PROPERTIES); try { // DruidDataSrouce工厂模式 druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(loader.getProperties()); } catch (Exception e) { log.error("获取配置失败"); } } /** * 数据库连接池单例 * @return */ public static synchronized DBPoolConnection getInstance(){ if (null == dbPoolConnection){ dbPoolConnection = new DBPoolConnection(); } return dbPoolConnection; } /** * 返回druid数据库连接 * @return * @throws SQLException */ public DruidPooledConnection getConnection() throws SQLException { return druidDataSource.getConnection(); } /** * 返回druid数据库连接 * @return * @throws SQLException */ public synchronized void closeConnection(Connection conn){ try { if (conn != null){ conn.close(); } log.info("关闭数据库连接成功"); } catch (SQLException e) { e.printStackTrace(); log.error("关闭数据库连接失败"); } } /** * @param string 配置文件名 * @return Properties对象 */ private static Properties loadPropertiesFile(String fullFile) { String webRootPath = null; if (null == fullFile || fullFile.equals("")){ throw new IllegalArgumentException("Properties file path can not be null" + fullFile); } webRootPath = DBPoolConnection.class.getClassLoader().getResource("").getPath(); webRootPath = new File(webRootPath).getParent(); InputStream inputStream = null; Properties p =null; try { inputStream = new FileInputStream(new File(webRootPath + File.separator + fullFile)); p = new Properties(); p.load(inputStream); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != inputStream){ inputStream.close(); } } catch (Exception e) { e.printStackTrace(); } } return p; } }
直接使用如下: @Autowired private DBPoolConnection connectionPool; public boolean selectStoreIsExistById(String id){ String sql = " select count(*) allCount from store where id = " + id; Connection conn = null; boolean flag = false; try { conn = connectionPool.getConnection(); ResultSet rs = conn.prepareStatement(sql).executeQuery(); int total = -1; if (rs != null) { rs.next(); total = rs.getInt("allCount"); } if (total > 0) { flag = true; } } catch (SQLException e) { e.printStackTrace(); logger.error("查询供应商信息"); logger.error("sql:" + sql); } finally { connectionPool.closeConnection(conn); } return flag; }
数据库
2019-05-20 16:24:06
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
本文介绍如何从windows上安装数据库。
1、安装步骤
1.1、安装包下载
https://dev.mysql.com/downloads/mysql/
1.2、配置
将文件下载之后解压,在根目录下,创建my.ini文件,内容如下: [mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3306 # 设置mysql的安装目录 basedir=C:/zhaoyi/mysql # 设置mysql数据库的数据的存放目录 datadir=C:/zhaoyi/mysql/data # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB
进入解压包的bin目录。依次执行如下命令 mysqld --install net start mysql # 修改密码 mysqladmin -u root password ********
1.3、操作
例如将密码修改为123456,则可以像linux上一样使用命令行进行操作了。 mysql -u root -p 123456
数据库
2019-05-20 14:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
格式 begin try --SQL end try begin catch --sql (处理出错动作) end catch
我们将可能会出错的sql 写在begin try...end try 之间,若出错,刚程序就跳到紧接着的begin try...end try 的beign catch...end catch中,执行beign catch...end catch错误处理SQL。try..catch 是可以嵌套的。在begin catch ...end catch中我们可以利用系统提供的下面四个函数得到出错信息:
error_number 返回错误代码
error_serverity 返回错误的严重级别
error_state 返回错误状态代码
error_message 返回完整的错误信息
上面的四个函数在同一个begin catch ...end catch可以在多次使用,值是不变的。
下面是一个简单的小例子。 declare @temp_date varchar(20), @date datetime begin try set @temp_date='' set @temp_date='2019-05-13' set @date=CONVERT(datetime,@temp_date) print '正确日期='+convert(varchar(20),@date,120) end try begin catch print '日期转换错误 转换对象'+@temp_date end catch
当 @temp_date='2019-05-13'
当 @temp_date='2019-05-1322'
数据库
2019-05-20 13:48:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MySQL8.0里引入了不少关于权限的改动,从这些改动可以看出来,权限管理更加的规范和遍历了,这和我们之前为rds mysql增加了大量权限管理很类似,想来Oracle也是通过这些改动为其云业务服务的吧。
本文主要简述下部分相关的权限改动,不会涉及代码实现部分。当前版本为8.0.16
Atomic ACL Statement
由于实现了新的数据词典表,所有的权限相关的信息都存储在innodb mysql tablespace里。而innodb是事务性引擎,具有ACID特性,所以对应的ACL操作也具有原子特性。
例如之前如果一个语句对多个user操作的时候,有些成功,有些会失败。而现在则是要么全部成功,要么全部失败。binlog也会在事务提交时记录到redo log里。
这里有个问题是当我们通过搭建备库的方式从5.7升级到8.0时,那些在5.7部分成功的acl操作,到了以8.0作为备库的实例上会全部失败.
关于atomic ddl 见 官方文档
Role
Role是一个期待已久的功能,可以认为是一组权限的集合, 你可以为多个账户赋予相同的role权限,这也使得权限的管理更加规范,大大方便了运维和管理。你可以通过 create role 'role_name' 创建一个role名,然后再通过grant语句为role赋予权限。之后就可以grant 'role_name' to 一个指定的账户了。
关于role,之前写了一篇文章介绍了,这里不再赘述,感兴趣的点 链接
参考:
官方文档
connection control plugin
引入了一个新的插件,代码在plugin/connection_control/下,该插件使用的是audit plugin接口,其功能是在数次登陆失败后,会延迟下次登陆的时间,这也有点类似于多次密码输入错误,会被冻结一会的意思。
在lib/plugin目录下,我们已经编译好了插件connection_control.so,安装也比较简单: mysql> INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so'; Query OK, 0 rows affected (0.01 sec) mysql> INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so'; Query OK, 0 rows affected (0.03 sec) mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'connection%'\G *************************** 1. row *************************** PLUGIN_NAME: CONNECTION_CONTROL PLUGIN_STATUS: ACTIVE *************************** 2. row *************************** PLUGIN_NAME: CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS PLUGIN_STATUS: ACTIVE 2 rows in set (0.00 sec) mysql> SHOW VARIABLES LIKE '%connection%control%'; +-------------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------------+------------+ | connection_control_failed_connections_threshold | 3 | | connection_control_max_connection_delay | 2147483647 | | connection_control_min_connection_delay | 1000 | +-------------------------------------------------+------------+ 3 rows in set (0.00 sec)
如何使用:
connection_control_failed_connections_threshold: 允许失败的次数,在这么多次失败后,会去增加delay的时间(设置为0则表示关闭该特性,不会去增加延迟)
当超出失败上限后,就根据之后失败的测试乘以connection_control_min_connection_delay作为delay时间,但最大不超过connection_control_max_connection_delay, 以默认配置为例子,当第四次失败时是1000毫秒,当第五次失败时就加倍到2000毫秒
官方文档
支持双重密码
这也是个有趣的特性,意思是支持一个账户两个密码,这通常发生在你修改了密码,但又不想导致正在运行的业务中断时。如worklog所述,当你有大规模的复制集群时,又想修改复制密码,当然不希望正在进行的复制中断拉。那怎么办,可以在保持两个密码在一段时间内都是有效的。用法也比较简单,我们举个简单的例子: root@test 10:07:00>CREATE USER arthurdent@localhost IDENTIFIED WITH 'mysql_native_password' BY 'abcd'; Query OK, 0 rows affected (0.00 sec) # 再创建一个密码,同时保持当前密码 root@test 10:07:02>ALTER USER arthurdent@localhost IDENTIFIED BY 'efgh' RETAIN CURRENT PASSWORD; Query OK, 0 rows affected (0.01 sec) #再创建一个密码,同时保持当前密码,但是第一个创建的密码abcd就失效了 root@test 10:07:18>ALTER USER arthurdent@localhost IDENTIFIED BY 'efghh' RETAIN CURRENT PASSWORD; Query OK, 0 rows affected (0.01 sec) 如果要抛弃旧密码,可以执行如下语句 root@test 10:11:36>ALTER USER arthurdent@localhost DISCARD OLD PASSWORD; Query OK, 0 rows affected (0.00 sec) 此时你再通过旧密码efgh就无法成功登录了。
mysql.user表被扩展了来存储两个密码,主密码存储在mysql.user.authentication_string中,次要密码存储在mysql.user.user_attributes中 root@test 10:31:36>select user, authentication_string, user_attributes from mysql.user where user = 'arthurdent'\G *************************** 1. row *************************** user: arthurdent authentication_string: *7538919BBFC125D3F772537519E66F8242CD2E6B user_attributes: {"additional_password": "*1ACFAF7821CBE8E2D6B7C3FA1A539F53CB41BB9D"} 1 row in set (0.00 sec)
除了ALTER USER外,SET PASSWORD也支持类似的语法: SET PASSWORD [FOR user] = 'auth_string' [REPLACE 'current_auth_string'] [RETAIN CURRENT PASSWORD]
参考文档:
WL#11540: Support 2 active passwords per user account
Partial Revoker
在之前如果你有create user权限,相应的也有了drop/create/modify任何账户的权限,包括root账户。 如果用户有delete/update权限的话,甚至还可以修改grant系统表, 因为有的时候我们需要把部分权限revoke掉
worklog举了个例子,这里直接列出来啦: mysql@root> CREATE USER foo; mysql@root> GRANT CREATE USER,UPDATE,DELETE ON *.* TO foo WITH GRANT OPTION; mysql@root> GRANT SELECT ON mysql.* TO foo with grant option; Now, foo has the ability to do the following: mysql@foo>CREATE USER bar; mysql@foo>ALTER USER root@localhost IDENTIFIED BY 'gibberish'; mysql@foo>DROP USER root@localhost; mysql@foo>DELETE FROM mysql.user WHERE user = 'root'; mysql@foo>UPDATE mysql.user SET authentication_string = 'gibberish' WHERE user='root';
如上例,当foo用户有了由root账户赋予的grant权限,他甚至可以去操作root账户。这个worklog的目的,就确保foo用户无法对root账户进行操作。
这个worklog把权限定义为三类: - Global Privileges: DDL/DML privileges that allow object manipulation on all databases. This includes administrative privileges, dynamic privileges. - Database Privileges: Restricted to a one (or more) databases. They provide ability to manipulate objects and data within database. - Restrictions_list: List of tuples - (user, database, privileges). Each entry in the list represents operations prohibited on a given database for given user. Restrictions list implies that even if user is granted GLOBAL privileges, if revocation list prevents the operation, user can not perform it for given database.
其中restrictions_list存储在mysql.user表中,主要是引入Partial revoke, 可以revoke部分库上的权限,例如mysql库,这实际上对于云业务而言是非常重要的功能:用户通常希望拥有超级权限,但云平台本身也有保留的账号做维护用,这些我们是不希望被修改的,举个简单的例子: root@(none) 09:26:43>CREATE USER foo; Query OK, 0 rows affected (0.00 sec) root@(none) 09:26:49>GRANT ALL ON *.* TO foo; Query OK, 0 rows affected (0.00 sec) root@(none) 09:27:00>SET GLOBAL partial_revokes = 0; Query OK, 0 rows affected (0.00 sec) root@(none) 09:27:05>REVOKE INSERT ON mysql.* FROM foo; ERROR 1141 (42000): There is no such grant defined for user 'foo' on host '%' root@(none) 09:27:12>SET GLOBAL partial_revokes = 1; Query OK, 0 rows affected (0.00 sec) root@(none) 09:27:14>REVOKE INSERT ON mysql.* FROM foo; Query OK, 0 rows affected (0.00 sec) root@(none) 09:27:24>REVOKE DELETE ON mysql.* FROM foo; Query OK, 0 rows affected (0.00 sec)
这里引入了一个全局参数partial_revokes, 只有打开了,你才能对账户做partial revoke操作,这里会产生一个对该账户的限制列表,存储在mysql库中: root@(none) 09:29:08>select user, authentication_string, user_attributes from mysql.user where user = 'foo'\G *************************** 1. row *************************** user: foo authentication_string: user_attributes: {"Restrictions": [{"Database": "mysql", "Privileges": ["INSERT", "DELETE"]}]} 1 row in set (0.00 sec)
可以看到针对该账户产生了一个限制列表Restrictions, 以json的形式存储。Partial Revoke的限制(摘自文档): Partial revokes must name the schema literally. Schema names that contain the % or _ SQL wildcard characters (for example, myschema%) are not permitted. It is possible to use partial revokes to place restrictions on nonexistent schemas , but only if the revoked privilege is granted globally. If a privilege is not granted globally, revoking it for a nonexistent schema produces an error. Partial revokes apply at the schema level only. You cannot use partial revokes for privileges that apply only globally (such as FILE or BINLOG_ADMIN), or for table, column, or routine privileges.
当一个有restrictions list的账户再去创建别的账户时,他受限的列表也会传递出去
在wl#12098中还引入了system user这样的权限类型,只有相同权限的账户才能修改这种类型的账户,普通账户无权对其进行修改。在之后又在wl#12364中,避免拥有CONNECTION_ADMIN权限的普通用户能够去kill超级用户的session或者query: root@(none) 08:20:40>GRANT SYSTEM_USER ON *.* TO foo; Query OK, 0 rows affected (0.00 sec) root@(none) 08:20:54>GRANT SYSTEM_USER ON *.* TO bar; Query OK, 0 rows affected (0.01 sec) baz@(none) 08:27:38>GRANT CONNECTION_ADMIN ON *.* to baz; Query OK, 0 rows affected (0.00 sec) #login foo foo@(none) 08:27:10>show grants; +---------------------------------------+ | Grants for foo@% | +---------------------------------------+ | GRANT USAGE ON *.* TO `foo`@`%` | | GRANT SYSTEM_USER ON *.* TO `foo`@`%` | +---------------------------------------+ 2 rows in set (0.00 sec) foo@(none) 08:28:04>show processlist; +-----+------+-----------+------+---------+------+----------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+------+---------+------+----------+------------------+ | 348 | foo | localhost | NULL | Query | 0 | starting | show processlist | +-----+------+-----------+------+---------+------+----------+------------------+ 1 row in set (0.00 sec) #login baz baz@(none) 08:29:03>show grants; +--------------------------------------------+ | Grants for baz@% | +--------------------------------------------+ | GRANT USAGE ON *.* TO `baz`@`%` | | GRANT CONNECTION_ADMIN ON *.* TO `baz`@`%` | +--------------------------------------------+ 2 rows in set (0.00 sec) baz@(none) 08:29:05>show processlist; +-----+------+-----------+------+---------+------+----------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+------+---------+------+----------+------------------+ | 349 | baz | localhost | NULL | Query | 0 | starting | show processlist | +-----+------+-----------+------+---------+------+----------+------------------+ 1 row in set (0.00 sec) #baz账户只能看到自己的线程,如果强制去kill foo呢 ? baz@(none) 08:30:30>kill 348; ERROR 1095 (HY000): You are not owner of thread 348
可以看到有connection_admin权限的账户被限制了,不仅无法看到system_user的链接,也无法去kill session.
简单来说,有system_user权限的账户可以修改system user和regular user的账户;而regular user则无法修改system user的账户
关于这块官方文档有非常详细的内容,笔者对这块也不太熟悉,就不多说了,感兴趣的直接翻阅如下文档吧:
WL#12098: MySQL system users
WL#12364: Kill administration for system users
WL#12820: Extend GRANT syntax to cover partial revokes information
Privilege Restriction Using Partial Revokes
Account Categories
Password Expiration
可以设置密码过期时间,提供了三种操作: 通过参数 default_password_lifetime 来控制 , 单位为天 root@(none) 09:21:31>SET PERSIST default_password_lifetime = 180; Query OK, 0 rows affected (0.00 sec)
该选项的值会被alter user覆盖 通过ALTER USER来控制
指定过期时间 CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY; 过期时间存储在mysql.user表中 root@(none) 09:35:46>select user,password_lifetime from mysql.user where user = 'jeffrey'\G *************************** 1. row *************************** user: jeffrey password_lifetime: 90 1 row in set (0.00 sec)
禁止密码过期 CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER;
默认过期时间为default_password_lifetime: CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT; 直接手动过期 ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE;
参考:
官方文档
WL#6587 : Protocol support for password expiration
密码复用
现在很多系统在忘记密码重设时,都会要求最近几次使用付的密码不允许再次使用,这也是为了安全考虑,MySQL也增加了这样的功能,和密码过期类似,也可以通过全局变量,ALTER USER来控制:
例如如下配置: password_history=6 password_reuse_interval=365
表示不要服用最近6次用到的密码或者365天内用过的密码。
也可以通过create/alter user来设置: CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY 5; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY 5; CREATE USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY;
同样的也可以把上例中的history 5 和 interval 365 day指定为default
参考:
官方文档
WL#6595: Password rotation policy
修改账户要求验证
同样是安全相关的,当修改一个账户时,需要去验证密码,可以使用参数 password_require_current 来控制。默认关闭,当打开该选项时,如果要修改账户密码,必须要提供当前的密码才允许修改,如下摘录的官方示例:
要求在修改时输入当前密码: CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT;
可选的输入当前密码(感觉有点多余...) CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT OPTIONAL; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT OPTIONAL;
根据参数配置来决定: CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;
那么修改密码时就需要显示当前密码: CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;
SET PASSWORD也一样. SET PASSWORD [FOR user] = password_option password_option : { 'auth_string' [REPLACE 'auth_string'] }
参考:
官方文档
WL#11544 Current password required for SET PASSWORD
限制SET PERSIST
MySQL提供了在线持久化参数修改的功能,通过接口SET PERSIST 和SET PERSIST ONLY来实现,但有些涉及敏感信息的变量则不应该被persist, 因此不应该通过远程终端来管理,而是要管理员登录机器,手动的修改my.cnf
新增参数persist_only_admin_x509_subject , 当打开这个参数时,只有通过SSL认证的用户才能Persist一些受限的系统参数。 官方文档 列举了些可持久化的参数和不可持久化的参数
参考:
参数:persist_only_admin_x509_subject
Nonpersistible and Persist-Restricted System Variables
skip-grant-tables
用过的人的都知道,当以skip-grant-tables启动时候,系统将不检查任何权限,这是是很危险的,但有时候如果application和数据库实例部署在同一台机器时,我们又可以通过该选项来获得更好的性能,但带来的风险是其他人只要知道host和端口号,也可以远程连接过来,这就有数据安全问题
因此MySQL加入了新选项 skip_networking ,不再监听tcp/ip连接请求。
另外最近也修复了一个有趣的 bug#94394 ,当mysql.user表损坏时,实例启动时仅仅打印了一条错误信息,并以skip-grant-tables的方式启动了。这实际上市不安全的,人们可能在install初始化阶段不小心忽略这个错误,而后数据库的正常运行,也会造成实例正确安装的错觉。
因此在8.0.16版本中,官方修复了这个问题,除非用户指定skip-grant-tables,实例将打印信息之后直接启动失败。
fk error不显示父表信息
这个修复很简单,就是说对父表没权限的用户,如果在子表上因为foreign key约束,导致错误的话,不应该将父表的信息暴露出来,这可能导致安全问题,而是返回统一的错误: ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
参考:
WL#8910: Ensure foreign key error does not reveal information about parent table for which user has no access privileges.
SESSION_VARIABLES_ADMIN
通常任何账户都允许设置session级别的变量,但某些session级别的变量只能特定权限的用户设置,例如binlog_format, sql_log_bin,火鹤sql_log_off等,需要需要SYSTEM_VARIABLES_ADMIN或者SUPER权限来设置。
从MySQL8.0.14开始了增加了一个新的权限位session_variables_admin, wl#12217列出了一些需要该权限位的变量:
The following vairables need to enforce SESSION_VARIABLES_ADMIN: auto_increment_increment auto_increment_offset binlog_direct_non_transactional_updates bulk_insert_buffer_size character_set_database character-set-filesystem collation_database pseudo_slave_mode pseudo_thread_id transaction_write_set_extraction rbr_exec_mode
The following variables will not be protected: default_storage_engine default_tmp_storage_engine max_allowed_packet rand_seed1 rand_seed2
These variables should transition from checking SYSTEM_VARIABLES_ADMIN to
SESSION_VARIABLES_ADMIN: histogram_generation_max_mem_size sql_log_off debug_sync original_commit_timestamp The not documented gtid_next The disabled and not documented gtid_next_list default_collation_for_utf8mb4 explicit_defaults_for_timestamp sql_log_bin explicit_defaults_for_timestamp The variable is mis-documented as not requiring SYSTEM_VARIABLES_ADMIN for SET SESSION. But in reality it does require it. Since the variable is deprecated we'll keep the current behavior. binlog_format binlog_row_image binlog_row_value_options binlog_rows_query_log_events
官方文档:SESSION_VARIABLES_ADMIN
WL#12217: SESSION_VARIABLE_ADMIN
作者:zhaiwx_yinfeng
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-05-20 11:20:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
nginx常用命令
nginx -c /usr/local/nginx/conf/nginx.conf 启动nginx(windows下start nginx);
nginx -s quit 停止ngix
nginx -s reload 重新载入nginx(当配置信息发生修改时)
nginx -s reopen 打开日志文件
nginx -v 查看版本
nginx -t 查看nginx的配置文件的目录
nginx -h 查看帮助信息
linux下搭建nginx环境
pwd 查看当前目录
cd /home/download 找到nginx安装包
tar -zxvf nginx-1.10.3.tar.gz 解压nginx安装包
cd nginx-1.10.3 进入nginx的目录
./configure 运行nginx配置文件(如果出现错误,可能缺少库文件,安装后再执行这一步)
su 进入root权限,回车后输入密码
cd / 进入到根目录
yum -y install gcc gcc-c++ autoconf automake 安装gcc和gcc-c++(-y安装时选择同意选项,autoconf automake 自动配置自动安装,出现complete安装成功)
yum -y install pcre pcre-devel 安装pcre库
yum -y install zlib zlib-devel 安装zlip库
./configure 进入到nginx目录再运行一次,直到成功后
make 编译
make install 安装nginx
cd /usr/local->ls 查看是否有nginx,如果有则安装完成
cd nginx conf目录放着配置文件 htmL放着网页程序 logs放着日志文件 sbin放着nginx的启动程序
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf 启动nginx
浏览器打开localhost查看
windows下安装
下载解压安装包后直接双击运行nginx.exe配置文件(或者start nginx命令)
浏览器打开localhost查看
linux将nginx配置到全局
cd ~ 进入用户根目录
ls -a 查看所有文件(包含隐藏)
vim .bashrc 进入环境变量配置文件
export NGINX=/usr/local/nginx/sbin/nginx
PATH=PATH: PATH:PATH:NGINX 修改环境变量
:qw 保存退出 (:q! 不保存退出ctrl d向下翻页ctrl u向上翻页)
source .bashrc 修改后的配置文件生效
nginx配置文件修改
nginx -t 查看nginx配置文件目录
cp nginx.conf nginx_bf.conf 将配置文件备份一下
vim /user/local/nginx/conf/nginx.conf 打开nginx配置文件 vim命令 :q! 不保存退出 :qw 保存退出 ctrl d向下翻页 ctrl u向上翻页 nginx -s reload 当配置信息发生修改时,重新载入nginx,才能生效
nginx配置文件说明
worker_processes 1; //开启进程数小于CPU数
error_log logs/error.log; //自定义错误日志保存位置,全局设置,默认logs/error.log
events {
worker_connections 1024; //每个进程最大连接数(最大连接=连接数x进程数)每个worker允许同时产生多少个链接,默认1024
} http { include mime.types; //文件扩展名与文件类型映射表 default_type application/octet-stream; //默认文件类型 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' //自定义日志文件输出格式 全局设置 '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; 自定义全局请求日志保存位置,全局设置,默认logs/access.log, 定义格式:文件存储位置 + 日志输出格式 sendfile on; //打开发送文件 keepalive_timeout 0; //连接超时时间 keepalive_timeout 65; gzip on; //打开gzip压缩 配置虚拟主机,基于域名、ip和端口,可以配置多个server server { listen 80; //监听端口,可以是ip:port 或者 port server_name 10.128.166.57; //监听域名,可以是ip或者域名,server_name有三种匹配方式:精准匹配(www.domain.com)、通配符匹配(*.domain.com 、www.*)、正则表达式匹配(~^(?.+)\.domain\.com$) access_log logs/host.access.log main; //自定义请求日志,局部,当前server有效 error_page 500 502 503 504 /50x.html; //错误页面及其返回地址 charset UTF-8; //设置字符集 location / { //当访问10.128.166.57:80时 proxy_pass http://10.128.166.57:80:8083; //实际上访问的时http://10.128.166.57:80:8083地址 } location ^~/data { //当访问10.128.166.57:80/data时 proxy_pass http://10.128.166.57:80:8084; //实际上访问的时http://10.128.166.57:80:8084地址 } } }
nginx日志分割备份
mkdir /usr/local/nginx/back_up_logs //创建存放备份文件目录
vim /usr/local/nginx/sbin/log.sh //创建脚本log.sh
chmod 755 log.sh //脚本授权
crontab -e //执行该命令设置定时任务
*/1 * * * * sh /usr/local/nginx/sbin/log.sh //每分钟执行一次,保存退出即可自动开始执行定时任务
crontab -l //查看所有定时任务
crontab -r //删除所有定时任务
log.sh文件的内容:
#!/bin/sh
#设置基路径
BASE_DIR=/usr/local/nginx
#要切割备份的日志文件名
BASE_FILE_NAME=access.log
#日志路径
LOG_PATH=KaTeX parse error: Expected 'EOF', got '#' at position 20: …_DIR/logs #̲日志切割后备份路径 …BASE_DIR/back_up_logs
#切割日志文件
LOG_FILE=LOGPATH/ LOG_PATH/LOG
P

ATH/BASE_FILE_NAME
#获取时间
BAK_TIME=/bin/date -d yesterday +%Y%m%d%H%M //以分钟为单位
#备份文件
BAK_FILE=BAKPATH/ BAK_PATH/BAK
P

ATH/BAK_TIME-$BASE_FILE_NAME
echo $BAK_FILE
#关闭nginx
$BASE_DIR/sbin/nginx -s stop
#移动切割文件
mv $LOG_FILE $BAK_FILE
#启动nginx
$BASE_DIR/sbin/ngin
解决端跨域问题(保证ip和端口相同)修改配置文件\nginx-1.10.3\conf\nginx.conf文件
server {
listen 8081;//前端调试打开localhost:8081页面;js文件中后台接口访问localhost:8081/data;这样就保证不跨域了
server_name localhost;
access_log logs/host.access.log main;
location / { //访问localhost:8081实际上访问是前端端口http://localhost:8080/
proxy_pass http://localhost:8080/;
}
location ^~ /data {//访问localhost:8081/data实际上访问是后端接口http://10.128.166.42:8533/
proxy_pass http://10.128.166.42:8533/;
}
数据库
2019-05-20 09:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1. Navicat官网->产品 下载 Navicat for Mariadb试用 得到 navicat121_mariadb_cs_x64.tar.gz
2.解压并复制到主文件夹
$ tar -xzvf navicat121_mariadb_cs_x64.tar.gz
$ cp -r navicat121_mariadb_cs_x64 ~
3.打开之前先解决Navicat界面乱码问题
# vi ~/navicat121_mariadb_cs_x64/start_navicat
将 export LANG="en_US.UTF-8" 改为 export LANG="zh_CN.UTF-8" ,保存,关闭即可。
4.无需安装即可启动
$ cd ~/navicat121_mariadb_cs_x64/
$ ./start_navicat
启动过程会自动安装“Wine Mono Installer”和“Wine Gecko Installer” 出现下图,表示成功了。
5.无限试用期
.navicat64是一个位于主文件夹下的隐藏文件,用来记录navicat的试用期限(删除它即可无限期试用)
切换至主文件夹 $ cd ~
删除.navicat64文件 $ rm -r .navicat64
6.若忘记处理乱码就启动navicat,同样需要先删除文件.navicat64,再将文件start_navicat`
中 export LANG="en_US.UTF-8" 改为 export LANG="zh_CN.UTF-8" ,最后重新启动。这个.navicat64文件类似于用户使用记录文件。
7.主界面【工具】-【选项...】
【常规】里面设置默认Droid Sans Fallback字体即可
数据库
2019-05-20 00:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.MariaDB_数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 _MariaDB_的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB。
安装
2.安装Mariadb # yum -y install mariadb mariadb-server
3.设置开机启动 # systemctl enable mariadb
4.启动MariaDB # systemctl start mariadb
5.进行MariaDB的相关简单配置 # mysql_secure_installation
(1)首先是设置密码,会提示先输入密码
Enter current password for root (enter for none):<–初次运行直接回车
设置密码 Set root password? [Y/n] 是否设置root用户密码,输入y并回车或直接回车
New password: 设置root用户的密码
Re-enter new password: 再输入一次你设置的密码
(2)其他配置:
Remove anonymous users? [Y/n] 是否删除匿名用户,回车
Disallow root login remotely? [Y/n] 是否禁止root远程登录,回车,
Remove test database and access to it? [Y/n] 是否删除test数据库,回车
Reload privilege tables now? [Y/n] 是否重新加载权限表,回车
最后如下图,表示完成配置:
6.初始化Mariadb后,本机测试root登陆 # mysql -u root -p
配置Mariadb的字符集
1.打开文件 vi /etc/my.cnf
2.在 [mysqld] 标签下添加
init_connect='SET collation_connection = utf8_unicode_ci ` init_connect='SET NAMES utf8 character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake
3.打开文件 #vi /etc/my.cnf.d/client.cnf
4.在 [client] 中添加 default-character-set=utf8
5.文件 #vi /etc/my.cnf.d/mysql-clients.cnf
6.在 [mysql] 中添加 default-character-set=utf8
7.重启Mariadb # systemctl restart mariadb
8.重新进入MariaDB查看字符集 # mysql -u root -p
MariaDB [(none)]> show variables like "%character%";show variables like "%collation%"
数据库
2019-05-19 23:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 自己的练习项目中涉及保存微信的nickname,之前一直正常使用,但是突然遇到一个之前没有遇到的问题。经过调试发现错误如下: Incorrect string value: '🙈ðŸ...' for column 'nickname' at row 1 经过仔细查看发现可以获得nickname的数据,但是无法保存到mysql数据库,查看用户的微信发现在nickname中使用了emoji字符。 到百度(只能用这个,其他的麻烦呀。)上查找发现主要解决方案就是MySQL的编码设置由utf8转为utf8mb4。 具体解释可见:[详细emoji表情与utf8mb4的关系][1] ,写的非常全面详细。
网上的解决办法大多是修改my.cnf参数,设置mysql的编码为utf8mb4,这种方法虽然彻底,但是通常要重启mysql,会造成生产系统临时当机。我认为写的比较好的方法是: mysql/Java服务端对emoji的支持 ,一般可参考以上方法。文章中的关键点也说的比较清楚。
下面是我的处理方法:
要求: 1.MySQL的版本不能太低,低于5.5.3的版本不支持utf8mb4编码。select version(); 2.JDBC驱动版本不能太低,mysql connector版本高于5.1.13。 mysql mysql-connector-java 5.1.38 3.将表中的对应字段,比如会员表的呢称字段,其字符集修改成utf8mb4。 4.最后修改druid数据源的配置,增加一行: 5.检查下jdbc连接串的设置: jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=utf8 这里要注意:有人建议删除useUnicode=true&characterEncoding=utf8,但好像我这里会发生保存数据时发生乱码的现象。
本文重要参考: mysql : utf8mb4 的问题

JAVA解决Emoji表情存储至Mysql报错问题
保存微信昵称时,Mysql报错。
Caused by: java.sql.SQLException: Incorrect string value: 'ðŸ˜...' for column 'nick_name' at row 1 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1074) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4096) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4028) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2490) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2651) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2734) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
报错原因: UTF-8编码有可能是两个、三个、四个字节。Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去。

网上解决办法:
1、修改my.ini [mysqld] character-set-server=utf8mb4在后台配置mysql连接参数中,不要加characterEncoding参数。 不加这个参数时,默认值就时autodetect。将已经建好的表也转换成utf8mb4。命令:(将TABLE_NAME替换成你的表名) ALTER TABLE `TABLE_NAME` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; 将需要使用emoji的字段设置类型为utf8mb4_general_ci: ALTER TABLE `TABLE_NAME`MODIFY COLUMN `COLUMN_NAME` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

照做之后,发现并没有解决问题。找不到原因。等以后空闲时间慢慢调错。先将这次BUG修复。选用另一种方法。
1、存储nickname的时候,先将nickname用base64编码。我照做以后,发现还是有问题。最后,使用了一种稍麻烦的办法。 String encodeNickname = new String(Base64.getEncoder().encode("Nickname".getBytes()));
2、取出的时候,先将nickname用base64解码。 String decodeNickname = new String(Base64.getDecoder().decode(encodeNickname.getBytes()));
3、数据库中的nickname手动用base64编码更新。

结束上述步骤后,查看后台发现傻了眼。新增的用户nickname可以正常显示emoji表情了。但是之前手动编码的nickname全部乱码了。
没有找到原因。以为是没有加上编码格式的原因。更改代码: String encodeNickname = new String (Base64.getEncoder().encode("Nickname".getBytes()),"utf-8"); System.out.println("编码后:"+encodeNickname); String decodeNickname = new String (Base64.getDecoder().decode(encodeNickname.getBytes()),"utf-8"); System.out.println("解码后:"+decodeNickname);
还是乱码!百思不得其解。福至心灵,拿gbk将之前编码的数据解码试了下。发现显示正常了。 new String (Base64.getDecoder().decode(encodeNickname.getBytes()),"gbk");
重新把之前nickname用gbk解码获取用户昵称,然后用utf-8编码存储,utf-8解码。一切正常了。

记一次生产事故踩坑。血淋淋的惨痛教训
众所周知 mysql 存 emoji 表情要用 utf8mb4 这个字符集
OK 没问题,设置 nick_name 为 utf8mb4 varchar(50)
测试的结果:
emoji 表情储存成功
没有问题 完全oj8k 发生产!
微信公众号做了推送,为了抗住流量,还准备了100台服务器。
晚上监控流量,服务器各项指标正常。
但是一看日志,发现日志疯狂报错:部分敏感信息及参数已删除 exception[order=UserInfoRequestType{activityId=, uid=, nickName=wing.?, headImgUrl=}] org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: Incorrect string value: '🐝' for column 'nick_name' at row 1 ### The error occurred while setting parameters ### SQL: insert into s_user_info (id,nick_name,uid,support_detail,popularity,img_url,DataChange_CreateTime,DataChange_LastTime,activity_id) values (?, ?, ?, ?, ?, ?, ?,?,?) ### Cause: java.sql.SQLException: Incorrect string value: '🐝' for column 'nick_name' at row 1 ; uncategorized SQLException for SQL []; SQL state [HY000]; error code [1366]; Incorrect string value: '🐝' for column 'nick_name' at row 1; nested exception is java.sql.SQLException: Incorrect string value: '🐝' for column 'nick_name' at row 1 at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
看到一堆的报错,马上就慌了。
nick_name 存不进去,仔细调研发现是 部分emoji表情的用户 的 昵称储存失败。OK,马上fixbug,字符串存不了,我转base64总可以了吧,改完发测试环境,测试测了这个接口没有问题,又发生产,结果引起了另外一个接口的报错,瞬间又是一堆错误日志,整个人瞬间斯巴达了 emmm...... 于是马上回退到上一个版本,让部分特殊emoji表情的用户无法活动。再继续fixbug。
现在库里既有base64的昵称 也有未 base64 的昵称 。真是让人头大。
经过周末两天的加班,终于把这个问题稳定的解决了:
数据库存 base64 encode 的 昵称, 从DB取出来时 decode一下。
总结: 1、我设置了utf8mb4 还是无法储存部分 moji 表情的原因是:新的moji 表情越来越多,mysql 版本却没有跟上,导致新的moji 表情存不进去。 2、mysql 存 moji 表情 不要完全依赖 utf8mb4这个字符集来帮你处理 , 可以就选用utf8 长度给长一点 转 base64 后存,取的时候再转成字符串就行,当然转base64 的 encode 和 decode 操作都会消耗你 CPU 的 性能,在高并发场景下要多测试,然后进行方案的取舍。 3、在GitHub上找到 emoji-java 这个来解决,下次我应该就会用这个来处理emoji表情了。 4、就算是高并发场景,遇到生产环境大量报错不要慌,不要急,要稳住心态,只要一些硬性指标(比如订单量)没有下降就还能撑住。 5、学会批判的看事情,百度得到的答案不一定对,相信很多人遇到要存moji 表情 都是 直接谷歌或是百度 mysql 如何存 emoji表情,然后看到一堆文章,找了篇看起来很多的,就拿着参考开始实践了。我们还需要站到对立面多思考一个问题:Emoji表情存储至Mysql报错问题,于是你就能找到这篇: JAVA解决Emoji表情存储至Mysql报错问题 就不会踩坑了
这是我的一点踩坑经历,希望能给看到文章的你一点帮助。

ps:
https://segmentfault.com/a/1190000004594385?utm_medium=referral&utm_source=tuicool
https://www.cnblogs.com/yugure/articles/7773013.html
https://cloud.tencent.com/developer/article/1393147
数据库
2019-05-16 16:46:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
200名数据库领域从业三年以上的会员投票和专业的评委评选,在如此严苛的条件之下,蚂蚁金服金融级分布式关系数据库OceanBase 2.0依然获得了专家评审团的一致青睐,荣获2019中国数据库技术大会的“年度最佳创新产品”奖。
蚂蚁金服资深总监韩鸿源代表OceanBase领奖
权威机构及专家高度认可OceanBase
今年是中国数据库技术大会举办的第十个年头,在行业里具有极高的影响力、公信力和权威性。
第十届中国数据库技术大会官网是这样评价OceanBase的。
2019中国数据库技术大会官网对OceanBase的评价
2019中国数据库技术大会官网对OceanBase的评价(因评选启动于2018年,所以评语说OceanBase立项于八年前)。
“OceanBas从2010年立项至今,给自己定的目标就是:把性价比做到传统数据库的十倍,并且做到别人做不到的事。”
“OceanBase在这八年时间里一直踏踏实实在做两件事:
1、 保证高可用的同时解决数据一致性问题 。OceanBase通过把可用性做到数据库系统内部来解决这个问题。高可用与主备库数据一致存在本质的矛盾,这是一个无法改变的客观规律。高可用和数据的一致性,OceanBase让两者都得到了保证。
2、 做有极致性能并且性价比最好的数据库 。OceanBase从0.x版本到1.x版本,再到现在的2.0版本,一直在推动的一件事就是把硬件的性能做到极致,希望在同样的硬件条件下能给业务带来更多性能的空间。OceanBase通过原生的读写分离,使得整个系统性能,特别是写的性能得到了很大的提升,同时将成本大幅度降低。”
那么,参与此次评选的专家是怎么看OceanBase的呢?
“ 有双十一背书的OceanBase不需要任何其他的评语 。”南瑞集团基石数据技术有限责任公司技术总监白鳝。
“OceanBase作为蚂蚁金服的核心数据库产品,支撑了海量高并发的金融交易, 具有原创、先进的数据产品能力 ,为国产金融核心系统开扩了自主之路, 是经过实践检验的卓越产品 。” 云和恩墨创始人盖国强
“OceanBase是一款非常优秀的OLTP数据库,其由蚂蚁金服、阿里巴巴完全自主研发。在经过多年的发展之后, OceanBase已经完全取代Oracle用以支撑支付宝的全部核心业务,可想而知其产品的稳定性、可靠性和性能表现 。更难能可贵的是,在不断完善功能和提升性能的同时,产品也非常关注可运维性等企业级特性,这往往是其他数据库容易忽略的。例如OceanBase支持DBReplay,可以让用户重放负载,使用产品更加放心。”平安科技数据库产品及存储产品部总经理汪洋。
“OceanBase是中国企业目前分布式数据库中 架构先进性、成熟度最高 的产品。” 招商银行数据库负责人田永江。
“OceanBase 性能、扩展性等能力较好,在业内有多个成功的应用案例 ;蚂蚁金服研发团队能力强,有很好的创新能力。”中国农业银行研发中心总经理蔡钊。
OceanBase 2.0之所以获得青睐的“硬核实力”
OceanBase究竟靠着怎样的“硬核实力”打动这些资深行业人士?
OceanBase是蚂蚁金服完全自主研发的金融级分布式关系数据库,具有数据强一致、高可用、高性能、在线扩展、高度兼容SQL标准和主流关系数据库、低成本等特点。OceanBase 2.0成功支撑了2018年双十一支付宝的核心链路,其性能比去年提升了50%,因此即便是在业务量暴增的情况下,也实现了“零成本”支撑大促。
具体来看,OceanBase 2.0的“硬核”实力主要体现在以下四个方面。
相较于之前,OceanBase 2.0在分布式架构方面更上一层楼。OceanBase 2.0进一步实现了用户对数据分布的无感知,同时实现了全局一致性快照,支持了全局索引功能,丰富了负载均衡策略。
除了日益强大的分布式架构,OceanBase 2.0在兼容性上有了质的飞跃。OceanBase 2.0版本能够同时支持两种兼容模式:MySQL和Oracle。OceanBase还为客户提供了一套完善的数据迁移服务使得传统企业的应用可以平滑迁移到OceanBase,在不改变业务代码的前提下充分享受分布式数据库在扩展性、可用性和系统成本等各个方面带来的收益。
OceanBase 2.0还在提升数据可用性方面显著改进。索引实时生效、闪回查询、在线分区分裂,这三个新特性,每一个都解决了业务使用中的痛点。
最后在性能方面,OceanBase 2.0主要做了两方面的工作:提交协议的优化和工程实现层面的优化。通过这一系列的改进,在OLTP场景的实际应用中,2.0版本相对于1.4版本,性能提升了50%以上,存储下降30%。
“身经百战”的OceanBase
作为最底层的系统,数据库和操作系统一样在整个技术体系里面占据着非常核心的位置。因此,其开发难度也可想而知。然而,蚂蚁金服的技术团队仅仅用了数年时间就研发出了这款通用型数据库OceanBase。
但最为重要的是,OceanBase并不是只存在于实验室的产品,而是经过了极为复杂场景验证的数据库!每年的双11,OceanBase都经受住了高并发流量的考验,并且保证了整个系统的持续可用。OceanBase至今已成功应用于支付宝全部核心业务:交易、支付、会员、账务等系统以及阿里巴巴淘宝(天猫)收藏夹、P4P广告报表等业务。
OceanBase已经在多家金融机构落地应用
从2017年开始,OceanBase开始走出支付宝、走出蚂蚁金服,在商业银行推广使用,最早的两家客户是浙商银行和南京银行。仅仅用了两年多的时间,OceanBase已经在人保健康险、常熟农商行、苏州银行、广东农信等数十家商业银行和保险机构上线。
2017年10月,南京银行“鑫云+”互金开放平台正式发布,该平台使得南京银行互金核心系统在贷款交易处理能力、成本控制和对接效率都得到了极大的提升。南京银行与互联网平台合作开展线上业务仅一年时间,业务量就已经达到了过去十年传统线下消费金融业务的总和。南京银行“鑫云+”平台上线后,业务快速增长,贷款交易处理量增长了数倍。
关于 OceanBase
OceanBase是由蚂蚁金服、阿里巴巴完全自主研发的金融级分布式关系数据库,始创于2010年。OceanBase具有数据强一致、高可用、高性能、在线扩展、高度兼容SQL标准和主流关系数据库、低成本等特点。OceanBase 对传统的关系数据库进行了开创性的革新。在普通硬件上实现金融级高可用,在金融行业首创“三地五中心”城市级故障自动无损容灾新标准,同时具备在线水平扩展能力,创造了4200万次/秒处理峰值的纪录。OceanBase至今已成功应用于支付宝全部核心业务:交易、支付、会员、账务等系统以及阿里巴巴淘宝(天猫)收藏夹、P4P广告报表等业务。从2017年开始,OceanBase开始服务外部客户,客户包括南京银行、浙商银行、印度Paytm、人保健康险等数十家商业银行和金融机构。OceanBase目前在杭州,北京和上海均设有办公室和研发中心。
作者:华蒙
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-05-16 13:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 作者:Dan 本文转载自公众号「白噪声OG」。
经历了上礼拜漫长的上线周期,终于有时间总结一下期间发生的故事。TiDB 是一款非常优秀的国产分布式 NewSQL 数据库,因其支持水平扩展性、强一致性、高可用性,从 18 年 3 月起已在国内银行的账务、支付类核心系统得到应用。
临近年中,银行重要系统的建设进入投产冲刺阶段,本次上线又有多个系统对接 TiDB,为了优化集群资源分配,引发了这次分享的主题—— 线上系统 TiKV 的缩容、region 的迁移 ,本文主要针对本次 TiKV 的缩容、迁移过程进行梳理总结。
TiDB 数据库的扩容已在官方文档进行了详细的说明 ( https://pingcap.com/docs-cn/op-guide/horizontal-scale/ ) 并被各路大咖广泛提及,但缩容迁移并在银行交易系统上的实践却少有分享,这也是本文的目的之一。
进入主题,先交代下环境,服务器集群采用 NVMe+SSD 的存储方案构建了 16 个 TiKV 实例,作为重要的核心支付类系统,两地三中心五副本不可少,每个 TiKV 上 8K+ 个 region。 整个迁移过程历时 5 个小时,过程中没有停止系统对外服务,很是顺滑平稳。
接下来还是看一下迁移的过程:
(一) TiKV 采用 Raft 一致性算法保证副本强一致性,迁移过程本质上是扩容的逆过程,确定下线的 TiKV 打上 label 后,将 region 搬移到最终保留下来的 TiKV 上。
(二) 接下来聚焦 region 1 的 Raft Group,对其副本进行搬移,实际上所有 region 的数据是一样的,只是在保留的 TiKV 内进行 region 数据的复制,新产生的副本由于数据不完整,作为 Raft Group 中的 learner。
(三) Learner 创建后,PD 会在这样的一个 Raft Group(5 个全副本 region + 2 个 learner)中发起选举: 选举会增加 label 限制,确保 leader 最终在保留的 TiKV 中产生; 由于 learner 没有投票权,选举实际还是个 5 副本选主,多数派 (N+1)/2 仍为 3。
(四) 这样新的 leader 选出来了,当两个新副本数据追平后,将删除下线 TiKV 中的 region。
(五) 这样一个新的 5 副本 Raft Group 我们就获得了。
这里再说几点:
1. 磁盘 IO 对迁移的效率影响还是很大的,测试环境使用普通的 SAS 盘,在更高并发的条件下,耗时长了很多。
2.(二)、(三)、(四)的过程并非原子化操作,当然 learner 的数据本身也不具备一致性,但对 raft 的改造最终要保证一致性,与 PingCAP 的开发同学确认后,这些会在之后加入。
3. 我认为最有意思,也最有意义的一点,learner 的引入是本次迁移过程中非常巧妙的设计,解决了数据不一致副本在选举过程中的尴尬地位,而 learner 也是 Multi-Raft 协议中的重要角色,HTAP 引擎 TiFlash&TiSpark 也以此引入列存副本,非常期待 TiDB 3.0。
PS:本次上线的重头戏 Cloud TiDB 在平稳运行后,希望有机会进行总结分享。TiDB 自上线后实现了多次重要变更操作,均未暂停系统对外服务,从一只开发狗的角度看 TiDB 在金融级 NewSQL 数据库的方向上的确投入了很多。
最后,感谢 PingCAP Gin 同学和研发大神们的支持,感谢运维爸爸们直到凌晨 4 点的奋斗。
数据库
2019-05-16 10:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>> PROCEDURE Create_Pick_Task(info_ OUT VARCHAR2, move_no_ IN OUT VARCHAR2, order_no_ IN VARCHAR2, line_no_ IN VARCHAR2, release_no_ IN VARCHAR2, line_item_no_ IN VARCHAR2, user_id_ IN VARCHAR2, order_type_ IN NUMBER) -- -- 1、库内移库 2、仓库间调拨 3、拣货 IS qty_to_move_ NUMBER; max_line_no_ NUMBER := 0; contract_ VARCHAR2(5); component_part_ VARCHAR(25); backflush_loc_ VARCHAR2(35); warehouse_ VARCHAR2(15); ean_no_ VARCHAR2(35); lot_batch_no_ VARCHAR2(20); serial_no_ VARCHAR2(50); qty_onhand_ NUMBER; sum_qty_onhand_ NUMBER; -- CURSOR get_purchase_order_line_comp IS SELECT (nvl(qty_required,0) - nvl(qty_issued,0)) ,contract,component_part,backflush_loc FROM purchase_order_line_comp_tab WHERE order_no = order_no_ AND line_no = line_no_ AND release_no = release_no_ AND line_item_no = line_item_no_; -- CURSOR get_line_no IS SELECT MAX(line_no) FROM m_move_order_line_tab WHERE move_no = move_no_; -- CURSOR get_inventory_locaiton IS SELECT warehouse FROM inventory_location_tab WHERE contract = contract_ AND location_no = backflush_loc_ ; -- CURSOR get_inventory_part IS SELECT ean_no FROM inventory_part_tab WHERE part_no = component_part_ AND contract = contract_; -- CURSOR get_inventory_part_in_stock IS SELECT lot_batch_no , serial_no , qty_onhand FROM inventory_part_in_stock_tab WHERE contract = contract_ AND part_no = component_part_ AND warehouse = warehouse_ ; -- CURSOR get_sum_stock IS SELECT SUM(qty_onhand) FROM inventory_part_in_stock_tab WHERE contract = contract_ AND part_no = component_part_ AND warehouse = warehouse_ ; BEGIN OPEN get_purchase_order_line_comp ; FETCH get_purchase_order_line_comp INTO qty_to_move_ ,contract_ , component_part_ , backflush_loc_; CLOSE get_purchase_order_line_comp; OPEN get_inventory_locaiton ; FETCH get_inventory_locaiton INTO warehouse_; CLOSE get_inventory_locaiton; OPEN get_inventory_part ; FETCH get_inventory_part INTO ean_no_; CLOSE get_inventory_part; OPEN get_sum_stock ; FETCH get_sum_stock INTO sum_qty_onhand_; CLOSE get_sum_stock; -- 拣货数量大于在库存 IF sum_qty_onhand_ < qty_to_move_ THEN Error_Sys.Record_General(lu_name_, 'QTYNOTALLOW: 拣货数量 :P1 大于在库库存 :P2 创建委外出库拣货任务失败 !', qty_to_move_, sum_qty_onhand_); END IF; IF qty_to_move_ >0 THEN -- 创建移库单头 IF nvl(move_no_ ,'') = '' THEN move_no_ := M_Move_Order_Api.Get_Move_No( SYSDATE ); INSERT INTO m_move_order_tab ( move_no, moved_date, warehouse, moved_user, order_type, to_warehouse, rowstate, created_date, created_user, updated_date, updated_user, rowversion) VALUES ( move_no_, SYSDATE, warehouse_, NULL, order_type_, -- 1、库内移库 2、仓库间调拨 3、拣货 warehouse_, 1, -- 1、已创建 2、拣货中 3、已拣货 4、已完成 SYSDATE, user_id_, SYSDATE, user_id_, SYSDATE); END IF; --Fetch 循环 获取库存批次 OPEN get_inventory_part_in_stock;--必须要明确的打开和关闭游标 LOOP FETCH get_inventory_part_in_stock INTO lot_batch_no_ ,serial_no_ , qty_onhand_ ; EXIT WHEN get_inventory_part_in_stock%NOTFOUND; IF qty_onhand_ >= qty_to_move_ THEN -- 创建明细行 OPEN get_line_no; FETCH get_line_no INTO max_line_no_; CLOSE get_line_no; INSERT INTO m_move_order_line_tab ( move_no, line_no, warehouse, part_no, ean_no, qty_to_move, qty_moved, from_loca_no, to_loca_no, lot_batch_no, barcode_no, created_date, created_user, updated_date, updated_user, rowversion) VALUES ( move_no_, max_line_no_ + 1, warehouse_, component_part_, ean_no_, qty_to_move_, 0, backflush_loc_, '*', lot_batch_no_, '*', sysdate, user_id_, sysdate, user_id_, sysdate) ; EXIT; ELSE -- 创建明细行 OPEN get_line_no; FETCH get_line_no INTO max_line_no_; CLOSE get_line_no; INSERT INTO m_move_order_line_tab ( move_no, line_no, warehouse, part_no, ean_no, qty_to_move, qty_moved, from_loca_no, to_loca_no, lot_batch_no, barcode_no, created_date, created_user, updated_date, updated_user, rowversion) VALUES ( move_no_, max_line_no_ + 1, warehouse_, component_part_, ean_no_, qty_onhand_, 0, backflush_loc_, '*', lot_batch_no_, '*', sysdate, user_id_, sysdate, user_id_, sysdate) ; qty_to_move_ :=( qty_to_move_ - qty_onhand_ ); END IF; END LOOP; CLOSE get_inventory_part_in_stock; END IF; END Create_Pick_Task;

-- 完成委外叫料拣货 PROCEDURE Finished_Call_Out_Material(info_ OUT VARCHAR2, move_no_ IN VARCHAR2, department_id_ IN VARCHAR2, user_id_ IN VARCHAR2) IS max_line_no_ NUMBER; barcode_rec_ m_barcode_status_tab%ROWTYPE; sum_barcode_qty_ NUMBER; qty_issue_ NUMBER := 0; qty_remain_ NUMBER := 0; contract_ VARCHAR2(5) := User_Allowed_Site_Api.Get_Default_Site; CURSOR get_m_move_order_line IS SELECT * FROM M_MOVE_ORDER_LINE_TAB m WHERE m.move_no = move_no_ AND m.qty_to_move > m.qty_moved ; CURSOR get_max_line_no IS SELECT MAX(m.line_no) FROM M_MOVE_ORDER_LINE_TAB m WHERE m.move_no = move_no_ ; BEGIN -- 获取移库明细行 FOR move_line_rec_ IN get_m_move_order_line LOOP qty_remain_ := (move_line_rec_.qty_to_move - move_line_rec_.qty_moved) ; -- 判断标签库存是否足够 DECLARE CURSOR get_sum_barcode_qty IS SELECT SUM(m.qty) FROM m_location_no_barcode_tab m WHERE m.part_no = move_line_rec_.part_no AND m.location_no = move_line_rec_.from_loca_no AND m.contract = contract_; BEGIN OPEN get_sum_barcode_qty ; FETCH get_sum_barcode_qty INTO sum_barcode_qty_ ; CLOSE get_sum_barcode_qty ; IF (move_line_rec_.qty_to_move - move_line_rec_.qty_moved) > sum_barcode_qty_ THEN Error_Sys.Record_General(lu_name_, 'NOTENOUGHTBARCODESTOCK: 标签库存 :P1 在库数量 :P2 小于移库数量 :P3 !', move_line_rec_.part_no||'-'||move_line_rec_.from_loca_no, sum_barcode_qty_, move_line_rec_.move_no||'-'||move_line_rec_.line_no||'-'||move_line_rec_.qty_to_move - move_line_rec_.qty_moved); END IF ; END; DECLARE CURSOR get_m_location_barcode_tab IS SELECT * FROM m_location_no_barcode_tab m WHERE m.part_no = move_line_rec_.part_no AND m.location_no = move_line_rec_.from_loca_no AND m.contract = contract_ ; BEGIN -- 获取标签库存行执行移库 FOR loca_barcode_rec_ IN get_m_location_barcode_tab LOOP qty_issue_ := least(loca_barcode_rec_.qty , qty_remain_); qty_remain_ := qty_remain_ - qty_issue_ ; -- 库存下架 m_inv_part_in_stock_util_api.Issue_Part(part_no_ => move_line_rec_.part_no, location_no_ => move_line_rec_.from_loca_no, qty_issued_ => qty_issue_, user_id_ => user_id_) ; -- 库存上架 m_inv_part_in_stock_util_api.Receive_Part(part_no_ => move_line_rec_.part_no, location_no_ => move_line_rec_.to_loca_no, qty_received_ => qty_issue_, user_id_ => user_id_); -- barcode 库存下架 m_location_no_barcode_api.Issue_Barcode(barcode_no_ => loca_barcode_rec_.barcode_no, from_loca_no_ => move_line_rec_.from_loca_no, qty_issue_ => qty_issue_, transaction_code_ => 'INVM-ISS', user_id_ => user_id_ ); -- barcode 库存上架 m_location_no_barcode_api.Receive_Barcode(barcode_no_ => loca_barcode_rec_.barcode_no, to_loca_no_ => move_line_rec_.to_loca_no, qty_received_ => qty_issue_, transaction_code_ => 'INVM-IN', user_id_ => user_id_); OPEN get_max_line_no ; FETCH get_max_line_no INTO max_line_no_ ; CLOSE get_max_line_no; max_line_no_ := nvl(max_line_no_ , 0); DECLARE CURSOR get_m_barcode_status IS SELECT * FROM m_barcode_status_tab m WHERE m.barcode_no = loca_barcode_rec_.barcode_no AND m.contract = contract_; BEGIN OPEN get_m_barcode_status ; FETCH get_m_barcode_status INTO barcode_rec_ ; CLOSE get_m_barcode_status ; -- 插入新的移库行 INSERT INTO m_move_order_line_tab ( move_no, line_no, warehouse, part_no, ean_no, qty_to_move, qty_moved, from_loca_no, to_loca_no, to_warehouse, lot_batch_no, barcode_no, created_date, created_user, updated_date, updated_user, rowversion, co_order_no, co_line_no, co_release_no, co_line_item_no ) VALUES ( move_no_, max_line_no_ + 1, move_line_rec_.warehouse, move_line_rec_.part_no, move_line_rec_.ean_no, qty_issue_, qty_issue_, move_line_rec_.from_loca_no, move_line_rec_.to_loca_no, move_line_rec_.to_warehouse, barcode_rec_.lot_batch_no, barcode_rec_.barcode_no, sysdate, user_id_, sysdate, user_id_, sysdate, move_line_rec_.co_order_no, move_line_rec_.co_line_no, move_line_rec_.co_release_no, move_line_rec_.co_line_item_no); -- 完成后,删除原来的行 IF qty_remain_ = 0 THEN DELETE FROM m_move_order_line_tab WHERE move_no = move_no_ AND line_no = move_line_rec_.line_no ; EXIT ; END IF ; END; END LOOP ; END; END LOOP ; -- 跟新移库单状态 UPDATE m_move_order_tab m SET m.rowstate=4 WHERE m.move_no = move_no_ ; END Finished_Call_Out_Material;
数据库
2019-05-15 20:31:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一、目的
在互联网中,我们会向网络运营商申请指定额度的带宽。实际传输时,由于网络QoS达不到要求,实际的传输带宽可能达不到标称值。本次测试在局域网环境中使用模拟工具模拟不同QoS下的网络条件,得出不同QoS网络下传输带宽
二、网络QoS指标
网络的QoS通常用以下指标衡量:
丢包率:由于网络拥塞,传输错误等造成的数据包丢失概率
延时:数据包发送到对端再返回到发送端的时长
延时抖动:衡量延时变化的程度
本次测试值模拟丢包率和延时与传输带宽的关系。
注:本文中的丢包率指的是在传输速率小于最大传输带宽时的丢包率。
三、测试方案
本次测试分两个阶段进行:
在丢包率为0的情况下,测试不同延时下的传输速率;
调整丢包率与延时,测试不同延时与丢包率组合下的传输速率。
四、测试环境
测试使用两位服务器:
Server A:10.10.168.151
Server B:10.10.168.153
两台Server之间通过千兆以太网连接(带宽为1000Mbps);
两台服务器上部署iperf,用于向网络加流量,并统计传输速率;其中Server A上运行iperf server端(iperf -s),Server B上运行iperf client端(iperf -c 10.10.168.151 -t 100000 -i2)。数据流量方向是:server B → server A。iperf client端启动1分钟,统计1分钟内的平均传输速率作为一次测试的结果。
在Server B上,使用tc 模拟不同QoS的网络环境。tc 可以在server的出口方向模拟延时和丢包的情况:
添加延时:tc qdisc add dev enp0s25 root netem delay 50ms
添加丢包:tc qdisc add dev enp0s25 root netem loss 3%
添加延时与丢包:tc qdisc add dev enp0s25 root netem delay 50ms loss 3%
清空规则:tc qdisc del dev enp0s25 root
查看规则:tc qdisc ls dev enp0s25
五、测试结果
5.1 延时与传输速率的关系
丢包设置为0,延时从1ms 变化到 5000ms,测试每一个延时下的传输速率。下表为测试结果:
延时在4000ms以内时,传输速率能在1Mbps以上。当延时到达5000ms时,传输速率下降到200kbps。延时越大,达到稳定传输速率的时间越长。
注意:以上测试是在iperf采用默认的window size测试的结果;window size会影响到max cwnd和max rwnd,在延时比较大的时候,max wnd = MIN(max cwnd, max rwnd)决定了最终的传输速度;rate = max wnd/rtt;从测试的结果看,max cwnd比较小,这个值决定的传输速度;以300ms延时的测试点为例,通过-w参数增加发送端window size,传输速率可以提升到83Mbps,此时max rwnd决定了传输速度,此时,再通过-w参数增加接收端window size,传输速率可以到达450Mbps
5.2、延时、丢表率与传输速率的关系
丢表率从1%-10%变化,延时从10ms变化到1000ms,测试每一个组合下的传输速率。当延时/丢包率增大时,传输速率会下降,当传输速率低于500kbps时,不再继续增大延时/丢包率。下表为测试结果:
 
通过上表可以绘制丢包/延时与传输速率的曲面图:
X轴:延时(ms)
Y轴:丢包率
Z轴:传输速率(kbps)
通过曲面图,可以找到传输速率要达到500kbps/1000kbps时,延时与丢包率需要满足的条件

以上测试是单个客户端测试的结果。在Server B上启动多个客户端同时进行测试,只要总的传输速率不超过最大带宽限制,每个客户端的速率与单个客户端测试的结果相同。
数据库
2019-05-15 18:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一.将MySQL配置文件修改如下,没有的就添加
[client]
port=3306
default-character-set = utf8mb4
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
port=3306
basedir=D:\mysql
#解压目录
datadir=D:\mysql\data
#解压目录下data目录
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
[WinMySQLAdmin]
D:\mysql\bin\mysqld.exe
[mysql]
default-character-set=utf8mb4
二.修改数据库和表字符集
修改数据库字符集:
ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

修改表的字符集:
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
三.重启mysql服务
四.查询字符编码格式
SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';
+--------------------------+--------------------+
| Variable_name | Value |
+--------------------------+--------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
| collation_connection | utf8mb4_unicode_ci |
| collation_database | utf8mb4_unicode_ci |
| collation_server | utf8mb4_unicode_ci |
+--------------------------+--------------------+
这边有个坑,如果用Navicat数据库管理工具查询,会跟用控制台查询出的结果不一样,以控制台为准。
五.如果修改以上都不行请查询sql
mysql> show variables like '%sql_mode%';
+---------------+--------------------------------------------+
| Variable_name | Value |
+---------------+--------------------------------------------+
| sql_mode | STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+---------------+--------------------------------------------+
1 row in set (0.00 sec)
如果sql_mode值为STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,那么任然存储不了,
去设置这个sql_mod 模式
mysql> set global sql_mode = 'NO_ENGINE_SUBSTITUTION';
mysql> show variables like '%sql_mode%';
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| sql_mode | NO_ENGINE_SUBSTITUTION |
+---------------+------------------------+
1 row in set (0.00 sec)
做完这些就完成了,让MySQL支持Emoji表情
这句sql可以模拟向数据库插入emoji表情,INSERT INTO test(id) VALUES('😀');
只要能够执行成功,就说明可以了,别管插入的信息在数据库里是什么样子,有可能是空白,有可能是问号。
数据库
2019-05-15 17:58:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.远程登录mysql
mysql -h ip -u root -p 密码
2.创建用户
格式:grant 权限 on 数据库.* to 用户名@登录主机 identified by “密码”;
例1:增加一个test1用户,密码为123456,可以在任何主机上登录,并对所有数据库有查询,增加,修改和删除的功能。需要在mysql的root用户下进行
mysql>grant select,insert,update,delete on *.* to test1@”%” identified by “123456″;
mysql>flush privileges;
例2:增加一个test2用户,密码为123456,只能在192.168.2.12上登录,并对数据库student有查询,增加,修改和删除的功能。需要在mysql的root用户下进行
mysql>grant select,insert,update,delete on student.* to test2@192.168.2.12 identified by “123456″;
mysql>flush privileges;
例3:授权用户test3拥有数据库student的所有权限
mysql>grant all privileges on student.* to test3 @localhost identified by ’123456′;
mysql>flush privileges;
3.修改用户密码
mysql>update mysql.user set password=password(’123456′) where User=’test1′ and Host=’localhost’;
mysql>flush privileges;
4.删除用户
mysql>delete from user where user=’test2′ and host=’localhost’;
mysql>flush privileges;
5.删除数据库和删除表
mysql>drop database 数据库名;
mysql>drop table 表名;
6.删除账户及权限
drop user 用户名@’%’
drop user 用户名@localhost
**************************************************************************************
grant 详细解析如下:
**************************************************************************************
MySQL 赋予用户权限命令的简单格式可概括为:
grant 权限 on 数据库对象 to 用户
一、grant 普通数据用户,查询、插入、更新、删除 数据库中所有表数据的权利。
grant select on testdb.* to common_user@’%’
grant insert on testdb.* to common_user@’%’
grant update on testdb.* to common_user@’%’
grant delete on testdb.* to common_user@’%’
或者,用一条 MySQL 命令来替代:
grant select, insert, update, delete on testdb.* to common_user@’%’
二、grant 数据库开发人员,创建表、索引、视图、存储过程、函数。。。等权限。
grant 创建、修改、删除 MySQL 数据表结构权限。
grant create on testdb.* to developer@’192.168.0.%’;
grant alter on testdb.* to developer@’192.168.0.%’;
grant drop on testdb.* to developer@’192.168.0.%’;
grant 操作 MySQL 外键权限。
grant references on testdb.* to developer@’192.168.0.%’;
grant 操作 MySQL 临时表权限。
grant create temporary tables on testdb.* to developer@’192.168.0.%’;
grant 操作 MySQL 索引权限。
grant index on testdb.* to developer@’192.168.0.%’;
grant 操作 MySQL 视图、查看视图源代码 权限。
grant create view on testdb.* to developer@’192.168.0.%’;
grant show view on testdb.* to developer@’192.168.0.%’;
grant 操作 MySQL 存储过程、函数 权限。
grant create routine on testdb.* to developer@’192.168.0.%’; — now, can show procedure status
grant alter routine on testdb.* to developer@’192.168.0.%’; — now, you can drop a procedure
grant execute on testdb.* to developer@’192.168.0.%’;
三、grant 普通 DBA 管理某个 MySQL 数据库的权限。
grant all privileges on testdb to dba@’localhost’
其中,关键字 “privileges” 可以省略。
四、grant 高级 DBA 管理 MySQL 中所有数据库的权限。
grant all on *.* to dba@’localhost’
五、MySQL grant 权限,分别可以作用在多个层次上。
1. grant 作用在整个 MySQL 服务器上:
grant select on *.* to dba@localhost; — dba 可以查询 MySQL 中所有数据库中的表。
grant all on *.* to dba@localhost; — dba 可以管理 MySQL 中的所有数据库
2. grant 作用在单个数据库上:
grant select on testdb.* to dba@localhost; — dba 可以查询 testdb 中的表。
3. grant 作用在单个数据表上:
grant select, insert, update, delete on testdb.orders to dba@localhost;
4. grant 作用在表中的列上:
grant select(id, se, rank) on testdb.apache_log to dba@localhost;
5. grant 作用在存储过程、函数上:
grant execute on procedure testdb.pr_add to ‘dba’@’localhost’
grant execute on function testdb.fn_add to ‘dba’@’localhost’
六、查看 MySQL 用户权限
查看当前用户(自己)权限:
show grants;
查看其他 MySQL 用户权限:
show grants for dba@localhost;
七、撤销已经赋予给 MySQL 用户权限的权限。
revoke 跟 grant 的语法差不多,只需要把关键字 “to” 换成 “from” 即可:
grant all on *.* to dba@localhost;
revoke all on *.* from dba@localhost;
八、MySQL grant、revoke 用户权限注意事项
1. grant, revoke 用户权限后,该用户只有重新连接 MySQL 数据库,权限才能生效。
2. 如果想让授权的用户,也可以将这些权限 grant 给其他用户,需要选项 “grant option“
grant select on testdb.* to dba@localhost with grant option;
这个特性一般用不到。实际中,数据库权限最好由 DBA 来统一管理。
Category: Post
You can follow any responses to this entry via RSS.
Comments are currently closed, but you can trackback from your own site.
=========================================================================
1.创建用户并授权
grant语句的语法:
grant privileges (columns) on what to user identified by “password” with grant option
要使用该句型,需确定字段有:
privileges 权限指定符权限允许的操作
alter 修改表和索引
create 创建数据库和表
delete 删除表中已有的记录
drop 抛弃(删除)数据库和表
index 创建或抛弃索引
insert 向表中插入新行
reference 未用
select 检索表中的记录
update 修改现存表记录
file 读或写服务器上的文件
process 查看服务器中执行的线程信息或杀死线程
reload 重载授权表或清空日志、主机缓存或表缓存。
shutdown 关闭服务器
all 所有;
all privileges同义词
usage 特殊的“无权限”权限
以上权限分三组:
第一组:适用于数据库、表和列如:alter create delete drop index insert select update
第二组:数管理权限 它们允许用户影响服务器的操作 需严格地授权 如:file process reload shut*
第三组:权限特殊 all意味着“所有权限” uasge意味着无权限,即创建用户,但不授予权限
columns
权限运用的列(可选)并且你只能设置列特定的权限。如果命令有多于一个列,应该用逗号分开它们。
what
权限运用的级别。权限可以是全局,定数据库或特定表.
user
权限授予的用户,由一个用户名和主机名组成,许两个同名用户从不同地方连接.缺省:mysql用户password
赋予用户的口令(可选),如果你对用户没有指定identified by子句,该用户口令不变.
用identified by时,口令字符串用改用口令的字面含义,grant将为你编码口令.
注:set password使用password()函数
with grant option
用户可以授予权限通过grant语句授权给其它用户(可选)
实例讲解:
grant all on db_book.* to huaying@koowo.com identified by “yeelion” 只能在本地连接
grant all on db_book.* to huaying@vpn.koowo.com identified by “yeeliong” 允许从此域连接
grant all on db_book.* to huaying@% identified by “yeelion” 允许从任何主机连接 注:”%”字符起通配符作用,与like模式匹配的含义相同。
grant all on db_book.* to huaying@%.koowo.com identified by “yeelion”; 允许huaying从koowo.com域的任何主机连接
grant all on db_book.* to huaying@192.168.1.189 identified by “yeelion”
grant all on db_book.* to huaying@192.168.1.% identified by “yeelion”
grant all on db_book.* to huaying@192.168.1.0/17 identified by “yeelion”
允许从单IP 段IP或一子网IP登陆
注:有时 用户@IP 需用引号 如”huaying@192.168.1.0/17″
grant all on *.* to huaying@localhost identified by “yeelion” with grant option
添加超级用户huaying 可在本地登陆做任何操作.
grant reload on *.* to huaying@localhost identified by “yeelion” 只赋予reload权限
grant all on db_book to huaying@koowo.com indetified by “yeelion” 所有权限
grant select on db_book to huaying@% indetified by “yeelion” 只读权限
grant select,insert,delete,update on db_book to huaying@koowo.com indetified by “yeelion”
只有select,insert,delete,update的权限
grant select on db_book.storybook to huaying@localhost indetified by “yeelion” 只对表
grant update (name) on db_book.storybook to huaying@localhost 只对表的name列 密码不变
grant update (id,name,author) on db_book.storybook to huaying@localhost 只对表的多列
grant all on book.* to “”@koowo.com 允许koowo.com域中的所有用户使用库book
grant all on book.* to huaying@%.koowo.com indetified by “yeelion” with grant option
允许huaying对库book所有表的管理员授权.
2.撤权并删除用户
revoke的语法类似于grant语句
to用from取代,没有indetifed by和with grant option子句. 如下:
revoke privileges (columns) on what from user
user:必须匹配原来grant语句的你想撤权的用户的user部分。
privileges:不需匹配,可以用grant语句授权,然后用revoke语句只撤销部分权限。
revoke语句只删权限不删用户,撤销了所有权限后user表中用户记录保留,用户仍然可以连接服务器.
要完全删除一个用户必须用一条delete语句明确从user表中删除用户记录:
delete from user where user=”huaying”
flush privileges; 重载授权表
注:使用grant和revoke语句时,表自动重载,而你直接修改授权表时不是.
实例:
1.创建数据库
CREATE DATABASE `fypay` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
2.为创建的数据库增加用户fypay
grant create,select,insert,update,delete,drop,alter on fypay.* to fypay@”%” identified by “testfpay”;
3.删除fypay用户
delete from user where user=”fypay”
drop user fypay@localhost
4.刷新数据库
flush privileges;
数据库
2019-05-15 16:44:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MySQL 的if既可以作为表达式用,也可在存储过程中作为流程控制语句使用,如下是做为表达式使用:
IF(expr1,expr2,expr3)
如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL),则 IF()的返回值为expr2; 否则返回值则为 expr3。IF() 的返回值为数字值或字符串值,具体情况视其所在语境而定。
SELECT
activity_id,
open_id,
rank
FROM
(
SELECT
sg_win.*,
IF( @id = activity_id, @rows := @rows + 1, @rows := 1 ) AS rank ,
@id :=activity_id
FROM sg_win,(select @id=null,@rows=0) r ORDER BY open_id
) s
查询出来的数据为
借鉴地址: https://www.cnblogs.com/niniya/p/9046449.html
数据库
2019-05-15 14:59:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
SQLite是一个开源的跨平台的轻型数据库,WINCE本身也有一个自带的数据库SQLCE ,但占用的资源会比较大。最近项目中考虑用到 SQLite,因此特别研究了一下,下面介绍一下具体的移植方法。
一、下载SQLite源码
去SQLite官网 http://www.sqlite.org/download.htm 下载最新的source code。我下载的是sqlite-amalgamation-3071401.zip。解压后会得得到四个文件(shell.c、sqlite3.c、sqlite3.h、sqlite3ext.h)。
二、编译生成WINCE下DLL
1. 在VS2005下新建一个Win32智能设备项目,选择相应的SDK,并选择应用程序类型为DLL。
2. 将sqlite-amalgamation-3071401.zip解压后的sqlite3.c、sqlite3.h文件拷贝到工程目录下并添加至工程目录。如下图所示。

3.在预处理中增加SQLITE_ENABLE_RTREE和SQLITE_ENABLE_COLUMN_METADATA宏定义。如图所示:

4.编译项目,会提示“error LNK2019: 无法解析的外部符号 localtime_s,该符号在函数 osLocaltime 中被引用”错误,此错误的解决方法是将localtime_s替换成_localtime64_s即可。
5.编译成功后会发现目录下会生成SQLite.dll文件,但会发现没有lib文件,这样在使用这个DLL时是没法编译的,所以还需要导出.lib文件,解决方法是在SQLite官网上下载sqlite-dll-win32-x86-XXXXXX.zip文件,解压后将目录下的sqlite3.def文件拷贝到DLL工程目录,在项目属性--链接器--输入--模块定义文件中添加sqlite3.def,如下图所示,然后编译即可。

这样SQLite的编译工作就大功告成了。
三、WINCE 下SQLite测试
新建一个SQLite测试程序,测试代码如下:

[cpp] view plain copy
#include // sqlite3的回调函数 int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name ) int main(int argc,char* argv[]) { sqlite3 * db = NULL; //声明sqlite关键结构指针 int result; // 打开或创建数据库 result = sqlite3_open("NAND2\\sqlite.db", &db ); if( result != SQLITE_OK ) { //数据库打开失败 return -1; } char * errmsg = NULL; // 数据库操作代码 #if 1 // 创建一个测试表,表名叫 MyTable,有2个字段: ID 和 name。其中ID是一个自动增加的类型,以后insert时可以不去指定这个字段,它会自己从0开始增加 result = sqlite3_exec( db, "create table MyTable( ID integer primary key autoincrement, name nvarchar(32) )", NULL, NULL, &errmsg ); if(result != SQLITE_OK ) { printf("创建表失败,错误码:%d,错误原因:%s\n", result, errmsg ); } // 插入记录 result = sqlite3_exec( db, "insert into MyTable( name ) values ( '张三' )", 0, 0, &errmsg ); if(result != SQLITE_OK ) { printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg ); } // 插入记录 result = sqlite3_exec( db, "insert into MyTable( name ) values ( '李四' )", 0, 0, &errmsg ); if(result != SQLITE_OK ) { printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg ); } #endif // 开始查询数据库 result = sqlite3_exec( db, "select * from MyTable", SQLiteQueryResultCallBack, NULL, &errmsg ); // 关闭数据库 sqlite3_close( db ); return 0; } // sqlite3的回调函数 int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name ) { printf( "******************************\n" ); printf("记录包含 %d 个字段\n", n_column ); for(int i = 0 ; i < n_column; i ++ ) { printf( "字段名:%s 字段值:%s\n", column_name[i], column_value[i] ); } printf( "******************************\n" ); return 0; } #include // sqlite3的回调函数 int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name ) int main(int argc,char* argv[]) { sqlite3 * db = NULL; //声明sqlite关键结构指针 int result; // 打开或创建数据库 result = sqlite3_open("NAND2\\sqlite.db", &db ); if( result != SQLITE_OK ) { //数据库打开失败 return -1; } char * errmsg = NULL; // 数据库操作代码 #if 1 // 创建一个测试表,表名叫 MyTable,有2个字段: ID 和 name。其中ID是一个自动增加的类型,以后insert时可以不去指定这个字段,它会自己从0开始增加 result = sqlite3_exec( db, "create table MyTable( ID integer primary key autoincrement, name nvarchar(32) )", NULL, NULL, &errmsg ); if(result != SQLITE_OK ) { printf("创建表失败,错误码:%d,错误原因:%s\n", result, errmsg ); } // 插入记录 result = sqlite3_exec( db, "insert into MyTable( name ) values ( '张三' )", 0, 0, &errmsg ); if(result != SQLITE_OK ) { printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg ); } // 插入记录 result = sqlite3_exec( db, "insert into MyTable( name ) values ( '李四' )", 0, 0, &errmsg ); if(result != SQLITE_OK ) { printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg ); } #endif // 开始查询数据库 result = sqlite3_exec( db, "select * from MyTable", SQLiteQueryResultCallBack, NULL, &errmsg ); // 关闭数据库 sqlite3_close( db ); return 0; } // sqlite3的回调函数 int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name ) { printf( "******************************\n" ); printf("记录包含 %d 个字段\n", n_column ); for(int i = 0 ; i < n_column; i ++ ) { printf( "字段名:%s 字段值:%s\n", column_name[i], column_value[i] ); } printf( "******************************\n" ); return 0; }
四、SQLite可视化管理工具



SQLite本身没有可视化管理工具,只提供了一个命令行的管理工具SQLite.exe。有一个第三方的可视化管理工具Sqlite Expert,用着还可以,下载地址: http://www.sqliteexpert.com/download.html
数据库
2019-05-15 11:46:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
如果发现某进程比较消耗资源,但变动频繁,并且想知道该进程在执行什么 SQL ,那么可以使用会话跟踪来找到该进程执行的SQL:
跟踪之前请设置timed_statistics=true及max_dump_file_size= unlimited ,根据操作系统进程的 SPID 找到上述的 ORACLE 内部会话的 sid , serial #,可以使用如下sql:
SELECT A.sid,A.Machine,A.Terminal,A.Program FROM V$ SESSION A, V$PROCESS B WHERE A.PAddr = B.Addr AND B.spid = '&spid';
执行跟踪:
sql> execute dbms_system.set_sql_trace_in_session(sid,serial#,true);
而10046事件跟踪是上述会话跟踪的增强版本,它比上述的会话跟踪能够得到更为详细的内容,推荐使用8级进行跟踪。10046等待事件有1,4,8,12四种等级,8级会记录等待事件,12级在记录等待事件的同时还会捕捉绑定变量的值。
跟踪自己所在的进程的方法:
sql> alter session set events '10046 trace name context forever, level8';
sql> alter session set events '10046 trace name context off';
跟踪其他用户所在的进程的方法,使用 ORACLE 级别的session号:
sql> exec dbms_system.set_ev(sid,serial#,10046,8,' username ');
sql> exec dbms_system.set_ev(sid,serial#,10046,0,' username ');
或者使用操作系统进程号:
sql> oradebug setospid
sql> oradebug unlimit
sql> oradebug event 10046 trace name context forever, level 8
跟踪自己,也可以使用:
alter session set sql _trace=true;
另外:
提取sid,serial#的语句,在下面加上serial#即可:
SELECT A.sid,A.Machine,A.Terminal,A.Program FROM V$SESSION A, V$PROCESS B WHERE A.PAddr = B.Addr AND B.spid = '&spid';
为:
SELECT A. sid ,a. serial #, A.Machine,A.Terminal,A.Program FROM V$ SESSION A, V$PROCESS B WHERE A.PAddr = B.Addr AND B.spid = '& spid ';
数据库
2019-05-14 17:56:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 作者:韩锋
出处:DBAplus社群分享
Themis开源地址: https://github.com/CreditEaseDBA
拓展阅读: 宜信开源|数据库审核软件Themis的规则解析与部署攻略
【技术沙龙002期】数据中台:宜信敏捷数据中台建设实践|宜信技术沙龙 将于5月23日晚8点线上直播,点击报名
一、面临的挑战
1、运维规模及种类
我相信,这也是很多公司、很多DBA正在面临或未来都会面临的一些问题。正是存在问题,促使我们考虑引入数据库审核平台。
首先是运维规模与人力资源之间的矛盾
从我们的情况来看,运维了包括Oracle、MySQL、MongoDB、Redis四类数据库,数据库规模几十套,支持公司千余名开发人员及上百套业务系统。也许有的朋友会问,从运维规模上看,并不是很大。
的确,与很多互联网公司相比,数据库数十套的估摸并不是太大;但与互联网类公司不同,类似宜信这类金融类公司对数据库的依赖性更大,大量的应用是重数据库类的,且其使用复杂程度也远比互联网类的复杂。DBA除了日常运维(这部分我们也在通过自研平台提升运维效率)外,还需要有大量精力应对数据库设计、开发、优化类的工作。当面对大量的开发团队需要服务时,这个矛盾就更加凸显出来。
2、案例
结构设计
第二个挑战,是数据库设计、开发质量参差不齐的问题
上图就展示了一个结构设计问题。某核心系统的核心表,在这个系统运行的SQL中,28%都是跟这个对象有关的。当我们分析其结构时,发现了很多的问题: 表的规模很大,从设计之初就没有考虑到拆分逻辑(例如分库、分表、分区设计),也没有必要的数据库清理、归档策略。 表存在100多个字段,字段数很多且不同字段使用特征也不一致,没有考虑到必要拆表设计。 表有13个索引,数目过多。表的索引过度,势必会影响其DML效率。 还存在一个索引,在持续监控中发现,其从未被使用过。显然这是一个“多余”的索引。 还有两个字段存在重复索引的现象,这也说明在建立索引之初是比较随意的。 单个记录定义长度为5800多个字节,但实际其平均保存长度只有不到400字节,最大长度也不长。 分析其字段内容,还发现有3个字段类型定义异常。即没有使用应有的类型保存数据,例如使用数字类型保存日期。
综上所述,这个表设计的问题还有很多,而且这个表非常重要,大量语句访问和其相关。
SQL语句
上图展示的是一个语句运行效率的问题。从字面可见,两个表做关联查询,但在指定条件时没有指定关联条件。在下面的执行计划中可见,数据库采用了笛卡尔积的方式运行。从后面的成本、估算时间等可见,这是一个多么“巨大”的SQL。其在线上运行的影响,可想而知。
也许有人会说,这是一个人为失误,一般不会发生。但我要说的是,第一,人为失误无法避免,谁也不能保证写出SQL的运行质量;第二,开发人员对数据库的理解不同,很难保证写出的SQL都是高效的;第三,开发人员面临大量业务需求,经常处理赶工状态,很难有更多的精力放在优化上面。这因为有这些问题,线上语句执行质量就成了DBA经常面临的挑战之一。
3、重心转移
这是一张很经典的图,它描述了和数据库相关工作的职能划分。作为DBA,除了面临以上挑战外,从数据库工作发展阶段及自身发展需求来看,也面临一个重心的转移:原有传统DBA的运维职能逐步被弱化,大量的工具、平台的涌现及数据库自我运维能力的提升,简化DBA的工作;紧随而来的数据库架构、结构设计、SQL质量优化逐步成为重点;再往上层的数据治理、建模等工作也越来越受到一些公司的重视。由此可见,DBA未来工作的中心也逐步上移。对中间数据逻辑结构部分,也需要一些工具、平台更好地支撑DBA的工作。
除上述情况外,我司还存在几种的不平衡。
从DBA日常工作来看,传统运维工作还是占了较大的比重,而架构优化类则相对较少。通过引入这一平台,可以帮助DBA更方便地开展架构、优化类工作。 公司使用了较多的商业产品,而开源则使用较少。从公司长远战略来看,开源产品的使用会越来越多。从功能角度来看,商业产品相较于开源产品是有优势的。基于开源产品的软件开发,对开发者自身技术技能要求更高。希望通过引入这一产品,可以更容易完成这一转型过程。 没有平台之前,DBA还是大量通过手工方式设计、优化数据库,其效率十分低下。特别是面对众多产品线、众多开发团队时,往往感觉力不从心。
公司自有团队人员上,还是以初中级为主,中高级人员相对较少。如何快速提升整体设计、优化能力,保证统一的优化效果成为摆在面前的问题。
正是有了上述多种的不平衡,促使我们考虑引入工具、平台去解决数据库质量问题。
我刚来到公司时,看到公司的这些问题,也曾考虑通过制度、规范的形式进行解决。一开始就着手制定了很多的规范,然后在各个部门去培训、宣讲。这种方式运行一段时间后,暴露出一些问题: 整体效果改善并不明显。实施效果取决于各个部门的重视程度及员工的个人能力。 规范落地效果无法度量,也很难做到量化分析。往往只能通过上线运行结果来直观感知。 缺乏长期有效的跟踪机制。无法对具体某个系统长期跟踪其运行质量。 从DBA的角度来看,面对大量的系统,很难依据每个规范,详细审核其结构设计、SQL运行质量。
面临上述这些挑战、现存的各种问题,该如何解决?
经过讨论,最后大家一致认为,引入数据库审核平台,可以帮助解决上面所述问题。
二、平台的选型
1、业内做法
在项目之初,我考察了业内其它企业是如何数据库审核的,大致可分为三个思路:
第一类,是以BAT公司为代表的互联网类公司。它们通过自研的SQL引擎,可实现成本分析、自动审核、访问分流、限流等,可做到事前审核、自动审核。但技术难度较大,公司现有技术能力明显不足。
第二类,是通过自研工具收集DB运行情况,根据事前定义规则进行审核,结合人工操作来完成整个审核流程。这种方案只能做到事后审核,但技术难度较小,灵活度很大。其核心就是规则集的制定,可根据情况灵活扩展。
第三类,是一些商业产品,实现思路类似第二类,但是加上一些自主分析能力,功能更为强大,但仍需人工介入处理且需要不小资金投入。而且考察几款商业产品,没有能完全满足所需功能的。
综合上面几类做法,最终确定我们采用“工具+人工审核”的方式,自研自己的审核平台。
2、我们的选择——自研
在启动研发这一平台之初,我们就在团队内部达成了一些共识。 DBA需要扭转传统运维的思想,每个人都参与到平台开发过程中。 过去我们积累的一些内容(例如前期制定的规范)可以作为知识库沉淀下来,并标准化,这些为后期规则的制定做好了铺垫。 在平台推进中,从最简单的部分入手,开发好的就上线实施,观察效果;根据实施效果,不断修正后面的工作。 结合我们自身的特点,定制目标;对于有些较复杂的部分,可果断延后甚至放弃。 参考其它公司或商业产品的设计思想,大胆引入。
三、审核平台实践
下面来看看,审核平台的基本功能及实现原理及方法,这部分是本次分享的重点。
1、平台定位
在项目之初,我们就平台的定位做了描述: 平台的核心能力是快速发现数据库设计、SQL质量问题。 平台只做事后审核,自主优化部分放在二期实现。当然在项目设计阶段引入这个,也可以起到一部分事前审核的功能。 通过Web界面完成全部工作,主要使用者是DBA和有一定数据库基础的研发人员。 可针对某个用户审核,可审核包括数据结构、SQL文本、SQL执行特征、SQL执行计划等多个维度。 审核结果通过Web页面或导出文件的形式提供。 平台需支持公司主流的Oracle、MySQL,其它数据库放在二期实现。 尽量提供灵活定制的能力,便于日后扩展功能。
2、平台使用者
作为平台的两类主要使用方,研发人员和DBA都可以从平台中受益。 对于研发人员而言,只用这平台可方便定位问题,及时进行修改;此外通过对规则的掌握,也可以指导他们设计开发工作。 对于DBA而言,可快速掌握多个系统的整体情况,批量筛选出低效SQL,并可通过平台提供的信息快速诊断一般性问题。
3、实现原理
整个平台的基本实现原理很简单,就是将我们的审核对象(目前支持四种),通过规则集进行筛选。符合规则的审核对象,都是疑似有问题的。平台会将这些问题及关联信息提供出来,供人工甄别使用。由此可见,平台的功能强大与否,主要取决于规则集的丰富程度。平台也提供了部分扩展能力,方便扩展规则集。
4、平台设计
审核对象
在开始介绍平台实现之前,再来熟悉下“审核对象”这个概念。目前我们支持的有四类对象,分别说明一下。 对象级。这里所说的对象就是指数据库对象,常见的表、分区、索引、视图、触发器等等。典型规则,例如大表未分区等。 语句级。这里所说的语句级,实际是指SQL语句文本本身。典型规则,例如多表关联。 执行计划级。这里是指数据库中SQL的执行计划。典型规则,例如大表全表扫描。 执行特征级。这里是指语句在数据库上的真实执行情况。典型规则,例如扫描块数与返回记录比例过低。
需要说明一下,这四类审核对象中,后三种必须在系统上线运行后才会抓取到,第一种可以在只有数据结构的情况下运行(个别规则还需要有数据)。
此外,上述规则中,除了第二类为通用规则外,其他都与具体数据库相关。即每种的数据库,都有自己不同的规则。
架构简图
这里画出是系统架构框架简图,我简单说明一下。
图中的方框部分,为平台的主要模块。底色不同的模块,表示当前的进度状态不同。虚线代表数据流,实线代表控制流。其核心为这几个模块: 数据采集模块。它是负责从数据源抓取审核需要的基础数据。目前支持从Oracle、MySQL抓取。 OBJ/SQL存储库。这是系统的共同存储部分,采集的数据和处理过程中的中间数据、结果数据都保存在这里。其核心数据分为对象类和SQL类。物理是采用的MongoDB。 核心管理模块。图中右侧虚线部分包含的两个模块:SQL管理和OBJ管理就是这部分。它主要是完成对象的全生命周期管理。目前只做了简单的对象过滤功能,因此还是白色底色,核心的功能尚未完成。 审核规则和审核引擎模块。这部分是平台一期的核心组件。审核规则模块是完成规则的定义、配置工作。审核引擎模块是完成具体规则的审核执行部分。 优化规则和优化引擎模块。这部分是平台二期的核心组件。目前尚未开发,因此为白色底色。 系统管理模块。这部分是完成平台基础功能,例如任务调度、空间管理、审核报告生成、导出等功能。
流程图
让我们从处理流程的角度,看看平台的整体处理过程。 “规则管理”部分,这部分主要完成以下一些功能。 初始化规则。平台本身内置了很多规则,在这一过程中到导入到配置库中。 新增规则。平台本身提供了一定的扩展能力,可以依据规范新增一条规则。 修改规则。可以根据自身情况开启或关闭规则。对于每条规则,还内置了一些参数,也可在此处修改。此外,针对违反规则的情况,还可以设置扣分方法(例如违反一次扣几分、最多可扣几分)等。 规则本身及相关参数、配置信息等都会存储在配置库中。 “任务管理”部分,这是后台管理的一个部分,主要完成与任务相关的工作。系统中的大多数交互都是通过作业异步完成的。其后台是通过celery+flower实现的。 “数据采集”部分,这部分是通过任务调度定时出发采集作业完成,也有少量部分是实时查询线上库完成的。采集的结果保存在数据库中,供后续分析部分调用。 “规则解析”部分,这部分是由用户通过界面触发,任务调度模块会启动一个后台异步任务完成解析工作。之所以设计为异步完成,主要是审核工作可能时间较长(特别是选择审核类别较多、审核对象很多、开启的审核规则较多)的情况。审核结果会保存在数据库中。 “任务查看、导出”部分,在用户发起审核任务后,可在此部分查看进度(处于审核中、还是审核完成)。当审核完成后,可选择审核任务,浏览审核结果或选择导出均可。如果是选择导出的话,会生成异步后台作业生成文件,放置在下载服务器上。
以上就是整个审核的大体流程。后续将看到各部分的详细信息。
模块划分
总结一下,平台主要是由上述四个模块组成:数据采集、规则解析、系统管理、结果展示。后面将针对不同模块的实现,进行详细说明。
5、数据采集
采集内容
先来看看数据采集模块。从表格可见,两种类型数据库的采集内容不同。
Oracle提供了较为丰富的信息,需要的基本都可采集到;MySQL功能相对能采集到的信息较少。
表格中的“对号+星号”,表示非定时作业完成,而是后面实时回库抓取的。下面简单说下,各部分的采集内容。 对象级,采集了对象统计信息、存储特征、结构信息、访问特征。 SQL级,采集了SQL文本,执行计划、缓存游标、绑定变量、执行特征等。
这些信息都将作为后面审核的依据。
采集原理
下面简单介绍下采集的与原理: Oracle部分,是通过定时作业采集的AWR数据,然后转储到一套MongoDB中。这里跟有些类似产品不同,没有直接采集内存中的数据,而是取自离线的数据。其目的是尽量减少对线上运行的影响。Oracle提供的功能比较丰富,通过对AWR及数据字典的访问,基本就可以获得全部的数据。 MySQL部分,情况就要复杂一些,原因是其功能没有那么丰富。多类数据是通过不同源来获取。SQL文本类及执行特征类的,是通过pt工具分析慢查询日志定时入到Anemometer平台库,然后从此库传入MongoDB。其它类信息(包括数据字典类、执行计划类等)是在需要时通过实时回库查询的。为了防止影响主库,一般是通过路由到从库上执行获得的。
6、规则解析
概要说明
下面介绍整个系统最为核心的部分—规则解析模块,它所完成的功能是依据定义规则,审核采集的数据,筛选出违反规则的数据。对筛选出的数据进行计分,并记录下来供后续生成审核报告使用。同时还会记录附加信息,用于辅助进行一些判断工作。
这里有个核心的概念—“规则”。后面可以看到一个内置规则的定义,大家就会比较清楚了。从分类来看,可大致分为以下几种。 从数据库类型角度来区分,规则可分为Oracle、MySQL。不是所有规则都区分数据库,文本类的规则就不区分。 从复杂程度来区分,规则可分为简单规则和复杂规则。这里所说的简单和复杂,实际是指规则审核的实现部分。简单规则是可以描述为MongoDB或关系数据库的一组查询语句;而复杂规则是需要在外部通过程序体实现的。 从审核对象角度来区分,规则可分为对象类、文本类、执行计划类和执行特征类。下面会针对每类审核对象,分别做说明。
规则定义
这是一个规则体的声明对象,我说明一下各字段含义,大家也可对规则有个清晰的认识。 db_type:规则的数据库类别,支持Oracle、MySQL。 input_parms:输入参数。规则是可以定义多个输出参数,这是一个参数列表,每个参数自身又是一个字典类,描述参数各种信息。 output_parms:输出参数。类似上面的输入参数,也是一个字典对象列表。描述了根据规则返回信息结构。 rule_complexity:规则是复杂规则还是简单规则。如果是简单规则,则直接取rule_cmd内容作为规则审核的实现。如果是复杂规则,则是从外部定义的rule_name命令脚本中获得规则实现。 rule_cmd:规则的实现部分。规则可能是mongodb的查询语句、可能是一个正则表达式,具体取决于rule_type。 rule_desc:规则描述,仅供显示。 rule_name:规则名称。是规则的唯一标识,全局唯一。 rule_status:规则状态,ON或是OFF。对于关闭的规则,在审核时会忽略它。 rule_summary:一个待废弃的字段,意义同rule_desc。 rule_text:规则类型,分为对象、文本、执行计划、执行特征四类。图中的示例标识一个文本类型的规则,rule_cmd是正则表达式。 solution:触发此规则的优化建议。 weight:权重,即单次违反规则的扣分制。 max_score:扣分上限,为了避免违反一个规则,产生过大影响,设置此参数。
规则定义(对象级)
先来看第一类规则—对象规则。这是针对数据库对象设置的一组规则。上面表格,显示了一些示例。常见的对象,诸如表、分区、索引、字段、函数、存储过程、触发器、约束、序列等都是审核的对象。以表为例,内置了很多规则。
例如:第一个的“大表过多”。表示一个数据库中的大表个数超过规则定义阀值。这里的大表又是通过规则输入参数来确定,参数包括表记录数、表物理尺寸。整体描述这个规则就是“数据库中超过指定尺寸或指定记录数的表的个数超过规定阀值,则触发审核规则”。其它对象的规则也类似。
规则实现(对象级)
对象规则的实现部分,比较简单。除个别规则外,基本都是对数据字典信息进行查询,然后依据规则定义进行判断。上面示例就是对索引的一个规则实现中,查询数据字典信息。
规则定义(执行计划级)
第二类规则是执行计划类的规则,它也划分为若干类别。例如访问路径类、表间关联类、类型转换类、绑定变量类等。
以最为常见的的访问路径类为例,进行说明下。如最为常见的一个规则“大表扫描”。它表示的是SQL语句的执行中,执行了对大表的访问,并且访问的路径是采用全表扫描的方式。这个规则的输入参数,包含了对大表的定义(物理大小或记录数);输出部分则包括了表名、表大小及附加信息(包括整个执行计划、指定大表的统计信息等内容)。
这类规则针对的数据源,是从线上数据库中抓取的。Oracle部分是直接从AWR中按时间段提取的,MySQL部分是使用explain命令返查数据库得到的。
信息存储格式
在这里特别说明一下,在保存执行计划的时候,使用了MongoDB这种文档性数据库。目的就是利用其schemaless特性,方便兼容不同数据库、不同版本执行计划的差异。都可以保存在一个集合中,后续的规则审核也是利用的mongo中的查询语句实现的。这也是最初引入mongo的初衷,后续也将其它类信息放入库中。现在整个审核平台,除了pt工具接入的部分使用MySQL外,其余都在MongoDB中。此外,MySQL库可以直接输出json格式的执行计划,很方便就入库了;Oracle部分也组成json格式入库。
规则实现(执行计划)
左边就是一个Oracle的执行计划保存在MongoDB中的样子。其实就是将sqlplan字典数据插入到mongo中。右侧就是一个规则实现的样例,就是基于mongo的查询语句。后面我们会可看到一个详细的示例。
7、平台实现
规则实现
这里以“大表全表扫描”规则为例,进行说明。上面是在Oracle中的数据字典保存的执行计划,下面是存在Mongo中的。可见,就是完全复制下来的。
基于这样的结构,如何实现规则过滤呢?其实就是通过mongo中的find语句实现的。下面具体解读下这个语句的执行步骤。 最上面的find()部分,是用来过滤执行计划的。将满足指定用户、时间范围、访问路径(“TABLE ACCESS”+”FULL”)的执行计划筛选出来。 筛选出的部分,会关联对象数据,将符合“大表”条件的部分筛选出来。大表规则是记录数大于指定参数或者物理大小大于指定参数的。 取得的结果,将保存期sql_id、plan_hash_value、object_name信息返回。这三个信息将分别用于后续提取SQL语句信息、执行计划信息、关联对象信息使用。 取得的全部结果集,将按照先前设定的扣分原则,统计扣分。 提取到的三部分信息+扣分信息,将作为结果返回,并在前端展示。
规则实现(执行计划)
这部分是MySQL中实现层次结果存储的一个实例。
第一个图展示的是原始的执行计划。
第二个图是代码实现的摘要。
第三个图是真正保存在库中的样子。核心部分就是对item_level的生成。
规则定义(文本级)
第三类规则是文本类的规则,这是一类与数据库种类无关、描述SQL语句文本特征的规则。在实现上是采用文本正则匹配或程序方式进行处理的。它的主要目的是规范开发人员的SQL写法,避免复杂的、性能较差的、不规范的SQL写法。
规则实现(文本级)
这部分描述的是文本规则的实现方式。第一个示例bad_join,是一种简单规则,通过正则文本匹配实现。第二个示例sub_query,是通过程序判断括号嵌套来完成对子查询(或多级子查询)的判断。
规则定义(执行特征级)
最后一类规则是执行特征类的。这部分是与数据库紧密关联的,将符合一定执行特征的语句筛选出来。这些语句不一定是低效的,可能只是未来考虑优化的重点,或者说优化效益最高的一些语句。这里面主要都是一些对资源的消耗情况等。
8、系统管理
规则管理
后面通过一些界面展示,介绍下平台的功能。
第一部分系统管理模块中规则管理的部分。在这部分,可完成新增自有规则。其核心是规则实现部分,通过SQL语句、Mongo查询语句、自定义Python文件的形式定义规则实现体。自定义规则的依据是现有抓取的数据源,定义者需要熟悉现有数据结构及含义。目前尚不支持自定义抓取数据源。
对定义好的规则,可在此处完成规则修改。主要是对规则状态、阀值、扣分项等进行配置。
任务管理
在配置好规则后,可在此处完成任务发布的工作。
上面是规则任务发布的界面,在选择数据源(ip、port、schema)后,选择审核类型及审核日期。目前审核数据源的定时策略还是以天为单位,因此日期不能选择当天。
当任务发布后,可在任务结果查看界面观察执行情况。根据审核类型、数据源对象多少、语句多少等,审核的时长不定,一般是在5分钟以内。当审核作业状态为“成功”时,代表审核作业完成,可以查看或导出审核结果了。
9、结果展示
对象审核结果概览
上图是一个对象审核报告的示例。在报告的开头部分,是一个概览页面。它集中展示审核报告中各类规则及扣分情况;并通过一个饼图展示其占比情况。这便于我们集中精力先处理核心问题。
在最上面,还可以观察到有一个规则总分的显示。这是我们将规则扣分按照百分制,折算后得到的一个分数。分值越高,代表违反的情况越少,审核对象的质量越高。引入“规则总分”这一项,在设计之初是有些争议的,担心有了这个指标会比较打击开发人员的积极性,不利于平台的推广使用。这里有几点,说明一下。 引入规则总分,是为了数据化数据库设计、开发、运行质量。以往在很多优化中,很难去量化优化前后的效果。这里提供了一种手段去做前后对比。可能这个方式不是太科学的,但是毕竟提供一种可量化的手段。 各业务系统差异较大,没有必要做横向对比。A系统60分,B系统50分,不代表A的质量就比B的质量高。 单一系统可多做纵向对比,即对比改造优化前后的规则总分。可在一定程度上反映出系统质量的变化。 规则总分,跟规则配置关系很大。如关闭规则或将违反规则的阀值调低,都会提高分数。这要根据系统自身情况来确定。同一规则,对不同系统使用,其阀值是可以不同的。举例而言,数据仓库类的应用,大表全部扫描就是一个比较正常的行为,可考虑关闭此规则或将单次违反阀值、总扣分上限降低。
对象审核结果明细
这部分是对象审核的明细部分,对应每个规则其详细情况,可在左侧链接中进一步查看对象信息。篇幅所限,不做展示了。
执行计划审核结果概览
这部分执行计划的概览展示,跟对象的情况类似。也是每种规则的扣分情况。
执行计划审核结果明细
这部分是执行计划的明细部分。
展开之后,可以看到违反每种规则的明细。上图就是违反全表扫描的规则的明细部分。
在上面是一些通用的解决方案说明。这里将可能触发此类规则的情况及解决方案进行了说明。相当于一个小知识库,便于开发人员优化。后面在平台二期,会做更为精准的优化引擎部分,这部分还会展开。
下面是每条违反的语句情况,我们可以看到语句文本、执行计划、关联信息(例如此规则的大表名称)等。还可以进一步点开语句,展开信息。

这部分是针对每条SQL的信息,包括语句文本、执行计划、执行特征、关联对象统计信息等。DBA可从这些信息就可以做一些初步的优化判断工作。
此外,平台也提供了导出功能。可导出为excel文件,供用户下载查看。这里就展示了。
10、我们遇到的坑
在实际开发过程中,碰到了很多问题。我们这里简单介绍两个,例如:
MySQL在解析json格式执行计划中暴露出的问题…
【会话进入sleep状态,假死】
解决方法:执行会话之前设置wait_timtout=3,这个时间根据实际情况进行调整。
【数据量过大,长时间没有结果】
会话处于query状态,但是数据量很大或因为数据库对format=json支持不是很好,长时间解析不出来,会影响其他会话。
解决方法:使用pt-kill工具杀掉会话。为了防止误杀,可打个标识“eXplAin format=json”,然后使用pt-kill识别eXplAin关键字。
11、推进流程
此平台在宜信公司运行以来,为很多系统提供了审核报告,大大加快了数据库结构、SQL优化的速度,减轻了DBA的日常工作压力。在工作实施过程中,我们也摸索了一套推行方法。该平台已开源后,如有朋友使用,可参考实施。
收集信息阶段
海量收集公司的数据库系统的运行情况,掌握第一手资料。快速了解各业务系统的质量,做好试点选择工作。
人工分析阶段
重点系统,人工介入分析。根据规则审核中暴露出的核心问题,“以点带面”,有针对性的给出分析及优化报告。
交流培训阶段
主动上门,跟开发团队沟通交流报告情况。借分析报告的机会,可对开发团队进行必要的培训工作,结合他们身边的案例,更具有说服作用。
反馈改进阶段
落实交流的成果,督促其改进。通过审核平台定期反馈改进质量。有一定基础的团队,可开发平台,供开发人员自己使用。使SQL质量问题,不再仅仅是DBA的问题,而和项目中的每个人都有关系。
内容来源: 宜信技术学院
数据库
2019-05-14 11:46:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Arangodb最短路径算法只能确定一条最短路径。
(图片来源百度echarts截图)
如图所示,我们从 郑志勇 到 邓志荣 的关系路径是:1、郑志勇-->徐贱云-->邓志荣  2、郑志勇-->徐贱云-->冯可梁-->邓志荣 3、郑志勇-->赵英杰-->徐贱云-->邓志荣  ...
当然还有多条路径。我们可以从这些路径中可以看出除了第一条长度为3,其余的都大于3,而第一条就是我们要找的最短路径。
那么如果在arangodb如何来查询最短路径呢?假设我们有个people的vertex document即顶点文档,graph(图)为relation,然后查询如下:
FOR p IN OUTBOUND SHORTEST_PATH "people/郑志勇" TO "people/邓志荣" edges return p
这样就能查询出最短路径,结果就是:郑志勇-->徐建云-->邓志荣
总结
最短路径在关系图谱查询非常有用,可以去除不必要的关系找到最短的那条关系
参考: https://www.w2us.com/thread-237.htm
数据库
2019-05-14 11:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
如下面的遍历 CREATE DEFINER = 'root'@'localhost' PROCEDURE zita_wms.DeliveryOrderUtil_ConfirmByList( OUT confirmed_no_ INT, OUT not_confirmed_no_ INT, IN company_id_ INT, IN order_list_ VARCHAR(20000), -- 12,23,4 IN user_id_ VARCHAR(40) ) BEGIN DECLARE order_id_ INT; DECLARE is_confirmed_ INT; DECLARE get_list CURSOR FOR SELECT d.id FROM delivery_order d, sys_array_tab a WHERE d.id = a.content AND d.logistics_company_id = company_id_; DECLARE CONTINUE HANDLER FOR NOT FOUND SET order_id_ = NULL; SET confirmed_no_ = 0 ; SET not_confirmed_no_ = 0 ; CALL sys_array(order_list_, ','); OPEN get_list; order_list: LOOP FETCH get_list INTO order_id_; IF order_id_ IS NULL THEN LEAVE order_list; END IF; -- CALL DeliveryOrderUtil_Confirm(is_confirmed_ , order_id_, user_id_ ); IF is_confirmed_ = 1 THEN -- 1:确认成功 0:确认失败 SET confirmed_no_ = confirmed_no_ + 1 ; ELSE SET not_confirmed_no_ = not_confirmed_no_ + 1 ; END IF; COMMIT; -- 业务需求, 确认一个订单就提交一个 END LOOP; CLOSE get_list; END
对应工具类, 创建临时表,删除数据,遍历数据插入 CREATE DEFINER = 'root'@'localhost' PROCEDURE zita_wms.sys_array(IN content_ VARCHAR(20000), IN separate_ VARCHAR(1)) BEGIN SET @i_ := 1; SET @count_ := CHAR_LENGTH(content_) - CHAR_LENGTH(REPLACE(content_, separate_,'')) + 1; SET @tmp_ := ''; -- DROP temporary table if exists sys_array_tab; CREATE TEMPORARY TABLE IF NOT EXISTS sys_array_tab( seq_no INT(11), content VARCHAR(2000) ); DELETE FROM sys_array_tab; WHILE @i_ <= @count_ DO SET @tmp_ := SUBSTRING_INDEX(SUBSTRING_INDEX(content_,separate_,@i_),separate_,-1); INSERT INTO sys_array_tab(seq_no, content) VALUES( @i_, @tmp_ ); SET @i_ := @i_ + 1; END WHILE; END
数据库
2019-05-14 11:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>

网络 已经 有了,就拿来保存下。 1,尽量利用一些sql语句来替代一些小循环,例如聚合函数,求平均函数等。 2,不再按照算法描述,以致将一条长达100多个字段的纪录分90次来更新,而是采用拼凑语句,将更新语句在循环中拼凑后,再统一更新。 3,使用确定的schema, 在使用表,函数,存储过程等等时,最好加上确定的schema,这样可以使sqlserver直接找到对应目标,避免去计划缓存中搜索,而且搜 索会导致编译锁定,最终影响性能。如select * from dbo.a比select * from a要好。 4,自定义存储过程不要以_sp开头。因为以_sp开头的存储过程默认为系统存储过程,所以首先会去master库中去找,然后再在当前数据库中找。 5,使用sp_executesql替代exec. sp_executesql可以使用参数化,从而可以重用执行计划。而exec就是纯拼sql语句。 6,中间结果存放于临时表,加索引。 7,少使用游标。sql是个集合语言,对于集合运算具有较高性能。而cursors是过程运算。比如对一个100万行的数据进行查询。游标需要读表100万次,而不使用游标则只需要少量几次读取。 8,事务越短越好。sqlserver支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢,cpu占用率极地。 9,使用try-catch处理错误异常。 10,查找语句尽量不要放在循环内。 ----------------- sql的使用规范: 1,尽量避免大事务操作,慎用holdlock字句,提高系统并发能力。 2,尽量避免反复访问同一张或几张表,尤其事数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。 3,尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。 4,注意where字句写法,必须考虑语句顺序。应该根据索引顺序,范围大小来确定条件字句的先后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。 5,不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统可能无法正确使用索引。 6,尽量使用exists代替select count(1)来判断是否存在纪录。count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。 7,尽量使用“>=”,不要使用“>”。 8,注意一些or子句和union子句之间的替换。 9,注意表之间连接的数据类型,避免不同类型数据之间的连接。 10,注意存储过程中参数和数据类型的关系。 11,注意insert、update操作的数据量,防止与其它应用冲突,如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级为表级 锁。 ------------ 索引的使用规范: 1,索引的创建要与应用结合考虑,建议大的oltp表不要超过6个索引。 2,尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引。 3,避免对大表查询时进行table scan,必要时考虑新建索引。 4,在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。 5,要注意索引的维护,周期性重建索引,重新编译存储过程。

https://www.cnblogs.com/hegx/p/6073597.html
这个链接的方法比较好,里面的三种方法都曾用过,,,,
https://blog.csdn.net/slowlifes/article/details/53817020
数据库
2019-05-17 14:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Redis作为当前最常用的开源内存数据库,性能十分高,据官方数据表示Redis读的速度是110000次/s,写的速度是81000次/s 。而且Redis支持数据持久化,众多数据结构存储,master-slave模式数据备份等多种功能。
但是长期将Redis作为缓存使用,难免会遇到内存空间存储瓶颈,当Redis内存超出物理内存限制时,内存数据就会与磁盘产生频繁交换,使Redis性能急剧下降。此时如何淘汰无用数据释放空间,存储新数据就变得尤为重要了。
对此,Redis在生产环境中,采用配置参数maxmemory 的方式来限制内存大小。当实际存储内存超出maxmemory 参数值时,开发者们可以通过这几种方法——Redis内存淘汰策略,来决定如何腾出新空间继续支持读写工作。
那么Redis内存淘汰策略是如何工作的呢?
首先,客户端会发起需要更多内存的申请;
其次,Redis检查内存使用情况,如果实际使用内存已经超出maxmemory,Redis就会根据用户配置的淘汰策略选出无用的key;
最后,确认选中数据没有问题,成功执行淘汰任务。
当前Redis3.0版本支持的淘汰策略有6种:
1. volatile-lru:从设置过期时间的数据集(server.db[i].expires)中挑选出最近最少使用的数据淘汰。没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失。
2. volatile-ttl:除了淘汰机制采用LRU,策略基本上与volatile-lru相似,从设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰,ttl值越大越优先被淘汰。
3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。当内存达到限制无法写入非过期时间的数据集时,可以通过该淘汰策略在主键空间中随机移除某个key。
4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰,该策略要淘汰的key面向的是全体key集合,而非过期的key集合。
5. allkeys-random:从数据集(server.db[i].dict)中选择任意数据淘汰。
6. no-enviction:禁止驱逐数据,也就是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失,这也是系统默认的一种淘汰策略。
上述是Redis的6种淘汰策略,关于使用这6种策略,开发者还需要根据自身系统特征,正确选择或修改驱逐。
1. 在Redis中,数据有一部分访问频率较高,其余部分访问频率较低,或者无法预测数据的使用频率时,设置allkeys-lru是比较合适的。
2. 如果所有数据访问概率大致相等时,可以选择allkeys-random。
3. 如果研发者需要通过设置不同的ttl来判断数据过期的先后顺序,此时可以选择volatile-ttl策略。
4. 如果希望一些数据能长期被保存,而一些数据可以被淘汰掉时,选择volatile-lru或volatile-random都是比较不错的。
5. 由于设置expire会消耗额外的内存,如果计划避免Redis内存在此项上的浪费,可以选用allkeys-lru 策略,这样就可以不再设置过期时间,高效利用内存了。
Redis缓存功能,是由edis.c文件中的freeMemoryIfNeeded函数实现的。如果maxmemory被设置,那么每次在执行命令钱,该函数都会被调用来判断内存是否够用、释放内存、返回错误。如果没有足够的内存程序主逻辑将会阻止设置了REDIS_COM_DENYOOM flag的命令执行,对其返回command not allowed when used memory > ‘maxmemory’的错误消息。
区分不同的淘汰策略选择不同的key,Redis淘汰策略主要分为LRU淘汰、TTL淘汰、随机淘汰三种机制。
LRU淘汰
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
在服务器配置中保存了 lru 计数器 server.lrulock,会定时(redis 定时程序 serverCorn())更新,server.lrulock 的值是根据 server.unixtime 计算出来进行排序的,然后选择最近使用时间最久的数据进行删除。另外,从 struct redisObject 中可以发现,每一个 redis 对象都会设置相应的 lru。每一次访问数据,会更新对应redisObject.lru
在Redis中,LRU算法是一个近似算法,默认情况下,Redis会随机挑选5个键,并从中选择一个最久未使用的key进行淘汰。在配置文件中,按maxmemory-samples选项进行配置,选项配置越大,消耗时间就越长,但结构也就越精准。
TTL淘汰
Redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。与 LRU 数据淘汰机制类似,TTL 数据淘汰机制中会先从过期时间的表中随机挑选几个键值对,取出其中 ttl 最大的键值对淘汰。同样,TTL淘汰策略并不是面向所有过期时间的表中最快过期的键值对,而只是随机挑选的几个键值对。
随机淘汰:
在随机淘汰的场景下获取待删除的键值对,随机找hash桶再次hash指定位置的dictEntry即可。
Redis中的淘汰机制都是几近于算法实现的,主要从性能和可靠性上做平衡,所以并不是完全可靠,所以开发者们在充分了解Redis淘汰策略之后还应在平时多主动设置或更新key的expire时间,主动删除没有价值的数据,提升Redis整体性能和空间。
数据库
2019-05-17 12:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
操作系统 centerOS 7
以下是具体安装步骤:
1、 增加虚拟内存 dd if=/dev/zero of=/swapadd bs=1024 count=2006424 mkswap /swapadd swapon /swapadd
2、 检查依赖包 rpm -q binutils compat-libcap1 compat-libstdc++-33 gcc gcc-c++ glibc glibc-devel ksh libaio libaio-devel libgcc libstdc++ libstdc++-devel libXi libXtst make sysstat unixODBC unixODBC-devel
3、 安装依赖包 yum -y install binutils compat-libcap1 compat-libstdc++-33 compat-libstdc++-33*i686 compat-libstdc++-33*.devel compat-libstdc++-33 compat-libstdc++-33*.devel gcc gcc-c++ glibc glibc*.i686 glibc-devel glibc-devel*.i686 ksh libaio libaio*.i686 libaio-devel libaio-devel*.devel libgcc libgcc*.i686 libstdc++ libstdc++*.i686 libstdc++-devel libstdc++-devel*.devel libXi libXi*.i686 libXtst libXtst*.i686 make sysstat unixODBC unixODBC*.i686 unixODBC-devel unixODBC-devel*.i686
4、 创建用户和组 groupadd oinstall groupadd dba useradd -g oinstall -G dba oracle passwd oracle
5、 修改内核参数 vi /etc/sysctl.conf 增加或者修改 fs.aio-max-nr = 1048576 fs.file-max = 6815744 kernel.shmall = 2097152 kernel.shmmax = 536870912 kernel.shmmni = 4096 kernel.sem = 250 32000 100 128 net.ipv4.ip_local_port_range = 9000 65500 net.core.rmem_default = 262144 net.core.rmem_max = 4194304 net.core.wmem_default = 262144 net.core.wmem_max = 1048576
执行sysctl -p使配置生效
6、 修改用户限制 vi /etc/security/limits.conf oracle soft nproc 2047 oracle hard nproc 16384 oracle soft nofile 1024 oracle hard nofile 65536 oracle soft stack 10240 vi /etc/pam.d/login session required pam_limits.so vi /etc/profile if [ $USER = "oracle" ]; then if [ $SHELL = "/bin/ksh" ]; then ulimit -p 16384 ulimit -n 65536 else ulimit -u 16384 -n 65536 fi fi source /etc/profile
7、 创建安装目录 mkdir -p /u01/app/ chown -R oracle:oinstall /u01/app/ chmod -R 775 /u01/app/ 创建vi /etc/oraInst.loc inventory_loc=/u01/app/oracle/oraInventory inst_group=oinstall chown oracle:oinstall /etc/oraInst.loc chmod 664 /etc/oraInst.loc
8、 设置oracle环境变量 su – oracle vi ~/.bash_profile export ORACLE_BASE=/u01/app/oracle export ORACLE_SID=dbsrv2 source /home/oracle/.bash_profile env
9、 解压 pwd /usr/local/ unzip linux.x64_11gR2_database_1of2.zip unzip linux.x64_11gR2_database_2of2.zip
10、复制响应文件模板 mkdir etc cp /usr/local/database/response/* /home/oracle/etc/ cp /home/oracle/database/response/* /home/oracle/etc/ chmod 777 /home/oracle/etc/*.rsp
11、静默安装Oracle软件 su – oracle vi /home/oracle/etc/db_install.rsp oracle.install.option=INSTALL_DB_SWONLY #安装类型 ORACLE_HOSTNAME=docker // 主机名称(hostname查询) UNIX_GROUP_NAME=oinstall // 安装组 INVENTORY_LOCATION=/u01/app/oraInventory SELECTED_LANGUAGES=en,zh_CN,zh_TW // 选择语言 ORACLE_HOME=/u01/app/oracle/product/11.2.0/db_1 //oracle_home ORACLE_BASE=/u01/app/oracle //oracle_base oracle.install.db.InstallEdition=EE     // oracle版本 oracle.install.db.isCustomInstall=false   //自定义安装,否,使用默认组件 oracle.install.db.DBA_GROUP=dba /  / dba用户组 oracle.install.db.OPER_GROUP=oinstall // oper用户组 oracle.install.db.config.starterdb.type=GENERAL_PURPOSE //数据库类型 oracle.install.db.config.starterdb.globalDBName=orcl //globalDBName oracle.install.db.config.starterdb.SID=dbsrv2 //SID oracle.install.db.config.starterdb.memoryLimit=8192 //自动管理内存的内存(M) oracle.install.db.config.starterdb.password.ALL=oracle //设定所有数据库用户使用同一个密码 SECURITY_UPDATES_VIA_MYORACLESUPPORT=false DECLINE_SECURITY_UPDATES=true   
12、安装 ./runInstaller -silent -responseFile /home/oracle/etc/db_install.rsp
安装完成执行脚本
su - root
/u01/app/oraInventory/orainstRoot.sh /u01/app/oracle/product/11.2.0/db_1/root.sh
13、环境变量 su - oracle vi ~/.bash_profile export ORACLE_BASE=/u01/app/oracle export ORACLE_SID=dbsrv2 export ROACLE_PID=ora11g #export NLS_LANG=AMERICAN_AMERICA.AL32UTF8 export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib export ORACLE_HOME=/u01/app/oracle/product/11.2.0/db_1 export PATH=$PATH:$ORACLE_HOME/bin export LANG="zh_CN.UTF-8" export NLS_LANG="SIMPLIFIED CHINESE_CHINA.AL32UTF8" export NLS_DATE_FORMAT='yyyy-mm-dd hh24:mi:ss' 使配置文件生效 source .bash_profile
14、配置监听程序 $ORACLE_HOME/bin/netca /silent /responseFile /home/oracle/etc/netca.rsp
15、安装数据库 vim /home/oracle/etc/dbca.rsp GENERAL] RESPONSEFILE_VERSION = "11.2.0" OPERATION_TYPE = "createDatabase" [CREATEDATABASE] GDBNAME = "dbsrv2" SID = "dbsrv2" TEMPLATENAME = "General_Purpose.dbc" CHARACTERSET = "AL32UTF8"
16、建库后实例检查 ps -ef | grep ora_ | grep -v grep | wc -l ps -ef | grep ora_ | grep -v grep
17、建库后监听检查 lsnrctl status
////////////////////////////////////////////////////////////////////////////////////////////////////////
安装过程中可能出现的问题:
1. 数据库启动 ORA-01507: database not mounted
解决方案:删除掉lk文件
该文件位于$ORACLE_HOME/dbs 目录中,查找lk开头的文件 直接rm命令删掉即可
2.数据库启动 ORA-00205: error in identifying control file, check alert log for more info
网上解决方案为 设置环境变量 export NLS_LANG=AMERICAN_CHINA.ZHS16GBK
如果重启数据库依然出现该问题 则查看alter log,该文件位于$ORACLE_BASE/diag/rdbms/[实例名]/[SID]/trace/alert_[SID].log,如果按照文中路径配置则为/u01/app/oracle/diag/rdbms/orcl11g/dbsrv2/trace/alert_dbsrv2.log
检查启动失败日志
我遇到的问题如下
大概意思是控制文件被占用 解决方案参考 https://yq.aliyun.com/articles/414635
以下是该博客内容
思路:
1、看一下"lk" and "sgadef.dbf"这两个文件是不是存在着,如果存在将其删掉;
2、看是不是有后台进程存在;
3、看一下oracle的共享内存段及信号集(semaphores)是不是还存在着;
实际操作:
[oracle @linux ~]$ cd $ORACLE_HOME/dbs
[oracle @linux dbs]$ ls
hc_SID.dat initdw.ora init.ora lkSID orapwSID spfileSID.ora
[oracle @linux dbs]$ rm lkSID
[oracle @linux dbs]$ ls
hc_SID.dat initdw.ora init.ora orapwSID spfileSID.ora
[oracle @linux dbs]$ ps -ef|grep ora_|grep SID
oracle 4981 1 0 09:00 ? 00:00:00 ora_pmon_SID
oracle 4983 1 0 09:00 ? 00:00:00 ora_psp0_SID
oracle 4985 1 0 09:00 ? 00:00:00 ora_mman_SID
oracle 4987 1 0 09:00 ? 00:00:00 ora_dbw0_SID
oracle 4989 1 0 09:00 ? 00:00:00 ora_lgwr_SID
oracle 4991 1 0 09:00 ? 00:00:01 ora_ckpt_SID
oracle 4993 1 0 09:00 ? 00:00:01 ora_smon_SID
oracle 4995 1 0 09:00 ? 00:00:00 ora_reco_SID
oracle 4997 1 0 09:00 ? 00:00:00 ora_cjq0_SID
oracle 4999 1 0 09:00 ? 00:00:07 ora_mmon_SID
oracle 5001 1 0 09:00 ? 00:00:00 ora_mmnl_SID
oracle 5003 1 0 09:00 ? 00:00:00 ora_d000_SID
oracle 5005 1 0 09:00 ? 00:00:00 ora_s000_SID
oracle 5023 1 0 09:00 ? 00:00:00 ora_qmnc_SID
oracle 5133 1 0 09:00 ? 00:00:00 ora_q000_SID
oracle 5139 1 0 09:00 ? 00:00:00 ora_q001_SID
oracle 31755 1 1 13:21 ? 00:00:00 ora_j000_SID
[oracle@linux dbs]$ ps -ef|grep ora_|grep SID|grep pmon
oracle 4981 1 0 09:00 ? 00:00:00 ora_pmon_SID
[oracle@linux dbs]$ kill -9 4981
[oracle@linux dbs]$ ps -ef|grep ora_|grep SID|grep pmon
[oracle@linux dbs]$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
重新startup数据库,问题解决。

数据库
2019-05-17 11:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
oracle11g下创建只读用户 2014年11月04日 15:07:24 louwzh 阅读数:6369


目的:创建一个用户(t1),可以访问其他用户(t2)下的表,但只能查询,不能增、删改。

1、用管理员用户创建一个新用户:t1,密码:t1 ,默认表空间:T
create user t1 identified by t1 default tablespace T;

2、给 t1 用户付权限
grant connect to t1 ;
grant create synonym to t1;

3、给t1用户付查询表的权限,可以查询t2下的所有表,下面是批量执行语句。

select 'grant select on '||owner||'.'||object_name||' to t1;'
from dba_objects
where owner in ('t2')
and object_type='TABLE';

4、为t1创建同义词,可以在管理员下,也可在t1用户下执行。

SELECT 'create or replace SYNONYM t1. ' || object_name|| ' FOR ' || owner || '.' || object_name|| ';' from dba_objects
where owner in ('t2')
and object_type='TABLE';

5、测试
在t1下,select表即可,可在同义词下看到很多创建成功的同义词。
数据库
2019-05-17 10:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
作者:屈鹏
本篇 TiKV 源码解析将为大家介绍 TiKV 的另一周边组件—— grpc-rs 。grpc-rs 是 PingCAP 实现的一个 gRPC 的 Rust 绑定,其 Server/Client 端的代码框架都基于 Future ,事件驱动的 EventLoop 被隐藏在了库的内部,所以非常易于使用。本文将以一个简单的 gRPC 服务作为例子,展示 grpc-rs 会生成的服务端代码框架和需要服务的实现者填写的内容,然后会深入介绍服务器在启动时如何将后台的事件循环与这个框架挂钩,并在后台线程中运行实现者的代码。
基本的代码生成及服务端 API
gRPC 使用 protobuf 定义一个服务,之后调用相关的代码生成工具就可以生成服务端、客户端的代码框架了,这个过程可以参考我们的 官方文档 。客户端可以直接调用这些生成的代码,向服务端发送请求并接收响应,而服务端则需要服务的实现者自己来定制对请求的处理逻辑,生成响应并发回给客户端。举一个例子: #[derive(Clone)] struct MyHelloService {} impl Hello for MyHelloService { // trait 中的函数签名由 grpc-rs 生成,内部实现需要用户自己填写 fn hello(&mut self, ctx: RpcContext, req: HelloRequest, sink: UnarySink) { let mut resp = HelloResponse::new(); resp.set_to(req.get_from()); ctx.spawn( sink.success(resp) .map(|_| println!("send hello response back success")) .map_err(|e| println!("send hello response back fail: {}", e)) ); } }
我们定义了一个名为 Hello 的服务,里面只有一个名为 hello 的 RPC。grpc-rs 会为服务生成一个 trait,里面的方法就是这个服务包含的所有 RPC。在这个例子中唯一的 RPC 中,我们从 HelloRequest 中拿到客户端的名字,然后再将这个名字放到 HelloResponse 中发回去,非常简单,只是展示一下函数签名中各个参数的用法。
然后,我们需要考虑的是如何把这个服务运行起来,监听一个端口,真正能够响应客户端的请求呢?下面的代码片段展示了如何运行这个服务: fn main() { // 创建一个 Environment,里面包含一个 Completion Queue let env = Arc::new(EnvBuilder::new().cq_count(4).build()); let channel_args = ChannelBuilder::new(env.clone()).build_args(); let my_service = MyHelloWorldService::new(); let mut server = ServerBuilder::new(env.clone()) // 使用 MyHelloWorldService 作为服务端的实现,注册到 gRPC server 中 .register_service(create_hello(my_service)) .bind("0.0.0.0", 44444) .channel_args(channel_args) .build() .unwrap(); server.start(); thread::park(); }
以上代码展示了 grpc-rs 的足够简洁的 API 接口,各行代码的意义如其注释所示。
Server 的创建和启动
下面我们来看一下这个 gRPC server 是如何接收客户端的请求,并路由到我们实现的服务端代码中进行后续的处理的。
第一步我们初始化一个 Environment,并设置 Completion Queue(完成队列)的个数为 4 个。完成队列是 gRPC 的一个核心概念,grpc-rs 为每一个完成队列创建一个线程,并在线程中运行一个事件循环,类似于 Linux 网络编程中不断地调用 epoll_wait 来获取事件,进行处理: // event loop fn poll_queue(cq: Arc) { let id = thread::current().id(); let cq = CompletionQueue::new(cq, id); loop { let e = cq.next(); match e.event_type { EventType::QueueShutdown => break, EventType::QueueTimeout => continue, EventType::OpComplete => {} } let tag: Box = unsafe { Box::from_raw(e.tag as _) }; tag.resolve(&cq, e.success != 0); } }
事件被封装在 Tag 中。我们暂时忽略对事件的具体处理逻辑,目前我们只需要知道,当这个 Environment 被创建好之后,这些后台线程便开始运行了。那么剩下的任务就是监听一个端口,将网络上的事件路由到这几个事件循环中。这个过程在 Server 的 start 方法中: /// Start the server. pub fn start(&mut self) { unsafe { grpc_sys::grpc_server_start(self.core.server); for cq in self.env.completion_queues() { let registry = self .handlers .iter() .map(|(k, v)| (k.to_owned(), v.box_clone())) .collect(); let rc = RequestCallContext { server: self.core.clone(), registry: Arc::new(UnsafeCell::new(registry)), }; for _ in 0..self.core.slots_per_cq { request_call(rc.clone(), cq); } } } }
首先调用 grpc_server_start 来启动这个 Server,然后对每一个完成队列,复制一份 handler 字典。这个字典的 key 是一个字符串,而 value 是一个函数指针,指向对这个类型的请求的处理函数——其实就是前面所述的服务的具体实现逻辑。key 的构造方式其实就是 // ,实际上就是 HTTP/2 中头部字段中的 path 的值。我们知道 gRPC 是基于 HTTP/2 的,关于 gRPC 的请求、响应是如何装进 HTTP/2 的帧中的,更多的细节可以参考 官方文档 ,这里就不赘述了。
接着我们创建一个 RequestCallContext ,然后对每个完成队列调用几次 request_call 。这个函数会往完成队列中注册若干个 Call,相当于用 epoll_ctl 往一个 epoll fd 中注册一些事件的关注。Call 是 gRPC 在进行远程过程调用时的基本单元,每一个 RPC 在建立的时候都会从完成队列里取出一个 Call 对象,后者会在这个 RPC 结束时被回收。因此,在 start 函数中每一个完成队列上注册的 Call 个数决定了这个完成队列上可以并发地处理多少个 RPC,在 grpc-rs 中默认的值是 1024 个。
小结
以上代码基本都在 grpc-rs 仓库中的 src/server.rs 文件中。在 start 函数返回之后,服务端的初始化及启动过程便结束了。现在,可以快速地用几句话回顾一下:首先创建一个 Environment,内部会为每一个完成队列启动一个线程;接着创建 Server 对象,绑定端口,并将一个或多个服务注册到这个 Server 上;最后调用 Server 的 start 方法,将服务的具体实现关联到若干个 Call 上,并塞进所有的完成队列中。在这之后,网络上新来的 RPC 请求便可以在后台的事件循环中被取出,并根据具体实现的字典分别执行了。最后,不要忘记 start 是一个非阻塞的方法,调用它的主线程在之后可以继续执行别的逻辑或者挂起。
本篇源码解析就到这里,下篇关于 grpc-rs 的文章我们会进一步介绍一个 Call 或者 RPC 的生命周期,以及每一阶段在 Server 端的完成队列中对应哪一种事件、会被如何处理,这一部分是 grpc-rs 的核心代码,敬请期待!
原文链接: https://www.pingcap.com/blog-cn/tikv-source-code-reading-7/
数据库
2019-05-16 18:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
过去几年来,“微服务架构”这个术语持续火热,它描述了一种将软件应用程序设计为可独立部署的服务套件的特定方式。尽管这种架构风格没有确切的定义,但围绕业务能力,自动化部署,网点智能以及语言和数据的分散控制等方面存在着某些共同特征。
简而言之,微服务架构是一种将单应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资源的API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。这些微服务的将集中化管理部分降到最少,同时,微服务还可以用不同的编程语言编写,并使用不同的数据存储技术。
而涉及到数据存储技术,就不得不谈到数据库,而实际上,微服务和数据库有着微妙的关系,微服务对于数据库也有着和传统架构不尽相同的需求,那么,微服务和数据库究竟有着什么样的关系?数据库又对微服务有何影响?如何选择适合微服务的数据库?巨杉数据库联合创始人兼CTO王涛向CSDN的记者分享了他的观点。

微服务架构催生分布式数据库
王涛认为,谈论数据库一定脱离不了应用。从应用程序开发来看,现在很多企业内部的应用开发都在从传统中间件加数据库的“烟囱式”开发,向微服务架构转型。而在微服务体系架构中,几乎每个微服务都需要提供数据持久化的能力,而用户也希望每个微服务所承载的数据量能够无限的弹性扩张。但是,在采用微服务架构的过程中,每个微服务使用自身独立的数据库存储又会使过去集中在一个地方的数据分散到很多不同的设备中,造成整个IT架构的数据严重碎片化。举例来说,一些互联网公司仅仅在生产系统中就维护着两、三万个MySQL数据库,这样的话,想要进行企业内部的数据整合是极为困难的。
实际上,此前,当企业用户采用微服务体系架构的时候,从数据管理的角度,业界有两种做法。
第一种做法,就是对应用程序进行微服务改造,底层数据库使用传统集中式数据库进行存储。这种做法对于应用程序的改造相对较小,对于DBA运维人员来说学习成本也较低,但是相应的,其存在数据紧耦合,无法弹性扩张,以及可能存在单点故障等问题。
第二种做法,可能也是现在业界使用比较多的方式,就是每一组微服务对应一个独立的小数据库,往往使用MySQL或PostgreSQL。这种机制能够解决集中式存储的问题,但是也带来了新的挑战,包括数据极度碎片化,在微服务之间无法共享,运维成本极其高昂。
因此,两种办法都不能很好的解决微服务下数据存储管理的问题,因此分布式数据库就是要解决上述的两个问题。第一就是针对每个微服务做到数据弹性扩张,第二就是对整个企业IT做到数据的统一治理从而避免碎片化存储。

打造适合微服务的分布式数据库
要打造适合微服务架构的数据库,巨杉数据库采用了计算存储分离的架构。其中存储层采用自研的原生分布式数据库引擎,上层计算层则可以创建成百上千个数据库实例,同时每个数据库实例对应用完全透明,不需感知。
因此,在这种系统架构下,从单个应用来看,和传统标准数据库完全一致,不需关注数据被切分在哪些不同物理设备上,做到弹性伸缩。同时,所有的物理设备从逻辑上进行统一管理,甚至不同实例里面的数据可以在可配置的权限下进行共享。
那么,适合微服务的分布式数据库都应该具有哪些特性呢?王涛认为这主要应该从两大维度、五个方面来看。
两大维度一是对传统技术的兼容,二是技术和架构的创新。
在对传统技术的兼容方面来看,首先,必须支持ACID。因为从数据库来看,尽管很多人说CAP不可兼得因此要牺牲一致性,但巨杉认为这是不可取的。对于大部分公司来说,数据都是核心生命线,绝对不能为了上分布式牺牲数据的一致性和安全性,需要对用户的财产和信息负责。因此,新型面向联机交易的分布式数据库必须对传统ACID有完美的支持,与传统Oracle DB2的数据安全性一致性保持兼容。
其次,SQL的完整性。这个主要是从对传统应用的兼容与开发人员能力重用的角度看。一般来说,SQL语法兼容的完整性,以及对已有标准的兼容必须具备,例如对MySQL、Oracle、DB2、PostgreSQL这种主流协议的兼容。
而从新技术的前瞻性来看,首先,未来是私有云和微服务应用的时代,那么作为分布式数据库,就不仅仅简单的将其定位成过去某一个数据库的替代。分布式数据库的核心价值在于,能够从数据库的层面以服务资源池的形式,向上层被从烟囱式架构向微服务架构拆散的成百上千个小服务提供数据库访问能力的平台。在这个定位下,数据库资源池在保证与传统数据库100%兼容的基础上,必须满足分布式弹性扩张,当资源池里面空间和计算能力不足时,需要通过动态增加计算存储节点的方式进行扩容。
其次,过去的数据库由于仅针对某一个特定应用,采用中间件和数据库一对一绑定的方式,因此只需要提供自身一种模式的访问就够了。但是当进行数据库资源池化的时候,上层应用自然面对来自不同开发商、不同业务类型、不同SLA级别的服务,大家采用的开发流程、SQL标准、以及安全策略各不相同,因此分布式数据库必须能够支持多种模式的访问接口。
最后,HTAP,即交易分析混合处理能力。譬如一些账务数据,可能最核心的关键应用来自于联机交易业务实时使用这些数据,但是同时一些后台的实时报表,或者安全审计机构需要进行统计分析的时候,来自不同微服务的业务可能需要对同一份数据同时以交易和分析的方式进行访问。这种情况下,能不能在资源池内对交易与分析业务进行物理资源隔离,及时对同一份数据同时访问并可以做到互不干扰尤为关键,因此,适合微服务的数据库必须具有较强的交易分析混合处理能力。

巨杉数据库,适合微服务的分布式数据库
正如同巨杉对于分布式数据库的技术定位和目标,巨杉数据库SequoiaDB本身就是以分布式存储底座与上层的数据库实例两层来进行构建的。底层的分布式存储作为资源池,自身负责数据的存储、分布式事务控制、记录和表锁等,都在底层原生分布式存储实现。
数据库实例层则提供对上层应用程序的SQL服务,用户可以创建MySQL、PostgreSQL、Spark SQL等结构化实例,也可以创建JSON或S3文件系统的非结构化实例。每个实例中的数据对上层应用来说完全透明。因此,在SequoiaDB中,一个MySQL表可以轻易存储十亿甚至百亿级别的数据,开发者在写SQL的时候完全不需要关注底层表到底被分散在多少台物理设备中。
作为业界原生分布式数据库以及新一代分布式数据库的代表,SequoiaDB对于分布式交易与ACID与传统技术完全兼容,架构与功能特性与传统数据库完全兼容。同时,SequoiaDB还积极拥抱新一代微服务与云计算框架,在面向微服务应用开发与云计算基础架构时,支持弹性扩张、资源隔离、多租户、可配置一致性、多模式(支持各类SQL协议)、集群内可配置容灾策略等一系列功能。
事实上,传统单点数据库的容量瓶颈,仅仅是分布式数据库所解决的问题之一。更重要的是在未来微服务化应用开发以及云化平台的趋势下,应用不再以“烟囱式”的中间件加数据库模式进行构建,而是采用数千甚至上万的微服务程序构建成的复杂网状模型。因此,分布式数据库需要能够满足上层应用的弹性扩展、高并发、高吞吐量、与灵活敏捷的需求。而SequoiaDB在这些方面都有着出色的表现,包括:
完整的ACID支持,事务和一致性保证;SQL的完整支持,传统数据库MySQL/PostgreSQL的语法完全兼容。分布式与扩展性,应对数据量的变化,实现存储层和计算层的弹性扩展;多模式访问接口,支持多类型数据管理和多种模式的访问接口; HTAP交易/分析混合处理能力,复杂业务需求下,实现数据的物理隔离,互不干扰。

而在此次大会最新发布的 3.2版本中,巨杉通对SequoiaDB进行大幅度性能优化与提升,使得其在分布式的交易型业务下,整体性能提升2~3倍,CPU消耗节省超过30%,从而大大提升了对微服务的支持力度。

SequoiaDB ,不仅仅是支持微服务而已
实际上,SequoiaDB 并不仅仅是微服务的“良师益友”,其更大维度下的定位是一款真正的金融级分布式关系型数据库。

巨杉数据库目前在企业级应用场景主要包括分布式在线交易、数据中台以及分布式内容管理。
在线交易是数据库最广泛应用的场景之一,通常用来支撑核心业务运营。分布式在线交易数据库核心业务价值包括,分布式架构转型,高并发、高处理能力,业务持续扩展能力以及自主可控与数据安全要求。SequoiaDB存储引擎采用原生分布式架构,扩展便捷;完整支持分布式事务、强一致多副本高可用;无单点故障,数据库引擎原生支持多中心容灾。
数据中台是当前十分火热的概念,数据中台在企业微服务架构中的角色十分重要,像齿轮一样连通上层快速迭代的微服务应用和下层基础架构,同时还可以提供全量数据的实时在线服务,泛指传统核心交易以外的所有对外服务业务,基于SequoiaDB构建的数据中台核心业务价值包括:数据高性能实时访问,海量数据全生命周期在线,业务持续扩展能力。
内容管理平台为企业提供存储、管理和使用海量非结构化数据能力。常见应用包括影像平台、文档管理平台、音视频双录系统等。基于SequoiaDB搭建的内容管理平台的核心业务价值包括,海量非结构化数据管理和实时访问,丰富的内容管理功能,海量非结构化数据全生命周期在线以及业务持续扩展能力。
据悉,目前巨杉数据库已在近百家大型商业银行核心生产业务上线,并广泛应用于金融、电信、政府、互联网、交通等领域,企业用户总数超过1000家。同时,巨杉也是中国首家连续两年入选Gartner 数据库报告的数据库厂商。
数据库
2019-05-16 17:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
部署dataguard时需要利用主库的备份创建容灾库,对于小库可以采用冷备方式,而对于大库,因做冷备导致过长的停机时间是很多企业无法容忍的。下面介绍在主库不关闭的情况下,做利用热备创建容灾库的方法。(不描述dg实施的细节,只针对主库热备及在容灾主机上做库恢复的方式)
1、主库热备
主库已开启归档功能,在数据库open状态下做全备:
Rman> run {
Sql ‘alter system archive log current’;
backup full database format '/data/backup/full_db_%U.bpk';
}
注:这里不备份在线归档日志,后面做容灾库恢复时,直接从主库copy归档文件。
2、容灾库创建
启动容灾库到mount状态:
sql> startup mount;
利用之前主库热备文件还原数据文件:
rman> restore database;
由于目前容灾库状态落后于主库,需要利用主库归档日志文件做recover,以实现状态一致。
需要确定要从主库复制哪些用于恢复的归档文件。
检查还原后datafile的checkpoint_change#
sql> select checkpoint_change# from v$datafile_header;
513326
那么主库上checkpoint在513326之后的归档日志文件都需要复制到容灾库,用于恢复。
登录主库,确定需要复制的归档文件:
sql> Select sequence,first_change#,next_change#,name from v$archived_log where first_change#>=513326;
将文件复制容灾库:
$ scp arch*log 容灾主机:/archive/
在容灾库上做日志恢复:
Sql> recover automatic standby database;
输入auto,恢复到最新的归档文件。
Sql> recover automatic standby database;
输入cancel,提示恢复成功。
sql> alter database open; #可以正常打开
sql> recover managed standby database cancel;
sql> recover managed standby database disconnect from session; #进入日志恢复状态
至此,在生产数据库不中断的情况下,完成了容灾数据库的创建。
还可以使用以下方式实现
rman >duplicate target database for standby
另外,11g可以不用对主库进行备份,就可以创建dataguard.
11g 不用对主库进行备份,就可以创建dg,具体的命令为:
run
{
allocate channel c1 type disk;
allocate auxiliary channel c2 type disk;
duplicate target database for standby from active database nofilenamecheck;
}
数据库
2019-05-16 17:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
mysql 安装 [root@hadoop ~]# tar -xvf mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar [root@hadoop ~]# ll 总用量 1036900 -rw-------. 1 root root 1435 1月 6 05:10 anaconda-ks.cfg -rw-r--r--. 1 root root 530882560 5月 13 21:54 mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar -rw-r--r--. 1 7155 31415 25381952 4月 15 17:20 mysql-community-client-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 280904 4月 15 17:21 mysql-community-common-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 3838100 4月 15 17:21 mysql-community-devel-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 47076368 4月 15 17:21 mysql-community-embedded-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 24086952 4月 15 17:21 mysql-community-embedded-compat-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 130023844 4月 15 17:21 mysql-community-embedded-devel-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 2274268 4月 15 17:21 mysql-community-libs-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 2118444 4月 15 17:21 mysql-community-libs-compat-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 173541272 4月 15 17:21 mysql-community-server-5.7.26-1.el7.x86_64.rpm -rw-r--r--. 1 7155 31415 122249684 4月 15 17:21 mysql-community-test-5.7.26-1.el7.x86_64.rpm [root@hadoop ~]# rpm -ivh mysql-community-client-5.7.26-1.el7.x86_64.rpm

解决冲突 [root@hadoop ~]# rpm -ivh mysql-community-common-5.7.26-1.el7.x86_64.rpm 警告:mysql-community-common-5.7.26-1.el7.x86_64.rpm: 头V3 DSA/SHA1 Signature, 密钥 ID 5072e1f5: NOKEY 准备中... ################################# [100%] file /usr/share/mysql/czech/errmsg.sys from install of mysql-community-common-5.7.26-1.el7.x86_64 conflicts with file from package mariadb-libs-1:5.5.41-2.el7_0.x86_64 file /usr/share/mysql/danish/errmsg.sys from install of mysql-community-common-5.7.26-1.el7.x86_64 conflicts with file from package mariadb-libs-1:5.5.41-2.el7_0.x86_64 file /usr/s
[root @hadoop ~]# yum -y remove mariadb-libs-1:5.5.41-2.el7_0.x86_64 [root@hadoop ~]# rpm -ivh mysql-community-common-5.7.26-1.el7.x86_64.rpm [root@hadoop ~]# rpm -ivh mysql-community-libs-5.7.26-1.el7.x86_64.rpm [root@hadoop ~]# rpm -ivh mysql-community-client-5.7.26-1.el7.x86_64.rpm [root@hadoop ~]# rpm -ivh mysql-community-server-5.7.26-1.el7.x86_64.rpm

注:ivh中, i-install 安装; v-verbose 进度条; h-hash 哈希校验
1、启动Mysql
安装完后,使用命令 service mysqld start 启动MySQL服务。 [root@hadoop ~]# service mysqld start
2、修改MySql的密码
由于MySQL5.7.4之前的版本中默认是没有密码的,登录后直接回车就可以进入数据库,进而进行设置密码等操作。其后版本对密码等安全相关操作进行了一些改变,在安装过程中,会在安装日志中生成一个随机密码。
怎么找到这个随机密码呢?
使用: [root@hadoop ~]# grep 'temporary password' /var/log/mysqld.log 2019-05-13T14:23:49.726858Z 1 [Note] A temporary password is generated for root@localhost: oyoIkilJ0q!, [root@hadoop mysql]# mysql -uroot -p'oyoIkilJ0q!,' mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'your passwd'; Query OK, 0 rows affected (0.01 sec) mysql> use mysql Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select user,host from user; +---------------+-----------+ | user | host | +---------------+-----------+ | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +---------------+-----------+ 3 rows in set (0.01 sec) mysql> update user set host='%' where user = 'root'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select user,host from user; +---------------+-----------+ | user | host | +---------------+-----------+ | root | % | | mysql.session | localhost | | mysql.sys | localhost | +---------------+-----------+ 3 rows in set (0.00 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) mysql>
2. 清理⽆⽤的库和⽤户 mysql> drop database test; mysql> drop user 'root'@'51'; mysql> drop user 'root'@'127.0.0.1'; mysql> drop user 'root'@'::1'; mysql> drop user ''@'localhost'; mysql> drop user ''@'51'; 3. 给root@localhost ⽤户设置密码 mysql> set password for 'root'@'localhost' = password(‘123456');

参考
https://www.cnblogs.com/xiaopotian/p/8196464.html
数据库
2019-05-13 20:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
--导出统计信息
BEGIN
dbms_stats.create_stat_table('scott','stat_tab');
dbms_stats.export_table_stats(ownname => 'scott',tabname => 'test_part',stattab=>'stat_tab');
END;
UPDATE stat_tab SET c2='P2';
commit;
--导入统计信息
BEGIN
dbms_stats.import_table_stats(ownname => 'scott',tabname => 'test_part',stattab=>'stat_tab');
END;
数据库
2019-05-13 17:47:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问:redis的pipeline有什么好处?
前面做测试数据的时候用到 cat /tmp/redisTest.txt | /redis-5.0/src/redis-cli -h 127.0.0.1 -p 6379 --pipe
就是一个pipeline管道批量执行指令,可以节省多次IO往返的时间,但是如果指令间有依赖建议分批发送
问:redis的同步机制?
主从同步原理,一般集群都是一个主多个从,主负责写,从负责读。开始主节点会启动命令开始全量同步,然后会启动增量同步。
全同步过程: Slave发送sync命令到Master Master启动一个后台进程,将Redis中的数据快照保存到文件中(bgsave) Master将保存的数据快照期间接收到的写命令也缓存起来(增量数据缓存) Master完成写操作之后,将该文件发送给Slave 使用新的AOF文件替换掉旧的AOF文件,然后写入内存中,恢复数据快照 Master将这期间收集的增量写命令也发送给Slave,Slave完成同步
增量同步过程: Master接收到用户的操作指令,判断是否需要传播到Slave 将操作记录追加到AOF文件 将该操作传播到其他Slave ,对齐住从库,往响应的缓存写入指令 将缓存中的数据发送给Slave
主从的弊端就是主服务器挂掉之后就不能进行写操作了,所以就有了sentinel 哨兵
解决主从同步Master宕机后的主从切换问题: 监控:检查主从服务器是否运行正常 提醒:通过API向管理员或者其他应用程序发送故障通知 自动故障迁移:主从切换
流言协议Gossip,在杂乱无章中寻求一致(区块链中有用到): 每个节点都随机地与对方通信,最终所有节点的状态达成一致 种子节点定期随机向其他节点发送节点列表以及传播的消息 不保证信息一定会传播给所有节点,但是最终会趋于一致

Redis的集群原理
问:如何从海量数据里快速找到所需? 分片:按照某种规则去划分数据,分散存储在多个节点上 常规的按照哈希(按哈希值取模)划分无法实现节点的动态增减(当动态增加或减少节点的时候会导致大量的key无法命中)
一致性哈希算法(哈希环):对2^32次方取模,将哈希值的空间组织成虚拟圆环

当NodeC宕机了(在一致性哈希算法中,如果一台服务器不可以,其影响的只是此服务器到哈希环的前一台服务器(逆时针方向上的第一台)之间的数据,其他数据不受影响,新增的数据也会到下一个节点,顺时针的下一台)
新增服务器NodeX (新增一台服务器,受影响的只是新服务器到其环的前一台服务器,逆时针第一台之间的数据)
Hash环数据倾斜问题(大部分数据缓存到一个服务器上,分配不均匀)
一致性哈希又引入了虚拟节点解决数据倾斜的问题(一个节点计算多个hash值,结算结果位置放置一个虚拟节点,具体做法可以在节点后面增加编号来实现)
在实际应用中把虚拟节点设置成32或更大,即使是很少的服务节点也能很好的数据分布。
数据库
2019-05-13 15:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问:Redis如何做持久化
AOF (Append-Only-File) 持久化:保持写状态 记录下除了查询以外的所有变更数据库状态的指令 以append的形式追加保存到aof文件中
默认redis.conf 是关闭的

always 总是写
everysec 每秒写一次(最常用)
no 将写入操作交给操作系统决定,操作系统一般等到缓冲区满了才写入磁盘
ps:保存配置 重启生效
问:日志重写解决AOF文件大小不断增大的问题?
原理如下: 调用fork(),创建一个子进程 子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件 主进程持续将新的变动同时写到内存和原来的AOF里 主进程获取子进程重写AOF的完成信号,往新AOF同步增量变动 使用新的AOF文件替换掉旧的AOF文件
重写也可以手动触发(和bgsave一样)使用bgrewrite 指令

RDB和AOF文件共存情况下的恢复流程:

RDB和AOF的优缺点: RDB优点:全量数据快照(二进制文件),文件小,恢复快 RDB缺点:无法保存最近一次快照之后的增量数据 AOF优点:可读性高,适合保存增量数据,数据不易丢失(刷盘是由appendfsync控制) AOF缺点:文件体积大,恢复时间长(人类可读的文本体积大,重启时指令回放,使用rewirte防止磁盘空间被撑满)
RDB-AOF混合持久化模式(redis 4.0之后支持,默认配置) bgsave做镜像全量持久化,AOF做增量持久化(bgsave的时候耗时较长,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,优先使用aof来恢复内存的状态,如果没有aof日志,就会使用rdb文件来恢复)




数据库
2019-05-13 14:04:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
图:Nacos Meetup @杭州
与你同行,抬头便是星空。
本文整理自Nacos Committer 张龙的现场分享,阿里巴巴中间件受权发布。
随着 Nacos 1.0.0 稳定版的发布,越来越多的企业开始在测试/预演/生产环境中逐步部署 Nacos。目前,除了部分企业已处于转型分布式架构的过程中,会考虑直接使用 Nacos 上生产,但仍有不少企业会考虑一些比较现实的问题: 存量用户如何迁移注册中心到 Nacos? 多区域注册中心之间如何同步? 已有注册中心与 Nacos 如何并存使用?
这里,我将通过对 Nacos Sync 的介绍,来回答这三个问题。
Nacos Sync 是什么?
Nacos Sync 是一个支持多种注册中心的同步组件,基于 SpringBoot 开发框架,数据层采用 Spring Data JPA,遵循了标准的 JPA 访问规范,支持多种数据源存储,默认使用 Hibernate 实现,更加方便的支持表的自动创建更新。
下图是 Nacos Sync 系统的概念图,Nacos Sync 通过从各个注册中心拉取注册的服务实例数据同步到 Nacos,左右两边是不同的注册中心,绿色代表目前是可以进行双向同步的,蓝色代表暂时只能进行单向同步。
组件特性
Nacos Sync 使用了高效的事件异步驱动模型,支持多种自定义事件,使得同步任务处理的延时控制在3s,8C16G的单机能够支持6K的同步任务。
除了单机部署,Nacos Sync 也提供了高可用的集群部署模式,作为无状态设计,支持将任务等状态数据迁移到了数据库,使得集群扩展非常方便。
系统模块架构
下图是 Nacos Sync 目前的系统架构图,画的比较简单,只是把一些比较重要的模块做了描述。
Web Console: 提供给用户进行注册中心和同步任务进行相关界面操作
Processor Frame: 注册中心和任务的业务处理逻辑
Timer Manager: 定时轮询数据库获取同步任务进行处理
Event Frame: 异步事件来进行同步任务的同步以及删除
Extension: 对接各种注册中心客户端的扩展实现
整体调用流程
我们来看一下 Nacos Sync 一次完整的调用流程:
通过 Web 控制台添加相关注册中心,一般都必须配置两个注册中心,一个源注册中心,另外一个是目标注册中心,注册中心相关数据会写入到数据库; 添加完注册中心以后,增加一个同步任务,添加需要同步的服务(对于Dubbo来说就是RPC接口,对于Spring Cloud就是应用名); Nacos Sync 会每隔 3s 从数据库捞取同步任务,并通过异步事件的方式进行发布; 同步服务管理监听到定时任务发布的的事件,目前有同步/删除这两种事件; 同步服务管理根据不同的策略选择相关的同步服务进行真正同步逻辑处理;
目前已经支持同步类型
下面的表格介绍了目前支持的几种同步类型,并且说明了在 Dubbo 和 Spring Cloud 下是否支持同步,表格中列举的几种注册中心,无论单向还是双向同步都是在和 Nacos 进行交互。
使用场景和 Demo 多个网络互通的 Region 之间进行服务共享,打破 Region 之间的服务调用限制;
双向同步功能,支持 Dubbo+ZooKeeper 服务平滑迁移到 Dubbo+Nacos ;
这里提供两个 Demo ,用来分别演示上面的两个经典使用场景,详细的操作步骤可以通过下方链接,进行访问。
单向同步场景,在 Spring Cloud 生态中,Eureka 同步到 Nacos 示例
https://github.com/paderlol/nacos-sync-example/tree/master/one-way-sync
双向同步场景,在 Dubbo 生态中 ZooKeeper 与 Nacos 互相同步示例
https://github.com/paderlol/nacos-sync-example/tree/master/two-way-sync
版本演进
Nacos Sync 目前已经发布3个小版本,通过下图,我们可以看到每个版本已经做了的一些工作,以及下个版本即将做的一些事情。
0.4.0 规划介绍 功能健壮性
同步数据一致性校验,0.4.0版本前,同步任务执行之后,双端数据一致性未进行检查的;
注册中心健康检查; 用户体验
同步数据实时状态透出 ,0.4.0版本前,控制台的任务状态是数据库的任务执行状态,用户没办法知道当前的数据状态;
任务批量删除;
多维度增加任务同步,优化只支持服务粒度,用户只能一个一个添加的用户体验; 功能完善
支持 Eureka/Consul 和 Nacos 的双向同步;
本文作者:张龙,Github ID: @paderlol ,Nacos Committer,就职于长沙蜜獾信息科技有限公司。
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
数据库
2019-05-13 12:53:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
作者:段兵
2019 年 5 月 10 日,TiDB 3.0.0-rc.1 版本正式推出,该版本对系统稳定性,性能,安全性,易用性等做了较多的改进,接下来逐一介绍。
提升系统稳定性
众所周知,数据库的查询计划的稳定性至关重要,此版本采用多种优化手段促进查询计划的稳定性得到进一步提升,如下: 新增 Fast Analyze 功能,使 TiDB 收集统计信息的速度有了数量级的提升,对集群资源的消耗和生产业务的影响比普通 Analyze 方式更小。 新增 Incremental Analyze 功能,对于值单调增的索引能够更加方便和快速地更新其统计信息。 在 CM-Sketch 中新增 TopN 的统计信息,缓解因为 CM-Sketch 哈希冲突导致估算偏大的问题,使代价估算更加准确。 优化 Cost Model,利用和 RowID 列之间的相关性更加精准的估算谓词的选择率,使得索引选择更加稳定和准确。
提升系统性能 TableScan,IndexScan,Limit 算子,进一步提升 SQL 执行性能。 TiKV 采用 Iterator Key Bound Option 存储结构减少内存分配及拷贝,RocksDB 的 Column Families 共享 block cache 提升 cache命中率等手段大幅提升性能。 TiDB Lightning encode SQL 性能提升 50%,将数据源内容解析成 TiDB 的 types.Datum,减少 encode 过程中多余的解析工作,使得性能得到较大的提升。
增强系统安全性
RBAC(Role-Based Access Control)基于角色的权限访问控制是商业系统中最常见的权限管理技术之一,通过 RBAC 思想可以构建最简单”用户-角色-权限“的访问权限控制模型。RBAC 中用户与角色关联,权限与角色关联,角色与权限之间一般是多对多的关系统,用户通过成为什么样的角色获取该角色所拥有的权限,达到简化权限管理的目的,通过此版本的迭代 RBAC 功能开发完成,欢迎试用。
提升产品易用性 新增 SQL 方式查询慢查询,丰富 TiDB 慢查询日志内容,如:Coprocessor 任务数,平均/最长/90% 执行/等待时间,执行/等待时间最长的 TiKV 地址,简化慢查询定位工作,提升产品易用性。 新增系统配置项合法性检查,优化系统监控项等,提升产品易用性。 支持对 TableReader 、 IndexReader 和 IndexLookupReader 算子进行内存追踪控制,对 Query 内存使用统计更加精确,可以更好地检测、处理对内存消耗较大的语句。
社区贡献
V3.0.0-rc.1 版本的开发过程中,开源社区贡献者给予了我们极大的支持,例如美团的同学负责开发的 SQL Plan Management 特性对于提升产品的易用性有很大的帮助,一点资讯的陈付同学与其他同学一起对 TiKV 线程池进行了重构,提高了性能并降低了延迟,掌门科技的聂殿辉 同学实现 TiKV 大量 UDF 函数帮忙 TiKV 完善 Coprocessor 功能,就不再一一列举。在此对各位贡献者表示由衷的感谢。接下来我们会开展更多的专项开发活动以及一系列面向社区的培训课程,希望能对大家了解如何做分布式数据库有帮助。
TiDB 3.0.0-rc.1 Release Notes
https://github.com/pingcap/docs-cn/blob/master/releases/3.0.0-rc.1.md
数据库
2019-05-13 11:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问:如何使用redis做异步队列
1.使用List作为队列,rpush生产消息,lpop消费消息
缺点:没有等待队列里有值就立刻消费
弥补:可以通过在应用层sleep机制去调用lpop重试
2.BLPOP key [key ...] timeout 阻塞直到队列有消息或者超时
这个方法缺点是只能为一个消费者提供消费!
3.pub/sub 主题发布订阅者模式(最常用)
注意:pub/sub是无状态的,不能保证消息是否可达,消息发出去就不管了,如果消费者没收到消息也不会有补偿机制。
想弥补这个缺点只有使用专业的mq(activemq,robbitmq,rocketmq,kafka,具体区别自行百度了)
数据库
2019-05-13 11:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
2019 年 5 月 10 日,TiDB 发布 3.0.0-rc.1 版,对应的 TiDB-Ansible 版本为 3.0.0-rc.1。相比 3.0.0-beta.1 版本,该版本对系统稳定性、易用性、功能、优化器、统计信息以及执行引擎做了很多改进。
TiDB SQL 优化器 利用列之间的顺序相关性提升代价估算准确度,并提供启发式参数 tidb_opt_correlation_exp_factor 用于控制在相关性无法被直接用于估算的场景下对索引扫描的偏好程度。 当过滤条件中包含相关列时,在抽取复合索引的访问条件时尽可能多地匹配索引的前缀列。 用动态规划决定连接的执行顺序,当参与连接的表数量不多于 tidb_opt_join_reorder_threshold 时启用。 在构造 Index Join 的的内表中,以复合索引作为访问条件时,尽可能多地匹配索引的前缀列。 提升对单列索引上值为 NULL 的行数估算准确度。 在逻辑优化阶段消除聚合函数时特殊处理 GROUP_CONCAT ,防止产生错误的执行结果。 当过滤条件为常量时,正确地将它下推到连接算子的子节点上。 在逻辑优化阶段列剪裁时特殊处理一些函数,例如 RAND() ,防止产生和 MySQL 不兼容的执行结果。 支持 FAST ANALYZE ,通过 tidb_enable_fast_analyze 变量控制。该特性通过用对 Region 进行采样取代扫描整个 region 的方式加速统计信息收集。 支持 SQL PLAN MANAGEMENT 。该特性通过对 SQL 进行执行计划绑定,以确保执行稳定性。该特性目前处于测试阶段,仅支持对 SELECT 语句使用绑定的执行计划,不建议在生产场景中直接使用。 执行引擎 支持对 TableReader 、 IndexReader 和 IndexLookupReader 算子进行内存追踪控制。 在慢日志中展示更多 COPROCESSOR 端执行任务相关细节。如 COPROCESSOR 任务数,平均/最长/90% 执行/等待时间,执行/等待时间最长的 TiKV 地址等。 支持 PREPARE 不含占位符的 DDL 语句。 Server TiDB 启动时,只允许 DDL owner 执行 bootstrap 新增 tidb_skip_isolation_level_check 变量控制检查隔离级别设置为 SERIALIZABLE 时不报错 在慢日志中,将隐式提交的时间与 SQL 执行时间融合在一起 RBAC 权限管理 支持 SHOW GRANT 支持 SET DEFAULT ROLE 支持 GRANT ROLE 修正了插件退出时导致 TiDB 退出的问题 修正只读语句被错误地放到事务历史中的问题 kill 语句可以更快的结束 SQL 的执行,并快速释放资源 增加启动选项 config-check 来检查配置文件的合法性 修正非严格模式下对于写入 NULL 字段的合法性检查 DDL 为 CREATE TABLE 添加了 pre_split_regions 选项,该选项可以在建表时预先分配 Table Region,避免建表后大量写入造成的写热点 优化了部分 DDL 语句的执行性能 FULLTEXT KEY 新增不支持全文索引的 warning 修正了旧版本 TiDB 中,UTF8 和 UTF8MB4 编码的兼容性问题 修正了一个表的 shard_row_id_bits 的潜在 BUG 修正了 ALTER TABLE Charset 后,Column Charset 不会跟随变化的 BUG 修正了使用 BINARY/BIT 作为 Column Default Value 时,SHOW COLUMN 可能出错的 BUG 修正了 SHOW FULL COLUMNS 语句中,CHARSET / COLLATION 显示的兼容性问题 现在 SHOW COLLATIONS 语句只会列出 TiDB 所实际支持的 COLLATIONS
PD 升级 ETCD 版本 统一 etcd 的日志格式与 pd server 一致 修复 prevote 可能无法选出 Leader 的问题 快速 drop 掉会失败的 propose 和 read 请求,减少阻塞后面的请求时间 修复 Lease 的死锁问题 修复 store 读热点的 keys 统计不正确问题 支持从单一 PD 节点强制重建 PD 集群 修复 Scatter Region 产生无效 Operator Step 的问题 修复 Region Merge Operator 超时时间过短的问题 热点调度使用高优先级 添加 PD server 端处理 TSO 请求的耗时 Metrics 添加相对应的 Store ID 和 Address 到 store 相关的 Metrics 支持 GetOperator 服务 修复 Heartbeat stream 下发送 error 找不到 store 的问题
TiKV Engine 修复读流量统计不准确问题 修复 prefix extractor panic 的问题 优化内存管理,减少 Iterator Key Bound Option 的内存分配和拷贝 修复 Merge Region 时未考虑 Learner log gap 造成的 panic 问题 支持不同的 column families 共享 block cache Server 减少 batch commands 的上下文切换开销 检查 seek iterator status 的合法性 RaftStore 可配置化 properties index distance Coprocessor 新增 batch index scan executor 新增向量化 evaluation 框架 新增 batch 执行器统计框架 构建 RPN expression 时检查 max column 以防止 evaluation 阶段 column offset 越界的问题 实现 BatchLimitExecutor ReadPool 使用 tokio-threadpool 替换原本的 futures-cpupool ,减少 context switch 新增 batch 聚合框架 新增 BatchSelectionExecutor 实现 batch aggression function AVG 实现 RPN function LogicalAnd Misc 支持选用 tcmalloc 为内存分配器
Tools TiDB-Binlog 修复 unsigned int 类型的主键列的 binlog 数据为负数,造成同步出错中断的问题 删除下游是 pb 时的压缩选项,修改下游名字 pb 成 file Pump 新增 storage.sync-log 配置项,支持 Pump 本地存储异步刷盘 Pump 和 Drainer 之间通讯支持流量压缩 Drainer 新增 syncer.sql-mode 配置项,支持使用不同 sql-mode 解析 DDL query Drainer 新增 syncer.ignore-table 配置项,支持过滤不需要同步的表 Lightning 使用 row id 或者列的默认值填充 dump 文件中缺少的 column 数据 Importer 修复部分 SST 导入失败依然返回导入成功的 bug Importer 支持 upload SST 到 TiKV 限速 Lightning 优化导入表的顺序,按照表的数据大小顺序进行导入,减少导入过程中大表执行 checksum 和 Analyze 对集群的影响,并且提高 Checksum 和 Analyze 的成功率 提升 Lightning encode SQL 性能,性能提升 50%,直接解析数据源文件内容成 TiDB 的 types.Datum,省去 KV encoder 的多余解析工作 日志格式改为 Unified Log Format 新增一些命令行选项,即使缺少配置文件也能使用 数据同步对比工具 (sync-diff-inspector) 支持 checkpoint,记录校验状态,重启后从上次进度继续校验 增加配置项 only-use-checksum,只通过计算 checksum 来检查数据是否一致
TiDB-Ansible TiKV 监控变更以及更新 Ansible、Grafana、Prometheus 版本 summary 监控适用于用户查看集群状态 trouble_shooting 监控适用于 DBA 排查问题 details 监控适用于开发分析问题 修复下载 Kafka 版本 Binlog 失败的 BUG 修改操作系统版本限制,仅支持 CentOS 7.0 及以上,Red Hat 7.0 及以上版本的操作系统 滚动升级时的版本检测改为多并发 更新 README 中文档链接 移除重复的 TiKV 监控项,新增 trouble shooting 监控项 优化 table-regions.py 脚本,按表显示 leader 分布 更新 drainer 配置文件 优化 TiDB 监控,新增以 SQL 类别显示延迟的监控项 更新 Lightning 配置文件,新增 tidb_lightning_ctl 脚本
数据库
2019-05-13 10:48:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问:如何通过Redis 实现分布式锁?
首先要明确分布式锁需要解决的问题: 互斥性(任意时刻只能有一个客户端获取锁) 安全性(锁只能由持有该锁的客户端删除,不能别其他客户端删除) 死锁(获取锁的客户端由于某些原因宕机,导致其他客户端无法获取到该锁) 容错(当部分redis节点服务器宕机,客户端还是能够获取锁和释放锁)
常用解决方法: SETNX key value:如果key不存在,则创建并赋值。时间复杂度O(1)。设置成功返回1 ,设置失败返回0
问:如何解决setnx长期有效的问题?
EXPIRE key seconds 设置key的有效时间,当key过期(时间为0),会被自动删除 。
缺点:原子性不能满足!
伪代码如下:
注意代码种status==1 之后 程序挂掉 这个锁就是死锁了

2. set 原子操作方法(redis 2.6之后支持)
SET key value [EX seconds] [PX milliseconds][NX||XX]
EX second: 设置键的过期时间为second秒
Px millisecond:设置键的过期时间为millisecond毫秒
NX:只在键不存在的时候才对键进行设置操作
XX:只在键存在时候才对键进行设置操作
SET操作成功完成时,返回ok,否则返回nil
ps:设置value的时候可以设置成requestID 或者线程ID 可以标记获取锁的资源
伪代码如下:


问:大量的key同时过期的注意事项?
集中过期,由于清除大量的key很耗时,会出现短暂的卡顿现象
解决方法:在设置key的过期时间时,给每个key加上随机值,使得过期时间分散些




数据库
2019-05-13 10:43:00