95% of the work is on the developer's side, 5% is on the infrastructure side. You can't do anything at all when you are dealing with commodity third party software unless you want to take over their maintenance.
That's why I said that it all starts with the developers.
It's easy to do a bunch of JOINs on a number of tables. However, this tells MySQL to create a temporary table that's N1 x N2 x N3 x ... Nn rows big where N1 to Nn is the number of rows on each joined table. If you join three tables with 1000 rows each you ask MySQL to create a temporary table with one billion rows. As you understand, this is doubleplusungood.
MySQL 5.7 and later offer a solution to that: subqueries with EXISTS, lateral derived tables, ROW subqueries, directly querying JSON data etc. Used correctly, these can reduce the database server load by orders of magnitude.
Of course, since we're writing PHP software, sometimes it's more efficient writing a query which returns a small subset and use that to create a new query with the key values of the subset coded into it. While it sounds like a Bad Thing To Do if you ask SQL purists, the reality is that this may be faster than a subquery, and possibly far easier to derive than an optimised subquery. This is especially true when you hit edge cases where the query parsing in MySQL and MariaDB result in diametrically opposite performance profiles with the same query.
Again, this is a problem on the software developer's side. If the software developer is completely oblivious to the inner workings of the database they can never write software with good performance. I don't claim to be a database administrator, or some SQL savant. I just know enough to understand where the bottlenecks are in my SQL code and figure out how to make it substantially better (and I suspect not ideal; I am aware of how much I don't know).
This means that unless you use software written by developers who have paid attention to performance you'll never get a great page load speed. At this point you have to consider whether your site is mostly static, in which case you can use a caching CDN (e.g. CloudFlare), move non-critical dynamic parts such as login boxes outside your main pages and into submenus etc. If you can live with your frontend not updating faster than once in a few minutes you can both reduce your server load impressively, and make your page load times fantastic – at least as far as the TTFB (time to first byte) is concerned.
Then you need to get rid of any premade template and template framework, coding your own in a way that optimises both the server-side page generation time, and the frontend page rendering time. Just by using CSS font swapping, reducing (if not eliminating) the liberal use of JS libraries, and optimising your LCP (largest content paintful) can do wonders.
The last two parts are, indeed, under your control. The thing is, they require substantial investment of time and effort. If page load speeds are critical for your use case it's very much worth it. If you have, dunno, a blog that's visited by a few thousand people a month, nah, it's not worth it beyond exercising your skills (that's basically what I did with my own blog, for exactly this reason).
Nicholas K. Dionysopoulos
Lead Developer and Director
🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!