找回密码
 立即注册
搜索

[数据库] mysql中utf8和utf8mb4区别

[复制链接]
智慧谋略 发表于 2022-8-25 00:06:31 | 显示全部楼层 |阅读模式
、简介

   MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。

   二、内容描述

   那上面说了既然utf8能够存下大部分中文汉字,那为什么还要使用utf8mb4呢? 原来mysql支持的 utf8 编码最大字符长度为 3 字节,如果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等等。

   三、问题根源

   最初的 UTF-8 格式使用一至六个字节,最大能编码 31 位字符。最新的 UTF-8 规范只使用一到四个字节,最大能编码21位,正好能够表示所有的 17个 Unicode 平面。

   utf8 是 Mysql 中的一种字符集,只支持最长三个字节的 UTF-8字符,也就是 Unicode 中的基本多文本平面。

   Mysql 中的 utf8 为什么只支持持最长三个字节的 UTF-8字符呢?我想了一下,可能是因为 Mysql 刚开始开发那会,Unicode 还没有辅助平面这一说呢。那时候,Unicode 委员会还做着 “65535 个字符足够全世界用了”的美梦。Mysql 中的字符串长度算的是字符数而非字节数,对于 CHAR 数据类型来说,需要为字符串保留足够的长。当使用 utf8 字符集时,需要保留的长度就是 utf8 最长字符长度乘以字符串长度,所以这里理所当然的限制了 utf8 最大长度为 3,比如 CHAR(100)  Mysql 会保留 300字节长度。至于后续的版本为什么不对 4 字节长度的 UTF-8 字符提供支持,我想一个是为了向后兼容性的考虑,还有就是基本多文种平面之外的字符确实很少用到。

   要在 Mysql 中保存 4 字节长度的 UTF-8 字符,需要使用 utf8mb4 字符集,但只有 5.5.3 版本以后的才支持(查看版本: select version();)。我觉得,为了获取更好的兼容性,应该总是使用 utf8mb4 而非 utf8.  对于 CHAR 类型数据,utf8mb4 会多消耗一些空间,根据 Mysql 官方建议,使用 VARCHAR  替代 CHAR。

   引用批注

   [1] 谈谈字符集与字符编码

   http://my.oschina.net/leejun2005/blog/232732#OSC_h3_4

   [2] 关于 MySQL UTF8 编码下生僻字符插入失败/假死问题的分析

   http://my.oschina.net/leejun2005/blog/343353






MySQL在 5.5.3 之后增加了 utf8mb4 字符编码,mb4即 most bytes 4。简单说 utf8mb4 是 utf8 的超集并完全兼容utf8,能够用四个字节存储更多的字符。

但抛开数据库,标准的 UTF-8 字符集编码是可以用 1~4 个字节去编码21位字符,这几乎包含了是世界上所有能看见的语言了。然而在MySQL里实现的utf8最长使用3个字节,也就是只支持到了 Unicode 中的 [url=]基本多文本平面[/url](U+0000至U+FFFF),包含了控制符、拉丁文,中、日、韩等绝大多数国际字符,但并不是所有,最常见的就算现在手机端常用的表情字符 emoji和一些不常用的汉字,如 “墅” ,这些需要四个字节才能编码出来。

注:QQ里面的内置的表情不算,它是通过特殊映射到的一个gif图片。一般输入法自带的就是。

也就是当你的数据库里要求能够存入这些表情或宽字符时,可以把字段定义为 utf8mb4,同时要注意连接字符集也要设置为utf8mb4,否则在 [url=]严格模式[/url] 下会出现 Incorrect string value: /xF0/xA1/x8B/xBE/xE5/xA2… for column 'name'这样的错误,非严格模式下此后的数据会被截断。

提示:另外一种能够存储emoji的方式是,不关心数据库表字符集,只要连接字符集使用 latin1,但相信我,你绝对不想这个干,一是这种字符集混用管理极不规范,二是存储空间被放大(读者可以想下为什么)。

02

