Live data from Hacker News

Cases where full scans are better than indexes

jefftk.com

101–110 of 226 posts

Re: Cases where full scans are better than indexes

#102
post #92

Earlier quoted context omitted.

It’s worth noting that if your DB instance is so heavily loaded that this is a real concern, you already have a huge problem that needs fixing.

AWS is particularly bad with their performance credit system on RDS... and there's to my knowledge no way to tell MySQL to limit index creation IOPS, which means in the worst case you're stuck with a system swamped under load for days and constantly running into IO starvation, if you forget to scale up your cluster beforehand. Even if the cluster is scaled to easily take on your normal workload, indexing may prove to…

That does seem like a real problem! Adding indexes periodically is a pretty regular thing for any production system where I come from.

Re: Cases where full scans are better than indexes

#103
post #99

Earlier quoted context omitted.

Justify the dev time to save micro-pennies worth of electricity to me instead. A typical naive index won't help with my regular expression based queries, which aren't easily accelerated by an index. Or given an in-memory index, you've just increased memory use from O(1) to O(N), and I'll OOM on large files. Perhaps you'll throw a database at the problem, complicating I/O (especially when the data is generated/accesse…

> Justify the dev time to save micro-pennies worth of electricity to me instead. KEY (user_id) I mean, it's a dozen characters. Do you need to know how fast I type before you run the calculation?

First tell me the minimum amount of time typing this would have to take for you to agree it's not worth it and I will try to keep adding things like the time it takes for someone to ask you to do this, for you to start VS Code, find the file, press ctrl-s, deploy the changes, and possibly some estimation of how long it takes a new developer to read and understand this system (please tell me how fast you speak and an agreeable value for how fast the average developer reads as well for this part) vs a simpler one until it goes over that limit.

Re: Cases where full scans are better than indexes

#104
post #68

Just because the table scan is under some threshold doesn't automatically make it better. If a table scan takes 250ms vs 0.01 for a indexed lookup, you're still gonna have to justify to me why making silicon work that damn hard is worth even the electrical use. Are you inserting and deleting so many rows that maintaining the index is prohibitive? Do you have space concerns, and are not able to keep the index in memor…

Justify the dev time to save micro-pennies worth of electricity to me instead. A typical naive index won't help with my regular expression based queries, which aren't easily accelerated by an index. Or given an in-memory index, you've just increased memory use from O(1) to O(N), and I'll OOM on large files. Perhaps you'll throw a database at the problem, complicating I/O (especially when the data is generated/accesse…

> Justify the dev time

And this is exactly the sentiment that got us where we are.

Re: Cases where full scans are better than indexes

#105
post #55

“We’ll add an index when it gets slow” is saying “screw the guy who’s on call the night this system starts timing out”. Invisible cliffs in your code that it will fall off at some unknown point in the future are the antithesis of engineering. If you deliberately aren’t implementing indexing, know at what point indexing would begin to matter. Put in place guard rails so the system doesn’t just blow up unexpectedly. Th…

> screw the guy who’s on call the night this system starts timing out This was a very small billing practice, and that person was going to be me. I thought then, and still think now, that I made a reasonable trade off between what would be work at the time and potential future urgent work. Additionally, this wasn't the sort of thing that would fail suddenly when you hit a critical point. Instead, running full text se…

Stealing time from future you also doesn’t pay off. Future you wants to take vacations and change jobs and have family events and sleep and stuff.

It doesn’t take much work to do the back of envelope math:

How long should these queries take? Less than two seconds?

How much slower do they get as record counts increase? 100ms every 250,000 records? Okay, so this will become intolerably slow when the record count hits about 5 million. When’s that going to happen? Never? Then we’re done here. Within a few months? Well let’s plan for that now. Within a few years? Maybe not within the lifetime of this system? Judgement call. Let’s put something in place that forces us to at least look back at this before it gets bad. Even if that’s just a google calendar reminder.

Re: Cases where full scans are better than indexes

#107
Lately, I've been investigating 'serverless' databases (PlanetScale, Turso) that charge based on the number of rows read/scanned. As a result, instead of just relying on the perceived performance of a query, I've started monitoring the number of rows scanned, which has emphasized the importance of indexes to me. Granted, the cost per rows read is minuscule (PlanetScale charges $1 per billion!), but it's still something I keep in mind.

Re: Cases where full scans are better than indexes

#108
post #83
post #80

Once upon a time (25+ years ago) I used to maintain an Oracle database that had around 50M records in its main table and 2M records in a related table. There were a dozen or so dimension (lookup) tables. Each day, we would receive on the order of 100K records that had to be normalized and loaded into database. I am simplifying this a lot, but that was the gist of the process. Our first design was a PL/SQL program tha…

> Set theory is your friend and SQL is great for that. Could you tell us how set theory was useful in your case?

Simple example.

