开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

深入理解Java中的队列!队列Queue的详细解析 队列 Q

发表于 2021-11-25

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

队列

  • 非阻塞队列:
    • 无界线程安全:ConcurrentLinkedQueue
    • 采用CAS机制:compareAndSwapObject原子操作
  • 阻塞队列:
    • 有界:ArrayBlockingQueue,设置了大小的LinkedBlockingQueue.通常情况下,LinkedBlockingQueue是有界队列
    • 无界:没有设置大小的LinkedBlockingQueue
    • 采用锁机制:DelayQueue,PriorityBlockingQueue
    • 使用ReetrantLock锁

  • 有界队列: 有固定大小的队列
    • 设定了固定大小的LinkedBlockingQueue
    • 大小为0,只是在生产者和消费者之间做中转使用的SynchronousQueue
  • 无界队列: 没有设置固定大小的队列
    • 无界队列的特点是可以直接入列,直到溢出. 溢出值为Integer.MAX_VALUE. 现实中不会达到这么大容量,所以通常可以认为是无界的
    • 没有设定固定大小的LinkedBlockingQueue
  • 在线程池中使用阻塞队列LinkedBlockingQueue时,一般情况下需要配置一下队列的大小,设置成有界队列,否则会导致JVM内存被耗尽

Queue基本概念

  • Queue: 通常情况下,一个队列就是一个先入先出FIFO的数据结构
  • Queue是与List,Set同一级别,继承自Collection接口
  • LinkedList实现了Deque接口

Queue实现

没有实现阻塞接口

  • 继承自AbstrsctQueue接口
  • 内置两个不阻塞队列 : PriorityQueue和ConcurrentLinkedQueue
  • PriorityQueue和ConcurrentLinkedQueue类在Collection框架下增加两个具体集合的实现
  • PriorityQueue:
    • 实质上维护了一个有序列表
    • 加入到Queue中的元素根据元素实现的java.util.Comparable排序或者传递给构造函数的java.util.Comparator来实现元素定位
  • ConcurrentLinkedQueue:
    • 是基于链接节点的线程安全队列
    • 并发访问不需要同步.因为ConcurrentLinkedQueue对元素的操作是在队列的尾部添加元素并从头部删除元素,所以不需要知道队列的大小
    • ConcurrentLinkedList可以很好地对公共集合进行共享访问
    • ConcurrentLinkedList对查询集合大小会很慢,因为需要遍历队列

实现阻塞接口

  • 在Java.util.concurrent包中加入了BlockingQueue接口和5个阻塞队列
  • BlockingQueue实质上就是带有一点扭曲的FIFO数据接口: 不是立即从队列中添加或者删除元素,会存在线程操作阻塞,直到队列中有空间或者有元素可用
  • BlockingQueue接口的5个阻塞队列:
    • ArrayBlockingQueue: 一个由数组支持的有界队列
      • 在构造时需要指定容量,并可以选择是否需要公平性
        • 如果公平参数被设置为true, 等待时间最长的线程会优先得到处理
        • 就是通过将ReetrantLock设置为true来达到等待时间最长的线程优先操作的公平性
        • 公平性需要消耗性能,只有在的确非常需要时才会使用
      • ArrayBlockingQueue是基于数组的阻塞循环队列,按照先进先出FIFO原则对元素进行排序
      • 实现简单,操作稳定. 添加删除使用同一个锁,性能较低
    • LinkedBlockingQueue: 一个由链表支持的可选有界队列
      • 使用LinkedBlockingQueue通常情况下需要选择指定最大容量
      • 在不指定LinkedBlockingQueue容量时容量为Integer.MAX_VALUE, 可以认为是无界的,但是在put时是会存在阻塞情况的
      • LinkedBlockingQueue是基于链表的队列,按照先进先出FIFO排序元素
      • LinkedBlockingQueue的添加和删除操作两把锁是分开的
    • PriorityBlockingQueue: 一个由优先级堆支持的无界优先级队列
      • 一个带优先级队列,而不是先进先出队列,元素按优先级顺序被移除 ,PriorityBlockingQueue队列没有上限
      • PriorityBlockingQueue是对PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,类似ArrayList, 所以在优先级阻塞队列上使用put时是不会发生阻塞的
      • 虽然PriorityBlockingQueue在逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作时会导致OutOfMemoryError异常
      • 如果队列为空,那么取元素的操作take就会发生阻塞,所以检索操作take是受阻的
      • 添加到PriorityBlockingQueue中的元素需要具有比较能力
    • DelayQueue: 一个由优先级堆支持的基于时间的调度队列
      • 基于PriorityQueue实现的基于时间的调度队列
      • 是一个存放Delayed元素的无界阻塞队列,只有在延迟期满时才能从中取出元素
      • DelayQueue元素的头部是延迟期满后保存时间最长的Delayed元素
      • 如果延迟都还没有期满,则DelayQueue没有头部,并且poll操作将会返回null
      • 当一个元素的getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或者等于0的值时,则延迟期满 ,poll就可以移除这个元素
      • 此队列不允许使用null元素
      • 通常用于清除缓存中超时的缓存数据
    • SynchronousQueue: 一个由阻塞队列BlockingQueue接口的简单聚集机制
      • 内部容量为0, 适用于元素数量少的场景. 特别适合作为交换数据使用
      • SynchronousQueue内部使用队列实现公平性的调度,使用栈实现非公平性的调度. 使用CAS实现锁逻辑
        在这里插入图片描述

阻塞队列的操作

  • remove, element, offer, poll, peek都是属于Queue接口
  • 阻塞队列的操作可以根据响应方式分为3类:
    • add, remove和element操作在为一个已满的队列增加元素或者从空队列取出元素时抛出异常
    • 在多线程的程序中,队列在任何情况下都可能是已满或者空状态,可以使用offer,poll,peek方法,这些方法在无法完成任务时,只是给出一个出错提示而不会抛出异常
      • 注意: 因为在多线程环境中,使用poll,peek操作出错时会返回null, 所以向队列中插入null值是不合法的
    • 阻塞操作有put和take, put方法在队列满时阻塞 ,take方法在队列空时阻塞

add

  • 增加一个元素
  • 如果队列已满,则抛出IllegalStateException异常

remove

  • 移除并返回队列头部的元素
  • 如果队列为空,则抛出NoSuchElementException异常

element

  • 返回队列头部的元素
  • 如果队列为空,则抛出一个NoSuchElementException异常

