<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: mysql</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/mysql.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-07-01T18:16:12+00:00</updated><author><name>Simon Willison</name></author><entry><title>Announcing PlanetScale for Postgres</title><link href="https://simonwillison.net/2025/Jul/1/planetscale-for-postgres/#atom-tag" rel="alternate"/><published>2025-07-01T18:16:12+00:00</published><updated>2025-07-01T18:16:12+00:00</updated><id>https://simonwillison.net/2025/Jul/1/planetscale-for-postgres/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://planetscale.com/blog/planetscale-for-postgres#vitess-for-postgres"&gt;Announcing PlanetScale for Postgres&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
PlanetScale formed in 2018 to build a commercial offering on top of the Vitess MySQL sharding open source project, which was originally released by YouTube in 2012. The PlanetScale founders were the co-creators and maintainers of Vitess.&lt;/p&gt;
&lt;p&gt;Today PlanetScale are announcing a private preview of their new horizontally sharded PostgreSQL solution, due to "overwhelming" demand.&lt;/p&gt;
&lt;p&gt;Notably, it doesn't use Vitess under the hood:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vitess is one of PlanetScale’s greatest strengths [...] We have made explicit sharding accessible to hundreds of thousands of users and it is time to bring this power to Postgres. We will not however be using Vitess to do this.&lt;/p&gt;
&lt;p&gt;Vitess’ achievements are enabled by leveraging MySQL’s strengths and engineering around its weaknesses. To achieve Vitess’ power for Postgres we are architecting from first principles.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Meanwhile, on June 10th Supabase announced that they had &lt;a href="https://supabase.com/blog/multigres-vitess-for-postgres"&gt;hired Vitess co-creator  Sugu Sougoumarane&lt;/a&gt; to help them build "Multigres: Vitess for Postgres". Sugu said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For some time, I've been considering a Vitess adaptation for Postgres, and this feeling had been gradually intensifying. The recent explosion in the popularity of Postgres has fueled this into a full-blown obsession. [...]&lt;/p&gt;
&lt;p&gt;The project to address this problem must begin now, and I'm convinced that Vitess provides the most promising foundation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I remember when MySQL was an order of magnitude more popular than PostgreSQL, and Heroku's decision to only offer PostgreSQL back in 2007 was a surprising move. The vibes have certainly shifted.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sharding"&gt;sharding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vitess"&gt;vitess&lt;/a&gt;&lt;/p&gt;



</summary><category term="databases"/><category term="mysql"/><category term="postgresql"/><category term="scaling"/><category term="sharding"/><category term="vitess"/></entry><entry><title>Announcing DuckDB 0.10.0</title><link href="https://simonwillison.net/2024/Feb/13/duckdb-0100/#atom-tag" rel="alternate"/><published>2024-02-13T17:57:17+00:00</published><updated>2024-02-13T17:57:17+00:00</updated><id>https://simonwillison.net/2024/Feb/13/duckdb-0100/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://duckdb.org/2024/02/13/announcing-duckdb-0100.html"&gt;Announcing DuckDB 0.10.0&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Somewhat buried in this announcement: DuckDB has Fixed-Length Arrays now, along with &lt;code&gt;array_cross_product(a1, a2)&lt;/code&gt;, &lt;code&gt;array_cosine_similarity(a1, a2)&lt;/code&gt; and &lt;code&gt;array_inner_product(a1, a2)&lt;/code&gt; functions.&lt;/p&gt;
&lt;p&gt;This means you can now use DuckDB to find related content (and other tricks) using vector embeddings!&lt;/p&gt;
&lt;p&gt;Also notable:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DuckDB can now attach MySQL, Postgres, and SQLite databases in addition to databases stored in its own format. This allows data to be read into DuckDB and moved between these systems in a convenient manner, as attached databases are fully functional, appear just as regular tables, and can be updated in a safe, transactional manner.&lt;/p&gt;
&lt;/blockquote&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/duckdb"&gt;duckdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/embeddings"&gt;embeddings&lt;/a&gt;&lt;/p&gt;



