mysql update column_MySQL8.0 新特性:Partial Update of LOB Column

摘要: MySQL8.0对json进行了比较完善的支持, 我们知道json具有比较特殊的存储格式,通常存在多个key value键值对,对于类似更新操作通常不会更新整个json列,而是某些键值。 对于某些复杂的应用,json列的数据可能会变的非常庞大,这时候一个突出的问题是:innodb并不识别json类型,对它而言这些存储统一都是LOB类型,而在之前的版本中Innodb处理LOB更新的方式是标记删除旧记录,并插入新记录,显然这会带来一些存储上的开销(尽管Purge线程会去后台清理),而写入的redo log和Binlog的量也会偏高,对于超大列,可能会严重影响到性能。

MySQL8.0对json进行了比较完善的支持, 我们知道json具有比较特殊的存储格式,通常存在多个key value键值对,对于类似更新操作通常不会更新整个json列,而是某些键值。

对于某些复杂的应用,json列的数据可能会变的非常庞大,这时候一个突出的问题是:innodb并不识别json类型,对它而言这些存储统一都是LOB类型,而在之前的版本中Innodb处理LOB更新的方式是标记删除旧记录,并插入新记录,显然这会带来一些存储上的开销(尽管Purge线程会去后台清理),而写入的redo log和Binlog的量也会偏高,对于超大列,可能会严重影响到性能。为了解决这个问题,MySQL8.0引入了LOB列部分更新的策略。

本文仅仅是笔者在理解该特性时做的一些简单的笔记,,记录的主要目的是用于以后如果涉及到相关的工作可以快速展开,因此比较凌乱

目前partial update需要通过JSON_SET, 或者JSON_REPLACE等特定接口来进行json列的更新,并且不是所有的更新都能够满足条件:

没有增加新的元素

空间足够大,可以容纳替换的新值

但类似数据长度(10 =>更新成7=>更新成9)是允许的

下面以json_set更新json列为例来看看相关的关键堆栈

检查是否支持partial update

如上所述,需要指定的json函数接口才能进行partial update

mysql_execute_command

|--> Sql_cmd_dml::execute

|--> Sql_cmd_dml::prepare

|--> Sql_cmd_update::prepare_inner

|---> prepare_partial_update

|-->Item_json_func::supports_partial_update

这里只是做预检查,对于json列的更新如果全部是通过json_set/replace/remove进行的,则将其标记为候选partial update的列(TABLE::mark_column_for_partial_update), 存储在bitmap结构TABLE::m_partial_update_columns

设置partial update

入口函数:TABLE::setup_partial_update()

在满足某些条件时,需要设置logical diff(用于记录partial update列的binlog,降低binlog存储开销):

binlog_row_value_options设置为"partial_json"

binlog 打开

log_bin_use_v1_row_events关闭

使用row format

然后创建Partial_update_info对象(Table::m_partial_update_info), 用于存储partial update执行过程中的状态

Table::m_enabled_logical_diff_columns

TABLE::m_binary_diff_vectors

TABLE::m_logical_diff_vectors

创建更新向量

当读入一行记录后,就需要根据sql语句来构建后镜像,而对于partial update所涉及的json列,会做特殊处理:

Sql_cmd_update::update_single_table

|--> fill_record_n_invoke_before_triggers

|-->fill_record

|--> Item::save_in_field

|--> Item_func::save_possibly_as_json

|--> Item_func_json_set_replace::val_json

|--> Json_wrapper::attempt_binary_update

|--> json_binary::Value::update_in_shadow

|--> TABLE::add_binary_diff

json_wrapper::attempt_binary_update : 做必要的数据类型检查(是否符合partial update的条件)后,计算需要的空间,检查是否有足够的空闲空间Value::has_space()来替换成新值。

Value::update_in_shadow: 进一步将变化的数据存储到binary diff对象中(TABLE::add_binary_diff),每个Binary_diff对象包含了要修改对象的偏移量,长度以及一个指向新数据的const指针

如下例,摘自函数Value::update_in_shadow的注释,这里提取出来,以便于理解json binary的格式,以及如何产生Binary Diff

创建测试表:

root@test 10:00:45>create table t (a int primary key, b json);

Query OK, 0 rows affected (0.02 sec)

root@test 10:01:06>insert into t values (1, '[ "abc", "def" ]');

Query OK, 1 row affected (0.07 sec)

json数据的存储格式如下:

0x02 - type: small JSON array

0x02 - number of elements (low byte)

