mongodb学习
什么场景需要使用mongo?对比mysql的优势是什么?
两者最大的区别是mongo适合存储非结构化的数据,mysql适合存储结构化的数据。
除此之外,mongo的优点还包括:
1、分布式部署和水平扩展:MongoDB支持分布式部署和水平扩展,可以通过添加节点来扩展存储和查询能力,而MySQL的扩展方式是垂直扩展,需要升级硬件来提升性能。对于大数据量和高并发的应用场景,MongoDB更加适合。
2、查询性能:MongoDB的查询性能比MySQL更快,尤其是在非结构化数据的查询和聚合操作上。MongoDB支持复杂的查询和聚合操作,而MySQL的查询和聚合操作相对简单。
但是,在结构化数据的存储和查询、事务处理和ACID特性等方面,MySQL仍然是更好的选择。
1 mongodb基础语法与了解
https://www.runoob.com/mongodb/nosql.html
概念:
库 colletion数据集 document文件 field字段
ObjectId:唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
接下来的 3 个字节是机器标识码
紧接的两个字节由进程 id 组成 PID
最后三个字节是随机数
由于 ObjectId 中保存了创建的时间戳,所以如果使用的是默认的id,则可以通过 getTimestamp 函数来获取文档的创建时间:
objectId.getTimestamp()
保存:
mongo中mongoTemplate或者MongoRepository实现的save,都是返回对应的整个documents,所以插入后id等信息的获取很方便。
注意如果save的对象中已经有id了,则mongo会将本次save操作视为update操作。
save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。
insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。
标准的修改方式应该如下:
Query query = new Query(Criteria.where("name").is("John"));
Update update = new Update().set("age", 35);
mongoTemplate.updateFirst(query, update, Person.class);
springboot项目引入mongodb
1)引入springboot的mongo包
2)增加配置
spring:
data:
mongodb:
host: dds-8vb744d*
port: 3717
database: ai_test
username: ai_test
password: je***
connection-pool:
max-size: 50
min-size: 20
3)连接池与连接建立
@Slf4j
@Configuration
@EnableMongoAuditing
public class MongoConfig extends AbstractMongoClientConfiguration {
@NacosValue(value = "${spring.data.mongodb.host}", autoRefreshed = true)
private String host;
......
@Override
protected String getDatabaseName() {
return database;
}
@Override
public MongoClient mongoClient() {
log.info("[创建mongodb数据库连接]");
MongoCredential credential = MongoCredential.createCredential(username, database, password.toCharArray());
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings(builder ->
builder.hosts(Arrays.asList(new ServerAddress(host, port), new ServerAddress(host2, port))))
.applyToConnectionPoolSettings(builder ->
builder.maxSize(maxSize).minSize(minSize))
.credential(credential)
.build();
return MongoClients.create(settings);
}
}
4)mongo操作实例
@Api(tags = "c++题库 题单")
@Slf4j
@RestController
@RequestMapping(value = "/queList")
public class MongoExpController {
@Autowired
MongoTemplate mongoTemplate;
private static final String COLLECTION_NAME = "users";
@ApiOperation(value = "mongo操作数据库实例", notes = "mongo操作数据库实例")
@GetMapping("/cleanJudgeData")
public String clean(
@ApiParam(value = "密钥", required = true) @RequestParam(required = true) String key
) {
// 设置用户信息
User user = new User();
user.setId("13");
user.setName("酒仙");
// 存储用户信息,如果文档信息已经存在就执行更新
User newUser = mongoTemplate.save(user);
// 输出存储结果
log.info("存储的用户信息为:{}", newUser);
Query query = new Query();
query.addCriteria(Criteria.where("name").is("酒仙"));
String name = mongoTemplate.findOne(query, User.class).getName();
return "abc";
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "user")
public class User {
@Id
private String id;
@Field("username")
private String username;
private String password;
private String registerTime;
private String phone;
private String name;
private String sex;
private String age;
}
3 最佳实践
时间自维护
MongoConfig上开启自维护注释,@EnableMongoAuditing
@CreatedDate
@Field("create_time")
private Date createTime;
/**
* 会话的修改时间
*/
@LastModifiedDate
@Field("update_time")
private Date updateTime;
但是:如果id不是由mongo自动维护,那createTime上面的注释不会生效。
所以如果主键是研发来维护,那对应的createTime也是由研发维护。
说明:robot 3T查看数据时可以指定时间的时区,options->display dates in
id自增的实现
mongo一般的主键id是字符串格式的,但是可能因为一些特殊的要求(如分页),需要集合的主键是int类型的,此时实现方案如下:
新建一个计数器集合,包括名称和序号
@Document(collection = "counters")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Counters {
@Id
private String id;// 这里id就是集合名
private Integer seq;// 这里seq就是集合名对应的主键当前值
}
// 这里的类,用来维护主键id和序号
@Repository
public class CountersRespositoryImpl {
@Autowired
private MongoOperations mongoOperations;
public Integer getNextSequence(String collectionName) {
Query query = new Query(Criteria.where("_id").is(collectionName));
Update update = new Update().inc("seq", 1);
Counters counters = mongoOperations.findAndModify(query, update, Counters.class);
if(counters == null){
counters = Counters.builder().id(collectionName).seq(1).build();
mongoOperations.save(counters);
}
return counters.getSeq();
}
}
使用如下:
AiChatRecord chat = AiChatRecord.builder()
.id(countersRespositoryImpl.getNextSequence("aiChatRecord"))
.status(AiRequestStatusEnum.REQUEST_START.getValue())
.createTime(new Date(System.currentTimeMillis()))
.build();
https://zhuanlan.zhihu.com/p/497736109?utm_medium=social&utm_oi=639733965527846912
mongodb规范参考:https://segmentfault.com/a/1190000022926656
引擎:MongoDB 默认存储引擎为 MMAPv1,建议根据实际情况选择存储引擎。
集合:page size 压缩算法 leaf_page_max等参数设置
文档:_id建议自增
说明:MongoDB的表与InnoDB相似,都是索引组织表,数据内容跟在主键后,而_id是MongoDB中的默认主键,一旦_id的值为非自增,当数据量达到一定程度之后,每一次写入都可能导致主键的二叉树大幅度调整,这将是一个代价极大的写入, 所以写入就会随着数据量的增大而下降,所以一定不要在_id中写入自定义的内容。
索引应该使用小且区分度高的字段
mongodto中常用注解:
mongodb.core.mapping中的主要注解:
@Document:用于将Java类映射到MongoDB中的集合。
@Id:用于指定Java类中的属性作为MongoDB文档的_id字段。
@Field:用于将Java类中的属性映射到MongoDB文档中的字段。
@Indexed:用于创建MongoDB索引。
@CompoundIndex:用于创建MongoDB复合索引。
@GeoSpatialIndexed:用于创建MongoDB地理空间索引。
@TextIndexed:用于创建MongoDB全文索引。
关于@Index
如果在Java中的MongoDB domain类上添加了@Index注解,但是对应的集合已经存在一段时间,那么这个索引会在下一次应用程序启动时被创建。
在Spring Data MongoDB中,当应用程序启动时,会扫描所有带有@Document注解的类,并将它们映射到MongoDB中的集合。如果这些类上还带有@Index注解,则Spring Data MongoDB会为它们创建相应的索引。如果对应的集合中已经存在数据,则创建索引可能需要一些时间,具体时间取决于数据量和服务器性能。
因此,在实际开发中,最好在创建集合之前就定义好所需的索引。这样可以确保索引在应用程序启动时就被创建,从而避免在运行时对索引进行创建和维护所带来的性能开销。
https://zhuanlan.zhihu.com/p/497736109?utm_medium=social&utm_oi=639733965527846912