</summary><category term="databases"/><category term="mysql"/><category term="postgresql"/><category term="sql"/><category term="sqlite"/><category term="duckdb"/><category term="embeddings"/></entry><entry><title>Upgrading GitHub.com to MySQL 8.0</title><link href="https://simonwillison.net/2023/Dec/10/upgrading-github-to-mysql-8/#atom-tag" rel="alternate"/><published>2023-12-10T20:36:23+00:00</published><updated>2023-12-10T20:36:23+00:00</updated><id>https://simonwillison.net/2023/Dec/10/upgrading-github-to-mysql-8/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.blog/2023-12-07-upgrading-github-com-to-mysql-8-0/"&gt;Upgrading GitHub.com to MySQL 8.0&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I love a good zero-downtime upgrade story, and this is a fine example of the genre. GitHub spent a year upgrading MySQL from 5.7 to 8 across 1200+ hosts, covering 300+ TB that was serving 5.5 million queries per second. The key technique was extremely carefully managed replication, plus tricks like leaving enough 5.7 replicas available to handle a rollback should one be needed.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/yqwasc/upgrading_github_com_mysql_8_0"&gt;lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ops"&gt;ops&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/replication"&gt;replication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zero-downtime"&gt;zero-downtime&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="mysql"/><category term="ops"/><category term="replication"/><category term="zero-downtime"/></entry><entry><title>Scaling Datastores at Slack with Vitess</title><link href="https://simonwillison.net/2020/Dec/1/scaling-datastores-slack-vitess/#atom-tag" rel="alternate"/><published>2020-12-01T21:30:26+00:00</published><updated>2020-12-01T21:30:26+00:00</updated><id>https://simonwillison.net/2020/Dec/1/scaling-datastores-slack-vitess/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://slack.engineering/scaling-datastores-at-slack-with-vitess/"&gt;Scaling Datastores at Slack with Vitess&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Slack spent three years migrating 99% of their MySQL query load to run against Vitess, the open source MySQL sharding system originally built by YouTube. “Today, we serve 2.3 million QPS at peak. 2M of those queries are reads and 300K are writes. Our median query latency is 2 ms, and our p99 query latency is 11 ms.”

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/zmagg/status/1333834229713539072"&gt;Maggie Zhou&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sharding"&gt;sharding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/youtube"&gt;youtube&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/slack"&gt;slack&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vitess"&gt;vitess&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="scaling"/><category term="sharding"/><category term="youtube"/><category term="slack"/><category term="vitess"/></entry><entry><title>Generated Columns in SQLite</title><link href="https://simonwillison.net/2020/Jan/24/generated-columns-sqlite/#atom-tag" rel="alternate"/><published>2020-01-24T04:20:35+00:00</published><updated>2020-01-24T04:20:35+00:00</updated><id>https://simonwillison.net/2020/Jan/24/generated-columns-sqlite/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.sqlite.org/gencol.html"&gt;Generated Columns in SQLite&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
SQLite 3.31.0 released today, and generated columns are the single most notable new feature. PostgreSQL 12 added these in October 2019, and MySQL has had them since 5.7 in October 2015. MySQL and SQLite both offer either “stored” or “virtual” generated columns, with virtual columns being calculated at runtime. PostgreSQL currently only supports stored columns.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://www.sqlite.org/releaselog/3_31_0.html"&gt;SQLite Release 3.31.0&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="postgresql"/><category term="sql"/><category term="sqlite"/></entry><entry><title>db-to-sqlite 1.0 release</title><link href="https://simonwillison.net/2019/Jul/1/db-to-sqlite/#atom-tag" rel="alternate"/><published>2019-07-01T01:35:40+00:00</published><updated>2019-07-01T01:35:40+00:00</updated><id>https://simonwillison.net/2019/Jul/1/db-to-sqlite/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/db-to-sqlite/releases/tag/1.0"&gt;db-to-sqlite 1.0 release&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I’ve released version 1.0 of my db-to-sqlite tool, which lets you create a SQLite database copy of any database supported by SQLAlchemy (I’ve tested it against MySQL and PostgreSQL). The tool has a bunch of new features: you can use --redact to redact specific columns, specify --table multiple times to copy a subset of tables, and the --all option now efficiently adds all foreign keys at the end of the import. The project now has unit tests which run against MySQL and PostgreSQL in Travis CI. Also included in the README: a shell one-liner for creating a local SQLite copy of a remote Heroku Postgres database based on extracting the connection string from a Heroku config environment variable.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/heroku"&gt;heroku&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="postgresql"/><category term="projects"/><category term="sqlite"/><category term="heroku"/><category term="datasette"/></entry><entry><title>MySQL: How to get the top N rows for each group</title><link href="https://simonwillison.net/2019/Mar/4/mysql-how-get-top-n-rows-each-group/#atom-tag" rel="alternate"/><published>2019-03-04T23:38:24+00:00</published><updated>2019-03-04T23:38:24+00:00</updated><id>https://simonwillison.net/2019/Mar/4/mysql-how-get-top-n-rows-each-group/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.sqlines.com/mysql/how-to/get_top_n_each_group"&gt;MySQL: How to get the top N rows for each group&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
MySQL doesn’t support the row_number() window function that’s available in PostgreSQL (and recent SQLite), which means it can’t easily answer questions like “for each of these authors, give me the most recent three blog entries they have written” in a single query. Only it turns out it can, if you abuse MySQL session variables in a devious way. This isn’t a new feature: MySQL has had this for over a decade, and in my rough testing it works quickly even on tables with millions of rows.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/></entry><entry><title>Vitess</title><link href="https://simonwillison.net/2019/Feb/14/vitess/#atom-tag" rel="alternate"/><published>2019-02-14T05:35:41+00:00</published><updated>2019-02-14T05:35:41+00:00</updated><id>https://simonwillison.net/2019/Feb/14/vitess/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://vitess.io/"&gt;Vitess&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I remember looking at Vitess when it was first released by YouTube in 2012. The idea of a proven horizontally scalable sharding mechanism for MySQL was exciting, but I was put off by the need for a custom Go or Java client library. Apparently that changed with Vitess 2.1 in April 2017, the first version to introduce a MySQL protocol compatible proxy which can be connected to by existing code written in any language. Vitess 3.0 came out last December so now the MySQL proxy layer is much more stable. Vitess is used in production by a bunch of other companies now (including Slack and Square) so it’s definitely worth a closer look.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://www.xaprb.com/blog/vitess/"&gt;Baron Schwartz&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sharding"&gt;sharding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/youtube"&gt;youtube&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/slack"&gt;slack&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vitess"&gt;vitess&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="scaling"/><category term="sharding"/><category term="youtube"/><category term="slack"/><category term="vitess"/></entry><entry><title>Migrating Messenger storage to optimize performance</title><link href="https://simonwillison.net/2018/Jun/27/migrating-messenger-storage-optimize-performance/#atom-tag" rel="alternate"/><published>2018-06-27T15:05:36+00:00</published><updated>2018-06-27T15:05:36+00:00</updated><id>https://simonwillison.net/2018/Jun/27/migrating-messenger-storage-optimize-performance/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://code.facebook.com/posts/201318390519340"&gt;Migrating Messenger storage to optimize performance&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fascinating case-study of a truly gargantuan migration. Messenger has over a billion users, and Facebook successfully migrated its backend storage from HBase to their MyRocks database (a fork of MySQL with a storage engine built on their SSD-optimized RocksDB key/value library) without any user-visible downtime. They ended up using two migration paths: one for the 99.9% of regular accounts, and a separate path for extremely high volume accounts (businesses with very active chat bots or support systems).

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=17402241"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/facebook"&gt;facebook&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/migration"&gt;migration&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zero-downtime"&gt;zero-downtime&lt;/a&gt;&lt;/p&gt;



