openGauss GUC参数配置

启动 gsql 工具后,有以下两种方式查询 openGauss 数据库的 GUC 参数:

通过 SQL 查询

所有的 GUC 参数都存储在视图 pg_settings 中,我们可以通过 SQL 语句查询:

1
2
select * from pg_settings; # 查看所有 GUC 参数
select * from pg_settings where name='password_encryption_type'; # 查看 password_encryption_type 参数设置

查询结果:

1
2
3
4
           name           | setting | unit |                           category                           |             short_desc             |                                        extra_desc                                        | context | vartype |       source       | min_val | max_val | enumvals | boot_val | reset_val |               sourcefile                | sourceline
--------------------------+---------+------+--------------------------------------------------------------+------------------------------------+------------------------------------------------------------------------------------------+---------+---------+--------------------+---------+---------+----------+----------+-----------+-----------------------------------------+------------
password_encryption_type | 0 | | Connections and Authentication / Security and Authentication | The encryption method of password. | This controls the encryption type of the password. A value of 2 uses the system default. | sighup | integer | configuration file | 0 | 2 | | 2 | 0 | /var/lib/opengauss/data/postgresql.conf | 870
(1 row)

password_encryption_type 参数说明

该字段决定采用何种加密方式对用户密码进行加密存储。修改此参数的配置不会自动触发已有用户密码加密方式的修改,只会影响新创建用户或修改用户密码操作。该参数属于 SIGHUP 类型参数,请参考表4-134中对应设置方法进行设置。

取值范围 0、1、2

  • 0 表示使用md5 方式对密码加密
  • 1表示采用sha256和md5两种方式分别对密码加密
  • 2表示采用sha256方式对密码加密

由查询结果可知,当前值为 0,即使用 md5 方式对密码加密;该项配置的源地址在 /var/lib/opengauss/data/postgressql.conf 的第 870 行。

通过 show 命令

此外,我们还可以通过 show 命令直接查询 GUC 参数:

1
2
show all; # 查询所有 GUC 参数
show password_encryption_type; # 查询 GUC 参数 password_encryption_type 的值

参考链接

  1. 配置GUC参数> 查看参数当前取值 - 华为云文档
  2. 数据仓库服务 GaussDB(DWS) - 华为云文档

gsql命令行客户端工具

gsql 是与 openGauss 数据库配套的命令行工具,我们可以利用它来连接数据库、执行SQL语句和执行一些元命令。

基本操作

1
2
3
4
5
6
7
8
[omm@4ae1997a3f9a ~]$ gsql
gsql ((openGauss 1.1.0 build 392c0438) compiled at 2020-12-31 20:07:42 commit 0 last mr )
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.

omm=#
omm=# \? # 获取帮助
omm=# \q # 退出

注意,除了执行一些元命令,如 \q 等外,执行 SQL 语句都要记住在末尾加上 ;,以防止和下一条语句冲突。

1
2
omm=# show server_version;
omm=# show all;

参考链接

  1. gsql命令行客户端工具/gsql概述 - 华为云文档

yum更换国内源

