<?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: Antônio Inocêncio</title>
    <description>The latest articles on DEV Community by Antônio Inocêncio (@antoninocencio).</description>
    <link>https://dev.to/antoninocencio</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%2F431720%2Fff70243f-7df4-4011-a549-83e387589426.png</url>
      <title>DEV Community: Antônio Inocêncio</title>
      <link>https://dev.to/antoninocencio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antoninocencio"/>
    <language>en</language>
    <item>
      <title>Start monitoring the cause, not the consequences.</title>
      <dc:creator>Antônio Inocêncio</dc:creator>
      <pubDate>Thu, 16 Nov 2023 13:01:44 +0000</pubDate>
      <link>https://dev.to/antoninocencio/start-monitoring-the-cause-not-the-consequences-3eb9</link>
      <guid>https://dev.to/antoninocencio/start-monitoring-the-cause-not-the-consequences-3eb9</guid>
      <description>&lt;p&gt;How a few queries were compromising the whole operation of a business.&lt;/p&gt;

&lt;p&gt;This situation happened to one of our customers and it shows how important it is to know exactly what is running in your database servers and to keep track of that.&lt;/p&gt;

&lt;p&gt;“MeusPedidos is a SaaS application that handle the processing of thousand of orders per second. Those Orders made by Industries, Sales Representative and Distributors can be done by users in their Web App, users of Mobile Apps, third party integrations of a public API and finally by some huge XLS Sheets imported directly in their Web App.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Initially, the RDS was running on a “m3.2xlarge” instance. After executing some tests, MeusPedidos’ team found some limitations in the “m3.2xlarge” instance with Multi-AZ (you can see more info about that here). They got in touch with the AWS Support and were told to upgrade to a “m4.2xlarge”.&lt;/p&gt;

&lt;p&gt;After the migration, there was a reduction in the frequency and intensity with which instabilities occurred in the system but it was still possible to identify Write Throughput and High DiskQueueDepth spikes with CloudWatch’s monitoring. As you can see in the picture below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ni4lXXll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebrzibh9mckynzp81lm6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ni4lXXll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebrzibh9mckynzp81lm6.png" alt="Write throughput and high DiskQueueDepth spikes (from CloudWatch)" width="720" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring with Nazar
&lt;/h2&gt;

&lt;p&gt;Then we started monitoring their application with Nazar to identify the offensive queries responsible for the spikes. After that we could then identify some infrequent queries with a high execution time.&lt;/p&gt;

&lt;p&gt;By crossing the time these queries were run and the peak times presented in CloudWatch, we identified the relationship between them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k54eI3dZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66una4bt57r5zf4ge9em.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k54eI3dZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66una4bt57r5zf4ge9em.png" alt="Write throughput and DiskQueueDepth spikes (from CloudWatch)" width="720" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause
&lt;/h2&gt;

&lt;p&gt;When these queries were executed, a relatively large amount of data was manipulated, which generated the need for disk write operations specially for the “Creating sort index” and “Sending data” stages in the execution plan.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--teohFRRr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf8ymo1qmx7im9bdrr8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--teohFRRr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf8ymo1qmx7im9bdrr8n.png" alt="Image description" width="720" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Duration 3.58268150&lt;br&gt;
Creating sort index 2.843240&lt;br&gt;
Sending data 0.721862&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;With Nazar it was very easy to identify the 3 queries that were presenting this characteristics and they were optimized by taking the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Index creation;&lt;/li&gt;
&lt;li&gt;SQL rewritten;&lt;/li&gt;
&lt;li&gt;Reducing the number of columns in the SELECT list;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After the implementation (04/18/2018) of the optimizations in the 3 queries identified, there was no more Write Throughput and High DiskQueueDepth spikes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XZa4rW_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d0kc009d1u8jcr5xlm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XZa4rW_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d0kc009d1u8jcr5xlm0.png" alt="No spikes after the optimizations" width="720" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The secret to anticipate that daily performance problems will scale and become critical issues is to observe them continuously and to monitor the cause, not the consequences. Even after upgrading to a higher instance the queries continued to be executed and to compromise the application’s performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The partnership with Nazar was really helpful to tackle down write throughput problems that were haunting us for a very long time." - Israel Fonseca — Senior Software Developer&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>databasetuning</category>
      <category>database</category>
      <category>databaseperformance</category>
      <category>aws</category>
    </item>
    <item>
      <title>Partial index</title>
      <dc:creator>Antônio Inocêncio</dc:creator>
      <pubDate>Tue, 14 Nov 2023 16:22:02 +0000</pubDate>
      <link>https://dev.to/antoninocencio/partial-index-49i</link>
      <guid>https://dev.to/antoninocencio/partial-index-49i</guid>
      <description>&lt;p&gt;A partial index is a valuable feature supported by various relational databases but is not commonly used. In this post, we will explore some use cases where you can benefit from using partial indexes.&lt;/p&gt;