</summary><category term="facebook"/><category term="migration"/><category term="mysql"/><category term="scaling"/><category term="zero-downtime"/></entry><entry><title>MySQL High Availability at GitHub</title><link href="https://simonwillison.net/2018/Jun/20/mysql-high-availability-github/#atom-tag" rel="alternate"/><published>2018-06-20T23:05:29+00:00</published><updated>2018-06-20T23:05:29+00:00</updated><id>https://simonwillison.net/2018/Jun/20/mysql-high-availability-github/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://githubengineering.com/mysql-high-availability-at-github/"&gt;MySQL High Availability at GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Cutting edge high availability case-study: GitHub are now using Consul, raft, their own custom load balancer and their own custom orchestrator replication management toolkit to achieve cross-datacenter failover for their MySQL master/replica clusters.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/highavailability"&gt;highavailability&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shlominoach"&gt;shlominoach&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="highavailability"/><category term="mysql"/><category term="scaling"/><category term="shlominoach"/></entry><entry><title>github/gh-ost: Thoughts on Foreign Keys?</title><link href="https://simonwillison.net/2018/Jun/19/thoughts-on-foreign-keys/#atom-tag" rel="alternate"/><published>2018-06-19T16:12:42+00:00</published><updated>2018-06-19T16:12:42+00:00</updated><id>https://simonwillison.net/2018/Jun/19/thoughts-on-foreign-keys/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/github/gh-ost/issues/331"&gt;github/gh-ost: Thoughts on Foreign Keys?&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The biggest challenge I’ve seen with foreign key constraints at scale (at least with MySQL) is how they conflict with online schema migrations using tools like pt-online-schema-change or GitHub’s gh-ost. This is a good explanation of the issue by Shlomi Noach, one of the gh-ost maintainers.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shlominoach"&gt;shlominoach&lt;/a&gt;&lt;/p&gt;