offer

  • 添加一个元素并返回true
  • 如果队列已满,则返回false

poll

  • 移除并返回队列头部的元素
  • 如果队列为空,则返回null

peek

  • 返回队列头部的元素
  • 如果队列为空,则返回null

put

  • 添加一个元素
  • 如果已满,则发生阻塞

take

  • 移除并返回队列头部的元素
  • 如果队列为空,则发生阻塞

Java集合框架位于java.util包中
Collections提供了对集合进行排序,遍历等多种算法实现,比如**sort(), reverse(), shuffle()**等

  • Collection
    • List : 采用线性列表的存储方式,存储的顺序与添加的顺序相同
      • ArrayList
      • Vector
      • LinkedList
    • Set : 不保证元素的顺序,不允许重复的元素
      • TreeSet
      • HashSet
  • Map : 采用键值对的存储方式,键值不允许重复
    • TreeMap
    • HashMap
    • HashTable

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

初学鸿蒙OS之分析一下鸿蒙项目的组成结构

发表于 2021-11-25

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

写在前面

上篇我们通过鸿蒙开发IDE来生成了一个示例项目,这个示例项目为我们展示了一个经典的桥段,那就是你好世界!Hello World!。

原本今天想在真机上试一下,但是发现这个学习次序不是很对,应该是先对鸿蒙项目有一个大概的了解,再去真机上试验比较好,不然打包估计都有点费劲。

所以,今天我同大家来分享一下鸿蒙项目的组成结构。

鸿蒙项目的组成结构

我先贴一张图给大家看一下。

image.png

从上图开始看,其中有一些关键信息展示出来了,首先就是使用了gradle来进行依赖管理,外部的这些文件大多都是gradle组件相关的文件。

其中也有几个文件不属于gradle,

local.properties:此文件中记录了本地SDK、nodejs外部依赖的文件路径的相关位置。

除了这些,最核心的就是entry模块,从图上我们就可以看出来,这是harmonydemo1项目的子模块。

其中存在两个文件夹,多个配置文件;这里就不说那几个配置文件了,没什么好说的。

libs文件夹:放置外部引入的依赖包的文件夹,比如引入Java中的一个jar包等。

src:这个就是我们的源码包了。

image.png

源码包中共分为主代码包main,和测试用例代码包ohosTest。

测试用例代码包不在这说了,说一下主代码包。

主代码包main中,如图看起来,是共有java、js、resources三类文件夹。

其中java包下偏向于写入一些配置,或者是后端逻辑代码。

js包下主要是写入页面UI方面的代码,后面我们会自己在此包下新建一些文件来写一个页面。

resources包,则是用于放置配置文件、数据文件。向我们上篇文章中所创建的Demo示例,其中Hello World,你好世界,就是读取此包下数据文件的数据。

这其中还有一个点需要注意一下,那就是config.json文件。

此文件是用于配置项目整体的框架信息的,其中的配置信息我们在后面慢慢学习。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

ElasticSearch进阶五:分片原理之倒排索引

发表于 2021-11-25

学习笔记:

分片是Elasticsearch最小的工作单元。但是究竟什么是一个分片,它是如何工作的?

传统的数据库每个字段存储单个值,但这对全文检索并不够。文本字段中的每个单词需要被搜索,对数据库意味着需要单个字段有索引多值的能力。最好的支持是一个字段多个值 需求的数据结构是倒排索引。

4.6.1 倒排索引

Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索。

见其名,知其意,有倒排索引,肯定会对应有正向索引。正向索引(forward index),反向索引(inverted index)更熟悉的名字是倒排索引。

所谓的正向索引,就是搜索引擎会将待搜索的文件都对应一个文件ID,搜索时将这个ID和搜索关键字进行对应,形成K-V对,然后对关键字进行统计计数

image.png

但是互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。

image.png

一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。例如,假设我们有两个文档,每个文档的 content 域包含如下内容:

  • The quick brown fox jumped over the lazy dog
  • Quick brown foxes leap over lazy dogs in summer

为了创建倒排索引,我们首先将每个文档的 content 域拆分成单独的 词(我们称它为 词条 或 tokens ),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。结果如下所示:

image.png

现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档:

image.png

两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单相似性算法,那么我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。

但是,我们目前的倒排索引有一些问题:

  • Quick 和 quick 以独立的词条出现,然而用户可能认为它们是相同的词。
  • fox 和 foxes 非常相似, 就像 dog 和 dogs ;他们有相同的词根。
  • jumped 和 leap, 尽管没有相同的词根,但他们的意思很相近。他们是同义词。

使用前面的索引搜索 +Quick +fox 不会得到任何匹配文档。(记住,+ 前缀表明这个词必须存在。)只有同时出现 Quick 和 fox 的文档才满足这个查询条件,但是第一个文档包含 quick fox ,第二个文档包含 Quick foxes 。

我们的用户可以合理的期望两个文档与查询匹配。我们可以做的更好。

如果我们将词条规范为标准模式,那么我们可以找到与用户搜索的词条不完全一致,但具有足够相关性的文档。例如:

  • Quick 可以小写化为 quick 。
  • foxes 可以 词干提取 –变为词根的格式– 为 fox 。类似的, dogs 可以为提取为 dog 。
  • jumped 和 leap 是同义词,可以索引为相同的单词 jump 。

现在索引看上去像这样:

image.png

这还远远不够。我们搜索 +Quick +fox 仍然 会失败,因为在我们的索引中,已经没有 Quick 了。但是,如果我们对搜索的字符串使用与 content 域相同的标准化规则,会变成查询 +quick +fox,这样两个文档都会匹配!分词和标准化的过程称为分析

这非常重要。你只能搜索在索引中出现的词条,所以索引文本和查询字符串必须标准化为相同的格式。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

