vacuum_count 和 analyze_count 在 VACUUM ANALYZE 之后都为零
Both vacuum_count and analyze_count are zero after VACUUM ANALYZE
我编写了一个单元测试,它修改了 table(INSERT
和 DELETE
),然后手动修改 VACUUM
和 ANALYZE
是它,然后我查询 pg_stat_user_tables
以确保 VACUUM
和 ANALYZE
确实有一些效果。
我使用以下 SQL:
select
tbl.relid,
tbl.schemaname,
tbl.n_tup_del,
tbl.n_live_tup,
tbl.n_dead_tup,
tbl.n_mod_since_analyze,
tbl.vacuum_count,
tbl.analyze_count
from
pg_stat_user_tables tbl
where
tbl.relname = lower('...')
and schemaname = current_schema();
对于常规 tables 和
select
tbl.relid,
tbl.schemaname,
tbl.n_tup_del,
tbl.n_live_tup,
tbl.n_dead_tup,
tbl.n_mod_since_analyze,
tbl.vacuum_count,
tbl.analyze_count
from
pg_stat_user_tables tbl
join
pg_namespace nsp
on
tbl.schemaname = nsp.nspname
where
tbl.relname = lower('...')
and nsp.oid = pg_my_temp_schema();
对于临时的。
仍然,当我 运行 我的例子时,我观察到 vacuum_count
和 analyze_count
都是零,并且 table 包含很多死元组,例如:
relid = 913896
schemaname = pg_temp_20
n_tup_del = 10000
n_live_tup = 10000
n_dead_tup = 10000
n_mod_since_analyze = 30000
vacuum_count = 0
analyze_count = 0
这是为什么?
自带的代码示例如下:
package com.example;
import static com.example.AutoCommitMode.COMMIT;
import static com.example.AutoCommitMode.ON;
import static com.example.AutoCommitMode.ROLLBACK;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ds.common.BaseDataSource;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public final class PostgreSqlVacuumTest {
private static final String JDBC_URL = "jdbc:postgresql://localhost:5432/postgres?currentSchema=sandbox";
@DataProvider
private static @Nullable Object @NonNull[]@NonNull[] getParameters() {
@NonNull final List<@Nullable Object @NonNull[]> parameters = new ArrayList<>();
parameters.add(new @Nullable Object[] {"A", false, ON});
parameters.add(new @Nullable Object[] {"B", false, COMMIT});
parameters.add(new @Nullable Object[] {"C", false, ROLLBACK});
parameters.add(new @Nullable Object[] {"D", true, ON});
parameters.add(new @Nullable Object[] {"E", true, COMMIT});
parameters.add(new @Nullable Object[] {"F", true, ROLLBACK});
return parameters.toArray(new @Nullable Object @NonNull[0]@NonNull[]);
}
@Test(dataProvider = "getParameters")
@SuppressWarnings({"static-method", "incomplete-switch"})
public void test(@NonNull final String tableName,
final boolean temporary,
final AutoCommitMode mode)
throws SQLException {
final DataSource dataSource = new PGSimpleDataSource();
((BaseDataSource) dataSource).setUrl(JDBC_URL);
try (final Connection conn = dataSource.getConnection("user", "password")) {
try (final Statement stmt = conn.createStatement()) {
try {
stmt.executeUpdate(format("drop table %s", tableName));
} catch (@SuppressWarnings("unused") final SQLException ignored) {
// ignore
}
stmt.executeUpdate(format("create %stable %s (id bigint not null)",
temporary ? "temporary " : "",
tableName));
conn.setAutoCommit(mode.autoCommit);
for (int i = 0; i < 10000; i++) {
stmt.executeUpdate(format("insert into %s (id) values (%d)", tableName, i));
}
stmt.executeUpdate(format("delete from %s", tableName));
for (int i = 0; i < 10000; i++) {
stmt.executeUpdate(format("insert into %s (id) values (%d)", tableName, i));
}
switch (mode) {
case COMMIT:
conn.commit();
break;
case ROLLBACK:
conn.rollback();
break;
}
try (final ResultSet countHolder = stmt.executeQuery(format("select count(*) from %s", tableName))) {
Assert.assertTrue(countHolder.next());
final long count = countHolder.getLong(1);
Assert.assertEquals(count, mode == ROLLBACK ? 0L : 10000L);
Assert.assertFalse(countHolder.next());
}
switch (mode) {
case ON:
stmt.executeUpdate(format("vacuum analyze %s", tableName));
break;
case COMMIT:
case ROLLBACK:
// VACUUM cannot be executed inside a transaction block.
conn.setAutoCommit(true);
try {
stmt.executeUpdate(format("vacuum analyze %s", tableName));
} finally {
conn.setAutoCommit(false);
}
break;
}
diagnose(tableName, temporary, stmt);
}
}
}
private static void diagnose(@NonNull final String tableName,
final boolean temporary,
@NonNull final Statement stmt)
throws SQLException {
final List<String> columns = asList("relid", "schemaname", "n_tup_del", "n_live_tup", "n_dead_tup", "n_mod_since_analyze", "vacuum_count", "analyze_count");
final String diagSql = temporary
? "select\n" +
" tbl.relid,\n" +
" tbl.schemaname,\n" +
" tbl.n_tup_del,\n" +
" tbl.n_live_tup,\n" +
" tbl.n_dead_tup,\n" +
" tbl.n_mod_since_analyze,\n" +
" tbl.vacuum_count,\n" +
" tbl.analyze_count\n" +
"from\n" +
" pg_stat_user_tables tbl\n" +
"join\n" +
" pg_namespace nsp\n" +
"on\n" +
" tbl.schemaname = nsp.nspname\n" +
"where\n" +
" tbl.relname = lower('%s')\n" +
" and nsp.oid = pg_my_temp_schema()"
: "select\n" +
" tbl.relid,\n" +
" tbl.schemaname,\n" +
" tbl.n_tup_del,\n" +
" tbl.n_live_tup,\n" +
" tbl.n_dead_tup,\n" +
" tbl.n_mod_since_analyze,\n" +
" tbl.vacuum_count,\n" +
" tbl.analyze_count\n" +
"from\n" +
" pg_stat_user_tables tbl\n" +
"where\n" +
" tbl.relname = lower('%s')\n" +
" and schemaname = current_schema()";
try (final ResultSet rset = stmt.executeQuery(format(diagSql, tableName))) {
while (rset.next()) {
System.out.println("---------------");
for (final String column : columns) {
System.out.println("\t" + column + " = " + rset.getObject(column));
}
}
}
}
}
enum AutoCommitMode {
ON(true),
COMMIT(false),
ROLLBACK(false),
;
final boolean autoCommit;
AutoCommitMode(final boolean autoCommit) {
this.autoCommit = autoCommit;
}
}
您确定用户(您用来手动 运行 VACUUM 的用户)具有足够的权限吗?
文档说:
"VACUUM processes every table in the current database that the current user has permission to vacuum"
VACUUM(没有 FULL)将死元组标记为可以重用,它也能够截断 table 末尾的死元组,但它不会消除那些死元组。
VACUUM FULL 从头开始重写整个 table,因此在此过程中删除了 dead tuplesgwt,但它需要对 table:
的独占锁定
https://www.postgresql.org/message-id/dcc563d10710021855l7ac247ebv62c5fc44a321ee1f%40mail.gmail.com
如果您有一个大的 table 并且想要在其上 运行 VACUUM FULL 并且能够在此过程中使用它,请考虑使用 https://github.com/reorg/pg_repack/.
我编写了一个单元测试,它修改了 table(INSERT
和 DELETE
),然后手动修改 VACUUM
和 ANALYZE
是它,然后我查询 pg_stat_user_tables
以确保 VACUUM
和 ANALYZE
确实有一些效果。
我使用以下 SQL:
select
tbl.relid,
tbl.schemaname,
tbl.n_tup_del,
tbl.n_live_tup,
tbl.n_dead_tup,
tbl.n_mod_since_analyze,
tbl.vacuum_count,
tbl.analyze_count
from
pg_stat_user_tables tbl
where
tbl.relname = lower('...')
and schemaname = current_schema();
对于常规 tables 和
select
tbl.relid,
tbl.schemaname,
tbl.n_tup_del,
tbl.n_live_tup,
tbl.n_dead_tup,
tbl.n_mod_since_analyze,
tbl.vacuum_count,
tbl.analyze_count
from
pg_stat_user_tables tbl
join
pg_namespace nsp
on
tbl.schemaname = nsp.nspname
where
tbl.relname = lower('...')
and nsp.oid = pg_my_temp_schema();
对于临时的。
仍然,当我 运行 我的例子时,我观察到 vacuum_count
和 analyze_count
都是零,并且 table 包含很多死元组,例如:
relid = 913896
schemaname = pg_temp_20
n_tup_del = 10000
n_live_tup = 10000
n_dead_tup = 10000
n_mod_since_analyze = 30000
vacuum_count = 0
analyze_count = 0
这是为什么?
自带的代码示例如下:
package com.example;
import static com.example.AutoCommitMode.COMMIT;
import static com.example.AutoCommitMode.ON;
import static com.example.AutoCommitMode.ROLLBACK;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ds.common.BaseDataSource;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public final class PostgreSqlVacuumTest {
private static final String JDBC_URL = "jdbc:postgresql://localhost:5432/postgres?currentSchema=sandbox";
@DataProvider
private static @Nullable Object @NonNull[]@NonNull[] getParameters() {
@NonNull final List<@Nullable Object @NonNull[]> parameters = new ArrayList<>();
parameters.add(new @Nullable Object[] {"A", false, ON});
parameters.add(new @Nullable Object[] {"B", false, COMMIT});
parameters.add(new @Nullable Object[] {"C", false, ROLLBACK});
parameters.add(new @Nullable Object[] {"D", true, ON});
parameters.add(new @Nullable Object[] {"E", true, COMMIT});
parameters.add(new @Nullable Object[] {"F", true, ROLLBACK});
return parameters.toArray(new @Nullable Object @NonNull[0]@NonNull[]);
}
@Test(dataProvider = "getParameters")
@SuppressWarnings({"static-method", "incomplete-switch"})
public void test(@NonNull final String tableName,
final boolean temporary,
final AutoCommitMode mode)
throws SQLException {
final DataSource dataSource = new PGSimpleDataSource();
((BaseDataSource) dataSource).setUrl(JDBC_URL);
try (final Connection conn = dataSource.getConnection("user", "password")) {
try (final Statement stmt = conn.createStatement()) {
try {
stmt.executeUpdate(format("drop table %s", tableName));
} catch (@SuppressWarnings("unused") final SQLException ignored) {
// ignore
}
stmt.executeUpdate(format("create %stable %s (id bigint not null)",
temporary ? "temporary " : "",
tableName));
conn.setAutoCommit(mode.autoCommit);
for (int i = 0; i < 10000; i++) {
stmt.executeUpdate(format("insert into %s (id) values (%d)", tableName, i));
}
stmt.executeUpdate(format("delete from %s", tableName));
for (int i = 0; i < 10000; i++) {
stmt.executeUpdate(format("insert into %s (id) values (%d)", tableName, i));
}
switch (mode) {
case COMMIT:
conn.commit();
break;
case ROLLBACK:
conn.rollback();
break;
}
try (final ResultSet countHolder = stmt.executeQuery(format("select count(*) from %s", tableName))) {
Assert.assertTrue(countHolder.next());
final long count = countHolder.getLong(1);
Assert.assertEquals(count, mode == ROLLBACK ? 0L : 10000L);
Assert.assertFalse(countHolder.next());
}
switch (mode) {
case ON:
stmt.executeUpdate(format("vacuum analyze %s", tableName));
break;
case COMMIT:
case ROLLBACK:
// VACUUM cannot be executed inside a transaction block.
conn.setAutoCommit(true);
try {
stmt.executeUpdate(format("vacuum analyze %s", tableName));
} finally {
conn.setAutoCommit(false);
}
break;
}
diagnose(tableName, temporary, stmt);
}
}
}
private static void diagnose(@NonNull final String tableName,
final boolean temporary,
@NonNull final Statement stmt)
throws SQLException {
final List<String> columns = asList("relid", "schemaname", "n_tup_del", "n_live_tup", "n_dead_tup", "n_mod_since_analyze", "vacuum_count", "analyze_count");
final String diagSql = temporary
? "select\n" +
" tbl.relid,\n" +
" tbl.schemaname,\n" +
" tbl.n_tup_del,\n" +
" tbl.n_live_tup,\n" +
" tbl.n_dead_tup,\n" +
" tbl.n_mod_since_analyze,\n" +
" tbl.vacuum_count,\n" +
" tbl.analyze_count\n" +
"from\n" +
" pg_stat_user_tables tbl\n" +
"join\n" +
" pg_namespace nsp\n" +
"on\n" +
" tbl.schemaname = nsp.nspname\n" +
"where\n" +
" tbl.relname = lower('%s')\n" +
" and nsp.oid = pg_my_temp_schema()"
: "select\n" +
" tbl.relid,\n" +
" tbl.schemaname,\n" +
" tbl.n_tup_del,\n" +
" tbl.n_live_tup,\n" +
" tbl.n_dead_tup,\n" +
" tbl.n_mod_since_analyze,\n" +
" tbl.vacuum_count,\n" +
" tbl.analyze_count\n" +
"from\n" +
" pg_stat_user_tables tbl\n" +
"where\n" +
" tbl.relname = lower('%s')\n" +
" and schemaname = current_schema()";
try (final ResultSet rset = stmt.executeQuery(format(diagSql, tableName))) {
while (rset.next()) {
System.out.println("---------------");
for (final String column : columns) {
System.out.println("\t" + column + " = " + rset.getObject(column));
}
}
}
}
}
enum AutoCommitMode {
ON(true),
COMMIT(false),
ROLLBACK(false),
;
final boolean autoCommit;
AutoCommitMode(final boolean autoCommit) {
this.autoCommit = autoCommit;
}
}
您确定用户(您用来手动 运行 VACUUM 的用户)具有足够的权限吗? 文档说:
"VACUUM processes every table in the current database that the current user has permission to vacuum"
VACUUM(没有 FULL)将死元组标记为可以重用,它也能够截断 table 末尾的死元组,但它不会消除那些死元组。
VACUUM FULL 从头开始重写整个 table,因此在此过程中删除了 dead tuplesgwt,但它需要对 table:
的独占锁定https://www.postgresql.org/message-id/dcc563d10710021855l7ac247ebv62c5fc44a321ee1f%40mail.gmail.com
如果您有一个大的 table 并且想要在其上 运行 VACUUM FULL 并且能够在此过程中使用它,请考虑使用 https://github.com/reorg/pg_repack/.