The ZODB, as I've described it so far, can only be used within a single Python process (though perhaps with multiple threads). ZEO, Zope Enterprise Objects, extends the ZODB machinery to provide access to objects over a network. The name "Zope Enterprise Objects" is a bit misleading; ZEO can be used to store Python objects and access them in a distributed fashion without Zope ever entering the picture. The combination of ZEO and ZODB is essentially a Python-specific object database.
ZEO consists of about 1400 lines of Python code. The code is relatively small because it contains only code for a TCP/IP server, and for a new type of Storage, ClientStorage. ClientStorage doesn't use disk files at all; it simply makes remote procedure calls to the server, which then passes them on a regular Storage class such as FileStorage. The following diagram lays out the system:
XXX insert diagram here later
Any number of processes can create a ClientStorage instance, and any number of threads in each process can be using that instance. ClientStorage aggressively caches objects locally, so in order to avoid using stale data, the ZEO server sends an invalidate message to all the connected ClientStorage instances on every write operation. The invalidate message contains the object ID for each object that's been modified, letting the ClientStorage instances delete the old data for the given object from their caches.
This design decision has some consequences you should be aware of. First, while ZEO isn't tied to Zope, it was first written for use with Zope, which stores HTML, images, and program code in the database. As a result, reads from the database are far more frequent than writes, and ZEO is therefore better suited for read-intensive applications. If every ClientStorage is writing to the database all the time, this will result in a storm of invalidate messages being sent, and this might take up more processing time than the actual database operations themselves.
On the other hand, for applications that have few writes in comparison to the number of read accesses, this aggressive caching can be a major win. Consider a Slashdot-like discussion forum that divides the load among several Web servers. If news items and postings are represented by objects and accessed through ZEO, then the most heavily accessed objects - the most recent or most popular postings - will very quickly wind up in the caches of the ClientStorage instances on the front-end servers. The back-end ZEO server will do relatively little work, only being called upon to return the occasional older posting that's requested, and to send the occasional invalidate message when a new posting is added. The ZEO server isn't going to be contacted for every single request, so its workload will remain manageable.