apache24 + windows 部署 Django(

发表于 2021-11-25

apache2.4 + windows 部署 Django

apache2.4 多站点部署

虚拟站点, 多站点 部署 django

文件名称 文件路径 文件备注
httpd.conf C:\Apache24\conf\httpd.conf apache 主配置文件 加载模块 监听端口 配置serverName(必须)
httpd-vhosts.conf C:\Apache24\conf\extra\httpd-vhosts.conf 多站点的配置文件
httpd.exe C:\Apache24\bin\httpd.exe apache 启动服务 httpd -k start
  1. 打开vhosts配置加载
1
2
3
shell复制代码# httpd.conf
# Virtual hosts
Include conf/extra/httpd-vhosts.conf
  1. 打开 mod_proxy 和 mod_proxy_http 模块加载 ( 反向代理要用到的模块)
1
2
3
shell复制代码# httpd.conf
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
  1. 编辑 httpd-vhosts.conf 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
shell复制代码# # for further details before you try to setup virtual hosts.
# You may use the command line option '-S' to verify your virtual host
# configuration.
# VirtualHost example:
# Almost any Apache directive may go into a VirtualHost container.
# The first VirtualHost section is used for all requests that do not
# match a ServerName or ServerAlias in any <VirtualHost> block.
#
# first VirtualHost
<VirtualHost *:80>
Define PROJECT_DIR "E:/www/project_root/" # 建议定义一个 项目主目录 | 网站根目录 方便后面使用
ServerAdmin server@server.com
ServerName www.test.com
ServerAlias www.test.com test.com # 网站别名 可以是旧网站的域名
ProxyPass "/foo/" "http://192.168.1.xxx:8080/" # 将 www.test.com/foo/a 的 请求转发 到 192.168.1.xxx:8080/a
ProxyPassReverse "/foo/" "http://192.168.1.xxx:3080" # 将 www.test.com/foo/a 代理到 192.168.1.xxx:8080/a

Alias /static "${PROJECT_DIR}/static" # 静态文件目录访问
# 权限 设置
<Directory "${PROJECT_DIR}/static">
<RequireAll>
Require all granted
</RequireAll>
</Directory>
# 日志设置 一天生成一个文件
SetEnvIf Request_URI "\.(gif|jpg|png|css|js)$" static_request
ErrorLog "|bin/rotatelogs.exe logs/error-qitay.com-%Y%m%d.log 86400 480" # 错误日志 apaache 有默认的日志格式
CustomLog "|bin/rotatelogs.exe logs/access-demo.com-%Y%m%d.log 86400 480" common env=!static_request # env=!static_request 不记静态文件的访问日志
CustomLog "|bin/rotatelogs.exe logs/details-demo.com-%Y%m%d.log 86400 480" combined # combined 是在 httpd 文件里面定义的 日志格式
</VirtualHost>

# second VirtualHost

<VirtualHost *:80>
ServerAdmin server@qitai.com
ServerName test.test.com
ServerAlias test.test.com
ProxyPass "/foo/" "http://192.168.1.xxx:8080/"
</VirtualHost>

django 项目配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
shell复制代码# httpd-vhosts.conf

# 在本机的python 环境下安装 pip install mod_swgi -r https://pypi.douban.com/simple
# 运行 `mod_wsgi-express module-config` 输出下面三行
LoadFile "C:/Users/Administrator/AppData/Local/Programs/Python/Python37/python37.dll"
LoadModule wsgi_module "C:/Users/Administrator/AppData/Local/Programs/Python/Python37/lib/site-packages/mod_wsgi/server/mod_wsgi.cp37-win_amd64.pyd"
WSGIPythonHome "C:/Users/Administrator/AppData/Local/Programs/Python/Python37"
<VirtualHost *:80>
Define DJANGO_PROJECT "E:/development/online/server/dj_demo"
ServerAdmin admin@email.com
ServerName www.demo.com
ServerAlias www.demo.com
DocumentRoot "${DJANGO_PROJECT}" # 网站根目录

# log
SetEnvIf Request_URI "\.(gif|jpg|png|css|js)$" static_request
ErrorLog "|bin/rotatelogs.exe logs/error-qitay.com-%Y%m%d.log 86400 480"
CustomLog "|bin/rotatelogs.exe logs/access-demo.com-%Y%m%d.log 86400 480" common env=!static_request
CustomLog "|bin/rotatelogs.exe logs/details-demo.com-%Y%m%d.log 86400 480" combined
WSGIScriptAlias / "${DJANGO_PROJECT}/dj_demo/wsgi.py"
# dj_demo wsgi.py 的文件权限
<Directory "${DJANGO_PROJECT}/dj_demo">
<Files wsgi.py>
<RequireAll>
Require all granted
</RequireAll>
</Files>
</Directory>

# 设置静态文件路径
Alias /static "${DJANGO_PROJECT}/static"
# 静态文件权限
<Directory "${DJANGO_PROJECT}/static">
<RequireAll>
Require all granted
</RequireAll>
</Directory>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
python复制代码# dj_demo/wsgi.py 
# 虚拟环境部署 不是虚拟环境的话 就不需要更改
# 这是参考一篇博客的,博客链接 找不到了....

import os
import sys
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, PROJECT_DIR) # 项目加入导包路径
virtualenv_dir = os.path.join(PROJECT_DIR, 'env', 'Lib', 'site-packages') # 虚拟环境python包文件夹
sys.path.insert(0, virtualenv_dir) # 加入导包路径

from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Qitai.settings')

application = get_wsgi_application()

部署流程大概分三步:

  1. 本机 python 下载 mod_swgi

pip install mod_swgi -i https://pypi.douban.com/simple

运行 mod_wsgi-express module-config

将输出的三行文本 复制 等待使用
2. 建立项目的虚拟环境

cd {PROJECT_DIR} && python -m venv env

pip install -r requirement.txt -i https://pypi.douban.com/simple

更改 项目的 wsgi.py 文件
3. 修改 vhosts.conf

将 复制的三行文本 复制到 vhosts.conf 头部

然后 复制粘贴上面的配置

修改 DJANGO_PROJECT ServerName ServerAlias

有必要的 话 修改 端口

到此为止 django 项目 部署完成

注意 第一步 下载 mod_swgi 只需要进行一次 如果需要发布多个django 网站, 只需要从第二步开始

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

简单谈谈MySQL的事务 简单谈谈MySQL的事务

发表于 2021-11-25

简单谈谈MySQL的事务

事务的基本特性和隔离级别

基本特性 ACID

事务的基本特性就是==ACID==

ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:

  • 原子性(atomicity,[ætəˈmɪsəti],或称不可分割性)
    • 一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节
  • 一致性(consistency,[kənˈsɪstənsi])
    • 在事务开始之前和事务结束以后,数据库的完整性没有被破坏
  • 隔离性(isolation,[aɪsəˈleɪʃn],又称独立性)
    • 一个事务的修改在最终提交前,对其他事务是不可见的
    • 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以**防止多个事务并发执行时由于交叉执行而导致数据的不一致 **
      • 事务隔离分为4个不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
  • 持久性(durability,[djʊərəˈbɪlɪti])
    • 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失

