Developing High Performance
Applications using
Aerospike Go
• Go Client Library
• CLI and Web Tools
• Benchmark Tools
• Hiring
What is Aerospike?
Distributed Database
Key-Value Store
Secondary Indexes
User-Defined Functions
Large Data Types
Why use Aerospike?
High Throughput
Low Latency
Highly Scalable
Highly Reliable
Aerospike is High Throughput
0100000200000300000400000500000600000700000800000900000
10000001100000120000013000001400000150000016000001700000
Balanced Read-Heavy
Aerospike 3 (in-memory)
Aerospike 3 (persistent)
Aerospike 2
Cassandra
MongoDB
Couchbase 1.8
Couchbase 2.0
Aerospike is Low Latency
0
2.5
5
7.5
10
0 50,000 100,000 150,000 200,000
Ave
rag
e L
ate
ncy,
ms
Throughput, ops/sec
Aerospike
Who uses Aerospike?
theTradeDesk
7 of the Top 20
Advertising
Platforms
Rank Name
1
2DoubleClick
2 Google AdSense
3 AppNexus
4 Google Remarketing
5 Yahoo Small Business
6 Google Publisher Tag
7 Openads/OpenX
8 Evidon
9Turn
10 Facebook Exchange
11 Rubicon Project
12 Pubmatic
13 The Trade Desk
14 BlueKai
15 Criteo
16 Casale Media
17 Rocket Fuel
18 Advertising.com (AOL)
19 ContextWeb
Knows High Performance
Performance Tips
Memory Allocation
Recycling
Pooling
Angry GC
<http://blog.cloudflare.com/recycling-memory-buffers-in-go/>
Mellow GC
Memory Allocation
• Avoid Dynamic AllocationNote: n resizes == (alloc + copy) * n
• Allocate exactly what you need upfront
• Its OK to allocate more than enough
• Garbage is your enemy
Dynamic vs Static
var b bytes.Buffer
b.Write(a)
368 ns/op
b := bytes.NewBuffer(a)
11.5 ns/op
Recycle
• Avoid GCNote: Releasing objects trigger GC
• Create and Reuse
• bytes.Buffer is slow for reuse
Creating a Buffer and Copying Data
b := make([]byte, 256)
copy(b, a)
20.6 ns/op
b := bytes.NewBuffer(a)
11.5 ns/op
Recycle
b:= make([]byte, 256)
copy(b, a)
10.6 ns/op
b := bytes.NewBuffer(a)
b.Reset()
b.Write(a)
25.8 ns/op
Pooling
• Pools work great for short lived concurrent tasks.
• Size pools sufficiently
• Avoid sync.Pool for pooling objectsNote: Not ideal for long lives pool of objects. Cleans up with each GC
sync.Pool
p := &sync.Pool{
New: NewBuffer,
}
b := p.Get().([]byte)
p.Put(b)
115 ns/op
Channel-based Pool (FIFO)
type PoolChan struct {
buffers chan []byte
}
func (p *PoolChan) Get() []byte
func (p *PoolChan) Put(b []byte) bool
Channel-based Pool
func (p *PoolChan) Get() []byte {
select {
case b := <-p.buffers:
return b
default:
}
return NewBuffer()
}
Channel-based Pool
func (p *PoolChan) Put(b []byte) bool {
select {
case p.buffers <- b:
return true
default:
}
return false
}
Channel-based Pool
p := &PoolChan{
buffers: make(chan []byte, 128),
}
b := p.Get()
p.Put(b)
67.4 ns/op
Pooling
p := &sync.Pool{...}
b := p.Get().([]byte)
p.Put(b)
115 ns/op
p := &PoolChan{...}
b := p.Get()
p.Put(b)
67.4 ns/op
Slice-based Pool (LIFO)
type PoolSlice struct {
buffers [][]byte
mutex sync.Mutex
}
func (p *PoolSlice) Get() []byte
func (p *PoolSlice) Put(b []byte) bool
Slice-based Pool
func (p *PoolSlice) Get() []byte {
if len(p.buffers) == 0 {
return NewBuffer()
} else {
mutex.Lock()
b := p.buffers[len(p.buffers)-1]
p.buffers = p.buffers[0 : len(p.buffers)-1]
mutex.Unlock()
return b
}
}
Slice-based Pool
func (p *PoolSlice) Put(b []byte) bool {
mutex.Lock()
p.buffers = append(p.buffers, b)
mutex.Unlock()
return true
}
Slice-based Pool
p := &PoolSlice{
buffers: make([][]byte, 0, 128),
}
b := p.Get()
p.Put(b)
51.6 ns/op
Pooling
p := &PoolSlice{...}
b := p.Get()
p.Put(b)
51.6 ns/op
p := &PoolChan{...}
b := p.Get()
p.Put(b)
67.4 ns/op
Now what?
Using Aerospike
Benchmark
Client Library
Data Model
API Highlights
Client Library
github.com/aerospike/aerospike-client-go
Data Model
namespace set key bins ttl gen
test demo x a: “abc”, b: 123
test demo y b: 345, c: [3,4,5]
test demo z a: “ghi”, c: [6,7,8]
... ... ... ...
Keys:
- Integer
- String
- Byte Array
Bin: Name -> Value
- Name:
- String
- Value:
- Integer (indexable)
- String (indexable)
- Byte Array
- List
- Map
- LDT (Map, List, Stack)
Create a Client
// using default policy
client, err := NewClient("10.1.1.1", 3000)
// using custom policy
client, err := NewClientWithPolicy(policy, "10.1.1.1", 3000)
// using custom policy and host list
client, err := NewClientWithPolicy(policy,
NewHost("10.1.1.1", 3000), NewHost("10.1.1.2", 3001))
Create a Key
// integer key
key, err := NewKey("test", "demo", 123)
// string key
key, err := NewKey("test", "demo","abc")
// byte array key
key, err := NewKey("test", "demo", []byte(uuid.NewRandom()))
Write a Record using a Bin Array
bins := []Bin{
NewBin("name", "Bob"),
NewBin("age", 26),
NewBin("interests", []string{"golang"}),
NewBin("location", map[string]string{
"city": "New York",
"state": "New York",
}),
}
client.PutBins(policy, key, bins...)
Write a Record using a BinMap
bins := BinMap{
"name": "Bob",
"age": 26,
"interests": []string{"golang"},
"location": map[string]string{
"city": "New York",
"state": "New York",
},
}
client.Put(policy, key, bins)
Read a Record
rec, err := client.Get(policy, key)
println("Name:", rec.Bins["name"].(string))
Query Records
stmt := NewStatement("test", "demo")
recordset, err := client.Query(policy, stmt)
for rec := range recordset.Records {
println("Name:", rec.Bins["name"].(string))
}
Query Records with Filter
stmt := NewStatement("test", "demo")
stm.Addfilter(NewRangeFilter("age", 18, 34)
recordset, err := client.Query(policy, stmt)
for rec := range recordset.Records {
println("Name:", rec.Bins["name"].(string))
}
Defining a User-Defined Function
// sample.lua
function store_if_larger(rec, bin, val)
if val > (rec[bin] or 0) then
r[bin] = b
aerospike:update(rec)
end
return r[bin]
end
Calling a User-Defined Function
// stores x=1
err := client.PutBins(policy, key, NewBin("x", 1))
// stores x=2, returns 2
res, err := client.Execute(policy, key, "sample", "store_if_larger", NewValue("x"),
NewValue(2))
// returns 2
res, err := client.Execute(policy, key, "sample", "store_if_larger", NewValue("x"),
NewValue(1))
Using a Large Stack (LDT)
// reference a large stack in database
lstack := NewLargeStack(client, policy, key, "timeline", "")
// push 1 million values on to the list
for i := 1; i <= 1000 * 1000; i++ {
err := lstack.Push(NewValue(i))
}
// pop the first 10
sub := lstack.Pop(10)
Run the Benchmark
$ go install github.com/aerospike/aerospike-client-go/tools/benchmark
$ ./bin/benchmark
Learn More
• Aerospike <aerospike.com>
• Aerospike Projects <github.com/aerospike>
• Aerospike Go Client <github.com/aerospike/aerospike-client-go>
• Community Projects <aerospike.com/community/labs>
• Community Forums <discuss.aerospike.com>
Top Related