SlideShare a Scribd company logo
Michael Glaesemann

   myYearbook.com

michael.glaesemann@myyearbook.com
We were somewhere around Barstow
on the edge of the desert
when the drugs began to take hold…
Visualizing Postgres
Visualizing
 Postgres
PGCon 2009
  Ottawa
2009-05-21
What’s going
 on here?
measure
explain-analyze.info
statistics
rocket
science
Visualizing Postgres
Tufte
7
ANALYZE
 pg_stats
 pg_class
cpu
memory
  io
snmp
           rrd
         Staplr
http://area51.myyearbook.com
Visualizing Postgres
Visualizing Postgres
overview
bloat
pg_database_size
pg_relation_size
pg_total_relation_size
pg_column_size
pg_size_pretty
bloat report
CREATE VIEW utility.index_byte_sizes AS
SELECT rel.oid AS relid, pg_index.indexrelid, pg_namespace.nspname, rel.relname, idx.relname AS indexrelname,
       pg_index.indisunique AS is_key,
       ((ceil(idx.reltuples
                 * ((constants.index_tuple_header_size
                      + constants.item_id_data_size
                      + CASE WHEN (COALESCE(SUM(CASE WHEN statts.staattnotnull THEN 0 ELSE 1 END), 0::BIGINT)
                                    + ((SELECT COALESCE(SUM(CASE WHEN atts.attnotnull THEN 0 ELSE 1 END), 0::BIGINT)
                                          FROM pg_attribute atts
                                          JOIN (SELECT pg_index.indkey[the.i] AS attnum
                                                   FROM generate_series(0, pg_index.indnatts - 1) the(i)) cols
                                                     ON atts.attnum = cols.attnum
                                                   WHERE atts.attrelid = pg_index.indrelid))) > 0
                               THEN (SELECT the.null_bitmap_size + constants.max_align
                                              - CASE WHEN (the.null_bitmap_size % constants.max_align) = 0 THEN constants.max_align
                                                     ELSE the.null_bitmap_size % constants.max_align END
                                       FROM (VALUES (pg_index.indnatts / 8
                                                       + CASE WHEN (pg_index.indnatts % 8) = 0 THEN 0 ELSE 1 END)) the(null_bitmap_size))
                               ELSE 0 END)::DOUBLE PRECISION
                    + COALESCE(SUM(statts.stawidth::DOUBLE PRECISION * (1::DOUBLE PRECISION - statts.stanullfrac)), 0::DOUBLE PRECISION)
                    + COALESCE((SELECT SUM(atts.stawidth::DOUBLE PRECISION * (1::DOUBLE PRECISION - atts.stanullfrac))
                                  FROM pg_statistic atts
                                  JOIN (SELECT pg_index.indkey[the.i] AS attnum
                                          FROM generate_series(0, pg_index.indnatts - 1) the(i)) cols
                                    ON atts.staattnum = cols.attnum
                                  WHERE atts.starelid = pg_index.indrelid), 0::DOUBLE PRECISION))
                 / (constants.block_size - constants.page_header_data_size::NUMERIC - constants.special_space::NUMERIC)::DOUBLE PRECISION)
           + constants.index_metadata_pages::DOUBLE PRECISION)
         * constants.block_size::DOUBLE PRECISION)::BIGINT AS ideal_idxsize,
       (idx.relpages::NUMERIC * constants.block_size)::BIGINT AS idxsize
  FROM pg_index
  JOIN pg_class idx ON pg_index.indexrelid = idx.oid
  JOIN pg_class rel ON pg_index.indrelid = rel.oid
  JOIN pg_namespace ON idx.relnamespace = pg_namespace.oid
  LEFT JOIN (SELECT pg_statistic.starelid, pg_statistic.staattnum,
                     pg_statistic.stanullfrac, pg_statistic.stawidth,
                     pg_attribute.attnotnull AS staattnotnull
                FROM pg_statistic
                JOIN pg_attribute ON pg_statistic.starelid = pg_attribute.attrelid
                                     AND pg_statistic.staattnum = pg_attribute.attnum) statts
    ON statts.starelid = idx.oid
  CROSS JOIN (SELECT current_setting('block_size'::TEXT)::NUMERIC AS block_size,
                      CASE WHEN substring(version(), 12, 3) = ANY (ARRAY['8.0'::TEXT, '8.1'::TEXT, '8.2'::TEXT]) THEN 27
                           ELSE 23 END AS tuple_header_size,
                      CASE WHEN version() ~ 'mingw32'::TEXT THEN 8
                           ELSE 4 END AS max_align,
                      8 AS index_tuple_header_size,
                      4 AS item_id_data_size,
                      24 AS page_header_data_size,
                      0 AS special_space,
                      1 AS index_metadata_pages) constants
  GROUP BY pg_namespace.nspname, rel.relname, rel.oid, idx.relname, idx.reltuples, idx.relpages,
           pg_index.indexrelid, pg_index.indrelid, pg_index.indkey, pg_index.indnatts, pg_index.indisunique,
           constants.block_size, constants.tuple_header_size, constants.max_align, constants.index_tuple_header_size,
           constants.item_id_data_size, constants.page_header_data_size, constants.index_metadata_pages, constants.special_space;
