I had someone telling me that:
Ruby can scale. Basecamp prooves that.
Now, you all know that I do not think that Ruby has ANY problems with scaling. However, applications such as basecamp have a huge advantage for scaling: minimal shared data.
The key to scalability is minimizing access to the same data. The less anal you are about how “correct” the data is the better you will be.
Microsoft Word scales very well as when a million people are writing a document, they are not editing the same one (I know, they could be on a shared drive blah blah).
Caching and all of the tricks are ways in which we can cheat the system. We can make copies of our REAL data and access those instead. This works for a lot of applications, and a lot of data. This is why you see “this stock data may be up to 15 minutes old”. Imagine if everyone needed access to the stock price right NOW. I mean NOW. I mean…..
To scale probably, you want to minimize any locking. How stale can your data be in different aspects? The more stale that you can deal with the better.
Where does Basecamp and company fit in here?
One of the great advantages to those applications is that there is little shared data.
If I sign up for an account for my company I can have a large amount of data on fooinc.grouphub.com. Someone else can have barinc.grouphub.com, and no data is shared. I do not need (or should be allowed) access to their data.
This means that if they wanted too, we could be on two different machines with out own MySQL instance. One per user doesn’t make sense of course (unless the users are GE and Ford), but how about splitting up: A-M and N-Z.
If load keeps going up you split again, and again, and again.
If you are in this situation you are a lucky man, and should be able to scale up anything :)
If you have an application where there is more shared data then you may need to get more creative. A lot of web apps are heavy on reads so you can scale up nicely via MySQL replication and putting out more slaves, but at some point the master will not be able to handle the writes. This is when you need to Give the DB a break! and use caching, and tweaking your archicture into pieces.
We do this with out Rails apps by making parts and pieces web services that we can scale up separately, but there is always the bottleneck on some part of the darn data!
May 8th, 2006 at 7:04 pm
Thank god someone finally pointed this out. “[Language] does/does not scale” is the dumbest comment ever. Applications do or do not scale, not the languages.
We are doing the exact approach you mentioned with HostedQA (http://www.hostedqa.com). Because our customers get a sub-domain, scaling is cake!
May 8th, 2006 at 11:50 pm
We have this very problem at our company! The scalability of our application is currently limited by our read-write MySQL master. We do caching and have 2 dedicated read-write slaves. Still, on a couple of very high traffic occasions we’ve had the write operations go high enough that our 10 web/application servers started waiting on the read-write database, then the db connections started backing up waiting for the lock to free, we hit max connections, and down we went.
We’re still using MyISAM (which does table level locking) and I’m hopeful that when we make the switch to InnoDB that load testing will show that InnoDB’s row level locking is much more performant for write operations while still being nearly as performant for reads.
As an aside, on the Java front we discovered that the MySQL JDBC driver offers a nice solution to easily load balance read operations across the slaves using their com.mysql.jdbc.ReplicationDriver: http://gabrito.com/post/load-balancing-across-mysql-servers-using-jdbc
May 9th, 2006 at 2:22 pm
s/out/our/g
May 21st, 2006 at 6:55 am
If you really want to scale and deal with concurrency issues you have to give up on the notion that read and write locks are anything but a hack. When you work out the read write logic in your code and run your code through some scenario testing. You will find that most of you locks are unneccessary. What is needed is transaction begin and commit statements around writes to the database to avoid partially stale data being read. If your writes to multiple tables are atomic your data can be distributed across multiple servers easily without preventing reads. Stale reads are only considered stale until the new data is written. There is never a need to lock a row and definitely not a whole table. Any real database is designed to serve read requests concurrently with writes. Work though the logic and you may be surprised by how most of your reads have almost nothing to do with your writes. The dependent database reads are already synchronized in you current code by their order of occurence after the datbase writes.