<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>postgres on random thoughts</title><link>https://awesomeprogrammer.com/categories/postgres/</link><description>Recent content in postgres on random thoughts</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Wed, 25 Nov 2015 00:00:00 +0000</lastBuildDate><atom:link href="https://awesomeprogrammer.com/categories/postgres/index.xml" rel="self" type="application/rss+xml"/><item><title>How to alter big postgres table</title><link>https://awesomeprogrammer.com/blog/2015/11/25/how-to-alter-big-postgres-table/</link><pubDate>Wed, 25 Nov 2015 00:00:00 +0000</pubDate><guid>https://awesomeprogrammer.com/blog/2015/11/25/how-to-alter-big-postgres-table/</guid><description><![CDATA[<p>Lately I had to migrate pretty fat (300GB+ data and 150GB+ of index data) postgres table - application grew, we had to get rid of one pretty big column that was redundant now, change main composite index, change one column type etc. etc. The problem was - do it without significant amount of downtime.</p>
<p>You can google up tons of solutions, but let me walk through approach I chose - hopefully you will find it somewhat useful.</p>
<p>I decided to create another table on the side, using <code>CREATE TABLE ... LIKE INCLUDING ALL</code> - then I dropped all the indexes on new table (you don&rsquo;t needed extra overhead for the process) and just started copying the data I really needed to new table using dead simple loop in ruby, something more or less like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">insert</span> <span style="color:#66d9ef">into</span> new_table (columns...) <span style="color:#66d9ef">select</span> columns... <span style="color:#66d9ef">from</span> old_table
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">where</span> id <span style="color:#66d9ef">between</span> <span style="color:#f92672">&lt;</span>batch <span style="color:#66d9ef">of</span> <span style="color:#f92672">~</span><span style="color:#ae81ff">10</span>_000_000 <span style="color:#66d9ef">rows</span><span style="color:#f92672">&gt;</span> <span style="color:#75715e">-- do this until insert results 0 rows
</span></span></span></code></pre></div><p>The whole process was actually pretty quick (you gotta love SSDs) and took just few hours - without indexes on new table I ended up with 185 GB of fresh &amp; clean data. So far so good.</p>
<p>Then I needed to apply new indexes, the problem was I wanted to use unique index and I knew the current dataset had duplicates, fortunately postgres wiki have a very <a href="https://wiki.postgresql.org/wiki/Deleting_duplicates">clever short article</a> how to deal with such problem. And that took another 3.5 hours.</p>
<p>Adding unique index took another 2 hours, then after removing all duplicates and adding index I did full vacuum &amp; analyze on the table and that took yet another hour.</p>
<p>The result was brand new table - with 185GB data &amp; 61GB index - pretty sweet. I prepared the application up-front for upcoming structural changes (that obviously sometimes cannot be done and you have to do some workarounds) and simply renamed both tables within single transaction.</p>
<p>And that&rsquo;s it. Sounds kinda unimpressive, but I was pretty happy with the result, also performance-wise it was a huge win - by simplifying just few things we lowered our response times by almost twice.</p>]]></description><content:encoded><![CDATA[<p>Lately I had to migrate pretty fat (300GB+ data and 150GB+ of index data) postgres table - application grew, we had to get rid of one pretty big column that was redundant now, change main composite index, change one column type etc. etc. The problem was - do it without significant amount of downtime.</p>
<p>You can google up tons of solutions, but let me walk through approach I chose - hopefully you will find it somewhat useful.</p>
<p>I decided to create another table on the side, using <code>CREATE TABLE ... LIKE INCLUDING ALL</code> - then I dropped all the indexes on new table (you don&rsquo;t needed extra overhead for the process) and just started copying the data I really needed to new table using dead simple loop in ruby, something more or less like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">insert</span> <span style="color:#66d9ef">into</span> new_table (columns...) <span style="color:#66d9ef">select</span> columns... <span style="color:#66d9ef">from</span> old_table
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">where</span> id <span style="color:#66d9ef">between</span> <span style="color:#f92672">&lt;</span>batch <span style="color:#66d9ef">of</span> <span style="color:#f92672">~</span><span style="color:#ae81ff">10</span>_000_000 <span style="color:#66d9ef">rows</span><span style="color:#f92672">&gt;</span> <span style="color:#75715e">-- do this until insert results 0 rows
</span></span></span></code></pre></div><p>The whole process was actually pretty quick (you gotta love SSDs) and took just few hours - without indexes on new table I ended up with 185 GB of fresh &amp; clean data. So far so good.</p>
<p>Then I needed to apply new indexes, the problem was I wanted to use unique index and I knew the current dataset had duplicates, fortunately postgres wiki have a very <a href="https://wiki.postgresql.org/wiki/Deleting_duplicates">clever short article</a> how to deal with such problem. And that took another 3.5 hours.</p>
<p>Adding unique index took another 2 hours, then after removing all duplicates and adding index I did full vacuum &amp; analyze on the table and that took yet another hour.</p>
<p>The result was brand new table - with 185GB data &amp; 61GB index - pretty sweet. I prepared the application up-front for upcoming structural changes (that obviously sometimes cannot be done and you have to do some workarounds) and simply renamed both tables within single transaction.</p>
<p>And that&rsquo;s it. Sounds kinda unimpressive, but I was pretty happy with the result, also performance-wise it was a huge win - by simplifying just few things we lowered our response times by almost twice.</p>]]></content:encoded></item></channel></rss>