SELECT total_relsize_bytes, replace(pg_size_pretty(total_relsize_bytes), 'bytes', 'B') AS total_relsize,
       relsize_bytes, replace(pg_size_pretty(relsize_bytes), 'bytes', 'B') AS relsize,
       free_space_bytes, replace(pg_size_pretty(free_space_bytes), 'bytes', 'B') AS free_space,
       (table_byte_sizes.free_space_bytes::numeric / table_byte_sizes.relsize_bytes::numeric)::numeric(4,3) AS bloat_rate,
       idxsize_bytes, replace(pg_size_pretty(idxsize_bytes), 'bytes', 'B') AS idxsize,
       (idxsize_bytes::numeric / total_relsize_bytes)::numeric(4,3) AS index_rate,
       toast_relsize_bytes, replace(pg_size_pretty(toast_relsize_bytes), 'bytes', 'B') AS toast_relsize,
       toast_idxsize_bytes, replace(pg_size_pretty(toast_idxsize_bytes), 'bytes', 'B') AS toast_idxsize,

       key_idxsize_bytes, replace(pg_size_pretty(key_idxsize_bytes), 'bytes', 'B') AS key_idxsize,
       CASE WHEN key_idxsize_bytes - ideal_key_idxsize_bytes < 0 THEN 0
            ELSE key_idxsize_bytes - ideal_key_idxsize_bytes END AS free_key_idxsize_bytes,
       replace(pg_size_pretty(CASE WHEN key_idxsize_bytes - ideal_key_idxsize_bytes < 0 THEN 0
                              ELSE key_idxsize_bytes - ideal_key_idxsize_bytes END), 'bytes', 'B') AS free_key_idxsize,
       (CASE WHEN key_idxsize_bytes = 0
                  OR key_idxsize_bytes - ideal_key_idxsize_bytes < 0 THEN 0
             ELSE (key_idxsize_bytes - ideal_key_idxsize_bytes)::numeric / key_idxsize_bytes END)::numeric(4,3) AS key_idx_bloat_rate,

      nonkey_idxsize_bytes, replace(pg_size_pretty(nonkey_idxsize_bytes), 'bytes', 'B') AS nonkey_idxsize,
      CASE WHEN nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes < 0 THEN 0
           ELSE nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes END AS free_nonkey_idxsize_bytes,
      replace(pg_size_pretty(CASE WHEN nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes < 0 THEN 0
                              ELSE nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes END), 'bytes', 'B') AS free_nonkey_idxsize,
      (CASE WHEN nonkey_idxsize_bytes = 0
                 OR nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes < 0 THEN 0
            ELSE (nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes)::numeric / nonkey_idxsize_bytes END)::numeric(4,3) AS nonkey_idx_bloat_rate,
      nspname, relname
 FROM utility.table_byte_sizes
 LEFT JOIN (SELECT nspname, relname,
                   CAST(SUM(CASE WHEN is_key THEN ideal_idxsize ELSE 0 END) AS BIGINT) AS ideal_key_idxsize_bytes,
                   CAST(SUM(CASE WHEN NOT is_key THEN ideal_idxsize ELSE 0 END) AS BIGINT) AS ideal_nonkey_idxsize_bytes,
                   CAST(SUM(CASE WHEN is_key THEN idxsize ELSE 0 END) AS BIGINT) AS key_idxsize_bytes,
                   CAST(SUM(CASE WHEN NOT is_key THEN idxsize ELSE 0 END) AS BIGINT) AS nonkey_idxsize_bytes
              FROM utility.index_byte_sizes
              GROUP BY nspname, relname) idx_sizes USING (nspname,relname)
 WHERE table_byte_sizes.nspname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])
 ORDER BY total_relsize_bytes DESC,
          free_space_bytes IS NULL,
          free_space_bytes DESC,
          relsize_bytes DESC,
          bloat_rate DESC,
          idxsize_bytes DESC;
DTrace
logs
log_min_duration_statement
log_duration
log_lock_waits
deadlock_timeout
log_temp_files
log_connections
log_disconnections
track_activities
track_activity_query_size*
track_counts
track_functions*
stats_temp_directory*
log_statement_stats
log_parser_stats
log_planner_stats
log_executor_stats
LOG: EXECUTOR STATISTICS
DETAIL: ! system usage stats:
    !   0.017621 elapsed 0.004762 user 0.000816 system sec
    !   [6.012501 user 0.336354 sys total]
    !   0/0 [0/0] filesystem blocks in/out
    !   0/0 [0/0] page faults/reclaims, 0 [0] swaps
    !   0 [1] signals rcvd, 0/10 [4/14944] messages rcvd/sent
    !   2/0 [210/0] voluntary/involuntary context switches
    ! buffer usage stats:
    !   Shared blocks:          9 read,          0 written, buffer hit rate = 0.00%
    !   Local blocks:           0 read,          0 written, buffer hit rate = 0.00%
    !   Direct blocks:          0 read,          0 written