utf8mb4_unicode_ci 与 utf8mb4_general_ci 如何选择


字符除了需要存储,还需要排序或比较大小,涉及到与编码字符集对应的 排序字符集(collation)。ut8mb4对应的排序字符集常用的有 utf8mb4_unicode_ci、utf8mb4_general_ci,到底采用哪个在 stackoverflow 上有个讨论,[url=]What’s the difference between utf8_general_ci and utf8_unicode_ci[/url]

主要从排序准确性和性能两方面看:

  • 准确性
    utf8mb4_unicode_ci 是基于标准的Unicode来排序和比较,能够在各种语言之间精确排序
    utf8mb4_general_ci 没有实现Unicode排序规则,在遇到某些特殊语言或字符是,排序结果可能不是所期望的。
    但是在绝大多数情况下,这种特殊字符的顺序一定要那么精确吗。比如Unicode把?、?当成ss和OE来看;而general会把它们当成s、e,再如????ā?各自都与 A 相等。

  • 性能
    utf8mb4_general_ci 在比较和排序的时候更快
    utf8mb4_unicode_ci 在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。
    但是在绝大多数情况下,不会发生此类复杂比较。general理论上比Unicode可能快些,但相比现在的CPU来说,它远远不足以成为考虑性能的因素,索引涉及、SQL设计才是。 我个人推荐是utf8mb4_unicode_ci,将来 8.0 里也极有可能使用变为默认的规则。


这也从另一个角度告诉我们,不要可能产生乱码的字段作为主键或唯一索引。我遇到过一例,以 url 来作为唯一索引,但是它记录的有可能是乱码,导致后来想把它们修复就特别麻烦。


 楼主| 智慧谋略 发表于 2022-8-25 00:06:45 | 显示全部楼层
3.1 “伪”转换

如果你的表定义和连接字符集都是utf8,那么直接在你的表上执行

ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8mb4;

则能够该表上所有的列的character类型变成 utf8mb4,表定义的默认字符集也会修改。连接的时候需要使用set names utf8mb4便可以插入四字节字符。(如果依然使用 utf8 连接,只要不出现四字节字符则完全没问题)。

上面的 convert 有两个问题,一是它不能ONLINE,也就是执行之后全表禁止修改,有关这方面的讨论见 [url=]mysql 5.6 原生Online DDL解析[/url];二是,它可能会自动该表字段类型定义,如 [url=]VARCHAR 被转成 MEDIUMTEXT[/url],可以通过 MODIFY 指定类型为原类型。

另外 ALTER TABLE tbl_name DEFAULT CHARACTER SET utf8mb4 这样的语句就不要随便执行了,特别是当表原本不是utf8时,除非表是空的或者你确认表里只有拉丁字符,否则正常和乱的就混在一起了。

最重要的是,你连接时使用的latin1字符集写入了历史数据,表定义是latin1或utf8,不要期望通过 ALTER ... CONVERT ... 能够让你达到用utf8读取历史中文数据的目的,没卵用,老老实实做逻辑dump。所以我才叫它“伪”转换

3.2 character-set-server

一旦你决定使用utf8mb4,强烈建议你要修改服务端 character-set-server=utf8mb4,不同的语言对它的处理方法不一样,c++, php, python可以设置character-set,但java驱动依赖于 character-set-server 选项,后面有介绍。


04

key 768 long 错误

字符集从utf8转到utf8mb4之后,最容易引起的就是索引键超长的问题。

对于表行格式是 COMPACT或 REDUNDANT,InnoDB有单个索引最大字节数 768 的限制,而字段定义的是能存储的字符数,比如 VARCHAR(200) 代表能够存200个汉字,索引定义是字符集类型最大长度算的,即 utf8 maxbytes=3, utf8mb4 maxbytes=4,算下来utf8和utf8mb4两种情况的索引长度分别为600 bytes和800bytes,后者超过了768,导致出错:Error 1071: Specified key was too long; max key length is 767 bytes。