0x00 - number of elements (high byte)

0x12 - number of bytes (low byte)

0x00 - number of bytes (high byte)

0x0C - type of element 0 (string)

0x0A - offset of element 0 (low byte)

0x00 - offset of element 0 (high byte)

0x0C - type of element 1 (string)

0x0E - offset of element 1 (low byte)

0x00 - offset of element 1 (high byte)

0x03 - length of element 0

'a'

'b' - content of element 0

'c'

0x03 - length of element 1

'd'

'e' - content of element 1

'f'

更新json列的'abc'为'XY', 则空出一个字节出来:

root@test 10:01:39>UPDATE t SET b = JSON_SET(b, '$[0]', 'XY');

Query OK, 1 row affected (0.01 sec)

Rows matched: 1 Changed: 1 Warnings: 0

此时的存储格式为:

0x02 - type: small JSON array

0x02 - number of elements (low byte)

0x00 - number of elements (high byte)

0x12 - number of bytes (low byte)

0x00 - number of bytes (high byte)

0x0C - type of element 0 (string)

0x0A - offset of element 0 (low byte)

0x00 - offset of element 0 (high byte)

0x0C - type of element 1 (string)

0x0E - offset of element 1 (low byte)

0x00 - offset of element 1 (high byte)

CHANGED 0x02 - length of element 0

CHANGED 'X'

CHANGED 'Y' - content of element 0

(free) 'c'

0x03 - length of element 1

'd'

'e' - content of element 1

'f'

此处只影响到一个element,因此 只有一个binary diff

再执行更新:

UPDATE t SET j = JSON_SET(j, '$[1]', 'XYZW')

第二个element从3个字节更新成4个字节,显然原地没有足够的空间,但可以利用其一个element的剩余空间

0x02 - type: small JSON array

0x02 - number of elements (low byte)

0x00 - number of elements (high byte)

0x12 - number of bytes (low byte)

0x00 - number of bytes (high byte)

0x0C - type of element 0 (string)

0x0A - offset of element 0 (low byte)

0x00 - offset of element 0 (high byte)

0x0C - type of element 1 (string)

CHANGED 0x0D - offset of element 1 (low byte)

0x00 - offset of element 1 (high byte)

0x02 - length of element 0

'X' - content of element 0

'Y' - content of element 0

CHANGED 0x04 - length of element 1

CHANGED 'X'

CHANGED 'Y'

CHANGED 'Z' - content of element 1

CHANGED 'W'

这里会产生两个binary diff,一个更新offset, 一个更新数据

我们再执行一条update,将字符串修改成整数,这种情况下,原来存储字符串offset的位置被更改成了整数,而原来字符串占用的空间变成Unused状态。这里只

UPDATE t SET b= JSON_SET(b, '$[1]', 456)

0x02 - type: small JSON array

0x02 - number of elements (low byte)

0x00 - number of elements (high byte)

0x12 - number of bytes (low byte)

0x00 - number of bytes (high byte)

0x0C - type of element 0 (string)

0x0A - offset of element 0 (low byte)

0x00 - offset of element 0 (high byte)

CHANGED 0x05 - type of element 1 (int16)

CHANGED 0xC8 - value of element 1 (low byte)

CHANGED 0x01 - value of element 1 (high byte)

0x02 - length of element 0

'X' - content of element 0

'Y' - content of element 0

(free) 0x04 - length of element 1

(free) 'X'

(free) 'Y'

(free) 'Z' - content of element 1

(free) 'W

类型从string变成int16,使用之前offset的字段记录int值,而原来string的空间则变成空闲状态, 这里产生一个binary diff。

我们再来看看另外一个相似的函数Value::remove_in_shadow,即通过json_remove从列上移除一个字段,以下样例同样摘自函数的注释:

json列的值为

{ "a": "x", "b": "y", "c": "z" }

存储格式:

0x00 - type: JSONB_TYPE_SMALL_OBJECT

0x03 - number of elements (low byte)

0x00 - number of elements (high byte)

0x22 - number of bytes (low byte)

0x00 - number of bytes (high byte)

0x19 - offset of key "a" (high byte)

0x00 - offset of key "a" (low byte)

0x01 - length of key "a" (high byte)

0x00 - length of key "a" (low byte)

0x1a - offset of key "b" (high byte)

0x00 - offset of key "b" (low byte)

0x01 - length of key "b" (high byte)

0x00 - length of key "b" (low byte)

0x1b - offset of key "c" (high byte)

