{"id":1864,"date":"2025-08-01T20:39:11","date_gmt":"2025-08-01T18:39:11","guid":{"rendered":"https:\/\/olvas.dev\/?p=1864"},"modified":"2025-08-01T20:56:38","modified_gmt":"2025-08-01T18:56:38","slug":"%d1%80%d0%b5%d0%b7%d0%b5%d1%80%d0%b2%d0%bd%d1%8b%d0%b5-%d0%ba%d0%be%d0%bf%d0%b8%d0%b8-postgresql-%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-%d1%81%d0%ba%d0%be%d1%80%d0%be%d1%81%d1%82","status":"publish","type":"post","link":"https:\/\/olvas.dev\/?p=1864","title":{"rendered":"\u0420\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 PostgreSQL: \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 pg_dump \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 \u0438 \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0443\u0440\u043e\u0432\u043d\u044f\u043c\u0438 \u0441\u0436\u0430\u0442\u0438\u044f"},"content":{"rendered":"\n<p>\u042f \u0441\u0434\u0435\u043b\u0430\u043b \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e \u0447\u0435\u0440\u0435\u0437&nbsp;<code>pg_dump<\/code>&nbsp;\u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0441\u044f \u0438\u0437 \u043d\u0435\u0451 21 \u0440\u0430\u0437\u0430. \u0420\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 \u0434\u0435\u043b\u0430\u043b \u0432 4 \u0440\u0430\u0437\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0442 1 \u0434\u043e 7 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0441\u0436\u0430\u0442\u0438\u044f \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0417\u0430\u043f\u0438\u0441\u0430\u043b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0438 \u0441\u0434\u0435\u043b\u0430\u043b \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0438\u0434\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b \u0434\u043b\u044f \u043c\u043e\u0435\u0433\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n\n\n\n<p>\u0414\u0435\u0442\u0430\u043b\u0438 \u0438 \u0437\u0430\u043c\u0435\u0440\u044b \u2014 \u043d\u0438\u0436\u0435.<a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u0417\u0430\u0447\u0435\u043c \u043c\u043d\u0435 \u044d\u0442\u043e\u0442 \u0437\u0430\u043c\u0435\u0440?<\/h2>\n\n\n\n<p>\u0423 \u043c\u0435\u043d\u044f \u0441\u0442\u043e\u044f\u043b\u0430 \u0432\u043f\u043e\u043b\u043d\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430: \u043d\u0430\u0439\u0442\u0438 \u043b\u0443\u0447\u0448\u0438\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439&nbsp;<code>pg_dump<\/code>. \u00ab\u041b\u0443\u0447\u0448\u0438\u0439\u00bb&nbsp;\u2014 \u0441&nbsp;\u0441\u0430\u043c\u044b\u043c \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435\u043c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438, \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430.<\/p>\n\n\n\n<p>\u042d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0432 \u0441\u0432\u043e\u0451\u043c open source \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0434\u043b\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 PostgreSQL (<a href=\"https:\/\/postgresus.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u0441\u0430\u0439\u0442\u00a0<\/a>\\\u00a0<a href=\"https:\/\/github.com\/RostislavDugin\/postgresus\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>). \u041e \u043d\u0435\u043c \u0435\u0441\u0442\u044c\u00a0<a href=\"https:\/\/habr.com\/ru\/articles\/927166\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u043e\u0431\u0437\u043e\u0440\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430 \u0425\u0430\u0431\u0440\u0435<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6fe\/3d1\/de4\/6fe3d1de4355f78c612803daaf4a8a42.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u044b\u043b\u0438 \u0434\u043e\u043f. \u0432\u0432\u043e\u0434\u043d\u044b\u0435:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0430\u044f \u043a\u043e\u043f\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u0436\u0438\u043c\u0430\u0442\u044c\u0441\u044f\u00a0<strong>\u0434\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438<\/strong>\u00a0\u043d\u0430 \u043c\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0442\u0438;<\/li>\n\n\n\n<li>\u0441\u0430\u043c \u0444\u0430\u0439\u043b \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438\u00a0<strong>\u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d<\/strong>\u00a0(\u0430 \u043d\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f), \u0447\u0442\u043e\u0431\u044b \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0442\u0440\u0438\u043c\u0438\u0442\u044c \u043a\u0430\u043a \u043d\u0430 \u0434\u0438\u0441\u043a, \u0442\u0430\u043a \u0438 \u0432 S3 \u0438\u043b\u0438 \u0432 \u043e\u0431\u043b\u0430\u043a\u043e;<\/li>\n\n\n\n<li>\u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043f\u0438\u0438 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0411\u0414 (\u043f\u043e\u044d\u0442\u043e\u043c\u0443 PgBackRest, WAL-G, pg_basebackup \u043e\u0442\u043f\u0430\u043b\u0438), \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 open source \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u043e\u043e\u0431\u0449\u0435 \u0441 \u043b\u044e\u0431\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 (\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u0432 Docker&#8217;e, \u0432 DBaaS, \u0441 \u0440\u0435\u043f\u043b\u0438\u043a\u043e\u0439 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u0438 \u0442.\u0434.).<\/li>\n<\/ul>\n\n\n\n<p><a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u041e \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u0438 \u0432\u0438\u0434\u0430\u0445 \u0441\u0436\u0430\u0442\u0438\u044f \u0432 pg_dump<\/h2>\n\n\n\n<p><code>pg_dump<\/code>&nbsp;\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 4 \u0444\u043e\u0440\u043c\u0430\u0442\u0430:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>\u0424\u043e\u0440\u043c\u0430\u0442<\/td><td>\u0421\u0436\u0430\u0442\u0438\u0435<\/td><td>\u041e\u0434\u0438\u043d \u0444\u0430\u0439\u043b?<\/td><td>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0439 \u0431\u0435\u043a\u0430\u043f<\/td><td>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435<\/td><\/tr><tr><td>Plain (SQL)<\/td><td>\u274c<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u274c<\/td><\/tr><tr><td>Custom (-Fc)<\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u2705<\/td><\/tr><tr><td>Directory (-Fd)<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u2705<\/td><td>\u2705<\/td><\/tr><tr><td>Tar (-Ft)<\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u274c<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>\u0411\u043e\u043b\u044c\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043c\u0435\u043d\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0439. \u041e\u043d\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0431\u0435\u043a\u0430\u043f\u043e\u0432. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e, \u043d\u043e \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0435\u0451 \u0432 \u043e\u0434\u0438\u043d \u0444\u0430\u0439\u043b. \u0410 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0443\u043c\u0435\u0435\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0438 \u0431\u0435\u043a\u0430\u043f\u0438\u0442\u044c, \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c, \u043d\u043e \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0451 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e.<\/p>\n\n\n\n<p>\u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432, \u0432 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0432\u0438\u0434\u0435, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0442\u0438\u043f\u044b \u0441\u0436\u0430\u0442\u0438\u0439:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435<\/strong><\/td><td><strong>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c (\u0432 \u0442\u0435\u043e\u0440\u0438\u0438)<\/strong><\/td><td><strong>\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0441\u0436\u0430\u0442\u0438\u044f \\ \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u043a\u0438 (\u0432 \u0442\u0435\u043e\u0440\u0438\u0438)<\/strong><\/td><td><strong>\u041a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u0441\u0436\u0430\u0442\u0438\u044f (\u0432 \u0442\u0435\u043e\u0440\u0438\u0438)<\/strong><\/td><\/tr><tr><td>gzip<\/td><td>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u0436\u0430\u0442\u0438\u044f<\/td><td>\u0441\u0440\u0435\u0434\u043d\u044f\u044f \\ \u0432\u044b\u0441\u043e\u043a\u0430\u044f<\/td><td>2-3 \u0440\u0430\u0437\u0430<\/td><\/tr><tr><td>lz4<\/td><td>\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u0436\u0430\u0442\u0438\u044f, \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c, \u0447\u0435\u043c gzip<\/td><td>\u043e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f \\ \u043e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f<\/td><td>1.5-2 \u0440\u0430\u0437\u0430<\/td><\/tr><tr><td>zstd<\/td><td>\u041e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u043e\u0432\u044b\u0439 (2016-\u0433\u043e \u0433\u043e\u0434\u0430) \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u0436\u0430\u0442\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 \u0441\u043e\u0446. \u0441\u0435\u0442\u0438 \u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0430 \u0426\u0443\u043a\u0435\u0440\u0431\u0435\u0440\u043a\u0430: \u0431\u0430\u043b\u0430\u043d\u0441 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0438 \u0441\u0436\u0430\u0442\u0438\u044f<\/td><td>\u0432\u044b\u0441\u043e\u043a\u0430\u044f \\ \u0432\u044b\u0441\u043e\u043a\u0430\u044f<\/td><td>3-5 \u0440\u0430\u0437<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 \u0441\u0436\u0430\u0442\u0438\u044f \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b&nbsp;\u043d\u0430 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0441\u0436\u0430\u0442\u0438\u0435 \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u0441\u044f 100% \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043e 100%-\u0439 \u0443\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 CPU. \u0415\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0411\u0414-\u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u043f\u0440\u043e\u0441\u0430\u0434\u044f\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0441\u0436\u0430\u0442\u0438\u044f (\u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u00ab\u043d\u0430 \u043b\u0435\u0442\u0443\u00bb).<\/p>\n\n\n\n<p>\u0414\u043e \u0442\u0435\u0441\u0442\u0430 \u044f \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u043b, \u0447\u0442\u043e \u0441\u0430\u043c\u044b\u043c \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 gzip \u0441\u0436\u0430\u0442\u0438\u0435\u043c (\u043a\u0430\u043a \u0437\u043e\u043b\u043e\u0442\u0430\u044f \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0430) \u0438 \u0432\u043e\u0437\u043b\u0430\u0433\u0430\u043b \u043e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u044b\u0435 \u043d\u0430\u0434\u0435\u0436\u0434\u044b \u043d\u0430&nbsp;<code>zstd<\/code>&nbsp;(\u043a\u0430\u043a \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442). \u041a\u0441\u0442\u0430\u0442\u0438,&nbsp;<code>zstd<\/code>&nbsp;\u043d\u0430\u0447\u0430\u043b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432 PostgreSQL 15.<a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 PostgreSQL<\/h2>\n\n\n\n<p>\u042f \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b \u0434\u0432\u0435 PostgreSQL \u0432 Docker Compose&#8217;e: \u043e\u0434\u043d\u0443 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 (\u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438) \u0438 \u043e\u0434\u043d\u0443 \u0434\u043b\u044f \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u043d\u0438\u0445. \u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u0438 \u0443 \u043c\u0435\u043d\u044f \u0437\u0430\u043d\u044f\u0442\u044b \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 PostgreSQL.<\/p>\n\n\n\n<p><code>docker-compose.yml<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>version: \"3.8\"<br><br>services:<br>  db:<br>    image: postgres:17<br>    container_name: db<br>    environment:<br>      POSTGRES_DB: testdb<br>      POSTGRES_USER: postgres<br>      POSTGRES_PASSWORD: testpassword<br>    ports:<br>      - \"7000:7000\"<br>    command: -p 7000<br>    volumes:<br>      - .\/pgdata:\/var\/lib\/postgresql\/data<br>    healthcheck:<br>      test: &#91;\"CMD-SHELL\", \"pg_isready -U postgres -d testdb -p 7000\"]<br>      interval: 10s<br>      timeout: 5s<br>      retries: 5<br>    restart: unless-stopped<br><br>  db-restore:<br>    image: postgres:17<br>    container_name: db-restore<br>    environment:<br>      POSTGRES_DB: testdb<br>      POSTGRES_USER: postgres<br>      POSTGRES_PASSWORD: testpassword<br>    ports:<br>      - \"7001:7001\"<br>    command: -p 7001<br>    volumes:<br>      - .\/pgdata-restore:\/var\/lib\/postgresql\/data<br>    healthcheck:<br>      test: &#91;\"CMD-SHELL\", \"pg_isready -U postgres -d testdb -p 7001\"]<br>      interval: 10s<br>      timeout: 5s<br>      retries: 5<br>    restart: unless-stopped<br>    depends_on:<br>      - db<\/code><\/pre>\n\n\n\n<p>\u0417\u0430\u0442\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0440\u0430\u0432\u0438\u043b&nbsp;<code>postgresql.conf<\/code>, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430. \u0423 \u043c\u0435\u043d\u044f AMD Ryzen 9 7950X (16 \u044f\u0434\u0435\u0440, 32 \u043f\u043e\u0442\u043e\u043a\u0430), 64 \u0413\u0411 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438 NVMe \u0434\u0438\u0441\u043a \u043d\u0430 1 \u0422\u0411. \u0411\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 4 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0438 16 \u0413\u0411 \u043f\u0430\u043c\u044f\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 PgTune.<\/p>\n\n\n\n<p><code>postgresql.conf<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># DB Version: 17<br># OS Type: linux<br># DB Type: web<br># Total Memory (RAM): 16 GB<br># CPUs num: 4<br># Connections num: 100<br># Data Storage: ssd<br><br>max_connections = 100<br>shared_buffers = 4GB<br>effective_cache_size = 12GB<br>maintenance_work_mem = 1GB<br>checkpoint_completion_target = 0.9<br>wal_buffers = 16MB<br>default_statistics_target = 100<br>random_page_cost = 1.1<br>effective_io_concurrency = 200<br>work_mem = 40329kB<br>huge_pages = off<br>min_wal_size = 1GB<br>max_wal_size = 4GB<br>max_worker_processes = 4<br>max_parallel_workers_per_gather = 2<br>max_parallel_workers = 4<br>max_parallel_maintenance_workers = 2<br><br>listen_addresses = '*'<\/code><\/pre>\n\n\n\n<p><a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n\n\n\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430, \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 3 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c\u0438 \u0438 9 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u043c\u0438 \u0441\u0443\u043c\u043c\u0430\u0440\u043d\u044b\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c ~11 \u0413\u0411. \u0414\u0430\u043d\u043d\u044b\u0435 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u043d\u043e\u0448\u0435\u0440\u0441\u0442\u043d\u044b\u0435. \u042f \u0431\u043e\u043b\u0435\u0435, \u0447\u0435\u043c \u0443\u0432\u0435\u0440\u0435\u043d, \u0447\u0442\u043e \u0441 \u043a\u0430\u043a\u0438\u043c\u0438-\u0442\u043e \u0442\u0438\u043f\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438&nbsp;<code>pg_dump<\/code>&nbsp;\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043b\u0443\u0447\u0448\u0435, \u0441 \u043a\u0430\u043a\u0438\u043c\u0438-\u0442\u043e \u2014 \u0445\u0443\u0436\u0435. \u041d\u043e \u043c\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0448\u0438\u0440\u043e\u043a\u0443\u044e \u0430\u0443\u0434\u0438\u0442\u043e\u0440\u0438\u044e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0440\u0438\u0442\u044c \u00ab\u0432 \u0441\u0440\u0435\u0434\u043d\u0435\u043c \u043f\u043e&nbsp;\u043f\u0430\u043b\u0430\u0442\u0435\u00bb.<\/p>\n\n\n\n<p>\u041d\u0438\u0436\u0435 \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0430\u0431\u043b\u0438\u0446.<\/p>\n\n\n\n<p><strong>\u0422\u0430\u0431\u043b\u0438\u0446\u044b<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>\u0422\u0430\u0431\u043b\u0438\u0446\u0430<\/strong><\/td><td><strong>\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/strong><\/td><td><strong>\u041a\u043e\u043b\u043e\u043d\u043a\u0438<\/strong><\/td><\/tr><tr><td>large_test_table<\/td><td>\u0421\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0442\u0438\u043f\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/td><td>18 \u043a\u043e\u043b\u043e\u043d\u043e\u043a, \u0432\u043a\u043b\u044e\u0447\u0430\u044f name, email, address, salary \u0438 \u0442.\u0434.<\/td><\/tr><tr><td>orders<\/td><td>\u0414\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043a\u0430\u0437\u043e\u0432 \u0438 \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439<\/td><td>13 \u043a\u043e\u043b\u043e\u043d\u043e\u043a, \u0432\u043a\u043b\u044e\u0447\u0430\u044f user_id, order_number, amounts \u0438 \u0442.\u0434.<\/td><\/tr><tr><td>activity_logs<\/td><td>\u041b\u043e\u0433\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438<\/td><td>13 \u043a\u043e\u043b\u043e\u043d\u043e\u043a, \u0432\u043a\u043b\u044e\u0447\u0430\u044f user_id, action, details, timestamps \u0438 \u0442.\u0434.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>\u0418\u043d\u0434\u0435\u043a\u0441\u044b<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>\u0422\u0430\u0431\u043b\u0438\u0446\u0430<\/strong><\/td><td><strong>\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u0430<\/strong><\/td><td><strong>\u041a\u043e\u043b\u043e\u043d\u043a\u0430<\/strong><\/td><td><strong>\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/strong><\/td><\/tr><tr><td>large_test_table<\/td><td>idx_large_test_name<\/td><td>name<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0438\u043c\u0435\u043d\u0438<\/td><\/tr><tr><td>large_test_table<\/td><td>idx_large_test_email<\/td><td>email<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u043f\u043e\u0447\u0442\u0435<\/td><\/tr><tr><td>large_test_table<\/td><td>idx_large_test_created_at<\/td><td>created_at<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c\u0443 \u043e\u0442\u0440\u0435\u0437\u043a\u0443<\/td><\/tr><tr><td>large_test_table<\/td><td>idx_large_test_department<\/td><td>department<\/td><td>\u0424\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u043e \u043e\u0442\u0434\u0435\u043b\u0443<\/td><\/tr><tr><td>orders<\/td><td>idx_orders_user_id<\/td><td>user_id<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0437\u0430\u043a\u0430\u0437\u043e\u0432<\/td><\/tr><tr><td>orders<\/td><td>idx_orders_order_date<\/td><td>order_date<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/td><\/tr><tr><td>orders<\/td><td>idx_orders_status<\/td><td>status<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0443<\/td><\/tr><tr><td>activity_logs<\/td><td>idx_activity_user_id<\/td><td>user_id<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e ID<\/td><\/tr><tr><td>activity_logs<\/td><td>idx_activity_timestamp<\/td><td>timestamp<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0434\u0430\u0442\u0435<\/td><\/tr><tr><td>activity_logs<\/td><td>idx_activity_action<\/td><td>action<\/td><td>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044e<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u0411\u0414 Python-\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c. \u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e 25 000 \u0441\u0442\u0440\u043e\u043a;<\/li>\n\n\n\n<li>\u0432 \u043a\u0430\u0436\u0434\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u043f\u043e \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e 100 000 \u0441\u0442\u0440\u043e\u043a \u0447\u0435\u0440\u0435\u0437\u00a0<code>COPY<\/code>;<\/li>\n\n\n\n<li>\u043f\u043e \u0434\u043e\u0441\u0442\u0438\u0436\u0435\u043d\u0438\u044e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u0411\u0414 \u0432 10 \u0413\u0411 \u2014 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u044b.<\/li>\n<\/ul>\n\n\n\n<p><strong>\u0421\u043a\u0440\u0438\u043f\u0442 \u0434\u043b\u044f \u043d\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0411\u0414<\/strong><\/p>\n\n\n\n<p><code>#!\/usr\/bin\/env python3<br>\"\"\"<br>Script to populate PostgreSQL database with ~10GB of test data for backup performance testing.<br>Inserts 1 million rows into each of 3 tables until reaching 10GB target.<br>\"\"\"<br><br><strong>import<\/strong> psycopg2<br><strong>import<\/strong> random<br><strong>import<\/strong> string<br><strong>import<\/strong> time<br><strong>import<\/strong> io<br><strong>from<\/strong> datetime <strong>import<\/strong> datetime, timedelta<br><strong>import<\/strong> sys<br><br><br># Database connection parameters<br>DB_CONFIG = {<br>    \"host\": \"localhost\",<br>    \"port\": 7000,<br>    \"database\": \"testdb\",<br>    \"user\": \"postgres\",<br>    \"password\": \"testpassword\",<br>}<br><br># Target database size in bytes (10GB)<br>TARGET_SIZE_GB = 10<br>TARGET_SIZE_BYTES = TARGET_SIZE_GB * 1024 * 1024 * 1024<br><br># Rows per table per round<br>ROWS_PER_TABLE = 100000<br><br><br><strong>def<\/strong> generate_random_string(length):<br>    \"\"\"Generate a random string of specified length.\"\"\"<br>    <strong>return<\/strong> \"\".join(random.choices(string.ascii_letters + string.digits + \" \", k=length))<br><br><br><strong>def<\/strong> generate_random_date():<br>    \"\"\"Generate a random date within the last 5 years.\"\"\"<br>    start_date = datetime.now() - timedelta(days=5 * 365)<br>    random_days = random.randint(0, 5 * 365)<br>    <strong>return<\/strong> start_date + timedelta(days=random_days)<br><br><br><strong>def<\/strong> create_tables(cursor):<br>    \"\"\"Create the 3 test tables with various data types.\"\"\"<br>    print(\"Creating 3 tables...\")<br><br>    # Table 1: Large table with mixed data types<br>    cursor.execute(\"\"\"<br>        CREATE TABLE IF NOT EXISTS large_test_table (<br>            id BIGSERIAL PRIMARY KEY,<br>            name VARCHAR(100),<br>            description TEXT,<br>            email VARCHAR(255),<br>            phone VARCHAR(20),<br>            address TEXT,<br>            city VARCHAR(100),<br>            country VARCHAR(100),<br>            postal_code VARCHAR(20),<br>            birth_date DATE,<br>            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,<br>            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,<br>            salary DECIMAL(10,2),<br>            is_active BOOLEAN DEFAULT TRUE,<br>            rating FLOAT,<br>            notes TEXT,<br>            department VARCHAR(100),<br>            employee_id VARCHAR(50)<br>        )<br>    \"\"\")<br><br>    # Table 2: Orders table for transactional data<br>    cursor.execute(\"\"\"<br>        CREATE TABLE IF NOT EXISTS orders (<br>            id BIGSERIAL PRIMARY KEY,<br>            user_id BIGINT,<br>            order_number VARCHAR(50) UNIQUE,<br>            total_amount DECIMAL(12,2),<br>            order_date TIMESTAMP,<br>            status VARCHAR(20),<br>            shipping_address TEXT,<br>            notes TEXT,<br>            payment_method VARCHAR(50),<br>            shipping_method VARCHAR(50),<br>            discount_amount DECIMAL(10,2),<br>            product_list TEXT,<br>            customer_notes TEXT<br>        )<br>    \"\"\")<br><br>    # Table 3: Activity logs with lots of text data<br>    cursor.execute(\"\"\"<br>        CREATE TABLE IF NOT EXISTS activity_logs (<br>            id BIGSERIAL PRIMARY KEY,<br>            user_id BIGINT,<br>            action VARCHAR(100),<br>            details TEXT,<br>            ip_address INET,<br>            user_agent TEXT,<br>            timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,<br>            session_id VARCHAR(100),<br>            browser VARCHAR(50),<br>            operating_system VARCHAR(50),<br>            referrer TEXT,<br>            response_time INTEGER,<br>            error_message TEXT<br>        )<br>    \"\"\")<br><br>    print(\"3 tables created successfully!\")<br><br><br><strong>def<\/strong> create_indexes(cursor):<br>    \"\"\"Create indexes for all 3 tables.\"\"\"<br>    print(\"Creating indexes...\")<br><br>    # Indexes for large_test_table<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_large_test_name ON large_test_table(name)\"<br>    )<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_large_test_email ON large_test_table(email)\"<br>    )<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_large_test_created_at ON large_test_table(created_at)\"<br>    )<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_large_test_department ON large_test_table(department)\"<br>    )<br><br>    # Indexes for orders<br>    cursor.execute(\"CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id)\")<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_orders_order_date ON orders(order_date)\"<br>    )<br>    cursor.execute(\"CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status)\")<br><br>    # Indexes for activity_logs<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_activity_user_id ON activity_logs(user_id)\"<br>    )<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_activity_timestamp ON activity_logs(timestamp)\"<br>    )<br>    cursor.execute(<br>        \"CREATE INDEX IF NOT EXISTS idx_activity_action ON activity_logs(action)\"<br>    )<br><br>    print(\"Indexes created successfully!\")<br><br><br><strong>def<\/strong> drop_indexes(cursor):<br>    \"\"\"Drop indexes for faster bulk loading.\"\"\"<br>    print(\"Dropping indexes for bulk loading...\")<br><br>    indexes_to_drop = [<br>        \"idx_large_test_name\",<br>        \"idx_large_test_email\",<br>        \"idx_large_test_created_at\",<br>        \"idx_large_test_department\",<br>        \"idx_orders_user_id\",<br>        \"idx_orders_order_date\",<br>        \"idx_orders_status\",<br>        \"idx_activity_user_id\",<br>        \"idx_activity_timestamp\",<br>        \"idx_activity_action\",<br>    ]<br><br>    <strong>for<\/strong> index <strong>in<\/strong> indexes_to_drop:<br>        <strong>try<\/strong>:<br>            cursor.execute(f\"DROP INDEX IF EXISTS {index}\")<br>        <strong>except<\/strong>:<br>            <strong>pass<\/strong><br><br>    print(\"Indexes dropped!\")<br><br><br><strong>def<\/strong> print_progress(inserted, total, start_time, operation_name):<br>    \"\"\"Print detailed progress information.\"\"\"<br>    current_time = time.time()<br>    elapsed = current_time - start_time<br>    rate = inserted \/ elapsed <strong>if<\/strong> elapsed > 0 <strong>else<\/strong> 0<br>    percentage = (inserted \/ total) * 100<br><br>    <strong>if<\/strong> rate > 0:<br>        eta_seconds = (total - inserted) \/ rate<br>        eta_minutes = eta_seconds \/ 60<br>        eta_str = f\"ETA: {eta_minutes:.1f}m\"<br>    <strong>else<\/strong>:<br>        eta_str = \"ETA: calculating...\"<br><br>    print(<br>        f\"{operation_name}: {inserted:,} \/ {total:,} ({percentage:.1f}%) | \"<br>        f\"Rate: {rate:,.0f} rows\/sec | Elapsed: {elapsed \/ 60:.1f}m | {eta_str}\"<br>    )<br><br><br><strong>def<\/strong> populate_large_table_batch(cursor, count=ROWS_PER_TABLE):<br>    \"\"\"Populate large_test_table with specified number of rows.\"\"\"<br>    print(f\"Inserting {count:,} rows into large_test_table...\")<br><br>    batch_size = 25000<br>    inserted = 0<br>    start_time = time.time()<br><br>    <strong>while<\/strong> inserted &lt; count:<br>        current_batch_size = min(batch_size, count - inserted)<br>        data_buffer = io.StringIO()<br><br>        <strong>for<\/strong> i <strong>in<\/strong> range(current_batch_size):<br>            name = (<br>                generate_random_string(random.randint(20, 100))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            description = (<br>                generate_random_string(random.randint(100, 500))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            email = f\"{generate_random_string(10)}@{generate_random_string(10)}.com\"<br>            phone = f\"+1-{random.randint(100, 999)}-{random.randint(100, 999)}-{random.randint(1000, 9999)}\"<br>            address = (<br>                generate_random_string(random.randint(50, 200))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            city = (<br>                generate_random_string(random.randint(10, 50))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            country = (<br>                generate_random_string(random.randint(5, 30))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            postal_code = f\"{random.randint(10000, 99999)}\"<br>            birth_date = generate_random_date().date()<br>            salary = f\"{random.randint(30000, 150000) + random.random():.2f}\"<br>            is_active = \"t\" <strong>if<\/strong> random.choice([<strong>True<\/strong>, <strong>False<\/strong>]) <strong>else<\/strong> \"f\"<br>            rating = f\"{random.uniform(1.0, 5.0):.2f}\"<br>            notes = (<br>                generate_random_string(random.randint(50, 300))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            department = random.choice(<br>                [\"HR\", \"Engineering\", \"Sales\", \"Marketing\", \"Finance\", \"Operations\"]<br>            )<br>            employee_id = (<br>                f\"EMP-{random.randint(1000, 9999)}-{generate_random_string(4)}\"<br>            )<br><br>            data_buffer.write(<br>                f\"{name}\\t{description}\\t{email}\\t{phone}\\t{address}\\t{city}\\t{country}\\t{postal_code}\\t{birth_date}\\t{salary}\\t{is_active}\\t{rating}\\t{notes}\\t{department}\\t{employee_id}\\n\"<br>            )<br><br>        data_buffer.seek(0)<br>        cursor.copy_from(<br>            data_buffer,<br>            \"large_test_table\",<br>            columns=(<br>                \"name\",<br>                \"description\",<br>                \"email\",<br>                \"phone\",<br>                \"address\",<br>                \"city\",<br>                \"country\",<br>                \"postal_code\",<br>                \"birth_date\",<br>                \"salary\",<br>                \"is_active\",<br>                \"rating\",<br>                \"notes\",<br>                \"department\",<br>                \"employee_id\",<br>            ),<br>            sep=\"\\t\",<br>        )<br><br>        inserted += current_batch_size<br>        <strong>if<\/strong> inserted % 100000 == 0:<br>            print_progress(inserted, count, start_time, \"Large table\")<br><br>    elapsed = time.time() - start_time<br>    print(<br>        f\"Completed large_test_table: {inserted:,} rows in {elapsed:.2f}s ({inserted \/ elapsed:,.0f} rows\/sec)\"<br>    )<br><br><br><strong>def<\/strong> populate_orders_batch(cursor, count=ROWS_PER_TABLE):<br>    \"\"\"Populate orders table with specified number of rows.\"\"\"<br>    print(f\"Inserting {count:,} rows into orders...\")<br><br>    batch_size = 25000<br>    inserted = 0<br>    start_time = time.time()<br><br>    <strong>while<\/strong> inserted &lt; count:<br>        current_batch_size = min(batch_size, count - inserted)<br>        data_buffer = io.StringIO()<br><br>        <strong>for<\/strong> i <strong>in<\/strong> range(current_batch_size):<br>            user_id = random.randint(1, 10000000)<br>            order_number = f\"ORD-{time.time_ns()}-{i:06d}\"<br>            total_amount = f\"{random.uniform(10.0, 5000.0):.2f}\"<br>            order_date = generate_random_date()<br>            status = random.choice(<br>                [\"pending\", \"processing\", \"shipped\", \"delivered\", \"cancelled\"]<br>            )<br>            shipping_address = (<br>                generate_random_string(random.randint(100, 300))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            notes = (<br>                generate_random_string(random.randint(50, 200))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>                <strong>if<\/strong> random.random() > 0.5<br>                <strong>else<\/strong> \"\"<br>            )<br>            payment_method = random.choice([\"credit_card\", \"paypal\", \"bank_transfer\"])<br>            shipping_method = random.choice([\"standard\", \"express\", \"overnight\"])<br>            discount_amount = (<br>                f\"{random.uniform(0, 50) <strong>if<\/strong> random.random() > 0.7 <strong>else<\/strong> 0:.2f}\"<br>            )<br>            product_list = (<br>                generate_random_string(random.randint(200, 800))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            customer_notes = (<br>                generate_random_string(random.randint(100, 400))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br><br>            data_buffer.write(<br>                f\"{user_id}\\t{order_number}\\t{total_amount}\\t{order_date}\\t{status}\\t{shipping_address}\\t{notes}\\t{payment_method}\\t{shipping_method}\\t{discount_amount}\\t{product_list}\\t{customer_notes}\\n\"<br>            )<br><br>        data_buffer.seek(0)<br>        cursor.copy_from(<br>            data_buffer,<br>            \"orders\",<br>            columns=(<br>                \"user_id\",<br>                \"order_number\",<br>                \"total_amount\",<br>                \"order_date\",<br>                \"status\",<br>                \"shipping_address\",<br>                \"notes\",<br>                \"payment_method\",<br>                \"shipping_method\",<br>                \"discount_amount\",<br>                \"product_list\",<br>                \"customer_notes\",<br>            ),<br>            sep=\"\\t\",<br>        )<br><br>        inserted += current_batch_size<br>        <strong>if<\/strong> inserted % 100000 == 0:<br>            print_progress(inserted, count, start_time, \"Orders\")<br><br>    elapsed = time.time() - start_time<br>    print(<br>        f\"Completed orders: {inserted:,} rows in {elapsed:.2f}s ({inserted \/ elapsed:,.0f} rows\/sec)\"<br>    )<br><br><br><strong>def<\/strong> populate_activity_logs_batch(cursor, count=ROWS_PER_TABLE):<br>    \"\"\"Populate activity_logs table with specified number of rows.\"\"\"<br>    print(f\"Inserting {count:,} rows into activity_logs...\")<br><br>    batch_size = 25000<br>    inserted = 0<br>    start_time = time.time()<br>    actions = [<br>        \"login\",<br>        \"logout\",<br>        \"view_product\",<br>        \"add_to_cart\",<br>        \"checkout\",<br>        \"update_profile\",<br>        \"search\",<br>        \"download\",<br>    ]<br><br>    <strong>while<\/strong> inserted &lt; count:<br>        current_batch_size = min(batch_size, count - inserted)<br>        data_buffer = io.StringIO()<br><br>        <strong>for<\/strong> i <strong>in<\/strong> range(current_batch_size):<br>            user_id = random.randint(1, 10000000)<br>            action = random.choice(actions)<br>            details = (<br>                generate_random_string(random.randint(100, 500))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>            )<br>            ip_address = f\"{random.randint(1, 255)}.{random.randint(1, 255)}.{random.randint(1, 255)}.{random.randint(1, 255)}\"<br>            user_agent = f\"Mozilla\/5.0 ({generate_random_string(50)}) {generate_random_string(30)}\".replace(<br>                \"\\t\", \" \"<br>            ).replace(\"\\n\", \" \")<br>            timestamp = generate_random_date()<br>            session_id = generate_random_string(32)<br>            browser = random.choice([\"Chrome\", \"Firefox\", \"Safari\", \"Edge\"])<br>            operating_system = random.choice(<br>                [\"Windows\", \"macOS\", \"Linux\", \"iOS\", \"Android\"]<br>            )<br>            referrer = (<br>                f\"https:\/\/{generate_random_string(10)}.com\"<br>                <strong>if<\/strong> random.random() > 0.3<br>                <strong>else<\/strong> \"\"<br>            )<br>            response_time = random.randint(50, 5000)<br>            error_message = (<br>                generate_random_string(random.randint(100, 300))<br>                .replace(\"\\t\", \" \")<br>                .replace(\"\\n\", \" \")<br>                <strong>if<\/strong> random.random() > 0.8<br>                <strong>else<\/strong> \"\"<br>            )<br><br>            data_buffer.write(<br>                f\"{user_id}\\t{action}\\t{details}\\t{ip_address}\\t{user_agent}\\t{timestamp}\\t{session_id}\\t{browser}\\t{operating_system}\\t{referrer}\\t{response_time}\\t{error_message}\\n\"<br>            )<br><br>        data_buffer.seek(0)<br>        cursor.copy_from(<br>            data_buffer,<br>            \"activity_logs\",<br>            columns=(<br>                \"user_id\",<br>                \"action\",<br>                \"details\",<br>                \"ip_address\",<br>                \"user_agent\",<br>                \"timestamp\",<br>                \"session_id\",<br>                \"browser\",<br>                \"operating_system\",<br>                \"referrer\",<br>                \"response_time\",<br>                \"error_message\",<br>            ),<br>            sep=\"\\t\",<br>        )<br><br>        inserted += current_batch_size<br>        <strong>if<\/strong> inserted % 100000 == 0:<br>            print_progress(inserted, count, start_time, \"Activity logs\")<br><br>    elapsed = time.time() - start_time<br>    print(<br>        f\"Completed activity_logs: {inserted:,} rows in {elapsed:.2f}s ({inserted \/ elapsed:,.0f} rows\/sec)\"<br>    )<br><br><br><strong>def<\/strong> get_database_size_bytes(cursor):<br>    \"\"\"Get the current database size in bytes.\"\"\"<br>    cursor.execute(\"SELECT pg_database_size('testdb')\")<br>    <strong>return<\/strong> cursor.fetchone()[0]<br><br><br><strong>def<\/strong> get_database_size_mb(cursor):<br>    \"\"\"Get the current database size in MB.\"\"\"<br>    size_bytes = get_database_size_bytes(cursor)<br>    <strong>return<\/strong> size_bytes \/ (1024 * 1024)<br><br><br><strong>def<\/strong> format_size_mb(size_bytes):<br>    \"\"\"Format size in bytes to MB string.\"\"\"<br>    <strong>return<\/strong> f\"{size_bytes \/ (1024 * 1024):.1f}MB\"<br><br><br><strong>def<\/strong> main():<br>    \"\"\"Main function to populate the database until reaching 10GB.\"\"\"<br>    print(\"Starting database population for backup performance testing...\")<br>    print(f\"Target: {TARGET_SIZE_GB * 1024:.0f}MB of data\")<br>    print(<br>        f\"Strategy: Insert {ROWS_PER_TABLE:,} rows into each of 3 tables per round until target reached\"<br>    )<br>    print(\"-\" * 80)<br><br>    <strong>try<\/strong>:<br>        # Connect to database<br>        print(\"Connecting to database...\")<br>        conn = psycopg2.connect(**DB_CONFIG)<br>        conn.autocommit = <strong>False<\/strong><br>        cursor = conn.cursor()<br><br>        initial_size_mb = get_database_size_mb(cursor)<br>        print(f\"Initial database size: {initial_size_mb:.1f}MB\")<br>        overall_start_time = time.time()<br><br>        # Create tables<br>        create_tables(cursor)<br>        conn.commit()<br><br>        # Drop indexes for faster loading<br>        drop_indexes(cursor)<br>        conn.commit()<br><br>        round_number = 1<br>        current_size_bytes = get_database_size_bytes(cursor)<br><br>        # Keep adding rounds of 1M rows per table until we reach 10GB<br>        <strong>while<\/strong> current_size_bytes &lt; TARGET_SIZE_BYTES:<br>            print(f\"\\n{'=' * 20} ROUND {round_number} {'=' * 20}\")<br>            round_start_time = time.time()<br><br>            # Populate all 3 tables with 1M rows each<br>            populate_large_table_batch(cursor)<br>            conn.commit()<br><br>            populate_orders_batch(cursor)<br>            conn.commit()<br><br>            populate_activity_logs_batch(cursor)<br>            conn.commit()<br><br>            # Check current size<br>            current_size_bytes = get_database_size_bytes(cursor)<br>            current_size_mb = format_size_mb(current_size_bytes)<br>            round_elapsed = time.time() - round_start_time<br><br>            print(f\"\\nRound {round_number} completed in {round_elapsed:.2f}s\")<br>            print(f\"Current database size: {current_size_mb}\")<br>            print(<br>                f\"Progress: {(current_size_bytes \/ TARGET_SIZE_BYTES) * 100:.1f}% of target ({TARGET_SIZE_GB * 1024:.0f}MB)\"<br>            )<br><br>            <strong>if<\/strong> current_size_bytes >= TARGET_SIZE_BYTES:<br>                print(f\"\u2705 Target size reached!\")<br>                <strong>break<\/strong><br><br>            round_number += 1<br><br>        # Recreate indexes<br>        print(f\"\\n{'=' * 20} CREATING INDEXES {'=' * 20}\")<br>        index_start_time = time.time()<br>        create_indexes(cursor)<br>        conn.commit()<br>        index_elapsed = time.time() - index_start_time<br>        print(f\"Indexes created in {index_elapsed:.2f}s\")<br><br>        # Final statistics<br>        overall_elapsed = time.time() - overall_start_time<br>        final_size_mb = get_database_size_mb(cursor)<br><br>        print(f\"\\n{'=' * 60}\")<br>        print(\"DATABASE POPULATION COMPLETED!\")<br>        print(f\"Final database size: {final_size_mb:.1f}MB\")<br>        print(f\"Total rounds: {round_number}\")<br>        print(<br>            f\"Total time: {overall_elapsed:.2f} seconds ({overall_elapsed \/ 60:.2f} minutes)\"<br>        )<br><br>        # Show table statistics<br>        cursor.execute(\"\"\"<br>            SELECT <br>                relname,<br>                n_tup_ins as \"Rows\",<br>                round(pg_total_relation_size('public.'||relname)::numeric \/ (1024*1024), 1) as \"Size_MB\"<br>            FROM pg_stat_user_tables <br>            ORDER BY pg_total_relation_size('public.'||relname) DESC<br>        \"\"\")<br><br>        print(f\"\\nTable Statistics:\")<br>        print(f\"{'Table':&lt;20} | {'Rows':&lt;12} | {'Size':&lt;10}\")<br>        print(\"-\" * 50)<br>        <strong>for<\/strong> row <strong>in<\/strong> cursor.fetchall():<br>            print(f\"{row[0]:&lt;20} | {row[1]:>12,} | {row[2]:.1f}MB\")<br><br>        cursor.close()<br>        conn.close()<br><br>    <strong>except<\/strong> psycopg2.Error <strong>as<\/strong> e:<br>        print(f\"Database error: {e}\")<br>        sys.exit(1)<br>    <strong>except<\/strong> Exception <strong>as<\/strong> e:<br>        print(f\"Error: {e}\")<br>        sys.exit(1)<br><br><br><strong>if<\/strong> __name__ == \"__main__\":<br>    main()<\/code><\/p>\n\n\n\n<p><a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0437\u0430\u043c\u0435\u0440\u043e\u0432<\/h2>\n\n\n\n<p>\u041f\u043e\u0441\u043b\u0435 21 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f, \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438;<\/li>\n\n\n\n<li>\u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438;<\/li>\n\n\n\n<li>\u043e\u0431\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f;<\/li>\n\n\n\n<li>\u0440\u0430\u0437\u043c\u0435\u0440 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u0411\u0414.<\/li>\n<\/ul>\n\n\n\n<p><strong>CSV \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u0441\u044b\u0440\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438<\/strong><\/p>\n\n\n\n<p><code>tool,format,compression_method,compression_level,backup_duration_seconds,restore_duration_seconds,total_duration_seconds,backup_size_bytes,database_size_bytes,restored_db_size_bytes,compression_ratio,backup_success,restore_success,backup_error,restore_error,timestamp<br>pg_dump,plain,none,0,100.39210295677185,735.2188968658447,835.6109998226166,9792231003,11946069139,11922173075,0.8197031918249641,True,True,,,2025-07-29T09:56:20.611844<br>pg_dump,custom,none,0,264.56927490234375,406.6467957496643,671.216070652008,6862699613,11946069139,11943709843,0.5744734550878778,True,True,,,2025-07-29T10:07:37.226681<br>pg_dump,custom,gzip,1,214.07211470603943,383.0168492794037,597.0889639854431,7074031563,11946069139,11943611539,0.5921639562511493,True,True,,,2025-07-29T10:17:39.801883<br>pg_dump,custom,gzip,5,260.6179132461548,393.76623010635376,654.3841433525085,6866440205,11946069139,11943718035,0.5747865783384196,True,True,,,2025-07-29T10:28:40.167485<br>pg_dump,custom,gzip,9,272.3802499771118,385.1409020423889,657.5211520195007,6856264586,11946069139,11943619731,0.5739347819121977,True,True,,,2025-07-29T10:39:42.912960<br>pg_dump,custom,lz4,1,84.0079517364502,379.6986663341522,463.7066180706024,9146843234,11946069139,11943685267,0.765678075990583,True,True,,,2025-07-29T10:47:32.131593<br>pg_dump,custom,lz4,5,150.24981474876404,393.44346714019775,543.6932818889618,8926348325,11946069139,11943718035,0.7472205477078983,True,True,,,2025-07-29T10:56:41.333595<br>pg_dump,custom,lz4,12,220.93980932235718,418.26913809776306,639.2089474201202,8923243046,11946069139,11943767187,0.7469606062188722,True,True,,,2025-07-29T11:07:26.574678<br>pg_dump,custom,zstd,1,87.83108067512512,419.07846903800964,506.90954971313477,6835388679,11946069139,11943767187,0.5721872692570225,True,True,,,2025-07-29T11:15:59.917828<br>pg_dump,custom,zstd,5,102.42366409301758,413.64263129234314,516.0662953853607,6774137561,11946069139,11944357011,0.567059966100871,True,True,,,2025-07-29T11:24:42.075008<br>pg_dump,custom,zstd,15,844.7868592739105,388.23959374427795,1233.0264530181885,6726189591,11946069139,11943636115,0.5630462633973209,True,True,,,2025-07-29T11:45:17.885163<br>pg_dump,custom,zstd,22,5545.566084384918,404.1370210647583,5949.7031054496765,6798947241,11946069139,11943750803,0.5691367731000038,True,True,,,2025-07-29T13:24:30.014902<br>pg_dump,directory,none,0,114.9900906085968,395.2716040611267,510.2616946697235,6854332396,11946069139,11943693459,0.5737730391684116,True,True,,,2025-07-29T13:33:05.944191<br>pg_dump,directory,lz4,1,53.48561334609985,384.92091369628906,438.4065270423889,9146095976,11946069139,11943668883,0.7656155233641663,True,True,,,2025-07-29T13:40:30.590719<br>pg_dump,directory,lz4,5,83.44352841377258,410.42058181762695,493.86411023139954,8925601067,11946069139,11943718035,0.7471579950814815,True,True,,,2025-07-29T13:48:50.201990<br>pg_dump,directory,lz4,12,114.15110802650452,400.04946303367615,514.2005710601807,8922495788,11946069139,11943758995,0.7468980535924554,True,True,,,2025-07-29T13:57:30.419171<br>pg_dump,directory,zstd,1,57.22735643386841,414.4600088596344,471.6873652935028,6835014976,11946069139,11943750803,0.5721559867493079,True,True,,,2025-07-29T14:05:28.529630<br>pg_dump,directory,zstd,5,60.121564865112305,398.27933716773987,458.4009020328522,6773763858,11946069139,11943709843,0.5670286835931563,True,True,,,2025-07-29T14:13:13.472761<br>pg_dump,directory,zstd,15,372.43965554237366,382.9877893924713,755.427444934845,6725815888,11946069139,11943644307,0.5630149808896062,True,True,,,2025-07-29T14:25:54.580924<br>pg_dump,directory,zstd,22,2637.47145485878,394.4939453601837,3031.9654002189636,6798573538,11946069139,11943660691,0.5691054905922891,True,True,,,2025-07-29T15:16:29.450828<br>pg_dump,tar,none,0,126.3212628364563,664.1294028759003,790.4506657123566,9792246784,11946069139,11942759571,0.8197045128452776,True,True,,,2025-07-29T15:29:45.280592<\/code><\/p>\n\n\n\n<p><strong>\u0421\u043a\u0440\u0438\u043f\u0442 \u0437\u0430\u043c\u0435\u0440\u0430<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env python3\n\"\"\"\nComprehensive PostgreSQL backup performance testing script.\nTests pg_dump with all possible formats and compression levels.\n\"\"\"\n\nimport subprocess\nimport time\nimport os\nimport shutil\nimport json\nimport csv\nfrom datetime import datetime\nfrom pathlib import Path\nimport psycopg2\nimport argparse\n\n\ndef log_with_timestamp(message):\n    \"\"\"Print a message with timestamp.\"\"\"\n    timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n    print(f\"&#91;{timestamp}] {message}\")\n\n\n# PostgreSQL binary path\nPG_BIN_PATH = Path(\".\/postgresql-17\/bin\")\n\n# Database connection parameters\nDB_CONFIG = {\n    \"host\": \"localhost\",\n    \"port\": 7000,\n    \"database\": \"testdb\",\n    \"user\": \"postgres\",\n    \"password\": \"testpassword\",\n}\n\n# Restore database connection parameters\nRESTORE_DB_CONFIG = {\n    \"host\": \"localhost\",\n    \"port\": 7001,\n    \"database\": \"testdb\",\n    \"user\": \"postgres\",\n    \"password\": \"testpassword\",\n}\n\n# Test configurations\nPG_DUMP_FORMATS = &#91;\n    (\"plain\", \"sql\"),\n    (\"custom\", \"dump\"),\n    (\"directory\", \"dir\"),\n    (\"tar\", \"tar\"),\n]\n\nCOMPRESSION_LEVELS = {\n    \"gzip\": &#91;1, 5, 9],\n    \"lz4\": &#91;1, 5, 12],\n    \"zstd\": &#91;1, 5, 15, 22],\n}\n\n# Available compression methods (depends on PostgreSQL version)\nCOMPRESSION_METHODS = &#91;\"none\", \"gzip\", \"lz4\", \"zstd\"]\n\n# Results storage\nresults: list&#91;dict] = &#91;]\n\n\ndef ensure_backup_directory():\n    \"\"\"Ensure backup directory exists and is clean.\"\"\"\n    backup_dir = Path(\".\/backups\")\n    if backup_dir.exists():\n        shutil.rmtree(backup_dir)\n    backup_dir.mkdir(exist_ok=True)\n    return backup_dir\n\n\ndef get_database_size():\n    \"\"\"Get current database size in bytes.\"\"\"\n    try:\n        conn = psycopg2.connect(**DB_CONFIG)\n        cursor = conn.cursor()\n        cursor.execute(\"SELECT pg_database_size('testdb')\")\n        size = cursor.fetchone()&#91;0]\n        cursor.close()\n        conn.close()\n        return size\n    except Exception as e:\n        log_with_timestamp(f\"Error getting database size: {e}\")\n        return 0\n\n\ndef get_restore_database_size():\n    \"\"\"Get restore database size in bytes.\"\"\"\n    try:\n        conn = psycopg2.connect(**RESTORE_DB_CONFIG)\n        cursor = conn.cursor()\n        cursor.execute(\"SELECT pg_database_size('testdb')\")\n        size = cursor.fetchone()&#91;0]\n        cursor.close()\n        conn.close()\n        return size\n    except Exception as e:\n        log_with_timestamp(f\"Error getting restore database size: {e}\")\n        return 0\n\n\ndef clean_restore_database():\n    \"\"\"Clean the restore database by dropping and recreating it.\"\"\"\n    import time\n\n    try:\n        # Connect to postgres database to drop\/create testdb\n        restore_config = RESTORE_DB_CONFIG.copy()\n        restore_config&#91;\"database\"] = \"postgres\"\n\n        conn = psycopg2.connect(**restore_config)\n        conn.autocommit = True\n        cursor = conn.cursor()\n\n        # Terminate any existing connections to testdb with retry logic\n        max_attempts = 5\n        for attempt in range(max_attempts):\n            try:\n                # Get count of active connections first\n                cursor.execute(\"\"\"\n                    SELECT COUNT(*)\n                    FROM pg_stat_activity\n                    WHERE datname = 'testdb' AND pid &lt;> pg_backend_pid()\n                \"\"\")\n                active_connections = cursor.fetchone()&#91;0]\n\n                if active_connections == 0:\n                    break\n\n                log_with_timestamp(\n                    f\"Found {active_connections} active connections to testdb, terminating...\"\n                )\n\n                # Terminate connections\n                cursor.execute(\"\"\"\n                    SELECT pg_terminate_backend(pid)\n                    FROM pg_stat_activity\n                    WHERE datname = 'testdb' AND pid &lt;> pg_backend_pid()\n                \"\"\")\n\n                # Fetch all results to ensure the query completes\n                terminated_pids = cursor.fetchall()\n                log_with_timestamp(f\"Terminated {len(terminated_pids)} connections\")\n\n                # Wait a bit for connections to actually close\n                time.sleep(2)\n\n            except Exception as term_error:\n                log_with_timestamp(\n                    f\"Warning: Error terminating connections (attempt {attempt + 1}): {term_error}\"\n                )\n                if attempt &lt; max_attempts - 1:\n                    time.sleep(1)\n                else:\n                    # Continue anyway, maybe we can still drop the database\n                    break\n\n        # Try to drop database with retry logic\n        drop_attempts = 3\n        for attempt in range(drop_attempts):\n            try:\n                cursor.execute(\"DROP DATABASE IF EXISTS testdb\")\n                log_with_timestamp(\"Database testdb dropped successfully\")\n                break\n            except Exception as drop_error:\n                if (\n                    \"is being accessed by other users\" in str(drop_error)\n                    and attempt &lt; drop_attempts - 1\n                ):\n                    log_with_timestamp(\n                        f\"Database still in use, waiting and retrying... (attempt {attempt + 1})\"\n                    )\n                    time.sleep(3)\n                else:\n                    raise drop_error\n\n        # Create fresh database\n        cursor.execute(\"CREATE DATABASE testdb\")\n\n        cursor.close()\n        conn.close()\n\n        log_with_timestamp(\"\u2713 Restore database cleaned and recreated\")\n        return True\n\n    except Exception as e:\n        log_with_timestamp(f\"\u2717 Error cleaning restore database: {e}\")\n        return False\n\n\ndef wait_for_restore_db():\n    \"\"\"Wait for restore database to be ready.\"\"\"\n    import time\n\n    max_attempts = 30\n    for attempt in range(max_attempts):\n        try:\n            conn = psycopg2.connect(**RESTORE_DB_CONFIG)\n            conn.close()\n            return True\n        except:\n            if attempt &lt; max_attempts - 1:\n                time.sleep(1)\n            else:\n                log_with_timestamp(\"\u2717 Restore database not ready after 30 seconds\")\n                return False\n\n\ndef restore_pg_dump(backup_path, format_name):\n    \"\"\"Restore a pg_dump backup.\"\"\"\n    env = os.environ.copy()\n    env&#91;\"PGPASSWORD\"] = RESTORE_DB_CONFIG&#91;\"password\"]\n\n    if format_name == \"plain\":\n        # Use psql for plain format\n        command = &#91;\n            str(PG_BIN_PATH \/ \"psql.exe\"),\n            \"-h\",\n            RESTORE_DB_CONFIG&#91;\"host\"],\n            \"-p\",\n            str(RESTORE_DB_CONFIG&#91;\"port\"]),\n            \"-U\",\n            RESTORE_DB_CONFIG&#91;\"user\"],\n            \"-d\",\n            RESTORE_DB_CONFIG&#91;\"database\"],\n            \"-f\",\n            str(backup_path),\n            \"-v\",\n            \"ON_ERROR_STOP=1\",\n        ]\n    else:\n        # Use pg_restore for custom, directory, tar formats\n        command = &#91;\n            str(PG_BIN_PATH \/ \"pg_restore.exe\"),\n            \"-h\",\n            RESTORE_DB_CONFIG&#91;\"host\"],\n            \"-p\",\n            str(RESTORE_DB_CONFIG&#91;\"port\"]),\n            \"-U\",\n            RESTORE_DB_CONFIG&#91;\"user\"],\n            \"-d\",\n            RESTORE_DB_CONFIG&#91;\"database\"],\n            \"--verbose\",\n            str(backup_path),\n        ]\n\n        # Add parallel processing for custom and directory formats\n        if format_name in &#91;\"custom\", \"directory\"]:\n            command.extend(&#91;\"-j\", \"4\"])\n        else:\n            # Only add --single-transaction for tar format (not parallel)\n            command.insert(-1, \"--single-transaction\")\n\n    return run_command(command, env=env)\n\n\ndef get_file_size(filepath):\n    \"\"\"Get file or directory size in bytes.\"\"\"\n    if os.path.isfile(filepath):\n        return os.path.getsize(filepath)\n    elif os.path.isdir(filepath):\n        total_size = 0\n        for dirpath, dirnames, filenames in os.walk(filepath):\n            for filename in filenames:\n                file_path = os.path.join(dirpath, filename)\n                total_size += os.path.getsize(file_path)\n        return total_size\n    return 0\n\n\ndef format_size(size_bytes):\n    \"\"\"Format size in GB with 1 decimal place.\"\"\"\n    size_gb = size_bytes \/ (1024.0 * 1024.0 * 1024.0)\n    return f\"{size_gb:.1f} GB\"\n\n\ndef format_minutes(seconds):\n    \"\"\"Format time in minutes with 1 decimal place.\"\"\"\n    minutes = seconds \/ 60.0\n    return f\"{minutes:.1f} mins\"\n\n\ndef run_command(command, timeout=7200, env=None):\n    \"\"\"Run a command and measure execution time.\"\"\"\n    log_with_timestamp(f\"Running: {' '.join(command)}\")\n    start_time = time.time()\n\n    try:\n        # Set environment variables for password\n        if env is None:\n            env = os.environ.copy()\n            env&#91;\"PGPASSWORD\"] = DB_CONFIG&#91;\"password\"]\n\n        result = subprocess.run(\n            command, capture_output=True, text=True, timeout=timeout, env=env\n        )\n\n        end_time = time.time()\n        duration = end_time - start_time\n\n        if result.returncode != 0:\n            log_with_timestamp(f\"Command failed with return code {result.returncode}\")\n            log_with_timestamp(f\"STDERR: {result.stderr}\")\n            return None, duration, result.stderr\n\n        return result, duration, None\n\n    except subprocess.TimeoutExpired:\n        log_with_timestamp(f\"Command timed out after {timeout} seconds\")\n        return None, timeout, \"Command timed out\"\n    except Exception as e:\n        end_time = time.time()\n        duration = end_time - start_time\n        log_with_timestamp(f\"Command failed with exception: {e}\")\n        return None, duration, str(e)\n\n\ndef test_pg_dump(skip_restore=False):\n    \"\"\"Test pg_dump with all format and compression combinations.\"\"\"\n    log_with_timestamp(\"\\n\" + \"=\" * 60)\n    log_with_timestamp(\"TESTING PG_DUMP\")\n    log_with_timestamp(\"=\" * 60)\n\n    backup_dir = Path(\".\/backups\")\n\n    for format_name, extension in PG_DUMP_FORMATS:\n        log_with_timestamp(f\"\\nTesting pg_dump format: {format_name}\")\n\n        # Test without compression\n        test_name = f\"pg_dump_{format_name}_no_compression\"\n        output_path = backup_dir \/ f\"{test_name}.{extension}\"\n\n        command = &#91;\n            str(PG_BIN_PATH \/ \"pg_dump.exe\"),\n            \"-h\",\n            DB_CONFIG&#91;\"host\"],\n            \"-p\",\n            str(DB_CONFIG&#91;\"port\"]),\n            \"-U\",\n            DB_CONFIG&#91;\"user\"],\n            \"-d\",\n            DB_CONFIG&#91;\"database\"],\n            \"-f\",\n            str(output_path),\n            \"--format\",\n            format_name,\n            \"--verbose\",\n        ]\n\n        if format_name == \"directory\":\n            # For directory format, create the directory first\n            output_path.mkdir(exist_ok=True)\n            # Replace the path after -f flag with the directory path\n            for i, item in enumerate(command):\n                if item == \"-f\" and i + 1 &lt; len(command):\n                    command&#91;i + 1] = str(output_path)\n                    break\n            # Add parallel processing for directory format\n            command.extend(&#91;\"-j\", \"4\"])\n\n        # Perform backup\n        result, backup_duration, error = run_command(command)\n\n        if result is not None:\n            backup_size = get_file_size(output_path)\n            db_size = get_database_size()\n            compression_ratio = backup_size \/ db_size if db_size > 0 else 0\n\n            # Clean restore database and perform restore\n            restore_success = False\n            restore_duration = 0\n            restore_error = None\n            restored_db_size = 0\n\n            if not skip_restore:\n                if clean_restore_database() and wait_for_restore_db():\n                    restore_result, restore_duration, restore_error = restore_pg_dump(\n                        output_path, format_name\n                    )\n                    if restore_result is not None and restore_result.returncode == 0:\n                        restore_success = True\n                        restored_db_size = get_restore_database_size()\n            else:\n                restore_success = True  # Mark as successful if skipped\n                restore_error = \"Skipped\"\n\n            results.append(\n                {\n                    \"tool\": \"pg_dump\",\n                    \"format\": format_name,\n                    \"compression_method\": \"none\",\n                    \"compression_level\": 0,\n                    \"backup_duration_seconds\": backup_duration,\n                    \"restore_duration_seconds\": restore_duration,\n                    \"total_duration_seconds\": backup_duration + restore_duration,\n                    \"backup_size_bytes\": backup_size,\n                    \"database_size_bytes\": db_size,\n                    \"restored_db_size_bytes\": restored_db_size,\n                    \"compression_ratio\": compression_ratio,\n                    \"backup_success\": True,\n                    \"restore_success\": restore_success,\n                    \"backup_error\": None,\n                    \"restore_error\": restore_error,\n                    \"timestamp\": datetime.now().isoformat(),\n                }\n            )\n\n            if skip_restore:\n                log_with_timestamp(\n                    f\"\u2713 {test_name}: Backup {format_minutes(backup_duration)}, \"\n                    f\"{format_size(backup_size)}, ratio: {compression_ratio:.3f} (restore skipped)\"\n                )\n            else:\n                log_with_timestamp(\n                    f\"\u2713 {test_name}: Backup {format_minutes(backup_duration)}, Restore {format_minutes(restore_duration)}, \"\n                    f\"{format_size(backup_size)}, ratio: {compression_ratio:.3f}\"\n                )\n        else:\n            results.append(\n                {\n                    \"tool\": \"pg_dump\",\n                    \"format\": format_name,\n                    \"compression_method\": \"none\",\n                    \"compression_level\": 0,\n                    \"backup_duration_seconds\": backup_duration,\n                    \"restore_duration_seconds\": 0,\n                    \"total_duration_seconds\": backup_duration,\n                    \"backup_size_bytes\": 0,\n                    \"database_size_bytes\": get_database_size(),\n                    \"restored_db_size_bytes\": 0,\n                    \"compression_ratio\": 0,\n                    \"backup_success\": False,\n                    \"restore_success\": False,\n                    \"backup_error\": error,\n                    \"restore_error\": \"Backup failed\",\n                    \"timestamp\": datetime.now().isoformat(),\n                }\n            )\n            log_with_timestamp(f\"\u2717 {test_name}: BACKUP FAILED - {error}\")\n\n        # Test with compression (only for formats that support it)\n        if format_name in &#91;\"custom\", \"directory\"]:\n            for compression_method in COMPRESSION_METHODS&#91;1:]:  # Skip 'none'\n                if compression_method == \"gzip\" and format_name == \"directory\":\n                    continue  # Directory format doesn't support gzip compression directly\n\n                compression_levels = COMPRESSION_LEVELS.get(compression_method, &#91;1])\n\n                for level in compression_levels:\n                    test_name = (\n                        f\"pg_dump_{format_name}_{compression_method}_level_{level}\"\n                    )\n                    output_path = backup_dir \/ f\"{test_name}.{extension}\"\n\n                    command = &#91;\n                        str(PG_BIN_PATH \/ \"pg_dump.exe\"),\n                        \"-h\",\n                        DB_CONFIG&#91;\"host\"],\n                        \"-p\",\n                        str(DB_CONFIG&#91;\"port\"]),\n                        \"-U\",\n                        DB_CONFIG&#91;\"user\"],\n                        \"-d\",\n                        DB_CONFIG&#91;\"database\"],\n                        \"-f\",\n                        str(output_path),\n                        \"--format\",\n                        format_name,\n                        \"--verbose\",\n                    ]\n\n                    # Add compression options\n                    if format_name == \"custom\":\n                        if compression_method == \"gzip\":\n                            command.extend(&#91;\"-Z\", str(level)])\n                        else:\n                            command.extend(\n                                &#91;\n                                    \"--compress\",\n                                    f\"{compression_method}:{level}\",\n                                ]\n                            )\n                    elif format_name == \"directory\":\n                        output_path.mkdir(exist_ok=True)\n                        # For directory format, replace the path after -f\n                        for i, item in enumerate(command):\n                            if item == \"-f\" and i + 1 &lt; len(command):\n                                command&#91;i + 1] = str(output_path)\n                                break\n                        if (\n                            compression_method != \"gzip\"\n                        ):  # Directory format supports lz4 and zstd\n                            command.extend(\n                                &#91;\n                                    \"--compress\",\n                                    f\"{compression_method}:{level}\",\n                                ]\n                            )\n                        # Add parallel processing for directory format\n                        command.extend(&#91;\"-j\", \"4\"])\n\n                    # Perform backup\n                    result, backup_duration, error = run_command(command)\n\n                    if result is not None:\n                        backup_size = get_file_size(output_path)\n                        db_size = get_database_size()\n                        compression_ratio = backup_size \/ db_size if db_size > 0 else 0\n\n                        # Clean restore database and perform restore\n                        restore_success = False\n                        restore_duration = 0\n                        restore_error = None\n                        restored_db_size = 0\n\n                        if not skip_restore:\n                            if clean_restore_database() and wait_for_restore_db():\n                                restore_result, restore_duration, restore_error = (\n                                    restore_pg_dump(output_path, format_name)\n                                )\n                                if (\n                                    restore_result is not None\n                                    and restore_result.returncode == 0\n                                ):\n                                    restore_success = True\n                                    restored_db_size = get_restore_database_size()\n                        else:\n                            restore_success = True  # Mark as successful if skipped\n                            restore_error = \"Skipped\"\n\n                        results.append(\n                            {\n                                \"tool\": \"pg_dump\",\n                                \"format\": format_name,\n                                \"compression_method\": compression_method,\n                                \"compression_level\": level,\n                                \"backup_duration_seconds\": backup_duration,\n                                \"restore_duration_seconds\": restore_duration,\n                                \"total_duration_seconds\": backup_duration\n                                + restore_duration,\n                                \"backup_size_bytes\": backup_size,\n                                \"database_size_bytes\": db_size,\n                                \"restored_db_size_bytes\": restored_db_size,\n                                \"compression_ratio\": compression_ratio,\n                                \"backup_success\": True,\n                                \"restore_success\": restore_success,\n                                \"backup_error\": None,\n                                \"restore_error\": restore_error,\n                                \"timestamp\": datetime.now().isoformat(),\n                            }\n                        )\n\n                        if skip_restore:\n                            log_with_timestamp(\n                                f\"\u2713 {test_name}: Backup {format_minutes(backup_duration)}, \"\n                                f\"{format_size(backup_size)}, ratio: {compression_ratio:.3f} (restore skipped)\"\n                            )\n                        else:\n                            log_with_timestamp(\n                                f\"\u2713 {test_name}: Backup {format_minutes(backup_duration)}, Restore {format_minutes(restore_duration)}, \"\n                                f\"{format_size(backup_size)}, ratio: {compression_ratio:.3f}\"\n                            )\n                    else:\n                        results.append(\n                            {\n                                \"tool\": \"pg_dump\",\n                                \"format\": format_name,\n                                \"compression_method\": compression_method,\n                                \"compression_level\": level,\n                                \"backup_duration_seconds\": backup_duration,\n                                \"restore_duration_seconds\": 0,\n                                \"total_duration_seconds\": backup_duration,\n                                \"backup_size_bytes\": 0,\n                                \"database_size_bytes\": get_database_size(),\n                                \"restored_db_size_bytes\": 0,\n                                \"compression_ratio\": 0,\n                                \"backup_success\": False,\n                                \"restore_success\": False,\n                                \"backup_error\": error,\n                                \"restore_error\": \"Backup failed\",\n                                \"timestamp\": datetime.now().isoformat(),\n                            }\n                        )\n                        log_with_timestamp(f\"\u2717 {test_name}: BACKUP FAILED - {error}\")\n\n\ndef save_tabular_results():\n    \"\"\"Save tabular test results to a CSV file.\"\"\"\n    timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n\n    # Save as tabular CSV\n    csv_file = f\"backup_performance_tabular_{timestamp}.csv\"\n\n    with open(csv_file, \"w\", newline=\"\") as f:\n        writer = csv.writer(f)\n\n        # Write header\n        writer.writerow(\n            &#91;\n                \"format\",\n                \"backup duration mins\",\n                \"restore duration mins\",\n                \"total duration mins\",\n                \"backup size GB\",\n                \"db size GB\",\n                \"restored DB size GB\",\n                \"db size % from original\",\n            ]\n        )\n\n        # Write data rows\n        for r in sorted(\n            results,\n            key=lambda x: (\n                x&#91;\"format\"],\n                x&#91;\"compression_method\"],\n                x&#91;\"compression_level\"],\n            ),\n        ):\n            if not r&#91;\"backup_success\"]:\n                continue\n\n            format_str = f\"{r&#91;'format']} {r&#91;'compression_method']}\"\n            if r&#91;\"compression_method\"] != \"none\":\n                format_str += f\" {r&#91;'compression_level']}\"\n\n            backup_mins = round(r&#91;\"backup_duration_seconds\"] \/ 60.0, 1)\n            restore_mins = round(r&#91;\"restore_duration_seconds\"] \/ 60.0, 1)\n            total_mins = round(backup_mins + restore_mins, 1)\n\n            backup_gb = round(r&#91;\"backup_size_bytes\"] \/ (1024.0 * 1024.0 * 1024.0), 1)\n            db_gb = round(r&#91;\"database_size_bytes\"] \/ (1024.0 * 1024.0 * 1024.0), 1)\n            restored_db_gb = round(\n                r&#91;\"restored_db_size_bytes\"] \/ (1024.0 * 1024.0 * 1024.0), 1\n            )\n\n            db_size_percent = round((backup_gb \/ db_gb) * 100) if db_gb > 0 else 0\n\n            writer.writerow(\n                &#91;\n                    format_str,\n                    backup_mins,\n                    restore_mins,\n                    total_mins,\n                    backup_gb,\n                    db_gb,\n                    restored_db_gb,\n                    db_size_percent,\n                ]\n            )\n\n    print(f\"Tabular results saved to {csv_file}\")\n\n\ndef save_results():\n    \"\"\"Save test results to JSON and CSV files.\"\"\"\n    timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n\n    # Save as JSON\n    json_file = f\"backup_performance_results_{timestamp}.json\"\n    with open(json_file, \"w\") as f:\n        json.dump(results, f, indent=2)\n    print(f\"\\nResults saved to {json_file}\")\n\n    # Save as CSV\n    csv_file = f\"backup_performance_results_{timestamp}.csv\"\n    if results:\n        fieldnames = results&#91;0].keys()\n        with open(csv_file, \"w\", newline=\"\") as f:\n            writer = csv.DictWriter(f, fieldnames=fieldnames)\n            writer.writeheader()\n            writer.writerows(results)\n        print(f\"Results saved to {csv_file}\")\n\n    # Save tabular results\n    save_tabular_results()\n\n\ndef print_summary():\n    \"\"\"Print a summary of test results.\"\"\"\n    print(\"\\n\" + \"=\" * 80)\n    print(\"PERFORMANCE TEST SUMMARY\")\n    print(\"=\" * 80)\n\n    if not results:\n        print(\"No results to display.\")\n        return\n\n    # Check if any tests had restore attempts\n    restore_attempted = any(\n        r&#91;\"restore_error\"] != \"Skipped\" for r in results if r&#91;\"backup_success\"]\n    )\n\n    if restore_attempted:\n        successful_tests = &#91;\n            r for r in results if r&#91;\"backup_success\"] and r&#91;\"restore_success\"]\n        ]\n        backup_only_success = &#91;\n            r\n            for r in results\n            if r&#91;\"backup_success\"]\n            and not r&#91;\"restore_success\"]\n            and r&#91;\"restore_error\"] != \"Skipped\"\n        ]\n        failed_tests = &#91;r for r in results if not r&#91;\"backup_success\"]]\n\n        print(f\"Total tests: {len(results)}\")\n        print(f\"Complete success (backup + restore): {len(successful_tests)}\")\n        print(f\"Backup only success: {len(backup_only_success)}\")\n        print(f\"Complete failures: {len(failed_tests)}\")\n\n        if successful_tests:\n            print(f\"\\nBest compression ratios (complete success):\")\n            best_compression = sorted(\n                successful_tests, key=lambda x: x&#91;\"compression_ratio\"]\n            )&#91;:5]\n            for test in best_compression:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Ratio: {test&#91;'compression_ratio']:.3f}, Backup: {format_minutes(test&#91;'backup_duration_seconds'])}, \"\n                    f\"Restore: {format_minutes(test&#91;'restore_duration_seconds'])}, Total: {format_minutes(test&#91;'total_duration_seconds'])}\"\n                )\n\n            print(f\"\\nFastest total time (backup + restore):\")\n            fastest_total = sorted(\n                successful_tests, key=lambda x: x&#91;\"total_duration_seconds\"]\n            )&#91;:5]\n            for test in fastest_total:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Total: {format_minutes(test&#91;'total_duration_seconds'])}, Ratio: {test&#91;'compression_ratio']:.3f}\"\n                )\n\n            print(f\"\\nFastest backup times:\")\n            fastest_backup = sorted(\n                successful_tests, key=lambda x: x&#91;\"backup_duration_seconds\"]\n            )&#91;:5]\n            for test in fastest_backup:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Backup: {format_minutes(test&#91;'backup_duration_seconds'])}, Restore: {format_minutes(test&#91;'restore_duration_seconds'])}\"\n                )\n\n            print(f\"\\nFastest restore times:\")\n            fastest_restore = sorted(\n                successful_tests, key=lambda x: x&#91;\"restore_duration_seconds\"]\n            )&#91;:5]\n            for test in fastest_restore:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Restore: {format_minutes(test&#91;'restore_duration_seconds'])}, Backup: {format_minutes(test&#91;'backup_duration_seconds'])}\"\n                )\n\n        if backup_only_success:\n            print(f\"\\nBackup-only successes (restore failed):\")\n            for test in backup_only_success:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Backup: {format_minutes(test&#91;'backup_duration_seconds'])}, Restore Error: {test&#91;'restore_error']}\"\n                )\n\n        if failed_tests:\n            print(f\"\\nComplete failures:\")\n            for test in failed_tests:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Backup Error: {test&#91;'backup_error']}\"\n                )\n    else:\n        # Restore was skipped for all tests\n        successful_tests = &#91;r for r in results if r&#91;\"backup_success\"]]\n        failed_tests = &#91;r for r in results if not r&#91;\"backup_success\"]]\n\n        print(f\"Total tests: {len(results)}\")\n        print(f\"Successful backups: {len(successful_tests)}\")\n        print(f\"Failed backups: {len(failed_tests)}\")\n        print(\"Note: Restore tests were skipped\")\n\n        if successful_tests:\n            print(f\"\\nBest compression ratios:\")\n            best_compression = sorted(\n                successful_tests, key=lambda x: x&#91;\"compression_ratio\"]\n            )&#91;:5]\n            for test in best_compression:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Ratio: {test&#91;'compression_ratio']:.3f}, Backup: {format_minutes(test&#91;'backup_duration_seconds'])}\"\n                )\n\n            print(f\"\\nFastest backup times:\")\n            fastest_backup = sorted(\n                successful_tests, key=lambda x: x&#91;\"backup_duration_seconds\"]\n            )&#91;:5]\n            for test in fastest_backup:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Backup: {format_minutes(test&#91;'backup_duration_seconds'])}, Ratio: {test&#91;'compression_ratio']:.3f}\"\n                )\n\n        if failed_tests:\n            print(f\"\\nFailed backups:\")\n            for test in failed_tests:\n                print(\n                    f\"  {test&#91;'tool']} {test&#91;'format']} {test&#91;'compression_method']}:{test&#91;'compression_level']} - \"\n                    f\"Backup Error: {test&#91;'backup_error']}\"\n                )\n\n\ndef print_tabular_report():\n    \"\"\"Print a tabular report of results similar to the requested format.\"\"\"\n    if not results:\n        print(\"No results to display.\")\n        return\n\n    print(\"\\n\" + \"=\" * 120)\n    print(\"TABULAR PERFORMANCE REPORT\")\n    print(\"=\" * 120)\n\n    # Print header\n    print(\n        f\"{'format':&lt;20}{'backup duration mins':&lt;20}{'restore duration mins':&lt;25}{'total duration mins':&lt;20}\"\n        f\"{'backup size GB':&lt;15}{'db size GB':&lt;15}{'restored DB size GB':&lt;20}{'db size % from original':&lt;25}\"\n    )\n    print(\"-\" * 120)\n\n    # Group results by format and compression method+level\n    for r in sorted(\n        results,\n        key=lambda x: (x&#91;\"format\"], x&#91;\"compression_method\"], x&#91;\"compression_level\"]),\n    ):\n        if not r&#91;\"backup_success\"]:\n            continue\n\n        format_str = f\"{r&#91;'format']} {r&#91;'compression_method']}\"\n        if r&#91;\"compression_method\"] != \"none\":\n            format_str += f\" {r&#91;'compression_level']}\"\n\n        backup_mins = r&#91;\"backup_duration_seconds\"] \/ 60.0\n        restore_mins = r&#91;\"restore_duration_seconds\"] \/ 60.0\n        total_mins = backup_mins + restore_mins\n\n        backup_gb = r&#91;\"backup_size_bytes\"] \/ (1024.0 * 1024.0 * 1024.0)\n        db_gb = r&#91;\"database_size_bytes\"] \/ (1024.0 * 1024.0 * 1024.0)\n        restored_db_gb = r&#91;\"restored_db_size_bytes\"] \/ (1024.0 * 1024.0 * 1024.0)\n\n        db_size_percent = (backup_gb \/ db_gb) * 100 if db_gb > 0 else 0\n\n        print(\n            f\"{format_str:&lt;20}{backup_mins:.1f}{'':&lt;14}{restore_mins:.1f}{'':&lt;19}{total_mins:.1f}{'':&lt;14}\"\n            f\"{backup_gb:.1f}{'':&lt;9}{db_gb:.1f}{'':&lt;9}{restored_db_gb:.1f}{'':&lt;14}{db_size_percent:.0f}\"\n        )\n\n\ndef main():\n    \"\"\"Main function to run pg_dump backup performance tests.\"\"\"\n    parser = argparse.ArgumentParser(\n        description=\"PostgreSQL pg_dump backup performance testing\"\n    )\n    parser.add_argument(\n        \"--skip-gzip\", action=\"store_true\", help=\"Skip gzip compression tests\"\n    )\n    parser.add_argument(\n        \"--skip-lz4\", action=\"store_true\", help=\"Skip lz4 compression tests\"\n    )\n    parser.add_argument(\n        \"--skip-zstd\", action=\"store_true\", help=\"Skip zstd compression tests\"\n    )\n    parser.add_argument(\n        \"--skip-restore\", action=\"store_true\", help=\"Skip restore tests\"\n    )\n\n    args = parser.parse_args()\n\n    # Filter compression methods based on skip flags\n    global COMPRESSION_METHODS\n    filtered_compression_methods = &#91;\"none\"]  # Always include 'none'\n\n    if not args.skip_gzip:\n        filtered_compression_methods.append(\"gzip\")\n    if not args.skip_lz4:\n        filtered_compression_methods.append(\"lz4\")\n    if not args.skip_zstd:\n        filtered_compression_methods.append(\"zstd\")\n\n    COMPRESSION_METHODS = filtered_compression_methods\n\n    log_with_timestamp(\"PostgreSQL pg_dump Backup Performance Testing\")\n    log_with_timestamp(\"=\" * 60)\n    log_with_timestamp(\n        f\"Source Database: {DB_CONFIG&#91;'database']} on {DB_CONFIG&#91;'host']}:{DB_CONFIG&#91;'port']}\"\n    )\n    log_with_timestamp(\n        f\"Restore Database: {RESTORE_DB_CONFIG&#91;'database']} on {RESTORE_DB_CONFIG&#91;'host']}:{RESTORE_DB_CONFIG&#91;'port']}\"\n    )\n    log_with_timestamp(f\"Database size: {format_size(get_database_size())}\")\n    log_with_timestamp(f\"Compression methods: {', '.join(COMPRESSION_METHODS)}\")\n    log_with_timestamp(\n        f\"Restore tests: {'Skipped' if args.skip_restore else 'Enabled'}\"\n    )\n    log_with_timestamp(f\"Test started: {datetime.now()}\")\n\n    # Ensure backup directory is ready\n    backup_dir = ensure_backup_directory()\n    log_with_timestamp(f\"Backup directory: {backup_dir.absolute()}\")\n\n    try:\n        # Run pg_dump tests only\n        test_pg_dump(skip_restore=args.skip_restore)\n\n        # Save and display results\n        save_results()\n        print_summary()\n        print_tabular_report()\n\n    except KeyboardInterrupt:\n        log_with_timestamp(\"\\nTest interrupted by user\")\n        if results:\n            save_results()\n            print_summary()\n            print_tabular_report()\n    except Exception as e:\n        log_with_timestamp(f\"\\nTest failed with error: {e}\")\n        if results:\n            save_results()\n            print_summary()\n            print_tabular_report()\n\n    log_with_timestamp(f\"\\nTest completed: {datetime.now()}\")\n\n\nif __name__ == \"__main__\":\n    main()<\/code><\/pre>\n\n\n\n<p>\u0418\u0437 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u043c\u043d\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0443\u0431\u0440\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u043e&nbsp;<code>zstd<\/code>&nbsp;\u0441 \u0443\u0440\u043e\u0432\u043d\u0435\u043c \u0441\u0436\u0430\u0442\u0438\u044f 15 \u0438&nbsp;<code>zstd<\/code>&nbsp;\u0441 \u0443\u0440\u043e\u0432\u043d\u0435\u043c \u0441\u0436\u0430\u0442\u0438\u044f 22. \u041e\u043d\u0438 \u0441\u0438\u043b\u044c\u043d\u043e \u0438\u0441\u043a\u0430\u0436\u0430\u043b\u0438 \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u0438\u0437-\u0437\u0430 \u0434\u043e\u043b\u0433\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u0436\u0430\u0442\u0438\u044f, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0434\u0430\u0432\u0430\u043b\u0438 \u043e\u0449\u0443\u0442\u0438\u043c\u043e\u0433\u043e \u043f\u0440\u0438\u0440\u043e\u0441\u0442\u0430 \u0432 \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u0438.\u0413\u0440\u0430\u0444\u0438\u043a\u0438 \u0441 zstd 15 \u0438 22<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/caa\/bc3\/c89\/caabc3c8908744f9179e082558d8acf3.svg\" alt=\"\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/438\/aea\/0e7\/438aea0e71a7da59d4ed638173d84ccc.svg\" alt=\"\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/84e\/070\/bf4\/84e070bf4202d93aa6c697318cfb9a5b.svg\" alt=\"\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ec2\/bd1\/e8a\/ec2bd1e8adc8b4cfd90bc2f71f5bb1bd.svg\" alt=\"\"\/><\/figure>\n\n\n\n<p>\u0418\u0442\u0430\u043a, \u0437\u0430\u043c\u0435\u0440\u044b.<\/p>\n\n\n\n<p>\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 (\u043c\u0435\u043d\u044c\u0448\u0435 \u2014 \u043b\u0443\u0447\u0448\u0435):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bf2\/fe4\/832\/bf2fe4832bdf195a508cbffe0db5a246.svg\" alt=\"\"\/><\/figure>\n\n\n\n<p>\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 (\u043c\u0435\u043d\u044c\u0448\u0435 \u2014 \u043b\u0443\u0447\u0448\u0435):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/36c\/4b2\/cf6\/36c4b2cf62489d40349f9623b42dd603.svg\" alt=\"\"\/><\/figure>\n\n\n\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0437 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 (\u043c\u0435\u043d\u044c\u0448\u0435 \u2014 \u043b\u0443\u0447\u0448\u0435):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0aa\/16d\/abe\/0aa16dabe538e3c730dd7e2c0229c201.svg\" alt=\"\"\/><\/figure>\n\n\n\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0432 % \u043e\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u0411\u0414 (\u043c\u0435\u043d\u044c\u0448\u0435 \u2014 \u043b\u0443\u0447\u0448\u0435):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5e3\/177\/0f2\/5e31770f26dae60d75abf15699fc2a15.svg\" alt=\"\"\/><\/figure>\n\n\n\n<p><a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u0412\u044b\u0432\u043e\u0434\u044b, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432<\/h2>\n\n\n\n<p>\u041f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u044b\u0432\u043e\u0434\u044b, \u0445\u043e\u0447\u0443 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u0436\u043d\u044b\u0439 \u0434\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440:&nbsp;<strong>\u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043b\u0441\u044f \u043d\u0430 \u0441\u0438\u043d\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u00ab\u0431\u043e\u0435\u0432\u044b\u043c\u0438\u00bb \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f<\/strong>. \u0421 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0442\u0435\u043d\u0434\u0435\u043d\u0446\u0438\u0435\u0439, \u043d\u043e \u0431\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u0440\u0430\u0437\u043d\u0438\u0446\u0435\u0439 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/p>\n\n\n\n<p>\u041f\u043e \u0438\u0442\u043e\u0433\u0443 \u0437\u0430\u043c\u0435\u0440\u043e\u0432 \u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u043d\u0430 \u0441\u0438\u043d\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043d\u0435\u0442 \u0440\u0430\u0434\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0442\u043b\u0438\u0447\u0438\u044f \u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u043c\u0435\u0436\u0434\u0443 plain \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c, \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438. \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e plain, \u0430 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u2014 \u0435\u0449\u0451 \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u0430\u043c\u0443 \u043a\u043e\u043f\u0438\u044e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e.<\/p>\n\n\n\n<p>\u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0438 plain \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 ~30%. \u041c\u0435\u0436\u0434\u0443 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0435\u0449\u0451 \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c ~20%. \u0420\u0438\u0441\u043a\u043d\u0443 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c, \u0447\u0442\u043e \u0432 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u043b\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0442\u0430\u0431\u043b\u0438\u0446 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u2014 \u0442\u043e\u0433\u0434\u0430 \u0431\u044b \u043e\u0442\u0440\u044b\u0432 \u043c\u0435\u0436\u0434\u0443 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u043c\u0438 \u0431\u044b\u043b \u0431\u044b \u043a\u0440\u0430\u0442\u043d\u044b\u043c.<\/p>\n\n\n\n<p>\u0418\u0442\u0430\u043a, \u044f \u043c\u043e\u0433\u0443 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u044b, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0437\u0430\u043c\u0435\u0440\u043e\u0432:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u0421\u0430\u043c\u044b\u0439 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u2014 \u0432 \u0432\u0438\u0434\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438.<\/strong><br><br>\u041a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 plain \u0438 tar \u043f\u043e \u043e\u0431\u0449\u0435\u043c\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u0438 \u043b\u044e\u0431\u044b\u0445 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u0445. \u0424\u043e\u0440\u043c\u0430\u0442 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 custom \u043f\u0440\u0438 \u043b\u044e\u0431\u044b\u0445 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u0445. \u0415\u0441\u043b\u0438 \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c, \u0440\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f.<br><br>\u041f\u0440\u0438\u0447\u0451\u043c \u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u0435\u0435, \u0447\u0435\u043c \u0432 2 \u0440\u0430\u0437\u0430. \u0427\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u043e, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f, \u0447\u0442\u043e\u00a0\u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0447\u0430\u0449\u0435, \u0447\u0435\u043c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0438\u0437 \u043d\u0438\u0445.<\/li>\n\n\n\n<li><strong>\u0421\u0430\u043c\u044b\u043c \u0432\u044b\u0433\u043e\u0434\u043d\u044b\u043c \u043f\u043e \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044e &#171;\u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0441\u0436\u0430\u0442\u0438\u044f&#187; \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f zstd \u0441 \u0443\u0440\u043e\u0432\u043d\u0435\u043c \u0441\u0436\u0430\u0442\u0438\u044f 5.<\/strong><br><br>\u041f\u043e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0435\u0433\u043e \u043e\u0431\u0433\u043e\u043d\u044f\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0431\u0435\u0437 \u0441\u0436\u0430\u0442\u0438\u044f. \u041f\u043e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043d \u0432 \u0441\u0440\u0435\u0434\u043d\u0435\u043c \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432 \u043d\u0430 ~4%. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0438\u043c\u0435\u0435\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u0436\u0430\u0442\u0438\u0435, \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c\u043e\u0435 \u0441 gzip \u0441 \u0443\u0440\u043e\u0432\u043d\u0435\u043c \u0441\u0436\u0430\u0442\u0438\u044f 9. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043e\u0431\u0445\u043e\u0434\u044f \u0435\u0433\u043e \u043f\u043e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u043d\u0430 ~18%. \u0421 \u0443\u0447\u0451\u0442\u043e\u043c \u043f\u043e\u0433\u0440\u0435\u0448\u043d\u043e\u0441\u0442\u0438 \u043d\u0430 \u0441\u0438\u043d\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0440\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f.<\/li>\n\n\n\n<li><strong>zstd 15 \u0438 zstd 22 \u043e\u043a\u0430\u0437\u0430\u043b\u0438\u0441\u044c \u0441\u0430\u043c\u044b\u043c\u0438 \u043d\u0435\u0432\u044b\u0433\u043e\u0434\u043d\u044b\u043c\u0438<\/strong>\u00a0\u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0442\u0435\u0441\u0442\u0435. \u041e\u043d\u0438 \u0434\u0430\u043b\u0438 \u0443\u0440\u043e\u0432\u043d\u0438 \u0441\u0436\u0430\u0442\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a\u0438\u0435 \u0436\u0435, \u043a\u0430\u043a \u0438 gzip 9, \u043d\u043e \u0432\u044b\u0434\u0430\u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u0432 2 \u0438 8 \u0440\u0430\u0437 \u0431\u043e\u043b\u044c\u0448\u0435\u0435.<br><br>\u0414\u0443\u043c\u0430\u044e, \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0431\u0430\u0437\u044b \u0445\u043e\u0442\u044f \u0431\u044b \u0432 1\u0422\u0411 \u0431\u0435\u0437 \u0441\u0438\u043d\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0445\u043e\u043b\u043e\u0434\u043d\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u2014 \u043e\u043d\u0438 \u043f\u043e\u043a\u0430\u0436\u0443\u0442 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0434\u0440\u0443\u0433\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438 \u043c\u043e\u0433\u0443\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u0440\u0435\u043d\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u043c\u0438 (\u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 \u0441\u043e\u0442\u043d\u044f\u043c\u0438 \u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0445 \u0434\u043e\u043b\u0433\u043e).<\/li>\n<\/ul>\n\n\n\n<p><a target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n\n\n\n<p>\u0414\u043b\u044f \u043c\u0435\u043d\u044f \u0437\u0430\u043c\u0435\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b, \u0447\u0442\u043e \u0441\u0430\u043c\u044b\u043c \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u0434 \u043c\u043e\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0441\u043e \u0441\u0436\u0430\u0442\u0438\u0435\u043c&nbsp;<code>zstd<\/code>&nbsp;\u0438 \u0443\u0440\u043e\u0432\u043d\u0435\u043c \u0441\u0436\u0430\u0442\u0438\u044f 5. \u042f \u043f\u043e\u043b\u0443\u0447\u0430\u044e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0443\u043c\u043c\u0430\u0440\u043d\u0443\u044e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0440\u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0436\u0430\u0442\u0438\u0438 \u0438 \u0438\u043c\u0435\u044f \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0435\u0434\u0438\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438.<\/p>\n\n\n\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u0432 \u043f\u0440\u043e\u0435\u043a\u0442 zstd 5 \u0432\u043c\u0435\u0441\u0442\u043e gzip 6, \u0440\u0430\u0437\u043c\u0435\u0440 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043b\u0441\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0434\u0432\u043e\u0435 \u043f\u0440\u0438 \u0447\u0443\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043f\u0438\u0438. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0441\u0438\u043d\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c 4.7 \u0413\u0411 \u0441\u0436\u0430\u043b\u0430\u0441\u044c \u0434\u043e 276\u041c\u0411 (\u0432 17 \u0440\u0430\u0437!):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/747\/b85\/047\/747b850475da7c9f159283abcbf83910.png\" alt=\"\u0421\u043d\u0438\u0437\u0443 gzip, \u0441\u0432\u0435\u0440\u0445\u0443 zstd\" title=\"\u0421\u043d\u0438\u0437\u0443 gzip, \u0441\u0432\u0435\u0440\u0445\u0443 zstd\"\/><figcaption class=\"wp-element-caption\">\u0421\u043d\u0438\u0437\u0443 gzip, \u0441\u0432\u0435\u0440\u0445\u0443 zstd<\/figcaption><\/figure>\n\n\n\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0439 \u0442\u0435\u0441\u0442 \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0442\u0435\u043c, \u043a\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u0438\u043b\u0438 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e \u0434\u0430\u043c\u043f\u0438\u0442 \u0431\u0430\u0437\u044b \u0447\u0435\u0440\u0435\u0437&nbsp;<code>pg_dump<\/code>&nbsp;\u0441\u043a\u0440\u0438\u043f\u0442\u0430\u043c\u0438. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c, \u044f \u043f\u0440\u043e\u0432\u0435\u0434\u0443 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0442\u0435\u0441\u0442, \u043d\u043e \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e\u043c \u043e\u0431\u044a\u0435\u043c\u0435 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n\n\n\n<p>\u0418 \u0435\u0449\u0451 \u0440\u0430\u0437 \u043d\u0430\u043f\u043e\u043c\u043d\u044e: \u0435\u043b\u0438 \u0432\u0430\u043c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439, \u0443 \u043c\u0435\u043d\u044f \u0435\u0441\u0442\u044c&nbsp;<a href=\"https:\/\/postgresus.com\/\" rel=\"noreferrer noopener\" target=\"_blank\">open source \u043f\u0440\u043e\u0435\u043a\u0442<\/a>&nbsp;\u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438. \u0411\u0443\u0434\u0443 \u043a\u0440\u0430\u0439\u043d\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u0442\u0435\u043b\u0435\u043d \u0437\u0432\u0435\u0437\u0434\u0435 \u043d\u0430&nbsp;<a href=\"https:\/\/github.com\/RostislavDugin\/postgresus\" rel=\"noreferrer noopener\" target=\"_blank\">GitHub<\/a>&nbsp;\u2764\ufe0f, \u043f\u0435\u0440\u0432\u044b\u0435 \u0437\u0432\u0451\u0437\u0434\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0442\u044f\u0436\u0435\u043b\u043e.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u042f \u0441\u0434\u0435\u043b\u0430\u043b \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e \u0447\u0435\u0440\u0435\u0437&nbsp;pg_dump&nbsp;\u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0441\u044f \u0438\u0437 \u043d\u0435\u0451 21 \u0440\u0430\u0437\u0430. \u0420\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 \u0434\u0435\u043b\u0430\u043b \u0432 4 \u0440\u0430\u0437\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0442 1 \u0434\u043e 7 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0441\u0436\u0430\u0442\u0438\u044f \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0417\u0430\u043f\u0438\u0441\u0430\u043b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0438 \u0441\u0434\u0435\u043b\u0430\u043b \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0438\u0434\u043e\u0432, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1869,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[389,88,531,17,480],"class_list":["post-1864","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-likbez","tag-backup","tag-databases","tag-pg_dump","tag-postgresql","tag-480","entry-card--wide"],"_links":{"self":[{"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/posts\/1864","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/olvas.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1864"}],"version-history":[{"count":6,"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/posts\/1864\/revisions"}],"predecessor-version":[{"id":1875,"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/posts\/1864\/revisions\/1875"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/olvas.dev\/index.php?rest_route=\/wp\/v2\/media\/1869"}],"wp:attachment":[{"href":"https:\/\/olvas.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1864"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/olvas.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1864"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/olvas.dev\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1864"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}