《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
collation[46] = new Collation(46, “utf8mb4_bin”, 0, MYSQL_CHARSET_NAME_utf8mb4);
…
collation[255] = new Collation(255, “utf8mb4_0900_ai_ci”, 0, “utf8mb4”);
…
Set tempUTF8MB4Indexes = new HashSet();
Collation notUsedCollation = new Collation(0, COLLATION_NOT_DEFINED, 0, NOT_USED);
for (int i = 1; i < MAP_SIZE; i++) {
Collation coll = collation[i] != null ? collation[i] : notUsedCollation;
COLLATION_INDEX_TO_COLLATION_NAME[i] = coll.collationName;
COLLATION_INDEX_TO_CHARSET[i] = coll.mysqlCharset;
String charsetName = coll.mysqlCharset.charsetName;
…
// 找到字符集名称为’utf8mb4’并且把对应的下表保存到 tempUTF8MB4Indexes 中
if (charsetName.equals(MYSQL_CHARSET_NAME_utf8mb4)) {
tempUTF8MB4Indexes.add(i);
}
}
CHARSET_NAME_TO_COLLATION_INDEX = Collections.unmodifiableMap(charsetNameToCollationIndexMap);
UTF8MB4_INDEXES = Collections.unmodifiableSet(tempUTF8MB4Indexes);
复制代码
Collation 类
class Collation {
// 下标
public final int index;
public final String collationName;
public final int priority;
public final MysqlCharset mysqlCharset;
public Collation(int index, String collationName, int priority, String charsetName) {
this.index = index;
this.collationName = collationName;
this.priority = priority;
this.mysqlCharset = CharsetMapping.CHARSET_NAME_TO_CHARSET.get(charsetName);
}
}
复制代码
首先,会初始化一个类型为 Collation
的数组,并且会创建多个 Collation
实例来保存 MySQL 的字符集(Charset)以及排序规则(Collation)。接下来会遍历该数组找到字符集名称为UTF8MB4
的下标,最终生成一个只读的 Set 赋值给UTF8MB4_INDEXES
变量。 此时UTF8MB4_INDEXES
中就有了所有字符集名称为UTF8MB4
的下标。
- MysqlIO
当一个 MySQL 连接被创建时,会通过MysqlIO
类与 MySQL Server 建立 TCP 连接。
当 TCP 连接建立成功后,MySQL Server 会返回一个 Greeting 包。
此时通过调用类中的doHandshake
方法:
void doHandshake(String user, String password, String database) throws SQLException {
// Read the first packet
this.checkPacketSequence = false;
this.readPacketSequence = 0;
Buffer buf = readPacket();
// Get the protocol version
this.protocolVersion = buf.readByte();
…
this.serverCapabilities = 0;
// read capability flags (lower 2 bytes)
if (buf.getPosition() < buf.getBufLength()) {
this.serverCapabilities = buf.readInt();
}
// 当版本
if ((versionMeetsMinimum(4, 1, 1) || ((this.protocolVersion > 9) && (this.serverCapabilities&CLIENT_PROTOCOL_41) != 0))) {
// read character set (1 byte)
this.serverCharsetIndex = buf.readByte() & 0xff;
…
}
…
}
复制代码
此时调用readPacket
方法,会获取到 MySQL Server 返回的 Greeting 包数据。其中的一个字节就代表serverCharsetIndex
(以上图为例serverCharsetIndex
就是位于f7
后面的21
,转为 10 进制就是33
),并且与0xff
进行&
操作保证它的值不会大于 255。
- ConnectionImpl
同样,在 MySQL 连接被创建时,会调用ConnectionImpl
中的configureClientCharacterSet
方法设置客户端字符编码类型。
private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
// 来自于 MySQL 属性 characterEncoding
String realJavaEncoding = getEncoding();
boolean characterSetAlreadyConfigured = false;
if (versionMeetsMinimum(4, 1, 0)) {
characterSetAlreadyConfigured = true;
// 设置变量 useUnicode 为true, 同时来自于 MySQL 属性 useUnicode
setUseUnicode(true);
configureCharsetProperties();
// we need to do this again to grab this for versions > 4.1.0
realJavaEncoding = getEncoding();
…
// 是否使用 unicode。MySQL 版本 >= 4.1.0 或者设置属性 useUnicode=true。
// 则返回为 true
if (getUseUnicode()) {
// 设置属性 characterEncoding 则不为 null
if (realJavaEncoding != null) {
if (realJavaEncoding.equalsIgnoreCase(“UTF-8”) || realJavaEncoding.equalsIgnoreCase(“UTF8”)) {
// MySQL 版本是否 >= 5.5.2, 该版本之后才支持 utf8mb4
boolean utf8mb4Supported = versionMeetsMinimum(5, 5, 2);
// UTF8MB4_INDEXES 是否包含 serverCharsetIndex
boolean useutf8mb4 = utf8mb4Supported && f(CharsetMapping.UTF8MB4_INDEXES.contains(this.io.serverCharsetIndex));
// 除非设置了 useOldUTF8Behavior 属性, 否则默认 false
if (!getUseOldUTF8Behavior()) {
// characterSetNamesMatches 方法主要判断 character_set_client、character_set_connection 这两个属性的值
if (dontCheckServerMatch || !characterSetNamesMatches(“utf8”) || (utf8mb4Supported && !characterSetNamesMatches(“utf8mb4”))) {
execSQL(null, "SET NAMES " + (useutf8mb4 ? “utf8mb4” : “utf8”), -1, null, DEFAULT_RESULT_SET_TYPE,DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false);
this.serverVariables.put(“character_set_client”, useutf8mb4 ? “utf8mb4” : “utf8”);
this.serverVariables.put(“character_set_connection”, useutf8mb4 ? “utf8mb4” : “utf8”);
}
}
}
}
}
}
…
}
复制代码
我们再来看看serverCharsetIndex
这个值
根据该数据包返回结果来看,serverCharsetIndex
的值应该是 33
。在 Collation 数组中下标 33
对应的字符集是UTF8
,所以该下标肯定是不存在于UTF8MB4_INDEXES
集合中的。变量useutf8mb4
的值为false
,在后续调用execSQL
方法时,完整的 SQL 应该是SET NAMES utf8
。
mysql-connector-java 5.1.47
5.1.47
在源码实现上与5.1.46
有些许不同,让我们看一下它对configureClientCharacterSet
方法的改动。
private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
…
// 改动 1
// 通过 getConnectionCollation()获取 connectionCollation 属性的值,如果不为空的话则进入循环
if (!getUseOldUTF8Behavior() && !StringUtils.isNullOrEmpty(getConnectionCollation())) {
for (int i = 1; i < CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME.length; i++) {
// COLLATION_INDEX_TO_COLLATION_NAME 集合保存的是 Collation 对象中的 collationName, 例如’utf8mb4_general_ci’
if (CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[i].equals(getConnectionCollation())) {
connectionCollationSuffix = " COLLATE " + CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[i];
// 找到Collation对应的charsetName, 赋值给connectionCollationCharset
完结
Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
[外链图片转存中…(img-AK6NYlqM-1714641034176)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!