0x00 - offset of key "c" (low byte)

0x01 - length of key "c" (high byte)

0x00 - length of key "c" (low byte)

0x0c - type of value "a": JSONB_TYPE_STRING

0x1c - offset of value "a" (high byte)

0x00 - offset of value "a" (low byte)

0x0c - type of value "b": JSONB_TYPE_STRING

0x1e - offset of value "b" (high byte)

0x00 - offset of value "b" (low byte)

0x0c - type of value "c": JSONB_TYPE_STRING

0x20 - offset of value "c" (high byte)

0x00 - offset of value "c" (low byte)

0x61 - first key ('a')

0x62 - second key ('b')

0x63 - third key ('c')

0x01 - length of value "a"

0x78 - contents of value "a" ('x')

0x01 - length of value "b"

0x79 - contents of value "b" ('y')

0x01 - length of value "c"

0x7a - contents of value "c" ('z')

将其中的成员$.b移除掉:

UPDATE t SET j = JSON_REMOVE(j, '$.b');

格式为:

0x00 - type: JSONB_TYPE_SMALL_OBJECT

CHANGED 0x02 - number of elements (low byte)

0x00 - number of elements (high byte)

0x22 - number of bytes (low byte)

0x00 - number of bytes (high byte)

0x19 - offset of key "a" (high byte)

0x00 - offset of key "a" (low byte)

0x01 - length of key "a" (high byte)

0x00 - length of key "a" (low byte)

CHANGED 0x1b - offset of key "c" (high byte)

CHANGED 0x00 - offset of key "c" (low byte)

CHANGED 0x01 - length of key "c" (high byte)

CHANGED 0x00 - length of key "c" (low byte)

CHANGED 0x0c - type of value "a": JSONB_TYPE_STRING

CHANGED 0x1c - offset of value "a" (high byte)

CHANGED 0x00 - offset of value "a" (low byte)

CHANGED 0x0c - type of value "c": JSONB_TYPE_STRING

CHANGED 0x20 - offset of value "c" (high byte)

CHANGED 0x00 - offset of value "c" (low byte)

(free) 0x00

(free) 0x0c

(free) 0x1e

(free) 0x00

(free) 0x0c

(free) 0x20

(free) 0x00

0x61 - first key ('a')

(free) 0x62

0x63 - third key ('c')

0x01 - length of value "a"

0x78 - contents of value "a" ('x')

(free) 0x01

(free) 0x79

0x01 - length of value "c"

0x7a - contents of value "c" ('z')

这里会产生两个binary diff,一个用于更新element个数,一个用于更新offset。

从上面的例子可以看到,每个Binary diff表示了一段连续更新的数据,有几段连续更新的数据,就有几个binary diff。 binary diff存储到TABLE::m_partial_update_info->m_binary_diff_vectors中,

写入logical diff

logical diff 主要用于优化写binlog

Sql_cmd_update::update_single_table

|--> fill_record_n_invoke_before_triggers

|-->fill_record

|--> Item::save_in_field

|--> Item_func::save_possibly_as_json

|--> Item_func_json_set_replace::val_json

|-->TABLE::add_logical_diff

新的LOB存储格式

相关代码:

