<?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: Kanishga Subramani</title>
    <description>The latest articles on DEV Community by Kanishga Subramani (@kanishga_subramani_49ad73).</description>
    <link>https://dev.to/kanishga_subramani_49ad73</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3951880%2F08e2b1d3-1c3e-4280-91fc-99fd18e39198.jpg</url>
      <title>DEV Community: Kanishga Subramani</title>
      <link>https://dev.to/kanishga_subramani_49ad73</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kanishga_subramani_49ad73"/>
    <language>en</language>
    <item>
      <title>CH-Ops Scheduled Alerts: Know Before Things Break</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Sat, 03 Oct 2026 16:42:02 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/ch-ops-scheduled-alerts-know-before-things-break-3pdk</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/ch-ops-scheduled-alerts-know-before-things-break-3pdk</guid>
      <description>&lt;h2&gt;
  
  
  What is CH-Ops
&lt;/h2&gt;

&lt;p&gt;CH-Ops is a browser-based operations platform built for managing ClickHouse® deployments. Instead of relying entirely on the command line or HTTP APIs, it provides a unified interface for executing SQL queries, monitoring clusters, managing users, backups, alerts, dashboards, and much more, all from a single web application.&lt;/p&gt;

&lt;p&gt;One of its useful features is &lt;strong&gt;Alert Rules&lt;/strong&gt;, which lets you turn a SQL query into a scheduled check and notify you when a defined condition is met.&lt;/p&gt;

&lt;p&gt;In this walkthrough, we'll use Email (SMTP) as the notification channel and create a simple scheduled alert from the CH-Ops interface. Let's see how you can create, test, and manage alerts without building a separate monitoring script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing CH-Ops Alert Rules
&lt;/h2&gt;

&lt;p&gt;Monitoring a ClickHouse® cluster is not only about looking at dashboards. Some conditions are easier to detect with a simple query.&lt;/p&gt;

&lt;p&gt;For example, you may want to know when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of failed operations goes above a threshold.&lt;/li&gt;
&lt;li&gt;A particular metric exceeds an expected value.&lt;/li&gt;
&lt;li&gt;A query returns an unexpected result.&lt;/li&gt;
&lt;li&gt;A cluster condition needs attention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of manually running the same query again and again, Alert Rules allows you to schedule the query and define what should trigger an alert. Once the rule is enabled, CH-Ops periodically executes the query and checks its result against the configured condition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up an email notification channel
&lt;/h2&gt;

&lt;p&gt;Before creating an alert, you need a notification channel that CH-Ops can use to deliver the alert.&lt;/p&gt;

&lt;p&gt;You can find this under:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control Panel → Notification Channels&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;New&lt;/strong&gt; to create a notification channel.&lt;/p&gt;

&lt;p&gt;For this walkthrough, select &lt;strong&gt;Email (SMTP)&lt;/strong&gt; as the notification type. The form provides fields for configuring the email connection:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What it's for&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;A name to identify the notification channel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMTP Host&lt;/td&gt;
&lt;td&gt;The mail server address used to send the alert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMTP Port&lt;/td&gt;
&lt;td&gt;The port that server listens on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMTP User&lt;/td&gt;
&lt;td&gt;The account CH-Ops authenticates as&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMTP Password&lt;/td&gt;
&lt;td&gt;That account's password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;From&lt;/td&gt;
&lt;td&gt;The sender address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;To&lt;/td&gt;
&lt;td&gt;The recipient address&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fill in the SMTP details provided by your email service and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once configured, the channel can be reused by alert rules instead of configuring email details every time you create a new alert.&lt;/p&gt;

&lt;p&gt;After creating an Email (SMTP) notification channel, use its &lt;strong&gt;Test&lt;/strong&gt; button to verify that the configured SMTP details can successfully send a test notification. A successful test confirms that the notification channel is ready to be used by alert rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an Alert Rule
&lt;/h2&gt;

&lt;p&gt;After configuring the notification channel, go to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Alerts → Alert Rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you haven't created any rules yet, CH-Ops displays an empty state with a &lt;strong&gt;New Rule&lt;/strong&gt; button. Click &lt;strong&gt;New Rule&lt;/strong&gt; to start creating an alert.&lt;/p&gt;

&lt;p&gt;The alert rule form brings the main configuration into one place.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Identifies the rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Severity&lt;/td&gt;
&lt;td&gt;How serious a firing alert is, for example warning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schedule (cron)&lt;/td&gt;
&lt;td&gt;How often the rule runs, for example &lt;code&gt;*/5 * * * *&lt;/code&gt; for every five minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operator&lt;/td&gt;
&lt;td&gt;How the result is compared to the threshold, for example greater than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threshold&lt;/td&gt;
&lt;td&gt;The value the operator compares against&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL (single value)&lt;/td&gt;
&lt;td&gt;A query that returns one number to check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Description&lt;/td&gt;
&lt;td&gt;Context shown when the alert fires (optional), but it is useful for documenting what the alert is checking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster&lt;/td&gt;
&lt;td&gt;Which cluster the rule runs against, defaulting to all clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target nodes&lt;/td&gt;
&lt;td&gt;Which nodes to run on, left empty to mean all nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before configuring, the Alert Rule also has its own &lt;strong&gt;Test&lt;/strong&gt; button. It executes the configured SQL query and checks the result against the defined condition and threshold.&lt;/p&gt;

&lt;p&gt;This lets you verify the alert logic before relying on the scheduled rule.&lt;/p&gt;

&lt;p&gt;Once created, the channel shows up as a card with its status, when it was last tested, and Edit, Enable, and Delete actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Query to Notification
&lt;/h2&gt;

&lt;p&gt;Once the alert is created, the workflow becomes straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Run the SQL query&lt;/strong&gt;&lt;br&gt;
CH-Ops executes the configured query according to the schedule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Check the result&lt;/strong&gt;&lt;br&gt;
The returned value is evaluated against the configured operator and threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Determine whether the condition is met&lt;/strong&gt;&lt;br&gt;
If the result satisfies the alert condition, the rule is triggered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Send the notification&lt;/strong&gt;&lt;br&gt;
The configured notification channel is used to deliver the alert. This turns a SQL query into a simple scheduled monitoring mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple End-to-End Flow
&lt;/h2&gt;

&lt;p&gt;The complete workflow can be summarized as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Query → Schedule → Condition → Notification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The important idea is that you don't need a separate script or external scheduler just to repeatedly check a SQL-based condition.&lt;/p&gt;

&lt;p&gt;CH-Ops brings the query, condition, schedule, and notification configuration together in one interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Worth Setting Up
&lt;/h2&gt;

&lt;p&gt;The value of an alert is how early it catches a problem. A five-minute schedule can flag an issue long before someone notices it during a manual dashboard check. Keeping the alert rule and notification channel separate also means you can change how you receive alerts without rewriting the alert logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email (SMTP)&lt;/strong&gt; is available in the open source version and is enough for many setups. CH-Ops Pro adds additional notification channels (&lt;strong&gt;Slack, Microsoft Teams, Google Chat, and PagerDuty&lt;/strong&gt;) for teams that need more immediate delivery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore CH-Ops Pro features:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.ch-ops.io/blog/clickhouse-alerts-in-slack-teams-google-chat-pagerduty" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/clickhouse-alerts-in-slack-teams-google-chat-pagerduty&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CH-Ops Installation Guide:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.ch-ops.io/blog/install-ch-ops-in-10-minutes-docker-binary-or-source" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/install-ch-ops-in-10-minutes-docker-binary-or-source&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CH-Ops Official GitHub Repository:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/Quantrail-Data/CH-Ops/tree/main" rel="noopener noreferrer"&gt;https://github.com/Quantrail-Data/CH-Ops/tree/main&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CH-Ops Demo Video:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.linkedin.com/posts/quantrail-data_clickhouse-opensource-dataengineering-activity-7489909665477091328-QOIY/" rel="noopener noreferrer"&gt;https://www.linkedin.com/posts/quantrail-data_clickhouse-opensource-dataengineering-activity-7489909665477091328-QOIY/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next in the CHOps series
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CH-Ops Access Control and App Data Backups&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>clickhouse</category>
      <category>database</category>
    </item>
    <item>
      <title>Search ClickHouse® Logs Without Writing SQL</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Tue, 29 Sep 2026 16:16:04 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/search-clickhouser-logs-without-writing-sql-1785</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/search-clickhouser-logs-without-writing-sql-1785</guid>
      <description>&lt;p&gt;When something goes wrong in a ClickHouse® deployment, logs are often the first place to look. But investigating them traditionally means knowing which system table to query, writing SQL, choosing the right time range, and filtering through the results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CH-Ops&lt;/strong&gt; provides a visual way to explore these logs without writing SQL.&lt;/p&gt;

&lt;p&gt;From the &lt;strong&gt;Logs&lt;/strong&gt; section, you can choose a log type, select a time range, view an overview of the activity, and switch to &lt;strong&gt;Search&lt;/strong&gt; when you need to investigate individual log records.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is CH-Ops?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CH-Ops&lt;/strong&gt; is a browser-based operations platform for ClickHouse® that provides a visual interface for managing and monitoring ClickHouse® deployments.&lt;/p&gt;

&lt;p&gt;Instead of relying entirely on the command line or HTTP API, CH-Ops brings common operational tasks into a single web application. It supports capabilities such as SQL querying, cluster monitoring, user management, backups, alerts, dashboards, and log exploration.&lt;/p&gt;

&lt;p&gt;In this article, we'll focus on the &lt;strong&gt;Logs&lt;/strong&gt; section and how CH-Ops makes ClickHouse® log investigation easier without requiring SQL for every search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring ClickHouse® Logs in CH-Ops
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Logs&lt;/strong&gt; section provides dedicated views for different ClickHouse® system logs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Crash Log&lt;/strong&gt; - investigate server crashes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Log&lt;/strong&gt; - understand recurring error types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text Log&lt;/strong&gt; - explore server messages across different log levels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Log&lt;/strong&gt; - review login and logout activity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each log provides two views:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overview&lt;/strong&gt; - understand the overall activity for a selected time range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt; - find specific records using filters relevant to that log.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick time ranges such as &lt;strong&gt;1h, 6h, 24h, 48h, 7d, and 30d&lt;/strong&gt; make it easy to focus on a specific period.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Crash Log: Investigating Server Crashes
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Crash Log&lt;/strong&gt; is based on &lt;code&gt;system.crash_log&lt;/code&gt; and is useful when a ClickHouse® process unexpectedly stops or a node restarts.&lt;/p&gt;

&lt;p&gt;The Overview provides a quick summary of crash activity, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total crashes&lt;/li&gt;
&lt;li&gt;Distinct signals&lt;/li&gt;
&lt;li&gt;Affected ClickHouse® versions&lt;/li&gt;
&lt;li&gt;Recent crash activity&lt;/li&gt;
&lt;li&gt;Crash distribution by signal and version&lt;/li&gt;
&lt;li&gt;Crash incidents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Crash Log Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once a crash is identified, the &lt;strong&gt;Search&lt;/strong&gt; view can be used to inspect individual crash records using details such as the event time, signal, query information, and exception trace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crash Log Search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This makes it easier to move from &lt;strong&gt;“Did a crash happen?”&lt;/strong&gt; to &lt;strong&gt;“What exactly was recorded when it happened?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An empty Crash Log can also be a good result. If no crashes were recorded during the selected period, there may simply be nothing to investigate.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Error Log: Finding Recurring Errors
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Error Log&lt;/strong&gt; is based on &lt;code&gt;system.error_log&lt;/code&gt; and helps identify the types of errors occurring on the server.&lt;/p&gt;

&lt;p&gt;The Overview summarizes the selected period through metrics and visualizations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total errors&lt;/li&gt;
&lt;li&gt;Number of error types&lt;/li&gt;
&lt;li&gt;Local versus remote errors&lt;/li&gt;
&lt;li&gt;Top error types&lt;/li&gt;
&lt;li&gt;Latest error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Error Log Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Top Error Types&lt;/strong&gt; chart provides a quick starting point. If one type accounts for a large portion of the errors, you can investigate that category further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching Error Logs
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Search&lt;/strong&gt; view lets you investigate error records without writing SQL. You can select a &lt;strong&gt;time range&lt;/strong&gt;, filter by &lt;strong&gt;Error Type&lt;/strong&gt;, search by &lt;strong&gt;Error Message&lt;/strong&gt;, and set the &lt;strong&gt;row limit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Log Search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The search results show the matching records along with details such as the &lt;strong&gt;event time, error type, error message, and query ID&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Text Log: Understanding Server Activity
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Text Log&lt;/strong&gt; is based on &lt;code&gt;system.text_log&lt;/code&gt; and provides a broader view of messages generated by the ClickHouse® server.&lt;/p&gt;

&lt;p&gt;Unlike the Error Log, it isn't limited to errors. It includes messages across different log levels, making it useful when something appears unusual but hasn't necessarily resulted in an error.&lt;/p&gt;

&lt;p&gt;The Overview provides information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total log lines&lt;/li&gt;
&lt;li&gt;Errors&lt;/li&gt;
&lt;li&gt;Warnings&lt;/li&gt;
&lt;li&gt;Number of loggers&lt;/li&gt;
&lt;li&gt;Recent activity&lt;/li&gt;
&lt;li&gt;Log volume by level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Text Log Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Log Volume by Level&lt;/strong&gt; chart provides a quick picture of server activity across the selected period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching Text Logs
&lt;/h3&gt;

&lt;p&gt;When you need to investigate a particular event, switch to the &lt;strong&gt;Search&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;You can filter logs by &lt;strong&gt;time range&lt;/strong&gt;, &lt;strong&gt;log level&lt;/strong&gt;, and &lt;strong&gt;message&lt;/strong&gt;, and set the number of results to display.&lt;/p&gt;

&lt;p&gt;The Search view presents the individual log records, including information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event time&lt;/li&gt;
&lt;li&gt;Log level&lt;/li&gt;
&lt;li&gt;Query ID&lt;/li&gt;
&lt;li&gt;Logger name&lt;/li&gt;
&lt;li&gt;Message&lt;/li&gt;
&lt;li&gt;Source file and line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Text Log Search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This lets you quickly narrow down relevant server activity and inspect the underlying records &lt;strong&gt;without writing SQL against &lt;code&gt;system.text_log&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Session Log: Tracking Login Activity
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Session Log&lt;/strong&gt; is based on &lt;code&gt;system.session_log&lt;/code&gt; and provides visibility into login and logout activity.&lt;/p&gt;

&lt;p&gt;The Overview summarizes session activity through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total events&lt;/li&gt;
&lt;li&gt;Successful logins&lt;/li&gt;
&lt;li&gt;Failed logins&lt;/li&gt;
&lt;li&gt;Logouts&lt;/li&gt;
&lt;li&gt;Distinct users&lt;/li&gt;
&lt;li&gt;Recent activity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Session Log Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Login Outcomes&lt;/strong&gt; chart provides a quick view of successful logins and logouts, while &lt;strong&gt;Top Users&lt;/strong&gt; highlights the accounts generating session activity.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Search&lt;/strong&gt; view can be used when you need to investigate specific session activity. You can filter events by &lt;strong&gt;event type, user, or failure reason&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This makes it easier to investigate authentication activity directly from CH-Ops without manually querying &lt;code&gt;system.session_log&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Overview to Search
&lt;/h2&gt;

&lt;p&gt;Across the different log types, CH-Ops follows a simple investigation pattern:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose a log → Select a time range → Load the Overview → Identify something interesting → Switch to Search → Investigate the records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Overview&lt;/strong&gt; helps you understand the bigger picture, while &lt;strong&gt;Search&lt;/strong&gt; helps you narrow down specific events and investigate the underlying records.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Use Each Log?
&lt;/h2&gt;

&lt;p&gt;Each log answers a different operational question:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Log&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Useful when you want to know&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crash Log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Did the ClickHouse® process crash, and what was recorded?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What types of errors are occurring, and how frequently?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Text Log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What has the ClickHouse® server been reporting about its activity?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Session Log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Who has been connecting, and are login attempts succeeding or failing?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Overview helps identify the problem, while Search helps investigate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Investigation to Alerts
&lt;/h2&gt;

&lt;p&gt;Logs help you investigate what has already happened. For conditions that require proactive attention, &lt;strong&gt;CH-Ops Alert Rules&lt;/strong&gt; can notify you when defined conditions occur.&lt;/p&gt;

&lt;p&gt;The workflow becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor → Detect → Alert → Investigate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;Alerts&lt;/strong&gt; to be notified about important conditions, and &lt;strong&gt;Logs&lt;/strong&gt; to investigate the details behind them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing ClickHouse® Log Investigation Into One Interface
&lt;/h2&gt;

&lt;p&gt;ClickHouse® system logs contain valuable information for monitoring and troubleshooting, but accessing them shouldn't always require writing SQL.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;CH-Ops&lt;/strong&gt;, you can explore &lt;strong&gt;Crash Log, Error Log, Text Log, and Session Log&lt;/strong&gt; from a single &lt;strong&gt;Logs&lt;/strong&gt; section. The &lt;strong&gt;Overview&lt;/strong&gt; gives you the big picture, while the &lt;strong&gt;Search&lt;/strong&gt; view lets you inspect the underlying records.&lt;/p&gt;

&lt;p&gt;Instead of starting every investigation with a SQL query, you can follow a simple visual workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Select the log → Choose the time range → Load the data → Understand the overview → Search the records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This makes ClickHouse® log investigation more accessible and helps you move from identifying an issue to finding the relevant details - &lt;strong&gt;without writing SQL for every search.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore CH-Ops
&lt;/h2&gt;

&lt;p&gt;Want to learn more about CH-Ops and explore its features? Visit the official website and explore the resources below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CH-Ops Official Website&lt;/li&gt;
&lt;li&gt;Getting Started with CH-Ops&lt;/li&gt;
&lt;li&gt;CH-Ops Official GitHub Repository&lt;/li&gt;
&lt;li&gt;CH-Ops Demo Video&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Next in the CHOps series
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;CH-Ops Scheduled Alerts: Know Before Things Break&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>analytics</category>
      <category>devops</category>
      <category>database</category>
    </item>
    <item>
      <title>Watch your clusters live on CHOps</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Fri, 25 Sep 2026 06:20:52 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/watch-your-clusters-live-on-chops-pb6</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/watch-your-clusters-live-on-chops-pb6</guid>
      <description>&lt;h2&gt;
  
  
  Complete Visibility into Your ClickHouse® Infrastructure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Monitor cluster health, analyze performance, track system resources, and gain real-time insights into every ClickHouse® node — all from a single, intuitive interface.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;ClickHouse® has established itself as a high-performance, real-time analytical database. However, sustaining sub-second query performance at scale requires continuous monitoring of database internals — including background pools, thread scheduling, storage parts, and I/O amplification.&lt;/p&gt;

&lt;p&gt;Our custom management platform, CHOps, provides real-time telemetry across every layer of your ClickHouse® nodes, bridging hardware metrics with native database engine states.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features &amp;amp; Dashboard Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Machine &amp;amp; Node Telemetry
&lt;/h3&gt;

&lt;p&gt;CHOps presents real-time gauge metrics updated dynamically, for example every 5 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Allocation:&lt;/strong&gt; CPU utilization, OS Memory, ClickHouse®-specific Memory, and Thread Pool saturation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node Summary:&lt;/strong&gt; Instant visibility into active database versions, total databases, table counts, active queries, running merges, and mutations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Disk &amp;amp; Storage Health Check
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disk Partitioning:&lt;/strong&gt; Tracks raw space utilization, with the default storage shown at 457.00 GiB and 53.6% used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part Formats:&lt;/strong&gt; Monitors the breakdown between Compact (76.46%) and Wide (23.54%) storage parts to help understand write behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System Health Checks:&lt;/strong&gt; Automated monitoring across 15+ critical health states, including Delayed inserts, Spilling to disk, Keeper expired, Readonly replicas, and Broken disks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Background Pools &amp;amp; Execution Shaping
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pool Capacity vs. Usage:&lt;/strong&gt; Real-time histograms for background tasks including Merges, Fetches, Moves, Schedule, Buffer Flush, Distributed operations, and Message Brokers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency &amp;amp; Compression Ratios:&lt;/strong&gt; Live tracking of Read Amplification, Write Amplification, and Read Compression.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read Amplification: &lt;strong&gt;566.1 rows/row&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Read Compression: &lt;strong&gt;16.0x&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Query &amp;amp; Time Execution Profiling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"Where the Time Goes":&lt;/strong&gt; Granular thread-level decomposition showing exact thread usage spent on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disk Read&lt;/li&gt;
&lt;li&gt;CPU User&lt;/li&gt;
&lt;li&gt;Merge Exec&lt;/li&gt;
&lt;li&gt;CPU Kernel&lt;/li&gt;
&lt;li&gt;Disk Write&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In-Flight Metrics:&lt;/strong&gt; Tracking active query locks, I/O in flight, open read/write operations, memory consumption by mapped files versus server runtime, and thread distribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboard Snapshot
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage Usage&lt;/td&gt;
&lt;td&gt;457 GiB (53.6% Used)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Part Breakdown&lt;/td&gt;
&lt;td&gt;Compact: 76.5% / Wide: 23.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Compression&lt;/td&gt;
&lt;td&gt;16.0x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Amplification&lt;/td&gt;
&lt;td&gt;566.1 rows/row&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Advantages of Using CHOps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Early Detection of Bottlenecks
&lt;/h3&gt;