我们需要根据事务的原子性、隔离性和持久性来保证数据的一致性

那么多个事务访问同一个资源时,如何保证资源的可见性呢,这里需要谈到隔离级别了

4个隔离级别

  • read uncommit :读未提交也叫做脏读,可能会读到其他事务未提交的数据
    • A向B转账,B本来有300,如果收了200就为500了,但是有可能事务会出现异常则修改失败应该回滚为300,可是此时另一个事务读取B的余额为500的脏数据,这就是脏读
  • read commit :读已提交也叫做不可重复读,两次读取结果不一致,oracle的默认级别
    • 不可重复读解决了脏读的问题,他只会读取已经提交的事务
    • 用户开启事务读取id=1的用户,查询到age=10,其他事务修改并提交了之后,再次读取发现结果=20
    • 在同一个事务里同一个查询读取到不同的结果叫做不可重复读
  • repeatable read :可重复读,这是mysql的默认级别,就是每次读取结果都一样
    • 但是有可能产生幻读,使用间隙锁解决
  • serializable :串行,一般不使用。给每一行读取的数据加锁, 会导致大量超时和锁竞争的问题

隔离级别中可能出现的问题

  • 脏读(Drity Read):可以给数据上写锁,让其他事务不能并发读和并发写,解决了脏数据的问题
    • 某个事务已更新一份数据, 另一个事务在此时读取了同一份数据,由于某些原因,前一个回滚了操作,则后一个事务所读取的数据就会是不正确的。
  • 不可重复读(Non-repeatable read):可以给数据上读锁,让其他事务可以并发读,但是不能写,解决了读取结果不一致的问题
    • 在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
  • 幻读(Phantom Read):使用间隙锁解决幻读问题(for update)
    • 在一次事务里面,多次查询之后,结果集的个数不一致的情况叫做幻读。而多或者少的那一行被叫做幻行
    • 例如事务1查询到3条记录但是不commit提交,事务2insert后就commit提交了,事务1再查询还是3条,但是执行全表update时却更新了4条,在更新完后再查询select,发现查询出来了4条数据,那么这就是产生了幻读,多出来的那条数据就是幻行
      • 因为select是快照读,而update是当前读
      • 如果加上锁之后,别的事务无法操作数据,也就不会出现幻读问题了
      • 间隙锁本质上是用于阻止其他事务在该间隙内插入新记录,而自身事务是允许在该间隙内插入数据的

ACID是靠什么保证的

  • 原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
  • 一致性由其他三大特性保证,程序代码要保证业务层面上的数据一致性
  • 隔离性由MVCC来保证
  • 持久性由内存、redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可以从redo log恢复
+ redo log的刷盘会在系统空闲时进行
+ > 将新数据写入内存,然后对InnoDB的redo log写盘,redolog中事务进入prepare状态
> 
> 
> 如果前面prepare成功,bin log写盘,将事务日志持久化到bin log
> 
> 
> 如果持久化成功,那么在redo log里面写一个commit 记录,表示InnoDB事务进入commit状态
+ 如果redolog写入完,变为prepare状态,可是断电了,没来得及写入bin log的话,在重新开启后会抛弃已经prepare的事务(因为它没有完成提交,所以不作数)

在innodb中有两个日志,undo log和redo log

在mysql server中有一个叫bin log的日志,也和事务有关

事务的成功不仅要在bin log中留下记录,而且也在redo log中写入commit提交状态的记录,表示事务已经完成提交

什么是MVCC

MVCC,全称 Multi-Version Concurrency Control ,即多版本并发控制

读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据——版本链

MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存

MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

MVCC只在RC和RR两钟隔离级别下工作,即read commit不可重复读和repeatable read可重复读

脏读每次读的都是最新的数据,不符合

串行会给所有读取的行都加锁,也不符合

一般我们认为MVCC有下面几个特点:

  • 每行数据都存在一个版本,每次事务更新数据时都更新该版本号
  • 修改时Copy出当前版本随意修改,和事务之间无干扰
  • 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)

在InnoDB中,会在每行数据后添加两个额外的隐藏的字段来实现MVCC ,一条记录除了包括各个字段值,还包括了当前事务id(trx_id)和一个回滚指针(roll_pointer)。

  • trx_id:生成这条记录(update/delete)的事务id
  • roll_pointer:指向了undo_log中保存着的原来的记录,根据一条条的记录中的回滚指针从而构成版本链

注:一个事务的事务id在第一次insert/delete/update时生成

readview是什么

其实Read View的最大作用是用来做可见性判断的

也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据

有可能读取到的是最新的数据,也有可能读取的是当前行记录的undolog中某个版本的数据

事务开启后会创建readview,用于维护当前活动的事务的id(活动=未提交的事务),并排序生成一个数组

readview中存储了当前Read View中最大的ID及最小的ID

之前提到了在数据的隐藏列中trx_id用于储存了事务id(获取的是最大的那个id,即最新的id),在读取数据时,会拿到这个事务id去和readview中的比对

  • up_limit_id是当时活跃事务列表的最小事务id
+ 如果row的`db_trx_id<up_limit_id`,说明这些数据在事务创建id的时都已经提交,如注释中的描述,这些数据均可见
+ 小于 可见
  • ow_limit_id是当时活跃事务的最大事务id
+ 如果读到row的`db_trx_id>=low_limit_id`,说明这些id在此之前的数据都没有提交,如注释中的描述,这些数据都不可见
+ 大于等于 不可见

MVCC是如何实现不可重复读和可重复读的

读已提交(不可重复读)隔离级别下的事务在查询的开始会创建一个独立的readview

可重复读隔离级别下的事务在第一次读的时候生成一个readview,之后的读都是复用的这个readview

即:通过不同的readview生成策略来实现不同的隔离级别

mysql的原子性和持久性是如何实现的

原子性:

  • 根据回滚指针roll_pointer指向的undolog中的上一条记录进行回滚

持久性:

  • 将新数据写入内存,然后对InnoDB的redo log写盘,redolog中事务进入prepare状态
  • 如果前面prepare成功,bin log写盘,将事务日志持久化到bin log
    • 如果持久化成功,那么在redo log里面写一个commit 记录,表示InnoDB事务进入commit状态
    • 如果持久化失败(断电了),如果redolog写入完,变为prepare状态,可是断电了,没来得及写入bin log的话,在重新开启后会抛弃已经prepare的事务(因为它没有完成提交,所以不作数)

当前读、快照读,到底读的是什么

