<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gaurav Ramesh</title>
    <description>The latest articles on DEV Community by Gaurav Ramesh (@outofdesk).</description>
    <link>https://dev.to/outofdesk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F412882%2F351275be-36ec-4436-8965-e5290af6183e.jpg</url>
      <title>DEV Community: Gaurav Ramesh</title>
      <link>https://dev.to/outofdesk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/outofdesk"/>
    <language>en</language>
    <item>
      <title>The Engineering Behind Fast Analytics: Columnar Storage Explained</title>
      <dc:creator>Gaurav Ramesh</dc:creator>
      <pubDate>Tue, 08 Jul 2025 15:28:47 +0000</pubDate>
      <link>https://dev.to/outofdesk/the-engineering-behind-fast-analytics-columnar-storage-explained-1j23</link>
      <guid>https://dev.to/outofdesk/the-engineering-behind-fast-analytics-columnar-storage-explained-1j23</guid>
      <description>&lt;h3&gt;
  
  
  A Very Short History of Columnar Stores
&lt;/h3&gt;

&lt;p&gt;The idea of storing data in columns is not new. It was considered to be first introduced comprehensively in 1985 by GP Copeland and SN Khoshafian. Their paper, "A &lt;strong&gt;decomposition storage model (DSM)&lt;/strong&gt;"[1], proposed storing data in binary relations, pairing each attribute value with the record's identifier. This approach organized data by columns rather than rows, which they argued offered advantages in simplicity and retrieval performance for queries involving a subset of attributes, though it required more storage space overall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MonetDB&lt;/strong&gt;[2] implemented these ideas by 1999, becoming one of the first systems to embrace columnar architecture for analytical workloads and showcasing their effectiveness. &lt;strong&gt;C-Store&lt;/strong&gt;[3], developed in the mid-2000s, marked another crucial milestone, and introduced advanced concepts explained further in the post that are now standards in modern columnar storage systems.&lt;/p&gt;