&lt;p&gt;Instantly spot write-amplification spikes or thread pool exhaustion before queries degrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Compression Optimization
&lt;/h3&gt;

&lt;p&gt;Track part churn and row compression ratios to optimize storage policies and partition schemes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplified Cluster Management
&lt;/h3&gt;

&lt;p&gt;Monitor cluster topology, replica delays, and Keeper/ZooKeeper connections in a single unified dashboard.&lt;/p&gt;




&lt;h1&gt;
  
  
  ClickHouse® Architecture: Clusters &amp;amp; Nodes Explained
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. What is a ClickHouse® Node?
&lt;/h3&gt;

&lt;p&gt;A Node is a single running instance of the ClickHouse® server process (&lt;code&gt;clickhouse-server&lt;/code&gt;) on an isolated physical machine, virtual server, or container.&lt;/p&gt;

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

&lt;p&gt;A node stores local table parts using engines such as MergeTree, receives SQL queries, parses and compiles them, and processes data using vectorized multi-threaded execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Your Dashboard:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dashboard highlights individual nodes, such as &lt;code&gt;node-1&lt;/code&gt; (&lt;code&gt;localhost ::1:9000&lt;/code&gt;), showing specific runtime metrics such as uptime, active queries, CPU, and RAM allocation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What is a ClickHouse® Cluster?
&lt;/h3&gt;

&lt;p&gt;A Cluster is a logical grouping of multiple ClickHouse® nodes working together to handle large analytical datasets across distributed infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharding &amp;amp; Replication:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clusters can split dataset subsets across different nodes through sharding or duplicate data for high availability through replication, using Distributed table engines and ClickHouse® Keeper/ZooKeeper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Horizontal Scaling:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When data volume or query load grows beyond a single machine's capacity, adding nodes to a cluster expands storage capacity and query execution power in parallel.&lt;/p&gt;

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

&lt;p&gt;As analytical workloads grow in volume and complexity, maintaining deep visibility into database internals becomes a necessity — not a luxury.&lt;/p&gt;

&lt;p&gt;CHOps bridges the gap between high-level database administration and low-level engine telemetry, helping keep ClickHouse® clusters resilient, performant, and cost-efficient.&lt;/p&gt;

&lt;p&gt;By transforming complex metrics — such as memory footprints, thread pool allocation, write amplification, and part formats — into clear, real-time visual insights, CHOps helps data engineers and DevOps teams identify potential bottlenecks before they impact production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why CHOps telemetry is different
&lt;/h3&gt;

&lt;p&gt;Don't confuse general system-level host monitoring, such as standard Prometheus or Grafana OS exporters, with the engine-native telemetry provided by CHOps.&lt;/p&gt;

&lt;p&gt;Standard infrastructure tools primarily track raw host metrics.&lt;/p&gt;

&lt;p&gt;CHOps explicitly surfaces ClickHouse®-specific engine internals, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background merge queues&lt;/li&gt;
&lt;li&gt;Part formats such as Compact vs. Wide&lt;/li&gt;
&lt;li&gt;Read/write amplification&lt;/li&gt;
&lt;li&gt;Thread pool scheduling&lt;/li&gt;
&lt;li&gt;ClickHouse®-specific memory and execution metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog focuses specifically on deep ClickHouse® database performance optimization using CHOps.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CH-OPS Open Source (OSS) Version – GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Quantrail-Data/CH-Ops" rel="noopener noreferrer"&gt;https://github.com/Quantrail-Data/CH-Ops&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CH-OPS Website:&lt;/strong&gt; &lt;a href="https://www.ch-ops.io/" rel="noopener noreferrer"&gt;https://www.ch-ops.io/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CH-OPS Installation Guide:&lt;/strong&gt; &lt;a href="https://www.ch-ops.io/blog/install-ch-ops-in-10-minutes-docker-binary-or-source" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/install-ch-ops-in-10-minutes-docker-binary-or-source&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CH-Ops Demo Video:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/posts/quantrail-data_ClickHouse-opensource-dataengineering-activity-7489909665477091328-QOIY/" rel="noopener noreferrer"&gt;https://www.linkedin.com/posts/quantrail-data_ClickHouse-opensource-dataengineering-activity-7489909665477091328-QOIY/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Next in the CHOps series
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Search ClickHouse® Logs Without Writing SQL&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.ch-ops.io/blog/search-clickhouse-logs-without-writing-sql" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/search-clickhouse-logs-without-writing-sql&lt;/a&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>devops</category>
      <category>analytics</category>
      <category>database</category>
    </item>
    <item>
      <title>CH-Ops Schema Tools: Visualizer, Indexes &amp; Projections</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Fri, 25 Sep 2026 05:19:00 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/ch-ops-schema-tools-visualizer-indexes-projections-557k</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/ch-ops-schema-tools-visualizer-indexes-projections-557k</guid>
      <description>&lt;p&gt;A ClickHouse® deployment rarely stays simple. A materialized view gets added to pre-aggregate something, then a second MV reads its output. A dictionary shows up for user lookups, a Distributed table sits in front of a sharded local table — and soon the schema is fully documented in DDL, and still hard to hold in your head.&lt;/p&gt;

&lt;p&gt;The questions that come up are relational, not definitional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What feeds this table, and what does it feed?&lt;/li&gt;
&lt;li&gt;What breaks if I drop it?&lt;/li&gt;
&lt;li&gt;Which skipping indexes already exist here?&lt;/li&gt;
&lt;li&gt;Is there already a projection for this query pattern?&lt;/li&gt;
&lt;li&gt;How do I add or remove an index without hand-writing &lt;code&gt;ALTER&lt;/code&gt; statements and remembering to backfill?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can answer all of these by hand — &lt;code&gt;SHOW CREATE TABLE&lt;/code&gt;, &lt;code&gt;system.tables&lt;/code&gt;, cross-referencing MV definitions. It's just slow, and it gets worse fast once a pipeline crosses databases.&lt;/p&gt;

&lt;p&gt;That's the gap Schema Tools in CH-Ops fills.&lt;/p&gt;

&lt;p&gt;Four screens — Schema Visualizer, Data Skipping Indexes, Projections, Index Management — read as one workflow: see how the schema connects, check what optimization already exists, weigh whether a projection helps, then manage indexes when the evidence supports it.&lt;/p&gt;

&lt;p&gt;It doesn't remove SQL — every index or projection action shows the exact statement before running it. What it removes is the friction around that: manual inspection, hand-written DDL, and the easy-to-miss materialization step.&lt;/p&gt;

&lt;h2&gt;
  
  
  The running example
&lt;/h2&gt;

&lt;p&gt;Raw request logs land in &lt;code&gt;staging.raw_logs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An MV parses them into &lt;code&gt;analytics.parsed_logs&lt;/code&gt; — sorted by service, then time — which feeds two more MVs rolling requests into hourly metrics and error summaries, plus a Distributed table for sharded reads.&lt;/p&gt;

&lt;p&gt;A view in &lt;code&gt;reporting&lt;/code&gt; aggregates the metrics for a dashboard.&lt;/p&gt;

&lt;p&gt;There's also &lt;code&gt;analytics.users_dict&lt;/code&gt;, a dictionary loaded from a users table.&lt;/p&gt;

&lt;p&gt;Keep that sort order — service, then time — in mind. It's what decides which queries are already fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Visualizer: how is this schema connected?
&lt;/h2&gt;

&lt;p&gt;Reconstructing a pipeline from DDL by hand means reading each MV's source and target and searching for anything downstream, one hop at a time.&lt;/p&gt;

&lt;p&gt;Cross-database dependencies are the worst case, since checking one database at a time hides them entirely.&lt;/p&gt;

&lt;p&gt;The Schema Visualizer draws the graph instead: tables, MVs, dictionaries, Distributed tables, and views as cards, with lines showing data flow.&lt;/p&gt;

&lt;p&gt;It's read-only — nothing here creates, alters, or drops anything.&lt;/p&gt;

&lt;p&gt;It doesn't render the whole schema at once, on purpose — that's slow and unreadable.&lt;/p&gt;

&lt;p&gt;Pick a database, then a table, and CH-Ops traces every relationship in both directions, draws just that connected subgraph top to bottom, and auto-fits it.&lt;/p&gt;

&lt;p&gt;The traversal isn't limited to one database.&lt;/p&gt;

&lt;p&gt;Starting at &lt;code&gt;raw_logs&lt;/code&gt; pulls in all three databases on one canvas, each node labelled with its full name — the fastest way to find a dependency you didn't know existed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node types
&lt;/h3&gt;

&lt;p&gt;Headers are colour-coded:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MergeTree family — stores data&lt;/li&gt;
&lt;li&gt;MaterializedView — fires on every insert&lt;/li&gt;
&lt;li&gt;Refreshable MV — fires on a schedule&lt;/li&gt;
&lt;li&gt;Dictionary&lt;/li&gt;
&lt;li&gt;Distributed — routes to shards, stores nothing itself&lt;/li&gt;
&lt;li&gt;View — a saved query, recomputed on read&lt;/li&gt;
&lt;li&gt;Other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One glance tells you what stores data versus what just moves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigation
&lt;/h3&gt;

&lt;p&gt;Dropdowns pick what's drawn.&lt;/p&gt;

&lt;p&gt;Search matches table and column names, highlighting hits and dimming the rest — handy for questions like "which tables use &lt;code&gt;user_id&lt;/code&gt;."&lt;/p&gt;

&lt;p&gt;Columns toggle switches between compact and detailed cards.&lt;/p&gt;

&lt;p&gt;Fit and Re-layout handle the camera and layout separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  The sidebar
&lt;/h3&gt;

&lt;p&gt;Click a node and it opens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engine&lt;/li&gt;
&lt;li&gt;Partition key&lt;/li&gt;
&lt;li&gt;Sort order&lt;/li&gt;
&lt;li&gt;Primary key, if different&lt;/li&gt;
&lt;li&gt;Rows/bytes&lt;/li&gt;
&lt;li&gt;What it reads from&lt;/li&gt;
&lt;li&gt;What reads from it&lt;/li&gt;
&lt;li&gt;Full &lt;code&gt;CREATE&lt;/code&gt; statement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reads and writes are clickable, so you can walk the pipeline hop by hop.&lt;/p&gt;

&lt;h3&gt;
  
  
  The heatmap
&lt;/h3&gt;

&lt;p&gt;A graph shows how data moves, not what's doing the work.&lt;/p&gt;

&lt;p&gt;The optional heatmap covers that for MVs, over the last 1/7/30 days, built from &lt;code&gt;system.query_views_log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When enabled, it colours and thickens edges by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View duration&lt;/li&gt;
&lt;li&gt;Rows/bytes written or read&lt;/li&gt;
&lt;li&gt;Peak memory&lt;/li&gt;
&lt;li&gt;Executions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The values are log-scaled, since volumes can span orders of magnitude.&lt;/p&gt;

&lt;p&gt;An empty heatmap can mean no MVs fired, &lt;code&gt;log_query_views&lt;/code&gt; is off, or the connecting user lacks &lt;code&gt;SELECT&lt;/code&gt; on that table.&lt;/p&gt;

&lt;p&gt;One hot edge next to cool ones makes the point: MVs at the same level of the graph don't cost the same to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answers: how is my schema connected, and where does data flow?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Skipping Indexes: what already exists here?
&lt;/h2&gt;

&lt;p&gt;An index doesn't sort anything.&lt;/p&gt;

&lt;p&gt;It summarizes each block — min/max, distinct values, a bloom filter, or text terms — so a filtering query can skip blocks that can't match.&lt;/p&gt;

&lt;p&gt;The sort key is what actually orders the data and makes those columns fast already.&lt;/p&gt;

&lt;p&gt;Indexes are for the filters the sort key doesn't cover.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;parsed_logs&lt;/code&gt;, &lt;code&gt;service&lt;/code&gt; is already fast.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;status_code&lt;/code&gt;, and &lt;code&gt;message&lt;/code&gt; are not.&lt;/p&gt;

&lt;p&gt;The Data Skipping Indexes screen is read-only:&lt;/p&gt;

&lt;p&gt;Database → table → each index, with type and covered expression, across everything.&lt;/p&gt;

&lt;p&gt;The expression next to the type is the useful part — it catches a duplicate before you create it.&lt;/p&gt;

&lt;p&gt;Every index costs on every insert, so a duplicate is pure downside.&lt;/p&gt;

&lt;h3&gt;
  
  
  Four types
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;minmax&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Min/max per block.&lt;/p&gt;

&lt;p&gt;Cheap and useful for dates and timestamps that correlate with insertion order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;set&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Distinct values per block.&lt;/p&gt;

&lt;p&gt;Good for low-cardinality equality filters; useless past its size limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;bloom_filter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Probabilistic.&lt;/p&gt;

&lt;p&gt;It can rule a block out for certain, but a "maybe" still means reading it.&lt;/p&gt;

&lt;p&gt;Useful for equality on high-cardinality columns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;text&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Indexes terms within a string for searching rather than matching exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answers: what indexes does this table already have, and what are they indexing?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Projections: a different representation of the same data
&lt;/h2&gt;

&lt;p&gt;A skipping index narrows a query.&lt;/p&gt;

&lt;p&gt;A projection changes what it reads from entirely — a second copy of the table, sorted differently or pre-aggregated.&lt;/p&gt;

&lt;p&gt;ClickHouse® decides on its own whether to use one.&lt;/p&gt;

&lt;p&gt;It's not free, and it's not an index.&lt;/p&gt;

&lt;p&gt;Think of it this way:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index → can I skip this block?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Projection → is there a better-suited copy of this data?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dashboards on this pipeline repeatedly ask for counts and latency by service and hour, recomputed every time.&lt;/p&gt;

&lt;p&gt;A pre-aggregated projection storing exactly that grouping solves it directly.&lt;/p&gt;

&lt;p&gt;CH-Ops covers the lifecycle in five tabs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;View&lt;/strong&gt; — the same tree as the index screen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Projection&lt;/strong&gt; — a form with table, name, select, optional grouping/ordering, cluster-wide and skip-if-exists options&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Materialize Projection&lt;/strong&gt; — builds it for existing rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear Projection&lt;/strong&gt; — empties data while keeping the definition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop Projection&lt;/strong&gt; — removes it entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One quirk: projections don't support &lt;code&gt;SELECT DISTINCT&lt;/code&gt; — CH-Ops strips it and tells you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grouping and ordering
&lt;/h3&gt;

&lt;p&gt;Grouping and ordering are separate fields.&lt;/p&gt;

&lt;p&gt;A projection grouped by day can't serve a query grouped by hour.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating isn't the same as filling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Projection definition
        │
        ├── new inserts ──────────► maintained automatically
        │
        └── rows already in table ─► need Materialize Projection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;New data is covered automatically.&lt;/p&gt;

&lt;p&gt;Existing rows need materializing — real work on a big table, so it can be scoped per-partition to check whether it's worth it before doing the rest.&lt;/p&gt;

&lt;p&gt;Clear then rebuild is the fix for a drifted projection.&lt;/p&gt;

&lt;p&gt;Drop removes it for good.&lt;/p&gt;

&lt;p&gt;Either way, remember the cost: a projection is a second copy of the data, written on every insert whether it's used or not.&lt;/p&gt;

&lt;p&gt;Worth it for a query run constantly; not worth adding speculatively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answers: can I give ClickHouse® a better-suited representation for an important query pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Index Management: create, materialize, drop
&lt;/h2&gt;

&lt;p&gt;Three tabs — Create, Materialize, Drop.&lt;/p&gt;

&lt;p&gt;This one needs admin access, unlike the read-only screens above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create
&lt;/h3&gt;

&lt;p&gt;Database → table → column → name → type → granularity.&lt;/p&gt;

&lt;p&gt;The column list comes with data types attached, so there's no typing from memory.&lt;/p&gt;

&lt;p&gt;Some types add fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bloom_filter&lt;/code&gt; — false-positive rate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text&lt;/code&gt; — tokenizer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Name it for what it does:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;idx_user_id&lt;/code&gt;, not &lt;code&gt;idx1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;CH-Ops shows the exact statement before running it — a safer way to produce the same DDL, not a black box.&lt;/p&gt;

&lt;p&gt;Granularity defaults to a sensible value and is one of the last things worth tuning — type and expression matter far more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Materialize
&lt;/h3&gt;

&lt;p&gt;Same gotcha as projections:&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  ──► applies to data inserted from now on
Materialize   ──► existing rows become covered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing errors when this is skipped — the index just quietly doesn't help.&lt;/p&gt;

&lt;p&gt;Unlike projections, there's no partition-scoping option here — &lt;code&gt;MATERIALIZE INDEX&lt;/code&gt; runs against the whole table in one go.&lt;/p&gt;

&lt;p&gt;This screen is the answer to:&lt;/p&gt;

&lt;p&gt;"I added the index and nothing changed."&lt;/p&gt;

&lt;h3&gt;
  
  
  Drop
&lt;/h3&gt;

&lt;p&gt;Removes the index, not the data.&lt;/p&gt;

&lt;p&gt;Every index costs on writes for as long as it exists.&lt;/p&gt;

&lt;p&gt;If it's not earning that back, drop it.&lt;/p&gt;

&lt;p&gt;That's normal tuning, not a mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answers: creating, materializing, and dropping — the three states of an index.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From schema discovery to optimization
&lt;/h2&gt;

&lt;p&gt;Here's the workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Visualizer and select &lt;code&gt;parsed_logs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;See three MVs and a Distributed table depend on it.&lt;/li&gt;
&lt;li&gt;Read the sort order from the sidebar.&lt;/li&gt;
&lt;li&gt;Check Data Skipping Indexes for what's already covered.&lt;/li&gt;
&lt;li&gt;Compare against your actual workload — endpoint/user filters aren't served by the sort key.&lt;/li&gt;
&lt;li&gt;Check Projections for an existing pre-aggregation.&lt;/li&gt;
&lt;li&gt;Decide: projection for a repeated aggregation, index for a plain filter.&lt;/li&gt;
&lt;li&gt;Create it.&lt;/li&gt;
&lt;li&gt;Materialize it.&lt;/li&gt;
&lt;li&gt;Check whether it helped. Drop it if not.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CH-Ops doesn't tell you which index or projection to build.&lt;/p&gt;

&lt;p&gt;It shows what exists and where the load sits, then removes the DDL friction.&lt;/p&gt;

&lt;p&gt;The judgment calls — steps 5 and 7 — are still yours.&lt;/p&gt;

&lt;p&gt;The Query Profiler is where you confirm a query is actually slow before reaching for any of this.&lt;/p&gt;

&lt;p&gt;Same table across all three panels — one object, four views of it.&lt;/p&gt;

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

&lt;p&gt;Understand the schema.&lt;/p&gt;

&lt;p&gt;Check what's already optimized.&lt;/p&gt;

&lt;p&gt;Weigh a projection.&lt;/p&gt;

&lt;p&gt;Manage indexes when the evidence supports it.&lt;/p&gt;

&lt;p&gt;The value isn't skipping SQL — you can write the DDL yourself, and CH-Ops shows you the statement anyway.&lt;/p&gt;

&lt;p&gt;It's having a fast, structured way to see what's already there before you touch it.&lt;/p&gt;

&lt;p&gt;Most bad schema decisions come from not seeing the dependency, the existing index, or the projection that was already there — not from an inability to write &lt;code&gt;ALTER TABLE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pick your busiest table, open the Schema Visualizer, and see what's attached to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Schema Tools Documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ch-ops.io/docs/guide/schema-visualizer?v=latest" rel="noopener noreferrer"&gt;https://www.ch-ops.io/docs/guide/schema-visualizer?v=latest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next in the CHOps series
&lt;/h2&gt;

&lt;p&gt;Watch your clusters live on CHOps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ch-ops.io/blog/ch-ops-cluster-dashboard" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/ch-ops-cluster-dashboard&lt;/a&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>devops</category>
      <category>database</category>
      <category>analytics</category>
    </item>
    <item>
      <title>CHOps Is Now GA: Open-Source Operations for Self-Hosted ClickHouse®</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Thu, 24 Sep 2026 12:44:28 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/chops-is-now-ga-open-source-operations-for-self-hosted-clickhouser-5b2i</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/chops-is-now-ga-open-source-operations-for-self-hosted-clickhouser-5b2i</guid>
      <description>&lt;p&gt;CHOps is now generally available.&lt;/p&gt;