INTAKE_TABLE contains 100k records and each record has up to 8 names and addresses. The following SQL statements would perform the name de-duplication, new id assignment and normalization.

  -- initialize
  truncate table STAGING_NAMES_TABLE;

  -- get unique names
  insert /*+ APPEND PARALLEL(STAGING_NAMES_TABLE, 4) */ into 
  STAGING_NAMES_TABLE (name)
  select /*+ PARALLEL(INTAKE_TABLE, 4) */ name1 from INTAKE_TABLE
   union
  select /*+ PARALLEL(INTAKE_TABLE, 4) */ name2 from INTAKE_TABLE
   union
  ...
   union
  select /*+ PARALLEL(INTAKE_TABLE, 4) */ name8 from INTAKE_TABLE;

  commit;

  -- assign new name ids using the name_seq sequence number
  insert /*+ APPEND */ into NAMES_TABLE (name, name_id)
  select name, name_seq.NEXT_VAL from 
   (select name from STAGING_NAMES_TABLE
     minus
    select name from NAMES_TABLE);

  commit;

  -- normalize the names
  insert /*+ APPEND PARALLEL(NORMALIZED_TABLE, 4) */ into NORMALIZED_TABLE (
   rec_id,
   name_id1,
   name_id2,
   ...
   name_id8 )
  select /*+ PARALLEL(SNT, 4) */
   int.rec_id rec_id,
   nt1.name_id name_id1, 
   nt2.name_id name_id2, 
   ..., 
   nt8.name_id name_id8 
  from 
   INTAKE_TABLE int,
   NAMES_TABLE nt1,
   NAMES_TABLE nt2,
   ...
   NAMES_TABLE nt8
  where
   int.name1 = nt1.name and
   int.name2 = nt2.name and
   ...
   int.name8 = nt8.name;

  commit;
The database can do sorting and de-duplication (i.e., the query UNION operation) much much faster than any application code. Even though the INTAKE_TABLE (100k records) are TABLE-SCANNED 8 times, the union query runs quite fast.

The new id generation does a set exclusion (i.e., the MINUS operation) and then generates new sequence numbers for each new unique name and adds the new records to the table.

The normalization (i.e., the name lookup) step joins the NAME_TABLE that now contains the new names and performs the name to id conversion with the join query.

Realizing that the UNION, MINUS and even nasty 8-way joins can be done by the database engine way faster than application code was eye-opening. I never feared table scans after that. Discovering that the reads (i.e., SELECTs) and writes (i.e., INSERTs) can be done in parallel with optimization hints such as the APPEND hint was a superpower.

Using patterns such a TRUNCATE TABLE (for staging tables) at the top of the SQL script made the scripts idempotent. i.e., we could trivially run the script again. The subsequent runs will not generate the new sequence numbers for the names. With some careful organization of the statements, this became rigorous.

Although I haven't shown here, we used to do the entire normalization process in staging tables and finally do a copy over to the main table using a final INSERT / SELECT statement.

My Oracle SQL-fu is a bit rusty. Apologies for any syntax errors.

Re: Cases where full scans are better than indexes

#109
post #68

Just because the table scan is under some threshold doesn't automatically make it better. If a table scan takes 250ms vs 0.01 for a indexed lookup, you're still gonna have to justify to me why making silicon work that damn hard is worth even the electrical use. Are you inserting and deleting so many rows that maintaining the index is prohibitive? Do you have space concerns, and are not able to keep the index in memor…

For small enough tables doing a full scan will be faster than an index. This is also true for regular non-database applications like checking if an item is present in a small collection: it's faster to do a linear scan over a small vector than it is to do a lookup in a hash table or b-tree. With a linear scan (whether it's for data on disk, or a data structure in memory) you don't have to do hashing or branching (exc…

All of this looks accurate, but it's worth contextualizing: this is an optimization that bears the most fruit in "local frames of reference" -- the timescales where linear scans beat index lookups are likely to be strictly dominated by the network latency to transceive from the database. The conclusion is then that the optimization ~only optimizes for cases that effectively don't matter.

Re: Cases where full scans are better than indexes

#110
post #99

Earlier quoted context omitted.

Justify the dev time to save micro-pennies worth of electricity to me instead. A typical naive index won't help with my regular expression based queries, which aren't easily accelerated by an index. Or given an in-memory index, you've just increased memory use from O(1) to O(N), and I'll OOM on large files. Perhaps you'll throw a database at the problem, complicating I/O (especially when the data is generated/accesse…

> Justify the dev time to save micro-pennies worth of electricity to me instead. KEY (user_id) I mean, it's a dozen characters. Do you need to know how fast I type before you run the calculation?

> Do you need to know how fast I type before you run the calculation?

I'll assume 100WPM, call that two words, billed at $200/hour and call that $0.06, falling under "too cheap to be worth arguing against", which falls under the aforementioned:

>> If it'a a 5 second "this is probably the right choice" kneejerk reaction, maybe it's fine.

That said, there's a decent chance those 6 cents won't pay for themselves if this is a login on a single user system, with any theoretical benefits of O(...) scaling being drowned out by extra compile times, extra code to load - and I'd be plenty willing to NAK code reviews that merely attempt to replace /etc/passwd and /etc/shadow with this, as the extra time code reviewing the replacement still has negative expected ROI, and it'll be a lot more involved than a mere dozen characters.

Now, maybe we're attempting to centralize login management with Kerberos or something, perhaps with good reasons, perhaps which does something similar under the hood, and we could talk about that and possible positive ROI, despite some actual downtime and teething concerns?

Post reply on HN