<?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: Tom Nats</title>
    <description>The latest articles on DEV Community by Tom Nats (@tnats).</description>
    <link>https://dev.to/tnats</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%2F1104575%2F3110419d-6a5e-4308-9a8c-4e7f22b92c2d.png</url>
      <title>DEV Community: Tom Nats</title>
      <link>https://dev.to/tnats</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tnats"/>
    <language>en</language>
    <item>
      <title>Iceberg Time Travel &amp; Rollbacks in Trino</title>
      <dc:creator>Tom Nats</dc:creator>
      <pubDate>Thu, 17 Aug 2023 18:54:46 +0000</pubDate>
      <link>https://dev.to/starburstdata/iceberg-time-travel-rollbacks-in-trino-j9e</link>
      <guid>https://dev.to/starburstdata/iceberg-time-travel-rollbacks-in-trino-j9e</guid>
      <description>&lt;p&gt;&lt;em&gt;I originally posted this on &lt;a href="https://www.starburst.io/blog/apache-iceberg-time-travel-rollbacks-in-trino/"&gt;Starburst's blog&lt;/a&gt;, as part of a series I've been publishing on Iceberg.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Travel
&lt;/h3&gt;

&lt;p&gt;Time travel in Trino using Iceberg is a handy feature to “look back in time” at a table’s history. As we covered &lt;a href="https://dev.to/starburstdata/introduction-to-apache-iceberg-in-trino-48g0"&gt;in this blog&lt;/a&gt;, each change to an Iceberg table creates a new “snapshot” which can be referred to by using standard SQL.&lt;/p&gt;

&lt;p&gt;As you can see from the diagram below, a new snapshot is created for the table creation, insert and update.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5JKgfTLU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ma0koe27hfiicqa7snp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5JKgfTLU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ma0koe27hfiicqa7snp2.png" alt="Snapshot example" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see the snapshots on a table, you can use the handy metadata table that exists for each table:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT * FROM "customer_iceberg$snapshots";&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KbJD4tk4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhkddynxfl29cfhcw2zt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KbJD4tk4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhkddynxfl29cfhcw2zt.png" alt="Snapshot files returned from customer table" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above snapshot table shows the create, insert and update operations on the customer_iceberg table. You can see what type of operation was performed and when it was executed.&lt;/p&gt;

&lt;p&gt;To select a certain snapshot, you use the “for version as of” syntax. In the following two examples, we show the customer name before and after an update:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT custkey,name&lt;br&gt;
FROM customer_iceberg FOR VERSION AS OF 5043425904354141100 where custkey = 2732;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vv_2C8iU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3fbdm6wsx96ojy5rj1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vv_2C8iU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3fbdm6wsx96ojy5rj1c.png" alt="Snapshot before update to custkey 2732" width="562" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT custkey,name&lt;br&gt;
FROM customer_iceberg FOR VERSION AS OF 3117754680069542695 where custkey = 2732;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xvhokg4u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxbyx0vv0xekk1mjg6y7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xvhokg4u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxbyx0vv0xekk1mjg6y7.png" alt="Snapshot after update to custkey 2732" width="434" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also specify a timeframe to retrieve an older snapshot of a table. For example, the following query brings back the data for the first snapshot on or before a given timestamp:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT custkey,name&lt;br&gt;
FROM s3lakehouse.demo_tpch.customer_iceberg FOR TIMESTAMP AS OF TIMESTAMP '2022-09-18 07:18:09.002 America/New_York' where custkey = 2732;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--asvfEXuU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gd2386kvm7q9pqkoizyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--asvfEXuU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gd2386kvm7q9pqkoizyz.png" alt="Retrieving older snapshot of custkey" width="562" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rolling back
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ddBBZ_Ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpjgcjr810iunmz90usy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ddBBZ_Ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpjgcjr810iunmz90usy.png" alt="Back to the Future meme captioned &amp;quot;Let's go back in time to a previous Iceberg snapshot&amp;quot;" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another great feature of Iceberg is the ability to roll back a table to a previous snapshot. Sometimes this is used when a row was accidentally deleted or updated. As long as the snapshot exists, (it hasn’t been cleaned up yet) then you can roll back to any existing snapshot.&lt;/p&gt;