&lt;p&gt;ClickHouse® Cloud has a nice UI and good tools to admin and monitor a&lt;br&gt;
cluster. If you self-host the open-source ClickHouse® database, you get&lt;br&gt;
control, but the tools are fragmented and the daily work is clumsy.&lt;/p&gt;

&lt;p&gt;We built CHOps for our customers who self-host the ClickHouse® database.&lt;br&gt;
It started as many small internal UIs. When we decided to make it public,&lt;br&gt;
we put them into one app.&lt;/p&gt;

&lt;p&gt;CHOps works with any deployment: VMs, bare metal, Kubernetes (Altinity®&lt;br&gt;
operator, and the official ClickHouse® operator in early access), and&lt;br&gt;
managed cloud services.&lt;/p&gt;

&lt;p&gt;We released a beta in July 2026. After several rounds of testing by our&lt;br&gt;
ClickHouse® DBAs and use on production clusters, CHOps is now generally&lt;br&gt;
available. It is tested with ClickHouse® database 26.8 LTS.&lt;/p&gt;

&lt;p&gt;The core is open source under Apache 2.0. CHOps Pro adds features for&lt;br&gt;
production teams, such as SSO, audit logs, Slack, Teams and PagerDuty&lt;br&gt;
alerts, and remote node management.&lt;/p&gt;

&lt;p&gt;Thank you to everyone who tested the beta and sent feedback.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/Quantrail-Data/CH-Ops" rel="noopener noreferrer"&gt;https://github.com/Quantrail-Data/CH-Ops&lt;/a&gt;&lt;br&gt;
CHOps home page: &lt;a href="https://ch-ops.io" rel="noopener noreferrer"&gt;https://ch-ops.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>database</category>
      <category>devops</category>
    </item>
    <item>
      <title>Design a ClickHouse® Table Without Hand-Writing DDL: Schema Studio</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Wed, 23 Sep 2026 16:10:58 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/design-a-clickhouser-table-without-hand-writing-ddl-schema-studio-3mk3</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/design-a-clickhouser-table-without-hand-writing-ddl-schema-studio-3mk3</guid>
      <description>&lt;p&gt;Designing a ClickHouse® table involves more than defining columns. Data types, sorting keys, partitioning, MergeTree configuration, indexes, and other settings can influence how the table performs as data grows.&lt;/p&gt;

&lt;p&gt;Writing the complete DDL manually gives you control, but it also means translating each design decision into the correct ClickHouse® syntax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema Studio&lt;/strong&gt; simplifies this process by providing a guided workflow for designing and creating ClickHouse® tables while keeping the generated SQL visible and editable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CH-Ops?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CH-Ops&lt;/strong&gt; is a browser-based operations platform for ClickHouse®. It brings common database operations into a single web interface, including SQL querying, cluster monitoring, user management, backups, alerts, dashboards, and other operational workflows.&lt;/p&gt;

&lt;p&gt;Alongside these operational capabilities, CH-Ops includes &lt;strong&gt;Schema Studio&lt;/strong&gt;, a SQL tool designed to simplify the process of building ClickHouse® table definitions from source data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Schema Studio in CH-Ops
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Schema Studio&lt;/strong&gt; is a guided table-design feature within CH-Ops.&lt;/p&gt;

&lt;p&gt;Starting with your source data, it helps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infer and refine the schema&lt;/li&gt;
&lt;li&gt;Configure ClickHouse®-specific table settings&lt;/li&gt;
&lt;li&gt;Generate and validate the DDL&lt;/li&gt;
&lt;li&gt;Optionally review the design with AI&lt;/li&gt;
&lt;li&gt;Create the table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Importantly, Schema Studio creates the &lt;strong&gt;table structure only&lt;/strong&gt;. The source data is used for schema inference and is not loaded into the newly created table.&lt;/p&gt;

&lt;p&gt;You can access it under:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Tools → Schema Studio&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a ClickHouse® Table with Schema Studio
&lt;/h2&gt;

&lt;p&gt;Schema Studio organizes the table-design process into four core stages.&lt;/p&gt;

&lt;p&gt;Each stage focuses on a specific part of the design, starting with understanding the source data and ending with a validated table definition ready to be created in ClickHouse®.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with Your Source Data
&lt;/h2&gt;

&lt;p&gt;Schema Studio first connects to the ClickHouse® instance selected in CH-Ops.&lt;/p&gt;

&lt;p&gt;Enter the ClickHouse® username and password to establish the session.&lt;/p&gt;

&lt;p&gt;Once connected, choose the data that will be used for schema inference.&lt;/p&gt;

&lt;p&gt;You can upload a local file in formats such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSV&lt;/li&gt;
&lt;li&gt;TSV&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;NDJSON/JSONL&lt;/li&gt;
&lt;li&gt;Parquet&lt;/li&gt;
&lt;li&gt;ORC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also configure an object-storage source such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;li&gt;Azure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Review and Shape the Schema
&lt;/h2&gt;

&lt;p&gt;After analyzing the source, Schema Studio displays the inferred columns and corresponding ClickHouse® data types.&lt;/p&gt;

&lt;p&gt;It also shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Approximate distinct values&lt;/li&gt;
&lt;li&gt;Null percentages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps you review the structure before moving to table configuration.&lt;/p&gt;

&lt;p&gt;The inferred schema remains editable.&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modify column names&lt;/li&gt;
&lt;li&gt;Change data types&lt;/li&gt;
&lt;li&gt;Add derived columns using &lt;code&gt;DEFAULT&lt;/code&gt;, &lt;code&gt;MATERIALIZED&lt;/code&gt;, &lt;code&gt;ALIAS&lt;/code&gt;, or &lt;code&gt;EPHEMERAL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configure additional column options such as codecs and comments&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Schema inference may produce nullable types such as &lt;code&gt;Nullable(Int64)&lt;/code&gt;. Review the inferred types before configuring &lt;code&gt;ORDER BY&lt;/code&gt;, and adjust the type if a nullable column needs to be used as the sorting key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Define the ClickHouse® Table Design
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Engine&lt;/strong&gt; step is where you configure how the table will be structured in ClickHouse®.&lt;/p&gt;

&lt;p&gt;You can select the database and table name, choose the required MergeTree behavior, and define key table clauses such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ORDER BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PRIMARY KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PARTITION BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SAMPLE BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;TTL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more advanced designs, you can also configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data-skipping indexes&lt;/li&gt;
&lt;li&gt;Projections&lt;/li&gt;
&lt;li&gt;Replication&lt;/li&gt;
&lt;li&gt;Distributed tables&lt;/li&gt;
&lt;li&gt;Frequently filtered columns&lt;/li&gt;
&lt;li&gt;Additional MergeTree settings&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For MergeTree tables, configure an &lt;code&gt;ORDER BY&lt;/code&gt; or &lt;code&gt;PRIMARY KEY&lt;/code&gt; before generating the DDL. If no sorting key is required, use &lt;code&gt;tuple()&lt;/code&gt; for &lt;code&gt;ORDER BY&lt;/code&gt;. Schema Studio flags a missing key in the Generate step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Engine Configuration
&lt;/h3&gt;

&lt;p&gt;The Engine Configuration step allows you to configure the MergeTree engine and key table clauses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced Configuration
&lt;/h3&gt;

&lt;p&gt;Advanced Configuration allows you to add options such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indexes&lt;/li&gt;
&lt;li&gt;Projections&lt;/li&gt;
&lt;li&gt;Replication&lt;/li&gt;
&lt;li&gt;Distributed tables&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Generate and Validate the DDL
&lt;/h2&gt;

&lt;p&gt;After the table design is configured, Schema Studio generates the corresponding ClickHouse® &lt;code&gt;CREATE TABLE&lt;/code&gt; statement in an editable SQL editor.&lt;/p&gt;

&lt;p&gt;You can review or modify the generated SQL.&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;Validate&lt;/strong&gt; to check the DDL before creating the table.&lt;/p&gt;

&lt;p&gt;If you want to discard editor changes, &lt;strong&gt;Rebuild from Form&lt;/strong&gt; regenerates the DDL using the configuration from the previous steps.&lt;/p&gt;

&lt;p&gt;This keeps the generated SQL visible and gives you the ability to make manual adjustments before creating the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Review the Design with AI
&lt;/h2&gt;

&lt;p&gt;Schema Studio also provides &lt;strong&gt;Evaluate with AI&lt;/strong&gt; for an optional review using the supported AI provider configured in CH-Ops.&lt;/p&gt;

&lt;p&gt;The evaluation can identify potential improvements related to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Nullability&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LowCardinality&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Sorting keys&lt;/li&gt;
&lt;li&gt;Partitioning&lt;/li&gt;
&lt;li&gt;Codecs&lt;/li&gt;
&lt;li&gt;Other table-design choices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommendations are presented for review and are not applied automatically.&lt;/p&gt;

&lt;p&gt;When a recommendation is useful, &lt;strong&gt;Apply to Editor&lt;/strong&gt; places the suggested DDL into the editor for further review and validation.&lt;/p&gt;

&lt;p&gt;This provides an additional review step while keeping the final decision with the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Create the Table
&lt;/h2&gt;

&lt;p&gt;After the DDL has been reviewed and validated, select &lt;strong&gt;Create Table&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Schema Studio displays a confirmation before executing the &lt;code&gt;CREATE TABLE&lt;/code&gt; statement on the connected ClickHouse® instance.&lt;/p&gt;

&lt;p&gt;Only the table structure is created.&lt;/p&gt;

&lt;p&gt;The source data used for schema inference is &lt;strong&gt;not loaded&lt;/strong&gt; into the table.&lt;/p&gt;

&lt;p&gt;After creation, open the table in the &lt;strong&gt;SQL Editor&lt;/strong&gt; to verify that it was created successfully and review its schema and configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Schema Studio Matters
&lt;/h2&gt;

&lt;p&gt;The value of Schema Studio is not simply that it generates a &lt;code&gt;CREATE TABLE&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;It brings the decisions involved in ClickHouse® table design into one workflow while keeping those decisions visible to the user.&lt;/p&gt;

&lt;p&gt;Schema inference provides a starting point.&lt;/p&gt;

&lt;p&gt;The Engine step exposes ClickHouse®-specific configuration.&lt;/p&gt;

&lt;p&gt;Validation checks the generated DDL.&lt;/p&gt;

&lt;p&gt;AI-assisted review provides an additional opportunity to refine the design before creation.&lt;/p&gt;

&lt;p&gt;This reduces the repetitive work of translating a table design into SQL while keeping the final DDL available for inspection and modification.&lt;/p&gt;

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

&lt;p&gt;Designing a ClickHouse® table requires decisions that can affect query performance, storage, and how the table behaves as data grows.&lt;/p&gt;

&lt;p&gt;Schema Studio brings those decisions together in a guided workflow, from understanding the source data to creating the final table.&lt;/p&gt;

&lt;p&gt;By combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema inference&lt;/li&gt;
&lt;li&gt;ClickHouse®-specific configuration&lt;/li&gt;
&lt;li&gt;DDL generation&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Optional AI-assisted review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Schema Studio provides a more structured path to table creation without hiding the SQL or taking control away from the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CH-Ops Installation Guide&lt;/li&gt;
&lt;li&gt;CH-Ops Official Repository&lt;/li&gt;
&lt;li&gt;CH-Ops Demo Video&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next in the CH-Ops Series
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CH-Ops Schema Tools: Visualizer, Indexes &amp;amp; Projections&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>devops</category>
      <category>analytics</category>
      <category>database</category>
    </item>
    <item>
      <title>Secure API Key Management: Your AI, Your Choice</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Tue, 22 Sep 2026 15:50:45 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/secure-api-key-management-your-ai-your-choice-5bof</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/secure-api-key-management-your-ai-your-choice-5bof</guid>
      <description>&lt;h2&gt;
  
  
  In this article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is CH-Ops?&lt;/li&gt;
&lt;li&gt;What is Qurioz AI?&lt;/li&gt;
&lt;li&gt;What is API Key Management?&lt;/li&gt;
&lt;li&gt;Why Configure Multiple AI Providers?&lt;/li&gt;
&lt;li&gt;Managing AI API Keys&lt;/li&gt;
&lt;li&gt;1. Add a New API Key&lt;/li&gt;
&lt;li&gt;2. Verify Before Saving&lt;/li&gt;
&lt;li&gt;3. Manage Saved API Keys&lt;/li&gt;
&lt;li&gt;4. Delete an API Key&lt;/li&gt;
&lt;li&gt;5. Edit an API Key&lt;/li&gt;
&lt;li&gt;Why API Key Management Matters&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is CH-Ops?
&lt;/h2&gt;

&lt;p&gt;CH-Ops is a browser-based operations platform for ClickHouse® that combines database administration, SQL development, monitoring, backups, dashboards, alerts, and AI-powered capabilities into a single interface.&lt;/p&gt;

&lt;p&gt;One of these AI-powered capabilities is Qurioz AI, which enables users to interact with ClickHouse® databases using natural language instead of manually writing SQL queries.&lt;/p&gt;

&lt;p&gt;To communicate with AI models securely, Qurioz AI requires one or more configured AI providers. CH-Ops provides API Key Management, allowing administrators to configure, verify, and manage these providers from a centralized location.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Qurioz AI?
&lt;/h2&gt;

&lt;p&gt;Qurioz AI is an AI-powered SQL assistant in CH-Ops that allows users to interact with ClickHouse® databases using natural language instead of manually writing SQL queries.&lt;/p&gt;

&lt;p&gt;Users can simply ask questions about their data, and Qurioz AI understands the request, generates the appropriate ClickHouse® SQL query, executes it against the selected database, and presents the results in an easy-to-understand format.&lt;/p&gt;

&lt;p&gt;Qurioz AI can also visualize query results using charts, making it easier to understand trends, comparisons, and patterns in the data.&lt;/p&gt;

&lt;p&gt;For example, instead of manually writing a SQL query such as:&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;product_name&lt;/span&gt;&lt;span class="p"&gt;,&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;total_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_sales&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;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_sales&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A user can simply ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me the total sales by product."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Qurioz AI can generate the corresponding ClickHouse® query, execute it against the selected database, and display the results. Users can then switch to the chart view to visualize the returned data.&lt;/p&gt;

&lt;p&gt;This makes it easier for users to explore data, generate SQL queries, understand query results, and visualize insights without requiring them to write SQL manually.&lt;/p&gt;

&lt;p&gt;To communicate with the selected AI model, Qurioz AI requires a valid and verified AI provider configuration. This is where API Key Management becomes important.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is API Key Management?
&lt;/h2&gt;

&lt;p&gt;API Key Management is the configuration center for all AI providers used by Qurioz AI.&lt;/p&gt;

&lt;p&gt;Instead of embedding API keys inside the application or requiring every user to configure providers individually, administrators can securely register AI providers once and make them available throughout Qurioz AI.&lt;/p&gt;

&lt;p&gt;Currently, CH-Ops supports configuring up to five AI providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI&lt;/li&gt;
&lt;li&gt;Google Gemini&lt;/li&gt;
&lt;li&gt;Claude&lt;/li&gt;
&lt;li&gt;Mistral&lt;/li&gt;
&lt;li&gt;Ollama&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once configured, these providers automatically become available inside Qurioz AI, allowing users to switch between AI models whenever required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Configure Multiple AI Providers?
&lt;/h2&gt;

&lt;p&gt;Different AI models are suited for different workloads.&lt;/p&gt;

&lt;p&gt;Some models generate SQL faster, while others provide more detailed explanations or perform better with complex analytical queries.&lt;/p&gt;

&lt;p&gt;By configuring multiple providers, users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare SQL generated by different AI models&lt;/li&gt;
&lt;li&gt;Switch providers without changing application settings&lt;/li&gt;
&lt;li&gt;Use both cloud-based and local models&lt;/li&gt;
&lt;li&gt;Continue working if one provider is temporarily unavailable&lt;/li&gt;
&lt;li&gt;Evaluate different models for various analytical workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Managing AI API Keys
&lt;/h2&gt;

&lt;p&gt;API Key Management provides a simple workflow for securely configuring AI providers before they are used inside Qurioz AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add a New API Key
&lt;/h3&gt;

&lt;p&gt;Navigate to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control Panel → AI API Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When no providers have been configured, the page displays an empty API Key Manager.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Add API Key&lt;/strong&gt; to configure a new provider.&lt;/p&gt;

&lt;p&gt;For each provider, enter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display Name&lt;/li&gt;
&lt;li&gt;AI Provider&lt;/li&gt;
&lt;li&gt;Model Name&lt;/li&gt;
&lt;li&gt;API Key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each provider uses its own supported models.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAI → GPT models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini → Gemini models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude → Claude models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistral → Mistral models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama → Local models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CH-Ops supports configuring up to five API providers, making it easy to manage multiple AI services from one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Verify Before Saving
&lt;/h3&gt;

&lt;p&gt;Before an API key can be stored, CH-Ops validates the credentials.&lt;/p&gt;

&lt;p&gt;Once a valid API key is entered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API key is successfully verified&lt;/li&gt;
&lt;li&gt;A success message is displayed&lt;/li&gt;
&lt;li&gt;The verification status appears in green&lt;/li&gt;
&lt;li&gt;The Save Key button becomes available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This verification step ensures that only valid API credentials are stored and prevents invalid configurations from being used by Qurioz AI.&lt;/p&gt;

&lt;p&gt;Since Qurioz AI depends on configured AI providers, a provider must be successfully verified and saved before it can be used for SQL generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Manage Saved API Keys
&lt;/h3&gt;

&lt;p&gt;After verification, the provider can be saved.&lt;/p&gt;

&lt;p&gt;The API Key Manager displays all configured providers together with their current status.&lt;/p&gt;

&lt;p&gt;Administrators can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View configured providers&lt;/li&gt;
&lt;li&gt;Edit the provider name&lt;/li&gt;
&lt;li&gt;Change the configured model&lt;/li&gt;
&lt;li&gt;Update the API key&lt;/li&gt;
&lt;li&gt;Delete providers that are no longer required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it easy to keep AI provider configurations up to date without recreating them from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Delete an API Key
&lt;/h3&gt;

&lt;p&gt;Administrators can delete an API key when an AI provider is no longer required.&lt;/p&gt;

&lt;p&gt;Selecting &lt;strong&gt;Delete&lt;/strong&gt; removes the provider from API Key Management and makes it unavailable in Qurioz AI.&lt;/p&gt;

&lt;p&gt;A new key can be added and verified whenever the provider is needed again.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Edit an API Key
&lt;/h3&gt;

&lt;p&gt;Administrators can edit an existing provider to update the &lt;strong&gt;model name, provider details, or API key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After making changes, the updated API key can be verified and saved without creating a new provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Configured Models in Qurioz AI
&lt;/h2&gt;

&lt;p&gt;After an AI provider has been configured, no additional setup is required.&lt;/p&gt;

&lt;p&gt;Navigate to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Tools → Qurioz AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The configured providers automatically appear in the model selection dropdown.&lt;/p&gt;

&lt;p&gt;Users can simply choose the model they want to use before asking questions.&lt;/p&gt;

&lt;p&gt;Once selected, Qurioz AI uses that provider to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand natural language questions&lt;/li&gt;
&lt;li&gt;Generate ClickHouse® SQL queries&lt;/li&gt;
&lt;li&gt;Execute queries against the selected database&lt;/li&gt;
&lt;li&gt;Explain query results in simple language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Switching the dropdown immediately changes the AI provider used for subsequent requests, allowing users to compare responses from different models without leaving the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why API Key Management Matters
&lt;/h2&gt;

&lt;p&gt;API Key Management separates provider configuration from everyday usage.&lt;/p&gt;

&lt;p&gt;Administrators configure providers only once, while users simply select the model they want inside Qurioz AI.&lt;/p&gt;

&lt;p&gt;By validating API keys before storage and exposing configured providers directly within the AI interface, CH-Ops creates a seamless workflow between configuration and AI-assisted querying.&lt;/p&gt;

&lt;p&gt;Support for multiple providers also makes it easy to compare models, evaluate their responses, and choose the most suitable AI for different analytical tasks.&lt;/p&gt;

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

&lt;p&gt;Reliable AI-assisted analytics begin with secure AI provider configuration.&lt;/p&gt;

&lt;p&gt;The API Key Management feature in CH-Ops provides a centralized way to configure and validate OpenAI, Gemini, Claude, Mistral, and Ollama providers before they are used by Qurioz AI.&lt;/p&gt;

&lt;p&gt;Once configured, these providers integrate directly with Qurioz AI, allowing users to switch between models effortlessly while generating ClickHouse® SQL queries using natural language.&lt;/p&gt;