&lt;p&gt;The late 2000s and early 2010s saw a rise in developments in this area, with projects like &lt;strong&gt;Apache Parquet&lt;/strong&gt;(4) (influenced by &lt;strong&gt;Google's Dremel&lt;/strong&gt;[5] paper) bringing columnar storage to the Hadoop ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Concept: Columnar vs. Row-Oriented Storage
&lt;/h3&gt;

&lt;p&gt;Traditional row-oriented databases store all data for a single entity together. The term &lt;em&gt;row&lt;/em&gt; in a row-oriented system signifies the conceptual model of storing them, like a sentence written left to right in a notebook. In contrast, columnar data stores store data in columns, with each column containing values for a single attribute across all rows. This seemingly simple change has profound implications for analytical performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ea6y7xkcd7u1ltecoo7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ea6y7xkcd7u1ltecoo7.png" alt="Row vs Columnar Storage" width="695" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two key concepts to know when discussing transactional and analytical systems: &lt;strong&gt;predicates&lt;/strong&gt; and &lt;strong&gt;projections&lt;/strong&gt;. &lt;strong&gt;Predicates&lt;/strong&gt; are the conditions by which you filter the entities(rows) you want(think of them as a &lt;code&gt;WHERE&lt;/code&gt; clause in an SQL query). &lt;strong&gt;Projections&lt;/strong&gt; are the fields(columns) that you want in the response(think of them as the column names you define in a &lt;code&gt;SELECT&lt;/code&gt; statement).&lt;/p&gt;

&lt;p&gt;If you think of your data as a list of rows, vertically stacked, predicates slice it horizontally, and projections slice it vertically. Transactional queries often rely on predicates to filter rows, with projections spanning the entire row, i.e. all the columns. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example #1&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="c1"&gt;-- Example #2 &lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Projections in analytical queries involve a small subset of fields of the entity being queried. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_orders&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;user_aggregates&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consider a table with 50 columns and millions of rows. In a row-oriented system, if you need only 3 columns, the database would still have to read all 50 columns for each row. With columnar storage, only the 3 relevant columns are accessed, massively reducing the I/O overhead, i.e. the amount of data you deal with while processing analytical queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hq7g8vwuqk378hfnxh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hq7g8vwuqk378hfnxh4.png" alt="Projections and Predicates" width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Techniques Powering Columnar Stores
&lt;/h3&gt;

&lt;p&gt;This idea of storing data in columns opens up new avenues for further optimizations. Here's a mental model to make sense of the following techniques: &lt;strong&gt;think of query execution as a pipeline that passes data through the various stages, potentially transforming it at each one&lt;/strong&gt;. And it's a two-way pipeline at that: all the way from the client that wants the data to the system that computes and serves that data and back. In each direction, you can think of the places that benefit from optimization are: the network, CPU, memory and the disk.&lt;/p&gt;

&lt;p&gt;In transactional systems(OLTP), the primary means of improving the performance of a query is &lt;em&gt;indexing&lt;/em&gt;, which helps you get to the data you need faster, potentially all from memory. It's sufficient since you're usually dealing with one entity at a given point in transactional systems. But in analytical systems, you deal with a large volume of data in a given query, and hence reducing the data you work with in each stage of the pipeline is primarily how you get a better performance. The smaller the data you work with, the lower the cost, and the faster the pipeline.&lt;/p&gt;

&lt;p&gt;Here are the primary ways of optimizing analytical pipelines. We'll look at each one of them in the post.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Representing/encoding the data as efficiently as possible&lt;/strong&gt;(data compression/column-specific compression), &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtering the data as much and as early as possible&lt;/strong&gt;(column pruning, predicate pushdown), &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expanding the data as late as possible&lt;/strong&gt;(direct operation on compressed data, late materialization)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster processing of the data&lt;/strong&gt;(vectorized processing, efficient joins)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A word of caution - Although these are described as distinct techniques here, in reality, they are much more intertwined. The boundaries between where one ends and the others begin are not as clear and they often rely on each other for maximum gains.&lt;/p&gt;

&lt;h4&gt;
  
  
  Data Compression/Column-specific Compression
&lt;/h4&gt;

&lt;p&gt;Columnar storage enables effective compression. Because data within a single column are of the same type and have similar characteristics, compression algorithms can achieve higher compression ratios. Techniques such as dictionary encoding, run-length encoding(RLE), bit packing, and delta encoding are commonly used in modern columnar stores.&lt;/p&gt;

&lt;p&gt;Let's take an example to understand some of them - Say you have a data store for analyzing traffic on your website and are tracking the source from which a user entered your site. You might have noticed that when you click on a link in your email, the link usually opens up with a &lt;em&gt;utm_source=email&lt;/em&gt;, or &lt;em&gt;utm_source=newsletter&lt;/em&gt;, for example. The &lt;em&gt;utm_source&lt;/em&gt; generally has a limited set of values that identifies the channel through which the user visited your site. The details of that source - domain, URL, time, cookies of the user - are tracked separately. Because each post or page could have thousands or millions of visits, your analytics database will have as many entries for each page, but with only a handful of values for the &lt;em&gt;utm_source&lt;/em&gt; column.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxlz0nz93cpog3j1idig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxlz0nz93cpog3j1idig.png" alt="Compression Techniques" width="735" height="915"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The string values of the source column can be &lt;strong&gt;dictionary encoded&lt;/strong&gt; using integer values that pack tighter and are easier to work with(e.g. source email = 1, source twitter = 2, and so on.). Once that's done, if the consecutive entries in the DB have the same value, which could happen in instances like when you send out a campaign or a newsletter, it can be further compressed using &lt;strong&gt;run-length encoding&lt;/strong&gt;. If a thousand consecutive entries have the same value for source, say 1, it can be stored as (1, 1000), rather than storing the same value a thousand times. Furthermore, if the integer representations take up less space than a 32-bit integer could hold, bit packing can compress it more, by reducing the number of bits needed to hold the value. In our case, if we have 200 different values for &lt;em&gt;source&lt;/em&gt;, we only need 8 bits, rather than 32!&lt;/p&gt;

&lt;h4&gt;
  
  
  Column Pruning
&lt;/h4&gt;

&lt;p&gt;One of the direct results of storing data in a columnar fashion is that it makes it easy to eliminate entire columns required(or rather, not required) for processing. Simply put, your query only ever touches the columns needed for the SELECT, WHERE, GROUP BY, ORDER BY or JOIN columns. Depending on the complexity of the query, you can eliminate a whole lot of data from even being brought into the query execution pipeline.&lt;/p&gt;

&lt;p&gt;Consider a &lt;em&gt;users&lt;/em&gt; table has 25 columns and the query is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;num_orders&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query only needs 5 columns, so through column pruning we potentially reduce 80% of I/O overhead by eliminating the other 20 columns.&lt;/p&gt;

&lt;h4&gt;
  
  
  Predicate Pushdown
&lt;/h4&gt;

&lt;p&gt;The idea behind predicate pushdown is to reduce the data footprint as close to the lowest possible level of execution as possible. Columnar stores do this by evaluating query predicates(think WHERE clause) at the storage layer thus removing the overhead of dealing with unnecessary data in memory and at the CPU. This becomes possible because columnar stores organize the data in blocks or chunks, with each block containing metadata about the range of values in the block(also called Zone Maps), like min/max values, null counts, and so on.&lt;/p&gt;

&lt;p&gt;So when a query comes in, the metadata can be used to skip entire blocks. Only blocks that contain the matching data are selected to be read from the disk. Within the selected blocks, the filtering of specific values happens during decompression.&lt;/p&gt;

&lt;p&gt;Example - Consider a simple query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'New York'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96741m7z1yl0r09wbmk2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96741m7z1yl0r09wbmk2.png" alt="Predicate Pushdown" width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without predicate pushdown, it'd read all column values for columns &lt;em&gt;age&lt;/em&gt; and &lt;em&gt;city&lt;/em&gt;, and then filter the data in memory. With predicate pushdown, it'd check block-level metadata for age and city columns, and skip blocks that have a max-age of 50, or those that don't have New York in the range of values within the block. For the remaining blocks, it'd apply the filter during decompression.&lt;/p&gt;

&lt;h4&gt;
  
  
  Direct Operation on Compressed Data
&lt;/h4&gt;

&lt;p&gt;Storing data by columns also makes it easier to work on partially compressed data, which also reduces the I/O overhead. Consider a sample query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1002&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's say that the data before compression looks something like this -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;department: 1001, 1001, 1001, 1002, 1002, 1002, 1002, 1002, 1003, 1003
salary: 100000, 110000, 100000, 100000, 95000, 95000, 100000, 100000, 100000, 100000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After dictionary encoding and run-length encoding, department column might look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dictionary: {1001: 1, 1002: 2, 1003: 3}
department: 1, 1, 1, 2, 2, 2, 2, 2, 3, 3 (dictionary encoded)
department: (1, 3), (2, 5), (3, 2) (run-length encoded)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, salaries will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;salary: (100000, 1), (110000, 1), (100000, 2), (95000, 2), (100000, 4)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While evaluating the &lt;code&gt;department&lt;/code&gt; predicate, the first three and the last two rows of the column can be skipped immediately merely by looking at the run-length encoded data(because they contain the departments that we're not interested in). The rows can then be encoded using a bitmap like &lt;code&gt;0001111100&lt;/code&gt;, where the 0s indicate the position of the rows that are to be excluded from the aggregation and the 1s indicate the rows that need to be included. Now the bitmap can be used to sum the salary column. The first two run-length encoded rows can be skipped. Since the next 5 rows need to be included, the third and the fourth blocks in salary can be multiplied right away, to get (100000 * 2 = 200000) and (95000 * 2 = 190000). The last block of salary needs to be expanded because only the first entry needs to be included out of the four, which gives us 100000. So the final sum is derived by adding three values rather than individually adding the salaries.&lt;/p&gt;

&lt;h4&gt;
  
  
  Late Materialization, aka Late Tuple Reconstruction
&lt;/h4&gt;

&lt;p&gt;In the spirit of minimizing the data you work with that we alluded to earlier, the idea behind late materialization is that you expand the data only when you need to. While predicate pushdown lets us operate on just the data we want, based on the predicates in the query, late materialization delays the assembly of required projected fields until we have determined what rows need to be returned. This also reduces the overhead of processing unnecessary data at each stage.&lt;/p&gt;

&lt;p&gt;Take the same query we considered in the predicate pushdown section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'New York'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can work with age and city columns the entire pipeline up until we're ready to send the results back to the user, at which stage, we'll need to fetch the &lt;code&gt;name&lt;/code&gt; column from the store.&lt;/p&gt;

&lt;h4&gt;
  
  
  Vectorized Processing
&lt;/h4&gt;

&lt;p&gt;Vectorized processing in columnar databases operates on batches of data rather than individual values, leading to significant performance improvements.&lt;/p&gt;

&lt;p&gt;SIMD(Single Instruction, Multiple Data) is a parallel processing technique invented to solve the problem of efficiently processing large &lt;em&gt;arrays of similar data&lt;/em&gt;(mathematically called vectors) that require the &lt;em&gt;same operation to be performed on each element&lt;/em&gt;. It's commonly found in most modern CPU architectures. Let's focus on the two main parts of the problem it solves - &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large arrays of similar data - When data is stored by columns, this is exactly what you get while processing a single column&lt;/li&gt;
&lt;li&gt;Same operation to be performed on each element - This is true for most analytical queries, where you want to apply a predicate on the values of a specific column&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider this query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To find the desired rows, you're looking at the column &lt;code&gt;user_id&lt;/code&gt; and performing the same operation, an equals check, on all the values in that column. In traditional processing you'd perform two things primarily sequentially - &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if value of &lt;code&gt;user_id&lt;/code&gt; column of a given row is equal to &lt;code&gt;1234&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;If it is, add the value of the &lt;code&gt;sum&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Repeat for the next row&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With vectorized processing, chunks of data, say 1000 &lt;code&gt;userid&lt;/code&gt; values at once is loaded into memory and compared to the value &lt;code&gt;1234&lt;/code&gt; in parallel using SIMD. A bit of matches is then created to sum corresponding prices values in parallel.&lt;/p&gt;

&lt;h4&gt;
  
  
  Efficient Join Implementations
&lt;/h4&gt;

&lt;p&gt;Columnar storage enables advanced join implementations beyond the traditional hash or merge joins. One example of such a technique is a &lt;strong&gt;semi-join&lt;/strong&gt;, which aims to determine if one table has matching values in the join column of another table, without needing to return all column values from the second table. It uses bloom filters to achieve this. They are probabilistic data structures that help efficiently check for the &lt;em&gt;potential&lt;/em&gt; existence of values, i.e. answer set membership queries. When asked whether a value exists in a set of values, they never produce false negatives(which means they can say with certainty that a value does not exist), but might produce false positives(which means they might say a value exists that does not). Instead of storing the actual join keys(which could be millions of values taking gigabytes), a bloom filter uses just a few megabytes to represent the same set with high accuracy.&lt;/p&gt;

&lt;p&gt;Here's how it would work for a query like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'EMEA'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It filters customers by &lt;code&gt;region&lt;/code&gt; column, say 10,000 out of a million customers. For the 10,000 customers, builds a bloom filter that only takes a few megabytes. Now it scans the &lt;code&gt;orders&lt;/code&gt; table(say 100mn records), and tests each &lt;code&gt;customer_id&lt;/code&gt; column against the bloom filter, skips the orders from customers that don't match, performs the more traditional and expensive hash joins on the remaining orders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Combining all the above techniques, columnar data stores not only save storage space on the disk but also reduce I/O overhead, both of which result in cost savings for the organization. By working on lesser data and faster, they also offer significant performance gains for analytical workloads, and in a scalable manner. They have been gaining wide adoption in the areas of web analytics, business intelligence, machine learning infrastructure, log and event analysis, and real-time analytics, to name a few.&lt;/p&gt;

&lt;p&gt;If you are a data practitioner who already works with columnar stores, I hope that the knowledge of the internals helps you squeeze the most juice out of them, and optimize them for your use cases. If you are an application developer building analytical products, think about your stack and consider introducing a columnar data store where you require performance and scalability. This post should help you make a case for why columnar storage might make sense for your needs. If you're an engineering leader considering adding a columnar datastore to your stack, knowledge of the above techniques should help you evaluate the trade-offs and make the right strategic decisions for your organization.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The post was originally published &lt;a href="https://outofdesk.netlify.app/blog/engineering-fast-analytics-columnar-storage-explained" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;(1) &lt;a href="https://dl.acm.org/doi/10.1145/971699.318923" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/971699.318923&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(2) &lt;a href="http://sites.computer.org/debull/A12mar/monetdb.pdf" rel="noopener noreferrer"&gt;http://sites.computer.org/debull/A12mar/monetdb.pdf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(3) &lt;a href="https://dl.acm.org/doi/10.5555/1083592.1083658" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.5555/1083592.1083658&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(4) &lt;a href="https://parquet.apache.org/" rel="noopener noreferrer"&gt;https://parquet.apache.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(5) &lt;a href="https://research.google/pubs/dremel-interactive-analysis-of-web-scale-datasets-2/" rel="noopener noreferrer"&gt;https://research.google/pubs/dremel-interactive-analysis-of-web-scale-datasets-2/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>analytics</category>
      <category>columnar</category>
      <category>database</category>
      <category>performance</category>
    </item>
    <item>
      <title>Book Notes: Cloud Native Patterns</title>
      <dc:creator>Gaurav Ramesh</dc:creator>
      <pubDate>Mon, 07 Feb 2022 02:41:21 +0000</pubDate>
      <link>https://dev.to/outofdesk/book-notes-cloud-native-patterns-ihl</link>
      <guid>https://dev.to/outofdesk/book-notes-cloud-native-patterns-ihl</guid>
      <description>&lt;p&gt;I have worked on cloud systems for a few years now, but as is said, &lt;em&gt;“Fish don’t know they’re in water”&lt;/em&gt;, when you’re deep into something it’s hard to zoom out and look at the big picture, to see how ideas and practices are connected to each other and how they share common goals.&lt;/p&gt;

&lt;p&gt;This is my attempt to do that, zoom out, learn more about and reflect on things I’ve worked on and make sense of them as a coherent whole, rather than a bunch of loosely and randomly connected ideas.&lt;/p&gt;

&lt;p&gt;I’m starting with a book called &lt;em&gt;&lt;a href="https://www.manning.com/books/cloud-native-patterns"&gt;“Cloud Native Patterns”, by Cornelia Davis&lt;/a&gt;&lt;/em&gt;, and I plan to use this space to document things I read, learn and think about along the way. As such, I expect this to be a constantly changing and evolving post, much like the cloud systems that the book talks about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basics
&lt;/h2&gt;




&lt;p&gt;&lt;strong&gt;Cloud-first before cloud-native&lt;/strong&gt; – Cloud-first is a good precursor to understanding what cloud-native means and what it has to offer. Although the way people define cloud-first strategy varies widely, it essentially is thinking of the cloud as the primary medium of running your software, as opposed to the older approach that tied all your systems to a set of servers with dedicated resources in a specific physical location that you owned or managed. Cloud, more than anything, is a layer of abstraction built to free the developers’ minds from the concerns of how to run systems and promote thinking about what to build. Hence moving to the cloud doesn’t necessarily mean taking your systems off-premises, but it’s a way of how you think(or rather, how much you don’t think) about them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Natural Side-effects of moving to the cloud&lt;/strong&gt; – Think of taking care of your babies at home versus sending them to daycare. A natural and inevitable side-effect of sending your baby away for a few hours is that you lose some control. It’s neither good or bad, it just is. So while it relieves you of some duties and responsibilities, it also raises the stakes in other ways. Similarly, when you take your systems from your premises to the cloud, it means that you lose some control. But it doesn’t magically solve all your problems, it solves some, and throws other, newer problems at you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inherent characteristics of the cloud&lt;/strong&gt; – Going back to the daycare example, it’s a more dynamic place than your home. There are other actors at play – other babies, the care-givers, other babies’ parents, and the challenges of an unfamiliar setting. In other words, it’s a chaotic place. Things are not always predictable, and are bound to go wrong, in one way or the other. Because of the sheer number of unknowns, learning about all of them, let alone preventing all the problems is not an option. Cloud is exactly like that too. There are other systems at play, their dependencies, and the shared infrastructure, needless to say all that bound by the laws of Physics. It sounds scary at first, but accepting these truths enables us to think about how to build systems that function amidst the chaos, despite all the failings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So why go to the cloud&lt;/strong&gt; – If you lose control and visibility into our systems, why on Earth would you want to migrate? Because absolute control and perfection are delusional goals to build your life or business upon. And while you lose some control on how your systems work together, you gain control on what your systems does, or how they provide value to the customers. If you had all the time in the world, you would have taken care of your babies at home. Not that you don’t like them going out, but that you would like to be around them as much as you can. But time is finite, and society and its changing needs throw a lot at you to juggle. In order to do the best with your time, you prioritize things and delegate or outsource some responsibilities. As technology has come to dominate most people’s lives, the changing needs of society often translates to changing needs of software. As the demand of your business increases, you’re compelled to not only prioritize time, but the limited material resources you possess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desirable characteristics of a successful business&lt;/strong&gt; – In order to serve the ever-changing needs of the society, certain characteristics naturally emerge that are desirable to run a successful business. Simply put, a business should provide the value promised to all its customers, anywhere and anytime, and evolve quickly to address their needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All its customers&lt;/em&gt; – In the world of the Internet, your customers come from different backgrounds, cultures, ages, and gender, they use different devices, and operate under widely different constraints. So accessibility, multi-language and multi-device support now become first-class citizens.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Anywhere and anytime&lt;/em&gt; – When your customers are based in different geographical locations across continents, compliance to legal and other policies, and the ability to operate in different time-zones, essentially being available around the clock are of prime importance.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Evolve quickly&lt;/em&gt; – Evolving quickly to serve such a diverse group of customers and a unique set of constraints means having shorter and accurate feedback cycles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud-native&lt;/strong&gt; – is an umbrella term used to capture a set of ideas, principles, processes and tools, that aims to marry the inherent characteristics of the cloud with the desirable characteristics of today’s businesses in a seamless manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Reading
&lt;/h2&gt;




&lt;p&gt;&lt;a href="https://www.manning.com/books/cloud-native-patterns"&gt;Cloud Native Patterns, by Cornelia Davis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/cloud-native/definition"&gt;What is Cloud Native&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>cloudnative</category>
      <category>engineering</category>
      <category>backend</category>
    </item>
    <item>
      <title>A Story About Simple and Obvious Things</title>
      <dc:creator>Gaurav Ramesh</dc:creator>
      <pubDate>Sun, 28 Jun 2020 14:42:20 +0000</pubDate>
      <link>https://dev.to/outofdesk/a-story-about-simple-and-obvious-things-33d0</link>
      <guid>https://dev.to/outofdesk/a-story-about-simple-and-obvious-things-33d0</guid>
      <description>&lt;p&gt;One afternoon on a weekday, I had a meeting with a few people at work to release the last piece of a project that was meant to change the way our clients interacted with the backend systems. It was a fairly simple change and was expected to be done in a few minutes. Not all people invited were strictly required in the meeting, so while a few joined, others kept an eye on their respective systems for signals and were kept abreast of the progress via Slack.&lt;/p&gt;

&lt;p&gt;So there I was, in a room with a few people, talking to a few on a video call, and chatting with a couple more on the side.&lt;/p&gt;

&lt;p&gt;Five minutes into the meeting, we did the change and shifted our attention to the monitoring systems to see early signs of success, or to minimize damage in the remote possibility of failure. Twenty minutes later, we still didn’t see any activity that we’d expected or hoped for.&lt;/p&gt;

&lt;p&gt;The nature of the change was such that we’d expected it to take a short period to propagate but didn’t know how long exactly. So we didn’t sweat. We had theories about why it might be taking longer than usual. About thirty minutes later, we decided to involve somebody from the iOS team to simulate tests from the clients’ perspective to get some validation.&lt;/p&gt;

&lt;p&gt;They took their sweet time to prepare for the test, and after about fifteen minutes, finally tested it. They saw some errors. When we dug into it, we learned that the clients had been seeing those errors all this time since I made the change — so for about fifty minutes. What was weird was that no whistles were blowing off, no pages triggered, so we doubted if the clients did see any errors at all, or had had any negative impact. In any case, we reverted the change, just to be safe. The errors, as you’d guess, went away.&lt;/p&gt;




&lt;p&gt;We all have been a part of something like this. Your first big screw-up is almost like a rite of passage to a long career in software engineering.&lt;/p&gt;

&lt;p&gt;When I first learned about the impact of the incident, I felt embarrassed and a little nervous. Not because it could have been avoided — that realization only came later — but because I instantly started questioning my abilities. I was trusted with it, and I was incapable of executing it. It was my lack of knowledge, I thought, that was the problem, and my self-doubt started to eat me up. At some point, I even went into denial and started blaming other people for it, at least in my mind, if not in reality.&lt;/p&gt;

&lt;p&gt;After it was all dealt with, my manager called me on my phone to have a chat. He was kind and responsible enough to reflect on it and think of ways he could have helped me. But there was one thing he mentioned which guided my thinking in the right direction.&lt;/p&gt;

&lt;p&gt;He asked me what my plan was before I started to work on it. How dare he ask me what the plan was, I thought. Was he asking if I had a plan at all? I’d obviously have had a plan.&lt;/p&gt;

&lt;p&gt;Only I didn’t.&lt;/p&gt;

&lt;p&gt;I went into the meeting, assuming that I knew all that had to be done, and I didn’t anticipate anything going wrong. So it wasn’t a gap in my knowledge that was the problem, but lack of thought and foresight, about the potential consequences and impact of the change on the system as a whole, both good and bad.&lt;/p&gt;

&lt;p&gt;There were many pieces to look after and even though each of those tasks was fairly simple, every one of them was also crucial to the success, and because of their fragmented nature, it was really easy to miss one or two.&lt;/p&gt;

&lt;p&gt;Atul Gawande, in his book &lt;a href="https://www.amazon.com/Checklist-Manifesto-How-Things-Right/dp/0312430000"&gt;The Checklist Manifesto: How to Get Things Right&lt;/a&gt;, mentions an essay from the 1970s by philosophers Samuel Gorovitz and Alasdair MacIntyre, which talks about human fallibility. One reason they talk about is “necessary fallibility”, which is when things go wrong because they’re beyond our capacity to control. These, by definition, can’t be prevented from happening.&lt;/p&gt;

&lt;p&gt;But he identifies two other reasons for why things go wrong — /ignorance/, the gaps in our knowledge, and /ineptitude/, our failure to apply the knowledge correctly.&lt;/p&gt;

&lt;p&gt;Ignorance, if we’re willing to learn, can be reduced over time; the more time we invest in something, the more we learn about it, the more knowledge we gain. But competence is something that doesn’t just come over time. Getting rid of ineptitude requires persistent effort and deliberate practice. In fact, there’s a case to be made that the more knowledge we possess, the more we stand a chance to blind ourselves to a holistic, systems thinking approach unless we’re constantly reevaluating ourselves.&lt;/p&gt;

&lt;p&gt;The more I thought about it, the more I realized that my mistake had a meta nature to it. One is that despite having the necessary knowledge to execute the task at hand, I had failed to come up with a plan, a checklist of things that I should have had in place before I jumped on it. Second, and the meta part, is that I knew the importance of checklists, but had failed to put that knowledge too, into practice.&lt;/p&gt;




&lt;p&gt;After this incident, I finally started taking the idea of checklists, the habit of note-taking, seriously.&lt;/p&gt;

&lt;p&gt;In that spirit, here’s a list of things I’ve learned and tried to follow since:&lt;/p&gt;

&lt;h4&gt;
  
  
  Meetings
&lt;/h4&gt;

&lt;p&gt;Take notes before, during, and after meetings. &lt;/p&gt;

&lt;p&gt;Before a meeting take note of the agenda, about what you intend to learn, what you want to say, what you expect to get out of the meeting.&lt;/p&gt;

&lt;p&gt;During a meeting, take note of what people say, along with their names, if possible, keywords or jargon mentioned, to read about them later, questions asked, observations, and decisions made.&lt;/p&gt;

&lt;p&gt;After a meeting, list the important things, organize and summarize ideas to make sense of it in the future. Put them in chronological order, relative to the notes of other meetings about a related, or similar topic.&lt;/p&gt;

&lt;h4&gt;
  
  
  Projects
&lt;/h4&gt;

&lt;p&gt;Other than the shared documents of a project like planning document, design document, and so on, it’s immensely helpful to maintain separate notes for yourself about things you’re working on. Like in software systems where you design different data models depending on the use-case and requirements, you can create multiple documents based on the nature of their use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Quick Reference” document for things you’d frequently need while working on a project, for example, necessary links and resources, usernames and passwords, contact info of people important for the project, and so on.&lt;/li&gt;
&lt;li&gt;“Questions” document for things you need clarifications on.&lt;/li&gt;
&lt;li&gt;“Details” document for technical details of the project&lt;/li&gt;
&lt;li&gt;“Planning” document for milestones and timelines associated with the project&lt;/li&gt;
&lt;li&gt;“Checklist” for the list of things you’re working on, will work on and have already completed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I usually tag them by their project name, and their type, so they’re all searchable with one keyword.&lt;/p&gt;

&lt;h4&gt;
  
  
  Releases
&lt;/h4&gt;

&lt;p&gt;This usually breaks out from one of the “Planning” items in the project documents. One document each for every release, with the list of things to take care of before and after the release.&lt;/p&gt;

&lt;h4&gt;
  
  
  Audience
&lt;/h4&gt;

&lt;p&gt;Remember that all of these are for your benefit and sanity. The primary audience is you. Don’t get too worked up about the format or the industry conventions. If it works for you, it’s good enough. It doesn’t even have to be full sentences or grammatically correct, as long as the information contained is accurate. Pictures, links, references, anything is game. When it’s time to share or present the information to people, you can clean it up, organize, and proof-read it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Progress
&lt;/h4&gt;

&lt;p&gt;While stand-up, stand-down, and check-in meetings are great to let other people know what you’re working on or blocked by keeping them as a list will help you go through the pending things in a streamlined, efficient and timely manner. You’ll also get a sense of accomplishment to see things get off of your checklist, and a feeling of closure.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connections
&lt;/h4&gt;

&lt;p&gt;Fragmented bits of data put together in one place helps you keep the focus on the bigger picture, and helps you find patterns and relationships between seemingly disparate ideas.&lt;/p&gt;




&lt;p&gt;While this list looks fairly simple and the things in it seem obvious, if there’s one thing I’ve learned from my experience is that things that seem simple and obvious, are not necessarily easy and not so obvious.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended Reading
&lt;/h2&gt;

&lt;p&gt;"Avoidable failures are common and persistent, not to mention demoralizing and frustrating, across many fields -- from medicine to finance, business to government. And the reason is increasingly evident: the volume and complexity of what we know has exceeded our individual ability to deliver its benefits correctly, safely, or reliably. Knowledge has both saved us and burdened us."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Checklist-Manifesto-How-Things-Right/dp/0312430000"&gt;The Checklist Manifesto: How to Get Things Right&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/bear/id1016366447"&gt;Bear Notes&lt;/a&gt; - After trying hundreds of note apps, I've found a sweet spot of functionality and aesthetics with this one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://outofdesk.github.io/work/productivity/learning/2020/06/27/simple-and-obvious.html"&gt;Original Post&lt;/a&gt; - Original post on my blog, which was edited a little for dev.to&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>learn</category>
      <category>notetaking</category>
      <category>writing</category>
    </item>
  </channel>
</rss>
