This is an overview of different approaches to caching in a distributed application environment. Distributed N-Tier applications generally have at least two server farms: web servers and application servers.
Web servers, of course, can cache entire HTTP responses, using, for example, ASP.NET OutputCache page directive. This is a blunt tool, though. It can result in high memory load and impact page processing logic. Sometimes it's not applicable at all. In these cases, application data - in form of objects - should be cached instead. Naturally, this is the only caching option for application servers.
Application data that we need to cache can be either static or dynamic. I'm not suggesting that static data doesn't change at all (otherwise we could just build it into the application), only that it changes very infrequently. Static data can be loaded into cache on every server (let's call it isolated cache). We get the benefit of fast reading, because data is always stored locally. On the other hand, it is heavily duplicated - every single server has to have a copy, which may be a waste of memory. Here's another drawback of isolated cache: imagine several servers joining the farm at the same time. How stressful it will be for the database server while they are filling up their respective isolated caches?
Caching dynamic data is much more complicated, because any server may need to modify it at any time. First thing that comes to mind is to use a common caching data store, such as a database or dedicated server (let's call it centralized cache). For example, ASP.NET allows you to have centralized data store for session state. Unfortunately, centralized mechanism always creates a single point of failure, so it may not be a good solution depending on your availability requirements. Another drawbacks of centralized cache are generally reduced performance and scalability.
A good alternative to centralized cache is distributed cache, which assumes some kind of communication and coordination among the servers. Distributed cache comes in two essential flavors: fully replicated and partitioned. In the fully replicated architecture once application puts an object into local cache on one server, it is immediately copied to all other servers in the cluster. The end result may look very similar to the isolated cache, but remember that isolated cache only works with static data. Still, as the server farm grows, it will take more and more time and memory to maintain fully replicated cache.
Enter partitioned cache. While "get" operation in the previous scenario was always local, getting data from partitioned cache could mean querying all servers in the cluster until one is found that holds the required object. "Put", on the other hand, is local. Partitioned architecture represents a trade-off: we utilize memory more efficiently and don't waste time replicating the data, but it may take longer to retrieve it.
Last but not least: tools. Enterprise Library from Microsoft is free and has Caching application block which unfortunately doesn't support distributed cache. Enterprise version of NCache from Alachisoft does support distributed cache but is far from being free.