&lt;p&gt;By combining secure API key management with flexible model selection, CH-Ops simplifies both the administration and everyday use of AI-powered analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CH-Ops Installation Guide&lt;/li&gt;
&lt;li&gt;CH-Ops Official Repository&lt;/li&gt;
&lt;li&gt;CH-Ops Demo Video&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next in the CH-Ops Series
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Design a ClickHouse® Table Without Hand-Writing DDL: Schema Studio&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>analytics</category>
      <category>database</category>
      <category>devops</category>
    </item>
    <item>
      <title>Ask your ClickHouse® database in plain English</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Mon, 21 Sep 2026 15:47:38 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/ask-your-clickhouser-database-in-plain-english-3a8f</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/ask-your-clickhouser-database-in-plain-english-3a8f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introducing Qurioz, the AI assistant built into CHOps.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CH-Ops?
&lt;/h2&gt;

&lt;p&gt;CH-Ops is a browser-based operations platform for ClickHouse®. Instead of relying entirely on the command line or HTTP API, it provides a visual interface for managing your ClickHouse® deployment.&lt;/p&gt;

&lt;p&gt;From running SQL queries and monitoring cluster health to managing users, backups, alerts, and dashboards, CH-Ops brings operational tasks into a single web application.&lt;/p&gt;

&lt;p&gt;It stores its own configuration in a local SQLite database without modifying your ClickHouse® data unless you explicitly execute queries.&lt;/p&gt;

&lt;p&gt;Every team that runs ClickHouse® has the same bottleneck.&lt;/p&gt;

&lt;p&gt;The data is there, the questions are obvious, and the answers are three or four hours away because someone has to remember which of the 200 tables holds the events, whether the timestamp column is &lt;code&gt;event_time&lt;/code&gt; or &lt;code&gt;created_at&lt;/code&gt;, and how to phrase a conditional count without accidentally scanning a billion rows.&lt;/p&gt;