</summary><category term="databases"/><category term="mysql"/><category term="scaling"/><category term="sql"/><category term="shlominoach"/></entry><entry><title>mycli</title><link href="https://simonwillison.net/2018/Jun/11/mycli/#atom-tag" rel="alternate"/><published>2018-06-11T19:08:00+00:00</published><updated>2018-06-11T19:08:00+00:00</updated><id>https://simonwillison.net/2018/Jun/11/mycli/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/dbcli/mycli"&gt;mycli&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Really neat auto-complete enabled MySQL terminal client, built using the excellent python-prompt-toolkit. Has a sister-project for PostgreSQL called pgcli.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="postgresql"/><category term="python"/></entry><entry><title>Showdown: MySQL 8 vs PostgreSQL 10</title><link href="https://simonwillison.net/2018/May/23/showdown-mysql-8-vs-postgresql-10/#atom-tag" rel="alternate"/><published>2018-05-23T17:02:40+00:00</published><updated>2018-05-23T17:02:40+00:00</updated><id>https://simonwillison.net/2018/May/23/showdown-mysql-8-vs-postgresql-10/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.dumper.io/showdown-mysql-8-vs-postgresql-10/"&gt;Showdown: MySQL 8 vs PostgreSQL 10&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
MySQL 8 makes comparisons between PostgreSQL and MySQL far more interesting, as it closes some of the key feature gaps. Meanwhile the PostgreSQL replication story (long one of MySQL’s key advantages) has improved dramatically in recent versions. This article offers a useful overview of the current differences, including diving into some of the less obvious implementation details that differ between the two.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;&lt;/p&gt;



</summary><category term="databases"/><category term="mysql"/><category term="postgresql"/></entry><entry><title>How to number rows in MySQL</title><link href="https://simonwillison.net/2018/May/16/how-number-rows-mysql/#atom-tag" rel="alternate"/><published>2018-05-16T21:06:57+00:00</published><updated>2018-05-16T21:06:57+00:00</updated><id>https://simonwillison.net/2018/May/16/how-number-rows-mysql/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/"&gt;How to number rows in MySQL&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
MySQL’s user variables can be used to add a “rank” or “row_number” column to a database query that shows the ranking of a row against a specific unique value. This means you can return the first N rows for any given column—for example, given a list of articles return just the first three tags for each article. I’ve recently found myself using this trick for a few different things—once you know it, chances to use it crop up surprisingly often.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/></entry><entry><title>What’s New in MySQL 8.0</title><link href="https://simonwillison.net/2018/Apr/19/whats-new-mysql-80/#atom-tag" rel="alternate"/><published>2018-04-19T16:03:49+00:00</published><updated>2018-04-19T16:03:49+00:00</updated><id>https://simonwillison.net/2018/Apr/19/whats-new-mysql-80/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://mysqlserverteam.com/whats-new-in-mysql-8-0-generally-available/"&gt;What’s New in MySQL 8.0&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
MySQL 8 has lots of exciting improvements: Window functions, SRS aware spatial types for GIS, utf8mb4 by default, a ton of JSON improvements and atomic DDL. I no longer feel at a significant disadvantage when I have to use MySQL in place of PostgreSQL.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/></entry><entry><title>What are the key insights in mastering SQL queries?</title><link href="https://simonwillison.net/2013/Nov/8/what-are-the-key/#atom-tag" rel="alternate"/><published>2013-11-08T09:16:00+00:00</published><updated>2013-11-08T09:16:00+00:00</updated><id>https://simonwillison.net/2013/Nov/8/what-are-the-key/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-key-insights-in-mastering-SQL-queries/answer/Simon-Willison"&gt;What are the key insights in mastering SQL queries?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You may find this article useful (despite the list-o-matic name): &lt;span&gt;&lt;a href="http://tech.pro/tutorial/1555/10-easy-steps-to-a-complete-understanding-of-sql"&gt;10 Easy Steps to a Complete Understanding of SQL&lt;/a&gt;&lt;/span&gt; - I've been using SQL for years but I found that some of the concepts explained there helped firm up my fundamental understanding of how to use it effectively.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/oracle"&gt;oracle&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="mysql"/><category term="oracle"/><category term="sql"/><category term="quora"/></entry><entry><title>How was FriendFeed's schema less db faster than pure MySQL?</title><link href="https://simonwillison.net/2013/Oct/30/how-was-friendfeeds-schema/#atom-tag" rel="alternate"/><published>2013-10-30T16:27:00+00:00</published><updated>2013-10-30T16:27:00+00:00</updated><id>https://simonwillison.net/2013/Oct/30/how-was-friendfeeds-schema/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-was-FriendFeeds-schema-less-db-faster-than-pure-MySQL?no_redirect=1"&gt;How was FriendFeed&amp;#39;s schema less db faster than pure MySQL?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The principle reason they switched to a schemaless DB was to work around the challenges of having to make schemes changes in MySQL, which can lock the table and take hours if bit days to complete in large tables.&lt;/p&gt;