当前读:读取的是数据的最新版本,总是能读取到最新的数据

快照读:读取的是历史版本的快照记录

为什么mysql中会有历史版本的记录呢?

原子性由undo log日志保证,它记录了需要回滚的日志信息,有时候可能会需要回滚,所以才将其记录了下来

在mysql默认的RR不可重复读隔离级别中,select读是快照读,读取的是历史版本的快照记录

而上锁或对表的操作是当前读

lock in share mode

for update

update

insert

delete

共享锁、排他锁

  • 共享锁:
+ 简称S锁,其他线程可以继续获取共享锁
+ 用`IN SHARE MODE`获得**共享锁**,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作
  • 排他锁:
+ 简称X锁,只允许获取一次,不容许其他线程获取独占锁。
+ 所以像select for update就会独占线程锁死这行
+ 但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用`FOR UPDATE`方式获得**排他锁**
+ 使用排它锁的事务不进行commit的话,别的事务是无法对被锁住的事务进行操作的
  • delete:
+ X锁
  • insert:
+ 一般不加锁,隐式锁
  • update:
+ 不更新主键的情况下:
    - X锁:就地更新
+ 更新主键的情况下:
    - X锁:更新主键的情况下,先执行delete操作,(隐式锁)再执行insert操作
1
2
3
4
5
sql复制代码# 共享锁(S):
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

# 排他锁(X):
SELECT * FROM table_name WHERE ... FOR UPDATE

关于mysql锁其实还有很多,可以查看一下这个博客www.cnblogs.com/zhuwenjoyce…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring AOP 切面应用 静态代理和动态代理

发表于 2021-11-25

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」。

概念

Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只

支持方法类型的连接点.

Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义,也可以说是连接点的集合

Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置

通知,异常通知,最终通知,环绕通知(切面要完成的功能)

Target(目标对象):代理的目标对象

Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.

spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载器织入

Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类

Aspect(切面): 是切入点和连接点,通知(引介)的结合

通知类型:

前置通知 :在目标方法执行之前执行.

后置通知 :在目标方法执行之后执行

环绕通知 :在目标方法执行前和执行后执行

异常抛出通知:在目标方法执行出现 异常的时候 执行

最终通知 :无论目标方法是否出现异常 最终通知都会 执行.

AOP XML配置方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
xml复制代码
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

<!-- 指定扫描cn.itcast.bean报下的所有类中的注解.

     注意:扫描包时.会扫描指定报下的所有子孙包

 -->

<context:component-scan base-package="li.entity"></context:component-scan>

<!--配置目标对象  -->

<bean name="UserDao" class="li.aspact.UserServiceImpl"></bean>

<!-- 配置通知类 -->

<bean name="MyAdvice" class="li.aspact.MyAdvice"></bean>

<!--配置将通知织入目标对象  -->

<aop:config>

<!-- 配置切入点

expression:li.aspact.UserServiceImpl.add():指定具体的切入点

*li.aspact.UserServiceImpl.*():指定该类不带参数所有的方法

*li.aspact.UserServiceImpl.*(..):指定该类无论带参还是不带参所有方法

*li.aspact.*ServiceImpl.*(..):指定aspect包下所有以ServiceImpl结尾的类中所有的方法

-->

<aop:pointcut expression="execution(*li.aspact.UserServiceImpl.add())" id="pc"/>

<aop:aspect ref="MyAdvice">

<!--  before方法设为前置通知-->

<aop:before method="before" pointcut-ref="pc"/>

<aop:after-returning method="afterReturning" pointcut-ref="pc"/>

<aop:after-throwing method="afterException" pointcut-ref="pc"/>

<aop:around method="around" pointcut-ref="pc"/>

<aop:after method="after" pointcut-ref="pc" />

</aop:aspect>

</aop:config>

</beans>

AOP 注解方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
java复制代码
@Component

@Aspect

public class AopConfig {

@Pointcut("execution(* com.spring.aop.*.*(..))")

public void pointCut() {

}

@Before("pointCut()")

public void before(){

System.out.println("before...");

}

@After("pointCut()")

public void after(){

System.out.println("after....");

}

@Around("pointCut()")

public void arround(ProceedingJoinPoint point){

System.out.println("arround-=-=-=-=-=-=before");

try {

point.proceed();

} catch (Throwable throwable) {

throwable.printStackTrace();

}

System.out.println("arround-=-=-=-=-=-=after");

}

}

静态代理和动态代理

概念

代理的作用就是增强对象的功能

代理对象可以理解成被增强的对象

目标对象就是被增强的对象

静态代理

继承

代理对象继承目标对象,重写需要增强的方法

缺点:代理对象会随着业务需求的不断扩大而变多,难以管理

聚合

目标对象和代理对象实现同一接口,代理对象,代理对象要包含目标对象

缺点:同样会产生许多代理对象

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

JSP入门——Application对象 1 JSP脚本中

发表于 2021-11-25

「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」

前言

1
复制代码大家好,我是程序猿小白 gw_Gw,很高兴能和大家一起学习进步。

以下内容部分来自于网络,如有侵权,请联系我删除,本文仅用于学习交流,不用作任何商业用途。

摘要

1
复制代码本文主要介绍JSP脚本中的9个内置对象中的application对象。
  1. JSP脚本中的9个内置对象

JSP脚本中包含9个内置对象,这九个内置对象其实就是JSP页面对应生成的Servlet类中的_jspService()方法中的形参或局部变量。

来看看什么是_jspService方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
    throws java.io.IOException, javax.servlet.ServletException {
​
  final java.lang.String _jspx_method = request.getMethod();
  if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
    response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
    return;
  }
​
  final javax.servlet.jsp.PageContext pageContext;
  final javax.servlet.ServletContext application;
  final javax.servlet.ServletConfig config;
  javax.servlet.jsp.JspWriter out = null;
  final java.lang.Object page = this;
  javax.servlet.jsp.JspWriter _jspx_out = null;
  javax.servlet.jsp.PageContext _jspx_page_context = null;

所以可以直接在JSP脚本中调用这些对象。

1.1 application对象

application对象用来操作application范围内的数据。application范围是整个Web应用,JSP和Servlet将数据放入到application中,该数据可以被整个Web应用下的Jsp和Servlet使用。

application对象的作用:

  1. 使得整个Web应用的JSP和Servlet可以共享数据。
  2. 可以访问web应用的配置参数。

1.1.1 共享数据

