Against an AKS based SQL Server 2019 BDC, I loaded the Flight_delay dataset that is available at www.kaggle.com. I wanted to test the performance of the various data stores, ie, master instance, data pool, HDFS-storage pool and ADLS-storage pool (via HDFS Tiering).
Against the pools - data pool, HDFS-storage pool and ADLS-storage pool - I created External tables to access as shown in the script below.
Select Count(*)
From [dbo].[Master_Flights] F
Inner join [dbo].[Master_Airports] A on F.ORIGIN_AIRPORT = A.IATA_CODE
Inner join [dbo].[Master_Airlines] L on F.AIRLINE = L.IATA_CODE
GO
Select Count(*)
From [dbo].[DataPool_Flights] F
Inner join [dbo].[DataPool_Airports] A on F.ORIGIN_AIRPORT = A.IATA_CODE
Inner join [dbo].[DataPool_Airlines] L on F.AIRLINE = L.IATA_CODE
GO
Select Count(*)
From [dbo].[HDFS_StoragePool_Flights] F
Inner join [dbo].[HDFS_StoragePool_Airports] A on F.ORIGIN_AIRPORT = A.IATA_CODE
Inner join [dbo].[HDFS_StoragePool_Airlines] L on F.AIRLINE = L.IATA_CODE
GO
Select Count(*)
From [dbo].[adls_StoragePool_Flights] F
Inner join [dbo].[adls_StoragePool_Airports] A on F.ORIGIN_AIRPORT = A.IATA_CODE
Inner join [dbo].[adls_StoragePool_Airlines] L on F.AIRLINE = L.IATA_CODE
GO
The performance from "best to worst":
Master Instance - 2 seconds
Data Pool - 16 seconds
HDFS-Storage Pool - 90 seconds
ADLS-Storage Pool - 220 seconds
Are my test results on expected lines? I was hoping to get pretty good performance results for the data pool option at least.
If the performance is this bad for the tables in the pools why would anyone want to use them?
Thanks,
grajee
let's ignore the ADLS for now - there is too much other stuff (network latency between regions etc.) involved.
This might be an indexing or IO issue.
How big is you AKS cluster (machine family and node count)?
How do the numbers compare when running the count JUST against the fact table (flights) without joining?
How does the HDFS perform when you convert the CSV to parquet?
Ben
Related
I've been trying to optimize this simple query on Postgres 12 that joins several tables to a base relation. They each have 1-to-1 relation and have anywhere from 10 thousand to 10 million rowss.
SELECT *
FROM base
LEFT JOIN t1 ON t1.id = base.t1_id
LEFT JOIN t2 ON t2.id = base.t2_id
LEFT JOIN t3 ON t3.id = base.t3_id
LEFT JOIN t4 ON t4.id = base.t4_id
LEFT JOIN t5 ON t5.id = base.t5_id
LEFT JOIN t6 ON t6.id = base.t6_id
LEFT JOIN t7 ON t7.id = base.t7_id
LEFT JOIN t8 ON t8.id = base.t8_id
LEFT JOIN t9 ON t9.id = base.t9_id
(the actual relations are a bit more complicated than this, but for demonstration purposes this is fine)
I noticed that the query is still very slow when I only do SELECT base.id which seems odd, because then query planner should know that the joins are unnecessary and shouldn't affect the performance.
Then I noticed that 8 seems to be some kind of magic number. If I remove any single one of the joins, the query time goes from 500ms to 1ms. With EXPLAIN I was able to see that Postgres is doing index only scans when joining 8 tables, but with 9 tables it starts doing sequential scans.
That's even when I only do SELECT base.id so somehow the amount of tables is tripping up the query planner.
We finally found out that there is indeed a configuration setting in postgres called join_collapse_limit, which is set to 8 by default.
https://www.postgresql.org/docs/current/runtime-config-query.html
The planner will rewrite explicit JOIN constructs (except FULL JOINs) into lists of FROM items whenever a list of no more than this many items would result. Smaller values reduce planning time but might yield inferior query plans. By default, this variable is set the same as from_collapse_limit, which is appropriate for most uses. Setting it to 1 prevents any reordering of explicit JOINs. Thus, the explicit join order specified in the query will be the actual order in which the relations are joined. Because the query planner does not always choose the optimal join order, advanced users can elect to temporarily set this variable to 1, and then specify the join order they desire explicitly.
After reading this article we decided to increase the limit, along with other values such as from_collapse_limit and geco_threshold. Beware that query planning time increases exponentially with the amount of joins, so the limit is there for a reason and should not be increased carelessly.
I use macOS and I am having issue with full-refresh on a large table. During the run it appears as if it hangs and there is no query running in redshift. It does not happen with smaller tables and it does not happen if I run an incremental. This table used to be smaller and I was able to run a full refresh as long as I specified the table. Now that it is bigger I seem to be running into this issue. There are 6 tables that this model is dependent on. Almost like the command isn’t being sent. Any suggestions?
There is no error because it just doesn't run. Other team members running this on windows and macos expect it to finish in 10 min. Currently it is 30 min but I have let it sit a lot longer than that.
My command is
dbt run --models +fct_mymodel --full-refresh --vars "run_date_start: 2020-06-01"
Thank you
Redhift UI usually shows only the long running queries. I ran into similar problems and they were caused by lock on some tables - in our case caused by uncommitted explicit transactions (BEGIN without COMMIT or ROLLBACK).
run this query to see current transactions and their locks:
select a.txn_owner, a.txn_db, a.xid, a.pid, a.txn_start, a.lock_mode, a.relation as table_id,nvl(trim(c."name"),d.relname) as tablename, a.granted,b.pid as blocking_pid ,datediff(s,a.txn_start,getdate())/86400||' days '||datediff(s,a.txn_start,getdate())%86400/3600||' hrs '||datediff(s,a.txn_start,getdate())%3600/60||' mins '||datediff(s,a.txn_start,getdate())%60||' secs' as txn_duration
from svv_transactions a
left join (select pid,relation,granted from pg_locks group by 1,2,3) b
on a.relation=b.relation and a.granted='f' and b.granted='t'
left join (select * from stv_tbl_perm where slice=0) c
on a.relation=c.id
left join pg_class d on a.relation=d.oid
where a.relation is not null;
read AWS knowledge base entry for more details https://aws.amazon.com/premiumsupport/knowledge-center/prevent-locks-blocking-queries-redshift/
I have a query which runs via cursors and it is slow because it monitors lots of fields.
I would like to run this query automatically every week. it takes around 2 minutes. so do you think a job scheduling? or a query to send an email will do? or do you have any other options??? Thank you!!!
In the below query it checks only for L_name but I have 50 more fields like that.
SELECT
a.invnumber, a.Accnum,
i.audit_field, i.field_after, name, i.maxdate AS Modified_date
FROM
#Iam a
JOIN
(SELECT
a.invnumber, a.Accnum, a.field_after, audit_field, maxdate
FROM
#Iam_audit a WITH(nolock)
INNER JOIN
(SELECT
Accnum, invnumber, MAX(Modified_Date) AS maxdate
FROM
#Iam_audit a2 WITH(nolock)
WHERE
a2.Audit_field = 'name'
GROUP BY
Accnum, invnumber) AS aa ON aa.Accnum = a.Accnum
AND aa.invnumber = a.invnumber
AND aa.maxdate = a.modified_Date
WHERE
a.Audit_Field = 'name') i ON i.audit_field = 'name'
AND i.Accnum = a.Accnum
AND i.invnumber = a.invnumber
AND a.name <> i.field_after
If you are using SQL Server, then scheduling a job using the SQL Agent would definitely be the way to go.
As stated in the comments, 2 minutes is not a very long time for the query. Simply schedule the job for a time when the system is not under heavy use.
But if you want to move to the more modern implementation of SQL I would look to implement set-based data operations rather than cursors. This may or may not improve the query timings, but is something I would definitely look at doing for a production server.
Cursors are generally looked down upon for production operations, although, you may be able to justify them in some cases.
I am doing some tests to quantify the performance/reliability of the Change Tracking feature of SQL Server. I have a single table t1 in which I insert 1 million rows, once with Change Tracking OFF and once with Change Tracking On. I am monitoring the sizes of syscommittab, the size of the change tracking table and the I/Os recorded against the database. As is to be expected, the change tracking table and syscommitab only get populated when Change Tracking is ON. And I expect the IOs recorded against the database to be "proportional" to the sizes of these tables. But to my surprise, these are way off. The IOs recorded against the database are many orders more than the sizes of these 2 tables. Wondering if anyone knows why or can give me pointers to figuring it out. I am using sys.dm_io_virtual_file_stats() to determine the IO activity on the database and the following query to determine the sizes of the tracked table and syscommittab.
SELECT sct1.name as CT_schema,
sot1.name as CT_table,
ps1.row_count as CT_rows,
ps1.reserved_page_count*8./1024. as CT_reserved_MB,
sct2.name as tracked_schema,
sot2.name as tracked_name,
ps2.row_count as tracked_rows,
ps2.reserved_page_count*8./1024. as tracked_base_table_MB,
change_tracking_min_valid_version(sot2.object_id) as min_valid_version
FROM sys.internal_tables it
JOIN sys.objects sot1 on it.object_id=sot1.object_id
JOIN sys.schemas AS sct1 on
sot1.schema_id=sct1.schema_id
JOIN sys.dm_db_partition_stats ps1 on
it.object_id = ps1. object_id
and ps1.index_id in (0,1)
LEFT JOIN sys.objects sot2 on it.parent_object_id=sot2.object_id
LEFT JOIN sys.schemas AS sct2 on
sot2.schema_id=sct2.schema_id
LEFT JOIN sys.dm_db_partition_stats ps2 on
sot2.object_id = ps2. object_id
and ps2.index_id in (0,1)
WHERE it.internal_type IN (209, 210)
and (sot2.name='t1' or sot1.name='syscommittab')
I am checkpointing before running the queries.
Any tip or pointer appreciated.
Acknowledgements to https://www.brentozar.com/archive/2014/06/performance-tuning-sql-server-change-tracking/ for the SQL above.
I have a DB table with 25M rows, ~3K each (i.e. ~75GB), that together with multiple indexes I use (an additional 15-20GB) will not fit entirely in memory (64GB on machine). A typical query locates 300 rows thru an index, optionally filters them down to ~50-300 rows using other indexes, finally fetching the matching rows. Response times vary between 20ms on a warm DB to 20 secs on a cold DB. I have two related questions:
At any given time how can I check what portion (%) of specific tables and indexes is cached in memory?
What is the best way to warm up the cache before opening the DB to queries? E.g. "select *" forces a sequential scan (~15 minutes on cold DB) but response times following it are still poor. Is there a built-in way to do this instead of via queries?a
Thanks, feel free to also reply by email (info#shauldar.com])
-- Shaul
Regarding your first point, the contrib module "pg_buffercache" allows you to inspect the contents of the buffer cache. I like to define this:
create or replace view util.buffercache_hogs as
select case
when pg_buffercache.reldatabase = 0
then '- global'
when pg_buffercache.reldatabase <> (select pg_database.oid from pg_database where pg_database.datname = current_database())
then '- database ' || quote_literal(pg_database.datname)
when pg_namespace.nspname = 'pg_catalog'
then '- system catalogues'
when pg_class.oid is null and pg_buffercache.relfilenode > 0
then '- unknown file ' || pg_buffercache.relfilenode
when pg_namespace.nspname = 'pg_toast' and pg_class.relname ~ '^pg_toast_[0-9]+$'
then (substring(pg_class.relname, 10)::oid)::regclass || ' TOAST'::text
when pg_namespace.nspname = 'pg_toast' and pg_class.relname ~ '^pg_toast_[0-9]+_index$'
then ((rtrim(substring(pg_class.relname, 10), '_index'))::oid)::regclass || ' TOAST index'
else pg_class.oid::regclass::text
end as key,
count(*) as buffers, sum(case when pg_buffercache.isdirty then 1 else 0 end) as dirty_buffers,
round(count(*) / (SELECT pg_settings.setting FROM pg_settings WHERE pg_settings.name = 'shared_buffers')::numeric, 4) as hog_factor
from pg_buffercache
left join pg_database on pg_database.oid = pg_buffercache.reldatabase
left join pg_class on pg_class.relfilenode = pg_buffercache.relfilenode
left join pg_namespace on pg_namespace.oid = pg_class.relnamespace
group by 1
order by 2 desc;
Additionally, the "pageinspect" contrib module allows you to access a specific page from a relation, so I suppose you could simply loop through all the pages in a relation grabbing them?
select count(get_raw_page('information_schema.sql_features', n))
from generate_series(0,
(select relpages-1 from pg_class where relname = 'sql_features')) n;
This will load all of information_schema.sql_features into the cache.
2) I usually solve this by having a log of queries from a live system and replaying them. This warms up the typical parts of the data and not the parts that aren't as frequently used (which would otherwise waste RAM).
Ad. 1 - I have absolutely no idead.
Ad. 2 - why don't you just choose randomly some queries that you know that are important, and run them on cold server? the more the queries you'll run, the better will be the warmup process.
Don't try to warm up memory, that's the postgresql and OS work. Just divide tables (and indexes) in partitions and try to work with smaller data sets. If you accomplish to establish a good partitioning plan then there is no problem with huge indexes or tables. If you still want to warm up tables and indexes then maybe can be cached completly in RAM because are smaller than before.