How I Reduced My DB Server Load by 80%
schneems.com
schneems.com
Being able to create indexes based on the results of a function is the solution to so many problems. You can even index the result of an XPath function on a huge, compressed XML document.
create index repo_name_lower on repos(LOWER(name)) where name IS NOT NULL;There were three problems with having the rule in Rails:
1. The need for an index was easily overlooked.
2. The rule would be bypassed if a different app used the same database.
3. The rule wasn't even foolproof. Only a database constraint would guarantee uniqueness when two users are saving at the same instant.
The problem is, SQL is hard. We should not forget how much a programmer must learn. For example: Ruby, Rails, Linux command line, HTML, CSS, JavaScript, vi, how to exit vi, etc. Each of these takes years to master.
SQL is especially SQuirreLy. However, it can't possibly be worse than learning the myriad JavaScript frameworks and complicated server-build tools that are completely optional for 99% of us. My advice: Don't do a SPA. Spend your time on SQL instead :D
If you didn't want to write SQL and use constraints, then why would you even use a database (over just plain files)? That's what they are designed for. RDBMS developers are probably all sitting around giggling (nihilistically) over experiences with users who think their database is slow on account of their failure to actually use it in the way it was optimized to be used.
This problematic state of affairs is only compounded by the fact that ORMs don’t properly and easily handle taking constraint, validation, and other data-focused application logic and actually writing it into the schema (some ORMs are better/worse at this than others, of course). They all seem to just default to letting as much of it be application-level code as possible. This then leaves new developers with the impression this is where such code belongs, so they never experience anything telling them it should be anywhere else. It’d be great to see more education offered within ORM docs, but the really important stuff seems to often be there only if you already know what you’re looking for. I’ve seen so many models defined over the years without indexes on the most obvious columns that are queried a bajillion times. Or maybe you can set a unique constraint on certain fields without a bunch of effort—sqlalchemy is decent at this in the Python world—but then you have to have unique validators for forms, rather than relying on the db to complain about a violation of the the constraint, and the ORM catching it in a friendly way devs can bubble up when needed. Oh, and then some insist on adding client-side validation of that same constraint. So, you take a constraint that matters at the db level, and is already easily handled at the db level, and people are writing client and server validation to enforce a condition the db is already supposed to be enforcing.
There’s madness here. Maybe it’s just me, though.
At the far end, any programming logic you put in the DB will be impossible to scale if needed. You can only have one DB, but any number of Rails servers.
Database constraints are a matter of preference and skill set. They make some things much harder when you have to delete things is certain convoluted orders, and I can get the correctness through tests. Your decisions may differ. That's fine.
You should always put indexes on things you query by. Be vigilant. This is the one case where "only optimize when you have a performance problem" rule doesn't apply.
> any programming logic you put in the DB will be impossible to scale if needed.
> You can only have one DB, but any number of Rails servers.
The difference in processing time between a database with no rules and one with all of them is usually negligible. > You should always put indexes on things you query by
I have put indexes on things, and PostgreSQL still decided it was more efficient to do a sequential scan. There are cases where they don't make a difference ("low cardinality" or something like that). Also, indexes slow down writes and eat disk space. Usually each is just a little. But it can add up sometimes.The advice I see most is to run EXPLAIN and judge from that. Even after you've added the index and run ANALYZE, run EXPLAIN again to make sure that the database is using the index and that it makes a difference.
Sometimes it's better not to index, if you have a table that is updated often and queried in varying, complex, rarely repeated ways.
Special cases though, usually write-only analytic tables that are queried once in a blue moon and have incredibly high traffic (~millions of rows per hour).
I wholeheartedly agree that developers learn SQL way too little and way too late (I blame ORM/AR overuse early on) but SPA/SQL are possibly the least mutually exclusive issues I can imagine.
This was learned the hard way: creative sales people manipulating sales data (via ODBC & Excel on NT3.51 no less). Since all logic was in the applications, they had a field day when they bypassed those.
This was 1996. Not much has changed.
Off topic: learning to exit vi takes "years to master"?
Joke aside, the basics of SQL can be learned in a very short time. Before fancy ORMs all novice programmers managed to get by using SQL in big-ridden PHP projects ;). I for example, got my introduction from querying a national database leak and than writing a simple blog with tricks I've learned from sqlzoo.
1. A cadre of diehards can't wait to post how amazing Postgress is or would be for whatever it is the OP is doing (as an aside, why isn't Postgres more popular if it's so amazing?); and
2. How averse people are to actual SQL.
Years ago I dealt with this crap in the Java world back when Hibernate and the like were all the rage. I was always amazed at how much confirmation bias there seemed to be. People decided these ORMs were amazing and then completely ignored all the bugs introduced by this layer and effort spent trying to figure out what the ORM was doing and how to make it do the right thing.
Back in the day I always liked a Java data mapper framework called iBatis (now dead, replaced by Mybatis it seems), which was pretty simple. Write some SQL in an XML file and call that SQL from your Java code. It was parameterized (so no SQL injection issues) and you could still do some funky things with discriminated types and the like. Plus, analytics were super easy because you knew how often each query was called and how long it took. Also, you could easily EXPLAIN PLAN those queries if you even had to (usually needed indexes were obvious).
Compare this to the auto-generated SQL from the likes of Hibernate. ugh.
I've come to the conclusion that people have this tendency to decide X is bad and then go completely out of their way to avoid X. You see it with SQL and ORMs. It largely explains (IMHO) thing slike Javascript and GWT.
At least half the time "X is bad" really means "I don't understand X and I don't want to learn it".
Joel Spolsky's "leaky abstractions" is good and time-honoured advice.
Take the Hibernate example. Once you bought into that framework you had to do all your data access that way or you broke the caching. That's mostly bad.
People also overestimate their needs. They rush to create Hadoop clusters and distributed NoSQL solutions because, you know, relational DBs can't keep up with their "Big Data" (which means, millions of rows) when in fact you can dump billions of rows into a single MySQL instance.
> People decided these ORMs were amazing and then completely > ignored all the bugs introduced by this layer and effort > spent trying to figure out what the ORM was doing and how > to make it do the right thing.
For every person that did this, I've run into at least one other that was convinced that ORMs were the most evil thing ever and that they should roll their own little Object Mapper. Every one of them would then completely ignore all the bugs introduced and effort spent trying to train new developers on their slightly unique thing and how to coerce it into doing the right thing.
"I've come to the conclusion that people have this tendency to decide X is bad and then go completely out of their way to avoid X. [...] Take the Hibernate example. Once you bought into that framework you had to do all your data access that way or you broke the caching."
There is nothing about Hibernate that requires you to fully buy into caching, or even to fully buy into its abstraction. In fact, I've generally avoided caching in Hibernate in order to retain the flexibility to do what I needed without it when I needed to.
Usually about 90% of the code ends up being really boring CRUD with trivial queries. The last 10% could be implemented with whatever crazy approach made sense.
And this isn't to knock those other frameworks either. iBatis and quite a few other early Java ORMs were quite good and arguably better than Hibernate was at the time, but Hibernate was marketed more effectively.
[1] - http://jdbi.org/
I'm a happy MySQL user, all of my side projects and on the job work is done in MySQL. That said, how else would Postgres become more popular if there isn't some level of evangelism to spread the word? I like reading about Postgres features and maybe someday I will switch.
For now, with my/our needs, MySQL is fine
Also
Hibernate was just horrible and I had to watch my entire team adopt it (EJB 1 era).
And there are many bad, or simply, "too simple" ORMs out there that really don't help you much. I haven't really found one better than Perl's DBIx::Class (though outside of Java, Python, Ruby, I have haven't really looked).
Case in point, I recently found this little gem[0] allowing easy correlated subqueries. I basically took a webpage that was loading in over 2 minutes, and reduced it to about 100ms and the outputted SQL was about 2 pages long (from about half a page of custom resultset orm code). I used several correlated subqueries to sort of pivot part of a table (well several tables actually). The original author of the code I was working on was fetching entire tables of data, with each row fetching (joining) to another entire table (it did this in several levels really) just to sum some values. This was all valid ORM code (even with handy 'if' statements checking every row id to do in ORM join (yes he didn't even use a dang where clause!)) but it showed a true lack of knowledge of the ORM at hand and even SQL in general. Nonetheless, I saved the day :)
[0] https://blog.afoolishmanifesto.com/posts/introducing-dbix-cl...
As a bonus, if you're thinking ahead, you can avoid writing queries that will perform poorly. If you're not thinking ahead, at least you can more easily fix the ones that do.
I've never really understood why people want to interact with their database outside of its native query language. There's just so much mismatch between an ORM and the underlying interface, it feels like fighting all the time. And I don't feel like it's really saving any time either. The one thing that's sort of nice is it's easier to write a general filtering function, but SQL filtering lets you write so many things that won't perform well, making that easier isn't really helping anybody.
Writing queries is easy. Validating data is not too difficult. Building objects/whatever from the resultsets is not so easy anymore, especially with joins.
By the way, the mistake this post is about is a classic with Rails. You have to use a unique index in the db, not making queries. You want to protect your data anyway, regardless of which application you start building your db for. Others will follow, possibly in different languages.
Turing your hash into parameters for an insert isn't going to be great fun, although perl hash slices aren't bad. I think I've seen something about named SQL variables instead of just ?, that might be usable with a hash directly?
Not true. You may use a graph database, or write SQL statements & apply functional transformations. Or you might be using a platform like smalltalk & just save your image. There are other paradigms besides OOP & relational databases.
> new hires will end up taking a ton of time to learn this "custom" orm framework of yours.
Or your new hire might really know how to leverage the particular database you're using, but you force him to throw away a lot of his experience & to start over from scratch & instrument the database through an ORM layer. There are a lot of ORMs, their APIs may even differ, so someone who knows Propel still has to stop & read the docs if you're using Doctrine, for example.
> it showed a true lack of knowledge of the ORM at hand and even SQL in general.
Quite the opposite, anyone who can craft a query that complex obviously knows the tool well. Their real problem is knowing when to not use that tool. Or they just write bad code, regardless of what tool/langauge they're using, for whatever reason.
There you go. I need 6 more months to deliver now.
The price you pay for a one-size-fits-all solution is that you lose the flexibility of arranging your queries the way you want. You'll automatically lose a lot of performance (that is not always important), but you also lose a kind of code clarity.
If you don't get a one-size-fits-all solution, you will have to write the translations by yourself. That leads to more code, but in a better structure because you don't need to beat your code until it fits the ORM's model.
All said, I do think an ORM is not something useful by itself, but may be part of a very useful solution. Python Django's CRUD generation and Haskell Persistent (not really ORM, but very similar) type checking are examples of that.
Or your new hire actually learns how to use the tech that makes their code work. I could use fewer devs who have no idea how the code they write actually works.
There is always a trade-off. The code you wrote, even if it is poorly re-implentation, you know it inside out, since you wrote it. The third party code, may be better, but people usually make a lot of implicit assumptions and end up not using it correctly, so you end up in situations like this.
For DB layer, I usually prefer custom code so I know exactly what is going on under the hood.
I'm always confused by how reluctant folks are, in general, to read underlying libraries.
Korma is a DSL. And I think you are confusing the concepts of ORM and DSL. I'd agree that most teams, over time, tend to develop (or borrow) their own DSLs for interacting with the database. And I've seen teams that use the term DSL loosely to refer to any code they write to interact with the database, so long as they are doing more than concatenating strings of SQL. But they don't necessarily use ORMs.
Tl;dr you aren’t wrong, but many home-rolled ORMs are simpler and much easier to use than, say, activerecord.
It is also complex and requires that you really master all the ingredients... If SQL is a mystery to you, it will just be a painful experience.
My favourite SQL library is Clojure's HugSQL, which avoids this issue by letting you write SQL in .sql files and then generating functions for you that execute these queries. https://www.hugsql.org/
I'm not a full-out fan for ORMs but... this is not an ORM issue. It's an issue with an ORM of the ActiveRecord variety that pushes database-logic back to code level for some strange reason. The ORM I'm most familiar with (Doctrine2, Object Mapper pattern) doesn't do this. It does other weird stuff, but not this.
[1] https://docs.djangoproject.com/en/1.11/topics/db/sql/#mappin...
No one likes copying the SQL command to a SQL IDE, making some changes, running it to make sure it works, and then copying it back to source. If you don't do that, you're dropping all of the productivity that syntax checkers do for you and risk losing productivity to typos or other dumb mistakes.
You get to use the orm for a lot of braindead simple, and mild complexity stuff, and can drop down to the sql builder as it makes sense for complexity or performance reasons.
It also understands the various databases very well, so if you choose e.g. postgres, you can access most of the features without needing much (if any) "raw sql".
Exactly as with sql upgrade scripts.
It look like this:
--name: post-event
SELECT post_log(@theId, @ENTITY, @ACTION, @CHANGEBY, @DATA, @VERSION)
GO
--name: get-location
SELECT * FROM "Location"
WHERE
id = @id
GO
--name: list-location
SELECT * FROM "Location"
ORDER BY country, state, city;
GO
Then I just parse this file (note the names with --) once and use this alike (in F#): module Location =
let ENTITY = "Location"
let SQL_LIST = SQL_CMDS.["list-location"]
type LocationRecord = {id:int64 option; address:string; country:string; state:string; city:string; version:int64}
type LocationQuery =
| All
| ById of int64
let query q =
use con = openConn()
let toRec = Db.toRecord<LocationRecord>
match q with
| All ->
Db.select con SQL_LIST []
|> Seq.map toRec
| ById(theId) ->
let id = [P("@id", theId)]
Db.select con SQL_BYID id
|> Seq.map toRec
And plug a micro-orm (mainly just a very thin layer over ADO.NET in .NET. I do similar over swift and python).This take me like a few hours. Let me test easily the sql. I can build the sql exactly as I want.
The only cons is the repetition on the scripts - because SQL is a terrible language that not allow composability, like similar to CSS - . Probably I will later use a template parser (like mustache or similar) but I think this is the closest to the holy grail ;)
What do you do when you have five similar queries, and each needs to be changed if one changes?
This seems like smaller projects might work, but I'd be concerned about using a strategy on a larger project.
Not a fan of this strategy in particular, but I can see arguments for it.
In short, I truly utilize a DB with full effects.
I don't follow the mantra of using a DB "only" as a dumb datastore. Something that I get validated as when people use NoSQL and have not option that use all of it.
This mean, that I use views and stored procedures/functions.
This cut a lot. Also, proper modeling of tables also help (a lot). I refactor DBs.
I mean, I truly treat a DB as "code".
Obviously, this strategy is not flawless, and it will benefit from some kind of ORM-ish layer to auto-generate the mechanical parts of the querys. I'm building that part myself.
But so far, is have show to be good.
I strongly recommend it. Having statically typed representation of the schema has many pros and the trade-off is a little overhead when changing the db schema to regenerate classes.
Some of the benefits include knowing exactly what part of the code stops compiling after changing the db schema, easily map POJOs to-from query results.
Also, there's simply no pressure that you have to be absolutely certain what each and every knob on your ORM does else you have bad data in your database. Been burned before with Hibernate defaults, and the thought of a junior dev creating wrong conf is just not worth it.
The database is basically the place were you least want a risk of misuse to have any unseen side effects.
Tbh thats currently my preferred methodology, just because it's usually nicer to clean up a mistaken query in something like sequel-pro, and its generally easy to verify correct output visually
And I've had to do the same thing with ORMs like sqlalchemy, and the only real difference is I first have to get the ORM to tell me what it generated (which is a pain, sometimes).
To my own surprise, it has become my favorite way to handle queries with multiple/variable joins.
By example:
$sql = "select myColFromTableB from tableA";
Inspection will give you have a "unknown column" warning.
(ps: sorry for late reply)
Ultimately it's a style preference but I've seen larger projects with thousands of lines of SQL in source with some of those queries spanning 6-7 joins with dozens of where clauses (plus string manipulation functions, ugh), so that is why I'm pretty fiercely against SQL in strings.
Sometimes it seems like people get hung up on doing everything in one query and end up making a Frankenstein's monster that nobody will want to touch when it inevitably breaks on the next database upgrade. Although sometimes the system forces this behavior by ingesting the output of the query automatically.
At least in my experience, assuming those queries are written "correctly" (that is, in a set-manner than a C-in-SQL manner), leaving it in the database means is usually vastly more performant and easier to verify it's correct. I've written a lot of many-hundred-line monsters with joins, CTEs, table-valued functions, and a whole lot of other stuff piled into them that were both more clear and much faster than the arcane application logic they once consisted of. Usually if it's used in the application, some management type will also want some kind of report based on pretty much the same query, too. And having it as SQL also makes it much quicker to compare the before-and-after if any changes are made.
Of course, YMMV and what I said isn't true all the time, and would certainly also depend on your database server - if queries did break between upgrades I'd be much more hesitant to rely on the db server.
But either way, I wouldn't dump a monster like that right in source code - that's crazy!
This is why the N+1 is such a big deal and you can get results magnitude faster of you query through the database.
And how exactly are you making sure your ORM code works?
Actually, I started to put SQL directly into my source code. IntelliJ, for instance, has really good support for embedded SQL code (syntax highlighting, auto-completion, executing queries including placeholders etc.).
So, SQL strings belong to the metal, not the UI.
Though there are certainly cases where it helps to be able to find someone more experienced and knowledgable than you.
Or perhaps you're a DBA that's tooting your horn so loudly that you don't believe anyone can do what you do.
I thought this thinking was 10 years dead at this point.
And, to be fair, having seen some of the abominations people have committed with SQL, I only half blame them.
But the issue is with saying "no developer should be writing SQL strings, ever".
It's like saying "no designer can handle HTML, leave that to the professionals".
Only for object oriented apps, where you want to tightly link behavior to your objects. For other kinds of app architectures, there may be no relational/object oriented mismatch to smooth out.
However, if you have well-defined requirements and are expecting high-volume traffic from the get-go, then it could be wise to skip the ORM.
All web developers ought to know the basics of SQL ie how to SELECT INSERT and UPDATE
That doesn't seem right to me. It's almost always something to consider when designing the data model. Maybe I'm being uncharitable, but this seems to me to be equivalent to claiming we rarely consider the use of an algorithm or interactions between algorithms and data structures when writing some code. I mean, for toys that's fine, but I wouldn't defer this discussion for something I intended for public use.
I still can't believe that Rails even attempts this. It's simply not possible to do this kind of enforcement in a race-free manner using SELECT statements with Postgres.
`I’ve been seeing that stray 30s+ spike in request time daily for months, maybe years. I never bothered to dig in because I thought it would be too much trouble to track down. It also only happened once a day, so the impact to users was pretty minimal.`
Edit: in this example, case insensitive uniqueness validation in Activerecord
Why would anyone code this kind of inefficiency instead of using inbuilt constraints. Has the code been reviewed, tested? Too many questions.
It's surprising given how popular Rails was and still is that something which should have been caught in the early days of Rails is discovered now years later. Aren't all the production apps seeing this? Didn't Twitter see this?
The real concern is a lot of highly promoted technologies in HN do not get the proper technical scrutiny that one should take for granted in a technical forum and increasingly hype is conflated to quality.