&lt;p&gt;The performance improvement shown in the graph is almost certainly because they almost entirely eliminated joins and complex queries when they switched to the new mechanism. This meant that all it their database traffic was now simple queries, which have much more predictable performance characteristics. MySQL (in fact all databases) are extremely fast at primary key lookups and index scans.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/friendfeed"&gt;friendfeed&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rdbms"&gt;rdbms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="friendfeed"/><category term="mysql"/><category term="nosql"/><category term="quora"/><category term="rdbms"/></entry><entry><title>Is there a maximum number of records one can fetch with a MySQL query?</title><link href="https://simonwillison.net/2013/Sep/23/is-there-a-maximum/#atom-tag" rel="alternate"/><published>2013-09-23T12:48:00+00:00</published><updated>2013-09-23T12:48:00+00:00</updated><id>https://simonwillison.net/2013/Sep/23/is-there-a-maximum/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Is-there-a-maximum-number-of-records-one-can-fetch-with-a-MySQL-query/answer/Simon-Willison"&gt;Is there a maximum number of records one can fetch with a MySQL query?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To my knowledge there is no upper limit - that's why good database libraries provide abstractions that let you iterate over large queries without loading the entire result set in to memory at once.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="quora"/></entry><entry><title>Should I store markdown instead of HTML into database fields?</title><link href="https://simonwillison.net/2013/Sep/8/should-i-store-markdown/#atom-tag" rel="alternate"/><published>2013-09-08T15:57:00+00:00</published><updated>2013-09-08T15:57:00+00:00</updated><id>https://simonwillison.net/2013/Sep/8/should-i-store-markdown/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Should-I-store-markdown-instead-of-HTML-into-database-fields/answer/Simon-Willison"&gt;Should I store markdown instead of HTML into database fields?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You should store the exact format that was entered by the user.&lt;/p&gt;

&lt;p&gt;- This lets you offer an "edit" feature without round-tripping between two formats.&lt;br /&gt;- This makes debugging much easier&lt;br /&gt;- Related: if you need to investigate a security bug, having the original input is essential.&lt;/p&gt;

&lt;p&gt;If you're worried about performance, you can cache the transformed HTML somewhere - or even denormalize it to an extra table column. Just make sure you always have the original input available.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cms"&gt;cms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/markdown"&gt;markdown&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cms"/><category term="databases"/><category term="html"/><category term="mysql"/><category term="quora"/><category term="markdown"/></entry><entry><title>How can I detect manual record insert from mysql cansole into my code in django .?</title><link href="https://simonwillison.net/2012/Dec/28/how-can-i-detect/#atom-tag" rel="alternate"/><published>2012-12-28T14:31:00+00:00</published><updated>2012-12-28T14:31:00+00:00</updated><id>https://simonwillison.net/2012/Dec/28/how-can-i-detect/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-can-I-detect-manual-record-insert-from-mysql-cansole-into-my-code-in-django/answer/Simon-Willison"&gt;How can I detect manual record insert from mysql cansole into my code in django .?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can't. The best you can do is have Django periodically poll MySQL to see if anything has changed (maybe with a custom management command run by cron) - having a TIMESTAMP field on every table which will be automatically set to the current time when a record is inserted will help you spot things that have changed.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="django"/><category term="mysql"/><category term="web-development"/><category term="quora"/></entry><entry><title>What tools and techniques are used for relational database version control (structure and data)?</title><link href="https://simonwillison.net/2012/Dec/24/what-tools-and-techniques/#atom-tag" rel="alternate"/><published>2012-12-24T12:29:00+00:00</published><updated>2012-12-24T12:29:00+00:00</updated><id>https://simonwillison.net/2012/Dec/24/what-tools-and-techniques/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-tools-and-techniques-are-used-for-relational-database-version-control-structure-and-data/answer/Simon-Willison"&gt;What tools and techniques are used for relational database version control (structure and data)?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The term you are looking for is database migrations (sometimes called database change scripts).&lt;/p&gt;

&lt;p&gt;The basic concept is pretty straight forward: you set up a table in the database that records which change scripts have already been applied. When you need to make a change (adding a column, adding a table, denormalising some data for performance reasons, adding an index etc) you write a change script that applies the change - in raw SQL or in another programming language, depending on how your migration system is set up.&lt;/p&gt;