COMPRESSED和DYNAMIC格式不受限制,但也依然不建议索引太长,太浪费空间和cpu搜索资源。

如果已有定义超过这个长度的,可加上前缀索引,如果暂不能加上前缀索引(像唯一索引),可把该字段的字符集改回utf8或latin1。
但是,( 敲黑板啦,很重要 ),要防止出现 Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation '=' 错误:连接字符集使用utf8mb4,但 SELECT/UPDATE where条件有utf8类型的列,且条件右边存在不属于utf8字符,就会触发该异常。表示踩过这个坑。

再多加一个友好提示:EXPLAIN 结果里面的 key_len 指的搜索索引长度,单位是bytes,而且是以字符集支持的单字符最大字节数算的,这也是为什么 INDEX_LENGTH 膨胀厉害的一个原因。

05

C/C++ 内存空间分配问题


这是我们这边的开发遇到的一个棘手的问题。C或C++连接MySQL使用的是linux系统上的 libmysqlclient 动态库,程序获取到数据之后根据自定义的一个网络协议,按照mysql字段定义的固定字节数来传输数据。从utf8转utf8mb4之后,c++里面针对character单字符内存空间分配,从3个增加到4个,引起异常。

这个问题其实是想说明,使用utf8mb4之后,官方建议尽量用 varchar 代替 char,这样可以减少固定存储空间浪费(关于char与varchar的选择,可参考 [url=]这里[/url])。但开发设计表时 varchar 的大小不能随意加大,它虽然是变长的,但客户端在定义变量来获取数据时,是以定义的为准,而非实际长度。按需分配,避免程序使用过多的内存。

06

java驱动使用


Java语言里面所实现的UTF-8编码就是支持4字节的,所以不需要配置 mb4 这样的字眼,但如果从MySQL读写emoji,MySQL驱动版本要在 5.1.13 及以上版本,数据库连接依然是 characterEncoding=UTF-8 。

但还没完,遇到一个大坑。[url=]官方手册[/url] 里还有这么一段话:

                                       

Connector/J did not support utf8mb4 for servers 5.5.2 and newer.

Connector/J now auto-detects servers configured with character_set_server=utf8mb4 or treats the Java encoding utf-8 passed