&lt;p&gt;For example, in the scenario above, if I wanted to roll back to the state of the table before the update on the customer, then I would issue the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CALL iceberg.system.rollback_to_snapshot('demo_tpch', 'customer_iceberg', 5043425904354141100)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then we can query the table again to see the customer’s name was “rolled back” to the previous version before the update:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT custkey,name&lt;br&gt;
FROM s3lakehouse.demo_tpch.customer_iceberg where custkey = 2732;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2LLX0ZJo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1k9w57j0fnh334yg6rvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2LLX0ZJo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1k9w57j0fnh334yg6rvi.png" alt="Rolled back version of custkey" width="562" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both time travel and rolling back are database functions that are now available in a modern data lake architecture. This is a game changer as it allows database type functionality to objects stores which were not available until now.&lt;/p&gt;

</description>
      <category>iceberg</category>
      <category>bigdata</category>
      <category>trino</category>
      <category>datalake</category>
    </item>
    <item>
      <title>Iceberg Schema Evolution in Trino</title>
      <dc:creator>Tom Nats</dc:creator>
      <pubDate>Wed, 09 Aug 2023 13:34:00 +0000</pubDate>
      <link>https://dev.to/starburstdata/iceberg-schema-evolution-in-trino-2fpc</link>
      <guid>https://dev.to/starburstdata/iceberg-schema-evolution-in-trino-2fpc</guid>
      <description>&lt;p&gt;&lt;em&gt;I originally posted this on &lt;a href="https://www.starburst.io/blog/apache-iceberg-schema-evolution-in-trino/"&gt;Starburst's blog&lt;/a&gt;, as part of a series I've been publishing on Iceberg.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Schema evolution simply means the modification of tables as business rules and source systems are modified over time. Trino’s Iceberg connector supports different modifications to tables, including changing the table name itself, as well as, column and partition changes.&lt;/p&gt;

&lt;p&gt;Much like a database, you perform “alters” to Iceberg tables to modify their structure. Since Iceberg is just a table format which contains metadata about a table, modifying the table is rather trivial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table Changes
&lt;/h3&gt;

&lt;p&gt;Rename a table&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table customer_iceberg rename to customer_iceberg_new;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: This is a change to the table name in the metastore and no changes will be made in the storage. So, the location s3:///customer_iceberg will remain the same.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Column Changes
&lt;/h3&gt;

&lt;p&gt;Adding a column:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table customer_iceberg add column tier varchar(1);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Rename a column:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table customer_iceberg rename column address to fulladdress;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Partition Changes
&lt;/h3&gt;

&lt;p&gt;Oftentimes a table is initially partitioned by a column or set of columns, only later it’s discovered this may not be optimal. With Iceberg, you can modify the partition columns at any time.&lt;/p&gt;

&lt;p&gt;For example, initially this table is partitioned by month:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create table orders_iceberg&lt;br&gt;
with (partitioning=ARRAY['month(orderdate)']) as&lt;br&gt;
select * from tpch.sf1.orders;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After reviewing query a patterns, it’s determined that partitioning by day would perform better as a majority of queries are filter by certain days. A simple alter table statement as seen below will modify the partitioning on this table from month to day:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table orders_iceberg SET PROPERTIES partitioning = ARRAY['day(orderdate)'];&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After new data is inserted into the table, you will see a change in the data directory where the table data is stored:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kV8EYfvT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdwj4tsa0qnaootluohf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kV8EYfvT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdwj4tsa0qnaootluohf.png" alt="S3 screenshot showing orderdate_month is now orderdate_day" width="583" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the orderdate_month is now orderdate_day. Note that queries that filter by day will partition prune at the partition day level but the existing monthly partitions will still need to be searched. If you would like to have the entire table partitioned by day then you could recreate the table using a CTAS (create table as) from the existing table.&lt;/p&gt;

&lt;p&gt;Example to create a new table partitioned by day from the existing table:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create table orders_iceberg_new with (partitioning=ARRAY['day(orderdate)']) (as select * from orders_iceberg)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Schema evolution in Trino’s Iceberg connector is very powerful and easy to use. These types of functions were not available in Hive and database veterans will be very happy to see them added to the data lake landscape.&lt;/p&gt;