&lt;p&gt;These change scripts (let's call them migrations from here) are numbered so they can be applied in the correct order. Then you run a command which checks for scripts that have not yet been applied and runs them in the correct order - then records that they have been run to the relevant database table.&lt;/p&gt;

&lt;p&gt;The setup I've described above is a pretty good start. Some systems let you have reversible migrations: each migration includes instructions for reversing its effect (removing the index that was added, moving data back to its old location) which lets you run a command to revert back to a previous database state. In practise this is a nice-to-have but not essential: many migrations are by their nature irreversible, but it can make development faster if you can easily try out and then revert a database structure change within your development environment.&lt;/p&gt;

&lt;p&gt;Really clever migration systems can even introspect your database, figure out what has changed and attempt to generate the migration scripts automatically! South, the most popular migration system for Django, does this with surprisingly good results for many cases.&lt;/p&gt;

&lt;p&gt;If you're interested in learning more, it's worth reading through the South documentation: &lt;span&gt;&lt;a href="http://south.readthedocs.org/en/latest/"&gt;http://south.readthedocs.org/en/...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/oracle"&gt;oracle&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rdbms"&gt;rdbms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="mysql"/><category term="oracle"/><category term="postgresql"/><category term="sql"/><category term="quora"/><category term="rdbms"/></entry><entry><title>Any source available to download sample data (in 10+ GB) for testing?</title><link href="https://simonwillison.net/2012/Oct/15/any-source-available-to/#atom-tag" rel="alternate"/><published>2012-10-15T13:21:00+00:00</published><updated>2012-10-15T13:21:00+00:00</updated><id>https://simonwillison.net/2012/Oct/15/any-source-available-to/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Any-source-available-to-download-sample-data-in-10+-GB-for-testing/answer/Simon-Willison"&gt;Any source available to download sample data (in 10+ GB) for testing?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wikipedia has some pretty interesting dumps, in both XML and SQL format: &lt;span&gt;&lt;a href="http://meta.wikimedia.org/wiki/Importing_a_Wikipedia_database_dump_into_MediaWiki"&gt;http://meta.wikimedia.org/wiki/I...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;It's pretty easy to generate 10GB of random data for testing though, which may be a better option as you could better approximate the kind of data your application will be dealing with. There's a neat Ruby module for doing this called Faker (itself a port of the Perl module of the same name): &lt;span&gt;&lt;a href="http://faker.rubyforge.org/"&gt;http://faker.rubyforge.org/&lt;/a&gt;&lt;/span&gt; - and here's a Python port of the Ruby one: &lt;span&gt;&lt;a href="https://github.com/threadsafelabs/python-faker"&gt;https://github.com/threadsafelab...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rdbms"&gt;rdbms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="nosql"/><category term="programming"/><category term="web-development"/><category term="quora"/><category term="rdbms"/></entry><entry><title>What is the most efficient way to lookup an object (e.g. a user) by only a string?</title><link href="https://simonwillison.net/2012/May/31/what-is-the-most/#atom-tag" rel="alternate"/><published>2012-05-31T17:27:00+00:00</published><updated>2012-05-31T17:27:00+00:00</updated><id>https://simonwillison.net/2012/May/31/what-is-the-most/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-is-the-most-efficient-way-to-lookup-an-object-e-g-a-user-by-only-a-string/answer/Simon-Willison"&gt;What is the most efficient way to lookup an object (e.g. a user) by only a string?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes - an index on a varchar column is exactly how you would implement this.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/twitter"&gt;twitter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/urls"&gt;urls&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="twitter"/><category term="urls"/><category term="quora"/></entry><entry><title>Python Django load MySQL database from csv files performance issue?</title><link href="https://simonwillison.net/2012/Feb/12/python-django-load-mysql/#atom-tag" rel="alternate"/><published>2012-02-12T11:02:00+00:00</published><updated>2012-02-12T11:02:00+00:00</updated><id>https://simonwillison.net/2012/Feb/12/python-django-load-mysql/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Python-Django-load-MySQL-database-from-csv-files-performance-issue/answer/Simon-Willison"&gt;Python Django load MySQL database from csv files performance issue?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Don't use the Django ORM for bulk imports - the performance overhead is pretty small for regular web page stuff, but it adds up if you are running millions of inserts.&lt;/p&gt;

&lt;p&gt;Either write your import code to call MySQLdb directly or use the mysqlimport  command line tool.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="django"/><category term="mysql"/><category term="python"/><category term="quora"/></entry><entry><title>How can you build a search engine for a website built in PHP/MySQL?</title><link href="https://simonwillison.net/2012/Feb/11/how-can-you-build/#atom-tag" rel="alternate"/><published>2012-02-11T18:39:00+00:00</published><updated>2012-02-11T18:39:00+00:00</updated><id>https://simonwillison.net/2012/Feb/11/how-can-you-build/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-can-you-build-a-search-engine-for-a-website-built-in-PHP-MySQL/answer/Simon-Willison"&gt;How can you build a search engine for a website built in PHP/MySQL?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are a bunch of options.&lt;/p&gt;

&lt;p&gt;The easiest to implement is to build search on top of MySQL LIKE queries - performance will be pretty terrible (since every search will require a full table scan) but provided your tables only have a few thousand records on them and your site doesn't have to cope with more than a dozen or so hits a second it should work fine.&lt;/p&gt;

&lt;p&gt;Next easiest: use MySQL's built-in full text indexing feature. It's not particularly good, and it requires you to use MyISAM tables (InnoDB is much more reliable, but doesn't support full text indexing) - but it will do the job. You could always keep your main site data in InnoDB and denormalise in to a MyISAM table just for search - or you could use the trick Flickr used to use, which is to set up MySQL replication and run MyISAM on one of the slaves purely to support fulltext search.&lt;/p&gt;

&lt;p&gt;Past that, you're looking at adding another component to the stack. Sphinx can integrate directly with MySQL and lets you run SQL-style queries against a proper full text index. Personally I'm a big fan of Solr, which runs as a separate (Java) server and requires you to index documents over HTTP. The great thing about Solr is that you can talk to it from any language that has an HTTP client library.&lt;/p&gt;

&lt;p&gt;The last option is to go for a hosted solution. Google Custom Search is free, but not particularly flexible. IndexTank was a good option here but they were acquired by LinkedIn and are shutting down the hosted service - they've since open sourced their software and other companies such as &lt;span&gt;&lt;a href="http://www.searchify.com/"&gt;http://www.searchify.com/&lt;/a&gt;&lt;/span&gt; are starting to offer it as a hosted solution.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/search-engines"&gt;search-engines&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-search"&gt;sphinx-search&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="php"/><category term="search-engines"/><category term="sphinx-search"/><category term="quora"/></entry><entry><title>What are XML feed best practices?</title><link href="https://simonwillison.net/2012/Jan/31/what-are-xml-feed/#atom-tag" rel="alternate"/><published>2012-01-31T14:29:00+00:00</published><updated>2012-01-31T14:29:00+00:00</updated><id>https://simonwillison.net/2012/Jan/31/what-are-xml-feed/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-XML-feed-best-practices/answer/Simon-Willison"&gt;What are XML feed best practices?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It sounds like you're pretty much screwed already, if you're dealing with companies that still think FTPing XML around is a sensible thing to do.&lt;/p&gt;

&lt;p&gt;I would suggest focusing on what you can control. Assume that you will be passed bad data - weird formats, not-well-formed XML, duplicate entries etc. Your job is to handle all of this without going mad, and without your codebase turning in to an unmanageable ball of mud.&lt;/p&gt;

&lt;p&gt;So, start by figuring out your own core data model / abstraction. It will need to be VERY loose - as few required fields as possible, since you can be sure some if the feeds you are consuming will come in with stuff missing at some point or another.&lt;/p&gt;

&lt;p&gt;Separate your feed consumers from the rest of your code. Having your own good internal Web API (which could consume JSON rather than XML since you control it) might be smart, since that will provide a solid separation and you can then write all of your feed consumers as separate pieces of code that just POST new items to the API.&lt;/p&gt;

&lt;p&gt;Learn to love, respect and cherish unique identifiers... but be very wary of supposedly unique identifiers from external sources unless you can be absolutely sure they won't change on you. Create your own unique IDs at the first available opportunity, treat them properly within your own system and map external identifiers to them whenever you can.&lt;/p&gt;

&lt;p&gt;Write your consumers in a dynamic language with a solid interactive prompt, like Python or Ruby. This will make them much easier to write and debug. Use whatever you like for your core data storage / API.&lt;/p&gt;

&lt;p&gt;Since your incoming data will come in all shapes and sizes, consider a document store such as MongoDB or Riak over a SQL database. Avoiding SQL migrations will help you out a lot.&lt;/p&gt;

&lt;p&gt;Log and store absolutely everything. Ideally you should be able to re-execute every import that the system has ever executed, in order, to make debugging and fixing errors non terrifying. That will almost certainly prove impossible, but it's a nice thought.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xml"&gt;xml&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="mysql"/><category term="php"/><category term="xml"/><category term="quora"/></entry><entry><title>Has anyone implemented a message queue with mysql and many workers?</title><link href="https://simonwillison.net/2012/Jan/3/has-anyone-implemented-a/#atom-tag" rel="alternate"/><published>2012-01-03T11:11:00+00:00</published><updated>2012-01-03T11:11:00+00:00</updated><id>https://simonwillison.net/2012/Jan/3/has-anyone-implemented-a/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Has-anyone-implemented-a-message-queue-with-mysql-and-many-workers/answer/Simon-Willison"&gt;Has anyone implemented a message queue with mysql and many workers?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Flickr built their own message queue on top of MySQL: &lt;span&gt;&lt;a href="http://code.flickr.com/blog/2008/09/26/flickr-engineers-do-it-offline/"&gt;http://code.flickr.com/blog/2008...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/flickr"&gt;flickr&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="flickr"/><category term="mysql"/><category term="quora"/></entry><entry><title>Is a relational database with many-to-many relationships difficult to develop into a web app?</title><link href="https://simonwillison.net/2011/Feb/8/is-a-relational-database/#atom-tag" rel="alternate"/><published>2011-02-08T18:28:00+00:00</published><updated>2011-02-08T18:28:00+00:00</updated><id>https://simonwillison.net/2011/Feb/8/is-a-relational-database/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Is-a-relational-database-with-many-to-many-relationships-difficult-to-develop-into-a-web-app/answer/Simon-Willison"&gt;Is a relational database with many-to-many relationships difficult to develop into a web app?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many to Many tables can be a bit of a pain to deal with using regular SQL, but a good ORM can abstract away any potential complexity almost entirely. I find using the Django ORM means I'm much less likely to shy away from a design that involves a many-to-many relationship because I know it won't increase the complexity of the application. I imagine the Rails ORM has the same effect.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webapps"&gt;webapps&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rdbms"&gt;rdbms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="google"/><category term="mysql"/><category term="webapps"/><category term="quora"/><category term="rdbms"/></entry><entry><title>What are the pros and cons of switching from MySQL to one of the NoSQL databases?</title><link href="https://simonwillison.net/2011/Jan/6/what-are-the-pros/#atom-tag" rel="alternate"/><published>2011-01-06T16:48:00+00:00</published><updated>2011-01-06T16:48:00+00:00</updated><id>https://simonwillison.net/2011/Jan/6/what-are-the-pros/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-pros-and-cons-of-switching-from-MySQL-to-one-of-the-NoSQL-databases/answer/Simon-Willison"&gt;What are the pros and cons of switching from MySQL to one of the NoSQL databases?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pro: If your own benchmarks tell you you need to switch to a specific NoSQL solution, you'll know exactly what the pro is.&lt;/p&gt;

&lt;p&gt;Pro: If you're doing something that's hard to model in a regular schema you might find it easier to use a document database such as CouchDB or MongoDB.&lt;/p&gt;

&lt;p&gt;Pro: Depending on how you approach the problem, you may find NoSQL makes schema modifications a LOT less painful than using a relational database.&lt;/p&gt;

&lt;p&gt;Con: For many projects, losing out on the relational model is a big disadvantage. Most NoSQL solutions require you to design your data storage with your queries in mind. When you are building a product you often don't know what kind of queries you are going to run. This has bitten me with AppEngine projects in the past. See also &lt;span&gt;&lt;a href="https://www.quora.com/What-did-Marissa-Mayer-mean-when-she-said-that-Orkut-failed-because-of-infrastructure-issues/answer/Edmond-Lau"&gt;Edmond Lau's answer to What did Marissa Mayer mean when she said that Orkut failed because of "infrastructure issues"?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;For my money, the smart way of taking advantage of NoSQL is in conjunction with a relational engine. Use a regular database for your core data, but take advantage of Redis or MongoDB for things like counters, smart caches, rolling log storage etc. Polyglot persistence is the way to go.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="nosql"/><category term="quora"/></entry><entry><title>Using MySQL as a NoSQL - A story for exceeding 750,000 qps on a commodity server</title><link href="https://simonwillison.net/2010/Oct/27/yoshinori/#atom-tag" rel="alternate"/><published>2010-10-27T23:10:00+00:00</published><updated>2010-10-27T23:10:00+00:00</updated><id>https://simonwillison.net/2010/Oct/27/yoshinori/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html"&gt;Using MySQL as a NoSQL - A story for exceeding 750,000 qps on a commodity server&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Very interesting approach: much of the speed difference between MySQL/InnoDB and memcached is due to the overhead involved in parsing and processing SQL, so the team at DeNA wrote their own MySQL plugin, HandlerSocket, which exposes a NoSQL-style network protocol for directly calling the low level MySQL storage engine APIs—resulting in a 7.5x performance increase.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="nosql"/><category term="scaling"/><category term="recovered"/></entry></feed>