Twissandra
Twissandra
Jonathan Ellis
[email protected] / @spyced
Why Cassandra?
Writer
Memtable
Reader
Commitlog
The Log-Structured Merge-Tree, Bigtable: A Distributed Storage System for Structured Data
Bigtable, 2006
Dynamo, 2007
OSS, 2008
Incubator, 2009
TLP, 2010
Cassandra in production
Digital Reasoning: NLP + entity analytics OpenWave: enterprise messaging OpenX: largest publisher-side ad network in the world Cloudkick: performance data & aggregation SimpleGEO: location-as-API Ooyala: video analytics and business intelligence ngmoco: massively multiplayer game worlds
FUD?
Durabilty
Write to commitlog
<row data 0> <row data 1> ... <row data 127> ... <row data 255> ... Sorted [clustered] by row key
Scaling
T L
T L
F
(A-L]
T L
(A-F]
T
(F-L]
Key C
T L
Reliability
Some headlines
Resyncing Broken MySQL Replication How To Repair MySQL Replication Fixing Broken MySQL Database Replication Replication on Linux broken after db restore MySQL :: Repairing broken replication
Y W A
Key C
T P
Y W A
Key C
hint P
X
L
Y W A
T P
Y W A
Key C
T P
Y W A
Key C
T L P
Tuneable consistency
Monitorable
JMX
OpsCenter
Ian Eure: If youre deploying memcache on top of your database, youre inventing your own ad-hoc, difficult to maintain NoSQL data store
Curt Monash: ACID-compliant transaction integrity commonly costs more in terms of DBMS licenses and many other components of TCO (Total Cost of Ownership) than [scalable NoSQL]. Worse, it can actually hurt application uptime, by forcing your system to pull in its horns and stop functioning in the face of failures that a non-transactional system might smoothly work around. Other flavors of complexity can be a bad thing apply as well. Thus, transaction integrity can be more trouble than its worth. [Curts emphasis]
Twitter: Fifteen months ago, it took two weeks to perform ALTER TABLE on the statuses [tweets] table.
ColumnFamilies
Static
Object data
Dynamic
static columnfamilies
Users
zznate driftx thobbs jbellis Password: * Password: * Password: * Password: * Name: Nate Name: Brandon Name: Tyler
Name: Jonathan
Site: riptano.com
dynamic columnfamilies
Following
zznate driftx thobbs jbellis zznate: driftx:
mdennis: pcmanus
driftx:
thobbs:
thobbs:
xedin:
zznate
Inserting
Really insert or update Not a key/value store update as much of the row as you want
Example: twissandra
http://twissandra.com
CREATE TABLE users ( id INTEGER PRIMARY KEY, username VARCHAR(64), password VARCHAR(64) ); CREATE TABLE following ( user INTEGER REFERENCES user(id), followed INTEGER REFERENCES user(id) ); CREATE TABLE tweets ( id INTEGER, user INTEGER REFERENCES user(id), body VARCHAR(140), timestamp TIMESTAMP );
Cassandrified
create column family users with comparator = UTF8Type and column_metadata = [{column_name: password, validation_class: UTF8Type}] create column family tweets with comparator = UTF8Type and column_metadata = [{column_name: body, validation_class: UTF8Type}, {column_name: username, validation_class: UTF8Type}] create column family friends with comparator = UTF8Type create column family followers with comparator = UTF8Type create column family userline with comparator = LongType and default_validation_class = UUIDType create column family timeline with comparator = LongType and default_validation_class = UUIDType
Connecting
CLIENT = pycassa.connect_thread_local('Twissandra') USER = pycassa.ColumnFamily(CLIENT, 'User')
User
RowKey: ericflo => (column=password, value=****, timestamp=1289446382541473) ------------------RowKey: jbellis => (column=password, value=****, timestamp=1289446438490709) uname = 'jericevans' password = '**********' columns = {'password': password} USER.insert(uname, columns)
driftx:
thobbs:
Tweets
RowKey: 92dbeb50-ed45-11df-a6d0-000c29864c4f => (column=body, value=Four score and seven years ago, timestamp=1289446891681799) => (column=username, value=alincoln, timestamp=1289446891681799) ------------------RowKey: d418a66e-edc5-11df-ae6c-000c29864c4f => (column=body, value=Do geese see God?, timestamp=1289501976713199) => (column=username, value=pdrome, timestamp=1289501976713199)
Userline
RowKey: ericflo => (column=1289446393708810, value=6a0b4834-ed44-11dfbc31-000c29864c4f, timestamp=1289446393710212) => (column=1289446397693831, value=6c6b5916-ed44-11dfbc31-000c29864c4f, timestamp=1289446397694646) => (column=1289446891681780, value=92dbeb50-ed45-11dfa6d0-000c29864c4f, timestamp=1289446891685065) => (column=1289446897315887, value=96379f92-ed45-11dfa6d0-000c29864c4f, timestamp=1289446897317676)
Userline
1289847840615: 3f19757a-c89d...
1289847887086: a20fcf52-595c...
Timeline
RowKey: ericflo => (column=1289446393708810, value=6a0b4834-ed44-11dfbc31-000c29864c4f, timestamp=1289446393710212) => (column=1289446397693831, value=6c6b5916-ed44-11dfbc31-000c29864c4f, timestamp=1289446397694646) => (column=1289446891681780, value=92dbeb50-ed45-11dfa6d0-000c29864c4f, timestamp=1289446891685065) => (column=1289446897315887, value=96379f92-ed45-11dfa6d0-000c29864c4f, timestamp=1289446897317676)
Adding a tweet
tweet_id = str(uuid()) body = '@ericflo thanks for Twissandra, it helps!' timestamp = long(time.time() * 1e6) columns = {'uname': useruuid, 'body': body} TWEET.insert(tweet_id, columns) columns = {ts: tweet_id} USERLINE.insert(uname, columns) TIMELINE.insert(uname, columns) for follower_uname in FOLLOWERS.get(uname, 5000): TIMELINE.insert(follower_uname, columns)
Reads
timeline = USERLINE.get(uname, column_reversed=True) tweets = TWEET.multiget(timeline.values()) start = request.GET.get('start') limit = NUM_PER_PAGE timeline = TIMELINE.get(uname, column_start=start, column_count=limit, column_reversed=True) tweets = TWEET.multiget(timeline.values())
Programatically
Don't use thrift directly Higher level clients have a lot of features you want
API layers
Running twissandra
Login: notroot/notroot
(root/riptano)
cd twissandra python manage.py runserver & Navigate to http://127.0.0.1:8000 Login as jim/jim, tom/tom, or create your own
!PUBLIC! userline
Exercise 1
$ cassandra-cli --host localhost ] use twissandra; ] help; ] help list; ] help get; ] help del; Delete the most recent tweet
Exercise 2
User jim is following user tom, but twissandra doesn't populate Timeline with tweets from before the follow action. Insert a tweet from tom before the follow action into jim's timeline
Exercise 3
Add a state column to the Tweet column family definition, with an index (index_type KEYS).
Hint: a no-op update column family on Tweet would be update column family Tweet with column_metadata=[{column_name:body, validation_class:UTF8Type}, {column_name:username, validation_class:UTF8Type}]
Set the state column on several tweets to TX. Select them using get where.
Language support
Python
pycassa telephus
Ruby
Speed is a negative
Java
Hector
PHP
phpcassa
Done yet?
1289847844275
Id: Id: 844e75e2-b546... 3f19757a-
1289847887086 1289847844275
Id: Id: a20fcf52-595c... 3f19757ac89d... uname: uname: zznate zznate
c89d... uname: uname: driftx zznate body: body: Rise geese see Do to vote sir ...
...
Supercolumns: limitations
Requires reading an entire SC (not the entire row) from disk even if you just want one subcolumn
UUIDs
Column names should be uuids, not longs, to avoid collisions Version 1 UUIDs can be sorted by time (TimeUUID) Any UUID can be sorted by its raw bytes (LexicalUUID)
Lucandra
feld
title title date
term
apache talk 20110201
freq
1 1 1
position
0 1 0
Lucandra ColumnFamilies
create column family documents with comparator = BytesType; Create column family terminfo with column_type = Super and comparator = BytesType and subcomparator = BytesType;
Lucandra data
Document Key col name value "documentId" => { fieldName , value } Term Key col name value "field/term" => { documentId , position vector }
Lucandra queries
FAQ: counting
Locking
UUIDs
counter1 counter2 672e34a2-ba33... 3f19757a-c89d... b681a0b1-58f2... 844e75e2-b546... a20fcf52-595c...
counter1 counter2
aggregated: 27 aggregated: 42
Counter API
key counter1: (14, 13, 9) counter2: (11, 15, 17)
General Tips
Start with queries, work backwards Avoid storing extra timestamp columns Insert instead of check-then-insert Use client-side clock to your advantage use TTL Learn to love wide rows