编辑 /etc/yum.repos.d/CentOS-Base.repo` 文件,将文件内容覆盖为以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[base]
name=CentOS-$releasever - Base
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/os/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:/etc/pki/rpm-gpg/RPM-GPG-KEY-7

[updates]
name=CentOS-$releasever - Updates
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/updates/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:/etc/pki/rpm-gpg/RPM-GPG-KEY-7

[extras]
name=CentOS-$releasever - Extras
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/extras/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:/etc/pki/rpm-gpg/RPM-GPG-KEY-7

[centosplus]
name=CentOS-$releasever - Plus
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:/etc/pki/rpm-gpg/RPM-GPG-KEY-7

openGauss鉴权配置文件pg_hba.conf

准确来说是 Postgres 的鉴权配置文件,因为 openGauss 源于 PostgresSQL 数据库,主要对内核进行和很大的改动,客户端和服务端的驱动协议修改比较少,但支持了 SHA256 等新的密码存储鉴权方式。

首先进入 docker 容器,

1
2
docker ps # 查看 openGauss 容器的 id
docker exec -it <container-id> /bin/bash; exit # 进入 docker 容器进行操作

切换到 omm 用户,启动 gsql:

1
2
su - omm # 切换到 omm 用户
gsql # 启动 gsql

在 gsql 交互式终端中输入以下命令获取 pg_hba.conf 鉴权配置文件的路径:

1
2
3
4
5
6
7
8
9
10
[omm@4ae1997a3f9a opengauss]$ gsql
gsql ((openGauss 1.1.0 build 392c0438) compiled at 2020-12-31 20:07:42 commit 0 last mr )
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.

omm=# show hba_file;
hba_file
-------------------------------------
/var/lib/opengauss/data/pg_hba.conf
(1 row)

此外,对于 enmotech/opengauss 镜像,openGauss 的安装路径在:

1
/usr/local/opengauss

数据路径在:

1
/var/lib/opengauss

pg_hba.conf

  • hba.conf 为 trust,跳过认证;
  • 配置 hba.conf 为 MD5 方式,修改 postgresq.conf 配置文件中的 GUC 参数 password_encryption_type 为 0,表示 MD5 密码存储方式。在这种方式下,与原来PG基本是一样,方便调通流程。可以看一下MD5 认证的基本流程;
  • 配置 hba.conf 为 SHA256 方式,修改 postgresq 配置文件中的 GUC 参数 pssword_encryption_type 为 2 表示 SHA256 密码存储方式(这是 openGauss 新的鉴权方式)调整 SHA256 认证方式;

对于 authmethod-options,其支持以下选项 (cert 和 gss 不要求,但如果有时间的话可以试试):

  • trust: 不验密,禁止远程主机使用trust 方式访问集群
  • reject: 拒绝访问
  • md5: md5认证,默认不支持
  • sha256: sha256认证(推荐使用)
  • cert: 客户端证书认证
  • gss: kerberos认证

password_encryption_type 参数说明

该字段决定采用何种加密方式对用户密码进行加密存储。修改此参数的配置不会自动触发已有用户密码加密方式的修改,只会影响新创建用户或修改用户密码操作。该参数属于 SIGHUP 类型参数,请参考表4-134中对应设置方法进行设置。

取值范围 0、1、2

  • 0 表示使用md5 方式对密码加密
  • 1表示采用sha256和md5两种方式分别对密码加密
  • 2表示采用sha256方式对密码加密

更多详细信息查阅参考链接中的官方文档 The pg_hba.conf - Postgres Documentation

参考链接

  1. How do I find the path to pg_hba.conf from the shell?
  2. The pg_hba.conf - Postgres Documentation

借助docker搭建openGauss测试环境

华为的 openGauss 数据库是一款基于 Postgres 数据库内核开发的一款高性能、高安全、高可靠的企业级关系型数据库。

本来是想跟着官方文档手动搭建环境将 openGauss 的数据库后端给跑起来的,但是无奈官方要求的安装环境过于严苛,我尝试了好几天终究还是失败了。最终选择 docker 方案,幸好已经有前辈做好相关的镜像了,我们只需拉取下来,直接运行就好:

1
2
docker pull enmotech/opengauss # 拉取镜像
docker run --name opengauss --privileged=true -d -e GS_PASSWORD=Enmo@123 -p 5432:5432 enmotech/opengauss:latest # 启动容器

运行容器后,openGauss 的数据库后端将监听在 5432 端口,有以下两种方式可以连接到 openGauss 数据库:

  1. 通过 gsql 命令连接到 openGauss 数据库

首先进入容器

1
2
docker ps # 查看所有正在运行的 docker 容器,确定当前 openGauss 容器的 id
docker exec -it <container-id> /bin/bash; exit # 进入该 docker 容器进行操作

进入容器后,切换到 omm 用户后即可使用 gsql 命令来对数据库执行相应操作

1
2
su - omm # 切换到 omm 用户 
gsql # 连接到 openGauss 数据库
  1. 使用 JetBrains 的 DataGrip 来连接

下载好 DataGrip 后,新建一个 postgres 的 data source,配置信息如下:

默认的用户名是 gaussdb,默认的密码是 Enmo@123,默认的数据库名称是 postgres,默认端口是 5432。

连接成功后,如果只是编写应用项目的话,只需要当作是一般的关系型数据库一样用就可以了,openGauss 支持绝大部分 SQL 特性,基本可以满足日常开发需求。

参考链接

  1. openGauss 官方文档
  2. enmotech/opengauss README - docker hub
  3. 怎样使用 DataGrip 连接数据库

关闭mac合盖休眠

1
2
sudo pmset -a disablesleep 1 # 禁用系统休眠
sudo pmset -a disablesleep 0 # 启用系统休眠

服务器Ping不通的排查方案

linux系统是否允许 ping是由好几个因素决定的,如内核参数和防火墙, 几个因素同时允许时才能ping通:

  1. 内核参数

    1
    2
    3
    4
    5
    # 如果是1则从内核层面忽略所有ICMP ECHO请求
    # 也就是Ping这台服务器收不到任何回应
    cat /proc/sys/net/ipv4/icmp_echo_ignore_all
    # 执行下面这个命令修改这项配置
    echo "0" >/proc/sys/net/ipv4/icmp_echo_ignore_all

  2. 防火墙:iptables

    看这篇教程 Linux系统的ECS中没有禁ping却ping不通的解决方法 - 阿里云

  3. 云防火墙

    云厂商一般会给ECS提供云防火墙,这时候需要检查一下安全组规则有没有拒绝ICMP请求

服务器磁盘叕满了

发现一起听歌的后台监控不能正常运行了,吓得我赶紧打开了阿里云看了一眼服务器状态,好家伙磁盘直接100%了。

清理工作启动!

  1. 查找大文件实用命令

    1
    2
    sudo du -ah --max-depth=1 | sort -h # 按文件(夹)大小排序显示列表
    df -h # 用人类可读的方式展示磁盘剩余空间

  2. 实用清理命令

  • 清理系统日志

    1
    2
    sudo journalctl --vacuum-time=2d # 仅保留最近两天的系统日志,多余的都自动删掉
    sudo journalctl --vacuum-size=50M # 仅保留50M大小的系统日志,多余的都自动删掉

  • 清理 PM2 日志

    1
    pm2 flush app_name

  • 清理 nginx 日志

    1
    rm /www/wwwlogs/*

  1. 恢复故障应用

    MongoDB 在磁盘满的情况下,拒绝执行,并且自动停止服务。可以通过以下命令重启:

    1
    2
    3
    sudo service mongod start # 启动 mongod
    sudo service mongod status # 查看 mongod 的状态
    pm2 restart app_name # 重启应用以便冲洗连接数据库

DOM生命周期事件

用框架的你,可能早已忽略了这些事件API

时间先后关系:

  1. DOMContentLoaded ——通过 document.addEventListener('DOMContentLoaded', handler) 监听。当浏览器已完全加载 HTML,并构建了 DOM 树时触发,但像 <img /> 和 css 样式表这些外部资源可能还未加载完成。 注意:当文档中遇到 <script /> 标签时,DOMContentLoaded 事件会等待脚本完全执行结束后再触发。除非带有是 async/defer 属性的脚本或者通过 document.createElement('script') 创建的脚本(这些脚本会异步加载并执行,不会阻塞 DOMContentLoaded 事件)。
  2. load ——通过 window.onload 监听。当浏览器不仅完全加载 HTML,图片样式表这些外部资源也加载完成后触发。
  3. beforeunload ——通过 window.onbeforeunload 监听。表示用户正在离开,我们可以检查用户是否保存了更改,并询问他是否真的要离开。
  4. unload ——通过 window.onunload 监听。表示用户已经离开,我们可以做一些善后工作。

防抖与节流

防抖和节流都可以用来控制函数执行的频率。

防抖 (debounce)

效果

  • 给定一个时间间隔,比如 200ms
  • 在第一次调用函数时,并不立即执行函数,而是延迟 200ms 再看情况执行
  • 如果在这 200ms 延迟内,函数又被调用了,取消之前的定时器,继续延迟 200ms 毫秒,直到一次 200ms 延迟正常结束且这段延迟内函数没有被调用,这时才真正执行这个函数。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const debounce = require('lodash/debounce');

(async () => {
const sayHello = debounce(() => {
console.log(new Date(), 'Hello World');
}, 2000);

for await (_ of Array(10).fill()) {
await new Promise(resume => setTimeout(resume, 1000));
console.log(new Date());
sayHello();
}
})();

// 运行结果:
2021-01-19T01:23:42.802Z
2021-01-19T01:23:43.814Z
2021-01-19T01:23:44.815Z
2021-01-19T01:23:45.819Z
2021-01-19T01:23:46.820Z
2021-01-19T01:23:47.823Z
2021-01-19T01:23:48.825Z
2021-01-19T01:23:49.828Z
2021-01-19T01:23:50.829Z
2021-01-19T01:23:51.829Z
2021-01-19T01:23:53.831Z Hello World

因为这里设置防抖的间隔是 2000ms,而调用 sayHello 是每隔一秒调用一次,所以每当延时一秒的时候,函数又被调用,重新设置了一个新的 2000ms 延时,直到第 10 次调用结束后,这次延时 2000 ms 的间隔内不再有新的函数调用,等延时结束后才真正执行我们所需要防抖的函数:

1
2
3
() => {
console.log(new Date(), 'Hello World');
}

举个极端点的例子,如果在程序运行时,始终保持 1000 ms 的间隔调用函数,而防抖的间隔设置为 2000 ms,那么被防抖的函数永远也不会被执行,因为永远满足不了延时 2000 ms 的时间间隔内这个函数没有被调用的条件。我们把上面那个例子改写一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const debounce = require('lodash/debounce');

const sayHello = debounce(() => {
console.log(new Date(), 'Hello World');
}, 2000);

setInterval(async () => {
await new Promise(resume => setTimeout(resume, 1000));
console.log(new Date());
sayHello();
}, 1000);

// 运行结果:
2021-01-19T01:36:35.478Z
2021-01-19T01:36:36.490Z
2021-01-19T01:36:37.492Z
2021-01-19T01:36:38.494Z
2021-01-19T01:36:39.496Z
2021-01-19T01:36:40.498Z
2021-01-19T01:36:41.498Z
2021-01-19T01:36:42.500Z
2021-01-19T01:36:43.503Z
2021-01-19T01:36:44.503Z
2021-01-19T01:36:45.506Z
2021-01-19T01:36:46.508Z
... // 永远也不会打印 Hello World

实现

1
2
3
4
5
6
7
8
9
10
const debounce = (fn, interval) => {
let timer = null;

return () => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, interval);
};
};

毫无疑问,debounce 是一个高阶函数,调用它的返回结果是一个函数。在 debounce 中我们还需要一个闭包变量来 timer 来记录定时器,返回的函数是对被防抖函数的封装,主要思想是每次调用函数时,清除之前的定时器(如果有的话),重新设置一个新的定时器,这样就实现了一个简单的防抖函数。

节流 (throttle)

效果

  • 给定一个时间间隔,比如 200ms
  • 在每个 200ms 节流间隔内,哪怕函数被重复调用多次,也只执行函数一次

例子

改写一下上面那个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const throttle = require('lodash/throttle');

(async () => {
const sayHello = throttle(() => {
console.log(new Date(), 'Hello World');
}, 5000);

for await (_ of Array(10).fill()) {
console.log(new Date());
sayHello();
await new Promise(resume => setTimeout(resume, 1000));
}
})();

// 执行结果:
2021-01-19T02:18:46.864Z
2021-01-19T02:18:46.872Z Hello World
2021-01-19T02:18:47.878Z
2021-01-19T02:18:48.879Z
2021-01-19T02:18:49.880Z
2021-01-19T02:18:50.882Z
2021-01-19T02:18:51.875Z Hello World
2021-01-19T02:18:51.884Z
2021-01-19T02:18:52.887Z
2021-01-19T02:18:53.890Z
2021-01-19T02:18:54.893Z
2021-01-19T02:18:55.896Z
2021-01-19T02:18:56.886Z Hello World

这里我们设置了节流的间隔为 5000ms ,函数调用的间隔仍然是 1000ms。注意到在 18:46:864 ~ 18:51:864 这段时间间隔内函数被调用了5次,但实际只被执行了1次,这就是节流的作用。在一个节流间隔内,无论调用函数多少次,在这个间隔内只真正执行函数一次。

实现

节流函数的实现有很多种,这里用时间戳的方式实现。

1
2
3
4
5
6
7
8
9
10
const throttle = (fn, interval) => {
let timestamp = 0;

return () => {
if (Date.now() - timestamp > interval) {
timestamp = Date.now();
return fn();
}
};
}

即每次成功执行一次函数后,接下来 5000ms 之内的调用都会被忽略,这就是节流的作用。