application对象通过使用setAttribute(String attrName,Object value)方法来设定共享数据的属性(attrName)和值(value)。

JSP和Servlet访问属性值需要使用getAttribute(String attrName)。

实例展示:

在index.jsp中定义变量并使用setAttribute来设置属性和值,并输出值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
xml复制代码<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<%!
int num;
%>
<%application.setAttribute("age",String.valueOf(++num));%>
<%=num%>
</body>
</html>

在test.jsp页面中使用getAttribute来获取对应属性的值并输出。

1
2
3
4
5
6
7
8
9
javascript复制代码<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>Title</title>
</head>
<body>
<%=application.getAttribute("age")%>
</body>
</html>

image-20211125215057967

image-20211125215116530

applocation就相当于一个Map容器,把数据根据键(属性)放入到容器中,其他JSP和Servlet根据键来获取对应的值。

使用Servlet来获取属性值和JSP获取有些区别,使用Servlet来获取需要先创建ServletContext实例。

1
2
ini复制代码ServletContecxt sc = getServletConfig().getServletContext();
sc.getAttribute("age");

1.1.2 获得Web应用配置参数

使用application对象的getInitParameter(String paramName)方法来获取web应用的配置参数。

web应用的配置参数在web.xml中使用context-param元素配置。

格式如下:

1
2
3
4
xml复制代码<context-param>
<param-name>name</param-name>
<param-value>value</param-value>
</context-param>

所以在使用getInitParameter(String paramName)方法来获取参数之前,需要先配置应用参数。

小结

以上就是关于JSP的九大内置对象之一的Application对象,希望能对读者有所帮助,如有不正之处,欢迎留言评论。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

☕【Java技术指南】「技术盲点」也许你不了解的Mapme

发表于 2021-11-25

这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战

前提介绍

本篇文章给大家带来的内容是关于Map.merge()的详细介绍,希望对有需要的朋友有所帮助,今天介绍Map的merge方法,让我们来看看它的强大之处。

Map的merge方法

在JDK的API中,merge方法它是很特别的,也很新颖,但它是值得我们花时间去了解的,同时也推荐你可以运用到实际的项目代码中,对你们应该帮助很大。Map.merge方法。这可能是Map中最通用的操作。但它也相当模糊,几乎很少人会去使用它。

merge方法概念

merge()可以解释如下:它将新的值赋值给到key中(如果不存在)或更新具有给定值的现有key(UPSERT)。

计算案例

让我们从最基本的例子开始:计算唯一的单词出现次数。在java8之前的时候,代码非常混乱,实际的实现其实已经失去了本质层面的设计意义。

1
2
3
4
5
6
7
8
9
java复制代码var map = new HashMap();
words.forEach(word -> {
    var prev = map.get(word);
    if (prev == null) {
        map.put(word, 1);
    } else {
        map.put(word, prev + 1);
    }
});

按照上述代码的逻辑,假设给定一个输入集合

1
ini复制代码var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz");

输出的结果如下;

1
ini复制代码{Bar=1, Fizz=2, Foo=3, Buzz=2}

改进V1

现在让我们来重构它,主要去掉它的一些判断逻辑;

1
2
3
4
java复制代码words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.put(word, map.get(word) + 1);
});

这样的改进,是可以满足我们的重构要求。putIfAbsent()的具体用法就不过多描述。putIfAbsent那一行代码是一定需要的,否则,后面的逻辑也就会报错。而在下面代码中,又出现了put、get这一点会很奇怪,让我们再继续的进行改进设计。

改进V2

1
2
3
4
java复制代码words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.computeIfPresent(word, (w, prev) -> prev + 1);
});

computeIfPresent是仅当word中的的key存在的时候才调用给定的转换。否则它什么都不处理。我们通过将key初始化为零来确保key存在,因此增量始终有效。这样的实现是不是已经足够完美?未必,还有其他的思路可以减少额外的初始化。

1
2
java复制代码words.forEach(word ->
    map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)

compute()就像是computeIfPresent(),但无论给定key的存在与否如何都会调用它。如果key的值不存在,则prev参数为null。将简单移动if到隐藏在lambda中的三元表达式也远远没有达到最佳的表现。在我向你展示最终版本之前,让我们看一下稍微简化的默认实现Map.merge()源码分析。

改进V3

merge()源码

1
2
3
4
5
6
7
8
9
10
11
java复制代码default V merge(K key, V value, BiFunction remappingFunction) {
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    if (newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

阅读源码总是能够发现新大陆,merge() 适用于两种情况。

如果给定的key不存在,它就变成了put(key, value)。

如果key已经存在一些值,我们remappingFunction可以选择合并的方式。这个功能是完美契机上面的场景:

  • 只需返回新值即可覆盖旧值: (old, new) -> new
  • 只需返回旧值即可保留旧值: (old, new) -> old
  • 以某种方式合并两者,例如: (old, new) -> old + new
  • 甚至删除旧值: (old, new) -> null

如你所见,它merge()是非常通用的。那么,我们的问题该如何使用merge()呢?代码如下:

1
2
3
JAVA复制代码words.forEach(word ->
    map.merge(word, 1, (prev, one) -> prev + one)
);

你可以按照如下思路理解:如果没有key,那么初始化的value等于1;否则,将1添加到现有值。代码中的 one 是一个常量,因为我们的场景中,默认一直是加1,具体变化可以随意切换。

场景

想象一下,merge()真的那么好用吗?它的场景可以有什么?

举一个例子。你有一个帐户操作类

1
2
3
4
java复制代码class Operation {
    private final String accNo;
    private final BigDecimal amount;
}

以及针对不同帐户的一系列操作:

1
2
3
4
5
6
7
8
9
10
11
java复制代码operations = List.of(
    new Operation("123", new BigDecimal("10")),
    new Operation("456", new BigDecimal("1200")),
    new Operation("123", new BigDecimal("-4")),
    new Operation("123", new BigDecimal("8")),
    new Operation("456", new BigDecimal("800")),
    new Operation("456", new BigDecimal("-1500")),
    new Operation("123", new BigDecimal("2")),
    new Operation("123", new BigDecimal("-6.5")),
    new Operation("456", new BigDecimal("-600"))
);

我们希望为每个帐户计算余额(总运营金额)。假如不用merge(),就变得非常麻烦了:

1
2
3
4
5
6
7
java复制代码Map balances = new HashMap();
operations.forEach(op -> {
    var key = op.getAccNo();
    balances.putIfAbsent(key, BigDecimal.ZERO);
    balances.computeIfPresent(key, (accNo, prev) ->
prev.add(op.getAmount()));
});

使用merge之后的代码

1
2
3
4
java复制代码operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(),
                (soFar, amount) -> soFar.add(amount))
);