storage/innobase/lob/*, 所有的类和函数定义在namesapce lob下面

从上面的分析可以看到,Server层已经提供了所有修改的偏移量,新数据长度,已经判断好了数据能够原地存储,对于innodb,则须要利用这些信息来实现partial update 。

在展开这个问题之前,我们先来看下innodb针对json列的新格式。从代码中可以看到,为了实现partial update, innodb增加了几种新的数据页格式:

压缩表:

FIL_PAGE_TYPE_ZLOB_FIRST

FIL_PAGE_TYPE_ZLOB_DATA

FIL_PAGE_TYPE_ZLOB_INDEX

FIL_PAGE_TYPE_ZLOB_FRAG

FIL_PAGE_TYPE_ZLOB_FRAG_ENTRY

普通表:

FIL_PAGE_TYPE_LOB_INDEX

FIL_PAGE_TYPE_LOB_DATA

FIL_PAGE_TYPE_LOB_FIRST

我们知道,传统的LOB列通常是在聚集索引记录内留一个外部存储指针,指向lob存储的page,如果一个page存储不下,就会产生lob page链表。而新的存储格式,则引入了lob index的概念,也就是为所有的lob page建立索引,格式如下:

ref pointer in cluster record

-------

|

FIL_PAGE_TYPE_LOG_FIRST

|

FIL_PAGE_TYPE_LOB_INDEX -----------> FIL_PAGE_TYPE_LOB_DATA

|

FIL_PAGE_TYPE_LOB_INDEX -------------> FIL_PAGE_TYPE_LOB_DATA

|

... ....

Note: 本文只讨论非压缩表的场景, 对于压缩表引入了更加复杂的数据类型,以后有空再在本文补上。

ref Pointer格式如下(和之前相比,增加了版本号)

字段 字节数 描述

BTR_EXTERN_SPACE_ID 4 space id

BTR_EXTERN_PAGE_NO 4 第一个 lob page的no

BTR_EXTERN_OFFSET/BTR_EXTERN_VERSION 4 新的格式记录version号

第一个FIL_PAGE_TYPE_LOG_FIRST页面的操作定义在 lob::first_page_t类中格式如下(参考文件: include/lob0first.h lob/lob0first.cc):

字段 字节数 描述

OFFSET_VERSION 1 表示lob的版本号,当前为0,用于以后lob格式改变做版本区分

OFFSET_FLAGS 1 目前只使用第一个bit,被设置时表示无法做partial update, 用于通知purge线程某个更新操作产生的老版本LOB可以被完全释放掉

OFFSET_LOB_VERSION 4 每个lob page都有个版本号,初始为1,每次更新后递增

OFFSET_LAST_TRX_ID 6

OFFSET_LAST_UNDO_NO 4

OFFSET_DATA_LEN 4 存储在该page上的数据长度

OFFSET_TRX_ID 6 创建存储在该page上的事务id

OFFSET_INDEX_LIST 16 维护lob page链表

OFFSET_INDEX_FREE_NODES 16 维护空闲节点

LOB_PAGE_DATA 存储数据的起始位置,注意第一个page同时包含了lob index 和lob data,但在第一个lob page中只包含了10个lob index记录,每个lob index大小为60字节

除了第一个lob page外,其他所有的lob page都是通过lob index记录来指向的,lob index之间链接成链表,每个index entry指向一个lob page,

普通Lob Page的格式如下

字段 字节数 描述

OFFSET_VERSION 1 lob data version,当前为0

OFFSET_DATA_LEN 4 数据长度

OFFSET_TRX_ID 6 创建该lob page的事务Id

LOB_PAGE_DATA lob data开始的位置

lob index entry的大小为60字节,主要包含如下内容(include/lob0index.h lob/lob0index.cc):

偏移量 字节数 描述

OFFSET_PREV 6 Pointer to the previous index entry

OFFSET_NEXT 6 Pointer to the next index entry

OFFSET_VERSIONS 16 Pointer to the list of old versions for this index entry

OFFSET_TRXID 6 The creator transaction identifier.

OFFSET_TRXID_MODIFIER 6 The modifier transaction identifier

OFFSET_TRX_UNDO_NO 4 the undo number of creator transaction.

OFFSET_TRX_UNDO_NO_MODIFIER 4 The undo number of modifier transaction.

OFFSET_PAGE_NO 4 The page number of LOB data page

OFFSET_DATA_LEN 4 The amount of LOB data it contains in bytes.

OFFSET_LOB_VERSION 4 The LOB version number to which this index entry belongs.

从index entry的记录格式我们可以看到 两个关键信息:

对lob page的修改会产生新的lob page(“lob::replace()”) 和新的lob index entry

lob page no及其数据长度,据此我们可以根据修改的数据在json column里的offset,通过lob index快速的定位到其所在的lob page

每个lob page的版本号: 为了实现mvcc多版本,用户线程先从undo log中找到对应版本的clust record,找出其中存储的版本号v1,然后在扫描lob index时,如index entry中记录的版本号<= v1,则是可见的,如果> v1, 那么就需要根据OFFSET_VERSIONS链表,找到对应版本的index entry,并

根据这个老的Index entry找到对应的lob page, 如下所示:

EXTERN REF (v2)

|

LOB IDX ENTRY (v1)

|

LOB IDX ENTRY(v2) -----> LOB IDX ENTRY(v1)

|

LOG IDX ...(v1)

多版本读判断参考函数 'lob::read'

lob更新lob::update: 根据binary diff,依次replace

Note: 不是所有的lob数据都需要partial update, 额外的lob index同样会带来存储开销,因此定义了一个threshold(ref_t::LOB_BIG_THRESHOLD_SIZE),超过2个page才去做partial update; 另外row_format也要确保lob列不存储列前缀到clust index ( ref btr_store_big_rec_extern_fields)

写入binlog

在更新完一行后,对应的变更需要打包到线程的cache中(THD::binlog_write_row() --> pack_row()), 这时候要对partial update进行特殊处理,需要设置特定选项:

binlog_row_image = MINIMAL;

binlog_row_value_options=PARTIAL_JSON

如上例第一个update产生的binlog如下:

UPDATE t SET b = JSON_SET(b, '$[0]', 'XY');

binlog:

'/*!*/;