&lt;p&gt;ClickHouse® is fast, but its SQL has a dialect of its own.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;countIf&lt;/code&gt; instead of &lt;code&gt;COUNT(CASE WHEN ...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uniq&lt;/code&gt; when an approximate distinct is good enough and &lt;code&gt;uniqExact&lt;/code&gt; when it isn't.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;FINAL&lt;/code&gt; to read the deduplicated state of a &lt;code&gt;ReplacingMergeTree&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ANY JOIN&lt;/code&gt; when you want one match instead of a fan-out.&lt;/p&gt;

&lt;p&gt;None of it is hard once you know it. All of it is a wall when you don't.&lt;/p&gt;

&lt;p&gt;That wall is what Qurioz removes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Qurioz?
&lt;/h2&gt;

&lt;p&gt;Qurioz is the AI assistant built into CHOps.&lt;/p&gt;

&lt;p&gt;It turns plain questions about your data into ready-to-run ClickHouse® SQL, without leaving the app.&lt;/p&gt;

&lt;p&gt;You ask a question the way you'd ask a colleague, Qurioz writes the query against your actual schema, runs it, and shows you the rows.&lt;/p&gt;

&lt;p&gt;You can then edit the query, chart the result, or pin it to a dashboard.&lt;/p&gt;

&lt;p&gt;New team members get productive on an unfamiliar schema in minutes instead of weeks.&lt;/p&gt;

&lt;p&gt;Experienced engineers skip the part where they look up column names for the fortieth time.&lt;/p&gt;

&lt;p&gt;You'll find it in the sidebar under &lt;strong&gt;SQL Tools → Qurioz AI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting set up
&lt;/h2&gt;

&lt;p&gt;Three things need to happen once. The first two are admin tasks; after that, anyone on the team can ask questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add an AI provider key
&lt;/h3&gt;

&lt;p&gt;Qurioz talks to a model you choose.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Control panel → AI API Keys&lt;/strong&gt; and find the &lt;strong&gt;Qurioz API Key Manager&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It supports &lt;strong&gt;Gemini, OpenAI, Mistral, Claude, and Ollama&lt;/strong&gt;, and you can store up to four keys and switch between them.&lt;/p&gt;

&lt;p&gt;Paste your key and press &lt;strong&gt;Verify AI API Key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Save Key&lt;/strong&gt; button stays disabled until verification passes a deliberate choice, so you find out the key is wrong here rather than halfway through your first question.&lt;/p&gt;

&lt;p&gt;Keys are encrypted before they're stored.&lt;/p&gt;

&lt;p&gt;This page is admin-only. If you're not an admin you'll see:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"API key management is only available for administrators."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Connect your ClickHouse® cluster
&lt;/h3&gt;

&lt;p&gt;If you're already using CHOps, this is done.&lt;/p&gt;

&lt;p&gt;If not, &lt;strong&gt;Control panel → Cluster Management&lt;/strong&gt; is where you add a cluster and its nodes.&lt;/p&gt;

&lt;p&gt;You can configure the node host, port, user, password, and an HTTPS toggle, with a per-node connection test.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Generate the schema
&lt;/h3&gt;

&lt;p&gt;This is the step that makes Qurioz specific to &lt;em&gt;your&lt;/em&gt; database rather than generically knowledgeable about SQL.&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;SQL Tools → Qurioz AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next to the status pill reading &lt;strong&gt;Database Not Selected&lt;/strong&gt;, click the pencil icon.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Database Schema Generator&lt;/strong&gt; opens, listing every database on the connected node.&lt;/p&gt;

&lt;p&gt;Select the ones you want Qurioz to know about. There's a &lt;strong&gt;Select All&lt;/strong&gt; toggle if you want everything.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add Schema&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Qurioz reads the structure of those databases and indexes it.&lt;/p&gt;

&lt;p&gt;When it's done you'll see:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Database ID is created successfully!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The pill then flips to &lt;strong&gt;Database Selected&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Schemas change, so the same modal has &lt;strong&gt;Refresh Selected Schema&lt;/strong&gt; to re-read a database after you've added tables or columns, and &lt;strong&gt;Delete Schema's&lt;/strong&gt; to remove one entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asking your first question
&lt;/h2&gt;

&lt;p&gt;Type into the box marked &lt;strong&gt;Search for query...&lt;/strong&gt; and press Enter.&lt;/p&gt;

&lt;p&gt;(&lt;code&gt;Shift+Enter&lt;/code&gt; gives you a new line if the question is long.)&lt;/p&gt;

&lt;p&gt;Ask it the way you'd say it out loud:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Which customers placed the most orders last month?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Show me the 20 slowest queries in the last 24 hours&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How many unique users hit the API each day this week?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Qurioz works out which tables are relevant, writes the query, and shows it in a block headed &lt;strong&gt;ClickHouse® Query&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It then runs it immediately and renders the rows underneath.&lt;/p&gt;

&lt;p&gt;There's no separate Run button to hunt for; the result is just there.&lt;/p&gt;

&lt;p&gt;If the query returns nothing you'll get:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"No data found. Please check the SQL query and try again."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If it fails outright, you get the error and a &lt;strong&gt;Retry&lt;/strong&gt; button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refining the answer
&lt;/h2&gt;

&lt;p&gt;The first query is a starting point, not a verdict.&lt;/p&gt;

&lt;p&gt;Qurioz is built around the assumption that you'll want to adjust it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edit&lt;/strong&gt; opens the SQL in an inline editor. Change what you want and press &lt;strong&gt;Update&lt;/strong&gt; or &lt;code&gt;Ctrl+Enter&lt;/code&gt; to re-run it. &lt;strong&gt;Cancel&lt;/strong&gt; backs out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy&lt;/strong&gt; puts the query on your clipboard for the SQL editor, a dashboard, or a code review.&lt;/li&gt;
&lt;li&gt;Hovering your own question reveals a pencil. Reword it, hit Enter, and Qurioz regenerates from the new phrasing, usually faster than describing the fix in a follow-up message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From result to chart to dashboard
&lt;/h2&gt;

&lt;p&gt;Once you have rows, &lt;strong&gt;Chart View&lt;/strong&gt; turns them into a visualization.&lt;/p&gt;

&lt;p&gt;Pick a &lt;strong&gt;Chart Type&lt;/strong&gt; and &lt;strong&gt;Subtype&lt;/strong&gt;, map your columns to the axes, and watch the &lt;strong&gt;Preview&lt;/strong&gt; update.&lt;/p&gt;

&lt;p&gt;Give it a name, choose a &lt;strong&gt;Dashboard&lt;/strong&gt;, and &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The chart lands on that dashboard alongside everything else your team tracks.&lt;/p&gt;

&lt;p&gt;Saving to a dashboard requires the editor role or above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions that span several databases
&lt;/h2&gt;

&lt;p&gt;Analytics rarely respects database boundaries.&lt;/p&gt;

&lt;p&gt;If you've generated schemas for more than one database, hover the &lt;strong&gt;Database Selected&lt;/strong&gt; pill and you'll get a list of them.&lt;/p&gt;

&lt;p&gt;Click to toggle each one in or out of the current question.&lt;/p&gt;

&lt;p&gt;Everything toggled on is fair game for the same question, so Qurioz can reach across databases when the question calls for it, and stay narrowly focused when you'd rather it didn't wander.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can actually write
&lt;/h2&gt;

&lt;p&gt;Qurioz isn't limited to &lt;code&gt;SELECT * FROM one_table&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It handles the shapes real analysis takes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Joins across tables&lt;/strong&gt;, worked out from how your columns are named, such as an &lt;code&gt;orders.customer_id&lt;/code&gt; matched to a &lt;code&gt;customers.id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggregations&lt;/strong&gt; using ClickHouse®'s own idioms: &lt;code&gt;countIf&lt;/code&gt; and &lt;code&gt;sumIf&lt;/code&gt; for conditional counts, &lt;code&gt;uniq&lt;/code&gt; for fast approximate distincts, and &lt;code&gt;uniqExact&lt;/code&gt; when you need the exact number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window functions&lt;/strong&gt; for rankings, running totals, and row-over-row comparisons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CTEs and subqueries&lt;/strong&gt; when a question needs staging before the final answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;FINAL&lt;/code&gt;&lt;/strong&gt; on &lt;code&gt;ReplacingMergeTree&lt;/code&gt; tables when you're asking about current state rather than raw history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every query comes back with a &lt;code&gt;LIMIT 10&lt;/code&gt; by default, so an exploratory question can't accidentally pull a billion rows.&lt;/p&gt;

&lt;p&gt;Ask for "top 5" or "limit 50" and it uses your number instead.&lt;/p&gt;

&lt;p&gt;One honest caveat:&lt;/p&gt;

&lt;p&gt;Qurioz gives you a well-formed starting query, not a guaranteed-correct one.&lt;/p&gt;

&lt;p&gt;It's working from your schema, not from knowledge of what your columns &lt;em&gt;mean&lt;/em&gt; in your business.&lt;/p&gt;

&lt;p&gt;Read the SQL before you trust the number. The query is right there above the results precisely so you can.&lt;/p&gt;

&lt;p&gt;If a question is genuinely ambiguous — two equally plausible tables, no sensible way to join them — Qurioz says so instead of inventing a join and handing you a confidently wrong answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your data and your choice of model
&lt;/h2&gt;

&lt;p&gt;Worth being precise about, because it's the question every data team asks first.&lt;/p&gt;

&lt;p&gt;The embedding and retrieval run locally.&lt;/p&gt;

&lt;p&gt;The step that writes the SQL does not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your question and the relevant table and column definitions are sent to whichever AI provider you configured.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Table names, column names, and types — not your row data.&lt;/p&gt;

&lt;p&gt;If that's more than your compliance posture allows, use &lt;strong&gt;Ollama&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Point Qurioz at a model running on your own infrastructure and nothing leaves your network at any stage.&lt;/p&gt;

&lt;p&gt;On the safety side, Qurioz generates read-only queries.&lt;/p&gt;

&lt;p&gt;Ask it to drop a table, truncate, or update rows and it declines.&lt;/p&gt;

&lt;p&gt;It's built to answer questions, not to change your data.&lt;/p&gt;

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

&lt;p&gt;Qurioz makes working with ClickHouse® data faster, simpler, and more accessible.&lt;/p&gt;

&lt;p&gt;Ask questions in plain language and get ready-to-run SQL based on your actual schema.&lt;/p&gt;

&lt;p&gt;You can review, edit, visualize, and save results without leaving CHOps.&lt;/p&gt;

&lt;p&gt;It helps teams spend less time searching through schemas and more time finding answers.&lt;/p&gt;

&lt;p&gt;With Qurioz, your ClickHouse® data is only a question away.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Qurioz ships with CHOps, the ClickHouse® administration and monitoring dashboard from Quantrail™ Data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installation Guide:&lt;/strong&gt; CH-Ops Installation Guide&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; ch-ops.io&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; ch-ops.io/docs — see the &lt;em&gt;Qurioz AI&lt;/em&gt; guide under SQL Tools, and &lt;em&gt;AI API Keys&lt;/em&gt; for provider setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; Quantrail-Data/CH-Ops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your team spends more time remembering schemas than asking questions, this is the part of the workflow we built Qurioz to delete.&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>devops</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Debugging Query Performance with Per-Second Metrics</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Sat, 19 Sep 2026 05:55:53 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/debugging-query-performance-with-per-second-metrics-5bik</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/debugging-query-performance-with-per-second-metrics-5bik</guid>
      <description>&lt;p&gt;A query can look perfectly healthy when all you see is its final execution time.&lt;/p&gt;

&lt;p&gt;It ran for 12 seconds. It used 8 GB of memory. It read 40 GB from disk.&lt;/p&gt;

&lt;p&gt;But those numbers don't tell you the whole story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did the memory spike? When did the query start waiting on disk? Did cache misses increase halfway through execution? Did a JOIN suddenly consume most of the memory? Did the query spill to disk near the end?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A final query profile can tell you &lt;em&gt;what happened overall&lt;/em&gt;. But to understand &lt;em&gt;why it happened&lt;/em&gt;, you need to see what the query was doing while it was running.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Query Metrics&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Query Metrics provides a second-by-second timeline of resource consumption during query execution. It exposes memory, CPU, disk I/O, cache hits and misses, network activity, and hundreds of other counters.&lt;/p&gt;

&lt;p&gt;Think of it as a &lt;strong&gt;heart-rate monitor for a query&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Per-Second Metrics Matter
&lt;/h2&gt;

&lt;p&gt;Consider a query that eventually reaches 10 GB of memory usage.&lt;/p&gt;

&lt;p&gt;That number alone doesn't tell you much.&lt;/p&gt;

&lt;p&gt;Maybe memory gradually increased from 1 GB to 10 GB because the query was building a large aggregation state.&lt;/p&gt;

&lt;p&gt;Or maybe memory stayed around 2 GB for most of the query, suddenly jumped to 10 GB while building a JOIN hash table, and then dropped back down.&lt;/p&gt;

&lt;p&gt;Those are completely different problems.&lt;/p&gt;

&lt;p&gt;The same applies to disk I/O.&lt;/p&gt;

&lt;p&gt;A query reading 100 GB over 30 seconds may be perfectly normal for its workload. But if disk writes suddenly spike at the 20-second mark, the query may have started spilling intermediate results to disk.&lt;/p&gt;

&lt;p&gt;Without a timeline, you see the final numbers.&lt;/p&gt;

&lt;p&gt;With a timeline, you see the &lt;strong&gt;behavior&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Query Metrics takes resource snapshots every second for queries that run for more than roughly one second. These snapshots are stored in &lt;code&gt;system.query_metric_log&lt;/code&gt;, with each snapshot containing hundreds of counters.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why was this query slow?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you can ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What happened at second 7 that made this query slow?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a much more useful question.&lt;/p&gt;




&lt;h2&gt;
  
  
  From Query Duration to a Query Timeline
&lt;/h2&gt;

&lt;p&gt;Query duration tells you &lt;strong&gt;how long&lt;/strong&gt; a query took.&lt;/p&gt;

&lt;p&gt;Query Metrics helps explain &lt;strong&gt;what happened during that time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The raw metric snapshots are converted into visual timelines grouped by category and unit. The X-axis represents the query's execution time, with one data point per second, while the Y-axis shows the metric value.&lt;/p&gt;

&lt;p&gt;For example, a query might show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory steadily increasing&lt;/li&gt;
&lt;li&gt;CPU remaining relatively flat&lt;/li&gt;
&lt;li&gt;Disk reads suddenly increasing&lt;/li&gt;
&lt;li&gt;Cache misses appearing midway through execution&lt;/li&gt;
&lt;li&gt;Network activity spiking near the end&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of looking through hundreds of raw counters, you can visually follow the query's execution.&lt;/p&gt;

&lt;p&gt;Metrics with different units are also separated into different charts, so bytes, counts, and time measurements don't get mixed together on the same scale.&lt;/p&gt;

&lt;p&gt;The result is much easier to reason about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don't just see the result of the query. You see its journey.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Getting to a query's metrics is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Tools → Query Metrics&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose the &lt;strong&gt;From&lt;/strong&gt; and &lt;strong&gt;To&lt;/strong&gt; timestamps.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Load Queries&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select a query that has per-second metric data.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Use This Query&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Show Query Metrics&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The charts are then grouped by category and unit.&lt;/p&gt;

&lt;p&gt;Only categories containing non-zero data are displayed. A simple query may show only Memory and CPU, while a more complex query involving JOINs, disk spilling, remote reads, or distributed execution can activate many more categories.&lt;/p&gt;

&lt;p&gt;This keeps the visualization focused on what actually happened during that query instead of showing hundreds of irrelevant metrics.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start With the Memory Chart
&lt;/h2&gt;

&lt;p&gt;Memory is often the first place to look when investigating an unstable query.&lt;/p&gt;

&lt;p&gt;The Memory chart typically includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;memory_usage&lt;/code&gt; — memory currently allocated by the query&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;peak_memory_usage&lt;/code&gt; — the highest memory usage seen so far&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationship between these two metrics can tell you a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Both Lines Climb Steadily
&lt;/h3&gt;

&lt;p&gt;The query may be continuously accumulating data in memory.&lt;/p&gt;

&lt;p&gt;This can happen during operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hash table construction&lt;/li&gt;
&lt;li&gt;Sorting&lt;/li&gt;
&lt;li&gt;Aggregation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Memory Spikes and Then Drops
&lt;/h3&gt;

&lt;p&gt;The query experienced a temporary memory burst.&lt;/p&gt;

&lt;p&gt;A large hash JOIN is one example. The query may allocate a large hash table, use it, and then release that memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peak Memory Keeps Climbing
&lt;/h3&gt;

&lt;p&gt;This can indicate that the query is progressively consuming more memory throughout its execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peak Memory Reaches the Configured Limit
&lt;/h3&gt;

&lt;p&gt;The query may be killed or throttled because it exceeded &lt;code&gt;max_memory_usage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is one of the biggest advantages of a timeline.&lt;/p&gt;

&lt;p&gt;A final memory number tells you &lt;strong&gt;how much&lt;/strong&gt; memory was used.&lt;/p&gt;

&lt;p&gt;A per-second graph tells you &lt;strong&gt;when and how&lt;/strong&gt; that memory was used.&lt;/p&gt;




&lt;h2&gt;
  
  
  Disk I/O: Find the Moment the Query Starts Waiting
&lt;/h2&gt;

&lt;p&gt;Disk metrics can reveal another important part of a query's execution.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OSReadBytes&lt;/code&gt; represents bytes read from disk. A steadily increasing value generally means the query is scanning data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OSWriteBytes&lt;/code&gt; is particularly interesting when it suddenly spikes. A spike can indicate that intermediate results are being written to disk.&lt;/p&gt;

&lt;p&gt;Then there is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DiskReadElapsedMicroseconds&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This represents the time spent waiting for disk reads.&lt;/p&gt;

&lt;p&gt;These metrics help distinguish between two very different situations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The query is reading a lot of data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;versus&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The query is spending a lot of time waiting for storage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those problems require very different optimizations.&lt;/p&gt;

&lt;p&gt;If the query is simply reading too much data, you may need to reduce the amount of data being scanned.&lt;/p&gt;

&lt;p&gt;If the query is spending significant time waiting for storage, the storage layer or filesystem cache may deserve closer attention.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cache Hits vs. Cache Misses
&lt;/h2&gt;

&lt;p&gt;Cache behavior is another area where a final query profile can hide useful information.&lt;/p&gt;

&lt;p&gt;Query Metrics exposes counters such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MarkCacheHits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MarkCacheMisses&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PageCacheHits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PageCacheMisses&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comparing hits and misses can help determine whether the cache is actually helping the workload.&lt;/p&gt;

&lt;p&gt;For example, a query with many mark-cache misses may be repeatedly going to disk instead of finding the required information in memory.&lt;/p&gt;

&lt;p&gt;The documentation uses a mark-cache miss ratio above 10% as a signal that the cache may be cold or too small.&lt;/p&gt;

&lt;p&gt;The important part is that you aren't simply observing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The query is slow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can start connecting that slowdown to a specific resource behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Useful Pattern: Correlating Spikes
&lt;/h2&gt;

&lt;p&gt;The real power of per-second metrics appears when you stop looking at individual charts in isolation.&lt;/p&gt;

&lt;p&gt;Suppose &lt;code&gt;peak_memory_usage&lt;/code&gt; suddenly jumps at second 18.&lt;/p&gt;

&lt;p&gt;Don't stop at the memory chart.&lt;/p&gt;

&lt;p&gt;Look at the other charts at that exact moment.&lt;/p&gt;

&lt;p&gt;Maybe &lt;code&gt;ArenaAllocBytes&lt;/code&gt; starts climbing at the same time.&lt;/p&gt;

&lt;p&gt;That suggests the query is allocating memory for a large aggregation or sort.&lt;/p&gt;

&lt;p&gt;Or perhaps an &lt;strong&gt;External Operations&lt;/strong&gt; category appears immediately afterward.&lt;/p&gt;

&lt;p&gt;That could mean the query exceeded its in-memory limits and started spilling intermediate results to disk.&lt;/p&gt;

&lt;p&gt;The debugging workflow becomes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the timestamp where the spike occurs.&lt;/li&gt;
&lt;li&gt;Look at the other metrics around that timestamp.&lt;/li&gt;
&lt;li&gt;Identify what operation was becoming more expensive.&lt;/li&gt;
&lt;li&gt;Determine whether the query transitioned from an in-memory operation to an external operation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where a timeline becomes more than a visualization.&lt;/p&gt;

&lt;p&gt;It becomes a way to reconstruct what the query was doing.&lt;/p&gt;




&lt;h2&gt;
  
  
  JOINs Have Their Own Story
&lt;/h2&gt;

&lt;p&gt;JOIN-heavy queries can be particularly interesting because memory usage may spike during hash-table construction.&lt;/p&gt;

&lt;p&gt;For a SELECT with a JOIN, Query Metrics exposes metrics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;peak_memory_usage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JoinBuildTableRows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JoinProbeTableRows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JoinResultRows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExternalJoinWritePart&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A large gap between steady-state memory and peak memory can indicate that the JOIN's hash table became large.&lt;/p&gt;

&lt;p&gt;You can also compare the number of rows involved in building and probing the JOIN.&lt;/p&gt;

&lt;p&gt;If the JOIN result is dramatically larger than its inputs, that may indicate a many-to-many JOIN and is worth investigating.&lt;/p&gt;

&lt;p&gt;And if external JOIN metrics appear, the JOIN has spilled to disk.&lt;/p&gt;

&lt;p&gt;That gives you considerably more information than simply knowing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The JOIN was slow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can start asking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was the build side too large? Did the JOIN produce too many rows? Did it exceed memory and spill?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  GROUP BY and ORDER BY: Watch for Spilling
&lt;/h2&gt;

&lt;p&gt;Aggregation and sorting introduce another common source of performance problems.&lt;/p&gt;

&lt;p&gt;For these queries, &lt;code&gt;ArenaAllocBytes&lt;/code&gt; can reveal a steadily growing aggregation state.&lt;/p&gt;

&lt;p&gt;This can be a sign of a large aggregation workload, particularly with high-cardinality &lt;code&gt;GROUP BY&lt;/code&gt; operations.&lt;/p&gt;

&lt;p&gt;External-operation metrics can reveal when sorting or aggregation spills to disk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ExternalSortWritePart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExternalSortMerge&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExternalAggregationWritePart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExternalAggregationMerge&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spilling isn't necessarily an error.&lt;/p&gt;

&lt;p&gt;It is a safety mechanism that allows an operation to continue when it cannot fit entirely in memory.&lt;/p&gt;

&lt;p&gt;But it is significantly slower than keeping the operation in memory.&lt;/p&gt;

&lt;p&gt;So if the timeline shows memory increasing and then external writes suddenly appearing, you've found an important part of the query's performance story.&lt;/p&gt;




&lt;h2&gt;
  
  
  CPU-Bound or Waiting?
&lt;/h2&gt;

&lt;p&gt;A query can consume a lot of wall-clock time without actually spending that time computing.&lt;/p&gt;

&lt;p&gt;Query Metrics separates several time measurements, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RealTimeMicroseconds&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserTimeMicroseconds&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SystemTimeMicroseconds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If real time is significantly greater than user plus system CPU time, the query is likely spending substantial time waiting—for example on I/O, locks, or network activity.&lt;/p&gt;

&lt;p&gt;If user CPU time dominates, the query is more likely CPU-bound.&lt;/p&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;Adding more CPU won't fix a query that is mostly waiting on storage.&lt;/p&gt;

&lt;p&gt;Likewise, increasing memory won't necessarily solve a problem caused by network latency.&lt;/p&gt;

&lt;p&gt;The timeline helps answer a fundamental question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the query spending its time computing, or waiting?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Distributed Queries Add Another Dimension
&lt;/h2&gt;

&lt;p&gt;When a query runs across shards, network metrics become important.&lt;/p&gt;

&lt;p&gt;Query Metrics exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NetworkSendBytes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NetworkReceiveBytes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NetworkReceiveElapsedMicroseconds&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DistributedConnectionMissCount&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Large amounts of data moving between shards may indicate that filters could be pushed down or that &lt;code&gt;PREWHERE&lt;/code&gt; could reduce the amount of data being transferred.&lt;/p&gt;

&lt;p&gt;High network receive time can point toward network bandwidth or latency as the bottleneck.&lt;/p&gt;

&lt;p&gt;Again, the goal isn't simply to find a large number.&lt;/p&gt;

&lt;p&gt;It's to understand &lt;strong&gt;where in the query's lifetime that behavior happened&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Query Metrics Finds the Important Counters
&lt;/h2&gt;

&lt;p&gt;There is an interesting challenge behind this feature.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;system.query_metric_log&lt;/code&gt; has more than 700 columns, and the available columns can change between ClickHouse® versions.&lt;/p&gt;

&lt;p&gt;Hardcoding a list of metrics would make the feature fragile.&lt;/p&gt;

&lt;p&gt;Instead, Query Metrics discovers the active metrics dynamically.&lt;/p&gt;

&lt;p&gt;For the selected query, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetches the rows from &lt;code&gt;system.query_metric_log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Scans every row to find columns containing non-zero values.&lt;/li&gt;
&lt;li&gt;Sorts metrics by activity.&lt;/li&gt;
&lt;li&gt;Keeps the 100 most active metrics when more than 100 are present.&lt;/li&gt;
&lt;li&gt;Classifies each metric by category and unit.&lt;/li&gt;
&lt;li&gt;Splits crowded categories into multiple charts.&lt;/li&gt;
&lt;li&gt;Builds the charts directly from the fetched data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach is especially useful because metrics can become active &lt;strong&gt;mid-query&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, an external sort metric might remain zero for the first few seconds and suddenly become non-zero when the sort starts spilling.&lt;/p&gt;

&lt;p&gt;Looking across all snapshots catches that transition.&lt;/p&gt;




&lt;h2&gt;
  
  
  You Don't Need to Understand Hundreds of Metrics
&lt;/h2&gt;

&lt;p&gt;At first, hundreds of metrics can feel overwhelming.&lt;/p&gt;

&lt;p&gt;But you don't need to memorize every counter.&lt;/p&gt;

&lt;p&gt;Start with a few questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did Memory Spike?
&lt;/h3&gt;

&lt;p&gt;Look at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;memory_usage&lt;/code&gt; vs. &lt;code&gt;peak_memory_usage&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Did the Query Read Too Much?
&lt;/h3&gt;

&lt;p&gt;Look at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OSReadBytes&lt;/code&gt;, &lt;code&gt;SelectedRows&lt;/code&gt;, and &lt;code&gt;SelectedMarks&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Storage the Bottleneck?
&lt;/h3&gt;

&lt;p&gt;Look at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DiskReadElapsedMicroseconds&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the Cache Helping?
&lt;/h3&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MarkCacheHits&lt;/code&gt; vs. &lt;code&gt;MarkCacheMisses&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Did the Query Spill?
&lt;/h3&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ExternalSort*&lt;/code&gt;, &lt;code&gt;ExternalAggregation*&lt;/code&gt;, or &lt;code&gt;ExternalJoin*&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Is It CPU-Bound?
&lt;/h3&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RealTimeMicroseconds&lt;/code&gt; with the CPU time metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a Distributed Query Moving Too Much Data?
&lt;/h3&gt;

&lt;p&gt;Look at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NetworkSendBytes&lt;/code&gt; and &lt;code&gt;NetworkReceiveBytes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These metrics cover many of the most common performance investigations.&lt;/p&gt;

&lt;p&gt;The goal isn't to understand everything.&lt;/p&gt;

&lt;p&gt;The goal is to find the &lt;strong&gt;few metrics that explain what changed&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compare a Slow Query With a Fast Query
&lt;/h2&gt;

&lt;p&gt;One of the most useful ways to use Query Metrics is to compare two queries.&lt;/p&gt;

&lt;p&gt;Start with the slow query.&lt;/p&gt;

&lt;p&gt;Identify the categories with unusually high activity.&lt;/p&gt;

&lt;p&gt;Then examine the fast query.&lt;/p&gt;

&lt;p&gt;The differences can point directly toward the cause.&lt;/p&gt;

&lt;p&gt;For example, imagine the slow query has many &lt;code&gt;MarkCacheMisses&lt;/code&gt;, while the fast query has significantly more &lt;code&gt;MarkCacheHits&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That suggests the slow query is reading more from disk while the fast query is benefiting from cache.&lt;/p&gt;

&lt;p&gt;The comparison turns performance debugging into a much simpler question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What did the fast query avoid doing that the slow query had to do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can be far more actionable than simply comparing execution times.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before You Start
&lt;/h2&gt;

&lt;p&gt;There are a few prerequisites for Query Metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ClickHouse® 26.3 LTS or newer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;query_metric_log&lt;/code&gt; enabled&lt;/li&gt;
&lt;li&gt;A query lasting more than roughly one second&lt;/li&gt;
&lt;li&gt;SELECT access to &lt;code&gt;system.query_metric_log&lt;/code&gt; and &lt;code&gt;system.query_log&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On ClickHouse® 26.3 LTS, &lt;code&gt;query_metric_log&lt;/code&gt; is enabled by default.&lt;/p&gt;

&lt;p&gt;The sampling interval defaults to 1000 ms.&lt;/p&gt;

&lt;p&gt;You can configure the interval using &lt;code&gt;query_metric_log_interval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lower values provide more granular data, but they also increase collection overhead. Setting it to &lt;code&gt;0&lt;/code&gt; disables metric collection entirely.&lt;/p&gt;

&lt;p&gt;So there is a trade-off:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More detail means more collection overhead.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Debugging Workflow
&lt;/h2&gt;

&lt;p&gt;When a query suddenly becomes slow—or gets killed for exceeding its memory limit—don't start by staring at the final query duration.&lt;/p&gt;

&lt;p&gt;Start with the timeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Find the Query
&lt;/h3&gt;

&lt;p&gt;Load queries for the relevant time range and select the query you want to investigate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Look at Memory
&lt;/h3&gt;

&lt;p&gt;Find the point where &lt;code&gt;peak_memory_usage&lt;/code&gt; changes sharply.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Check What Happened at That Timestamp
&lt;/h3&gt;

&lt;p&gt;Look at CPU, disk, cache, JOIN, aggregation, and external-operation metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Identify the Transition
&lt;/h3&gt;

&lt;p&gt;Did the query start reading heavily from disk?&lt;/p&gt;

&lt;p&gt;Did a JOIN hash table grow?&lt;/p&gt;

&lt;p&gt;Did aggregation state increase?&lt;/p&gt;

&lt;p&gt;Did the operation start spilling?&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Validate With Related Metrics
&lt;/h3&gt;

&lt;p&gt;One metric rarely tells the whole story.&lt;/p&gt;

&lt;p&gt;Look for multiple metrics changing together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Compare Against a Healthy Query
&lt;/h3&gt;

&lt;p&gt;If possible, compare the timeline with a faster or previously successful execution.&lt;/p&gt;

&lt;p&gt;This approach shifts debugging from guessing to observation.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What configuration should I change?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you first ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What actually happened?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That usually leads to a much better fix.&lt;/p&gt;




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

&lt;p&gt;Query duration tells you &lt;strong&gt;that&lt;/strong&gt; something happened.&lt;/p&gt;

&lt;p&gt;Per-second Query Metrics helps you understand &lt;strong&gt;what happened along the way&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A memory spike, a burst of disk I/O, a sudden increase in cache misses, a growing JOIN hash table, or an operation spilling to disk doesn't magically appear at the end of a query. It happens at a specific point during execution.&lt;/p&gt;

&lt;p&gt;That is what makes the timeline so valuable.&lt;/p&gt;

&lt;p&gt;Instead of looking at a query as a collection of final numbers—12 seconds, 8 GB of memory, 40 GB read—you can follow its execution second by second and correlate changes across memory, CPU, storage, cache, network, and execution metrics.&lt;/p&gt;

&lt;p&gt;The debugging workflow becomes simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find the spike → find what changed → correlate the metrics → identify the bottleneck → fix the query.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the real value of Query Metrics.&lt;/p&gt;

&lt;p&gt;It doesn't just tell you that a query was slow or expensive. It gives you the evidence needed to understand &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And when a production query starts behaving differently, that difference between &lt;em&gt;knowing something is wrong&lt;/em&gt; and &lt;em&gt;knowing exactly when and why it went wrong&lt;/em&gt; can save a lot of debugging time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't just measure how long a query takes. See what it did along the way.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Quantrail-Data/CH-Ops" rel="noopener noreferrer"&gt;CH-Ops Repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>analytics</category>
      <category>devops</category>
    </item>
    <item>
      <title>Where Did the Time Go? Query Profiler in CHOps</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Fri, 04 Sep 2026 09:46:05 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/where-did-the-time-go-query-profiler-in-chops-2kk3</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/where-did-the-time-go-query-profiler-in-chops-2kk3</guid>
      <description>&lt;p&gt;A query takes 9 seconds. You read the SQL. You read &lt;code&gt;EXPLAIN&lt;/code&gt;. Nothing looks wrong.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXPLAIN&lt;/code&gt; tells you what ClickHouse® &lt;em&gt;plans&lt;/em&gt; to do. It does not tell you where the 9 seconds actually went. For that you need to watch the query run — and that is what the CHOps Query Profiler does.&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;SQL Tools → Query Profiler&lt;/strong&gt;, pick a query, and CHOps draws a flame graph of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a flame graph actually is
&lt;/h2&gt;

&lt;p&gt;A flame graph is a picture of where time went. That's it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;bottom bar&lt;/strong&gt; is where the query starts.&lt;/li&gt;
&lt;li&gt;Every &lt;strong&gt;bar&lt;/strong&gt; is one function that ran.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Width&lt;/strong&gt; = time. Wider bar, more time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stacked bars&lt;/strong&gt; = the call chain. A called B, B called C.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read it bottom to top for &lt;em&gt;what called what&lt;/em&gt;, left to right for &lt;em&gt;what ran&lt;/em&gt;. But mostly you just find the widest bar. That's the bottleneck.&lt;/p&gt;

&lt;p&gt;You do not need to know C++ or ClickHouse® internals for this. The function names carry enough meaning on their own:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If the widest bar says…&lt;/th&gt;
&lt;th&gt;The query is…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReadBufferFromFileDescriptor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IO-bound — reading too much from disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HashTable::insert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;building hash tables for GROUP BY or JOIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MergeTreeDataSelectExecutor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;scanning MergeTree parts — likely missing the primary index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemoryTracker::allocImpl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;allocating — expected at the top of a memory trace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Widest bar, read the name, act on it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First, understand what a "sample" is
&lt;/h2&gt;

&lt;p&gt;This is the part that trips people up, and it explains most of the confusion people have with the tool.&lt;/p&gt;

&lt;p&gt;ClickHouse® does &lt;strong&gt;not&lt;/strong&gt; record every function call. That would be ruinously slow. Instead it takes periodic snapshots: a timer fires, and whatever function each thread happens to be executing at that instant gets written as one row into &lt;code&gt;system.trace_log&lt;/code&gt;. That row is one &lt;strong&gt;sample&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A flame graph is nothing more than those rows counted and stacked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bar width = how many snapshots caught that function running.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default ClickHouse® takes &lt;strong&gt;one snapshot per second, per thread&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sampling period&lt;/th&gt;
&lt;th&gt;Snapshots/sec/thread&lt;/th&gt;
&lt;th&gt;A 5s query on 4 threads gives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;1000000000&lt;/code&gt; ns (1s — the default)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;~20 samples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;10000000&lt;/code&gt; ns (10ms)&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;~2,000 samples&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Twenty samples produce a few blocky bars and percentages that swing on every re-run. Two thousand produce a graph you can trust.&lt;/p&gt;

&lt;p&gt;So if your flame graph looks thin, the query is usually fine — the sampling rate is the problem.&lt;/p&gt;

&lt;p&gt;Raise it for that one 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="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;heavy&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;SETTINGS&lt;/span&gt;
    &lt;span class="n"&gt;query_profiler_real_time_period_ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query_profiler_cpu_time_period_ns&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those go on &lt;strong&gt;the query you want to profile&lt;/strong&gt;, when you run it.&lt;/p&gt;

&lt;p&gt;They change how ClickHouse® records that query while it executes. Nothing on the server changes, and no other query is affected.&lt;/p&gt;

&lt;p&gt;Memory traces work on a different trigger: instead of a timer, ClickHouse® records a stack every time the query allocates another &lt;code&gt;memory_profiler_step&lt;/code&gt; bytes (4 MiB by default). Lower that value for a denser memory graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Set &lt;strong&gt;From&lt;/strong&gt; and &lt;strong&gt;To&lt;/strong&gt; to when the query ran. The range is capped at &lt;strong&gt;24 hours&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Load Queries&lt;/strong&gt;. The header shows how many queries in that window have trace data.&lt;/li&gt;
&lt;li&gt;Find your query. The picker searches by query text or query ID, and each row shows the ID, a SQL preview, the duration, the sample count and the timestamp.&lt;/li&gt;
&lt;li&gt;Click it — the selected ID appears below the list, with &lt;strong&gt;Clear&lt;/strong&gt; beside it.&lt;/li&gt;
&lt;li&gt;Choose a &lt;strong&gt;Trace Type&lt;/strong&gt;, then &lt;strong&gt;Generate Flame Graph&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sample count in the list is the number to watch.&lt;/p&gt;

&lt;p&gt;Single digits means you're about to get a graph that tells you nothing; go back and re-run the query with a higher sampling rate.&lt;/p&gt;

&lt;p&gt;Above the graph you get three quick stats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total samples&lt;/li&gt;
&lt;li&gt;Unique stacks&lt;/li&gt;
&lt;li&gt;Max depth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Unique stacks&lt;/strong&gt; is the one people overlook.&lt;/p&gt;

&lt;p&gt;A low number means the query took very few distinct code paths, so there's little to see regardless of sample count.&lt;/p&gt;

&lt;p&gt;If a query you just ran doesn't appear, run:&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;SYSTEM&lt;/span&gt; &lt;span class="n"&gt;FLUSH&lt;/span&gt; &lt;span class="n"&gt;LOGS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trace data is buffered before it's written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trace types: pick the one that matches your symptom
&lt;/h2&gt;

&lt;p&gt;Nine options, and picking the right one matters more than anything else on the page.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trace Type&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;th&gt;Use when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;All Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;everything at once&lt;/td&gt;
&lt;td&gt;first look, general orientation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;where CPU went&lt;/td&gt;
&lt;td&gt;the default choice for a slow query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wall Clock (Real)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;where clock time went, waits included&lt;/td&gt;
&lt;td&gt;slow query, but CPU looks idle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory (Watermark)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;what caused the biggest allocations&lt;/td&gt;
&lt;td&gt;query was killed for memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory (Sampled)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the spread of memory use&lt;/td&gt;
&lt;td&gt;broad memory picture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Peak&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;what was running at the memory high point&lt;/td&gt;
&lt;td&gt;pinning down a spike&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Profile Events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;which internal counters moved most&lt;/td&gt;
&lt;td&gt;advanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jemalloc Samples&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;what the allocator is doing&lt;/td&gt;
&lt;td&gt;advanced — fragmentation debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;XRay instrumentation traces&lt;/td&gt;
&lt;td&gt;advanced — needs &lt;code&gt;SYSTEM INSTRUMENT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Not all nine work out of the box.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some rely on collectors that are off by default, and if a collector is off you get an empty graph rather than an error.&lt;/p&gt;

&lt;p&gt;Worth knowing before you assume the tool is broken:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trace type&lt;/th&gt;
&lt;th&gt;On a stock server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU Time, Wall Clock (Real)&lt;/td&gt;
&lt;td&gt;work — but at 1 sample/sec/thread, thin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory (Watermark), Memory Peak&lt;/td&gt;
&lt;td&gt;work — a stack every 4 MiB allocated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory (Sampled)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;empty&lt;/strong&gt; — needs &lt;code&gt;memory_profiler_sample_probability &amp;gt; 0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Profile Events&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;empty&lt;/strong&gt; — needs &lt;code&gt;trace_profile_events = 1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jemalloc Samples&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;empty&lt;/strong&gt; unless the jemalloc profiler is enabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instrumentation&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;empty&lt;/strong&gt; unless &lt;code&gt;SYSTEM INSTRUMENT&lt;/code&gt; is on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Check where you stand:&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="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%profiler%'&lt;/span&gt;
   &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'trace_profile_events'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graph also re-labels itself for the trace type you picked.&lt;/p&gt;

&lt;p&gt;On a CPU or All Types graph, the stats read &lt;strong&gt;total samples&lt;/strong&gt; and the tooltip shows counts.&lt;/p&gt;

&lt;p&gt;Switch to a memory type and the same line reads &lt;strong&gt;total bytes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A caption next to the Generate button spells out what the current selection measures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory graphs get a second dropdown
&lt;/h2&gt;

&lt;p&gt;Pick a memory trace type and a &lt;strong&gt;Memory Context&lt;/strong&gt; filter appears next to it, with five options.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;th&gt;Shows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All Contexts&lt;/td&gt;
&lt;td&gt;everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global (server)&lt;/td&gt;
&lt;td&gt;server-wide allocations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User (user/merge)&lt;/td&gt;
&lt;td&gt;user and merge allocations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Process (query)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;just this query&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread&lt;/td&gt;
&lt;td&gt;thread-level allocations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Choose &lt;strong&gt;Process (query)&lt;/strong&gt; when debugging one query.&lt;/p&gt;

&lt;p&gt;Otherwise background merges and server caches get mixed into the graph and you end up optimising something unrelated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the shapes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One very wide bar
&lt;/h3&gt;

&lt;p&gt;Easiest case.&lt;/p&gt;

&lt;p&gt;One function owns the query. Read its name, act on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Many narrow towers
&lt;/h3&gt;

&lt;p&gt;Normal for queries with joins, subqueries and several aggregations — no single function dominates.&lt;/p&gt;

&lt;p&gt;Scan across all the towers for the widest single bar.&lt;/p&gt;

&lt;p&gt;Still your best lever.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two towers of similar width
&lt;/h3&gt;

&lt;p&gt;Work split across parallel paths.&lt;/p&gt;

&lt;p&gt;Widths are shares of the total, so two 50% towers means the cost is genuinely divided — fix both, or reduce the input feeding them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flat, no towers
&lt;/h3&gt;

&lt;p&gt;One code path, or too few samples.&lt;/p&gt;

&lt;p&gt;Check the unique-stacks count before concluding anything about the query.&lt;/p&gt;

&lt;p&gt;Hover any bar for its full function name and share of the total.&lt;/p&gt;

&lt;p&gt;Click to zoom into that subtree; the toolbox has restore, download and full-screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  What CHOps is doing under the hood
&lt;/h2&gt;

&lt;p&gt;No magic, and the UI doesn't hide it — a &lt;strong&gt;View Generated SQL&lt;/strong&gt; panel under the graph shows the exact query CHOps ran:&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;arrayStringConcat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;arrayReverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;arrayMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;demangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addressToSymbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="n"&gt;trace&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;';'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace_log&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;query_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;
&lt;span class="n"&gt;SETTINGS&lt;/span&gt; &lt;span class="n"&gt;allow_introspection_functions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what each part does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;addressToSymbol&lt;/code&gt; turns raw addresses into C++ symbols.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;demangle&lt;/code&gt; makes those symbols readable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;arrayReverse&lt;/code&gt; puts the root at the bottom, where a flame graph expects it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GROUP BY stack&lt;/code&gt; counts how often each call chain was sampled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CHOps folds those stacks into a tree and renders it.&lt;/p&gt;

&lt;p&gt;That folded-stack format is the same one every flame graph tool uses, which is why the output looks familiar if you've used &lt;code&gt;perf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note this query only &lt;em&gt;reads&lt;/em&gt; what was already captured.&lt;/p&gt;

&lt;p&gt;It can't create samples after the fact — which is why the sampling settings belong on the query you profile, not here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query Profiler or Processors Profile?
&lt;/h2&gt;

&lt;p&gt;CHOps ships two profilers.&lt;/p&gt;

&lt;p&gt;Different tables, different questions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Query Profiler (flame graph)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Processors Profile (pipeline)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;system.trace_log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;system.processors_profile_log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;C++ functions inside the engine&lt;/td&gt;
&lt;td&gt;logical pipeline steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;engine-level debugging&lt;/td&gt;
&lt;td&gt;day-to-day query optimisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example finding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;most of the time in a read-pool function&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ReadFromMergeTree&lt;/code&gt; 7.2s, &lt;code&gt;AggregatingTransform&lt;/code&gt; 0.3s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Start with Processors Profile.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Its names map directly to query plan steps, so the fix is usually obvious.&lt;/p&gt;

&lt;p&gt;Switch to the Query Profiler when Processors Profile has told you &lt;em&gt;which step&lt;/em&gt; is slow and you need to know &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you get a graph
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;system.trace_log&lt;/code&gt; enabled&lt;/td&gt;
&lt;td&gt;that's where samples live&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT count() FROM system.trace_log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow_introspection_functions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;needed for symbol resolution&lt;/td&gt;
&lt;td&gt;CHOps passes it for you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT on &lt;code&gt;system.trace_log&lt;/code&gt;, &lt;code&gt;system.query_log&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;the CHOps ClickHouse® user needs read access&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GRANT SELECT ON system.trace_log TO your_user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clickhouse-common-static-dbg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;optional — resolves system frames to names&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dpkg -l clickhouse-common-static-dbg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When it doesn't work
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Graph is thin — a handful of bars&lt;/td&gt;
&lt;td&gt;default 1 sample/sec/thread&lt;/td&gt;
&lt;td&gt;re-run with &lt;code&gt;query_profiler_*_period_ns = 10000000&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"No trace data found"&lt;/td&gt;
&lt;td&gt;query finished before a snapshot fired&lt;/td&gt;
&lt;td&gt;longer query, or a higher sampling rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty graph on a specific trace type&lt;/td&gt;
&lt;td&gt;that collector is off by default&lt;/td&gt;
&lt;td&gt;see the trace-type table above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query missing from the picker&lt;/td&gt;
&lt;td&gt;trace data not flushed yet&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYSTEM FLUSH LOGS;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bars show raw hex addresses&lt;/td&gt;
&lt;td&gt;no debug symbols on the server&lt;/td&gt;
&lt;td&gt;install &lt;code&gt;clickhouse-common-static-dbg&lt;/code&gt;; only new traces resolve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty query list&lt;/td&gt;
&lt;td&gt;nothing traced in that window&lt;/td&gt;
&lt;td&gt;widen the range (up to 24h)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Four habits worth forming
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Raise the sampling rate before you profile
&lt;/h3&gt;

&lt;p&gt;More snapshots, better picture.&lt;/p&gt;

&lt;p&gt;This single setting is the difference between a useless graph and a useful one.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Profile something heavy
&lt;/h3&gt;

&lt;p&gt;A query finishing in milliseconds leaves almost nothing to sample, whatever the rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Always compare CPU against Real
&lt;/h3&gt;

&lt;p&gt;Fastest way to tell computation from waiting.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Take a before and after
&lt;/h3&gt;

&lt;p&gt;Generate a graph, add the index or projection, generate again.&lt;/p&gt;

&lt;p&gt;The change in bar widths is your proof the fix worked — a far better artifact for a PR than "feels faster now."&lt;/p&gt;

&lt;p&gt;That last one is the real value.&lt;/p&gt;

&lt;p&gt;Flame graphs get sold as a diagnosis tool, but they're just as good as a verification tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimisation without a before-and-after is guessing.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;CHOps is free and open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Quantrail-Data/CH-Ops" rel="noopener noreferrer"&gt;https://github.com/Quantrail-Data/CH-Ops&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://www.ch-ops.io/docs/guide/query-profiler" rel="noopener noreferrer"&gt;https://www.ch-ops.io/docs/guide/query-profiler&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker:&lt;/strong&gt; &lt;a href="https://hub.docker.com/r/quantrailadmin1/ch-ops" rel="noopener noreferrer"&gt;https://hub.docker.com/r/quantrailadmin1/ch-ops&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run one slow query with the sampling rate turned up.&lt;/p&gt;

&lt;p&gt;Profile it.&lt;/p&gt;

&lt;p&gt;Look at the widest bar.&lt;/p&gt;

&lt;p&gt;You'll know more in thirty seconds than in an hour of reading &lt;code&gt;EXPLAIN&lt;/code&gt; output.&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>devops</category>
      <category>chops</category>
      <category>analytics</category>
    </item>
    <item>
      <title>CHOps SQL Editor: A Complete Guide to Faster ClickHouse® Query Development and Optimization</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Fri, 04 Sep 2026 06:13:08 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/chops-sql-editor-a-complete-guide-to-faster-clickhouser-query-development-and-optimization-1ml6</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/chops-sql-editor-a-complete-guide-to-faster-clickhouser-query-development-and-optimization-1ml6</guid>
      <description>&lt;h2&gt;
  
  
  What is CHOps?
&lt;/h2&gt;

&lt;p&gt;CHOps is a browser-based operations platform built for managing ClickHouse® deployments. Instead of relying entirely on the command line or HTTP APIs, it provides a unified interface for executing SQL queries, monitoring clusters, managing users, backups, alerts, dashboards, and much more — all from a single web application.&lt;/p&gt;

&lt;p&gt;One of its most powerful features is the SQL Editor, designed to simplify the entire query development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the CHOps SQL Editor
&lt;/h2&gt;

&lt;p&gt;When working with ClickHouse®, writing SQL is only one part of the job. Developers and Data Engineers often spend just as much time browsing schemas, remembering table names, checking execution plans, estimating query costs, and debugging performance issues.&lt;/p&gt;

&lt;p&gt;With traditional SQL editors, these tasks usually involve switching between multiple tools or browser tabs. That constant context switching slows development and makes query optimization more difficult.&lt;/p&gt;

&lt;p&gt;The CHOps SQL Editor is designed to solve these challenges by bringing everything into a single workspace. Whether you're exploring a new dataset, optimizing analytical queries, or troubleshooting production workloads, this editor provides all the tools needed to build, analyze, and optimize queries without leaving the page.&lt;/p&gt;

&lt;p&gt;Let's explore how these features streamline the ClickHouse® query development workflow and performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SQL Editor at a Glance
&lt;/h2&gt;

&lt;p&gt;The SQL Editor is organized into four main areas, each designed for a specific part of the query workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left:&lt;/strong&gt; Schema Explorer for browsing databases and tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top of the center:&lt;/strong&gt; Query Tabs for working on multiple queries simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Center:&lt;/strong&gt; The SQL Editor and its toolbar where you write SQL, choose how to run it, and reach the history, bookmarks, share, export, fullscreen, and AI controls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bottom:&lt;/strong&gt; The Results Panel for viewing results, execution statistics, and debugging information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layout keeps everything within reach, eliminating the need to jump between multiple applications while developing queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting First
&lt;/h2&gt;

&lt;p&gt;The SQL Editor allows direct authentication using ClickHouse® credentials.&lt;/p&gt;

&lt;p&gt;Simply enter your:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Username&lt;/li&gt;
&lt;li&gt;Password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once authenticated, the SQL editor connects securely and unlocks schema browsing, query execution, and analysis features. This design separates application login from database authentication, improving security and flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in Database Explorer
&lt;/h2&gt;

&lt;p&gt;Once connected, the first place you'll likely visit is the Schema Explorer.&lt;/p&gt;

&lt;p&gt;The Schema Explorer lets you browse your ClickHouse® databases and tables directly from the left panel. Expand a database to view its tables, then select a table to insert its fully qualified name into the SQL editor.&lt;/p&gt;

&lt;p&gt;Need to check the table structure first? The code icon beside each table opens its complete CREATE TABLE statement, making it easy to review columns, sorting keys, and table engines without leaving the editor.&lt;/p&gt;

&lt;p&gt;For databases where AI assistance is needed, the sparkles icon provides quick access to AI SQL generation. We'll explore that workflow later in Generating SQL with AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smart SQL Autocomplete
&lt;/h2&gt;

&lt;p&gt;One of the biggest productivity boosters of the SQL Editor is Autocomplete.&lt;/p&gt;

&lt;p&gt;As you type:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the editor automatically suggests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL keywords&lt;/li&gt;
&lt;li&gt;ClickHouse® functions&lt;/li&gt;
&lt;li&gt;Database names&lt;/li&gt;
&lt;li&gt;Table names&lt;/li&gt;
&lt;li&gt;Column names&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike a generic SQL editor, these suggestions come directly from your connected ClickHouse® cluster. Instead of showing generic SQL objects, the editor suggests only the databases, tables, functions, and columns that actually exist on your server.&lt;/p&gt;

&lt;p&gt;Autocomplete reduces syntax mistakes while making query writing significantly faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Instead of remembering:&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;sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_transactions_archive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you simply type:&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;sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and choose the required table from the suggestion list.&lt;/p&gt;

&lt;p&gt;Autocomplete also displays function signatures and descriptions, making it much easier to discover ClickHouse® functions without switching to external documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Your Query
&lt;/h2&gt;

&lt;p&gt;Once your query is ready, executing it is straightforward.&lt;/p&gt;

&lt;p&gt;Simply click &lt;strong&gt;Go&lt;/strong&gt;, or use &lt;code&gt;Ctrl + Enter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The action performed depends on the selected execution mode. You can either execute the SQL directly or choose one of the available EXPLAIN modes to analyze the query before running it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand Queries with EXPLAIN
&lt;/h2&gt;

&lt;p&gt;Understanding how ClickHouse® executes a query is often more valuable than simply seeing the result.&lt;/p&gt;

&lt;p&gt;Normally, checking a query plan requires manually prefixing every query with &lt;code&gt;EXPLAIN&lt;/code&gt;, executing it, reviewing the output, and then removing the prefix before running the actual query.&lt;/p&gt;

&lt;p&gt;The CHOps SQL Editor simplifies this workflow. Simply select an EXPLAIN mode from the dropdown beside the &lt;strong&gt;Go&lt;/strong&gt; button and execute the query. No query rewriting is required.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Explain mode&lt;/th&gt;
&lt;th&gt;What it shows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Explain &amp;amp; Explain plan&lt;/td&gt;
&lt;td&gt;Displays the execution plan in order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain syntax&lt;/td&gt;
&lt;td&gt;Your query after ClickHouse®'s internal rewrites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain query tree&lt;/td&gt;
&lt;td&gt;The analyzed, internal form of the query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain pipeline&lt;/td&gt;
&lt;td&gt;The actual processors that will do the work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain estimate&lt;/td&gt;
&lt;td&gt;Estimated rows, parts, and marks to be read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain AST (graph)&lt;/td&gt;
&lt;td&gt;Visualizes the parsed query structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain pipeline (graph)&lt;/td&gt;
&lt;td&gt;Displays the execution pipeline as an interactive graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain plan (JSON)&lt;/td&gt;
&lt;td&gt;Returns the execution plan in JSON format&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  AI-Powered SQL Generation
&lt;/h2&gt;

&lt;p&gt;Not every query needs to start from scratch. Sometimes you know what information you need but not the exact SQL syntax.&lt;/p&gt;

&lt;p&gt;The Generate SQL feature helps convert natural language into SQL queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example prompt:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Show the top 10 products by revenue during the last month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The generated SQL can then be reviewed, modified, and executed like any manually written query, making it useful for rapid prototyping and learning new schemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Tools Around the Query
&lt;/h2&gt;

&lt;p&gt;A few smaller features exist specifically to shorten the time between "something feels slow" and "here is why":&lt;/p&gt;

&lt;h3&gt;
  
  
  Estimate Query Cost
&lt;/h3&gt;

&lt;p&gt;Before executing an expensive query, it's often useful to understand how much work ClickHouse® is expected to perform.&lt;/p&gt;

&lt;p&gt;The Cost feature estimates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rows to be read&lt;/li&gt;
&lt;li&gt;Parts to be scanned&lt;/li&gt;
&lt;li&gt;Marks to be processed&lt;/li&gt;
&lt;li&gt;Available indexes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without actually executing the query.&lt;/p&gt;

&lt;p&gt;This provides a quick way to identify potentially expensive queries before they impact your cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Max Rows Control
&lt;/h3&gt;

&lt;p&gt;Max Rows caps how many rows come back to the browser, so an accidental unbounded query does not lock up the tab while you are trying to debug something else.&lt;/p&gt;

&lt;p&gt;When larger datasets are required, the limit can be adjusted or the results can be exported instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action Buttons After a Run
&lt;/h3&gt;

&lt;p&gt;Once a query finishes and ClickHouse® has assigned it a query ID, a row of buttons appears in the statistics bar to take you straight into the profiling tools for that exact query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query_id:&lt;/strong&gt; copies ClickHouse®'s ID for the query to your clipboard, useful for looking it up in system tables or handing to whoever administers the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flame Graph:&lt;/strong&gt; opens the Query Profiler with this query loaded, showing where it spent its time. Reach for this first when something is slow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline:&lt;/strong&gt; opens the Processors Profile with the query loaded, rendering its execution as a diagram so you can see which step dominated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics:&lt;/strong&gt; opens Query Metrics with the query loaded, showing a second-by-second view of how it used resources while it ran.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  History
&lt;/h3&gt;

&lt;p&gt;History keeps every query you have run, with its timing and success or failure.&lt;/p&gt;

&lt;p&gt;Instead of rewriting or searching through old SQL files, you can quickly reopen previous queries, review execution results, and continue where you left off.&lt;/p&gt;

&lt;p&gt;History keeps your most recent queries and drops the oldest as new ones arrive. The &lt;strong&gt;Clear&lt;/strong&gt; button empties it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bookmarks
&lt;/h3&gt;

&lt;p&gt;Some queries become part of your daily workflow.&lt;/p&gt;

&lt;p&gt;Bookmarks let you save the ones you check often, like a recurring health check query, so you are not retyping the same debugging query every day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share Option
&lt;/h3&gt;

&lt;p&gt;Need a second opinion on a query?&lt;/p&gt;

&lt;p&gt;The Share feature generates a link containing the current SQL, allowing teammates to open the query directly in their own SQL Editor.&lt;/p&gt;

&lt;p&gt;Since every user reconnects using their own ClickHouse® credentials, sharing a query never grants access to your database. It simply shares the SQL itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare Query Performance
&lt;/h2&gt;

&lt;p&gt;Next to the connect control is a second switch: &lt;strong&gt;Regular&lt;/strong&gt; and &lt;strong&gt;Comparison&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Regular is the single query view described above.&lt;/p&gt;

&lt;p&gt;Comparison splits the screen into a current query on the left and an experimental rewrite on the right, each with its own cost estimate and run button.&lt;/p&gt;

&lt;p&gt;This is the mode built for tuning.&lt;/p&gt;

&lt;p&gt;Paste the slow query on one side, write a rewrite on the other, and check whether the rewrite actually reduces estimated cost before you touch production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export Option
&lt;/h2&gt;

&lt;p&gt;Once your query is complete, exporting the results is just as simple.&lt;/p&gt;

&lt;p&gt;The SQL Editor supports exporting query results in multiple formats, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSV&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;Parquet&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exports are processed on the server, allowing large datasets to be generated without affecting the browser session.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Workflow
&lt;/h2&gt;

&lt;p&gt;A typical workflow inside the CHOps SQL Editor looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect to your ClickHouse® deployment.&lt;/li&gt;
&lt;li&gt;Browse databases and tables using the Schema Explorer.&lt;/li&gt;
&lt;li&gt;Use autocomplete to discover tables, columns, and functions.&lt;/li&gt;
&lt;li&gt;Write your SQL query.&lt;/li&gt;
&lt;li&gt;Use EXPLAIN to understand query execution.&lt;/li&gt;
&lt;li&gt;Estimate query cost before running expensive workloads.&lt;/li&gt;
&lt;li&gt;Execute the query.&lt;/li&gt;
&lt;li&gt;Review the results and execution statistics.&lt;/li&gt;
&lt;li&gt;Use Query Profiler, Pipeline, or Metrics when troubleshooting.&lt;/li&gt;
&lt;li&gt;Use Comparison Mode to test query improvements.&lt;/li&gt;
&lt;li&gt;Bookmark useful queries for future use.&lt;/li&gt;
&lt;li&gt;Share SQL with teammates when collaboration is needed.&lt;/li&gt;
&lt;li&gt;Export results when required.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Each feature in the SQL Editor solves a small part of the query development process.&lt;/p&gt;

&lt;p&gt;Autocomplete reduces typing, EXPLAIN helps you understand execution plans, Cost estimation highlights expensive queries before execution, and Comparison Mode makes performance tuning much easier.&lt;/p&gt;

&lt;p&gt;Individually, these features improve specific tasks.&lt;/p&gt;

&lt;p&gt;Together, they reduce context switching, simplify query optimization, and provide a smoother development experience for anyone working with ClickHouse®.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;If you're new to CHOps or would like to explore it further, the following resources are a great place to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting Started with CHOps (Installation Guide) - &lt;a href="https://www.ch-ops.io/blog/install-ch-ops-in-10-minutes-docker-binary-or-source" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/install-ch-ops-in-10-minutes-docker-binary-or-source&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CHOps Official GitHub Repository - &lt;a href="https://github.com/Quantrail-Data/CH-Ops/tree/main" rel="noopener noreferrer"&gt;https://github.com/Quantrail-Data/CH-Ops/tree/main&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CHOps Demo Video - &lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7489909665477091328" rel="noopener noreferrer"&gt;https://www.linkedin.com/feed/update/urn:li:activity:7489909665477091328&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>chops</category>
      <category>devops</category>
      <category>analytics</category>
    </item>
    <item>
      <title>ClickHouse® 26.8 LTS Release: What's New and Why It Matters</title>
      <dc:creator>Kanishga Subramani</dc:creator>
      <pubDate>Sat, 29 Aug 2026 05:45:03 +0000</pubDate>
      <link>https://dev.to/kanishga_subramani_49ad73/clickhouser-268-lts-release-whats-new-and-why-it-matters-4lk4</link>
      <guid>https://dev.to/kanishga_subramani_49ad73/clickhouser-268-lts-release-whats-new-and-why-it-matters-4lk4</guid>
      <description>&lt;p&gt;ClickHouse® 26.8 is the newest LTS release, and it is a substantial one: &lt;strong&gt;21 backward-incompatible changes, 49 new features, 127 performance improvements, and around 30 settings that changed their default value.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article covers 26.8 on its own — what landed, what breaks, and what to verify before you upgrade.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Release status at time of writing (27 August 2026).&lt;/strong&gt; ClickHouse® 26.8 has been announced, but the release is not fully published yet and the upstream changelog still marks the 26.8 section as in progress. Confirm the current release status before you plan an upgrade window.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  In This Article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;At a glance&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Breaking changes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingestion and the write path&lt;/li&gt;
&lt;li&gt;Type and function semantics&lt;/li&gt;
&lt;li&gt;Query planning and output&lt;/li&gt;
&lt;li&gt;Security and configuration&lt;/li&gt;
&lt;li&gt;Removals&lt;/li&gt;
&lt;li&gt;Monitoring and introspection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Default settings that changed in 26.8&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Behaviour&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Performance (enabled by default)&lt;/li&gt;
&lt;li&gt;MergeTree - on-disk formats&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What's new&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL surface&lt;/li&gt;
&lt;li&gt;Server and operations&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Joins and text search&lt;/li&gt;
&lt;li&gt;Data lakes&lt;/li&gt;
&lt;li&gt;AI functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upgrade checklist&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Summary&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  At a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Entries&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backward incompatible changes&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New features&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Experimental features&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance improvements&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Improvements&lt;/td&gt;
&lt;td&gt;138&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bug fixes&lt;/td&gt;
&lt;td&gt;556&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Breaking Changes
&lt;/h1&gt;

&lt;p&gt;All 21 breaking changes, grouped by what they affect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ingestion and the Write Path
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;max_insert_threads&lt;/code&gt; now defaults to &lt;code&gt;auto&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It resolves to the number of CPU cores available to the server, parallelising &lt;code&gt;INSERT SELECT&lt;/code&gt; by default. It can also parallelise the writing side of a plain &lt;code&gt;INSERT&lt;/code&gt; where the destination write path can safely fan out.&lt;/p&gt;

&lt;p&gt;Two consequences worth planning for: the number of parts created by such queries changes, and so does the order of inserted rows.&lt;/p&gt;

&lt;p&gt;If you have tight &lt;code&gt;parts_to_throw_insert&lt;/code&gt; thresholds, or anything depending on insertion order — a non-deterministic &lt;code&gt;ORDER BY&lt;/code&gt; tie-break, &lt;code&gt;_part&lt;/code&gt; or &lt;code&gt;_block_number&lt;/code&gt; assumptions, a ReplacingMergeTree without a proper version column — test this before rolling out.&lt;/p&gt;

&lt;p&gt;A detail that catches people diffing configurations: the declared default is 0 in both 26.7 and 26.8.&lt;/p&gt;

&lt;p&gt;What changed is the setting's type, from &lt;code&gt;UInt64&lt;/code&gt; to &lt;code&gt;MaxThreads&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under &lt;code&gt;UInt64&lt;/code&gt;, both 0 and 1 meant single-threaded; under &lt;code&gt;MaxThreads&lt;/code&gt;, 0 resolves to auto.&lt;/p&gt;

&lt;p&gt;So the value column in &lt;code&gt;system.settings&lt;/code&gt; reads 0 before and after, while the behaviour has flipped.&lt;/p&gt;

&lt;p&gt;Upstream's own compatibility mapping records the change as 1 → 0 for this reason — 1 is what you now set to get the old behaviour, not what the declaration used to say.&lt;/p&gt;

&lt;p&gt;Restore with:&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;SET&lt;/span&gt; &lt;span class="n"&gt;max_insert_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use a compatibility version below 26.8.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lightweight &lt;code&gt;UPDATE&lt;/code&gt; patch parts use a new v2 on-disk format
&lt;/h3&gt;

&lt;p&gt;They are now sorted by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(sorting_key..., _block_number, _block_offset)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and applied with a new merging algorithm.&lt;/p&gt;

&lt;p&gt;Peak memory during apply is bounded by the largest equal-sort-key run rather than the full patch, and updates crossing merge boundaries no longer fall back to an in-memory Join apply.&lt;/p&gt;

&lt;p&gt;Old-format patch parts remain readable.&lt;/p&gt;

&lt;p&gt;For replicated clusters this requires a rolling-upgrade pin.&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;patch_parts_version&lt;/code&gt; is a MergeTree setting rather than a session setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;merge_tree&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;patch_parts_version&amp;gt;&lt;/span&gt;v1&lt;span class="nt"&gt;&amp;lt;/patch_parts_version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/merge_tree&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&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;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;my_table&lt;/span&gt; &lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;SETTING&lt;/span&gt; &lt;span class="n"&gt;patch_parts_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'v1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hold that until every replica is on 26.8, then remove it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Object-storage disk transactions
&lt;/h3&gt;

&lt;p&gt;Object-storage disk transactions now use the metadata storage's native transactions by default instead of the previous fake transactions.&lt;/p&gt;

&lt;p&gt;The upstream changelog does not explain why this is listed as backward-incompatible, so treat it as worth testing if you run object-storage disks.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;disable_insertion_and_mutation&lt;/code&gt; changes
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;disable_insertion_and_mutation&lt;/code&gt; now also stops background consumption from &lt;code&gt;Kafka&lt;/code&gt;, &lt;code&gt;RabbitMQ&lt;/code&gt; and &lt;code&gt;NATS&lt;/code&gt; tables, while still permitting direct writes to external storage.&lt;/p&gt;

&lt;p&gt;Gated &lt;code&gt;Kafka2&lt;/code&gt;, &lt;code&gt;NATS&lt;/code&gt; and &lt;code&gt;RabbitMQ&lt;/code&gt; tables no longer initialise consumers for a direct &lt;code&gt;SELECT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Separately, &lt;code&gt;message_queue_disable_insertion&lt;/code&gt; now requires a server restart to take effect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Type and Function Semantics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unquoted JSON numbers are now Unix timestamps
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;JSONEachRow&lt;/code&gt; and similar formats, an unquoted number for a &lt;code&gt;DateTime&lt;/code&gt; or &lt;code&gt;DateTime64&lt;/code&gt; column is read as a Unix timestamp with optional sub-second precision, consistent with &lt;code&gt;Values&lt;/code&gt;, &lt;code&gt;CAST&lt;/code&gt; and &lt;code&gt;toDateTime64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Previously a fractional value such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1703363853.035
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;was rejected outright, and a bare integer such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1703363853
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;was read as the raw scaled value of &lt;code&gt;DateTime64&lt;/code&gt;, producing a 1970 timestamp.&lt;/p&gt;

&lt;p&gt;Quoted strings and ClickHouse®'s own JSON output are unaffected.&lt;/p&gt;

&lt;p&gt;For most pipelines this is a silent fix.&lt;/p&gt;

&lt;p&gt;If yours was compensating for the old raw-value behaviour, it will now double-correct.&lt;/p&gt;

&lt;p&gt;Restore with:&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;SET&lt;/span&gt; &lt;span class="n"&gt;input_format_read_datetime_number_as_raw_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;Date32&lt;/code&gt; range extended
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Date32&lt;/code&gt; range is extended from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1900-01-01, 2299-12-31]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0000-01-01, 9999-12-31]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;matching &lt;code&gt;DateTime64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Parsing and conversions accept the extended range instead of silently clamping.&lt;/p&gt;