using characterEncoding=... as utf8mb4 in the SET NAMES= calls it makes when establishing the connection. (Bug #54175)

意思是,java驱动会自动检测服务端 character_set_server 的配置,如果为utf8mb4,驱动在建立连接的时候设置 SET NAMES utf8mb4。然而其他语言没有依赖于这样的特性。

07

主从复制错误


这个问题没有遇到,只是看官方文档有提到,曾经也看到过类似的技术文章。
大概就是从库的版本比主库的版本低,导致有些字符集不支持;或者人工修改了从库上的表或字段的字符集定义,都有可能引起异常。

08

join 查询问题


这个问题是之前在姜承尧老师公众号看到的一篇文章 “MySQL表字段字符集不同导致的索引失效问题”,自己也验证了一下,的确会有问题:

        

 楼主| 智慧谋略 发表于 2022-8-25 00:06:59 | 显示全部楼层
数据库字符集选utf8,需要支持Supplementary Multilingual Plane字符的列用utf8mb4。

作者:artisan
链接:https://www.zhihu.com/question/38815156/answer/2316506766
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

是用 utf8 还是 utf8mb4?请无脑选 utf8mb4。
注意限制条件。
MySQL从4.1版本开始支持UTF-8
MySQL在 5.5.3 之后增加了 utf8mb4 字符编码
在MySQL 8.0以后默认连接字符集从latin1改成了utf8mb4,字符序从latin1_swedish_ci改成了utf8mb4_0900_ai_ci

utf8mb4_bin 二进制存储
utf8mb4_general_ci 不区分大小写,区分字母变体
utf8mb4_unicode_ci 不区分大小写,不区分字母变体(也就是搜a的时候 α和a都会返回)
utf8mb4_unicode_ci 、utf8mb4_unicode_520_ci、utf8mb4_0900_ai_ci 分别对应的是unicode组织的UCA规范
不同版本4.0.0(2004年出)、5.2.0(2009年出)、9.0.0 (2016年出)。而UCA这货最新版本已经更新到14.0.0了...
参考文章:

utf8mb4_bin、utf8mb4_unicode_ci、utf8mb4_general_ci的区别是排序和性能,对于特别看中文本内容的二进制一致性的应该用utf8mb4_bin,否则选utf8mb4_general_ci,而对于希望用英文字母模糊匹配欧洲字母的考虑用utf8mb4_unicode_ci。
中文编程环境下我们具体看下使用场景。
1.排序:MySQL下一般我们都是拿数字字段或小字符串的字段做排序,极少碰到多国语的字母序排序问题
2.搜索匹配:由于中英文是日常数据的主要来源,遇到一个搜索希腊字母α却返回匹配英文字母a的结果反而会造成较大的困扰,因此需求不大。
3.版本:主流的MySQL大版本是5.6、5.7、8.0 但由于数据库存储数据的特性,企业往往升级版本的动力不强,除非新项目可以考虑在8.0上跑。

因此使用utf8mb4基本达成共识,如果考虑兼容性建议日常表设计时默认采用utf8mb4_general_ci足矣,如果确定向前看只用8.0以上版本,可以用默认的utf8mb4_0900_ai_ci,而随着unicode的标准演变后面可能还要再改字符集配置,很头大。


 楼主| 智慧谋略 发表于 2022-8-25 00:07:12 | 显示全部楼层

简单明了!utf8和utf8mb4的区别2020-01-25 00:13:33 分类:php开发 阅读(494)




一、简介

MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。utf8mb4是utf8

的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。

二、内容描述

那上面说了既然utf8能够存下大部分中文汉字,那为什么还要使用utf8mb4呢? 原来mysql支持的 utf8 编码最大字符长度为 3 字节,如

果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种

平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji

是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等等(utf8的缺

点)。

通常,计算机在存储字符时,会根据不同类型的字符以及编码方式分配存储空间。例如以下几种编码方式;

①ASCII编码中,一个英文字母(不分大小写)占用一个字节的空间,一个中文汉字占用两个字节的空间。一个二进制的数字序列,在计算机中作为一个数字单元存储时,一般为8位二进制数,换算为十进制。最小值0,最大值255。

②UTF-8编码中,一个英文字符占用一个字节的存储空间,一个中文(含繁体)占用三个字节的存储空间。

③Unicode编码中,一个英文占用两个字节的存储空间,一个中文(含繁体)占用两个字节的存储空间。

④UTF-16编码中,一个英文字母字符或一个汉字字符存储都需要占用2个字节的存储空间(Unicode扩展区的一些汉字存储需要4个字节)。

⑤UTF-32编码中,世界上任何字符的存储都需要占用4个字节的存储空间。

既然utf8能兼容绝大部分的字符,为什么要扩展utf8mb4?

随着互联网的发展,产生了许多新类型的字符,例如emoji这种类型的符号,也就是我们通常在聊天时发的小黄脸表情,这种字符的出

现不在基本多平面的Unicode字符之中,导致无法在MySQL中使用utf8存储,MySQL于是对utf8字符进行了扩展,增加了utf8mb4这个编码。

所以,设计数据库时如果想要允许用户使用特殊符号,最好使用utf8mb4编码来存储,使得数据库有更好的兼容性,但是这样设计会

导致耗费更多的存储空间。

以上就是简单明了!utf8和utf8mb4的区别的详细内容



utf8与utf8mb4的区别
utf8字符集表示字符需要使用1~4个字节,但是我们常用的一些字符使用1~3个字节就可以表示了。而在MySQL中字符集表示一个字符所用最大字节长度在某些方面会影响系统的存储和性能,所以MySQL定义了以下两个概念:
  • utf8mb3:阉割过的 utf8 字符集,只使用1~3个字节表示字符。
  • utf8mb4:正宗的 utf8 字符集,使用1~4个字节表示字符
在MySQL中,utf8 是 utf8mb3 的别名,所以之后在MySQL中提到 utf8 就意味着使用1~3个字节来表示一个字符,但是如果有使用4个字节编码一个字符的情况,比如说存储一些emoji表情之类的,就使用 utf8mb4 。
字符集比较规则
比较规则的作用通常体现在比较字符串大小的表达式以及对某个字符串列进行排序中,所以有时候也称为排序规则
后缀英文释义描述
_aiaccent insensitive不区分重音
_asaccent sensitive区分重音
_cicase insensitive不区分大小写
_cscase sensitive区分大小写
_binbinary以二进制方式比较
比如 utf8_general_ci 是以 _ci 结尾,说明不区分大小写
所以说如果以后在对字符串作比较或者对某个字符串做排序操作时没有得到想象中的结果,需要考虑一下是不是比较规则的问题

 楼主| 智慧谋略 发表于 2022-8-25 00:07:28 | 显示全部楼层
为何不要在MySQL中使用UTF-8编码方式详解
MySQL的UTF-8编码方式
MySQL 从 4.1 版本开始支持 UTF-8,也就是 2003 年,然而目前流行的UTF-8 标准(RFC 3629)是在此之后规定的。正因此,才造就了MySQL中的UTF-8与我们日常开发中的UTF-8不一致,从到导致了些问题。MySQL的UTF-8只支持每个字符最多三个字节,而真正的 UTF-8 是每个字符最多四个字节。
问题复现
有数据库表如下:utf8编码方式
[url=]
[/url]
往数据库存一条记录:
@Testpublic void testInsert() { User user = new User(); user.setUsername("\uD83D\uDE00 "); user.setPassword("123456"); userRepo.save(user);}
这里只是部分代码,看不懂没关系,这里是给user表插入一条记录。其中username是\uD83D\uDE00 。
其实\uD83D\uDE00是一个emoji表情。
[url=]
[/url]
因为MySQL中utf8字符集只支持三字节UTF-8编码的Unicode范围,而emoji字符属于四字节编码部分,所以程序运行预期是会报错的。运行这段代码:
[url=]
[/url]
与预期一致,报错了。
解决问题
虽然MySQL的UTF-8存在缺陷,但是MySQL(包括mariadb)官方却没有修复此bug,而是通过2010年重新发布的“utf8mb4”来支持真正的 UTF-8。因此想要解决此问题,只能够将MySQL数据库设置为utf8mb4字符集才行。
总结
这个问题,也是因为某次保存数据的时候保存了一个emoji表情才发现的。其实最早开始用MySQL的时候,就有发现一个utf8mb4了,但是自己却没有却去了解UTF8和UTF8MB4的区别。通过此次教训,以后使用MySQL就老老实实的设置字符集为utf8mb4吧。
简单概括如下:
1.MySQL的“utf8mb4”是真正的“UTF-8”。
2.MySQL的“utf8”是一种“专属的编码”,它能够编码的Unicode字符并不多。
我要在这里澄清一下:所有在使用“utf8”的MySQL和MariaDB用户都应该改用“utf8mb4”,永远都不要再使用“utf8”。
那么什么是编码?什么是UTF-8?
我们都知道,计算机使用0和1来存储文本。比如字符“C”被存成“01000011”,那么计算机在显示这个字符时需要经过两个步骤:
1.计算机读取“01000011”,得到数字67,因为67被编码成“01000011”。
2.计算机在Unicode字符集中查找67,找到了“C”。
同样的:
1.我的电脑将“C”映射成Unicode字符集中的67。
2.我的电脑将67编码成“01000011”,并发送给Web服务器。
几乎所有的网络应用都使用了Unicode字符集,因为没有理由使用其他字符集。
Unicode字符集包含了上百万个字符。最简单的编码是UTF-32,每个字符使用32位。这样做最简单,因为一直以来,计算机将32位视为数字,而计算机最在行的就是处理数字。但问题是,这样太浪费空间了。
UTF-8可以节省空间,在UTF-8中,字符“C”只需要8位,一些不常用的字符,比如“”需要32位。其他的字符可能使用16位或24位。一篇类似本文这样的文章,如果使用UTF-8编码,占用的空间只有UTF-32的四分之一左右。
MySQL的“utf8”字符集与其他程序不兼容,它所谓的“”,可能真的是一坨……
MySQL简史
为什么MySQL开发者会让“utf8”失效?我们或许可以从提交日志中寻找答案。
MySQL从4.1版本开始支持UTF-8,也就是2003年,而今天使用的UTF-8标准(RFC 3629)是随后才出现的。
旧版的UTF-8标准(RFC 2279)最多支持每个字符6个字节。2002年3月28日,MySQL开发者在第一个MySQL 4.1预览版中使用了RFC 2279。
同年9月,他们对MySQL源代码进行了一次调整:“UTF8现在最多只支持3个字节的序列”。
是谁提交了这些代码?他为什么要这样做?这个问题不得而知。在迁移到Git后(MySQL最开始使用的是BitKeeper),MySQL代码库中的很多提交者的名字都丢失了。2003年9月的邮件列表中也找不到可以解释这一变更的线索。
不过我可以试着猜测一下。
2002年,MySQL做出了一个决定:如果用户可以保证数据表的每一行都使用相同的字节数,那么MySQL就可以在性能方面来一个大提升。为此,用户需要将文本列定义为“CHAR”,每个“CHAR”列总是拥有相同数量的字符。如果插入的字符少于定义的数量,MySQL就会在后面填充空格,如果插入的字符超过了定义的数量,后面超出部分会被截断。
MySQL开发者在最开始尝试UTF-8时使用了每个字符6个字节,CHAR(1)使用6个字节,CHAR(2)使用12个字节,并以此类推。
应该说,他们最初的行为才是正确的,可惜这一版本一直没有发布。但是文档上却这么写了,而且广为流传,所有了解UTF-8的人都认同文档里写的东西。
不过很显然,MySQL开发者或厂商担心会有用户做这两件事:
1.使用CHAR定义列(在现在看来,CHAR已经是老古董了,但在那时,在MySQL中使用CHAR会更快,不过从2005年以后就不是这样子了)。
2.将CHAR列的编码设置为“utf8”。
我的猜测是MySQL开发者本来想帮助那些希望在空间和速度上双赢的用户,但他们搞砸了“utf8”编码。
所以结果就是没有赢家。那些希望在空间和速度上双赢的用户,当他们在使用“utf8”的CHAR列时,实际上使用的空间比预期的更大,速度也比预期的慢。而想要正确性的用户,当他们使用“utf8”编码时,却无法保存像“”这样的字符。
在这个不合法的字符集发布了之后,MySQL就无法修复它,因为这样需要要求所有用户重新构建他们的数据库。最终,MySQL在2010年重新发布了“utf8mb4”来支持真正的UTF-8。
为什么这件事情会让人如此抓狂
因为这个问题,我整整抓狂了一个礼拜。我被“utf8”愚弄了,花了很多时间才找到这个bug。但我一定不是唯一的一个,网络上几乎所有的文章都把“utf8”当成是真正的UTF-8。
“utf8”只能算是个专有的字符集,它给我们带来了新问题,却一直没有得到解决。
总结
如果你在使用MySQL或MariaDB,不要用“utf8”编码,改用“utf8mb4”。这里( https://mathiasbynens.be/notes/mysql-utf8mb4#utf8-to-utf8mb4 )提供了一个指南用于将现有数据库的字符编码从“utf8”转成“utf8mb4”。


高级模式
B Color Image Link Quote Code Smilies

本版积分规则

Archiver|手机版|小黑屋|探索掌握未知、共创美好未来

GMT+8, 2026-9-17 02:31 , Processed in 0.278205 second(s), 40 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表