STATEMENT: select * from posuta.index_statistics limit 1000;
LOG: duration: 42.422 ms
csv
2009-05-19 10:25:35.470 EDT,"grzm","posuta_production",99595,"[local]",
4a12c078.1850b,28,"SELECT",2009-05-19 10:21:44 EDT,2/30525,0,LOG,00000,"EXECUTOR
STATISTICS","! system usage stats:
!   1.786288 elapsed 0.065964 user 0.074493 system sec
!   [6.079580 user 0.412469 sys total]
!   2/0 [2/0] filesystem blocks in/out
!   0/0 [0/0] page faults/reclaims, 0 [0] swaps
!   0 [1] signals rcvd, 0/13 [5/14960] messages rcvd/sent
!   1008/0 [1230/0] voluntary/involuntary context switches
! buffer usage stats:
!   Shared blocks:       1073 read,          0 written, buffer hit rate = 0.00%
!   Local blocks:           0 read,          0 written, buffer hit rate = 0.00%
!   Direct blocks:          0 read,          0 written",,,,,"select * from
posuta.index_statistics where index_id = 265 limit 1000;",,
bvr
contrib
pg_freespacemap
pg_buffercache
pgrowlocks
pgstattuple
statistics
collector
pg_stat_activity

pg_locks
pg_stat_get_numscans

pg_stat_get_tuples_returned

pg_stat_get_tuples_fetched
pg_stat_get_tuples_inserted

pg_stat_get_tuples_updated

pg_stat_get_tuples_hot_updated

pg_stat_get_tuples_deleted
pg_stat_get_live_tuples

pg_stat_get_dead_tuples
pg_stat_get_blocks_fetched

pg_stat_get_blocks_hit
pg_stat_get_last_vacuum_time

pg_stat_get_last_autovacuum_time

pg_stat_get_last_analyze_time

pg_stat_get_last_autoanalyze_time
pg_stat_get_function_calls*

pg_stat_get_function_time*

pg_stat_get_function_self_time*
pg_stat_get_db_xact_commit

pg_stat_get_db_xact_rollback
pg_stat_get_bgwriter_timed_checkpoints

pg_stat_get_bgwriter_requested_checkpoints

pg_stat_get_bgwriter_buf_written_checkpoints

pg_stat_get_bgwriter_buf_written_clean

