服务由正排索引和倒排索引组成
正排索引
结构如下
type ForwardIndex struct {
map[int]interface{}
}
正排索引比较简单,map类型,key是广告id,value 是ad
倒排索引
首先服务会维护一个全局的定向列表,如下所示
type FieldConfig struct {
Name string
Field string
empty bool
}
FieldsConfigs = []FieldConfig{
{Name: "gender", Field:"Gender",emptyNone: true},
{Name: "location", Field:"Location",emptyNone: true},
{Name:"includePackage", Feild:"IncludePackage", emptyNone: true},
{Name: "excludePackage", Field:"ExcludePackage", emptyNone:false}
}
FieldConfig 里的field字段是用来反射结构体的字段 这个服务速度非常快,支持高并发,尽可能降低内存的使用,实现
- 数据全部维护在内存里,
- 写时复制,尽量避免使用锁
- bitmap ,降低内存使用
- sync.Pool
倒排索引的数据结构
type Field struct {
name string
none bitmap
int64Map bitmap
stringMap bitmap
}
func(f *Field) add(key int, value interface()) bool
func(f *Feild) get(value interface{}) bitmap
func(f *Feild) get(value interface{}) bitmap {
switch value.(type) {
case int64:
return f.getSingleForInt64(value.(int64))
}
}
func (f *Field) getSingleForInt64(value int64) bitmap {
// none 与int64 求并集
b1 := f.int64Data[value]
bnone := f.none
return bitmapOr(b1, bnone)
}
func (f *Field) Add(key uint32, value interface{}) bool {
// key是广告id
// 如果值为空,表示,该广告未在找个field上做定向,如果该定向emptyNone =true, 则放到 none上,
// 如果不为空,根据value 的type 映射,放到 int64Map, 或stringMap 里
switch value.(type) {
case int64:
return f.addSingleForInt64(key, value.(int64))
case string:
return f.addSingleForString(key, value.(string))
case []int64:
return f.addArrayForInt64(key, value.([]int64))
case []string:
return f.addArrayForString(key, value.([]string))
case nil && emptyNone:
return f.addNone(key)
}
return false
}
type InvertedIndex struct {
fields map[string]*Feild
}
将一个广告加入到倒排索引里
func (i *Inverted) Add(value interface{}) bool {
ad := value.(*Ad)
for _, conf := range AdInvertedFields {
if !i.addField(conf, ad.AdId, ad) {
return false
}
}
return true
}
func (i *Inverted) addField(conf FieldConf, id uint32, data interface{}) bool {
// 通过反射拿到广告对应filed的值,
// 如果value不为空,就调用field.Add(id, value)
// 如果value为空且empty=true, 就调用field.Add(id, nil)
field := i.indexer.Field(conf.Name)
if field == nil {
return false
}
rValue := reflect.ValueOf(data).Elem().FieldByName(conf.Field)
if !rValue.IsValid() {
if conf.EmptyNone {
return field.Add(id, nil)
}
return true
}
value := rValue.Interface()
if conf.EmptyNone {
return field.Add(id, value)
}
switch value.(type) {
case []int64:
if len(value.([]int64)) > 0 {
return field.Add(id, value)
}
case []string:
if len(value.([]string)) > 0 {
return field.Add(id, value)
}
}
return true
}
// 根据定向去倒排索引查找,
如果exclude为空,则各字段求交集
否则,对include 求交集得到A集合,对exclude求并集得到B集合,最后结果为A-B
func (d *DarwinMatch) searchInverted(indexer *advertisement.Inverted,
include, exclude map[string]interface{}) []uint32 {
if len(exclude) > 0 {
d.searcher = indexer.GetSearchBox(indexing.RelationDifference,
indexer.GetSearchDo(indexing.RelationIntersection, include),
indexer.GetSearchDo(indexing.RelationUnion, exclude))
} else {
d.searcher = indexer.GetSearchDo(indexing.RelationIntersection, include)
}
return d.searcher.Do()
}
// 广告加入到倒排索引
func (i *Inverted) Add(a *db.Ad) bool {
for _, conf := range FieldsConfigs {
if !i.addField(conf, a.AdId, a) {
return false
}
}
return true
}