Ways to shoot yourself in the foot with Postgres
philbooth.me
philbooth.me
- Configure Vacuum and maintenance_work_mem regularly if your DB size increases, if you allocate too much or too often it can clog up your memory.
- If you plan on deleting more than a 10000 rows regularly, maybe you should look at partition, it's surprisingly very slow to delete that "much" data. And even more with foreign key.
- Index on Boolean is useless, it's an easy mistake that will take memory and space disk for nothing.
- Broad indices are easier to maintain but if you can have multiple smaller indices with WHERE condition it will be much faster
- You can speed up, by a huge margin, big string indices with md5/hash index (only relevant for exact match)
- Postgres as a queue is definitely working and scales pretty far
- Related: be sure to understand the difference between transaction vs explicit locking, a lot of people assume too much from transaction and it will eventually breaks in prod.
However if the field is highly biased (e.g. 90 or 99% one value) it can be useful to create a partial index on the rarer value. Though even better is to create a partial index on the other stuff filtered by that value, especially if the smaller set is the commonly queried one (e.g. soft-deletes).
I was referring of indexing the column without distinction, the last time I checked (years ago) Postgres didn't do any statistical distribution so the query planner was always discarding the index anyway.
A complete index over a column will have entries for all records, and can be used on "x is null" and "x is not null" filters.
Why? Is it because an index on the bool alone, with symmetric distribution, will still leave you with half the table to scan? In other words, does that statement apply to biased distribution (as mentioned by another response) or indices on multiple fields of which one is a boolean?
But then, given the number of things that very rarely happen in a database, you are prone to have 1 or 2 of them happening every time you do something. Just not that specific thing; but if you keep all of those rules in mind, you will always be surprised.
I’ve seen this advice elsewhere as well, but recently tried it and found it wasn’t the case on my data set. I have about 5m rows, with an extremely heavy bias on one column being ‘false’. Adding a plain index on this column cut query time in about half. We’re in the millisecond ranges here, but still.
CREATE INDEX ON session(is_active) WHERE is_active;CREATE INDEX ON session(id) WHERE is_active;
If so, you can’t, in general, efficiently find the false rows if you know which rows have true or vice versa.
You also can only use an index on rows with true values to efficiently find those with other values if the index can return the true rows in order (so that you can use the logic “there’s a gap in the index ⇒ there are non-true values in that gap)
I recently went from:
* somewhat understanding the concept of transactions and combining that with a bunch of manual locking to ensure data integrity in our web-app;
to: * realizing how powerful modern Postgres actually is and delegating integrity concerns to it via the right configs (e.g., applying "serializable" isolation level), and removing the manual locks.
So I'm curious what situations are there that should make me reconsider controlling locks manually instead of blindly trusting Postgres capabilities. Any transaction which is run at a transaction isolation level other than SERIALIZABLE will not be affected by SSI. If you want to enforce business rules through SSI, all transactions should be run at the SERIALIZABLE transaction isolation level, and that should probably be set as the default.
Given that running everything at SERIALIZABLE probably isn’t practical for you, I think it’s more clear code wise to use explicit locks. That way, you can grep for what queries are related synchronization wise, vs. SERIALIZABLE being implicit.Explicit locks can mean just calling LOCK TABLE account_balances IN SHARE ROW EXCLUSIVE MODE; early in the transaction and then doing SELECT ... FOR UPDATE; or similar configurations to enforce business rules where it matters.
If you manually SELECT FOR UPDATE SKIP LOCKED LIMIT 1, then the second process will be forced to select the next task without waiting for the lock.
Do you mean a https://www.postgresql.org/docs/current/indexes-types.html#I... index? It's a 32-bit hash (but which hash is it, is it CRC32?). How to do a MD5 index?
Anyway, MD5 is slow, does Postgres offer fast hashes like SipHash (DoS resistant) or FNV (not DoS resistant)?
What I did to not maintain a second column is to use the function directly in the index:
``` CREATE UNIQUE INDEX CONCURRENTLY "groupid_md5_uniq" ON "crawls" ("group_id", md5("url")); ```
``` SELECT * FROM crawls WHERE group_id= $0 AND md5(url) = md5($1) ```
This simple trick, that did not required an extensive refactor, speed up the query time by a factor of thousand.
Dumb question: what's the use case for having a md5/hash field in your database?
For equality comparisons of large types it's quite beneficial
In my case, I had to index big tables with URLs, with no need for partial match. I did it naively but did help a lot.
You mean with triggers and listen/notify ?
But I find that listen/notify seem to be drastically underused.
I’ve probably worked with hundreds of people now who use a database daily either in code or just to explore data and can count on two hands (optimistically…) the number of folks who actually read the fine manual in any other way than googling something specific. Pro tip: read it so you know what to google for!
One time I pointed out that, rather than reordering the tables to make loading work with foreign key constraints, we could pause trigger execution, load all the data, and then resume the triggers. pg_dump can even do it for you if you pick the options right (might be default), in addition to natively ordering the data correctly, but if you're messing around with large SQL files anyway, it's helpful..
*put perform = outperform
He wasn't the first to observe the general principle, it's at least as old as the Bible[0].
I just enter my vague question into this AI chat thingy and I try the first thing that it tells me on my production server.
Has worked fine for me so far. What could possibly go wrong?
"Let me just install this 2MB js dependency real quick. I don't know anything about its author nor if its maintained at all, but it will prevent me from writing 10 lines of vainilla js."
ChatGPT is a vast improvement from this.
Here we have an agent integrated into langchain that executes the command directly on the server. If there are any errors it uses ai to debug and fix them too.
!/s see https://python.langchain.com/en/latest/modules/agents/toolki...
CREATE EXTENSION pggpt;
and you're done. The AI watches your db and adjusts the params on the fly as needed. We're joking, but I give it less than 12 months before there'll be something like this.
Project based learning is best for sql and excel formulas. Start at the start and it builds up quickly.
Don’t worry it’s way less work than trying to make a nosql database into a sql database.
seq_page_cost = 1.0
random_page_cost = 4.0
Which is fine if you are using spinning disks to store your data. It makes postgresql prefer sequential scans over index usageI think it's time the default were changed to suit SSDs, where a random page cost is the same as a sequential one.
seq_page_cost = 1.0
random_page_cost = 1.0You may want to set random page costs higher than 1.0, in part because DB/FS-level pages and SSD blocks are completely different (and going through a block will be more efficient than having to hit multiple blocks), but probably 1.5 to 2.5.
Interestingly enough according to some folks “seek” on EBS is highly concurrent, whereas “scan” is slow and more erratic, so you may want to set random_page_cost lower than on SSDs in order to favour seeks.
> Although the system will let you set random_page_cost to less than seq_page_cost, it is not physically sensible to do so. However, setting them equal makes sense if the database is entirely cached in RAM, since in that case there is no penalty for touching pages out of sequence. Also, in a heavily-cached database you should lower both values relative to the CPU parameters, since the cost of fetching a page already in RAM is much smaller than it would normally be.
https://www.postgresql.org/docs/current/runtime-config-query...
Curious though, lowering both values is something I haven't done before but now I am curious about.
Too often, I ruled out Postgres as a solution to a certain problem before even trying and jumped to more specialized solutions or moved the problem to the application layer.
It took me years to stop underestimating what this awesome software can do.
So... what are we saying here, exactly? Are we saying that you don't need a no-SQL database, and that just SQL is enough with some Redis frosting on the cake to taste? I agree with that, but now we're not really talking about PostgreSQL any more, we're just debating SQL vs. no-SQL.
That's basically all you need.
From there, it's PostgreSQL and Redis specifically because Postgres is the best SQL database and Redis is the best high-performance KV store.
I use Postgres a lot, but with a largish database, I managed to get the whole database footprint in Clickhouse smaller than the primary key index in Postgresql. (~80GB vs 160GB, and 1.2 TB for the whole unencrypted PG database) Now, it wasn't a great index key and the original schema wasn't good, but optimizing PG got me about a factor of 3 or 4, and Clickhouse compression got me another order of magnitude.
Why are functions and procedures (an abstraction layer at db layer) considered harmful to performance when the same abstraction layer will be required at the application layer (introducing out of process overhead and possibly network traffic)? I don't agree with this advice. (Or I don't understand it.)
[1] https://www.postgresql.org/docs/current/sql-explain.html
It is true that we were much more familiar with application layer technologies, but the lack of expertise can also be considered a restriction, I think.
One the one hand, it is sensible to try and keep all your business logic in one place (could be the database, could be the application) as spreading it across multiple places can make it hard to maintain.
The current trend is to do your business logic in the application and treat the db as a data storage layer. The point in the article is that if you're using this model, but then stored procs and functions start creeping in to your db and it turns out they're actually doing some heavy lifting, then this can negatively impact the performance of your 'data storage layer' (which is actually not a data storage layer any more).
Author seems to be arguing against long functions/procedures. But if you move that to the client, presumably with ORM support - you're going to be executing more or less the same sequence of SQL queries and commands. Only difference is that when doing it on client you will have a lot of latency.
Yes, you can cache some data in between those commands to avoid same multiple queries, but if you use temp tables to do so, they will use memory only if it is available, otherwise you are limited with the actual storage size.
Only time I had memory issues with PostgreSQL when I used too much data in arrays and json's. Those are memory only variables. For example, I'd return a huge json to client and I'd run out of memory on PostgreSQL. I started streaming row by row and problem solved.
Edit: but I was referencing specific performances claims, that you will somehow take some load of database server. I just don't see it.
So, personally, I read that section as “logic in the database is not a zero cost abstraction.
Moving the traversal logic into the application and just sending SQL queries to Postgres (we don't use an ORM) eliminated the lag. RTT between the application and the db was a few ms and this wasn't user-facing logic anyway, so extra latency wasn't an issue in this case.
Probably the fundamental problem here was a sub-optimal schema, but sometimes you're just working with what you've got. Plus a commenter on Reddit pointed out that if we used pure SQL functions instead of PL/pgSQL, we'd also have seen better performance then.
So, would the better advice not have been to use simpler SQL instead of complex recursive statements, instead of taking a drastic approach to abandon ship (move logic to a completely new layer)?
Also, if you're doing string concats manually for your Json, this might cause some overhead for larger objects. ??
Probably, yep! But I didn't know that when I wrote it.
I didn't want to give any concrete advice at all tbh. The entire rationale for the post was that I'm not an expert and I've broken prod in some surprising ways and if I share those ways maybe it will stop other people making similar mistakes in future. But I guess I over-stepped in my discussion for this mistake, sorry about that.
Good point, I hadn't considered that part of it. It wasn't string concats, we were building it with `jsonb_set`, but I can definitely see the JSON structure in memory as being part of the problem now you mention it (although maybe that reinforces the argument for doing it in the application layer).
Most comments about debugability are nonsense. It’s just different, with some pros and cons. One simple example - if you have a bug in production, you’re not going to attach a debugger to your production application. But you can absolutely open a readonly connection to the database and start running queries, invoking functions, etc. It helps if you can architect your functions to distinguish pure readonly functions from those with side effects, but you can still debug even if that’s not the case.
Anyways, if you were able to solve one, you got a $5k bonus and a two week vacation. While I was there, someone figured one of them out, and it was due to logic in the DB. ...but I have a feeling that all of them were due to logic in the DB.
Functions are restricted in their available languages, ability to A/B test and scale. There's also complexity entailed by having two sources of business logic because people can forget which one does what, needing to constantly switch back and forth between procedures and app code when debugging.
Additionally the networking, resiliency and update patterns of databases are often not well suited to functions. You may want your functions to have public internet access but not you DB or rollbacks of your data but not function versions.
All of these issues can be overcome by people who're confident DBAs and sysadmin types in addition to being application developers but that's a small group of people.
I wish there were more startups in this area working to improve the developer experience of DB functions because you're correct about the superior performance and I'm pretty sure most of the issues I raised could be solved with well thought out tooling. However, at the moment such tools don't exist so these functions are painful to use.
Stored procedures will eliminate insane levels of latency if there are many records to be updated in ways that are hard to do in application layer code without repeated calls the the db. I use them a lot for DB maintenance. Often for that kind of work its also a lot simpler and easier to reason with than app layer code.
My team wrote a few critically important pieces of software that are running on Oracle, and here's why we did this:
1. Concurrency issues were the biggest pain point that we tried to avoid. We still had to fix a lot of bugs in the logic itself, and debugging them without unit tests was painful 2. We were tightly integrated with another system written in PL/SQL. When we started on v2, an independent solution, I moved almost all logic out of the database except for the critical synchronization logic. 3. We had a veteran team of PL/SQL devs in house. We still needed to get a subcontractor that wrote the API layer in Java, something PL/SQL isn't suited for at all. 4. Upgrades and rollbacks were a pain, especially after we had to move to a 24x7 SLA that left us with no upgrade window. Oracle has edition-based redefinition, but Postgres doesn't.
Are there any links that could be helpful to work through the programs and caveats of such a SP?
Part of it for me is creating a table that can store the process in the database to be able to traverse it reasonably.
Having application logic in the db is harder to debug and test (and possibly scale, but that also depends). But as you mention, it can be much faster if the logic is working on a lot of records.
Also, IME, the data store often far outlives the original application. Having the logic tightly coupled to the data model means future applications are less like to break the assumptions made in the original data model.
ANYONE that have a problem with RDBMS "functions and procedures & views (!)" are invariably mishandling the RDBMS: Bad schemas, null refactoring in the DB after years/decades(!) of cruft, re-implementation, poorly, of things the RDBMS already have (like for example, date types), procedural doing stuff that SQL already do easier and in short time, too big SQL that never, ever, use VIEWS to abstract away, the RDBMS was never upgraded or is assumed never will so nothing of the new things inventing like 10 years ago is used.
And that is a short list.
---
If you consider the RDBMS like the BEST programming language environment (sans SQL but still better than most languages for data!) and use the most BASIC ideas around it: like think a little about how do your schemas considering the queries you will do, some refactoring at least once every 5 years, pls!, use the CORRECT data types, pls pls!, use VIEWS pls pls pls!, etc your logic in triggers/functions MUST BE short and EASY.
As a rule, processing cost should give you a default bias into moving anything away from the database. Multiple sources, the need for temporary storage, and the existence of concerns that don't deal directly with your data should bias you more towards moving your code away from the database.
On the other hand, data consolidation and enforcing non-local (to a record) rules should bias you towards moving your code into the database. If a lot of those happen, moving it there may even reduce the load of your database.
Any one sided advice here is guaranteed to be wrong.
The biggest way I have seen this be true is with fragmented tables/indexes - same data but organized more compactly can change planner behavior.
Article actually touches on another way that can be true - if your `work_mem` is drastically different the planner may prefer not to use an index, for example, and there are similar settings. Even with identical settings PG may choose different plans, it can be a bit tempermental in my experience, so sometimes you have to run the nonperformant query in offpeak production to understand performance.
For new table design in Serious Business Databases I do two or three step: offline replica with the same data and settings (but not 100% identical), usually in a transaction on a logical replica or main production (postgres supports DDL transactions, which is a big help, but if you alter a table in a transaction it can block tons of other transactions).
It's important to carefully construct your alter table / create table / create index statements to ensure that everything is concurrent and won't block, of course, and there are plenty of pitfalls there, but definitely doable.
If you have:
- Same resources (CPU/memory) - Same settings (eg work_mem, amongst others) - Same dataset - Same/similar statistics (gathered with ANALYZE or the autovacuum)
you should get the same results. If I'm wrong, please somebody correct me!
Is the overhead of running EXPLAIN ANALYZE so much worse than running slow_query itself?
The real problem you run into is when the query in question is doing something pathologically bad - locking a bunch of tables and then grinding away for an hour, which effectively is a denial of service attack.
- Triggers for validation are awesome. Avoid triggers for logic if you can help it -- harder to debug and update than a server sending SQL and easier than you might think to cause performance problems and cascading triggers.
- Use custom error codes in validation triggers and add as much context as possible to the message when raising the exception. Future you will thank you.
RAISE EXCEPTION USING
ERRCODE = 'SR010',
MESSAGE = 'cannot add a draft invoice ' || new.invoice_id || ' to route ' || new.route_id;
- Postgres exceptions abort transactions, so if using explicit transactions, make sure you have a defer Rollback() so you don't return an aborted transaction to the server connection pool.- For better trigger performance, prefer statement-level triggers [1] or conditional before-row-based triggers.
[1]: https://www.postgresql.org/docs/current/trigger-definition.h...
SEQUENCEs, used to implement SERIAL and BIGSERIAL primary keys, are not transacted. "BEGIN; {insert 1,000,000 rows}; ROLLBACK" always adds 1,000,000 to the table's primary key SEQUENCE, despite the ROLLBACK. Likewise for upsert (via INSERT ON CONFLICT).
The end result: A table's SERIAL (32-bit signed integer) primary key can overflow even when it contains far fewer than 2^31 rows.
if you hit the max integer for the sequence and need space to implement a fundamental fix you can quickly change the sequence to start at -1 and go down. there's no issue with negative ids since they're also integers.
On a related note, one could say that the C++ Core Guidelines[1] at least partially represent such a list. 55 matches for “don't use” and 247 for “avoid”, although not all of them are about language features, obviously.
[1]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
Does anybody know why this is the case? Usually, an index is not used if the semantics of the index do not match the semantics of the query, so "using" it cannot ever produce correct results. But the workaround presented seems to have identical semantics to IS DISTINCT FROM and still uses the index, so why isn't IS DISTINCT FROM using the index then?
Most of the time the answer to that is: because nobody cared enough or had time enough to implement it
Anyway, I don't know if it's the same in Postgres, but in Sql Server an OR condition like that could kill your performance quite easily in a relatively complex query, often I had to refactor my query to a UNION (usually with ALL to avoid a distinct step, but it depends on the specific case).
SQL Server is a damn fine DB if you can afford it. Highly recommended.
If not, be prepared to check for sql injection vulnerabilities the first n PRs from any new team member.
Also to explain how to do it and your reasoning for it.
"select * from sometable where id=" + id
feels like a logical next step unless you actually have studied the subject or read the manual ;-)Since the Postgres planner doesn't really allow you to tune your query there aren't many ways to construct your query in a way which would to a much worse execution plan. Over the years we have migrated most raw SQL back to using the ORM without taking performance hits, pretty much the only remaining raw queries are CTEs where we force a certain order of query execution.
Usually these ORM problems are caused by schema design anyways. If you need 10+ joins you are going to have a hard time with or without an ORM.
Im a happy user of prisma, I value It supports several databases and not only pg. using for example sqlite for localhost dev has its perks, and it's easy to move later to other stuff if you have already planned for it and not using types that are incompatible between your two targets
We can pretend that the proliferation of managed databases, newfangled NoSQL datastores, and other abstractions preclude the need for accumulated empirical observation across a range of tech stacks and time, but sometimes there's really no substitute for greybeard wisdom.
"Automatically truncate all tables that have foreign-key references to any of the named tables, or to any tables added to the group due to CASCADE."
So it will simply clear out other tables that reference table to truncate, even if you have `on delete set null` and the foreign key column is null.
You should be very hesitant about using TRUNCATE on a production database unless that table (and all related foreign keyed tables) are truly ephemeral. Even if the data is cleared every night at midnight, for example, is there going to be a 10 second analysis transaction running across the midnight boundary that will fail hard with suddenly missing rows?
Running a full delete on the rows and vacuum will still result in a tiny file on storage and doesn't wake me up in a cold sweat when I have a flashback. Even renaming the table is in many ways safer.
The only difference to other DML statements is, that it will put an exclusive lock on the table. So until the TRUNCATE is committed, no other transaction can read from the table.
Truncate is DDL. It's like dropping and recreating the table in a single operation.
Sorry, that's what I mean. It's safe in the sense that you can roll it back, but it's not safe in the sense that other concurrent transactions will see the table as empty if they are long-running.
I don't know of any database system that does this. In the case for SQL Server, the foreign keys usually get added by the ORM layer (Entity Framework migrations if you're using dotnet).
But there's a factor that even worsens this: parallel query, which is typically enabled by default, and will add another multiple to work_mem.
Tuning work_mem is a hard art, and requires a delicate balance between trying to optimize some query's performance (that could avoid touching disk or using some indexes) vs the risk of causing db-wide errors like OOMs (very dangerous) or running out of SHM (errors only on queries being run, but still not desirable). So I normally lean on being quite conservative (db stability first!) so I divide the available memory (server memory - OS memory - shared_buffers and other PG buffers memory) by the number of connections, also divided by the parallelism and by another multiple factor --and then leave some additional room.
In any case I'd recommend reviewing the detailed information, suggestions and links on the topic on postgresqlCONF [2] (disclaimer: a free project built by my company)
[1]: https://news.ycombinator.com/item?id=35697986
[2]: https://postgresqlco.nf/doc/en/param/work_mem/
(edit: formatting)
1. Contrary to popular belief, Postgres isn't fully ACID (specifically the "I") with the default isolation mode. For example, selecting the sum of a column then inserting conditionally on that creates a race condition. Serializable mode is fully isolated, but it has many caveats and shouldn't be used often, so you should instead become familiar with what's isolated and what isn't. See https://www.postgresql.org/docs/current/transaction-iso.html
2. timestamp (without time zone) is bad; always use timestamptz, no exceptions. Unintuitively, timestamp (without time zone) is the one that makes your DB's time zone affect your selected data. Neither one actually stores a time zone, it's just a difference in output formatting. This is a moot point if your DB's locale is set to UTC, but that's not the default.
I’d argue it isn’t used enough given its isolation advantages
Spanner and some other DBMSs are fully serializable always. They do a better job of that than Postgres, I guess cause they focus on it, but they're still a lot slower in the end.
What would be cool is some extension to watch your non-serializable transactions for concurrency anomalies like 1% of the time and use that tiny sample to alert you of race conditions. Like a thread sanitizer does for code. Does that exist??
I’m not sure how you could detect such anomalies though without running the same serialization checking logic within postgres
For the sampling, you'd have to run the same logic. The idea is just to do it infrequently as to not totally ruin your overall performance. Idk if this would work.
This bothers me. It's technically true, but ignores a lot of nuance/complexity around real-world event processing needs. This approach means you will never be able to retry event processing in case it fails (or your server is shut down/crashes). So you either have to update the logic to also process events where "acquired_at is older than some timeout", which breaks your "handled only once" guarantee, or you can change to a SELECT FOR UPDATE SKIP LOCKED approach which has its own problems like higher database resource usage (but at least it won't process a slow job twice at the same time).
That’s a mistake that you’ll be paying for for a while.
One trick worth noting, is that you can override the working memory at the transaction level. If you have a query you know needs more memory (e.g doing a distinct or plain sorting on a large table), within a transaction you can do:
`set local work_mem = '50MB'`
That will override the setting for operations inside this transaction only.
1. Get a better name, PL/pgSQL doesn't exactly roll off the tongue.
2. Get rid of those $$ at the start and end of any PL/pgSQL, it's just verbose and ugly.
https://www.postgresql.org/docs/current/sql-syntax-lexical.h...
The Postgres parser itself doesn't know the language rules and thus can not parse the source code. The body of the function/procedure is passed to the language handler as a string without Postgres looking at it. The procedural code (PL/pgSQL, PL/python, PL/perl, plv8, ...) will not be parsed until the function/procedure is actually executed.
Today there might be better ways to implement such a dynamic system to register new languages, but I guess it will be huge effort to change this to a way that would understand the old and the new syntax.
Since Postgres 14, at least "LANGUAGE SQL" functions/procedures can be written without using dollar quoting if the SQL/PSM syntax is used.
For things like formatting and transforming data, I prefer to have that work done on the client (web browser) if it's for a presentation concern, or middle tier if it's for an integration. My theory there is "don't make the database work hard". Use it for what it's great at: fast reads and data consistency.
DB functions and sprocs are a great way to reduce latency and load, and boost performance if used for the right things.
An easy rule of thumb is that if my function isn't using SQL as the language, I need to really think about whether it belongs in the DB.
That all being said, I do use udfs to construct JSON results for some reports also, when it makes sense based on the params and number of round-trips to the server it would cause in the middle or UI tiers. Even though it violates some of the things above, it's really damn convenient and usually limited to low-traffic queries.
Asking readers to keep mentally flipping the sense of the thing you're telling them to do just adds cognitive load and makes it harder for them to pay attention to what you want them to pay attention to.
Write "do"s, not "don't"s.
UPDATE event_queue
SET acquired_at = now()
WHERE id IN (
SELECT id
FROM event_queue
WHERE acquired_at IS NULL
ORDER BY occurred_at
LIMIT 1000 -- Set this limit according to your usage
)
RETURNING *;Would you need a FOR UPDATE in that subquery?
There are also plenty of other weird planner choices which I can't work out but which were much easier to understand in SQL Server. Sometimes, the smallest change will stop using an index e.g. using LIMIT on a query can completely bypass an index.
Or more specifically transaction isolation levels.
Didn't read further.
Thanks for pointing it out though, feedback from experts is the big payoff to posting from my p.o.v. and now I know a thing which I didn't know before.
that sounds like a bug; are you aware of an issue/ticket tracking that?
Here's my issue on StackExchange, for anyone that wants to delve deeper into my issue: https://dba.stackexchange.com/questions/325104/error-could-n...
Here's an thread talking about the issue, while OP's issue doesn't seem to match exactly what I was experiencing, one of the replies describes my exact issue: https://postgrespro.com/list/thread-id/2546853
* work_mem is postgres specific
* stored procs and functions don't perform as badly in SQL Server
* Triggers behave differently
* NOTIFY is postgres specific
etc.
I have been a heavy user of the Percona distribution of MySQL for many years and highly recommend it. I think that most of the MySQL articles on percona.com are relevant for other MySQL distributions.
As a side note, depending on the software stack, ProxySQL can have a major positive impact on performance and scalability.
- Start compiling queries and functions to native code and hash them just like other DBs
- Improve connection scalability by using tasks instead of threads (each thread needs a lot of RAM!)
- Automatically maintained clustered index (like in SQL Server) would be nice to have
In our Postgres DB we have more than 4 TB of data, which I don’t think is too big. We didn’t need any special sauce, no vendors chiming in, only a better understanding of the technology than average Joe.
On the big company part - I have yet to employ more than five developers.
Did you try a filtered index using WHERE ... IS NOT NULL instead?
Or maybe an index on an expression coalesce(the_column, false) and then use that expression the where clause?
MySQL is easy: apt-get update/docker stop && docker rm && docker run/kubectl apply, depending on your stack that is literally all you need to do.
PostgreSQL in contrast is hell. You have to shut down the existing database server, install the new one in parallel, manually do the upgrade (that involves copying the whole dataset), remove the old stuff, and then start back up again. Or you have to export the whole database into an SQL dump and import it on a fresh instance.
In any case, way more involved, way more likely to go bonkers, and way, WAY more downtime needed.
> If you use link mode, the upgrade will be much faster (no file copying) and use less disk space, but you will not be able to access your old cluster once you start the new cluster after the upgrade. Link mode also requires that the old and new cluster data directories be in the same file system. (Tablespaces and pg_wal can be on different file systems.) Clone mode provides the same speed and disk space advantages but does not cause the old cluster to be unusable once the new cluster is started. Clone mode also requires that the old and new data directories be in the same file system. This mode is only available on certain operating systems and file systems.
(Note that one approach if concerned about the potential for downtime if it goes wrong might be to `pg_upgrade` your secondary in a hot-standby replication setup, then failover and `pg_upgrade` the former primary. In practice, `pg_upgrade` runs very quickly — seconds even on multi-terabyte databases — afaik it doesn't touch the actual data but just the metadata, but read the man pages for more intricate details on that :)).
Last time I checked, MySQL supported it in more cases. MySQL can also be explicit about it via "lock assertions":
ALTER TABLE ..., LOCK=NONE;
will give an error if the requested operation can't be performed while still allowing concurrent reads and writes to the table (if you're fine with preventing writes you can use LOCK=SHARED).The LOCK clause isn't just an assertion and can actually affect how MySQL performs the operation, but I tend to think of it as asserting "this won't cause downtime by locking this multi billion-row table while it spends an hour rewriting it".
For me, the big win is to have a cluster without having to pay for SQL Server Enterprise edition therefore having money to invest into the community by having a Postgres consultant help configure it for me.