pg_stat_get_bgwriter_maxwritten_clean
snapshot
topHeapHitters
topIndexHitters
0300
Those who cannot
remember the past are
condemned to repeat it.
posuta
Postgres
statistics
ˈposu̥ta
Visualizing Postgres
Ruby
PHP
web.py
Visualizing Postgres
clojure
compojure
(defn request-accepts-re [request re]
  (let [accept-headers (str-utils/re-split #"," ((request :headers) "accept"))]
    (some #(not (= () (re-seq re %))) accept-headers)))

(defroutes posuta
  (GET "/targets/:target/databases/:database/schemas/:schema/relations/:relation/stats/analyses"
    (if (request-accepts-re request #"application/json")
      (analysis-statistics-controller/jsonp (params :callback)
                                            (params :target) (params :database)
                                            (params :schema) (params :relation)
                                            (params :offset) (params :duration))
      (analysis-statistics-controller/index (params :target) (params :database)
                                            (params :schema) (params :relation))))
  (GET "/targets/:target/databases/:database/schemas/:schema/relations/:relation/stats/vacuums"
    (if (request-accepts-re request #"application/json")
      (vacuum-statistics-controller/jsonp (params :callback)
                                          (params :target) (params :database)
                                          (params :schema) (params :relation)
                                          (params :offset) (params :duration))
      (vacuum-statistics-controller/index (params :target) (params :database)
                                          (params :schema) (params :relation))))
  (GET "/"
    (targets-controller/index))
  (GET "/*"
    (or (serve-file (params :*)) :next))
  (ANY "*" (error-404 (params :*))))
(defn index [target-label database-name schema-name relation-name]
  (let [title (str relation-name " vacuums")
        relation (relation/relation target-label database-name schema-name relation-name)]
    (page (h (str relation-name " vacuums"))
          (html (javascript-tag (str "$(document).ready(function(){initVacuumStatistics("
                                     (json-str {"target" target-label
                                                "database" database-name
                                                "schema" schema-name
                                                "relation" relation-name}) ");});")))
           (html
            (link-to (relation-statistics-uri relation) relation-name)
            [:dl#charts {:class "chart"}]
            [:pre#debug]))))

(defn jsonp [callback target-label database-name schema-name relation-name offset period-str]
  (let [day-offset (Integer/parseInt offset)
        duration (as-sql-interval (parse-iso-period period-str))
        stats (vacuum-statistics/vacuum-statistics
               target-label database-name schema-name relation-name
               day-offset duration)
        chart-data (if (empty? stats) []
                       (let [bounds (let [row (first stats)]
                                       (vector (row :lower_bound_js_epoch)
                                               (row :upper_bound_js_epoch)))
                             get-series (fn [row col-name]
                                           (vector (row :occurred_at_js_epoch) (row col-name)))
                             map-stats (fn [col-name stats] (map #(get-series % col-name) stats))]
                         (hash-map "vacuum" (map-stats :vacuum stats)
                                    "auto-vacuum" (map-stats :autovacuum stats)
                                    "bounds" bounds
                                    "label" day-offset)))]
    (jsonp-response callback chart-data)))
(defn banner []
  (html [:div#banner (link-to application-base-path
                              [:img {:alt "posuta" :src "/images/logotype.png"}])]))

(defn page
  ([title body]
       (page title nil body))
  ([title head-elts body]
     (html (doctype :xhtml-strict)
           [:head [:title title]
            (include-css "/css/reset.css" "/css/layout.css")
            (include-js "/js/debug.js" "/js/jquery.js"
                          "/js/jquery.flot.js" "/js/posuta.js"
                          "/js/jquery-ui-1.7.1.custom.min.js")
            head-elts]
           (banner)
           [:body
            [:h1#title (h title)]
            [:div#content body
             [:pre#debug]]])))

(defn jsonp-response [callback content]
  (let [response (str callback "(" (json-str content) ")")]
    [200 {:headers {"Content-Type" "application/json"
                    "Content-Length" (str (.length response))
                    "X-Server" "Posuta"}} response]))
(defn vacuum-statistics
      [target-label database-name schema-name relation-name day-offset duration]
      (db/sql-query-join
       ["SELECT posuta.js_epoch(lower_bound + bounds.shift) AS lower_bound_js_epoch,"
               "posuta.js_epoch(upper_bound + bounds.shift) AS upper_bound_js_epoch,"
               "posuta.js_epoch(vacuumed_at + bounds.shift) AS occurred_at_js_epoch,"
               "target, database_name, schema_name, relation_name,"
               "CASE WHEN is_autovacuum THEN 0 ELSE 1 END AS vacuum,"
               "CASE WHEN is_autovacuum THEN 1 ELSE 0 END AS autovacuum"
          "FROM posuta.vacuums_view"
          "NATURAL JOIN (SELECT target, database_name, schema_name, relation_name,"
                                "(latest_occurred_at - latest.shift - CAST(? AS INTERVAL))”
                                   “AS lower_bound,"
                                "(latest_occurred_at - latest.shift) AS upper_bound,"
                                "latest.shift"
                           "FROM (SELECT target, database_name,"
                                        "schema_name, relation_name,"
                                        "MAX(vacuumed_at) AS latest_occurred_at,"
                                        "p.shift"
                                   "FROM posuta.vacuums_view"
                                   "NATURAL JOIN (SELECT (? * INTERVAL '24 hours') AS shift) AS p"
                                   "GROUP BY target, database_name,"
                                            "schema_name, relation_name,"
                                            "p.shift) AS latest) AS bounds"
          "WHERE (target, database_name, schema_name, relation_name) = (?, ?, ?, ?)"
                "AND vacuumed_at BETWEEN lower_bound AND upper_bound"
          "ORDER BY vacuumed_at"])
       duration day-offset
       target-label database-name schema-name relation-name))
Postgres
http://postgresql.org
Ruby
http://www.ruby-lang.org
Clojure
http://clojure.org
Compojure
http://github.com/weavejester/compojure
jQuery
http://jquery.com
flot
http://code.google.com/p/flot
Visualizing
 Postgres
          posuta
michael.glaesemann@myyearbook.com
Inspirational art by Ralph Steadman and xkcd.com.
         Challenger chart by Edward Tufte.
       Everything used without permission.

More Related Content

PDF
MariaDB 10.3 Optimizer - where does it stand
PDF
R for you
KEY
Postgres rules
PDF
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
PPTX
The rise of json in rdbms land jab17
PDF
Assist9 bmis
PDF
Modern Application Foundations: Underscore and Twitter Bootstrap
PPTX
Indexing and Query Optimization
MariaDB 10.3 Optimizer - where does it stand
R for you
Postgres rules
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
The rise of json in rdbms land jab17
Assist9 bmis
Modern Application Foundations: Underscore and Twitter Bootstrap
Indexing and Query Optimization

What's hot (20)

PDF
The Ring programming language version 1.3 book - Part 34 of 88
PPTX
Indexing & Query Optimization
PDF
Data handling in r
PDF
Python dictionary : past, present, future
PDF
Groovy collection api
PDF
Stata cheatsheet transformation
PDF
Stata Programming Cheat Sheet
PDF
R code for data manipulation
PDF
The Ring programming language version 1.8 book - Part 50 of 202
PDF
Stata cheat sheet: data transformation
PPTX
Reducing Development Time with MongoDB vs. SQL
PDF
Stata Cheat Sheets (all)
DOCX
library(sparkline)
PDF
Chaining and function composition with lodash / underscore
PDF
Functional es6
PPTX
MongoDB and Indexes - MUG Denver - 20160329
PDF
Erlang for data ops
PDF
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
PDF
RxSwift 시작하기
PDF
The Ring programming language version 1.5.4 book - Part 44 of 185
The Ring programming language version 1.3 book - Part 34 of 88
Indexing & Query Optimization
Data handling in r
Python dictionary : past, present, future
Groovy collection api
Stata cheatsheet transformation
Stata Programming Cheat Sheet
R code for data manipulation
The Ring programming language version 1.8 book - Part 50 of 202
Stata cheat sheet: data transformation
Reducing Development Time with MongoDB vs. SQL
Stata Cheat Sheets (all)
library(sparkline)
Chaining and function composition with lodash / underscore
Functional es6
MongoDB and Indexes - MUG Denver - 20160329
Erlang for data ops
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
RxSwift 시작하기
The Ring programming language version 1.5.4 book - Part 44 of 185
Ad

Viewers also liked (7)

PPTX
M2M Evolution
PPT
Establishing your district's relationship with google
PDF
Deployment de Rails
PDF
pgpool: Features and Development
PPT
PPT
Quiénes son los estudiantes de la escuela secundaria
PDF
PESQUISA ELEITORAL
M2M Evolution
Establishing your district's relationship with google
Deployment de Rails
pgpool: Features and Development
Quiénes son los estudiantes de la escuela secundaria
PESQUISA ELEITORAL
Ad

Similar to Visualizing Postgres (20)

PDF
Managing terabytes: When PostgreSQL gets big
PDF
Managing terabytes: When Postgres gets big
PDF
Rob Sullivan at Heroku's Waza 2013: Your Database -- A Story of Indifference
PPTX
Postgres indexes: how to make them work for your application
PPTX
Postgresql Database Administration- Day4
PDF
PostgreSQL on Solaris
PDF
PostgreSQL on Solaris
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
Postgres performance for humans
PDF
PostgreSQL Performance Tuning
PPTX
Postgres indexes
PDF
Deep dive into PostgreSQL internal statistics / Алексей Лесовский (PostgreSQL...
PDF
20070920 Highload2007 Training Performance Momjian
PDF
Using PostgreSQL statistics to optimize performance
PDF
Creating PostgreSQL-as-a-Service at Scale
PDF
3 indexes
Managing terabytes: When PostgreSQL gets big
Managing terabytes: When Postgres gets big
Rob Sullivan at Heroku's Waza 2013: Your Database -- A Story of Indifference
Postgres indexes: how to make them work for your application
Postgresql Database Administration- Day4
PostgreSQL on Solaris
PostgreSQL on Solaris
pg_proctab: Accessing System Stats in PostgreSQL
pg_proctab: Accessing System Stats in PostgreSQL
pg_proctab: Accessing System Stats in PostgreSQL
pg_proctab: Accessing System Stats in PostgreSQL
pg_proctab: Accessing System Stats in PostgreSQL
Postgres performance for humans
PostgreSQL Performance Tuning
Postgres indexes
Deep dive into PostgreSQL internal statistics / Алексей Лесовский (PostgreSQL...
20070920 Highload2007 Training Performance Momjian
Using PostgreSQL statistics to optimize performance
Creating PostgreSQL-as-a-Service at Scale
3 indexes

More from elliando dias (20)

PDF
Clojurescript slides
PDF
Why you should be excited about ClojureScript
PDF
Functional Programming with Immutable Data Structures
PPT
Nomenclatura e peças de container
PDF
Geometria Projetiva
PDF
Polyglot and Poly-paradigm Programming for Better Agility
PDF
Javascript Libraries
PDF
How to Make an Eight Bit Computer and Save the World!
PDF
Ragel talk
PDF
A Practical Guide to Connecting Hardware to the Web
PDF
Introdução ao Arduino
PDF
Minicurso arduino
PDF
Incanter Data Sorcery
PDF
PDF
Fab.in.a.box - Fab Academy: Machine Design
PDF
The Digital Revolution: Machines that makes
PDF
Hadoop + Clojure
PDF
Hadoop - Simple. Scalable.
PDF
Hadoop and Hive Development at Facebook
PDF
Multi-core Parallelization in Clojure - a Case Study
Clojurescript slides
Why you should be excited about ClojureScript
Functional Programming with Immutable Data Structures
Nomenclatura e peças de container
Geometria Projetiva
Polyglot and Poly-paradigm Programming for Better Agility
Javascript Libraries
How to Make an Eight Bit Computer and Save the World!
Ragel talk
A Practical Guide to Connecting Hardware to the Web
Introdução ao Arduino
Minicurso arduino
Incanter Data Sorcery
Fab.in.a.box - Fab Academy: Machine Design
The Digital Revolution: Machines that makes
Hadoop + Clojure
Hadoop - Simple. Scalable.
Hadoop and Hive Development at Facebook
Multi-core Parallelization in Clojure - a Case Study

Visualizing Postgres

  • 1. Michael Glaesemann myYearbook.com michael.glaesemann@myyearbook.com
  • 2. We were somewhere around Barstow on the edge of the desert when the drugs began to take hold…
  • 5. PGCon 2009 Ottawa 2009-05-21
  • 12. Tufte
  • 13. 7
  • 16. snmp rrd Staplr http://area51.myyearbook.com
  • 20. bloat
  • 23. CREATE VIEW utility.index_byte_sizes AS SELECT rel.oid AS relid, pg_index.indexrelid, pg_namespace.nspname, rel.relname, idx.relname AS indexrelname, pg_index.indisunique AS is_key, ((ceil(idx.reltuples * ((constants.index_tuple_header_size + constants.item_id_data_size + CASE WHEN (COALESCE(SUM(CASE WHEN statts.staattnotnull THEN 0 ELSE 1 END), 0::BIGINT) + ((SELECT COALESCE(SUM(CASE WHEN atts.attnotnull THEN 0 ELSE 1 END), 0::BIGINT) FROM pg_attribute atts JOIN (SELECT pg_index.indkey[the.i] AS attnum FROM generate_series(0, pg_index.indnatts - 1) the(i)) cols ON atts.attnum = cols.attnum WHERE atts.attrelid = pg_index.indrelid))) > 0 THEN (SELECT the.null_bitmap_size + constants.max_align - CASE WHEN (the.null_bitmap_size % constants.max_align) = 0 THEN constants.max_align ELSE the.null_bitmap_size % constants.max_align END FROM (VALUES (pg_index.indnatts / 8 + CASE WHEN (pg_index.indnatts % 8) = 0 THEN 0 ELSE 1 END)) the(null_bitmap_size)) ELSE 0 END)::DOUBLE PRECISION + COALESCE(SUM(statts.stawidth::DOUBLE PRECISION * (1::DOUBLE PRECISION - statts.stanullfrac)), 0::DOUBLE PRECISION) + COALESCE((SELECT SUM(atts.stawidth::DOUBLE PRECISION * (1::DOUBLE PRECISION - atts.stanullfrac)) FROM pg_statistic atts JOIN (SELECT pg_index.indkey[the.i] AS attnum FROM generate_series(0, pg_index.indnatts - 1) the(i)) cols ON atts.staattnum = cols.attnum WHERE atts.starelid = pg_index.indrelid), 0::DOUBLE PRECISION)) / (constants.block_size - constants.page_header_data_size::NUMERIC - constants.special_space::NUMERIC)::DOUBLE PRECISION) + constants.index_metadata_pages::DOUBLE PRECISION) * constants.block_size::DOUBLE PRECISION)::BIGINT AS ideal_idxsize, (idx.relpages::NUMERIC * constants.block_size)::BIGINT AS idxsize FROM pg_index JOIN pg_class idx ON pg_index.indexrelid = idx.oid JOIN pg_class rel ON pg_index.indrelid = rel.oid JOIN pg_namespace ON idx.relnamespace = pg_namespace.oid LEFT JOIN (SELECT pg_statistic.starelid, pg_statistic.staattnum, pg_statistic.stanullfrac, pg_statistic.stawidth, pg_attribute.attnotnull AS staattnotnull FROM pg_statistic JOIN pg_attribute ON pg_statistic.starelid = pg_attribute.attrelid AND pg_statistic.staattnum = pg_attribute.attnum) statts ON statts.starelid = idx.oid CROSS JOIN (SELECT current_setting('block_size'::TEXT)::NUMERIC AS block_size, CASE WHEN substring(version(), 12, 3) = ANY (ARRAY['8.0'::TEXT, '8.1'::TEXT, '8.2'::TEXT]) THEN 27 ELSE 23 END AS tuple_header_size, CASE WHEN version() ~ 'mingw32'::TEXT THEN 8 ELSE 4 END AS max_align, 8 AS index_tuple_header_size, 4 AS item_id_data_size, 24 AS page_header_data_size, 0 AS special_space, 1 AS index_metadata_pages) constants GROUP BY pg_namespace.nspname, rel.relname, rel.oid, idx.relname, idx.reltuples, idx.relpages, pg_index.indexrelid, pg_index.indrelid, pg_index.indkey, pg_index.indnatts, pg_index.indisunique, constants.block_size, constants.tuple_header_size, constants.max_align, constants.index_tuple_header_size, constants.item_id_data_size, constants.page_header_data_size, constants.index_metadata_pages, constants.special_space;
  • 24. SELECT total_relsize_bytes, replace(pg_size_pretty(total_relsize_bytes), 'bytes', 'B') AS total_relsize, relsize_bytes, replace(pg_size_pretty(relsize_bytes), 'bytes', 'B') AS relsize, free_space_bytes, replace(pg_size_pretty(free_space_bytes), 'bytes', 'B') AS free_space, (table_byte_sizes.free_space_bytes::numeric / table_byte_sizes.relsize_bytes::numeric)::numeric(4,3) AS bloat_rate, idxsize_bytes, replace(pg_size_pretty(idxsize_bytes), 'bytes', 'B') AS idxsize, (idxsize_bytes::numeric / total_relsize_bytes)::numeric(4,3) AS index_rate, toast_relsize_bytes, replace(pg_size_pretty(toast_relsize_bytes), 'bytes', 'B') AS toast_relsize, toast_idxsize_bytes, replace(pg_size_pretty(toast_idxsize_bytes), 'bytes', 'B') AS toast_idxsize, key_idxsize_bytes, replace(pg_size_pretty(key_idxsize_bytes), 'bytes', 'B') AS key_idxsize, CASE WHEN key_idxsize_bytes - ideal_key_idxsize_bytes < 0 THEN 0 ELSE key_idxsize_bytes - ideal_key_idxsize_bytes END AS free_key_idxsize_bytes, replace(pg_size_pretty(CASE WHEN key_idxsize_bytes - ideal_key_idxsize_bytes < 0 THEN 0 ELSE key_idxsize_bytes - ideal_key_idxsize_bytes END), 'bytes', 'B') AS free_key_idxsize, (CASE WHEN key_idxsize_bytes = 0 OR key_idxsize_bytes - ideal_key_idxsize_bytes < 0 THEN 0 ELSE (key_idxsize_bytes - ideal_key_idxsize_bytes)::numeric / key_idxsize_bytes END)::numeric(4,3) AS key_idx_bloat_rate, nonkey_idxsize_bytes, replace(pg_size_pretty(nonkey_idxsize_bytes), 'bytes', 'B') AS nonkey_idxsize, CASE WHEN nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes < 0 THEN 0 ELSE nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes END AS free_nonkey_idxsize_bytes, replace(pg_size_pretty(CASE WHEN nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes < 0 THEN 0 ELSE nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes END), 'bytes', 'B') AS free_nonkey_idxsize, (CASE WHEN nonkey_idxsize_bytes = 0 OR nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes < 0 THEN 0 ELSE (nonkey_idxsize_bytes - ideal_nonkey_idxsize_bytes)::numeric / nonkey_idxsize_bytes END)::numeric(4,3) AS nonkey_idx_bloat_rate, nspname, relname FROM utility.table_byte_sizes LEFT JOIN (SELECT nspname, relname, CAST(SUM(CASE WHEN is_key THEN ideal_idxsize ELSE 0 END) AS BIGINT) AS ideal_key_idxsize_bytes, CAST(SUM(CASE WHEN NOT is_key THEN ideal_idxsize ELSE 0 END) AS BIGINT) AS ideal_nonkey_idxsize_bytes, CAST(SUM(CASE WHEN is_key THEN idxsize ELSE 0 END) AS BIGINT) AS key_idxsize_bytes, CAST(SUM(CASE WHEN NOT is_key THEN idxsize ELSE 0 END) AS BIGINT) AS nonkey_idxsize_bytes FROM utility.index_byte_sizes GROUP BY nspname, relname) idx_sizes USING (nspname,relname) WHERE table_byte_sizes.nspname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name]) ORDER BY total_relsize_bytes DESC, free_space_bytes IS NULL, free_space_bytes DESC, relsize_bytes DESC, bloat_rate DESC, idxsize_bytes DESC;
  • 26. logs
  • 31. LOG: EXECUTOR STATISTICS DETAIL: ! system usage stats: ! 0.017621 elapsed 0.004762 user 0.000816 system sec ! [6.012501 user 0.336354 sys total] ! 0/0 [0/0] filesystem blocks in/out ! 0/0 [0/0] page faults/reclaims, 0 [0] swaps ! 0 [1] signals rcvd, 0/10 [4/14944] messages rcvd/sent ! 2/0 [210/0] voluntary/involuntary context switches ! buffer usage stats: ! Shared blocks: 9 read, 0 written, buffer hit rate = 0.00% ! Local blocks: 0 read, 0 written, buffer hit rate = 0.00% ! Direct blocks: 0 read, 0 written STATEMENT: select * from posuta.index_statistics limit 1000; LOG: duration: 42.422 ms
  • 32. csv
  • 33. 2009-05-19 10:25:35.470 EDT,"grzm","posuta_production",99595,"[local]", 4a12c078.1850b,28,"SELECT",2009-05-19 10:21:44 EDT,2/30525,0,LOG,00000,"EXECUTOR STATISTICS","! system usage stats: ! 1.786288 elapsed 0.065964 user 0.074493 system sec ! [6.079580 user 0.412469 sys total] ! 2/0 [2/0] filesystem blocks in/out ! 0/0 [0/0] page faults/reclaims, 0 [0] swaps ! 0 [1] signals rcvd, 0/13 [5/14960] messages rcvd/sent ! 1008/0 [1230/0] voluntary/involuntary context switches ! buffer usage stats: ! Shared blocks: 1073 read, 0 written, buffer hit rate = 0.00% ! Local blocks: 0 read, 0 written, buffer hit rate = 0.00% ! Direct blocks: 0 read, 0 written",,,,,"select * from posuta.index_statistics where index_id = 265 limit 1000;",,
  • 34. bvr
  • 49. 0300
  • 50. Those who cannot remember the past are condemned to repeat it.
  • 55. Ruby
  • 56. PHP
  • 61. (defn request-accepts-re [request re] (let [accept-headers (str-utils/re-split #"," ((request :headers) "accept"))] (some #(not (= () (re-seq re %))) accept-headers))) (defroutes posuta (GET "/targets/:target/databases/:database/schemas/:schema/relations/:relation/stats/analyses" (if (request-accepts-re request #"application/json") (analysis-statistics-controller/jsonp (params :callback) (params :target) (params :database) (params :schema) (params :relation) (params :offset) (params :duration)) (analysis-statistics-controller/index (params :target) (params :database) (params :schema) (params :relation)))) (GET "/targets/:target/databases/:database/schemas/:schema/relations/:relation/stats/vacuums" (if (request-accepts-re request #"application/json") (vacuum-statistics-controller/jsonp (params :callback) (params :target) (params :database) (params :schema) (params :relation) (params :offset) (params :duration)) (vacuum-statistics-controller/index (params :target) (params :database) (params :schema) (params :relation)))) (GET "/" (targets-controller/index)) (GET "/*" (or (serve-file (params :*)) :next)) (ANY "*" (error-404 (params :*))))
  • 62. (defn index [target-label database-name schema-name relation-name] (let [title (str relation-name " vacuums") relation (relation/relation target-label database-name schema-name relation-name)] (page (h (str relation-name " vacuums")) (html (javascript-tag (str "$(document).ready(function(){initVacuumStatistics(" (json-str {"target" target-label "database" database-name "schema" schema-name "relation" relation-name}) ");});"))) (html (link-to (relation-statistics-uri relation) relation-name) [:dl#charts {:class "chart"}] [:pre#debug])))) (defn jsonp [callback target-label database-name schema-name relation-name offset period-str] (let [day-offset (Integer/parseInt offset) duration (as-sql-interval (parse-iso-period period-str)) stats (vacuum-statistics/vacuum-statistics target-label database-name schema-name relation-name day-offset duration) chart-data (if (empty? stats) [] (let [bounds (let [row (first stats)] (vector (row :lower_bound_js_epoch) (row :upper_bound_js_epoch))) get-series (fn [row col-name] (vector (row :occurred_at_js_epoch) (row col-name))) map-stats (fn [col-name stats] (map #(get-series % col-name) stats))] (hash-map "vacuum" (map-stats :vacuum stats) "auto-vacuum" (map-stats :autovacuum stats) "bounds" bounds "label" day-offset)))] (jsonp-response callback chart-data)))
  • 63. (defn banner [] (html [:div#banner (link-to application-base-path [:img {:alt "posuta" :src "/images/logotype.png"}])])) (defn page ([title body] (page title nil body)) ([title head-elts body] (html (doctype :xhtml-strict) [:head [:title title] (include-css "/css/reset.css" "/css/layout.css") (include-js "/js/debug.js" "/js/jquery.js" "/js/jquery.flot.js" "/js/posuta.js" "/js/jquery-ui-1.7.1.custom.min.js") head-elts] (banner) [:body [:h1#title (h title)] [:div#content body [:pre#debug]]]))) (defn jsonp-response [callback content] (let [response (str callback "(" (json-str content) ")")] [200 {:headers {"Content-Type" "application/json" "Content-Length" (str (.length response)) "X-Server" "Posuta"}} response]))
  • 64. (defn vacuum-statistics [target-label database-name schema-name relation-name day-offset duration] (db/sql-query-join ["SELECT posuta.js_epoch(lower_bound + bounds.shift) AS lower_bound_js_epoch," "posuta.js_epoch(upper_bound + bounds.shift) AS upper_bound_js_epoch," "posuta.js_epoch(vacuumed_at + bounds.shift) AS occurred_at_js_epoch," "target, database_name, schema_name, relation_name," "CASE WHEN is_autovacuum THEN 0 ELSE 1 END AS vacuum," "CASE WHEN is_autovacuum THEN 1 ELSE 0 END AS autovacuum" "FROM posuta.vacuums_view" "NATURAL JOIN (SELECT target, database_name, schema_name, relation_name," "(latest_occurred_at - latest.shift - CAST(? AS INTERVAL))” “AS lower_bound," "(latest_occurred_at - latest.shift) AS upper_bound," "latest.shift" "FROM (SELECT target, database_name," "schema_name, relation_name," "MAX(vacuumed_at) AS latest_occurred_at," "p.shift" "FROM posuta.vacuums_view" "NATURAL JOIN (SELECT (? * INTERVAL '24 hours') AS shift) AS p" "GROUP BY target, database_name," "schema_name, relation_name," "p.shift) AS latest) AS bounds" "WHERE (target, database_name, schema_name, relation_name) = (?, ?, ?, ?)" "AND vacuumed_at BETWEEN lower_bound AND upper_bound" "ORDER BY vacuumed_at"]) duration day-offset target-label database-name schema-name relation-name))
  • 66. Visualizing Postgres posuta michael.glaesemann@myyearbook.com
  • 67. Inspirational art by Ralph Steadman and xkcd.com. Challenger chart by Edward Tufte. Everything used without permission.