NoSQL数据库选型MongoDB vs Redis vs Cassandra引言NoSQL数据库已经成为现代应用架构中不可或缺的组件。MongoDB、Redis和Cassandra是三种最流行的NoSQL数据库它们各有特点适用于不同的场景。本文将从数据模型、性能特点、适用场景等多个维度进行深入对比分析。一、数据模型对比1.1 数据模型概述特性MongoDBRedisCassandra类型文档数据库KV/数据结构数据库列族数据库数据结构JSON/BSON文档String, Hash, List, Set, ZSet, etc列族Column Families嵌套支持原生支持有限支持通过复合列支持模式动态模式无模式动态列1.2 Go语言客户端对比package nosql import ( context fmt time github.com/redis/go-redis/v9 go.mongodb.org/mongo-driver/mongo github.com/gocql/gocql )二、MongoDB深度解析2.1 MongoDB特点灵活的文档模型适合敏捷开发强大的查询语言和聚合框架支持二级索引和全文索引自动分片水平扩展能力强丰富的生态系统2.2 MongoDB应用场景package mongo import ( context fmt time go.mongodb.org/mongo-driver/bson go.mongodb.org/mongo-driver/mongo go.mongodb.org/mongo-driver/mongo/options ) type MongoService struct { client *mongo.Client database *mongo.Database } func NewMongoService(uri, dbName string) (*MongoService, error) { ctx, cancel : context.WithTimeout(context.Background(), 10*time.Second) defer cancel() client, err : mongo.Connect(ctx, options.Client().ApplyURI(uri)) if err ! nil { return nil, fmt.Errorf(failed to connect: %w, err) } if err : client.Ping(ctx, nil); err ! nil { return nil, fmt.Errorf(failed to ping: %w, err) } return MongoService{ client: client, database: client.Database(dbName), }, nil } type UserProfile struct { ID string bson:_id Name string bson:name Email string bson:email Age int bson:age Tags []string bson:tags Address Address bson:address CreatedAt time.Time bson:created_at UpdatedAt time.Time bson:updated_at } type Address struct { Street string bson:street City string bson:city Country string bson:country } func (s *MongoService) CreateUserProfile(ctx context.Context, profile *UserProfile) error { collection : s.database.Collection(user_profiles) profile.CreatedAt time.Now() profile.UpdatedAt time.Now() _, err : collection.InsertOne(ctx, profile) return err } func (s *MongoService) GetUserProfile(ctx context.Context, id string) (*UserProfile, error) { collection : s.database.Collection(user_profiles) var profile UserProfile err : collection.FindOne(ctx, bson.M{_id: id}).Decode(profile) if err ! nil { return nil, err } return profile, nil } func (s *MongoService) UpdateUserProfile(ctx context.Context, id string, updates bson.M) error { collection : s.database.Collection(user_profiles) updates[updated_at] time.Now() _, err : collection.UpdateOne( ctx, bson.M{_id: id}, bson.M{$set: updates}, ) return err } func (s *MongoService) SearchByTags(ctx context.Context, tags []string) ([]*UserProfile, error) { collection : s.database.Collection(user_profiles) cursor, err : collection.Find(ctx, bson.M{ tags: bson.M{$in: tags}, }) if err ! nil { return nil, err } defer cursor.Close(ctx) var profiles []*UserProfile if err : cursor.All(ctx, profiles); err ! nil { return nil, err } return profiles, nil } func (s *MongoService) GetUserStats(ctx context.Context, minAge, maxAge int) ([]bson.M, error) { collection : s.database.Collection(user_profiles) pipeline : []bson.M{ {$match: bson.M{age: bson.M{$gte: minAge, $lte: maxAge}}}, {$group: bson.M{ _id: $address.city, count: bson.M{$sum: 1}, avgAge: bson.M{$avg: $age}, }}, {$sort: bson.M{count: -1}}, } cursor, err : collection.Aggregate(ctx, pipeline) if err ! nil { return nil, err } defer cursor.Close(ctx) var results []bson.M if err : cursor.All(ctx, results); err ! nil { return nil, err } return results, nil }三、Redis深度解析3.1 Redis特点极高性能单实例可达10万 QPS丰富的数据结构持久化支持RDB/AOF主从复制和集群支持事务和Lua脚本支持Pub/Sub消息订阅3.2 Redis应用场景package redis import ( context encoding/json fmt time github.com/redis/go-redis/v9 ) type RedisService struct { client *redis.Client } func NewRedisService(addr, password string, db int) (*RedisService, error) { client : redis.NewClient(redis.Options{ Addr: addr, Password: password, DB: db, }) ctx, cancel : context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err : client.Ping(ctx).Err(); err ! nil { return nil, fmt.Errorf(failed to connect: %w, err) } return RedisService{client: client}, nil } type Session struct { UserID string json:user_id Token string json:token CreatedAt time.Time json:created_at ExpiresAt time.Time json:expires_at } func (s *RedisService) CreateSession(ctx context.Context, session *Session) error { key : fmt.Sprintf(session:%s, session.Token) data, err : json.Marshal(session) if err ! nil { return err } ttl : time.Until(session.ExpiresAt) return s.client.Set(ctx, key, data, ttl).Err() } func (s *RedisService) GetSession(ctx context.Context, token string) (*Session, error) { key : fmt.Sprintf(session:%s, token) data, err : s.client.Get(ctx, key).Bytes() if err ! nil { return nil, err } var session Session if err : json.Unmarshal(data, session); err ! nil { return nil, err } return session, nil } func (s *RedisService) DeleteSession(ctx context.Context, token string) error { key : fmt.Sprintf(session:%s, token) return s.client.Del(ctx, key).Err() } type RateLimiter struct { client *redis.Client } func NewRateLimiter(client *redis.Client) *RateLimiter { return RateLimiter{client: client} } func (rl *RateLimiter) Allow(ctx context.Context, key string, limit int, window time.Duration) (bool, error) { fullKey : fmt.Sprintf(ratelimit:%s, key) count, err : rl.client.Incr(ctx, fullKey).Result() if err ! nil { return false, err } if count 1 { rl.client.Expire(ctx, fullKey, window) } return count int64(limit), nil } type DistributedLock struct { client *redis.Client } func NewDistributedLock(client *redis.Client) *DistributedLock { return DistributedLock{client: client} } func (dl *DistributedLock) Acquire(ctx context.Context, key string, ttl time.Duration) (bool, error) { fullKey : fmt.Sprintf(lock:%s, key) return dl.client.SetNX(ctx, fullKey, 1, ttl).Result() } func (dl *DistributedLock) Release(ctx context.Context, key string) error { fullKey : fmt.Sprintf(lock:%s, key) return dl.client.Del(ctx, fullKey).Err() } type MessageQueue struct { client *redis.Client } func NewMessageQueue(client *redis.Client) *MessageQueue { return MessageQueue{client: client} } func (mq *MessageQueue) Enqueue(ctx context.Context, queue string, message interface{}) error { data, err : json.Marshal(message) if err ! nil { return err } return mq.client.LPush(ctx, fmt.Sprintf(queue:%s, queue), data).Err() } func (mq *MessageQueue) Dequeue(ctx context.Context, queue string, timeout time.Duration) ([]byte, error) { return mq.client.BRPop(ctx, timeout, fmt.Sprintf(queue:%s, queue)).Result() }四、Cassandra深度解析4.1 Cassandra特点分布式、去中心化架构线性可扩展高可用无单点故障强一致性可配置CQL查询语言类似SQL适合写入密集型工作负载4.2 Cassandra应用场景package cassandra import ( context fmt time github.com/gocql/gocql ) type CassandraService struct { session *gocql.Session } func NewCassandraService(hosts []string, keyspace string) (*CassandraService, error) { cluster : gocql.NewCluster(hosts...) cluster.Keyspace keyspace cluster.Consistency gocql.Quorum cluster.Timeout 5 * time.Second session, err : cluster.CreateSession() if err ! nil { return nil, fmt.Errorf(failed to create session: %w, err) } return CassandraService{session: session}, nil } func (s *CassandraService) CreateSchema(ctx context.Context) error { queries : []string{ CREATE TABLE IF NOT EXISTS sensor_data ( sensor_id text, timestamp timeuuid, temperature float, humidity float, PRIMARY KEY (sensor_id, timestamp) ) WITH CLUSTERING ORDER BY (timestamp DESC), CREATE TABLE IF NOT EXISTS user_events ( user_id text, event_type text, event_id timeuuid, payload mapstring, string, created_at timestamp, PRIMARY KEY ((user_id), event_type, event_id) ), CREATE TABLE IF NOT EXISTS page_views ( page_id text, date text, views counter, PRIMARY KEY (page_id, date) ), } for _, query : range queries { if err : s.session.Query(query).Exec(); err ! nil { return fmt.Errorf(failed to execute query: %w, err) } } return nil } type SensorData struct { SensorID string Timestamp time.Time Temperature float64 Humidity float64 } func (s *CassandraService) RecordSensorData(ctx context.Context, data *SensorData) error { query : INSERT INTO sensor_data (sensor_id, timestamp, temperature, humidity) VALUES (?, ?, ?, ?) timestamp : gocql.TimeUUID() return s.session.Query(query).Scan( data.SensorID, timestamp, data.Temperature, data.Humidity, ) } func (s *CassandraService) GetRecentSensorData(ctx context.Context, sensorID string, limit int) ([]SensorData, error) { query : SELECT sensor_id, timestamp, temperature, humidity FROM sensor_data WHERE sensor_id ? LIMIT ? var results []SensorData iter : s.session.Query(query).Bind(sensorID, limit).Iter() var sensorIDResult string var timestamp time.Time var temp, humidity float64 for iter.Scan(sensorIDResult, timestamp, temp, humidity) { results append(results, SensorData{ SensorID: sensorIDResult, Timestamp: timestamp, Temperature: temp, Humidity: humidity, }) } if err : iter.Close(); err ! nil { return nil, fmt.Errorf(failed to close iter: %w, err) } return results, nil } func (s *CassandraService) IncrementPageView(ctx context.Context, pageID, date string) error { query : UPDATE page_views SET views views 1 WHERE page_id ? AND date ? return s.session.Query(query).Exec(pageID, date) } func (s *CassandraService) Close() { s.session.Close() }五、选型指南5.1 场景对比表场景推荐数据库原因内容管理/CMSMongoDB灵活文档模型便于内容迭代会话存储Redis高性能TTL支持丰富数据结构实时分析Redis/MongoDB聚合能力实时计算消息队列RedisPub/SubLlist实现队列物联网时序数据Cassandra高写入性能时序数据友好排行榜RedisZSet天然支持排序分布式锁RedisSETNX语义简单性能高用户行为分析MongoDB/Cassandra灵活存储聚合查询支持缓存层Redis性能最高数据结构丰富社交网络MongoDB/Cassandra图数据模型灵活5.2 选型决策树开始 │ ▼ ┌─────────────────┐ │ 数据结构是什么 │ └────────┬────────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ 简单KV │ │ 文档结构 │ │ 多列 │ └────┬────┘ └────┬─────┘ └────┬─────┘ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ 需要持久化│ │ 需要复杂查询│ │ 需要高写入│ │ ? │ │ ? │ │ ? │ └────┬────┘ └────┬─────┘ └────┬─────┘ │ │ │ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ Redis 文件系统 MongoDB ES Cassandra六、混合使用实践6.1 多数据库架构package nosql import ( context github.com/gocql/gocql github.com/redis/go-redis/v9 go.mongodb.org/mongo-driver/mongo ) type MultiDatabaseService struct { mongo *mongo.Database redis *redis.Client cassandra *gocql.Session } func NewMultiDatabaseService( mongoDB *mongo.Database, redisClient *redis.Client, cassandraSession *gocql.Session, ) *MultiDatabaseService { return MultiDatabaseService{ mongo: mongoDB, redis: redisClient, cassandra: cassandraSession, } } type UserActivity struct { UserID string bson:user_id Action string bson:action Metadata map[string]interface{} bson:metadata CreatedAt int64 bson:created_at } func (s *MultiDatabaseService) RecordUserActivity(ctx context.Context, activity *UserActivity) error { if err : s.recordToMongo(ctx, activity); err ! nil { return err } if err : s.recordToRedis(ctx, activity); err ! nil { return err } return nil } func (s *MultiDatabaseService) recordToMongo(ctx context.Context, activity *UserActivity) error { collection : s.mongo.Collection(user_activities) _, err : collection.InsertOne(ctx, activity) return err } func (s *MultiDatabaseService) recordToRedis(ctx context.Context, activity *UserActivity) error { key : activity:count: activity.UserID return s.redis.Incr(ctx, key).Err() } func (s *MultiDatabaseService) GetUserActivities(ctx context.Context, userID string, limit int64) ([]UserActivity, error) { collection : s.mongo.Collection(user_activities) cursor, err : collection.Find(ctx, map[string]interface{}{ user_id: userID, }) if err ! nil { return nil, err } defer cursor.Close(ctx) var activities []UserActivity if err : cursor.All(ctx, activities); err ! nil { return nil, err } return activities, nil }七、总结NoSQL数据库选型需要根据具体业务场景来决定MongoDB适合需要灵活数据模型、复杂查询、敏捷开发的场景Redis适合高性能缓存、会话存储、实时分析、队列等场景Cassandra适合高写入量、高可用、时序数据等场景实际应用中很多系统会采用多数据库组合的架构根据不同数据的特点选择最合适的存储方案。关键在于深入理解业务需求和数据特点做出合理的选型决策。