随着我们的app使用人数快速发展,用户行为数据变得越来越重要,这些数据可以用来优化产品设计、提升用户体验、实施精准营销策略,并支持业务决策。然而,处理海量的用户行为数据需要高效的存储和分析能力,传统的数据库方案往往难以满足这些需求。
为什么选择使用Cassandra作为用户事件存储解决方案?
高性能写入:Cassandra的设计使得它非常适合高写入场景。它的写操作是无锁的,并且所有节点都可以接受写请求,这大大提高了写入性能。
一致性模型:Cassandra提供了多种一致性级别(如ONE, QUORUM, ALL等),可以根据具体需求调整,以平衡一致性和可用性。
自动分片:Cassandra会自动将数据分布在集群中的多个节点上,确保负载均衡。
动态添加节点:可以随时向集群中添加新的节点,以提高容量和性能,而无需停机维护。
快速读取:Cassandra支持高效的点查询和范围查询,适合读取特定用户的所有事件。
二级索引:虽然Cassandra不支持复杂的查询,但它提供了一定程度的二级索引功能,可以帮助进行更灵活的查询。
多副本复制:Cassandra默认会在多个节点之间复制数据,确保数据的安全性和可靠性。
强容错能力:即使某些节点宕机,其他节点仍然可以继续提供服务。
定期快照和增量备份:支持定期快照和增量备份,方便数据恢复和灾难恢复。
免费开源:作为Apache软件基金会的一个顶级项目,Cassandra是免费且开源的。
硬件利用率高:Cassandra能够充分利用现有的硬件资源,减少额外的成本投入。
哪些公司选择Cassandra?
Capital One:用于存储金融交易数据和客户行为分析。
Spotify:用于存储音乐播放记录、用户偏好等数据。
Rovio (Angry Birds):用于存储游戏内玩家的行为数据和统计信息。
Uber:用于存储司机和乘客的位置数据、行程信息等。
Netflix:用于存储和处理大量的用户行为数据,如观看历史、搜索记录等。
Instagram:用于存储照片和视频的元数据,以及用户活动日志。
Apple:用于移动设备的推送通知系统和其他内部应用的数据存储。
Reddit:用于存储用户提交的内容、评论和其他社交数据。
Zynga:用于存储在线游戏中的用户活动和统计数据。
代码实操
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>cassandra-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cassandra-demo</name>
<description>Demo project for Spring Boot and Cassandra integration</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
# 配置Cassandra连接信息
spring.data.cassandra.contact-points=localhost # Cassandra节点地址
spring.data.cassandra.port=9042 # Cassandra端口号
spring.data.cassandra.keyspace-name=user_events # keyspace名称
spring.data.cassandra.schema-action=create_if_not_exists # 如果keyspace不存在则创建
实体类
package com.example.cassandrareactivedemo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.mapping.Table;
import java.util.Date;
/**
* 用户事件实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("user_events")
publicclass UserEvent {
@Id// 标记为主键
private String eventId; // 事件ID
private String userId; // 用户ID
private String eventType; // 事件类型
private Date eventTime; // 事件发生时间
}
Repository
package com.example.cassandrareactivedemo.repository;
import com.example.cassandrareactivedemo.model.UserEvent;
import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;
import reactor.core.publisher.Flux;
public interface UserEventRepository extends ReactiveCassandraRepository<UserEvent, String> {
/**
* 根据用户ID查找所有相关的用户事件
* @param userId 用户ID
* @return 包含所有匹配用户的事件流
*/
Flux<UserEvent> findByUserId(String userId);
}
Service
package com.example.cassandrareactivedemo.service;
import com.example.cassandrareactivedemo.model.UserEvent;
import com.example.cassandrareactivedemo.repository.UserEventRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 用户事件服务类,处理业务逻辑
*/
@Service
publicclass UserEventService {
@Autowired
private UserEventRepository userEventRepository;
/**
* 保存用户事件到Cassandra数据库
* @param userEvent 用户事件对象
* @return 包含已保存用户的Mono对象
*/
public Mono<UserEvent> save(UserEvent userEvent) {
return userEventRepository.save(userEvent);
}
/**
* 根据用户ID查找所有相关的用户事件
* @param userId 用户ID
* @return 包含所有匹配用户的事件流
*/
public Flux<UserEvent> findByUserId(String userId) {
return userEventRepository.findByUserId(userId);
}
}
Controller
package com.example.cassandrareactivedemo.controller;
import com.example.cassandrareactivedemo.model.UserEvent;
import com.example.cassandrareactivedemo.service.UserEventService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api/user-events")
publicclass UserEventController {
@Autowired
private UserEventService userEventService;
/**
* 创建新的用户事件
* @param userEvent 用户事件对象
* @return 包含新创建用户的Mono对象
*/
@PostMapping("/")
public Mono<UserEvent> createUserEvent(@RequestBody UserEvent userEvent) {
return userEventService.save(userEvent);
}
/**
* 根据用户ID获取所有相关用户事件
* @param userId 用户ID
* @return 包含所有匹配用户的事件流
*/
@GetMapping("/{userId}")
public Flux<UserEvent> getUserEventsByUserId(@PathVariable String userId) {
return userEventService.findByUserId(userId);
}
}
Application
package com.example.cassandrareactivedemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CassandraDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CassandraDemoApplication.class, args);
}
}
测试
创建新的用户事件
curl -X POST http://localhost:8080/api/user-events/ \
-H "Content-Type: application/json" \
-d '{
"eventId": "event1",
"userId": "user1",
"eventType": "login",
"eventTime": "2025-05-22T20:10:06Z"
}'
Respons
{
"eventId": "event1",
"userId": "user1",
"eventType": "login",
"eventTime": "2025-05-22T20:10:06Z"
}
获取特定用户的用户事件
curl -X GET http://localhost:8080/api/user-events/user1
Respons
[
{
"eventId": "event1",
"userId": "user1",
"eventType": "login",
"eventTime": "2025-05-22T20:10:06Z"
}
]
关注我,送Java福利
/**
* 这段代码只有Java开发者才能看得懂!
* 关注我微信公众号之后,
* 发送:"效率手册",
* 即可获得一本由清华大学为本校学生诚意出品
* 《2025学年清华大学效率手册》Pdf电子书
* 福利截止日期为2025年05月28日止
* 手快有手慢没!!!
*/
System.out.println("请关注我的微信公众号:");
System.out.println("Java知识日历");