&lt;p&gt;The compatibility note matters: in &lt;code&gt;toDate32(N)&lt;/code&gt;, values in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[120530, 2932896]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;are now interpreted as day numbers — dates from 2300-01-01 to 9999-12-31 — rather than Unix timestamps in early 1970.&lt;/p&gt;

&lt;p&gt;Numbers below the day number of &lt;code&gt;0000-01-01&lt;/code&gt;, and timestamps after &lt;code&gt;9999-12-31&lt;/code&gt;, saturate to the new boundaries.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;arrayIntersect&lt;/code&gt; and &lt;code&gt;arraySymmetricDifference&lt;/code&gt; deduplication fixed
&lt;/h3&gt;

&lt;p&gt;A value repeated inside a single argument is no longer treated as though it appeared in several arguments.&lt;/p&gt;

&lt;p&gt;A value now counts for an argument only when it was present in every argument before it, so the result contains exactly the values present in all arguments.&lt;/p&gt;

&lt;p&gt;Examples:&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;arrayIntersect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- []&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;arrayIntersect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- [2]&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;arraySymmetricDifference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- [2, 1]&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;arraySymmetricDifference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- [2, 1]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;arrayUnion&lt;/code&gt; is unaffected.&lt;/p&gt;

&lt;p&gt;As part of the same change, &lt;code&gt;arrayIntersect&lt;/code&gt; builds its hash table from the smallest argument rather than all of them — up to 1.85x faster and a third less memory when argument sizes differ significantly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Window functions over &lt;code&gt;AggregateFunction&lt;/code&gt; columns are rejected
&lt;/h3&gt;