再进行优化的逻辑。

1
2
3
java复制代码operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add)
);

当然结果是正确的,这样简洁的代码心动吗?对于每个操作,add在给定的amount给定accNo。

1
ini复制代码{ 123 = 9.5,456 = - 100 }

ConcurrentHashMap

当我们再延伸到ConcurrentHashMap来,当 Map.merge的出现,和ConcurrentHashMap的结合那是非常的完美的。这样的搭配场景是对于那些自动执行插入或者更新操作的单线程安全的逻辑。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

MySQL中常用函数的日常总结

发表于 2021-11-25

「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战」

前言

  很多朋友都学习过数据库相关的操作,SQL作为访问和处理数据库的标准计算机语言,也有很多的函数方便我们调用。近期针对SQL中的函数进行了一次总结,特分享给大家。

SQL函数

  SQL函数主要分为两大类,一类是基于计算的函数Aggregate函数,另一类是基于数据处理的Scalar函数。

  其中基于计算的Aggregate函数主要是针对数据库中值得计算主要包含:平均值函数、最大值最小值函数、计算总和、计算总行数等一类函数。基于Scalar函数的函数主要是针对数据进行处理,例如:转大写、转小写、数据长度、格式化数据、字符串截取等相关函数。下面根据实际例子进行介绍。

  本次将基于下表中信息进行函数的演示。

1
2
3
4
5
6
7
8
9
10
11
js复制代码CREATE TABLE `goods` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`goods_name` varchar(255) DEFAULT NULL COMMENT '商品名称',
`price` decimal(10,2) DEFAULT NULL COMMENT '单价',
`discount` decimal(10,2) DEFAULT NULL COMMENT '优惠',
`freight` decimal(10,2) DEFAULT NULL COMMENT '运费',
`goods_num` int(11) DEFAULT NULL COMMENT '商品数量',
`class_type` varchar(100) DEFAULT NULL COMMENT '商品类型 文体用品 服饰',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

测试数据截图如下:

图片.png

Aggregate函数

MAX()

MAX() 函数的作用MAX()函数返回满足条件的列中的最大值,其中null是不参与计算的。

MAX() 函数的语法SELECT MAX(列名称) FROM 表名 WHERE 查询条件;

MAX() 函数的示例查询商品表中分类为“文体用品”的最高价格是多少。查询SQL如下:

1
js复制代码SELECT MAX(price) FROM goods WHERE class_type="文体用品";

MAX() 函数的执行结果查询商品表中分类为“文体用品”的最高价格是10元钱。

图片.png

MIN()

MIN()函数的作用MIN()函数返回满足条件的列中的最小值,其中null是不参与计算的。

MIN()函数的语法SELECT MIN(列名称) FROM 表名 WHERE 查询条件;

MIN()函数的示例查询商品表中分类为“服饰”的最低价格是多少。查询SQL如下:

1
js复制代码SELECT MIN(price) FROM goods WHERE class_type="服饰";

MIN()函数的执行结果查询商品表中分类为“服饰”的最低价格是30元。

图片.png

SUM()

SUM()函数的作用SUM()函数主要作用是计算指定列的总和。

SUM()函数的语法SELECT SUM(列名称) FROM 表名 WHERE 查询条件;

SUM()函数的示例计算商品表中所有商品的单价总和是多少;
计算商品表中分类为“文体用品”的商品总价值。SQL如下:

1
2
3
4
js复制代码-- 计算商品表中所有商品的单价总和是
SELECT SUM(price) FROM goods;
-- 计算商品表中分类为“文体用品”的商品总价值
SELECT SUM(price*goods_num) FROM goods WHERE class_type="文体用品";

SUM()函数的执行结果计算商品表中所有商品的单价总和是323元

图片.png
计算商品表中分类为“文体用品”的商品总价值225.00元。

图片.png

AVG()

AVG()函数的作用AVG()函数的作用是返回满足条件的指定列的平均值。

AVG()函数的语法SELECT AVG(列名称) FROM 表名 WHERE 查询条件;

AVG()函数的示例查询商品表中类目为“文体用品”的商品售价平均值。

1
js复制代码SELECT AVG(price) FROM goods WHERE class_type="文体用品";

AVG()函数的执行结果查询商品表中类目为“文体用品”的商品售价平均值为4.5元。

图片.png

COUNT()

COUNT()函数的作用COUNT()函数是查询满足条件的总行数。

COUNT()函数的语法SELECT COUNT(*) FROM 表名 WHERE 查询条件;

COUNT()函数的示例查询商品表中所有在销售的商品种类数量。

1
js复制代码SELECT COUNT(*) FROM goods;

COUNT()函数的执行结果查询商品表中所有在销售的商品种类数量是8件。

图片.png

FIRST()

FIRST()函数的作用FIRST() 函数返回指定的字段中第一个记录的值。

FIRST()函数的语法SELECT FIRST(*) FROM 表名 WHERE 查询条件 ORDER BY 列名;

FIRST()函数的示例

1
js复制代码SELECT FIRST(price) FROM goods WHERE class_type="服饰" ORDER BY id DESC;

LAST()

LAST()函数的作用LAST() 函数返回指定的字段中最后一个记录的值。

LAST()函数的语法SELECT LAST(*) FROM 表名 WHERE 查询条件 ORDER BY 列名;

LAST()函数的示例

1
js复制代码SELECT LAST(price) FROM goods WHERE class_type="服饰" ORDER BY id DESC;

Scalar 函数

UCASE()

UCASE()函数的作用UCASE()函数作用是将满足条件的指定列的值转为大写。

UCASE()函数的语法SELECT UCASE(列名) FROM 表名 WHERE 条件;

UCASE()函数的示例将商品表中类目为“文体用品”的商品名称转为大写

1
js复制代码SELECT UCASE(goods_name) FROM goods WHERE class_type="文体用品";

UCASE()函数的执行结果将商品表中类目为“文体用品”的商品名称转为大写执行结果如下:
图片.png

LCASE()

LCASE()函数的作用LCASE()函数作用是将满足条件的指定列的值转为小写。

LCASE()函数的语法SELECT LCASE(列名) FROM 表名 WHERE 条件;