</description>
      <category>iceberg</category>
      <category>trino</category>
      <category>schema</category>
      <category>datalake</category>
    </item>
    <item>
      <title>Iceberg DML &amp; Maintenance in Trino</title>
      <dc:creator>Tom Nats</dc:creator>
      <pubDate>Mon, 07 Aug 2023 12:39:00 +0000</pubDate>
      <link>https://dev.to/starburstdata/apache-iceberg-dml-maintenance-in-trino-opc</link>
      <guid>https://dev.to/starburstdata/apache-iceberg-dml-maintenance-in-trino-opc</guid>
      <description>&lt;p&gt;&lt;em&gt;I originally posted this on &lt;a href="https://www.starburst.io/blog/apache-iceberg-dml-update-delete-merge-maintenance-in-trino/"&gt;Starburst's blog&lt;/a&gt;, as part of a series I've been publishing on Iceberg.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One key feature of the Apache Iceberg connector is Trino’s ability to modify data that resides on object storage. As we all know, storage like Amazon S3 is immutable which means it cannot be modified. This was a challenge in the Hadoop era where data needed to be modified or removed at the individual row level. Trino allows for full DML (data manipulation language) using the Iceberg connector which means full support for update, delete and merge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5w82anWw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pv2shh3cp0vq98dygvpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5w82anWw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pv2shh3cp0vq98dygvpc.png" alt="Merge, Insert, Update, Delete diagram on top of object storage options" width="678" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Iceberg is a table format, when DML commands are issued, transaction logs are created and stored in the same storage, so clients that read from the same table will reflect these changes in subsequent queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insert
&lt;/h3&gt;

&lt;p&gt;Inserts are one of the most used commands used in a modern data lake. Data is constantly being added and as you would expect, the Iceberg connector for Iceberg supports a standard insert statement:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;insert into customer_iceberg values&lt;br&gt;
(90000,'Testing','33 Main',3,'303-867-5309',323,'MACHINERY','Testing Iceberg');&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note that update, deletes and merges should be run in serial and or batch against a single table. This is to ensure there are no conflicts.&lt;/p&gt;
&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;

&lt;p&gt;Updates in Trino with the Iceberg connector act just like ordinary updates. If there are current select statements executing against this table, they will see the data in the previous snapshot to ensure read integrity.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;update customer_iceberg set name = 'Tim Rogers' where custkey = 2732;&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Delete
&lt;/h3&gt;

&lt;p&gt;Delete statements are rarely used in a modern data lake, instead “soft” deletes are most common which means the row is updated to be flagged as deleted. There is usually a status column or something similar to filter out reading this data by select queries. In the event a row or set of rows need to be deleted, this can be done with a typical delete statement:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;delete customer_iceberg where custkey = 2732;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: A delete statement doesn’t actually physically delete the data off the storage. In order to ensure the data has been removed, an &lt;a href="https://trino.io/docs/current/connector/iceberg.html#expire-snapshots"&gt;expire_snapshots&lt;/a&gt; procedure needs to be executed with a date that is less than when the delete statement was ran.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Merge
&lt;/h3&gt;

&lt;p&gt;The merge statement is a very handy tool to add logic based operations to a sql statement. Merge is often used when you have new or modified data that is staged in a table first. A good example is customer data that is being pulled from an operational system. CDC (change data capture) data is extracted from a CRM system into a staging table in S3. Or with Trino, a merge can use an existing table from the source system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t8xikmKP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/upxtmbqcw4midf1ldku3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t8xikmKP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/upxtmbqcw4midf1ldku3.png" alt="Diagram showing data extracted from a CRM system into a staging table in S3" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use merge, you can either stage data that needs to be inserted or updated into your target table or you can use data directly from the source table(s).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example 1: If there are rows that don’t exist in the target table, insert them. This is a very basic merge statement. The &lt;code&gt;customer_land&lt;/code&gt; table below could be a staged table in object storage like S3 or it could be from a source system such as MySQL or SQL Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MERGE INTO s3lakehouse.blog.customer_base AS b
USING (select * from s3lakehouse.blog.customer_land) AS l
ON (b.custkey = l.custkey)
WHEN NOT MATCHED
      THEN INSERT (custkey, name, state, zip, cust_since,last_update_dt)
            VALUES(l.custkey, l.name, l.state, l.zip, l.cust_since,l.last_update_dt);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example 2: With merge, we can issue a single statement to insert new rows and update existing ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MERGE INTO s3lakehouse.blog.customer_base AS b
USING s3lakehouse.blog.customer_land AS l
ON (b.custkey = l.custkey)
WHEN MATCHED and b.name != l.name
THEN UPDATE
SET name = l.name ,
          state = l.state,
          zip = l.zip,
          cust_since = l.cust_since