&lt;p&gt;A window &lt;code&gt;PARTITION BY&lt;/code&gt; or &lt;code&gt;ORDER BY&lt;/code&gt; over an &lt;code&gt;AggregateFunction&lt;/code&gt; column now raises &lt;code&gt;ILLEGAL_COLUMN&lt;/code&gt;, as top-level &lt;code&gt;ORDER BY&lt;/code&gt; over such a column already did.&lt;/p&gt;

&lt;p&gt;Previously at least one analyzer accepted it, and window &lt;code&gt;PARTITION BY&lt;/code&gt; partitioned differently depending on &lt;code&gt;max_threads&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The refusal covers states nested in &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;Tuple&lt;/code&gt;, &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Variant&lt;/code&gt; or &lt;code&gt;SimpleAggregateFunction&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;SimpleAggregateFunction&lt;/code&gt; over an ordinary type, &lt;code&gt;QBit&lt;/code&gt;, and &lt;code&gt;GROUP BY&lt;/code&gt; or &lt;code&gt;DISTINCT&lt;/code&gt; over a state are all unaffected.&lt;/p&gt;




&lt;h3&gt;
  
  
  Timespan settings that overflow &lt;code&gt;Int64&lt;/code&gt; microseconds are rejected
&lt;/h3&gt;

&lt;p&gt;This applies to millisecond and second settings.&lt;/p&gt;




&lt;h1&gt;
  
  
  Query Planning and Output
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Trivial views over &lt;code&gt;Distributed&lt;/code&gt; tables are pushed to the shards
&lt;/h3&gt;

&lt;p&gt;For a view whose body is a plain &lt;code&gt;SELECT&lt;/code&gt; over a single &lt;code&gt;Distributed&lt;/code&gt; table, the whole outer query now goes to the shards.&lt;/p&gt;

&lt;p&gt;This is the new:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;optimize_trivial_view_pushdown_to_distributed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;setting, enabled by default.&lt;/p&gt;

&lt;p&gt;Observable behaviour changes in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FINAL&lt;/code&gt; and &lt;code&gt;SAMPLE&lt;/code&gt; written on the view reference are now propagated to the shard-local table instead of being ignored.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extremes&lt;/code&gt; is not reported on single-shard clusters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you had views where &lt;code&gt;FINAL&lt;/code&gt; was silently a no-op, results will change.&lt;/p&gt;

&lt;p&gt;Set the setting to &lt;code&gt;0&lt;/code&gt; to restore the previous behaviour:&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;SET&lt;/span&gt; &lt;span class="n"&gt;optimize_trivial_view_pushdown_to_distributed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;EXPLAIN SYNTAX&lt;/code&gt; returns a single record
&lt;/h3&gt;

&lt;p&gt;The reformatted query comes back as one &lt;code&gt;String&lt;/code&gt; value with embedded newlines rather than one record per line.&lt;/p&gt;

&lt;p&gt;So:&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;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="n"&gt;SYNTAX&lt;/span&gt; &lt;span class="p"&gt;...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To restore the old behaviour:&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;SET&lt;/span&gt; &lt;span class="n"&gt;explain_syntax_single_record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other &lt;code&gt;EXPLAIN&lt;/code&gt; kinds — &lt;code&gt;PLAN&lt;/code&gt;, &lt;code&gt;PIPELINE&lt;/code&gt;, &lt;code&gt;AST&lt;/code&gt; — keep their per-line tree output.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security and Configuration
&lt;/h1&gt;

&lt;p&gt;A clear theme this release: the server no longer opens filesystem paths supplied from SQL, because it opens them with its own privileges.&lt;/p&gt;

&lt;h3&gt;
  
  
  MySQL source TLS credentials
&lt;/h3&gt;

&lt;p&gt;MySQL source TLS credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ssl_ca&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssl_cert&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssl_key&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;can no longer be given as file paths from SQL — not in &lt;code&gt;CREATE NAMED COLLECTION&lt;/code&gt;, query arguments, or &lt;code&gt;CREATE DICTIONARY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Paths remain supported in the server configuration file.&lt;/p&gt;

&lt;p&gt;Elsewhere, pass the contents via the new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ssl_ca_pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssl_cert_pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssl_key_pem&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;parameters, which are masked in logs and &lt;code&gt;SHOW&lt;/code&gt; output the way passwords are.&lt;/p&gt;




&lt;h3&gt;
  
  
  NATS credentials move inline
&lt;/h3&gt;

&lt;p&gt;The new &lt;code&gt;nats_credentials&lt;/code&gt; setting takes the same payload as a &lt;code&gt;.creds&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nats_credential_file&lt;/code&gt; is no longer accepted from SQL.&lt;/p&gt;

&lt;p&gt;It can only be set in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a named collection defined in the server configuration, or&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nats.credential_file&lt;/code&gt; in the server configuration itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A query may replace a configured path with inline &lt;code&gt;nats_credentials&lt;/code&gt; unless the operator pinned it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nats_credential_file&lt;/span&gt; &lt;span class="na"&gt;overridable=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tables created before the restriction keep working.&lt;/p&gt;




&lt;h3&gt;
  
  
  PostgreSQL database engines respect &lt;code&gt;remote_url_allow_hosts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;PostgreSQL&lt;/code&gt; and &lt;code&gt;MaterializedPostgreSQL&lt;/code&gt; database engines now honour &lt;code&gt;remote_url_allow_hosts&lt;/code&gt;, as the table engine, table function and DDL-created dictionaries already did.&lt;/p&gt;

&lt;p&gt;With it configured, &lt;code&gt;CREATE DATABASE&lt;/code&gt; and user-issued &lt;code&gt;ATTACH DATABASE&lt;/code&gt; pointing at disallowed hosts fail with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNACCEPTABLE_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Existing databases still load at startup.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;SYSTEM ... CACHE ON CLUSTER&lt;/code&gt; privilege checks are granular
&lt;/h3&gt;

&lt;p&gt;Each command now uses its own privilege rather than the &lt;code&gt;SYSTEM DROP CACHE&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;This lets a holder of a single granular cache privilege run its matching command, and prevents a holder of the group from running:&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;SYSTEM&lt;/span&gt; &lt;span class="n"&gt;SYNC&lt;/span&gt; &lt;span class="n"&gt;FILESYSTEM&lt;/span&gt; &lt;span class="k"&gt;CACHE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;CLUSTER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SYSTEM SYNC FILESYSTEM CACHE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;include_from&lt;/code&gt; no longer defaults to &lt;code&gt;/etc/metrika.xml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;That file was previously used for configuration substitutions whenever it existed, even with nothing in the ClickHouse® configuration referring to it.&lt;/p&gt;

&lt;p&gt;If you relied on it, add the element explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;include_from&amp;gt;&lt;/span&gt;/etc/metrika.xml&lt;span class="nt"&gt;&amp;lt;/include_from&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separately loaded &lt;code&gt;users.xml&lt;/code&gt; and XML dictionary configs each need their own &lt;code&gt;include_from&lt;/code&gt; element.&lt;/p&gt;




&lt;h1&gt;
  
  
  Removals
&lt;/h1&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;library&lt;/code&gt; dictionary source is gone
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SOURCE(LIBRARY(...))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now fails with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN_ELEMENT_IN_CONFIG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;dictionaries_lib_path&lt;/code&gt; server setting is obsolete with no effect.&lt;/p&gt;




&lt;h3&gt;
  
  
  Apache Arrow library-based reader and writer removed
&lt;/h3&gt;

&lt;p&gt;The Apache Arrow library-based reader and writer for &lt;code&gt;Arrow&lt;/code&gt; and &lt;code&gt;ArrowStream&lt;/code&gt; are removed.&lt;/p&gt;

&lt;p&gt;The native ClickHouse® implementation, default since 26.7, is now the only one.&lt;/p&gt;

&lt;p&gt;The following settings are still accepted but have no effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input_format_arrow_use_native_reader
output_format_arrow_use_native_writer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a query that set them to &lt;code&gt;0&lt;/code&gt; to force the Apache Arrow path now silently uses the native one.&lt;/p&gt;




&lt;h3&gt;
  
  
  Experimental &lt;code&gt;ALP(STD)&lt;/code&gt; codec changes
&lt;/h3&gt;

&lt;p&gt;The experimental &lt;code&gt;ALP(STD)&lt;/code&gt; codec now performs &lt;code&gt;Float32&lt;/code&gt; scaling arithmetic in &lt;code&gt;Float64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This improves compression ratios and eliminates exception-heavy compression of decimal data, but &lt;code&gt;Float32&lt;/code&gt; values written by earlier versions may decode 1 ULP differently.&lt;/p&gt;




&lt;h1&gt;
  
  
  Monitoring and Introspection
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Asynchronous metrics can now be &lt;code&gt;Map&lt;/code&gt;-typed
&lt;/h3&gt;

&lt;p&gt;Asynchronous metrics can now be &lt;code&gt;Map&lt;/code&gt;-typed, and the per-CPU-core and per-device metrics were consolidated.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OSUserTimeCPU0
OSUserTimeCPU1
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;became a single:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OSUserTimeCPU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;metric holding a map from core number to value.&lt;/p&gt;

&lt;p&gt;The same applies to the other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OS*TimeCPU*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CPUFrequencyMHz_*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Temperature*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EDAC*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Block*_*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Network(Receive|Send)*_*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Disk*_*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*BlobsQueueEstimate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AsyncLogging*QueueSize&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;metrics.&lt;/p&gt;

