PgSQL - 如何仅在数据库完全为空时导入数据库转储?
PgSQL - How to import database dump only when database completely empty?
实际使用 ansible
自动执行此操作的用例。我想在数据库完全为空(内部没有表)时导入数据库转储 only。当然总是有办法执行 sql 语句,但这是最后的手段,我相信应该有更优雅的解决方案。
pg_restore
据我所知手册没有提供这个选项。
以下是我打算如何使用 ansible 来实现:
- name: db_restore | Receive latest DB backup
shell: s3cmd --skip-existing get `s3cmd ls s3://{{ aws_bucket }}/ | grep sentry | tail -1 | awk '{print }'` sql.latest.tgz
args:
chdir: /root/
creates: sql.latest.tgz
- name: db_restore | Check if file exists
stat: path=/root/sql.latest.tgz
register: sql_latest
- name: db_restore | Restore latest DB backup if backup file found
shell: PGPASSWORD={{ dbpassword }} tar -xzOf /root/sentry*.tgz db.sql | psql -U{{ dbuser }} -h{{ pgsql_server }} --set ON_ERROR_STOP=on {{ dbname }}
when: sql_latest.stat.exists
ignore_errors: True
理想情况下,这应该检查数据库是否为空。不存在用于此目的的 ansible 模块。 Google也在沉默中。。目前的方案其实也可以,导入失败时会报错,我可以忽略错误,但看到误报有点痛苦。
并没有像 "empty" 这样的东西;它通常具有内置类型、默认 PL/PgSQL 语言等,即使您从 template0
创建。如果您从不同的模板创建,可能会有更多内容。
PostgreSQL 不保留对数据库的第一次非模板写入的记录,因此您也不能说 "changed since created"。
这就是 pg_restore
没有 --if-empty
选项的原因。这真的没有意义。
到目前为止,最好的选择是执行 psql
来查询 information_schema
并确定 public
模式中是否有任何 table。或者,更好的是,查询是否存在特定的 tables 和您知道将由转储创建的类型。
例如
psql -qAt mydbname -c "select 1 from information_schema.tables where table_schema = 'public' and table_name = 'testtable';"
然后您可以测试在 stdout 上返回的 zero/nonzero 行。或者将其包装在 SELECT EXISTS(...)
中以从 psql
中获取布尔值。如果 table 存在,则使用 DO
块 ERROR
如果您需要来自 psql
的 zero/nonzero 退出状态。
要说数据库是空的,我们必须知道从创建的时候就没有添加任何东西。由于 postgres 不跟踪这一点(正如@Craig Ringer 已经提到的),我建议使用不同的方法来处理 ansible。
所以,只需使用像这样的处理程序机制:
- name: Create zabbbix postgres DB
postgresql_db: name="{{zabbix_db_name}}"
notify:
- Init zabbix database
既然不好说,如果一个数据库是"empty",正如其他人解释的那样,检查起来就容易多了,如果数据库存在,则创建和恢复一步到位。我是这样做的:
- name: Check my_database database already exists
become: yes
become_user: postgres
shell: psql -l | grep my_database
ignore_errors: true
register: my_database_db_existence
- debug: var=my_database_db_existence
- name: Copy backup of the my-database database
shell: your-s3-command here
when: my_database_db_existence | failed
- name: Restore my_database database on first run
become_user: postgres
shell: createdb -O my_user my_database && psql -d my_database -f /path/to/my_dump.sql
when: my_database_db_existence | failed
P.S。还写了一个 detailed blog post 解释实现中的每个 ansible 任务。
在我的 Ansible 持续部署中,我更喜欢不检查是否为空数据库。我 运行 具有默认属性的容器,如果它不存在则创建数据库,然后我恢复数据库(创建方案、表等):
- hosts: all
vars:
database_name: "maindb"
pg_admin_name: "postgres"
pg_admin_password: "postgres"
pghost: "localhost"
pg_user_name: "vr_user"
pg_user_password: "ChanGeMe2021"
tasks:
- name: Check if database is exist
community.postgresql.postgresql_info:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
filter:
- "databases"
register: pg_info
- name: Create database if not exist
block:
- name: Say status
ansible.builtin.debug:
msg: "Database is not exist!"
- name: Copy dadabase shchema
ansible.builtin.copy:
src: "./files/maindb.sql"
dest: "/tmp/maindb.sql"
- name: Create database
community.postgresql.postgresql_db:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ database_name }}"
encoding: UTF-8
# lc_collate: ru_RU.utf8
# lc_ctype: ru_RU.utf8
- name: Create role
community.postgresql.postgresql_user:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ pg_user_name }}"
password: "{{ pg_user_password }}"
- name: Restore database
community.postgresql.postgresql_db:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ database_name }}"
state: restore
target: "/tmp/maindb.sql"
register: pg_restore_result
failed_when: "'ERROR' in pg_restore_result.stderr"
- name: Print restore result
ansible.builtin.debug:
msg: "{{ pg_restore_result }}"
rescue:
- name: Rollback database
community.postgresql.postgresql_db:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ database_name }}"
state: absent
- name: Print when errors
ansible.builtin.debug:
msg: "Restore failed, because: {{ pg_restore_result.stderr_lines[1] }}"
when: pg_info.databases[database_name] is not defined
这个代码你可以找到here
实际使用 ansible
自动执行此操作的用例。我想在数据库完全为空(内部没有表)时导入数据库转储 only。当然总是有办法执行 sql 语句,但这是最后的手段,我相信应该有更优雅的解决方案。
pg_restore
据我所知手册没有提供这个选项。
以下是我打算如何使用 ansible 来实现:
- name: db_restore | Receive latest DB backup
shell: s3cmd --skip-existing get `s3cmd ls s3://{{ aws_bucket }}/ | grep sentry | tail -1 | awk '{print }'` sql.latest.tgz
args:
chdir: /root/
creates: sql.latest.tgz
- name: db_restore | Check if file exists
stat: path=/root/sql.latest.tgz
register: sql_latest
- name: db_restore | Restore latest DB backup if backup file found
shell: PGPASSWORD={{ dbpassword }} tar -xzOf /root/sentry*.tgz db.sql | psql -U{{ dbuser }} -h{{ pgsql_server }} --set ON_ERROR_STOP=on {{ dbname }}
when: sql_latest.stat.exists
ignore_errors: True
理想情况下,这应该检查数据库是否为空。不存在用于此目的的 ansible 模块。 Google也在沉默中。。目前的方案其实也可以,导入失败时会报错,我可以忽略错误,但看到误报有点痛苦。
并没有像 "empty" 这样的东西;它通常具有内置类型、默认 PL/PgSQL 语言等,即使您从 template0
创建。如果您从不同的模板创建,可能会有更多内容。
PostgreSQL 不保留对数据库的第一次非模板写入的记录,因此您也不能说 "changed since created"。
这就是 pg_restore
没有 --if-empty
选项的原因。这真的没有意义。
到目前为止,最好的选择是执行 psql
来查询 information_schema
并确定 public
模式中是否有任何 table。或者,更好的是,查询是否存在特定的 tables 和您知道将由转储创建的类型。
例如
psql -qAt mydbname -c "select 1 from information_schema.tables where table_schema = 'public' and table_name = 'testtable';"
然后您可以测试在 stdout 上返回的 zero/nonzero 行。或者将其包装在 SELECT EXISTS(...)
中以从 psql
中获取布尔值。如果 table 存在,则使用 DO
块 ERROR
如果您需要来自 psql
的 zero/nonzero 退出状态。
要说数据库是空的,我们必须知道从创建的时候就没有添加任何东西。由于 postgres 不跟踪这一点(正如@Craig Ringer 已经提到的),我建议使用不同的方法来处理 ansible。
所以,只需使用像这样的处理程序机制:
- name: Create zabbbix postgres DB
postgresql_db: name="{{zabbix_db_name}}"
notify:
- Init zabbix database
既然不好说,如果一个数据库是"empty",正如其他人解释的那样,检查起来就容易多了,如果数据库存在,则创建和恢复一步到位。我是这样做的:
- name: Check my_database database already exists
become: yes
become_user: postgres
shell: psql -l | grep my_database
ignore_errors: true
register: my_database_db_existence
- debug: var=my_database_db_existence
- name: Copy backup of the my-database database
shell: your-s3-command here
when: my_database_db_existence | failed
- name: Restore my_database database on first run
become_user: postgres
shell: createdb -O my_user my_database && psql -d my_database -f /path/to/my_dump.sql
when: my_database_db_existence | failed
P.S。还写了一个 detailed blog post 解释实现中的每个 ansible 任务。
在我的 Ansible 持续部署中,我更喜欢不检查是否为空数据库。我 运行 具有默认属性的容器,如果它不存在则创建数据库,然后我恢复数据库(创建方案、表等):
- hosts: all
vars:
database_name: "maindb"
pg_admin_name: "postgres"
pg_admin_password: "postgres"
pghost: "localhost"
pg_user_name: "vr_user"
pg_user_password: "ChanGeMe2021"
tasks:
- name: Check if database is exist
community.postgresql.postgresql_info:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
filter:
- "databases"
register: pg_info
- name: Create database if not exist
block:
- name: Say status
ansible.builtin.debug:
msg: "Database is not exist!"
- name: Copy dadabase shchema
ansible.builtin.copy:
src: "./files/maindb.sql"
dest: "/tmp/maindb.sql"
- name: Create database
community.postgresql.postgresql_db:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ database_name }}"
encoding: UTF-8
# lc_collate: ru_RU.utf8
# lc_ctype: ru_RU.utf8
- name: Create role
community.postgresql.postgresql_user:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ pg_user_name }}"
password: "{{ pg_user_password }}"
- name: Restore database
community.postgresql.postgresql_db:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ database_name }}"
state: restore
target: "/tmp/maindb.sql"
register: pg_restore_result
failed_when: "'ERROR' in pg_restore_result.stderr"
- name: Print restore result
ansible.builtin.debug:
msg: "{{ pg_restore_result }}"
rescue:
- name: Rollback database
community.postgresql.postgresql_db:
login_host: "{{ pghost }}"
login_user: "{{ pg_admin_name }}"
login_password: "{{ pg_admin_password }}"
name: "{{ database_name }}"
state: absent
- name: Print when errors
ansible.builtin.debug:
msg: "Restore failed, because: {{ pg_restore_result.stderr_lines[1] }}"
when: pg_info.databases[database_name] is not defined
这个代码你可以找到here