### UPDATE `test`.`t`

### WHERE

### @1=1 /* INT meta=0 nullable=0 is_null=0 */

### SET

### @2=JSON_REPLACE(@2, '$[0]', 'XY') /* JSON meta=4 nullable=1 is_null=0 */

由于存在主键,因此前镜像只记录了主键值,而后镜像也只记录了需要更新的列的内容,对于超大Json列,binlog上的开销也是极小的,考虑到binlog通常会成为性能瓶颈点,预计这一特性会带来不错的吞吐量提升

原文链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/506902.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

myeclipse打包java文件_MyEclipse将Java项目打包成jar文件的三种方法

方案一&#xff1a;用Eclipse自带的Export功能步骤1&#xff1a;准备主清单文件 “MANIFEST.MF”&#xff0c;由于是打包引用了第三方jar包的Java项目&#xff0c;故需要自定义配置文件MANIFEST.MF&#xff0c;在该项目下建立文件MANIFEST.MF&#xff0c;内容如下&#xff1a;M…

java 对象等于_java 之类对象等于对象 | 学步园

/** 使per2等于per1&#xff0c;其实是引用&#xff0c;当改变per2的属性时&#xff0c;实质上是改变了per2,per1所共同指向的堆内存里的数据。*/package StringClass;/**** author zendao*/public class CopyAClass {public static void main(String args[]) {NewPeople per1 …

java insert access_java连接access时无法使用INSERT语句添加数据

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼求大神指教~~~~java连接access数据库的问题&#xff0c;那一句话的其他功能和access中运行都没有问题&#xff0c;但是就是不能插入数据库中去&#xff0c;好纠结&#xff0c;到底是怎麼回事&#xff1f;public static void Update…

java程序模拟atm机_Java项目实现模拟ATM机

本文实例为大家分享了Java实现模拟ATM机的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下项目名称模拟ATM机项目描述简单实现ATM机功能代码实现测试类public class Test {//模拟多功能ATM机public static void main(String[] args) {ATM atm new ATM();atm.opearte(…

java计算器流程图_帮帮忙:Java小计算器代码,及需求分析.流程图.

展开全部package example;import java.awt.*;import java.awt.event.*;public class Calculator extends Frame {/*** 本实例实现功能如下 1.普通加减乘除运算 2.小数点的情况已经解32313133353236313431303231363533e58685e5aeb931333236393738决 3.开始按0已经解决 4.消去键可…

mysql 5.6.22编译_saltstack全编译安装mysql5.6.22

关闭selinux和firewalld&#xff0c;iptables开放4505和4506端口目的&#xff1a;通过salt-master实现对salt-minion的批量自动安装mysql步骤(都在master执行)&#xff1a;1&#xff0c;检测连通性salt ‘*‘ test.ping2&#xff0c;下载mysql的源码包至/srv/salt/mysql/files/…

java 邮件跟踪_如何跟踪邮件已读状态(Java)

>图片的展示形式&#xff1a;html中图片元素表示&#xff1a;其中src部分是比较好玩的&#xff0c;我们可以用图片服务器链接地址&#xff0c;例如&#xff1a;我们也可以使用base64的图片字符串&#xff0c;例如&#xff1a;>利用图片加载来跟踪用户打开邮件状态&#x…

vs2012 entity framework mysql_MVC4,MVC3,VS2012+ entity framework Migration from Sqlserver

在开发的初期个人认为因VS与Sqlserver的配合很默契&#xff0c;即可以方便的实现Code First&#xff0c;又可以使用SqlServer Manager很漂亮的进行建模与变更&#xff0c;也许是个人的使用习惯MS的界面做的很好&#xff0c;乎开源的产品美感上都追不上商用版。 所以个人比较喜欢…

用java做一个截图工具_Java制作屏幕截图软件(还可以保存到剪切板内)

原标题&#xff1a;Java制作屏幕截图软件(还可以保存到剪切板内)package com.kaige123;import java.awt.Rectangle;import java.awt.Robot;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.image.BufferedIm…

java c语言 for_Java能写C语言编译器吗

用java是可以写语言编译器的&#xff0c;用任何语言工具写都可以。各语言开发工具间只有开发效率的差异&#xff0c;没有可行不可行的区别。编译器其实就是一个翻译工具&#xff0c;它可以把代码直接翻译成二进制文件交给CPU执行(二进制指令就是CPU的指令集&#xff0c;当然可以…

手机 java服务器ip地址_java获取系统当前服务器IP地址

BZOJ-1008 越狱 数论快速幂1008: [HNOI2008]越狱 Time Limit: 1 Sec Memory Limit: 162 MB Submit: 6192 Solved: 2636 [Submit][Status] ...VBScript - CUD registry key and valuehttp://msdn.microsoft.com/en-us/library/aa384906(vvs.85).aspx HKEY_LOCAL_MACHINE &H…

linux定时结束java进程_使用zt-exec库定时清理linux休眠进程

在几个月前上线的一个采集项目&#xff0c;构架是基于java selenium chromedriver chrome实现的采集。至于为哈不直接用jsoup或httpclient实现采集功能&#xff0c;是因为很多被采集页面都是通过js来渲染内容的&#xff0c;所以必须用webdriverchrome来模拟真正的浏览器访问…

java的流传输的进度条_JAVA程序设计(17)----- 制作文件拷贝软件 进程 输入流输出流 NIO 进度条 底层拷贝 多线程...

使用NIO对文件进行底层拷贝(按照字节)多线程技术初级应用 不阻塞程序运行package com.lovo.homework01;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOu…

java 安卓调试_【转】Android 调试技术

一、JAVA层单步调试二、Native层单步调试三、JAVA层堆栈打印1. 在指定的函数内打印相关java调用 Log.d(TAG,Log.getStackTraceString(new Throwable()));2. 普通JAVA进程堆栈 ActivityManagerService.dumpStackTraces保存在系统设置dalvik.vm.stack-trace-file指定的文件data/a…

java spring4 jar包_spring jar包官方下载|

spring-web jar包是用来SSH配置的jar包 配置了监听器&#xff0c;还出现严重: Error configuring application listener of class org.springframework.web.context.ContextLoader就需要它&#xff0c;欢迎有需要的用户前来it猫扑下载&#xff01;spring-web.jar作用spring-web…

java 找不到符号 con_我的java程序运行时,提示找不到符号,求解!

我的java程序运行时&#xff0c;提示找不到符号&#xff0c;求解!关注:163 答案:4 mip版解决时间 2021-02-07 23:57提问者人潮拥挤你不在2021-02-06 23:57import java.awt.*;import java.awt.event.*;import java.applet.Applet;import java.applet.AudioClip;public class M…

java字符串为空抛出异常_Java 判断字符串是否为空的四种方法,及效率比较。

以下是Java 判断字符串是否为空的四种方法:方法一: 最多人使用的一个方法, 直观, 方便, 但效率很低:if(s null ||"".equals(s));方法二: 比较字符串长度, 效率高, 是我知道的最好一个方法:if(s null || s.length() < 0);方法三: Java SE 6.0 才开始提供的方法, …

java抽组件_Java实现的基于模板的网页结构化信息精准抽取组件:HtmlExtractor

HtmlExtractor由2个子项目构成&#xff0c;html-extractor和html-extractor-web。html-extractor实现了数据抽取逻辑&#xff0c;是从节点&#xff0c;html-extractor-web提供web界面来维护抽取规则&#xff0c;是主节点。html-extractor是一个jar包&#xff0c;可通过maven引用…

mysql索引过多为什么会慢_mysql – 为什么索引使这个查询更慢?

摘要问题是由于b-trees的性质,字段不适合索引.说明假设你有一张表有500,000个掷硬币的结果,其中抛掷是1(头)或0(尾)&#xff1a;CREATE TABLE toss (id int NOT NULL AUTO_INCREMENT,result int NOT NULL DEFAULT 0,PRIMARY KEY ( id ))select result, count(*) from toss grou…

pythonweb管理电脑_Python远程控制局域网计算机网络远程控制软件Python的开发,python,电脑,web...

[Python] 纯文本查看 复制代码import web import osimport timefrom PIL import ImageGrabimport numpy as npimport cv2urls (/reboot_html/(.*), reboot_html,/jp_html/(.*), jp_html,/shutdown_html/(.*), shutdown_html,/(js|css|images)/(.*), static)app web.applicati…