知识碎片
快来分享你的内容吧~
- 2024-07-22·Java后端查看全文大家好,我是程序媛雪儿,有两天没和大家唠唠技术了,今天我们聊聊AI流式调用,这块我也是总结鱼皮哥AI答题应用平台项目中的技术,大家看了有兴趣可以学习一下,实操体验一下怎么应用哦~ 现在AI已经在各大软件中广泛应用,你们有没有想过如何在AI应用中实时处理数据流?今天咱们聊聊怎么用SSE Rxjav...鱼友0412:好巧,刚好实习的ai项目也是流式调用,看的云里雾里,知其然不知其所以然1310分享
- 2024-07-16·Java后端查看全文一、是什么 Redis内存不够的时候,此时向redis中添加新的key,那么,redis会按照你配置的规则将数据删除掉 ...程序媛雪儿:redis数据淘汰策略610分享
- 2024-07-14·Java后端查看全文大家好,我是程序媛雪儿,今天给大家带来的知识碎片是缓存雪崩。 一、是什么 缓存雪崩:因为大量的key过期,或者redis直接宕机,导致大量的请求到达数据库,带来了巨大的压力。...程序员鱼皮:感谢分享620分享
- 2024-03-12·后端引言:在 Java 编程领域,掌握接口和抽象类的区别不仅是基础知识,也是面试中经常遇到的问题。这两种类型在设计模式和面向对象编程中扮演着关键角色,理解它们的不同之处对于 Java 开发者而言极为重要。本文将详细解析接口和抽象类的定义、区别及应用场景,帮助你在技术讨论和面试中更加自信地阐述它们的特点和用法。 题目 接口和抽象类的使用场景,你真的知道吗? 推荐解析 目的和设计哲学 1)接口旨在定义一个查看全文程序员小白条:点燃求职热情!每周持续更新,海量面试题等你挑战!赶紧关注面试鸭公众号,轻松备战春招和暑期实习!本文更好的文章排版如下:https://mp.weixin.qq.com/s/KyAoVVMhYN9WeSXd5WGCmA1611分享
SpringBoot轻松实现多数据源切换
## 一.需求背景 项目需要实现在多个数据源之间读写数据,例如在 A 数据源和 B 数据源读取数据,然后在 C 数据源写入数据 或者 部分业务数据从 A 数据源中读取、部分从B数据源中读取诸如此类工作。本文将简单模拟在SpringBoot项目中实现不同数据源之间读取数据。 ## 二.技术概述 `dynamic-datasource` 是一个开源的 SpringBoot 多数据源启动器,提供了丰富的功能,**使用者只需完成基本的数据源配置,配合使用`@DS`注解即可轻松实现多数据源之间的切换**。其支持 **Jdk 1.7+, SpringBoot 1.5.x 2.x.x 3.x.x**。JPA用户不建议使用,JPA自带事务,无法连续切库。 以下特性和约定摘录自官方文档:[dynamic-datasource ](https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611) - 特性 - 支持 **数据源分组** ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 - 支持数据库敏感配置信息 **加密(可自定义)** ENC()。 - 支持每个数据库独立初始化表结构schema和数据库database。 - 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。 - 支持 **自定义注解** ,需继承DS(3.2.0+)。 - 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。 - 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。 - 提供 **自定义数据源来源** 方案(如全从数据库加载)。 - 提供项目启动后 **动态增加移除数据源** 方案。 - 提供Mybatis环境下的 **纯读写分离** 方案。 - 提供使用 **spel动态参数** 解析数据源方案。内置spel,session,header,支持自定义。 - 支持 **多层数据源嵌套切换** 。(ServiceA >>> ServiceB >>> ServiceC) - 提供 **基于seata的分布式事务方案** 。 - 提供 **本地多数据源事务方案。** - 约定 1. 本框架只做 **切换数据源** 这件核心的事情,并**不限制你的具体操作**,切换了数据源可以做任何CRUD。 2. 配置文件所有以下划线 `_` 分割的数据源 **首部** 即为组的名称,相同组名称的数据源会放在一个组下。 3. 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。、 4. 默认的数据源名称为 **master** ,你可以通过 `spring.datasource.dynamic.primary` 修改。 5. 代码块里主动切换 > 方法上的注解优 > 类上注解(就近原则) 6. DS支持继承抽象类上的DS,支持继承接口上的DS。 **注**:官网文档**只有基础篇可免费查阅**,其他内容查阅需付费`29.9¥`,因此如果基础篇所讲解功能无法满足需求(业务场景比较复杂)且不愿付费请勿使用本框架! ## 三.编码实现 业务说明:从A数据源查询出学生数据同时从B数据源查询出教师数据。 技术选型:SpringBoot 2.7.6 + MyBatis-Plus 3.5.4 + MySQL 8.0 1)引入Maven坐标 ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.2</version> </dependency> ``` 2)创建模拟数据库表, ```sql # A 数据源 create DATABASE testA; use testA; CREATE TABLE student( id int, username VARCHAR(12) ); # B 数据源 create DATABASE testB; use testB; CREATE TABLE teacher( id int, username VARCHAR(12) ); ``` 3)在yml文件中配置数据源 ```yml spring: datasource: dynamic: primary: testA #设置默认的数据源或者数据源组,默认值即为master strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: testA: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/testA username: root password: root testB: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/testB username: root password: root ``` 4)可以用MyBatis-X插件快速生成项目骨架。 <img src="https://pic.code-nav.cn/post_picture/1619305100524748802/i37xAp5S-1.webp" alt="1.jpg" width="100%" /> 5)使用`@DS`注解在mapper文件中指定数据源。注意,数据源名称需要与yml配置中的一致。 ```java // 指定学生表的数据源为testA @DS("testA") public interface StudentMapper extends BaseMapper<Student> { } // 指定教师表的数据源为testB @DS("testB") public interface TeacherMapper extends BaseMapper<Teacher> { } ``` 5)创建测试类进行测试 ```java @SpringBootTest public class MultipleDataSourceTest { @Resource private StudentService studentService; @Resource private TeacherService teacherService; @Test void testSelect() { System.out.println("====== 学生表数据 ======"); for (Student student : studentService.list()) { System.out.println(">>> " + student); } System.out.println("====== 教师表数据 ======"); for (Teacher teacher : teacherService.list()) { System.out.println(">>> " + teacher); } } } ``` 6)相关执行日志查看,可以通过日志看到已经成功加载两个数据源并且指定默认数据源为[testA],同时我们所查询的数据也能正常出来。 ```java INFO 312 --- [main] com.zaxxer.hikari.HikariDataSource: testA - Starting... INFO 312 --- [main] com.zaxxer.hikari.HikariDataSource: testA - Start completed. INFO 312 --- [main] com.zaxxer.hikari.HikariDataSource: testB - Starting... INFO 312 --- [main] com.zaxxer.hikari.HikariDataSource: testB - Start completed. INFO 312 --- [main] c.b.d.d.DynamicRoutingDataSource: dynamic-datasource - add a datasource named [testB] success INFO 312 --- [main] c.b.d.d.DynamicRoutingDataSource: dynamic-datasource - add a datasource named [testA] success INFO 312 --- [main] c.b.d.d.DynamicRoutingDataSource: dynamic-datasource initial loaded [2] datasource,primary datasource named [testA] ====== 学生表数据 ====== >>> Student(id=1, username=学生1) >>> Student(id=2, username=学生2) >>> Student(id=3, username=学生3) >>> Student(id=4, username=学生4) >>> Student(id=5, username=学生5) ====== 教师表数据 ====== >>> Teacher(id=1, username=教师1) >>> Teacher(id=2, username=教师2) >>> Teacher(id=3, username=教师3) >>> Teacher(id=4, username=教师4) >>> Teacher(id=5, username=教师5) INFO 312 --- [ionShutdownHook] c.b.d.d.DynamicRoutingDataSource: dynamic-datasource start closing .... INFO 312 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource: testB - Shutdown initiated... INFO 312 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource: testB - Shutdown completed. INFO 312 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource: testA - Shutdown initiated... INFO 312 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource: testA - Shutdown completed. INFO 312 --- [ionShutdownHook] c.b.d.d.DynamicRoutingDataSource: dynamic-datasource all closed success,bye ``` ## 四.补充说明 以上示例可以完成基本的多数据源切换需求,对于一些复杂场景,由于官方文档付费,请根据需要使用。例如: - 在需要切换数据源的代码最外层加上了事务注解`@Transactional`将会导致数据源切换失效,上述代码加上注解运行将报错(由于无法切换数据源,将使用默认数据源,如果默认数据源中没有对应的表将报错),在网上检索发现通过添加官方提供的`@DSTransactional`即可实现事务下数据源切换: ```java @Test @Transactional void testSelect() { System.out.println("====== 学生表数据 ======"); for (Student student : studentService.list()) { System.out.println(">>> " + student); } System.out.println("====== 教师表数据 ======"); for (Teacher teacher : teacherService.list()) { System.out.println(">>> " + teacher); } } ``` - 在多线程情况下,如果使用编码方式实现数据源切换也将导致问题,需要进行一些其他处理。 最后,补充说明想表达的由于官网文档是付费的,不付费的话许多问题只能搜索寻找解决方法比较麻烦,因此,请根据自身业务情况选择使用本框架或其他框架。
大家好,我是程序媛雪儿,有两天没和大家唠唠技术了,今天我们聊聊AI流式调用,这块我也是总结鱼皮哥AI答题应用平台项目中的技术,大家看了有兴趣可以学习一下,实操体验一下怎么应用哦~ 现在AI已经在各大软件中广泛应用,你们有没有想过如何在AI应用中实时处理数据流?今天咱们聊聊怎么用SSE Rxjava处理实时数据流。 一、SSE是什么 SSE(后端主动推送给前端) 前端发请求并和后端建立连接,后端实时推动数据给前端 SSE的重要特点 单向通信:SSE只支持服务器向客户端的单向通信 文本格式:SSE使用纯文本格式传输数据,HTTP响应的text/event-stream 保持连接:SSE会保持一个持久的HTTP连接,实现服务器向客户端推送数据 自动重连:如果连接中断,浏览器会尝试自动重连 为什么处理AI流式数据要用SSE? AI对话是服务器单向给客户端流式传输数据,用SSE更加 简单:不需要WebSocket那么复杂,基本的HTTP和JavaScript就搞定。 实时更新:长连接,随时获取最新数据。 轻量:适合频繁更新的小数据量。 二、RxJava是什么 RxJava是一个基于事件驱动的、利用可观测序列来实现异步编程的类库 1、事件驱动 事件可以是任何事情。比如用户的点击操作、网络请求的结果、文件的读写等 2、可观测序列 可观测序列指一系列按照时间序列发出的数据项,可以被观察处理 RxJava的核心知识点 观察者模式 RxJava是基于观察者模式实现的。 观察者:观测数据流 observer 被观察者:实时传输数据流 observable和flowable observable适合处理相对较小的、可控的、不会产生大量数据的场景,不具备背压能力 flowable具备背压能力。也就是说,如果生产数据过快,超过了大多数数据消费者速度,flowable提供了多种背压策略来处理这种情况,保证大量数据仍然能稳定 建立订阅关系 被观察者.subscribe(观察者) 三、后端Rxjava流式调用的demo // region // 生成AI题目流式生成 @GetMapping("/ai_generate/sse") public SseEmitter aiGenerateSSE(AiGenerateRequest aiGenerateRequest){ // 获取应用信息 // 建立SSE连接对象,0表示永不超时 SseEmitter sseEmitter = new SseEmitter(0L); // AI生成(调用AI流式接口),SSE流式返回 Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, null); // 截取流式数据进行数据处理后返回给前端 modelDataFlowable // 指定观察者的线程池 .observeOn(Schedulers.io()) // 先获取数据 .map(modelData -> modelData.getChoices().get(0).getDelta().getContent()) // 先处理数据把没用的空格都去掉 .map(message -> message.replaceAll("\\s","")) .filter(StrUtil::isNotBlank) .flatMap(message ->{ List<Character> characterList = new ArrayList<>(); for (char c : message.toCharArray()) { characterList.add(c); } return Flowable.fromIterable(characterList); }) .doOnNext(c -> { // 按照业务需要处理数据 }) .doOnError((e) -> log.error("异常处理")) .doOnComplete(()->{ sseEmitter.complete(); }) .subscribe(); return sseEmitter; } // endregion 04 四、前端使用sse开启连接,获取数据 /** * 提交流式生成题目,一个一个的生成题目 */const handleSSESubmit = async () => { // 创建SSE请求 const eventSource = new EventSource( // 手动填写完整的后端地址 "http://localhost:8101/ai_generate/sse" ); let first = true; // 接收消息 eventSource.onmessage = function(event) { console.log(event.data); if(first){ console.log('第一次连接'); first = !first; } console.log('传输数据',event.data); }; // 报错或连接关闭时触发 eventSource.onerror = function(event) { // 关闭SSE连接 if(event.eventPhase === EventSource.CLOSED){ console.log('关闭连接'); eventSource.close(); } }; // 连接打开时触发 eventSource.onopen = function(event) { console.log('连接成功'); };}; 基本上就是后端用Rxjava框架观察处理数据,处理成前端需要的形式传给前端,前端用SSE的方式接收数据,进行实时的数据展示。是不是很简单?那今天雪儿的分享就结束啦,也欢迎各位宝宝关注我的微信公众号,程序媛雪儿,雪儿基本上天天都会分享技术碎片,我们一起学习,一起进步,加油哦~#知识碎片# #学习总结#
大家好,我是程序媛雪儿,今天不说晚安,说早上好,哈哈,今天我们唠唠redis数据删除策略。 一、惰性删除 设置key的过期时间,当需要该key,再检查是否过期,如果过期,就删掉,没过期,就返回(只有key过期才会检查) set name zhangsan 10 get name 优点:不会额外消耗cpu 缺点:大量过期的数据占了内存,未及时处理 二、定期删除 每隔一段时间,就对key进行检查(从一定数量的数据库抽取一定数量的key),并删除其中的过期key 两种模式 slow模式:默认是10hz,每次不超过25ms,可以通过修改redis.conf的hz选项来调整这个次数 fast模式:两次间隔不低于2ms,每次耗时不超过1ms 优点:可以通过限制操作删除的执行时长和频率来控制对cpu和内存的影响 缺点:难确定删除的执行时长和频率 Redis的过期删除策略:惰性删除 定期删除配合使用 三、内存淘汰机制(是上篇讲的) 8种策略,nginx.conf中的配置 maxmemory-policy noeviction #默认策略,不淘汰任何key,内存满了不允许写入新数据 volatile-lru:从设置了过期时间的数据集中挑选最近最少使用的数据淘汰。 allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。 volatile-lfu:从设置了过期时间的数据集中挑选使用频率最低的数据淘汰。 allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。 volatile-random:从设置了过期时间的数据集中随机挑选数据淘汰。 allkeys-random:从数据集中随机挑选数据淘汰。 volatile-ttl:从设置了过期时间的数据集中挑选剩余生存时间最短的数据淘汰。 noeviction:禁止驱逐数据,新的写操作会报错。 还是推荐大家像我上篇文章那样,画个图图记这个知识点 其中,解释一下LRU和LFU算法 LRU(least recently used)最近最少使用,当前时间-最后访问时间,这个值越大越优先淘汰,换句话说就是淘汰最长时间没访问的 LFU (least frequently used ) 最少频率使用,会统计每个key的访问频率,值越小淘汰优先级越高 今天的知识碎片到这里就结束啦~咱们顺便唠唠嗑,我最近每天下班在健身房泡一个小时,跑步机30-40min,拉伸10min,练练背,玩玩哑铃做做力量训练,我觉得真的会很舒服,咱们不管是写代码还是实验室里搞研究,天天坐的时间太久了,能时不时舒展一下,是一件很棒的事情哦,之前雪儿经常肩膀痛,现在已经不痛啦,很推荐各位宝子试一下昂~ 欢迎大家关注我的微信公众号程序媛雪儿,雪儿会经常分享知识碎片和大家一起进步#知识碎片#
一、是什么 Redis内存不够的时候,此时向redis中添加新的key,那么,redis会按照你配置的规则将数据删除掉 二、两个算法 先说两个算法,LRU、LFU LRU(least recently used)最近最少使用,当前时间-最后访问时间,这个值越大越优先淘汰,换句话说就是淘汰最长时间没访问的 LFU (least frequently used ) 最少频率使用,会统计每个key的访问频率,值越小淘汰优先级越高 三、八种策略 8种策略,nginx.conf中的配置 maxmemory-policy noeviction #默认策略,不淘汰任何key,内存满了不允许写入新数据 #知识碎片# 图片只能以这种形式出现,这篇不带这个图片真是不能发,今天雪儿的分享到这里就结束啦,如果大家想详细了解,欢迎关注同名微信公众号,里面有带图解的笔记,雪儿祝大家秋招顺利,早日发财,那么,晚安啦~[月亮] #知识碎片#
大家好,我是程序媛雪儿,今天给大家带来的是redis持久化。 一、RDB(redis数据快照) 含义:Redis数据快照,也就是把内存中的所有数据整体记录到磁盘中,如果redis实例故障重启,可以从磁盘中读取快照文件,恢复数据 实操命令 redis-cli #连接redis# save #Redis主进程执行rdb,会阻塞所有命令 bgsave # 开启子进程执行rdb,避免主进程受到影响 redis.conf文件下也有对应的设置 # 900s内,如果至少有1个key被修改,则执行bgsave save 900 1 save 300 10 save 60 10000 执行原理 执行bgsave命令的时候会把主进程fork得到子进程,子进程和主进程共享同一片物理内存,这时子进程就可以读取内存文件存到rdb中了 这种方式存在脏读的问题,所以fork采用的是copy-on-write技术,也就是让数据加上read-only的锁 当主进程执行读操作时,访问共享内存 当主进程执行写操作时,只能先拷贝一份数据,执行写操作 二、AOF(追加文件) 含义:可以称为追加文件,redis所处理的每一个写命令都会记录到AOF中,可以看作是命令文件 aof默认是关闭的,需要修改redis.conf配置来开启 # 是否开启AOF功能,默认是no appendonly yes # aof文件的名称 appendfilename "appendonly.aof" # aof命令记录的频率 appendfsync no/always/everysec 因为AOF是记录命令,所以要比记录数据的RDB大得多,因为AOF会记录对同一个key多次写操作,但只有最后一次才有意义,那么为了减少aof体积,我们可以合并命令,通过bgrewriteaof命令,可以让aof文件执行重写命令,用最少的命令达到相同的效果 redis.conf配置aof触发阈值重写aof # 文件比上次超多百分之百,也就是上次aop文件两倍触发重写 auto-aof-rewrite-percentage 100 # aop文件体积最小达到多大触发重写 auto-aof-rewrite-min-size 64mb 今天雪儿的分享到这里就结束啦,如果大家想详细了解,欢迎关注同名微信公众号和b站视频,里面有带图解的笔记和视频资料,雪儿祝大家秋招顺利,早日发财,那么,晚安啦~[月亮] #知识碎片#
大家好,我是程序媛雪儿,今天给大家带来的知识碎片是缓存雪崩。 一、是什么 缓存雪崩:因为大量的key过期,或者redis直接宕机,导致大量的请求到达数据库,带来了巨大的压力。 二、解决方案 1、如果是因为大量的key过期,那就在设置过期的时间TTL加个随机值,可以减少key同时过期的概率 2、如果是redis宕机了,那就在部署redis的时候,使用redis集群,比如哨兵模式、三主三从的集群模式,来提高服务的可用性 3、给缓存业务添加降级限流策略,比如nginx或spring cloud gateway去添加限流策略 4、给业务添加多级缓存 ,比如把Guava或Caffeine作为一级缓存,nginx作为二级缓存 今天雪儿的分享到这里就结束啦,如果大家想详细了解,欢迎关注同名微信公众号和b站视频,里面有带图解的笔记和视频资料,雪儿祝大家学的开心,学有所获,晚安~[月亮]#知识碎片#
主题:缓存击穿 一、是什么 缓存击穿:给某个Key设置了过期时间,key过期的时候,刚好有大量的请求过来,瞬间把数据库压垮 也就是key过期,发请求发现没有,从数据库往redis存数据的这50ms把数据库击垮 二、解决方案 解决方案1:互斥锁 流程:如果出现查询不到数据的情况,直接对该数据加锁,然后去请求db,把数据写入缓存,才释放锁,如果这个操作过程中其他线程想要获取该数据,会发现被上了锁,就休眠一会再试,直到缓存命中为止。 优点:可以保证数据的强一致性 缺点:性能比较差,所有线程都需要等到数据写入缓存才能返回数据 解决方案2:逻辑过期 所有数据加个过期字段 {"id":1,"expire":156563269} 流程:如果查询数据,发现数据已经过期,加互斥锁,开启新进程去完成从db中往缓存取数据,修改过期时间等,本进程直接返回过期数据,如果此时还有其他进程也访问该数据,但是发现加了互斥锁,直接返回过期数据。直到新线程把数据写到缓存,释放了互斥锁,线程就可以获取新数据了。 优点:高可用、性能好 缺点:返回的数据不一致 使用建议:看应用场景,如果需要高度一致,比如和钱相关的金融业务,那么必须要用互斥锁,如果为了快速响应,提高用户体验,可以用逻辑过期这种处理方式。 回顾 缓存穿透:查询一个不存在的数据,mysql查询不到的数据也不会直接写入缓存,导致每次请求都查数据库。 解决方案: 1、缓存空数据 2、使用布隆过滤器 缓存击穿:给某个Key设置了过期时间,key过期的时候,刚好有大量的请求过来,瞬间把数据库压垮 解决方案: 1、互斥锁 2、逻辑过期 今天雪儿的分享就到这里啦,如果大家想看带图解的可以搜微信公众号程序媛雪儿查看带图解的笔记,如果想看视频,可以在b站上搜程序媛雪儿,视频标题就是缓存击穿。希望大家学有所获,学的开心,我们下次见~#知识碎片#
大家好,我是程序媛雪儿,今天想和大家聊聊redis缓存穿透。 一、什么是缓存穿透 缓存穿透:查询一个不存在的数据,mysql查询不到的数据也不会直接写入缓存,导致每次请求都查数据库。 二、解决方案 方案一:缓存空数据,就是把空数据也缓存到redis中 优点:简单 缺点:消耗内存。如果该id有了数据,从缓存里取到的还是空数据,会发生数据不一致问题 方案二:布隆过滤器 布隆过滤器算法如下: 简单说就是把数据id用hash计算3次,得到三个位数,如果有值就记为1。 那么通过布隆过滤器判断id是否有值,就是把用户传入的id用hash计算3次,看一下对应位置是否都是1,如果不是,说明数据不存在。 缺点:存在误判率,原因如下 id3的数据不存在,但是因为反hash后的值正好对应id1和id2的某几位数值,导致布隆过滤器认为id3存在。 解决方案: 可以通过扩大数组来减少误判,但是会增加内存消耗 本来我这篇文章是有图解和测试代码的,但是在星球不好发,同学们如果感兴趣完整的知识可以去我的微信公众号程序媛雪儿观看。雪儿打算每天发点技术知识点分享给大家,和大家一起学习,我们一起加油~[爱心][爱心]#知识碎片#
MySQL基础知识:DDL、DML、DQL、DCL及TPL的使用
<html> <head></head> <body> <div class="content ql-editor"> <p>分享 MySQL 的一些基础知识,主要关于 DDL、DML、DQL、DCL 及 TPL 的使用</p> <p><br></p> <p>更好的阅读体验可移步到:<a href="https://www.codefather.cn/post/1779534142459944962" target="_blank">MySQL基础知识:DDL、DML、DQL、DCL及TPL的使用 - 编程导航 (</a><a href="http://codefather.cn" target="_blank">code-nav.cn</a><a href="https://www.codefather.cn/post/1779534142459944962" target="_blank">)</a></p> <p><br></p> <p># SQL分类</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/ddwxqy65.jpeg"></p> <p><br></p> <p>## DDL数据定义语言</p> <p><br></p> <p>> 针对数据库对象进行操作,如数据库、表、视图等对象</p> <p><br></p> <p>### 1、创建数据库</p> <p><br></p> <p>`create database 数据库名 [default charset utf8] ;`</p> <p><br></p> <p>数据库名要求:不区分大小写,英文,见名知义</p> <p><br></p> <p>数据库默认字符集:latin1</p> <p><br></p> <p>### 2、创建表</p> <p><br></p> <p>```sql</p> <p>create table 表名(</p> <p>列名 数据类型(长度),</p> <p>......</p> <p>列名 数据类型(长度)</p> <p>) [character set utf8 collate utf8_general_ci] ;</p> <p>```</p> <p><br></p> <p>### 3、修改表结构</p> <p><br></p> <p>3.1 修改表名:</p> <p><br></p> <p>`alter table 原表名 rename [to] 新表名;`</p> <p><br></p> <p>3.2 修改表中的列:(列名、列类型、列长度)</p> <p><br></p> <p>`alter table 表名 change 原列名 新列名 新类型(新长度);`</p> <p><br></p> <p>3.3 新增一个列:</p> <p><br></p> <p>`alter table 表名 add 新列名 新类型(新长度);`</p> <p><br></p> <p>3.4 删除一个列:</p> <p><br></p> <p>`alter table 表名 drop 原列名;`</p> <p><br></p> <p><br></p> <p><br></p> <p>### 4、删除</p> <p><br></p> <p>删除table:`drop table 表名;`</p> <p><br></p> <p>删除数据库:`drop database 数据库名;`</p> <p><br></p> <p><br></p> <p>### 5、数据类型</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/yo7emczv.jpeg"></p> <p><br></p> <p><br></p> <p>## DML数据操作语言</p> <p><br></p> <p>> 操作的是表中的数据信息</p> <p><br></p> <p>主要分为写数据(新增、删除、修改)、读数据(查询)</p> <p><br></p> <p>### 1、新增记录</p> <p><br></p> <p>`insert into 表名 (列名, ......, 列名) values (值, ......, 值);`</p> <p><br></p> <p>**注意:**</p> <p><br></p> <p>a. 如果新增全部列,可以省略表名后的括号及内容,此时values后的值的个数、类型、顺序要与表中列保持一致。</p> <p><br></p> <p>b. 若想要一条insert语句新增多个记录,每个 (值, ......, 值) 之间用逗号隔开。</p> <p><br></p> <p><br></p> <p>### 2、删除记录</p> <p><br></p> <p>`delete from 表名 [where ....] ;`</p> <p><br></p> <p>没有where条件会删除表中所有数据</p> <p><br></p> <p><br></p> <p>### 3、修改记录</p> <p><br></p> <p>`update 表名 set 列=值, ... , 列=值 [where ...] ;`</p> <p><br></p> <p>没有where条件会将对应的列的数据全部都修改</p> <p><br></p> <p><br></p> <p>## DQL数据查询语言</p> <p><br></p> <p>### 1、查询记录</p> <p><br></p> <p>`select 列名, ... , 列名 from 表名 [where ...] ;`</p> <p><br></p> <p>需要起别名使用**as**关键字 `列名 as 别名`</p> <p><br></p> <p>去重使用关键字`**distinct**`** **</p> <p><br></p> <p>根据列去重复,如果有一样的列信息,将一样的信息合并,行数可能会减少。</p> <p><br></p> <p>`distinct 列1,列2` 两个列以上的,会当作一个整体,整体一样的才去重,否则全部显示。</p> <p><br></p> <p><br></p> <p>### 2、条件筛选</p> <p><br></p> <p>用来筛选符合条件的记录行,并不是控制显示的列。</p> <p><br></p> <p>按照某个列或某些条件进行筛选</p> <p><br></p> <p>使用**where**关键字,其后可以跟:</p> <p><br></p> <p>1、比较运算符:**>**、 **>=**、 **<**、 **<=**、 **!=**、 **=**</p> <p><br></p> <p>2、算术运算符:**+**、**-**、*****、**/**</p> <p><br></p> <p>3、逻辑运算符:and、or、not(如果and和or同时出现,and优先级更高)</p> <p><br></p> <p>4、[not] between ... and ...</p> <p><br></p> <p>5、[not] in (... , ...)</p> <p><br></p> <p>6、[not] like `%xxx_`(**%** 匹配0~n个字符,**_** 仅匹配一个字符)</p> <p><br></p> <p><br></p> <p>### 3、排序</p> <p><br></p> <p>`order by 列名 asc/desc`(连接在查询语句后,先筛选再排序)</p> <p><br></p> <p>asc 升序 (默认)</p> <p><br></p> <p>desc 降序</p> <p><br></p> <p>联合排序:`order by 列名1, 列名2`</p> <p><br></p> <p>按书写顺序依次排序,不同的列可以有不同的排序规则</p> <p><br></p> <p><br></p> <p>### 4、函数</p> <p><br></p> <p>1、MySQL的函数都有返回值,但不一定都有参数列表。</p> <p><br></p> <p>2、函数直接放置在SQL语句中相当于调用,可以放置在:</p> <p><br></p> <p>a. 查询中用来显示的部分`select 函数(列) from 表名;`</p> <p><br></p> <p>b. 条件筛选where的后面</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/eomjav1u.jpeg"></p> <p><br></p> <p><br></p> <p>### 5、分组条件</p> <p><br></p> <p>`group by 列`</p> <p><br></p> <p>一旦搭配了分组条件,select能展示的信息就只有两种:</p> <p><br></p> <p>1、分组条件(按什么分组就可显示什么)</p> <p><br></p> <p>2、聚合函数(结果就剩一个值)</p> <p><br></p> <p>查询时分组了,查询的行数会减少。</p> <p><br></p> <p>比如按班级分组统计每个班的人数,那么原来可能有多个班级数据,一个班级可能又有多条学生数据,但现在查询出来就一个班级一条数据,统计着每个班级的人数。</p> <p><br></p> <p><br></p> <p>先分组?还是先筛选条件?</p> <p><br></p> <p>1、先筛选后分组:where + group by</p> <p><br></p> <p>2、先分组再筛选:group by + having</p> <p><br></p> <p>3、先筛选一部分后分组,分完组再筛选符合条件的:where + group by + having</p> <p><br></p> <p><br></p> <p>**优先级**:where > group by > having > order by</p> <p><br></p> <p><br></p> <p>### 6、嵌套</p> <p><br></p> <p>一个完整的SQL语句中嵌套了另一个完整的SQL语句(用括号括起来优先级更高)</p> <p><br></p> <p>1、嵌套可以将一个查询的结果当作另一个查询的条件来使用(同一张表格)</p> <p><br></p> <p>2、可以将查询的结果当作一张表格,在这个表格的基础上再次查询</p> <p>- 注意:在将结果当作表格时,需要给这个表格起别名才能用(表别名不需要as关键字,空格即可)</p> <p><br></p> <p>3、嵌套将一个查询结果当作条件时,使用的表格可以不是同一张,将另外一张表格查询的信息,当作当前表格的查询条件。(即:与第1点不同在于是可以**不同表格**)</p> <p><br></p> <p><br></p> <p>### 7、模糊查询</p> <p><br></p> <p>使用`like`关键字</p> <p><br></p> <p>% :表示任意 0 个或多个字符。</p> <p><br></p> <p>_: 表示任意单个字符。</p> <p><br></p> <p><br></p> <p>### 8、limit分页查询</p> <p><br></p> <p>语法:`select * from xxx limit a,b;`</p> <p><br></p> <p>a:想要显示的起始行索引,偏移量从0开始(包括此行)。</p> <p><br></p> <p>b:想要显示的行数(多少条记录)。</p> <p><br></p> <p><br></p> <p>### in关键字</p> <p><br></p> <p>**in()** 括号内是待查询的子集,满足下面一个即可:</p> <p><br></p> <p>1、常量固定值</p> <p><br></p> <p>2、另一条SQL语句查询出来的结果</p> <p><br></p> <p>默认以`=`进行比较</p> <p><br></p> <p><br></p> <p>以下三个关键字使用与in相似,查询是否满足子集中的条件,区别在于不允许写固定值,只允许写SQL语句,通过嵌套来获取子集。</p> <p><br></p> <p><br></p> <p>### any关键字</p> <p><br></p> <p>满足查询子集中的某一个即可</p> <p><br></p> <p>### some关键字</p> <p><br></p> <p>与any一样</p> <p><br></p> <p>### all关键字</p> <p><br></p> <p>满足查询子集中的全部才可以</p> <p><br></p> <p>## 补充:集合操作union</p> <p><br></p> <p>用union连接两条查询语句,将两个查询结果进行合并(是那种上下拼合的效果)</p> <p><br></p> <p>1、要求前后两个查询子集的列数一致</p> <p><br></p> <p>2、对应的类型没有要求</p> <p><br></p> <p>3、拼接后显示的列名是前一个查询子集默认的列名</p> <p><br></p> <p><br></p> <p>**union和union all的区别:**</p> <p><br></p> <p>union 合并后做去重复的处理,性能比较慢,如果有重复元素,记录的是第一次出现的那一行。</p> <p><br></p> <p>union all 将两个查询到的子集直接合并,不做任何处理,性能比较快。</p> <p><br></p> <p><br></p> <p>## 补充:列的约束</p> <p><br></p> <p>表格中列的约束全部都是在创建表格之后做的表格列结构的修改。</p> <p><br></p> <p>需要用到DDL语句进行操作(alter、drop)</p> <p><br></p> <p><br></p> <p>### 主键约束(primary key)</p> <p><br></p> <p>- 每一个表格内只能有一个列被设置为主键约束</p> <p>- 主键约束通常是用来标记表格中数据的唯一存在</p> <p>- 主键约束要求当前的列 **不能为null值**</p> <p>- 主键约束要求当前的列 **值是唯一存在的 不能重复**</p> <p><br></p> <p>添加主键约束:</p> <p><br></p> <p>语法:`alter table 表名 add constraint 约束名字 约束类型 (列);`</p> <p><br></p> <p>例:`alter table myclass add constraint pk_myclass primary key (classid);`</p> <p><br></p> <p>简写:`alter table 表名 add primary key (列);`</p> <p><br></p> <p><br></p> <p>主键自增:</p> <p><br></p> <p>语法1:`alter table 表名 modify 列名 字段类型 auto_increment;`</p> <p><br></p> <p>例:`alter table myclass modify classid int(4) auto_increment;`</p> <p><br></p> <p>语法2:`alter table 表名 change 列名 字段类型 auto_increment;`</p> <p><br></p> <p>例:`alter table myclass change classid int(4) auto_increment;`</p> <p><br></p> <p>没有做起始值的说明,主键列的值会从1开始。</p> <p><br></p> <p>设置自增起始值:`alter table 表名 auto_increment = 起始值;`</p> <p><br></p> <p><br></p> <p>删除主键约束:</p> <p><br></p> <p>`alter table 表名 drop primary key;`</p> <p><br></p> <p>注意:删除主键约束以后,不重复的特性取消了,但非空特性还在。</p> <p><br></p> <p>需要手动取消非空:`alter table 表名 modify 字段名 字段类型 null;`</p> <p><br></p> <p><br></p> <p>### 唯一约束(unique key)</p> <p><br></p> <p>- 可以为表格中的某一个列添加唯一约束,唯一约束在表格中可以存在多个列</p> <p>- 唯一约束表示的是列的**值不能重复,但可以为空**</p> <p><br></p> <p>添加唯一约束:</p> <p><br></p> <p>语法:`alter table 表名 add constraint 约束名 约束类型 (列); `</p> <p><br></p> <p>例:`alter table myclass add constraint uk_myclass unique [key] (loc);`</p> <p><br></p> <p>简写:`alter table 表名 add unique key(字段); `约束名为默认的列名</p> <p><br></p> <p><br></p> <p>删除唯一约束:</p> <p>`alter table 表名 drop index 约束名;`</p> <p><br></p> <p><br></p> <p>### 非空约束</p> <p><br></p> <p>- 在表格中的某一个列上添加非空约束</p> <p>- 当前列的值不能为null</p> <p><br></p> <p>添加非空约束:</p> <p><br></p> <p>语法1:</p> <p><br></p> <p>`alter table 表名 modify 原列名 原类型 原长度 [not] null default xxx;`</p> <p><br></p> <p>语法2:</p> <p><br></p> <p>`alter table 表名 change 原列名 原列名 原类型 原长度 [not] null default xxx;`</p> <p><br></p> <p>删除非空约束:</p> <p><br></p> <p>`alter table 表名字 modify 列 类型 长度 null;`</p> <p><br></p> <p>`alter table 表名字 change 列 列 类型 长度 null;`</p> <p><br></p> <p><br></p> <p>### 检查约束(check)</p> <p><br></p> <p>列在存值的时候做一个细致的检查</p> <p><br></p> <p>例如:范围是否合理</p> <p><br></p> <p>`alter table student add constraint ck_sage check( sage>15 and sage<30);`</p> <p><br></p> <p><br></p> <p>### 外键约束(foreign key)</p> <p><br></p> <p>- 约束自己表格内的信息不能随意填写</p> <p>- 受到另外一个表格某一个列的影响,当前列的值要去另外一张表格内寻找(另外一张表格的列是唯一的【主键、唯一】)</p> <p>- 表格中可以有多个列被设置为外键约束</p> <p>- 当前列的值可以为空,可以重复</p> <p><br></p> <p>添加外键约束:</p> <p><br></p> <p>`alter table 表名字 add constraint fk_当前表_关联表 foreign key(列) references 另一个表(列);`</p> <p><br></p> <p>简写:</p> <p><br></p> <p>`alter table student add foreign key(列) references 另一个表(列);`</p> <p><br></p> <p>注意: 如果是简写的形式添加外键,外键的名字不是默认列名</p> <p><br></p> <p><br></p> <p>查看外键约束:</p> <p><br></p> <p>【通过`show keys from 表; `或 `desc 表;`或 `show create table 表名;`】</p> <p><br></p> <p>PRI UNI MUL---->multiple(多样 并联)</p> <p><br></p> <p><br></p> <p>删除外键约束:</p> <p><br></p> <p>`alter table 表名字 drop foreign key 约束名字;`</p> <p><br></p> <p>注意:通过上述语句其实已经将外键约束删掉了</p> <p><br></p> <p>但会自动在当前表格内添加一个新的key</p> <p><br></p> <p>需要再次手动将这个生成的key删掉,外键约束才真的删除干净</p> <p><br></p> <p>`alter table 表名字 drop key 约束名字;`</p> <p><br></p> <p><br></p> <p>## 联合查询</p> <p><br></p> <p>### 1、广义笛卡尔积</p> <p><br></p> <p>将两张表格或多张表格,进行无条件的拼接。(即使两张表格没有关系也可以进行拼接)</p> <p><br></p> <p>语法:`select * from A , B `</p> <p><br></p> <p>列的个数 :A和B表之和,A数据在左边显示,B数据在右边显示(按上述语法)</p> <p><br></p> <p>行的个数 :A和B表乘积</p> <p><br></p> <p><br></p> <p>### 2、等值连接</p> <p><br></p> <p>语法:`select * from A , B where 条件`</p> <p><br></p> <p>在拼接后的一张大表格的基础上进行了where的筛选 -> 等值连接</p> <p><br></p> <p>在笛卡尔积的基础上进行了where条件筛选,只不过筛选条件都是按照值相等来进行的。</p> <p><br></p> <p>拼接后相当于在一张大的表格中挑选有用的记录,性能比较慢。</p> <p><br></p> <p><br></p> <p>### 3、外连接</p> <p><br></p> <p>外连接分为左外连接和右外连接。</p> <p><br></p> <p>语法:`select * from A left/right [outer] join B on 条件`</p> <p><br></p> <p>可省略outer关键字</p> <p><br></p> <p>1)两张表格A和B ,谁的数据在左边显示?</p> <p><br></p> <p>A和B表格出现的顺序决定了 谁在左边 谁在右边</p> <p><br></p> <p>A表格先出现,A左边显示</p> <p><br></p> <p>B表格后出现,B右边显示</p> <p><br></p> <p><br></p> <p>2)left和right来决定以哪一个表格的数据作为基准</p> <p><br></p> <p>作为基准的表格数据必须全部显示出来</p> <p><br></p> <p>非基准的表格按照on条件与之拼接,若找到则正常显示,若找不到满足条件的则显示 null。</p> <p><br></p> <p>因此,外连接查询结果有可能比等值连接的最终结果多一部分数据。</p> <p><br></p> <p>### 4、内连接(自连接)</p> <p><br></p> <p>语法:`select * from A inner join B on 条件`</p> <p><br></p> <p>特点:</p> <p><br></p> <p>1)查询出的结果与等值连接的结果一致。</p> <p><br></p> <p>2)内连接不分左右,不能省略inner关键字。</p> <p><br></p> <p>3)A和B是可以不同的两张表格。</p> <p><br></p> <p>4)A和B也可以是相同的一张表格,但必须给表格起别名,可能还需要当前的表格有一定的设计。</p> <p><br></p> <p><br></p> <p>## DCL数据控制语言</p> <p><br></p> <p>数据控制语言Data Control Language:**控制用户的权限**</p> <p><br></p> <p>### 1、先创建一个新用户</p> <p><br></p> <p>语法:`create user '用户名'@'IP' identified by '密码'; `</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/2ds2zxbv.jpeg"></p> <p><br></p> <p><br></p> <p>### 2、查看用户权限</p> <p><br></p> <p>语法:`show grants for '用户名'@'IP';`</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/b81vkz2s.jpeg"></p> <p><br></p> <p>用户被创建成功 (只有一个默认的权限 Usage 只允许登录 不允许做其他事情)</p> <p><br></p> <p><br></p> <p>### 3、给用户赋予权限</p> <p><br></p> <p>语法:`grant 权限 on 数据库名.表名 to '用户'@'IP'; `</p> <p><br></p> <p>赋予权限之后最好做一个刷新`flush privileges;`</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/rct0f8s2.jpeg"></p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/lgpkln6y.jpeg"></p> <p><br></p> <p><br></p> <p>### 4、回收用户权限</p> <p><br></p> <p>语法:`revoke 权限 on 数据库名.表名 from '用户名'@'IP';`</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/1hrwinbf.jpeg"></p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/lssd3bgd.jpeg"></p> <p><br></p> <p>上述回收了用户删除数据表或数据库的权限。</p> <p><br></p> <p><br></p> <p>### 5、MySQL权限分类</p> <p><br></p> <p>1、数据库/数据表/数据列权限:</p> <p><br></p> <p>Create: 建立新的数据库或数据表</p> <p><br></p> <p>Alter : 修改已存在的数据表(例如增加/删除列)</p> <p><br></p> <p>Drop : 删除数据表或数据库</p> <p><br></p> <p>Insert: 增加表的记录</p> <p><br></p> <p>Delete: 删除表的记录</p> <p><br></p> <p>Update: 修改表中已存在的记录</p> <p><br></p> <p><br></p> <p>Select: 显示/搜索表的记录</p> <p><br></p> <p><br></p> <p>References: 允许创建外键</p> <p><br></p> <p>Index : 建立或删除索引</p> <p><br></p> <p>Create View: 允许创建视图</p> <p><br></p> <p>Create Routine: 允许创建存储过程和包</p> <p><br></p> <p>Execute: 允许执行存储过程和包</p> <p><br></p> <p>Trigger: 允许操作触发器</p> <p><br></p> <p>Create User: 允许更改、创建、删除、重命名用户和收回所有权限</p> <p><br></p> <p><br></p> <p>2、全局管理MySQL用户权限:</p> <p><br></p> <p>Grant Option: 允许向其他用户授予或移除权限</p> <p><br></p> <p>Show View: 允许执行SHOW CREATE VIEW语句</p> <p><br></p> <p>Show Databases: 允许账户执行SHOW DATABASE语句来查看数据库</p> <p><br></p> <p>Lock Table: 允许执行LOCK TABLES语句来锁定表</p> <p><br></p> <p>File: 在MySQL服务器上读写文件</p> <p><br></p> <p>Process: 显示或杀死属于其它用户的服务线程</p> <p><br></p> <p>Reload: 重载访问控制表,刷新日志等</p> <p><br></p> <p>ShutDown: 关闭MySQL服务</p> <p><br></p> <p><br></p> <p>3、特别的权限:</p> <p><br></p> <p>All: 允许做任何事(和root一样)</p> <p><br></p> <p>Usage: 只允许登录,其它什么也不允许做</p> <p><br></p> <p><br></p> <p><br></p> <p>### 6、修改用户密码</p> <p><br></p> <p>1)查看用户密码</p> <p><br></p> <p>`select u.User, u.Host,u.authentication_string from mysql.user u where u.User = 'Lizi';`</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/8kbs1cx9.jpeg"></p> <p><br></p> <p><br></p> <p>2)修改用户密码</p> <p><br></p> <p>`update mysql.user set authentication_string = password('新密码') where user = '用户名';`</p> <p><br></p> <p>刷新:`flush privileges;`</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/skxam0s6.jpeg"></p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/l198oufg.jpeg"></p> <p><br></p> <p><br></p> <p>### 7、删除用户</p> <p><br></p> <p>语法:`drop user '用户名'@'IP';`</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/36nz1sqb.jpeg"></p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/h99uyaoz.jpeg"></p> <p><br></p> <p><br></p> <p>## TPL事务处理语言</p> <p><br></p> <p>### 事务理解</p> <p><br></p> <p>SQL语句是我们给数据库发送指令,让数据库帮我们做事情。</p> <p><br></p> <p>事务:可以理解为是让数据库做的事情,可能存在多个 SQL 操作。</p> <p><br></p> <p>所有的操作应该是统一的,要么都成功,要么都失败。</p> <p><br></p> <p><br></p> <p>事务的本质可以理解成:多线程并发操作同一张表格可能带来的安全问题。</p> <p><br></p> <p><br></p> <p>### 事务的四大特性(ACID)</p> <p><br></p> <p>**1、Atomicity 原子性**</p> <p><br></p> <p>一个事务中的所有操作是一个整体,不可再分。事务中的所有操作要么都成功,要么都失败。</p> <p><br></p> <p>**2、Consistency 一致性**</p> <p><br></p> <p>一个用户操作了数据,提交以后,另一个用户看到的数据效果是一致。</p> <p><br></p> <p>**3、Isolation 隔离性**</p> <p><br></p> <p>多个用户并发访问数据库时,一个用户操作数据库,另一个用户不能有所干扰。</p> <p><br></p> <p>多个用户之间的数据事务操作要互相隔离。</p> <p><br></p> <p>**4、Durability 持久性**</p> <p><br></p> <p>一个用户操作数据的事务一旦被提交(缓存-->文件),对数据库底层真实的改变是永久性的。</p> <p><br></p> <p><br></p> <p>### 事务操作</p> <p><br></p> <p>1、开启一个事务</p> <p><br></p> <p>每一次执行的一条sql语句之前,mysql数据库都会默认的开启</p> <p><br></p> <p>`begin;`或 `start transaction;`</p> <p><br></p> <p>2、执行操作(执行SQL)</p> <p><br></p> <p>3、事务处理</p> <p><br></p> <p>mysql数据库会默认的执行提交事务</p> <p><br></p> <p>提交 `commit`</p> <p><br></p> <p>回滚 `rollback`</p> <p><br></p> <p>保存还原点 `save point xx `</p> <p><br></p> <p><br></p> <p>### 事务的隔离级别</p> <p><br></p> <p>事务的隔离性可能会产生多线程并发操作同一个数据库表格的问题,会带来数据的安全隐患。</p> <p><br></p> <p>1.脏读:一个人读到了另外一个人还没有提交的数据。</p> <p><br></p> <p>A、B在操作同一张表格</p> <p><br></p> <p>A修改了数据,还没有提交,B读取到了</p> <p><br></p> <p>A不提交了,回滚回来,B刚刚读取到的那些数据就是无用的----脏数据</p> <p><br></p> <p><br></p> <p>2、不可重复读</p> <p><br></p> <p>A、B在操作同一个表格</p> <p><br></p> <p>A先读取了一些数据,读完之后B此时将数据做了**修改/删除**</p> <p><br></p> <p>A再按照之前的条件重新读一遍,与第一次读取的不一致</p> <p><br></p> <p><br></p> <p>3、幻读(虚读)</p> <p><br></p> <p>A、B在操作同一个表格</p> <p><br></p> <p>A先读取了一些数据,读完之后B此时将数据做了**新增**</p> <p><br></p> <p>A再按照之前的条件重新读一遍,与第一次读取的不一致</p> <p><br></p> <p><br></p> <p>隔离级别</p> <p><br></p> <p>1、Serializable 级别最高,可以避免所有出现的问题,性能很慢</p> <p><br></p> <p>2、Repeatable Read 可重复读 (避免脏读、不可重复读)</p> <p><br></p> <p>3、Read Committed 读已提交 (避免脏读)</p> <p><br></p> <p>4、Read UnCommitted 读未提交 (所有效果均无法保证)</p> <p><br></p> <p><br></p> <p>MySQL数据库提供默认隔离级别 Repeatable Read</p> <p><br></p> <p>Oracle数据库提供默认隔离级别 Read Committed</p> <p><br></p> <p>查看数据库隔离级别:`select @@tx_isolation;`</p> <p><br></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/vdkduyaw.jpeg"></p> <p><br></p> <p>修改隔离级别:`set session transaction isolation level xxx;`</p> <p><br></p> <p>一般情况下不需要改动。</p> <p><br></p> <p><br></p> <p># 常用SQL</p> <p><br></p> <p>```sql</p> <p># 显示有哪些database</p> <p>show databases;</p> <p><br></p> <p>#使用哪个数据库</p> <p>use database名字;</p> <p><br></p> <p># 显示当前数据库有哪些table</p> <p>show tables;</p> <p><br></p> <p># 查询数据库的字符集</p> <p>select schema_name, default_character_set_name from information_schema.schemata where schema_name = `查询的数据库名`;</p> <p><br></p> <p># 查询表格信息</p> <p>show table status from 数据库名 like `表名`;</p> <p><br></p> <p>#查看表有什么约束</p> <p>desc 表名;</p> <p>show keys from 表名;</p> <p>show create table 表名;</p> <p>```</p> <p><br></p> <p><br></p> <p>#MySQL# #数据库# #知识碎片#</p> </div> </body> </html>
接口和抽象类的使用场景,你真的知道吗?
<html> <head></head> <body> <div class="content ql-editor"> <p>引言:在 Java 编程领域,掌握接口和抽象类的区别不仅是基础知识,也是面试中经常遇到的问题。这两种类型在设计模式和面向对象编程中扮演着关键角色,理解它们的不同之处对于 Java 开发者而言极为重要。本文将详细解析接口和抽象类的定义、区别及应用场景,帮助你在技术讨论和面试中更加自信地阐述它们的特点和用法。</p> <p></p> <h2>题目</h2> <p></p> <p>接口和抽象类的使用场景,你真的知道吗?</p> <p></p> <h2>推荐解析</h2> <p></p> <h3>目的和设计哲学</h3> <p></p> <p>1)<strong>接口</strong>旨在定义一个合约,它规定了一组方法,但不涉及这些方法的实现。接口是一种完全的抽象,所有方法都是抽象的。接口主要用于定义对象间的交互协议。</p> <p></p> <p>2)<strong>抽象类</strong>既可以包含抽象方法也可以包含具体实现。一个类可以继承一个抽象类,并通过提供抽象方法的具体实现来实现更具体的功能。抽象类允许你定义一些通用的功能和结构,同时为更具体的类留下实现细节。</p> <p></p> <h3>实现与继承</h3> <p></p> <p>1)一个类可以实现多个接口,从而可以从多个源继承类型和方法的声明。这提供了一种形式的多重继承。</p> <p></p> <p>2)一个类只能继承一个抽象类,因为 Java 和许多其他面向对象的语言不支持多重类继承,这限制了抽象类的灵活性。</p> <p></p> <h3>默认方法</h3> <p></p> <p>1)在 Java 8 及以后的版本中,接口可以包含默认方法和静态方法。默认方法是带有实现的方法,允许接口在不影响实现类的情况下增加新功能。</p> <p></p> <p>2)抽象类可以包含具体实现的方法,包括静态方法、非静态方法以及构造方法。</p> <p></p> <h3>成员变量</h3> <p></p> <p>1)接口只能包含静态、最终(final)变量,即常量。</p> <p></p> <p>2)抽象类可以包含静态变量、非静态变量、最终变量等,提供了更大的灵活性。</p> <p></p> <h3>访问修饰符</h3> <p></p> <p>1)接口中的方法默认是 public 的,而且直到 Java 9 之前,接口中的方法不能是 private 或 protected 的。</p> <p></p> <p>2)抽象类中的方法可以是 public、protected 或 private,提供更细致的访问控制。</p> <p></p> <h3>使用场景</h3> <p></p> <p>1)当你想要定义一组相关类必须实现的公共接口,但不需要从这些类中共享代码时,使用接口是更好的选择。</p> <p></p> <p>2)当你需要共享代码或拥有部分实现的方法时,使用抽象类更为合适。</p> <p></p> <h3>加深记忆的例子</h3> <p></p> <p>1)<strong>接口(Interface)</strong>:可以比喻为一套标准的交通规则。这套规则定义了在路上行驶的车辆应该遵循的行为准则,例如红灯停、绿灯行,以及如何变道和停车。就像接口定义了一系列方法规范,具体的类(就像各种车辆)需要按照这些规则实现这些方法。这套规则确保了不同的车辆(类)可以在同一环境下协同工作,尽管它们的具体实现(如何驾驶)可能会有所不同。</p> <p></p> <p>2)<strong>抽象类(Abstract Class)</strong>:可以比喻为半成品的车辆。这种车辆已经安装了引擎和轮子,即一些基本的功能已经实现了,但是车辆的其余部分还需要进一步的定制和完成。例如,车身的形状和颜色可能需要根据特定的需求来完成。这就像抽象类一样,它提供了一些已经实现的方法(引擎和轮子),确保了子类(完全定制的车辆)拥有一些基本的功能。然而,抽象类中还有一些抽象方法(未完成的部分),需要由继承它的子类来具体实现,以满足特定的需求。</p> <p></p> <h3>设计模式</h3> <p></p> <p><strong>抽象类</strong></p> <p></p> <p>1)**模板方法模式:**在这个模式中,抽象类定义了执行某些任务的模板,即一系列步骤的执行顺序。这个模板会调用一系列基本操作,其中一些可以在抽象类中实现(提供默认行为),而一些则留给具体子类去实现。</p> <p></p> <p>2)**策略模式:**在策略模式的某些实现中,可以使用抽象类来定义策略接口的一些公共功能或默认行为。具体的策略实现将继承这个抽象类,并覆盖其中的方法以提供特定的行为。</p> <p></p> <p>3)**观察者模式:**在某些实现中,观察者基类可以作为一个抽象类,其中定义了通知接口和一些基本操作。具体的观察者类继承这个抽象类,并实现必要的方法来响应主题的状态变化。</p> <p></p> <p>4)**状态模式:**状态模式允许一个对象在其内部状态改变时改变它的行为。使用抽象类可以定义一个状态的接口及其部分实现,而具体状态的实现则可以继承这个抽象类,提供对特定状态的行为。</p> <p></p> <p>5)**桥接模式:**在桥接模式中,抽象部分和实现部分是分开的,允许它们独立变化。抽象基类定义了一个接口,它维护了一个对实现部分对象的引用。这个模式允许在抽象层次上操作,而具体的实现细节则由实现部分的具体类来提供。</p> <p></p> <p><strong>接口</strong></p> <p></p> <p>1)**观察者模式:**接口在这里定义了观察者和被观察者(主题)之间的契约。观察者实现一个接口,以便主题在状态变化时能通知它们。这使得观察者和主题之间的耦合度最小化。</p> <p></p> <p>2)<strong>工厂模式 和 抽象工厂模式:</strong>:这些模式通过接口定义了创建对象的方法,使得系统可以在不指定对象具体类的情况下生成对象。特别是在抽象工厂模式中,一个接口代表了一个工厂,它创建一系列相关或依赖的对象,而不是单一对象。</p> <p></p> <p>3)**代理模式:**在这个模式中,接口定义了实际对象和代理对象的共同功能。代理控制对实际对象的访问,可以在访问实际对象之前或之后执行某些操作,如懒加载、访问控制、日志记录等。</p> <p></p> <p>4)**适配器模式:**接口在适配器模式中用于定义客户端期望从适配器中获取的操作。适配器实现这个接口并将客户端的请求转换为对一个或多个被适配者的调用,使得原本不兼容的接口能够一起工作。</p> <p></p> <p>5)**装饰器模式:**在装饰器模式中,接口定义了一个对象可以执行的操作。装饰器类实现了这个接口,并包含了对同一接口的另一个实现的引用。这允许用户在运行时动态地向对象添加新的功能,同时不改变其结构。</p> <p></p> <p>6)**命令模式:**接口在命令模式中定义了执行操作的方法,允许将请求封装为对象,从而让用户能够根据不同的请求、队列或日志请求以及支持撤销操作来参数化其他对象。</p> <p></p> <h2>其他补充</h2> <p></p> <p>鱼聪明 AI 的回答:</p> <p></p> <p>鱼聪明 AI 地址:https://www.yucongming.com/</p> <p></p> <p>1)<strong>多继承 vs. 单继承</strong>:</p> <p></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>接口支持多继承的概念,一个类可以实现多个接口,这使得你可以构造出复杂的继承结构,而不受单继承的限制。</li> <li data-list="bullet"><span class="ql-ui"></span>抽象类遵循单继承原则,一个类只能继承一个抽象类。这限制了继承的灵活性,但也避免了多继承可能引入的复杂性和问题,如菱形继承问题。</li> </ol> <p></p> <p>2)<strong>默认实现</strong>:</p> <p></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>接口在 Java 8 之后允许有默认实现。这意味着接口不再完全是一个约定集合,它可以提供一些方法的实现。这使得接口在不破坏已有代码的基础上,能够更灵活地进化。</li> <li data-list="bullet"><span class="ql-ui"></span>抽象类可以提供成员方法的完整实现,包括字段和构造函数,这对于定义复杂的抽象或部分实现非常有用。</li> </ol> <p></p> <p>3)<strong>设计意图</strong>:</p> <p></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>使用接口表达了一种"能做什么"(can-do)的关系,它定义了对象的一组行为,而不预设这些行为的具体实现方式。</li> <li data-list="bullet"><span class="ql-ui"></span>抽象类表达了一种"是什么"(is-a)的关系,它通常用于对象的类型继承中,为子类提供一个共同的基类。</li> </ol> <p></p> <p>4)<strong>兼容性与未来扩展</strong>:</p> <p></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>接口被广泛用于定义模块间的公共 API,因为它们可以帮助你保持系统的组件之间解耦。使用接口可以在不改变现有代码的基础上,添加新的实现类,从而提高代码的兼容性和可扩展性。</li> <li data-list="bullet"><span class="ql-ui"></span>抽象类更适合在你知道未来会添加方法或需要共享代码逻辑时使用。因为添加新的抽象方法到抽象类中,可能会要求所有现有的子类都更新来实现这些新方法。</li> </ol> <p></p> <p>5)<strong>选择使用接口还是抽象类</strong>:</p> <p></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>如果你要定义一个不包含任何实现但由多个不相关的类实现的类型,使用接口。</li> <li data-list="bullet"><span class="ql-ui"></span>如果你正在设计一个提供一些实现的基本功能,同时要求用户扩展这些功能的框架,使用抽象类。</li> </ol> <p></p> <p>6)<strong>设计原则</strong>:</p> <p></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>设计接口时,遵循单一职责原则(SRP)和接口隔离原则(ISP),确保接口的粒度保持小而专注。</li> <li data-list="bullet"><span class="ql-ui"></span>抽象类的设计应考虑到开闭原则(OCP),即允许类容易扩展但是在修改现有功能上应尽可能的闭合。</li> </ol> <p></p> <h3>推荐文章和书籍</h3> <p></p> <p>文章:https://zhuanlan.zhihu.com/p/86293659</p> <p></p> <p>书籍:《 Java 核心技术卷 I 》</p> <p></p> <h2>欢迎交流</h2> <p></p> <p>在深入研究 Java 中的接口和抽象类之后,你应该能够理解它们在面向对象编程中的作用和差异,以及如何有效地使用它们来设计灵活、可扩展的软件系统。为了进一步深化理解,并促进在实际应用设计和性能优化方面的讨论,这里提出三个问题,欢迎在评论区或讨论中分享你的观点和经验,以促进知识的共享和进步</p> <p></p> <p>1)在哪些具体场景下应该优先考虑使用接口或抽象类,以及它们在这些场景中的优势是什么?</p> <p></p> <p>2)如何利用接口和抽象类设计高效且可维护的软件架构,尤其在面对需要频繁变动或扩展的应用需求时,如何通过这些概念优化设计和提高灵活性?</p> <p></p> <p>3)在并发和多线程环境下,使用接口和抽象类有哪些特别的考虑?它们对应用的性能和稳定性有何影响?</p> <p></p> <p>这些问题旨在促进对接口和抽象类更深层次的理解,以及它们在实际软件开发和设计中的应用。通过分享和讨论,我们可以更好地掌握这些基本概念,提高我们设计和实现软件解决方案的能力。期待你的见解和贡献!</p> <p>#面试题#</p> </div> </body> </html>