&lt;p&gt;Partial indexes can be useful in various scenarios where you want to improve query performance by reducing the size of an index or filtering which rows are included in the index. Here are some examples of when to use partial indexes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Selective Indexing: When you have a large table with many rows but want to index only a subset of those rows that meet specific criteria. For example, you may have a table of customer orders, and you want to create an index only for orders that are not canceled (status != ‘canceled’).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Range Filtering: When you have a table with a date or timestamp column and want to create an index for a specific time range. This can be helpful for time-based data, like logs or sensor readings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Boolean or Enum Columns: When you have boolean or enum columns and want to create an index on specific values. For instance, you might have a table of products and want to index only the active products.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sparse Data: When you have a column with a low cardinality and want to create an index for a specific value that is not present in most rows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Query Optimization: When you have queries that frequently filter by a specific condition, a partial index can significantly speed up those queries. For example, if you often search for unshipped orders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Storage Optimization: Partial indexes can reduce the storage requirements of your database because they are smaller due to indexing a subset of rows. This can be especially valuable in scenarios with limited storage capacity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrency: In databases with high levels of concurrent write operations, partial indexes can reduce contention and locking conflicts by only indexing a subset of rows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s consider a scenario where you have a table for user accounts, and it includes a boolean column called active to track whether a user account is active (true) or inactive (false). In this case, most of the user accounts are inactive. We want to compare the use of a partial index and a non-partial index for this scenario.&lt;/p&gt;

&lt;p&gt;Non-Partial Index Example:&lt;/p&gt;