LCASE()函数的示例将商品表中类目为“文体用品”的商品名称转为小写

1
js复制代码SELECT LCASE(goods_name) FROM goods WHERE class_type="文体用品";

LCASE()函数的执行结果将商品表中类目为“文体用品”的商品名称转为小写,执行结果如下:
图片.png

LENGTH()

LENGTH()函数的作用LENGTH()函数作用是输出满足条件的指定列的值的长度大小。需要注意的是有的数据库使用的函数是LEN()。

LENGTH()函数的语法SELECT LENGTH(列名) FROM 表名 WHERE 条件;

LENGTH()函数的示例将商品表中类目为“文体用品”的商品名称和商品名称值得长度查询出。

1
js复制代码SELECT goods_name,LENGTH(goods_name) FROM goods WHERE class_type="文体用品";

LENGTH()函数的执行结果将商品表中类目为“文体用品”的商品名称和商品名称值得长度查询结果如下:

图片.png

ROUND()

ROUND()函数的作用ROUND()的主要作用是对满足条件的某个数值字段进行指定小数位数的四舍五入

ROUND()函数的语法SELECT ROUND(列名,小数位数) FROM 表名 WHERE 条件;

ROUND()函数的示例将商品表中类目为“服饰”的商品名称和价格查询出来,并将价格保留三位小数。

1
js复制代码SELECT goods_name,ROUND(price,3) FROM goods WHERE class_type="服饰";

ROUND()函数的执行结果将商品表中类目为“服饰”的商品名称和价格查询出来,并将价格保留三位小数。执行结果如下:

图片.png

FORMAT()

FORMAT()函数的作用FORMAT()函数主要作用是对满足条件的指定列进行格式化显示。

FORMAT()函数的语法SELECT FORMAT(列名,格式) FROM 表名 WHERE 条件;

FORMAT()函数的示例使用DATE_FORMAT对商品表中的创建时间格式化显示为年-月-日的形式。

1
js复制代码SELECT goods_name,price,DATE_FORMAT(create_time,'%Y-%c-%d') FROM goods

FORMAT()函数的执行结果使用DATE_FORMAT对商品表中的创建时间格式化显示为年-月-日的形式。结果如下:

图片.png

NOW()

NOW()函数的作用NOW()函数的作用是返回当前系统的时间

NOW()函数的语法SELECT NOW();

NOW()函数的示例获取当时数据库的时间。

1
js复制代码SELECT NOW();

NOW()函数的执行结果

图片.png

SUBSTR()

SUBSTR()函数的作用SUBSTR()函数是将满足添加的指定列的数据按一定规则截取显示,部分数据库使用的是MID() 函数。

SUBSTR()函数的语法SELECT SUBSTR(列名,起始位置默认为1,返回的字符数可以为空) FROM 表名 WHERE 条件;

SUBSTR()函数的示例将商品表中类目为“文体用品”的商品名保留前两个字符输出显示。

1
js复制代码SELECT goods_name,SUBSTR(goods_name,1,2) FROM goods WHERE class_type="文体用品";

SUBSTR()函数的执行结果将商品表中类目为“文体用品”的商品名保留前两个字符输出显示结果如下:

图片.png

结语

  好了,以上就是MySQL中常用函数的日常总结,感谢您的阅读,希望您喜欢,如对您有帮助,欢迎点赞收藏。如有不足之处,欢迎评论指正。下次见。

  作者介绍:【小阿杰】一个爱鼓捣的程序猿,JAVA开发者和爱好者。公众号【Java全栈架构师】维护者,欢迎关注阅读交流。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

互联网通信快速入门

发表于 2021-11-25

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

前言

互联网通信是web开发中十分重要的东西,今天我们就来简单看看互联网通信的一些内容

什么是互联网通信?

两台计算机通过网络实现文件共享行为,简单来说就是利用互联网来进行用户之间信息交换的行为就是互联网通信

互联网通信过程角色划分

客户端计算机:用于发送请求,来索要资源文件的计算机

客户端是我们提供给用户来进行访问的,所以客户端在设计时要更加的考虑用户的需求

服务端计算机:用于接收请求,并提供对应的资源文件计算机

服务端是我们开发人员来提供服务的,我们需要在服务端写好相关的方法

互联网通信模型

互联网通信主要分两种:C/S 通信模型 和B/S通信模型

C/S通信模型:

C(client software)指的是客户端软件,就像我们经常使用的那些APP比如QQ,微信,支付宝….

它具有以下特点:

  1. 安装在用户客户端计算机上
  2. 由它向指定服务端计算机发送请求,索要资源文件
  3. 解析从服务端返回的相关信息(二进制->文字,图片,视频…..)

S (server software) 指的是服务器软件

它具有以下特点:

1
2
3
4
5
6
> markdown复制代码 1. 安装在服务端计算机上
> 2. 接收来自于特定的客户端软件发送请求
> 3. 接收到请求之后自动的在服务端计算机上定位被访问的资源文件
> 4. 自动的将定位的文件内容解析为【二进制数据】通过网络发送回发起请求的客户端软件上
>
>

C/S通信模型的好处:

​ 1. 因为客户端软件已经下载了一些文件内容,所以它不用经常访问服务端,降低服务端计算机工作压力

​ 2. 也是因为与服务端访问的次数减少,所以安全性比较高

缺点:

​ 1.因为访问次数少所以每次要获取的资源就比较多,增加客户获得服务的成本

​ 2.因为通信一次成本比较高,维护起来比较繁琐

B/S通信模型:

B(browser)浏览器 ,浏览器是我们平常使用较多的访问外界资源的方式,它轻便快捷,使得很多人都喜欢使用这种方式来获取资源。

它的特点是:

1
2
3
4
5
> markdown复制代码  1. 浏览器安装在客户端计算机软件
> 1. 可以通过IP地址任意服务器发送请求,索要资源文件
> 1. 可以将服务器返回的【二进制数据】解析为【文字,数字,图片,视频,命令】
>
>

​

S: server software 服务器软件,B/S通信模型的服务器软件比较特别就是可以接收任意浏览器发送请求

B/S通信模型的好处

​ 1.因为每次从服务端拿取的文件数都比较少,不会增加用户获得服务的成本
​ 2.因为访问次数多,几乎不需要更新浏览器

缺点:

​ 1.因为访问次数多,容易被攻击,几乎无法有效对服务端计算机资源文件进行保护
​ 2.通信过于频繁,服务端计算机工作压力异常巨大

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…188189190…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%