WHEN NOT MATCHED
      THEN INSERT (custkey, name, state, zip, cust_since,last_update_dt)
            VALUES(l.custkey, l.name, l.state, l.zip, l.cust_since,l.last_update_dt);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This statement will insert new rows where the custkey doesn’t exist in the target table. It will update rows in the target table if the custkey matches and the name has changed. Of course in real-world situations, there will be numerous columns that are checked to see if they have changed to issue an update. I chose name for this simple example but you can see the power of merge and why it’s a game changer for a modern data lake.&lt;/p&gt;

&lt;p&gt;Example 3: Slowly Changing Dimension (SCD Type 2)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MERGE INTO s3lakehouse.blog.customer_base as b
USING
( SELECT null as custkey_match, custkey, name, state, zip, cust_since, last_update_dt,'Y' as active_ind,current_timestamp as end_dt
FROM s3lakehouse.blog.customer_land
UNION ALL
SELECT
custkey as custkey_match,custkey, name, state, zip, cust_since, last_update_dt,active_ind,end_dt
FROM s3lakehouse.blog.customer_base
WHERE custkey IN
(SELECT custkey FROM s3lakehouse.blog.customer_land where active_ind = 'Y')
) as scdChangeRows
ON (b.custkey = scdChangeRows.custkey and b.custkey = scdChangeRows.custkey_match)
WHEN MATCHED and b.active_ind = 'Y' THEN
UPDATE SET end_dt = current_timestamp,active_ind = 'N'
WHEN NOT MATCHED THEN
        INSERT (custkey, name, state, zip, cust_since,last_update_dt,active_ind,end_dt)
            VALUES(scdChangeRows.custkey, scdChangeRows.name, scdChangeRows.state, scdChangeRows.zip,
                    scdChangeRows.cust_since,scdChangeRows.last_update_dt,'Y',null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A SCD Type 2 simply means we insert new rows and we also “end date” existing rows and insert a new row. This allows history to be maintained in a single table. This is a data warehousing technique that has been around for a long time. The ability to perform this feature in a data lake is new though and opens up a choice to provide data warehousing features right out of a cloud storage.&lt;/p&gt;

&lt;p&gt;There is a lot going on in this merge so we’ll cover a few points. We first select data from the landing table and union it with our base table while only pulling active rows. From there, we insert any new rows in addition to any modified rows. Lastly, we update or “end date” the old row by setting the active_ind to ‘N’ and providing a date in the end_dt column.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimize
&lt;/h3&gt;

&lt;p&gt;As your Iceberg tables grow and have many operations performed against them, it’s a good idea to optimize them from time to time. The optimize command not only makes small files larger for better performance, it also cleans up the metadata which improves queries due to less metadata that needs to be read.&lt;/p&gt;

&lt;p&gt;To scan the table for small files and make them larger, you simply issue the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table &amp;lt;table&amp;gt; execute optimize;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will look for any files under 100MB and combine them into larger ones. You can also choose the file size if 100MB:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ALTER TABLE &amp;lt;table&amp;gt; EXECUTE optimize(file_size_threshold =&amp;gt; '10MB')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your Iceberg table becomes very large and the optimize command above is taking too long to run, you can just optimize the files that have arrived recently:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table &amp;lt;table&amp;gt; execute optimize where $file_modified_time &amp;gt; &amp;lt;yesterday&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will look for files that have arrived since yesterday and optimize them. On a very active table where lots of changes are taking place, this will greatly reduce the amount of time the optimize command takes.&lt;/p&gt;

&lt;p&gt;For tables that are being modified constantly, it’s a good idea to optimize at regular intervals.&lt;/p&gt;

&lt;p&gt;We’ve covered one of the most powerful features of Iceberg with Trino which allows database type updates/deletes/merges to be performed on your modern data lake. This opens the door to increasing the use cases and reliability of the data lake on a variety of clouds and platforms.&lt;/p&gt;

</description>
      <category>iceberg</category>
      <category>trino</category>
      <category>datalake</category>
      <category>aws</category>
    </item>
    <item>
      <title>Iceberg Partitioning and Performance Optimizations in Trino</title>
      <dc:creator>Tom Nats</dc:creator>
      <pubDate>Tue, 01 Aug 2023 12:43:00 +0000</pubDate>
      <link>https://dev.to/starburstdata/iceberg-partitioning-and-performance-optimizations-in-trino-27a7</link>
      <guid>https://dev.to/starburstdata/iceberg-partitioning-and-performance-optimizations-in-trino-27a7</guid>
      <description>&lt;p&gt;&lt;em&gt;I originally posted this on &lt;a href="https://www.starburst.io/blog/iceberg-partitioning-and-performance-optimizations-in-trino-partitioning/"&gt;Starburst's blog&lt;/a&gt;, as part of a series I've been publishing on Iceberg.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Partitioning
&lt;/h3&gt;

&lt;p&gt;Partitioning is used to narrow down the scope of the data that needs to be read for a query. When dealing with big data, this can be crucial for performance and can be the difference between getting a query that takes minutes or even  hours down to seconds!&lt;/p&gt;

&lt;p&gt;One of the advantages of Apache Iceberg is how it handles partitions. One of the biggest drawbacks from using Hive based tables was the method on how you had to partition your data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EARkfGjL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zgnjxm2d0fu126658iaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EARkfGjL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zgnjxm2d0fu126658iaz.png" alt="Hive vs. Iceberg logos" width="470" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, most tables that you would plan to partition have some sort of date or timestamp that indicates when the row of data was created. Example table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BC3JJTuh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rwk2e8ogv55ybdu6pjpj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BC3JJTuh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rwk2e8ogv55ybdu6pjpj.png" alt="Table showing timestamp" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Hive, if you wanted to partition by day, you would have to break out the created_ts column into year, month and day. Then, you would have to teach your users to always include these columns in their queries even if they wanted to query on created_ts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create table hive.orders (event_id, integer, created_ts timestamp, metric integer, year varchar, month varchar, day varchar);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With Iceberg, you simply partition the data on created_ts using day and end users would query this table just like they would in a database. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- create iceberg table partitioned by day on the created_ts column
create table orders_iceberg 
(event_id integer, created_ts timestamp(6),metric integer)
with (type='iceberg',partitioning=ARRAY['day(created_ts)']);
-- insert rows
insert into orders_iceberg values (1,timestamp '2022-09-10 10:45:38.527000',5.5);
insert into orders_iceberg values (1,timestamp '2022-09-11 03:12:23.522000',5.5);
insert into orders_iceberg values (1,timestamp '2022-09-12 10:46:13.516000',5.5);
insert into orders_iceberg values (1,timestamp '2022-09-13 04:34:05.577000',5.5);
insert into orders_iceberg values (1,timestamp '2022-09-14 09:10:23.517000',5.5);
-- query the table only looking for certain days
select * from orders_iceberg where created_ts BETWEEN date '2022-09-10' AND date '2022-09-12';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data in S3 for example looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oF7rZv4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoymtfi8n85g2hm0sucx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oF7rZv4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoymtfi8n85g2hm0sucx.png" alt="S3 screenshot showing partitioned files by day" width="483" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trino is smart enough to read the Iceberg Manifest List and then only look at files that meet the partition requirement of the query. In the example above, it would only be 2022-09-10 and 2022-09-11. A list of functions to partition by can be found &lt;a href="https://trino.io/docs/current/connector/iceberg.html#partitioned-tables"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: Trino’s Iceberg implementation includes the timezone for the timestamp data type (timestamp(6)). This was a conscious decision based on industry standard of supporting timezones within timestamp data types that Hive didn’t support.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although we’ll cover this in a separate schema evolution blog, you aren’t stuck with this partitioning scheme. At any time, you can modify your partition column. For example, if we decided that partitioning on day is too granular, we can modify the table to now be partitioned by month:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table orders_iceberg SET PROPERTIES partitioning = ARRAY['month(created_ts)'];&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;New data will be created in directories named: created_ts_month=2022-09 for example. The existing data will remain partitioned by day unless the table is recreated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance and Optimizations
&lt;/h3&gt;

&lt;p&gt;When it comes to performance, Iceberg can be a very performant table format. This is because metadata is stored about all of the files that “belong” to a table for a given snapshot in time along with statistics about each one which helps with “file skipping”. This is a fancy term for files that do not need to be read based on the query that is issued.&lt;/p&gt;

&lt;p&gt;With partitioning, the field of files is narrowed down even further by first only looking at the metadata for files after partition pruning is completed then looking at the metadata of the remaining files. When data is ordered by columns that appear in a where clause, this can greatly improve the performance of selective queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--woneTW8_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbhs7hqkp9bxhrkpbhj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--woneTW8_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbhs7hqkp9bxhrkpbhj3.png" alt="Flow chart showing from current snapshot to file statistics" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The manifest file contains information about the different files that belong to the table. Each entry has the location of the file in addition to statistics such as the minimum and maximum value for each column, the number of nulls and other useful information. Trino will use this metadata about each file to determine if the file needs to be read. If the data is sorted by “id” and a where clause has predicate similar to: where id = 5 then this query will see a large performance improvement because only a handful of files (if not just one) will need to be read.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing for performance
&lt;/h3&gt;

&lt;p&gt;Iceberg includes some file management features that help with performance. Traditional data lakes have use cases where there is constant data being ingested. This data is written in small files because of the need to have it available to be queried immediately. This can hurt performance in any system that needs to read a bunch of small files especially in cloud storage. Iceberg includes an optimize feature that combines small files into larger ones ensuring maximum performance when it comes to querying.&lt;/p&gt;

&lt;p&gt;The idea here is you want to ingest data as fast as possible, making it available for queries even though it might not be of the highest performance, then offer the ability to combine those files into larger ones at a given interval.&lt;/p&gt;

&lt;p&gt;To scan the table for small files and make them larger, you simply issue the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alter table &amp;lt;table&amp;gt; execute optimize;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will look for any files under 100MB and combine them into larger ones. You can also choose the file size of 100MB:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ALTER TABLE &amp;lt;table&amp;gt; EXECUTE optimize(file_size_threshold =&amp;gt; '10MB')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your Iceberg table becomes very large and the optimize command above is taking too long to run, you can just optimize the files that have arrived recently:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ALTER TABLE &amp;lt;table&amp;gt; EXECUTE optimize where "$file_modified_time" &amp;gt; current_date - interval '1' day;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will look for files that have arrived since yesterday and optimize them. On a very active table where lots of changes are taking place, this will greatly reduce the amount of time the optimize command takes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Network events are streamed in 1 minute intervals. Small files are dropped into an S3 bucket using the Iceberg API and the data is available immediately using standard SQL. Based on the volume of data and the files created, the optimize command can be run at given intervals to consolidate these smaller files into larger ones. This will greatly improve the performance of of subsequent queries against this table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l7FGYtOJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k9cymhek313gapkhjdt0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l7FGYtOJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k9cymhek313gapkhjdt0.png" alt="Example of using the optimize command in Iceberg" width="676" height="1078"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleaning up snapshots
&lt;/h3&gt;

&lt;p&gt;From time to time, older snapshots of tables should be cleaned up. These older snapshots contain previous states of the table which are no longer needed.&lt;/p&gt;

&lt;p&gt;There are two operations that clean up old snapshots and data. One is “expire_snapshots” and the other is “remove_orphan_files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://trino.io/docs/current/connector/iceberg.html#expire-snapshots"&gt;expire_snapshots&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
This function removes snapshots that are older than the value provided during the execution. An example is the below command that will remove snapshots that are older than 7 days:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ALTER TABLE &amp;lt;table&amp;gt; EXECUTE expire_snapshots(retention_threshold =&amp;gt; ‘7d’)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hWXwflcL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhc79l587upinucyi7xh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hWXwflcL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhc79l587upinucyi7xh.png" alt="Diagram depicting deletion of snapshots after 7 days" width="709" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;[remove_orphan_files](https://trino.io/docs/current/connector/iceberg.html#remove-orphan-files)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This function removes files that are left on storage when a query is unable to complete for a variety of reasons. This doesn’t happen too often but it’s a good idea to include this when you run snapshot cleanups. A similar alter table statement is used as shown in the this example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ALTER TABLE test_table EXECUTE remove_orphan_files(retention_threshold =&amp;gt; ‘7d’)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Iceberg + Trino brings some very exciting features along with tremendous performance advantages to complete your modern data lake.&lt;/p&gt;

</description>
      <category>iceberg</category>
      <category>trino</category>
      <category>partitioning</category>
      <category>performance</category>
    </item>
    <item>
      <title>Introduction to Apache Iceberg in Trino</title>
      <dc:creator>Tom Nats</dc:creator>
      <pubDate>Wed, 26 Jul 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/starburstdata/introduction-to-apache-iceberg-in-trino-48g0</link>
      <guid>https://dev.to/starburstdata/introduction-to-apache-iceberg-in-trino-48g0</guid>
      <description>&lt;p&gt;&lt;em&gt;I originally &lt;a href="https://www.starburst.io/blog/introduction-to-apache-iceberg-in-trino/"&gt;posted this on Starburst's blog&lt;/a&gt;, as part of a series I've been publishing on Iceberg.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;Apache Iceberg is an open source table format that brings database functionality to object storage such as S3, Azure’s ADLS, Google Cloud Storage and MinIO. This allows an organization to take advantage of low-cost, high performing cloud storage while providing data warehouse features and experience to their end users without being locked into a single vendor.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Apache Iceberg?
&lt;/h3&gt;

&lt;p&gt;Apache Iceberg is a table format, originally created by Netflix, that provides database type functionality on top of object stores such as Amazon S3. Iceberg allows organizations to finally build true data lakehouses in an open architecture, avoiding vendor and technology lock-in.&lt;/p&gt;

&lt;p&gt;The excitement around Iceberg began last year and has greatly increased in 2022. Most of the customers and prospects I speak with on a weekly basis are either considering migrating their existing Hive tables to it or have already started. They are excited a true open source table format has been created with many engines both open source and proprietary jumping on board.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of Apache Iceberg
&lt;/h3&gt;

&lt;p&gt;One of the best things about Iceberg is the vast adoption by many different engines. In the diagram below, you can see many different technologies can work the same set of data as long as they use the open-source Iceberg API. As you can see, the popularity and work that each engine has done is a great indicator of the popularity and usefulness that this exciting technology brings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z8b-TEEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsa0kczz269325eiec7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z8b-TEEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsa0kczz269325eiec7w.png" alt="Diagram showing how Iceberg works with many query engines and many object stores" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With more and more technologies jumping on board, Iceberg isn’t a passing fad. It has been growing in popularity, not only because of how useful it is, but also because it’s truly an open source table format, many companies have contributed and helped improve the specification making it a true community based effort.&lt;/p&gt;

&lt;p&gt;Here is a list of the many features Iceberg provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choose your engine&lt;/strong&gt;: As you can see from the diagram above, there are many engines that support Iceberg. This offers the ultimate flexibility to own your own data and choose the engine that fits your use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid Data Lock-in&lt;/strong&gt;: The data Iceberg and these engines work on, is YOUR data in YOUR account which avoids data lock-in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid Vendor Lock-out&lt;/strong&gt;: Iceberg metadata is always available to all engines. So you can guarantee consistency, even with multiple writers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DML (modifying table data)&lt;/strong&gt;: Modifying data in Hadoop was a huge challenge. With Iceberg, data can easily be modified to adhere to use cases and compliance such as GDPR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema evolution&lt;/strong&gt;: Much like a database, Iceberg supports full schema evolution including columns and even partitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Since Iceberg stores a table state in a snapshot, the engine simply needs to read the metadata in that snapshot then start retrieving the data from storage saving valuable time and reduced cloud object store retrieval costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database feel&lt;/strong&gt;: Partitioning is performed on any column and end users query Iceberg tables just like they would a database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Iceberg Architecture
&lt;/h2&gt;

&lt;p&gt;Iceberg is a layer of metadata over your object storage. It provides a transaction log per table very similar to a traditional database. This log keeps track of the current state of the table including any modifications. It also keeps a current “snapshot” of the files that belong to the table and statistics about them in order to reduce the amount of data that is needed to be read during queries greatly, improving performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xCBDJXu9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9ccooorons5fynfn2v8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xCBDJXu9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9ccooorons5fynfn2v8.png" alt="Diagram showing how metadata is layered over object storage with Iceberg" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Snapshots
&lt;/h4&gt;

&lt;p&gt;Everytime a modification to an Iceberg table is performed, (insert, update, delete, etc.) a new snapshot of the table is created. When an Iceberg client (let’s say Trino) wants to query a table, the latest snapshot is read and the files that “belong” to that snapshot are read. This makes a very powerful feature called time travel available because the table at any given point contains a set of snapshots over time which can be queried with the proper syntax.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--caBY5M6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7j152tdc8inkl2eid5j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--caBY5M6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7j152tdc8inkl2eid5j5.png" alt="Example of how snapshots of taken at different points in time" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under the covers, Iceberg uses a set of Avro-based files to keep track of this metadata. A Hive compatible metastore is used to “point” to the latest metadata file that has the current state of the table. All engines that want to interact with the table first get the latest “pointer” from the metastore then start interacting with Iceberg metadata files from there.&lt;/p&gt;

&lt;p&gt;Here is a very basic diagram of the different files that are created during a CTAS (create table as select):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hNsx5Ioj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1flpxvmoz93qovipll6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hNsx5Ioj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1flpxvmoz93qovipll6q.png" alt="Diagram of the different files that are created during a CTAS" width="434" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata File Pointer (fp1)&lt;/strong&gt; – This is an entry in a Hive compatible metastore (AWS Glue for example) that points to the current metadata file. This is the start to any query against an Iceberg table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata File (mf1)&lt;/strong&gt; – A json file that contains the latest version of a table. Any changes made to a table create a new metadata file. The contents of this file are simply lists of manifest list files with some high level metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manifest List (ml1)&lt;/strong&gt; – List of manifest files that make up a snapshot. This also includes metadata such as partition bounds in order to skip files that do not need to be read for the query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manifest File (mf1)&lt;/strong&gt; – Lists a set of files and metadata about these files. This is the final step for a query as only files that need to be read are determined using these files saving valuable querying time.&lt;/p&gt;

&lt;p&gt;Here is a sample table named &lt;code&gt;customer_iceberg&lt;/code&gt; that was created on S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_iceberg-a0ae01bc83cb44c5ad068dc3289aa1b9/
  data/
    20221005_142356_18493_dnvqc-43a7f422-d402-41d8-aab3-38d88f9a8810.orc
    20221005_142356_18493_dnvqc-548f81e0-b9c3-4015-99a7-d0f19416e39c.orc
  metadata/
    00000-8364ea6c-5e89-4b17-a4ea-4187725b8de6.metadata.json
    54d59fe-8368-4f5e-810d-4331dd3ee243-m0.avro
    snap-2223082798683567304-1-88c32199-6151-4fc7-97d9-ed7d9172d268.avro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Table directory&lt;/strong&gt; – This is the name of the table with a unique uuid in order to support table renames.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data directory&lt;/strong&gt; – This holds the Orc, Parquet or Avro files and could contain subdirectories depending on if the table is partitioned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata directory&lt;/strong&gt; – This directory holds the manifest files as covered above.&lt;/p&gt;

&lt;p&gt;Again, this might be too nitty-gritty for the average user but the point is a tremendous amount of thought and work has been put into Iceberg to ensure it can handle many different types of analytical queries along with real-time ingestion. It was built to fill the gap between low-cost, cloud object stores and the demanding processing engines such as Trino and Spark.&lt;/p&gt;

&lt;h4&gt;
  
  
  Partitioning
&lt;/h4&gt;

&lt;p&gt;Using partitions in Iceberg is just like with a database. Most data you ingest into your data lake has a timestamp and partitioning by that column is very easy:&lt;/p&gt;

&lt;p&gt;Example – partition by month from a timestamp column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create table orders_iceberg
with (partitioning=ARRAY['month(create_date),region'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Querying using a standard where clause against the partitioned column will result in partition pruning and much higher performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select * from orders_iceberg
WHERE CAST(create_date AS date) BETWEEN date '1993-06-01' AND date '1993-11-30';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trino's Iceberg Support
&lt;/h3&gt;

&lt;p&gt;Trino has full support for Iceberg with a feature matrix listed below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Table&lt;/li&gt;
&lt;li&gt;Modify Table (update/delete/merge)&lt;/li&gt;
&lt;li&gt;Add/Drop/Modify table column&lt;/li&gt;
&lt;li&gt;Rename table&lt;/li&gt;
&lt;li&gt;Rollback to previous snapshot&lt;/li&gt;
&lt;li&gt;View support (includes AWS Glue)&lt;/li&gt;
&lt;li&gt;Time travel&lt;/li&gt;
&lt;li&gt;Maintenance (Optimize/Expire Snapshots)&lt;/li&gt;
&lt;li&gt;Alter table partition&lt;/li&gt;
&lt;li&gt;Metadata queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using Iceberg in Trino is very easy. There is a dedicated connector page located &lt;a href="https://trino.io/docs/current/connector/iceberg.html"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you're new to Trino, &lt;a href="https://www.starburst.io/platform/starburst-galaxy/"&gt;Starburst Galaxy's&lt;/a&gt; free tier is the easiest and fastest way to test out the power of Trino and Iceberg. &lt;/p&gt;

</description>
      <category>iceberg</category>
      <category>trino</category>
      <category>sql</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