&lt;p&gt;Suppose you have a &lt;code&gt;user_accounts&lt;/code&gt; table with 8,4 million rows and the following schema:&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 tb_user_accounts (
    user_id serial PRIMARY KEY,
    username varchar(255) NOT NULL,
    active boolean NOT NULL
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create a non-partial index on the &lt;code&gt;active&lt;/code&gt; column, you would typically create an index that covers all rows, including both active and inactive users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE INDEX idx_all_users_active ON tb_user_accounts (active);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This index will include all user accounts, regardless of their active status.&lt;/p&gt;

&lt;p&gt;Partial Index Example:&lt;/p&gt;

&lt;p&gt;Now, let’s consider the scenario where you want to optimize queries that involve active user accounts (where &lt;code&gt;active = true&lt;/code&gt;). To do this, you can create a partial index as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE INDEX idx_active_users_active ON tb_user_accounts (user_id) 
WHERE active = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the &lt;code&gt;WHERE&lt;/code&gt; clause specifies that only rows where &lt;code&gt;active&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; will be included in the index. This results in a smaller, more focused index that includes only active user accounts, leading to better query 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 pg_size_pretty(pg_table_size('idx_all_users_active'));

pg_size_pretty|
--------------+
56 MB         |

select pg_size_pretty(pg_table_size('idx_active_users_active'));

pg_size_pretty|
--------------+
240 kB        |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above syntax is compatible with PostgreSQL. In Oracle Database and MySQL 8, you can create an function based index using a &lt;code&gt;CASE&lt;/code&gt; expression to effectively achieve a partial index-like behavior. Here's 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 INDEX idx_active_users_active ON user_accounts 
(CASE WHEN active = 1 THEN user_id ELSE NULL END);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In summary, a partial index is advantageous when you have specific criteria for indexing a subset of rows within a table, such as optimizing queries for active user accounts in a scenario where most rows have the value &lt;code&gt;active = false&lt;/code&gt;. It can lead to improved query performance, reduced storage requirements, and better concurrency handling compared to non-partial indexes, which index all rows indiscriminately.&lt;/p&gt;

&lt;p&gt;Written by Matheus Mendonça, DBA &amp;amp; co-founder at &lt;a href="https://nazar.ai"&gt;Nazar&lt;/a&gt;&lt;/p&gt;

</description>
      <category>databaseperformance</category>
      <category>sqltuning</category>
      <category>sql</category>
      <category>database</category>
    </item>
    <item>
      <title>[SQL Performance Killers] Individual inserts vs. Bulk inserts</title>
      <dc:creator>Antônio Inocêncio</dc:creator>
      <pubDate>Thu, 09 Nov 2023 18:52:06 +0000</pubDate>
      <link>https://dev.to/antoninocencio/sql-performance-killers-individual-inserts-vs-bulk-inserts-51h4</link>
      <guid>https://dev.to/antoninocencio/sql-performance-killers-individual-inserts-vs-bulk-inserts-51h4</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was written by my co-founder &lt;a href="https://twitter.com/matheusmo"&gt;Matheus&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An important part of my daily work is to help developers, database administrators, devops, sysadmins and project managers to identify (using &lt;a href="https://nazar.ai"&gt;Nazar.ai&lt;/a&gt;) and fix bad SQL code. And the idea with the [SQL Performance Killers] series is to share some practical sql tuning examples.&lt;/p&gt;

&lt;p&gt;Continuing (after a very long time) the [SQL performance killers series] in this post I’ll explain why bulk insert operations are generally faster than many individual insert operations.&lt;/p&gt;

&lt;p&gt;Bulk inserts are significantly faster than individual inserts when working with a database for several reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reduced network traffic:&lt;/strong&gt; Bulk inserts reduce the amount of data transferred between the application and the database server. In many cases, network latency can be a bottleneck for database performance. By sending a single batch of data, you can reduce the impact of network latency and improve efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reduced transaction overhead:&lt;/strong&gt; Each individual insert operation is typically wrapped in a transaction, which can lead to increased overhead due to transaction management. Bulk inserts can be enclosed in a single transaction, reducing the overhead associated with transaction management and ensuring data consistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Locking and concurrency:&lt;/strong&gt; When you perform many individual inserts, each insert may require locks on the affected rows, leading to potential contention and concurrency issues in a multi-user environment. Bulk inserts often lock the entire table or a specific set of rows, reducing contention and improving concurrency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logging and indexing:&lt;/strong&gt; Databases often maintain transaction logs and indexes to ensure data consistency and query performance. Bulk inserts are more efficient in terms of logging and indexing because they involve fewer transactions and updates to indexes.&lt;/p&gt;

&lt;p&gt;In the example below I inserted 40,000 rows in a sample table, first using individual inserts and then using bulk insert.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO TB_INSERT(NAME) VALUES ('AF9CB08DF4F7B71F033CC857ECF30C21');
INSERT INTO TB_INSERT(NAME) VALUES ('B16D3C99C04F223E362BC0E1B4FFE7CD');
...
INSERT INTO TB_INSERT(NAME) VALUES ('BEDB35BC448FD5F32F37B86BECFDF225');
INSERT INTO TB_INSERT(NAME) VALUES ('4436A24C954EA17AEE9E92D4F16FAD20');

UPDATED ROWS 40000
START TIME MON NOV 06 14:52:02 PST 2023
FINISH TIME MON NOV 06 14:52:22 PST 2023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO TB_INSERT(NAME) VALUES
('AF9CB08DF4F7B71F033CC857ECF30C21'),
('B16D3C99C04F223E362BC0E1B4FFE7CD'),
...
('BEDB35BC448FD5F32F37B86BECFDF225'),
('4436A24C954EA17AEE9E92D4F16FAD20');


UPDATED ROWS 40000
START TIME MON NOV 06 14:56:32 PST 2023
FINISH TIME MON NOV 06 14:56:34 PST 2023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, inserting 40,000 rows into a sample table using bulk load was 10 times faster than using individual inserts. While individual inserts took 20 seconds to complete, bulk inserts were done in only 2 seconds.&lt;/p&gt;

&lt;p&gt;However, it's essential to note that the suitability of bulk inserts depends on the database management system (DBMS) you are using and the specific use case. Always consider the characteristics of your database and the requirements of your application when deciding whether to use bulk inserts or individual inserts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It is not lack of hardware,&lt;br&gt;
It is not network traffic,&lt;br&gt;
It is not slow front ends,&lt;br&gt;
the main performance problem in the huge majority of database applications is bad SQL code."&lt;br&gt;
Joe Celko&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>database</category>
      <category>tuning</category>
      <category>sql</category>
      <category>databasetuning</category>
    </item>
  </channel>
</rss>