&lt;p&gt;Downstream effects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;system.asynchronous_metrics&lt;/code&gt; gains &lt;code&gt;key_values Map(LowCardinality(String), Float64)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;value&lt;/code&gt; column is &lt;code&gt;NaN&lt;/code&gt; for these metrics.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;system.asynchronous_metric_log&lt;/code&gt; logs one row per key via a new &lt;code&gt;key&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;The Prometheus endpoint exports them with a label, for example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClickHouse®AsyncMetrics_BlockReadBytes{device="sda"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Graphite's &lt;code&gt;MetricsTransmitter&lt;/code&gt; sends them as:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;prefix&amp;gt;.&amp;lt;Metric&amp;gt;.&amp;lt;key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the change most likely to break existing dashboards.&lt;/p&gt;

&lt;p&gt;Panels keyed on the old metric names will not error — they will simply return nothing, which is easy to miss.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;system.users.valid_until&lt;/code&gt; changed type
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;system.users.valid_until&lt;/code&gt; changed type from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array(DateTime)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array(DateTime64(0))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so deadlines beyond the year 2106 are represented exactly.&lt;/p&gt;

&lt;p&gt;Tooling reading this column needs to handle the new type.&lt;/p&gt;

&lt;p&gt;This arrived alongside a new:&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;VALID&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;clause on &lt;code&gt;CREATE USER&lt;/code&gt; and &lt;code&gt;ALTER USER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is a shorthand for &lt;code&gt;VALID UNTIL&lt;/code&gt;, where the deadline is computed at query execution time and stored in &lt;code&gt;VALID UNTIL&lt;/code&gt; form.&lt;/p&gt;




&lt;h1&gt;
  
  
  Default Settings That Changed in 26.8
&lt;/h1&gt;

&lt;p&gt;Around 30 core settings and several MergeTree settings changed their defaults.&lt;/p&gt;

&lt;p&gt;These do not appear in the breaking-change list but will change behaviour on upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behaviour
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Previous&lt;/th&gt;
&lt;th&gt;New&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max_insert_threads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1&lt;/code&gt; - single-threaded&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;auto&lt;/code&gt; - all available cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;input_format_read_datetime_number_as_raw_value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optimize_trivial_view_pushdown_to_distributed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;explain_syntax_single_record&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filesystem_cache_wait_for_concurrent_download_timeout_milliseconds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;60000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Previous&lt;/th&gt;
&lt;th&gt;New&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ai_function_allow_insecure_endpoint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ai_function_max_api_calls_per_query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (unbounded)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Performance (Enabled by Default)
&lt;/h2&gt;

&lt;p&gt;The following performance improvements are now enabled by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;enable_adaptive_aggregator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enable_group_by_top_k_optimization&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enable_packed_string_keys_in_aggregation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enable_parallel_single_level_merge&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read_in_order_use_virtual_row&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query_plan_push_down_volume_reducing_functions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query_plan_short_circuit_constant_false_join&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;use_query_condition_cache_for_top_k&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;allow_distinct_partitions_independently&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;allow_window_partitions_independently&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;allow_creating_set_partitions_independently&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;optimize_trivial_count_with_sparsity_filter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;input_format_parquet_spatial_filter_push_down&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query_plan_optimize_count_from_text_index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;materialize_statistics_on_insert&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last setting has a 25 GiB table-size cap.&lt;/p&gt;




&lt;h1&gt;
  
  
  MergeTree - On-Disk Formats
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Previous&lt;/th&gt;
&lt;th&gt;New&lt;/th&gt;
&lt;th&gt;Compatibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text_index_serialization_version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v1_with_codec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v2_with_positions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Older servers cannot read the new format. Pin to v1 during a rolling upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packed_skip_index_max_bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1 MiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New parts only; still readable by older servers, though pre-26.6 ignores packed indices for pruning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compute_exact_num_defaults_for_sparse_columns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The flag in &lt;code&gt;serialization.json&lt;/code&gt; is ignored by older versions, so parts survive downgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text_index_posting_list_apply_mode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;materialize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lazy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Posting lists decoded on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text_index_max_memory_usage_before_flush&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1 GiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Memory-based flush trigger for index builders&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One protocol-level note: the native protocol changed how &lt;code&gt;String&lt;/code&gt; columns are transmitted — a separate stream of cumulative byte offsets followed by concatenated data — once both peers are on revision 54489 or later.&lt;/p&gt;

&lt;p&gt;This is around 4x faster for client-side reads.&lt;/p&gt;

&lt;p&gt;It is negotiated by protocol revision, so old clients and servers are unaffected, but maintainers of custom native-protocol clients should handle the new revision.&lt;/p&gt;




&lt;h1&gt;
  
  
  What's New
&lt;/h1&gt;

&lt;h2&gt;
  
  
  SQL Surface
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pipe operators
&lt;/h3&gt;

&lt;p&gt;GoogleSQL-style &lt;code&gt;|&amp;gt;&lt;/code&gt; chaining is now supported.&lt;/p&gt;

&lt;p&gt;Each pipe wraps the preceding query in a subquery, so the resulting AST matches the nested equivalent.&lt;/p&gt;

&lt;p&gt;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;FROM&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;AGGREGATE&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a query starting with &lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt; is now optional and defaults to:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;GROUPS&lt;/code&gt; window frame mode
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GROUPS&lt;/code&gt; is a SQL:2011 window frame mode.&lt;/p&gt;

&lt;p&gt;Frame boundaries count whole peer groups rather than physical rows or value distances, completing the set alongside &lt;code&gt;ROWS&lt;/code&gt; and &lt;code&gt;RANGE&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Query AST as JSON
&lt;/h3&gt;

&lt;p&gt;New functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parseQueryToJSON
formatQueryFromJSON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An experimental &lt;code&gt;ClickHouse®_json&lt;/code&gt; dialect is also available behind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enable_json_ast_dialect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;arr[indexes]&lt;/code&gt; for subscripting an array with an array of positions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;notHas&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mergedJSONPatch&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Server and Operations
&lt;/h1&gt;

&lt;h3&gt;
  
  
  SQL-defined HTTP handlers
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CREATE HANDLER&lt;/code&gt;, &lt;code&gt;ALTER HANDLER&lt;/code&gt; and &lt;code&gt;DROP HANDLER&lt;/code&gt; define custom HTTP endpoints from SQL.&lt;/p&gt;

&lt;p&gt;Handlers can be persisted locally or in Keeper.&lt;/p&gt;

&lt;p&gt;Supporting additions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;currentHandler()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;currentRequestURL()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;system.handlers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http_handler_name&lt;/code&gt; in &lt;code&gt;system.query_log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http_request_url&lt;/code&gt; in &lt;code&gt;system.query_log&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams maintaining small API services that exist only to expose a query over HTTP, this is worth evaluating.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;run_query_in_background&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The server accepts the query, returns immediately, and runs it to completion regardless of what happens to the connection.&lt;/p&gt;

&lt;p&gt;Track it by &lt;code&gt;query_id&lt;/code&gt; in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;system.processes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;system.query_log&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is aimed at long-running operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INSERT SELECT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CREATE TABLE AS SELECT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POPULATE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Atomic &lt;code&gt;POPULATE&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A plain:&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;CREATE&lt;/span&gt; &lt;span class="n"&gt;MATERIALIZED&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;POPULATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is now locally atomic, controlled by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;materialized_views_populate_atomically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and enabled by default.&lt;/p&gt;

&lt;p&gt;Rows inserted through the same server during population are no longer missed or duplicated.&lt;/p&gt;

&lt;p&gt;This applies to the local insert path only and requires a snapshot-capable source — the MergeTree family or Memory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CREATE OR REPLACE&lt;/code&gt;, &lt;code&gt;REPLACE&lt;/code&gt;, and &lt;code&gt;Replicated&lt;/code&gt; databases keep the legacy path.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POPULATE&lt;/code&gt; also works with &lt;code&gt;TO&lt;/code&gt; now.&lt;/p&gt;




&lt;h3&gt;
  
  
  Introspection port
&lt;/h3&gt;

&lt;p&gt;A native-protocol TCP listener starts before tables attach and stops after detach completes.&lt;/p&gt;

&lt;p&gt;This makes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOW PROCESSLIST
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system.stack_trace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;available during startup and shutdown.&lt;/p&gt;

&lt;p&gt;Alongside it, &lt;code&gt;shutdown_wait_unfinished&lt;/code&gt; moved from 5 seconds to 120 seconds.&lt;/p&gt;

&lt;p&gt;The old default was shorter than the connection poll interval.&lt;/p&gt;




&lt;h3&gt;
  
  
  Keeper on-disk storage
&lt;/h3&gt;

&lt;p&gt;Keeper now supports on-disk storage via a custom LSM tree.&lt;/p&gt;

&lt;p&gt;Enable with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use_lsmt_storage = true
storage_memory_only = false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data_storage_path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data_storage_disk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can point at an &lt;code&gt;s3_plain&lt;/code&gt; disk for S3.&lt;/p&gt;

&lt;p&gt;The Keeper dashboard also gains a Cluster tab showing Raft membership as a topology graph.&lt;/p&gt;




&lt;h3&gt;
  
  
  HTTP URL-path access to tables
&lt;/h3&gt;

&lt;p&gt;Tables can now be accessed through HTTP URL paths such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/database/table.format.gz?filter=a&amp;gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is behind a set of &lt;code&gt;http_allow_*&lt;/code&gt; opt-ins.&lt;/p&gt;




&lt;h3&gt;
  
  
  Framing output formats
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;framing_output_format&lt;/code&gt; multiplexes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data chunks&lt;/li&gt;
&lt;li&gt;totals and extremes&lt;/li&gt;
&lt;li&gt;progress&lt;/li&gt;
&lt;li&gt;profile events&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;exceptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;into a single HTTP stream.&lt;/p&gt;

&lt;p&gt;Available formats include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EventStream
JSONEachPacketBase64
JSONEachPacketString
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Also new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;default_session_user&lt;/code&gt; as a server setting&lt;/li&gt;
&lt;li&gt;Prometheus constant labels via a &lt;code&gt;&amp;lt;labels&amp;gt;&lt;/code&gt; element inside &lt;code&gt;&amp;lt;prometheus&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALTER TABLE ... MODIFY PROJECTION&lt;/code&gt; to change projection settings without a rebuild&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Projection changes are applied lazily via merges.&lt;/p&gt;




&lt;h1&gt;
  
  
  Observability
&lt;/h1&gt;

&lt;p&gt;Several useful observability improvements landed in 26.8:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;system.user_query_log&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Every user sees their own query log rows without needing access to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system.query_log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;create_union_system_log_tables&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This creates auto-maintained &lt;code&gt;all_...&lt;/code&gt; tables such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system.all_query_log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These union:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a log table&lt;/li&gt;
&lt;li&gt;its rotated versions&lt;/li&gt;
&lt;li&gt;the same table across cluster replicas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;remoteSecure&lt;/code&gt;, &lt;code&gt;cluster&lt;/code&gt; and &lt;code&gt;clusterAllReplicas&lt;/code&gt; now accept a trailing &lt;code&gt;SETTINGS&lt;/code&gt; clause.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;system.mutations.finish_time&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Provides mutation duration without having to infer it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;system.tables.skipping_indices_types&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Provides a cheap summary of which index types a table uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Play UI
&lt;/h3&gt;

&lt;p&gt;The Play UI now supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;server-side sorting&lt;/li&gt;
&lt;li&gt;filtering&lt;/li&gt;
&lt;li&gt;paging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These settings are encoded in the page URL so a shared link reproduces the result.&lt;/p&gt;




&lt;h1&gt;
  
  
  Joins and Text Search
&lt;/h1&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;IEJoin&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;IEJoin&lt;/code&gt; is a sort-based algorithm for &lt;code&gt;ON&lt;/code&gt; clauses containing two inequality comparisons.&lt;/p&gt;

&lt;p&gt;Previously such joins ran as a CROSS JOIN with a filter, and only as INNER.&lt;/p&gt;

&lt;p&gt;Enable by adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ie_join
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;join_algorithm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;parallel_full_sorting_merge&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;parallel_full_sorting_merge&lt;/code&gt; shards a full sorting merge join by join-key hash across threads.&lt;/p&gt;

&lt;p&gt;Upstream benchmarks put it around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2.4x faster&lt;/li&gt;
&lt;li&gt;3.3x lighter on memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;than &lt;code&gt;parallel_hash&lt;/code&gt;, while keeping streaming memory behaviour.&lt;/p&gt;

&lt;p&gt;The result is unordered.&lt;/p&gt;




&lt;h3&gt;
  
  
  New text index tokenizers
&lt;/h3&gt;

&lt;p&gt;New tokenizers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;japanese&lt;/code&gt; — MeCab&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chinese&lt;/code&gt; — jieba-style dictionary plus HMM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;icu&lt;/code&gt; — locale-aware Unicode segmentation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;splitByRegexp&lt;/code&gt; — keeps tokens such as &lt;code&gt;C++&lt;/code&gt; and &lt;code&gt;C#&lt;/code&gt; intact&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Data Lakes
&lt;/h1&gt;

&lt;p&gt;A number of data-lake integrations have been expanded.&lt;/p&gt;

&lt;h3&gt;
  
  
  BigQuery
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;bigquery&lt;/code&gt; table function and &lt;code&gt;BigQuery&lt;/code&gt; table engine are now available.&lt;/p&gt;

&lt;p&gt;Authentication supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OAuth token&lt;/li&gt;
&lt;li&gt;service account JSON&lt;/li&gt;
&lt;li&gt;refresh token&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Snowflake Horizon
&lt;/h3&gt;

&lt;p&gt;Snowflake Horizon catalog support is available for reading and writing Iceberg.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 Tables
&lt;/h3&gt;

&lt;p&gt;The S3 Tables catalog now supports:&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;INSERT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Puffin
&lt;/h3&gt;

&lt;p&gt;Puffin file format support has been added.&lt;/p&gt;

&lt;h3&gt;
  
  
  URL database engine
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;URL&lt;/code&gt; database engine and &lt;code&gt;s3_base&lt;/code&gt; setting complete the URL unification.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ClickHouse®-local&lt;/code&gt;'s default database now uses it with a &lt;code&gt;file://&lt;/code&gt; base.&lt;/p&gt;

&lt;p&gt;This means:&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="s1"&gt;'https://example.com/data.csv'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;works directly.&lt;/p&gt;




&lt;h1&gt;
  
  
  AI Functions
&lt;/h1&gt;

&lt;p&gt;The experimental:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aiSimilarity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aiFilter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aiRedact&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;functions were hardened in this release.&lt;/p&gt;

&lt;p&gt;Insecure &lt;code&gt;http&lt;/code&gt; endpoints to remote hosts are denied by default.&lt;/p&gt;

&lt;p&gt;Outbound calls per query are bounded at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provider error responses are also sanitised before logging.&lt;/p&gt;




&lt;h1&gt;
  
  
  Performance
&lt;/h1&gt;

&lt;p&gt;ClickHouse® 26.8 contains &lt;strong&gt;127 performance entries&lt;/strong&gt;, with the majority enabled by default.&lt;/p&gt;

&lt;p&gt;The improvements with the broadest reach include:&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregation
&lt;/h2&gt;

&lt;p&gt;A new adaptive parallel &lt;code&gt;GROUP BY&lt;/code&gt; allows each thread to aggregate into its own cache-resident hash table until it hits a threshold, then freezes it.&lt;/p&gt;

&lt;p&gt;Other aggregation improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bounded-heap pruning for &lt;code&gt;GROUP BY ... ORDER BY ... LIMIT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;smaller hash-table cells for single-&lt;code&gt;String&lt;/code&gt; keys&lt;/li&gt;
&lt;li&gt;parallelised final merge of single-level tables&lt;/li&gt;
&lt;li&gt;aggregations without aggregate functions now use &lt;code&gt;HashSet&lt;/code&gt; rather than &lt;code&gt;HashMap&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter can be up to 1.8x faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reads
&lt;/h2&gt;

&lt;p&gt;Lazy materialization for Parquet on object storage can significantly reduce I/O.&lt;/p&gt;

&lt;p&gt;For example, on a 200 MB S3 file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER BY ... LIMIT 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3.3 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;171 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That represents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;51x less I/O&lt;/li&gt;
&lt;li&gt;8x faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;read_in_order_use_virtual_row&lt;/code&gt; is enabled by default and reduces peak memory when reading in primary key order across many parts.&lt;/p&gt;

&lt;p&gt;The Parquet V3 reader also gains dictionary-page-based row group skipping.&lt;/p&gt;




&lt;h2&gt;
  
  
  Per-Partition Processing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;DISTINCT&lt;/code&gt;, window functions and &lt;code&gt;IN (subquery)&lt;/code&gt; set building can now keep each partition's rows in a single stream when the partition expression is a deterministic function of the relevant columns.&lt;/p&gt;

&lt;p&gt;This skips the hash scatter entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Important Trade-Off
&lt;/h2&gt;

&lt;p&gt;The trade-off worth stating plainly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query plans and resource usage will change after upgrading, even for queries you did not touch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most workloads benefit, but plan for a period of observation rather than assuming parity.&lt;/p&gt;




&lt;h1&gt;
  
  
  Upgrade Checklist
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Before Upgrading
&lt;/h2&gt;

&lt;p&gt;Check for dictionaries using the removed library source:&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="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dictionaries&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="s1"&gt;'%library%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check non-default settings you are carrying:&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server_settings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check by hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named collections and dictionaries using MySQL &lt;code&gt;ssl_ca&lt;/code&gt; / &lt;code&gt;ssl_cert&lt;/code&gt; / &lt;code&gt;ssl_key&lt;/code&gt; paths&lt;/li&gt;
&lt;li&gt;NATS tables or collections using &lt;code&gt;nats_credential_file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Reliance on the implicit &lt;code&gt;/etc/metrika.xml&lt;/code&gt; substitutions file&lt;/li&gt;
&lt;li&gt;PostgreSQL / MaterializedPostgreSQL databases against hosts outside &lt;code&gt;remote_url_allow_hosts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ingestion clients writing unquoted epoch numbers into &lt;code&gt;DateTime64&lt;/code&gt; columns via &lt;code&gt;JSONEachRow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Queries using &lt;code&gt;toDate32&lt;/code&gt; on epoch-second values&lt;/li&gt;
&lt;li&gt;Anything parsing &lt;code&gt;EXPLAIN SYNTAX&lt;/code&gt; output&lt;/li&gt;
&lt;li&gt;Tooling reading &lt;code&gt;system.users.valid_until&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dashboards keyed on individual per-CPU or per-device asynchronous metric names&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  During a Rolling Upgrade
&lt;/h2&gt;

&lt;p&gt;Pin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;patch_parts_version = 'v1'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text_index_serialization_version = 'v1_with_codec'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;until every replica is on 26.8.&lt;/p&gt;

&lt;p&gt;Then remove both pins.&lt;/p&gt;




&lt;h2&gt;
  
  
  After Upgrading
&lt;/h2&gt;

&lt;p&gt;Watch part counts and merge queue depth in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system.parts
system.merges
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for the &lt;code&gt;max_insert_threads&lt;/code&gt; effect.&lt;/p&gt;

&lt;p&gt;Also monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system.errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNACCEPTABLE_URL
ILLEGAL_COLUMN
UNKNOWN_ELEMENT_IN_CONFIG
BAD_ARGUMENTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These cover most of the new rejections.&lt;/p&gt;

&lt;p&gt;If you want to stage the transition:&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;SET&lt;/span&gt; &lt;span class="n"&gt;compatibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'26.7'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This restores the majority of the default flips in one move, letting you upgrade the binaries first and enable the new behaviour deliberately afterwards.&lt;/p&gt;

&lt;p&gt;It does not cover removals or type changes — those need code fixes, which the checks above should surface.&lt;/p&gt;




&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;The three changes most likely to affect a running deployment are:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;max_insert_threads&lt;/code&gt; defaulting to &lt;code&gt;auto&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This changes part creation patterns and insert row order on every:&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;INSERT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Asynchronous metric consolidation
&lt;/h3&gt;

&lt;p&gt;This can break dashboards silently rather than loudly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Patch parts v2 format
&lt;/h3&gt;

&lt;p&gt;This requires a rolling-upgrade pin on replicated clusters.&lt;/p&gt;

&lt;p&gt;Beyond that, 26.8 is a strong release.&lt;/p&gt;

&lt;p&gt;The security tightening around credential handling is overdue and welcome.&lt;/p&gt;

&lt;p&gt;The operational additions — SQL-defined handlers, background queries, atomic &lt;code&gt;POPULATE&lt;/code&gt;, and the introspection port — address real gaps.&lt;/p&gt;

&lt;p&gt;And the &lt;strong&gt;127 performance improvements&lt;/strong&gt;, mostly enabled by default, represent a meaningful return on the upgrade work.&lt;/p&gt;

&lt;p&gt;The key lesson is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat ClickHouse® 26.8 as a real upgrade project, not just a version bump.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Review the defaults, test workload behaviour, validate observability, and plan the rollout carefully.&lt;/p&gt;

&lt;p&gt;Read more... &lt;a href="https://www.ch-ops.io/blog/clickhouse-268-lts-release-whats-new-and-why-it-matters" rel="noopener noreferrer"&gt;https://www.ch-ops.io/blog/clickhouse-268-lts-release-whats-new-and-why-it-matters&lt;/a&gt;&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>analytics</category>
      <category>database</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
