<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) --> <tx:adviceid="txAdvice"transaction-manager="txManager"> <!-- the transactional semantics... --> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:methodname="get*"read-only="true"/> <!-- other methods use the default transaction settings (see below) --> <tx:methodname="*"/> </tx:attributes> </tx:advice>
<!-- ensure that the above transactional advice runs for any execution of an operation defined by the FooService interface --> <aop:config> <aop:pointcutid="fooServiceOperation"expression="execution(* x.y.service.FooService.*(..))"/> <aop:advisoradvice-ref="txAdvice"pointcut-ref="fooServiceOperation"/> </aop:config>
......
事务配置可通过修改 <tx:method/> 的属性,详见脑图。
基于注解方式配置事务管理
除了使用基于 XML 的方式(<tx:advice/>)声明事务配置之外,您还可以使用基于注解的方式(@Transactional )。直接在 Java 源代码中声明事务语义会使声明更靠近受影响的代码,易于配置和修改。这样之所以不存在过度耦合的原因是因为,无论如何,用于事务处理的代码几乎总是以事务的方式进行部署。
The default advice mode for processing @Transactional annotations is proxy, which allows for interception of calls through the proxy only. Local calls within the same class cannot get intercepted that way. For a more advanced mode of interception, consider switching to aspectj mode in combination with compile-time or load-time weaving.
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object calling another method of the target object) does not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.
The proxy-target-class attribute controls what type of transactional proxies are created for classes annotated with the @Transactional annotation. If proxy-target-class is set to true, class-based proxies are created. Ifproxy-target-class is false or if the attribute is omitted, standard JDK interface-based proxies are created. (See [aop-proxying] for a discussion of the different proxy types.)
The Spring team recommends that you annotate only concrete classes (and methods of concrete classes) with the @Transactional annotation, as opposed to annotating interfaces. You certainly can place the @Transactional annotation on an interface (or an interface method), but this works only as you would expect it to if you use interface-based proxies. The fact that Java annotations are not inherited from interfaces means that, if you use class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"), the transaction settings are not recognized by the proxying and weaving infrastructure, and the object is not wrapped in a transactional proxy.
When you use proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. If you need to annotate non-public methods, consider using AspectJ.
Spring 的 JDBC 框架承担了资源管理和异常处理的工作,从而简化了底层 JDBC API 代码,让我们只需编写从数据库读写数据所需的代码。具体特性如下:
Spring 为读取和写入数据库的几乎所有错误提供了丰富的异常,且不与特定的持久化框架相关联(如下图)。异常都继承自的父类 DataAccessException,是一个非受检异常,无需捕获,因为 Spring 认为触发异常的很多问题是不能在 catch 代码块中修复,因此不强制开发人员编写 catch 代码块。这把是否要捕获异常的权利留给了开发人员。
Spring 将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template) 和 回调(callback)。模板管理过程中固定的部分(如事务控制、资源管理、异常处理),而回调处理自定义的数据访问代码(如 SQL 语句、绑定参数、整理结果集)。针对不同的持久化平台,Spring 提供了多个可选的模板:
依赖安装
要在 Spring 中使用 JDBC,需要依赖 spring-jdbc。如果使用 Spring Boot 的话,可以直接导入起步依赖 spring-boot-starter-jdbc:
1 2 3 4 5 6 7 8 9 10 11
<!-- Spring JDBC 起步依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
<!-- MySQL JDBC 驱动程序 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
List<TestPO> testPOList = jdbcOperations.query( "SELECT id, name, city FROM test WHERE name = ? AND city = ?", (rs, rowNum) -> newTestPO( rs.getLong("id"), rs.getString("name"), rs.getString("city") ), "李四", "beijing" ); log.info("Result is {}", testPOList); // Result is [TestPO(id=2, name=李四, city=beijing)]
TestPOtestPO= jdbcOperations.queryForObject( "SELECT id, name, city FROM test WHERE id = ?", (rs, rowNum) -> newTestPO( rs.getLong("id"), rs.getString("name"), rs.getString("city") ), 2 ); log.info("Result is {}", testPO); // Result is TestPO(id=2, name=李四, city=beijing)
Stringname= jdbcOperations.queryForObject("SELECT name FROM test WHERE id = ?", String.class, 2); log.info("Result is {}", name); // Result is 李四
List<String> names = jdbcOperations.queryForList("SELECT name FROM test WHERE city = ?", String.class, "beijing"); log.info("Result is {}", names); // Result is [李四, 王五]
List<Map<String, Object>> testMapList = jdbcOperations.queryForList("SELECT id, name, city FROM test WHERE city = ?", "beijing"); log.info("Result is {}", testMapList); // Result is [{id=2, name=李四, city=beijing}, {id=3, name=王五, city=beijing}]
Map<String, Object> testMap = jdbcOperations.queryForMap("SELECT id, name, city FROM test WHERE id = ?", 2); log.info("Result is {}", testMap); // Result is {id=2, name=李四, city=beijing}
List<TestPO> testPOList = namedParameterJdbcOperations.query( "SELECT id, name, city FROM test WHERE name = :name AND city = :city", paramMap, (rs, rowNum) -> newTestPO( rs.getLong("id"), rs.getString("name"), rs.getString("city") ) );
log.info("Result is {}", testPOList); // Result is [TestPO(id=2, name=李四, city=beijing)]
// ... will convert from the SQL type of the column to the requested Java data type, if the conversion is supported. If the conversion is not supported or null is specified for the type, a `SQLException` is thrown. <T> T getObject(int columnIndex, Class<T> type)throws SQLException; <T> T getObject(String columnLabel, Class<T> type)throws SQLException;
⚠️ 特别注意:对于最后一组动态数据访问方法,参数二 type 的值要与 ResultSetMetaData.GetColumnClassName() 返回的类型相匹配,类型转换才能成功。否则抛出异常如下:
Some databases need to know the value’s type even if the value itself is NULL. For this reason, for maximum portability, it’s the JDBC specification itself that requires the java.sql.Types to be specified:
PreparedStatement 接口:
1 2 3
// Sets the designated parameter to SQL NULL. voidsetNull(int parameterIndex, int sqlType)throws SQLException; voidsetNull(int parameterIndex, int sqlType, String typeName)throws SQLException;
PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)throws SQLException;
autoGeneratedKeys - a flag indicating whether auto-generated keys should be returned; one of Statement.RETURN_GENERATED_KEYS or Statement.NO_GENERATED_KEYS
1 2 3 4 5 6 7 8 9 10 11 12 13
/** * The constant indicating that generated keys should be made available for retrieval. * * @since 1.4 */ intRETURN_GENERATED_KEYS=1;
/** * The constant indicating that generated keys should not be made available for retrieval. * * @since 1.4 */ intNO_GENERATED_KEYS=2;
// 调用 getGeneratedKeys ,然后又会获取到一个 ResultSet 对象,从这个游标中就可以获取到刚刚插入数据的 id ResultSetrs= ps.getGeneratedKeys(); intid=0; if (rs.next()) { id = rs.getInt(1); } return id;
批处理
PreparedStatement 还提供了批处理方式,减少网络请求,提升性能,API 如下:
1 2 3 4 5 6 7 8 9 10
/** * Adds a set of parameters to this <code>PreparedStatement</code> * object's batch of commands. * * @exception SQLException if a database access error occurs or * this method is called on a closed <code>PreparedStatement</code> * @see Statement#addBatch * @since 1.2 */ voidaddBatch()throws SQLException;
未使用批处理方法:
1 2 3 4 5 6 7 8 9
PreparedStatementps= conn.prepareStatement("INSERT into employees values (?, ?, ?)");
for (n = 0; n < 100; n++) { ps.setString(name[n]); ps.setLong(id[n]); ps.setInt(salary[n]); // 多次执行PreparedStatement,多次数据库请求(网络请求) ps.executeUpdate(); }
使用批处理方法,一次性执行多条 SQL:
1 2 3 4 5 6 7 8 9 10 11
PreparedStatementps= conn.prepareStatement("INSERT into employees values (?, ?, ?)"); for (n = 0; n < 100; n++) { ps.setString(name[n]); ps.setLong(id[n]); ps.setInt(salary[n]); // 添加批次 ps.addBatch(); } // 调用父接口 Statement#executeBatch() 执行批次 ps.executeBatch();
JDBC 4.1 (Java SE 7) introduces the ability to use a try-with-resources statement to automatically close java.sql.Connection, java.sql.Statement, and java.sql.ResultSet objects, regardless of whether a SQLException or any other exception has been thrown. See The try-with-resources Statement for more information.
在 JDBC 之前,ODBC API 是用于连接和执行命令的数据库 API 标准。但是,ODBC API 是使用 C 语言编写的驱动程序,依赖于平台。这就是为什么 Java 定义了自己的 JDBC API,它使用的 JDBC 驱动程序,是用 Java 语言编写的,具有与平台无关的特性,支持跨平台部署,性能也较好。
什么是 JDBC 驱动程序?
由于 JDBC API 只是一套接口规范,因此要使用 JDBC API 操作数据库,首先需要选择合适的驱动程序:
驱动程序四种类型
有四种类型的 JDBC 驱动程序:
JDBC-ODBC bridge driver (In Java 8, the JDBC-ODBC Bridge has been removed.)
因为ODBC 不适合直接在 Java 中使用,因为它使用 C 语言接口。从Java 调用本地 C代码在安全性、实现、坚固性和程序的自动移植性方面都有许多缺点。从 ODBC C API 到 Java API 的字面翻译是不可取的。例如,Java 没有指针,而 ODBC 却对指针用得很广泛(包括很容易出错的指针”void *”)。
javax.net.ssl.SSLException MESSAGE: closing inbound before receiving peer's close_notify
STACKTRACE:
javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify at sun.security.ssl.Alert.createSSLException(Alert.java:133) at sun.security.ssl.Alert.createSSLException(Alert.java:117) at sun.security.ssl.TransportContext.fatal(TransportContext.java:340) at sun.security.ssl.TransportContext.fatal(TransportContext.java:296) at sun.security.ssl.TransportContext.fatal(TransportContext.java:287) at sun.security.ssl.SSLSocketImpl.shutdownInput(SSLSocketImpl.java:737) at sun.security.ssl.SSLSocketImpl.shutdownInput(SSLSocketImpl.java:716) at com.mysql.cj.protocol.a.NativeProtocol.quit(NativeProtocol.java:1319) at com.mysql.cj.NativeSession.quit(NativeSession.java:182) at com.mysql.cj.jdbc.ConnectionImpl.realClose(ConnectionImpl.java:1750) at com.mysql.cj.jdbc.ConnectionImpl.close(ConnectionImpl.java:720) at com.zaxxer.hikari.pool.PoolBase.quietlyCloseConnection(PoolBase.java:135) at com.zaxxer.hikari.pool.HikariPool.lambda$closeConnection$1(HikariPool.java:441) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
Use server-side prepared statements if the server supports them?
MySQL 是否默认开启预编译,与 MySQL Server 的版本无关,而与 MySQL Connector/J(驱动程序)的版本有关,Connector/J 5.0.5 之前的版本默认开启预编译。Connector/J 5.0.5 及以后的版本默认不开启预编译,想启用 MySQL 预编译,就必须设置 useServerPrepStmts=true。
Allow the use of ; to delimit multiple queries during one statement (true/false). Default is false, and it does not affect the addBatch() and executeBatch() methods, which rely on rewriteBatchedStatements instead.
Should the driver use multi-queries (regardless of the setting of allowMultiQueries) as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called?
Notice that this has the potential for SQL injection if using plain java.sql.Statement and your code doesn’t sanitize input correctly.
Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don’t specify stream lengths when using PreparedStatement.set*Stream(), the driver won’t be able to determine the optimum number of parameters per batch and you might receive an error from the driver that the resultant packet is too large.
Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements.
Please be aware using rewriteBatchedStatements=true with INSERT .. ON DUPLICATE KEY UPDATE that for rewritten statement server returns only one value as sum of all affected (or found) rows in batch and it isn’t possible to map it correctly to initial statements; in this case driver returns 0 as a result of each batch statement if total count was 0, and the Statement.SUCCESS_NO_INFO as a result of each batch statement if total count was > 0.
在 MySQL 中,rewriteBatchedStatements 的默认值取决于 MySQL 版本和 JDBC 驱动程序的配置。
在 MySQL 8.0 版本之前,rewriteBatchedStatements 的默认值是 false,这意味着批处理语句不会被重写优化。
从 MySQL 8.0 版本开始,rewriteBatchedStatements 的默认值被更改为 true,以提高默认情况下的性能。
Maximum allowed packet size to send to server. If not set, the value of system variable max_allowed_packet will be used to initialize this upon connecting. This value will not take effect if set larger than the value of max_allowed_packet. Also, due to an internal dependency with the property “blobSendChunkSize“, this setting has a minimum value of “8203” if “useServerPrepStmts“ is set to “true“.
publicclassDriverManager { /** * Load the initial JDBC drivers by checking the System property * jdbc.properties and then use the {@code ServiceLoader} mechanism */ static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }
privatestaticvoidloadInitialDrivers() { String drivers; try { drivers = AccessController.doPrivileged(newPrivilegedAction<String>() { public String run() { return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } // If the driver is packaged as a Service Provider, load it. // Get all the drivers through the classloader // exposed as a java.sql.Driver.class service. // ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(newPrivilegedAction<Void>() { public Void run() {
/* Load these drivers, so that they can be instantiated. * It may be the case that the driver class may not be there * i.e. there may be a packaged driver with the service class * as implementation of java.sql.Driver but the actual class * may be missing. In that case a java.util.ServiceConfigurationError * will be thrown at runtime by the VM trying to locate * and load the service. * * Adding a try catch block to catch those runtime errors * if driver not available in classpath but it's * packaged as service and that service is there in classpath. */ try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { // Do nothing } returnnull; } });
@Override public java.sql.Connection connect(String url, Properties info)throws SQLException {
try { if (!ConnectionUrl.acceptsUrl(url)) { /* * According to JDBC spec: * The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. This will be common, as when the * JDBC driver manager is asked to connect to a given URL it passes the URL to each loaded driver in turn. */ returnnull; }
ConnectionUrlconStr= ConnectionUrl.getConnectionUrlInstance(url, info); switch (conStr.getType()) { case SINGLE_CONNECTION: return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost());
case LOADBALANCE_CONNECTION: return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl) conStr);
case FAILOVER_CONNECTION: return FailoverConnectionProxy.createProxyInstance(conStr);
case REPLICATION_CONNECTION: return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl) conStr);
default: returnnull; }
} catch (UnsupportedConnectionStringException e) { // when Connector/J can't handle this connection string the Driver must return null returnnull;
/** * The database URL type which is determined by the scheme section of the connection string. */ publicenumType { SINGLE_CONNECTION("jdbc:mysql:", HostsCardinality.SINGLE), // FAILOVER_CONNECTION("jdbc:mysql:", HostsCardinality.MULTIPLE), // LOADBALANCE_CONNECTION("jdbc:mysql:loadbalance:", HostsCardinality.ONE_OR_MORE), // REPLICATION_CONNECTION("jdbc:mysql:replication:", HostsCardinality.ONE_OR_MORE), // XDEVAPI_SESSION("mysqlx:", HostsCardinality.ONE_OR_MORE);
If the fsp argument is given to specify a fractional seconds precision from 0 to 6, the return value includes a fractional seconds part of that many digits.
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.) By default, the current time zone for each connection is the server’s time. The time zone can be set on a per-connection basis. As long as the time zone setting remains constant, you get back the same value you store. If you store a TIMESTAMP value, and then change the time zone and retrieve the value, the retrieved value is different from the value you stored. This occurs because the same time zone was not used for conversion in both directions. The current time zone is available as the value of the time_zone system variable. For more information, see Section 5.1.13, “MySQL Server Time Zone Support”.
Returns a representation of unix_timestamp as a DATETIME or VARCHAR value. The value returned is expressed using the session time zone. (Clients can set the session time zone as described in Section 5.1.13, “MySQL Server Time Zone Support”.) unix_timestamp is an internal timestamp value representing seconds since '1970-01-01 00:00:00' UTC, such as produced by the UNIX_TIMESTAMP() function.
unix_timestamp
When unix_timestamp is an integer, the fractional seconds precision of the DATETIME is 0.
When unix_timestamp is a decimal value, the fractional seconds precision of the DATETIME is the same as the precision of the decimal value, up to a maximum of 6.
When unix_timestamp is a floating point number, the fractional seconds precision of the datetime is 6.
format
If format is omitted, the value returned is a DATETIME.
If format is supplied, the value returned is a VARCHAR.
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-- 不指定 format 格式,返回 DATETIME 类型 SELECT FROM_UNIXTIME(1447430881); ->'2015-11-13 10:08:01'
If no date argument, it returns a Unix timestamp representing seconds since '1970-01-01 00:00:00' UTC.
If with a date argument, it returns the value of the argument as seconds since '1970-01-01 00:00:00' UTC.
The server interprets date as a value in the session time zone and converts it to an internal Unix timestamp value in UTC. (Clients can set the session time zone as described in Section 5.1.13, “MySQL Server Time Zone Support”.)
or a number in YYMMDD, YYMMDDhhmmss, YYYYMMDD, or YYYYMMDDhhmmss format.
If the argument includes a time part, it may optionally include a fractional seconds part.
The return value is
an integer if no argument is given or the argument does not include a fractional seconds part,
or DECIMAL if an argument is given that includes a fractional seconds part.
When the date argument is a TIMESTAMP column, UNIX_TIMESTAMP() returns the internal timestamp value directly, with no implicit “string-to-Unix-timestamp” conversion.
The valid range of argument values is the same as for the TIMESTAMP data type: '1970-01-01 00:00:01.000000' UTC to '2038-01-19 03:14:07.999999' UTC. If you pass an out-of-range date to UNIX_TIMESTAMP(), it returns 0.
This is the inverse of the DATE_FORMAT() function. It takes a string str and a format string format. STR_TO_DATE() returns a DATETIME value if the format string contains both date and time parts, or a DATE or TIME value if the string contains only date or time parts. If the date, time, or datetime value extracted from str is illegal, STR_TO_DATE() returns NULL and produces a warning.
MySQL 支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要。不管存储哪种类型的数据,下面几个简单的原则都有助于作出更好的选择。
Smaller is usually better
更小的通常更好:
In general, try to use the smallest data type that can correctly store and represent your data. Smaller data types are usually faster, because they use less space on the disk, in memory, and in the CPU cache. They also generally require fewer CPU cycles to process.
Make sure you don’t underestimate the range of values you need to store, though, because increasing the data type range in multiple places in your schema can be a painful and time-consuming operation. If you’re in doubt as to which is the best data type to use, choose the smallest one that you don’t think you’ll exceed. (If the system is not very busy or doesn’t store much data, or if you’re at an early phase in the design process, you can change it easily later.)
更小的数据类型通常更快,因为占用更少的磁盘、内存和 CPU 缓存,并且处理时需要的 CPU 周期也更少。
Simple is good
简单就好:
Fewer CPU cycles are typically required to process operations on simpler data types. For example, integers are cheaper to compare than characters, because character sets and collations (sorting rules) make character comparisons complicated. Here are two examples: you should store dates and times in MySQL’s builtin types instead of as strings, and you should use integers for IP addresses. We discuss these topics further later.
简单数据类型的操作通常需要更少的 CPU 周期、索引性能更好。例如,整型比字符操作代价更低,因为字符集和校对规则(排序规则)使得字符比整型更复杂。
-- 测试结果 -- INET_ATON() Return the numeric value of an IP address -- INET_NTOA() Return the IP address from a numeric value -- INET6_ATON() Return the numeric value of an IPv6 address -- INET6_NTOA() Return the IPv6 address from a numeric value SELECT INET_NTOA(ip1), ip2 FROM t_iptable; +-----------------+-----------------+ | INET_NTOA(ip1) | ip2 | +-----------------+-----------------+ |0.0.0.0|0.0.0.0| |10.198.1.1|10.198.1.1| |255.255.255.255|255.255.255.255| +-----------------+-----------------+
-- 测试结果 -- BIT_LENGTH() Return length of a string in bits -- LENGTH() Return length of a string in bytes SELECT HEX(hash1), BIT_LENGTH(hash1), LENGTH(hash1), hash2, BIT_LENGTH(hash2), LENGTH(hash2) FROM t_hash; +------------------------------------------+-------------------+---------------+------------------------------------------+-------------------+---------------+ | HEX(hash1) | BIT_LENGTH(hash1) | LENGTH(hash1) | hash2 | BIT_LENGTH(hash2) | LENGTH(hash2) | +------------------------------------------+-------------------+---------------+------------------------------------------+-------------------+---------------+ | E562F69EC36E625116376F376D991E41613E9BF3 |160|20| E562F69EC36E625116376F376D991E41613E9BF3 |320|40| +------------------------------------------+-------------------+---------------+------------------------------------------+-------------------+---------------+
Avoid NULL if possible
避免使用 NULL:
A lot of tables include nullable columns even when the application does not need to store NULL (the absence of a value), merely because it’s the default. It’s usually best to specify columns as NOT NULL unless you intend to store NULL in them.
It’s harder for MySQL to optimize queries that refer to nullable columns, because they make indexes, index statistics, and value comparisons more complicated. A nullable column uses more storage space and requires special processing inside MySQL. When a nullable column is indexed, it requires an extra byte per entry and can even cause a fixed-size index (such as an index on a single integer column) to be converted to a variable-sized one in MyISAM.
The performance improvement from changing NULL columns to NOT NULL is usually small, so don’t make it a priority to find and change them on an existing schema unless you know they are causing problems. However, if you’re planning to index columns, avoid making them nullable if possible.
There are exceptions, of course. For example, it’s worth mentioning that InnoDB stores NULL with a single bit, so it can be pretty space-efficient for sparsely populated data. This doesn’t apply to MyISAM, though.
NULL 列使得 MySQL 索引、索引统计和值比较都更复杂。值可为 NULL 的列会使用更多的存储空间(例如当可为 NULL 的列被索引时,每个索引记录需要一个额外的字节),在 MySQL 里也需要特殊处理。如果计划在列上建索引,应该尽量避免。
bytes for binary string types (BINARY(M)、VARBINARY(M))
characters for nonbinary string types (CHAR(M)、VARCHAR(M))
L represents the actual length in bytes of a given string value.
Binary Strings (Byte Strings)
Nonbinary Strings (Character Strings)
Storage Required
Fixed-length types
CHAR(M)
L = M × w bytes, 0 < M <= 255, where w is the number of bytes required for the maximum-length character in the character set.
Fixed-length types
BINARY(M)
M bytes, 0 <= M <= 255
Variable-length types
VARBINARY(M)
VARCHAR(M)
L = M × w bytes + 1 bytes if column values require 0 − 255 bytes L = M × w bytes + 2 bytes if values may require more than 255 bytes 其有效最大字节长度取决于行大小限制(默认 65,535 Bytes,在所有列中共享) ,参考:《表列数量和行数限制》。
Variable-length types
TINYBLOB
TINYTEXT
L + 1 bytes, where L < 2^8 = 256 bytes
Variable-length types
BLOB
TEXT
L + 2 bytes, where L < 2^16 = 64 KB
Variable-length types
MEDIUMBLOB
MEDIUMTEXT
L + 3 bytes, where L < 2^24 = 16 MB
Variable-length types
LONGBLOB
LONGTEXT
L + 4 bytes, where L < 2^32 = 4 GB
variable-length types 变长类型需要额外的 1 到 4 个字节记录长度:
1 Byte = 8 bits 刚好可以记录 0~2^8-1 (255) bytes
2 Bytes = 16 bits 刚好可以记录 0~2^16-1 (65,535) bytes
3 Bytes = 24 bits 刚好可以记录 0~2^24 bytes
4 Bytes = 32 bits 刚好可以记录 0~2^32 bytes
Description
Binary Strings (Byte Strings)
They have the binary character set and collation, and comparison and sorting are based on the numeric values of the bytes in the values.
Nonbinary Strings (Character Strings)
They have a character set other than binary, and values are sorted and compared based on the collation of the character set.
对于 Nonbinary Strings (Character Strings),M 和 L 换算关系如下:
The BINARY and VARBINARY types are similar to CHAR and VARCHAR, except that they store binary strings rather than nonbinary strings. That is, they store byte strings rather than character strings. This means they have the binary character set and collation, and comparison and sorting are based on the numeric values of the bytes in the values.
Hexadecimal literal values are written using X'val' or 0xval notation, where val contains hexadecimal digits (0..9, A..F). Lettercase of the digits and of any leading X does not matter. A leading 0x is case-sensitive and cannot be written as 0X.
Legal hexadecimal literals:
1 2 3 4 5 6
X'01AF' X'01af' x'01AF' x'01af' 0x01AF 0x01af
By default, a hexadecimal literal is a binary string, where each pair of hexadecimal digits represents a character:
1 2 3 4 5 6 7 8 9
-- BIT_LENGTH() Return length of a string in bits -- LENGTH() Return length of a string in bytes -- CHARSET() Returns the character set of the string argument. SELECT0x41, X'41', UNHEX('41'), BIT_LENGTH(0x41), LENGTH(0x41), CHARSET(0x41); +------+-------+-------------+------------------+--------------+---------------+ |0x41| X'41'| UNHEX('41') | BIT_LENGTH(0x41) | LENGTH(0x41) | CHARSET(0x41) | +------+-------+-------------+------------------+--------------+---------------+ | A | A | A |8|1|binary| +------+-------+-------------+------------------+--------------+---------------+
For a string argument str, UNHEX(str) interprets each pair of characters in the argument as a hexadecimal number and converts it to the byte represented by the number. The return value is a binary string.
A timestamp. The range is '1970-01-01 00:00:01.000000' UTC to '2038-01-19 03:14:07.999999' UTC. TIMESTAMP values are stored as the number of seconds since the epoch ('1970-01-01 00:00:00' UTC). A TIMESTAMP cannot represent the value '1970-01-01 00:00:00' because that is equivalent to 0 seconds from the epoch and the value 0 is reserved for representing '0000-00-00 00:00:00', the “zero” TIMESTAMP value.
An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value of 0 signifies that there is no fractional part. If omitted, the default precision is 0.
…
TIMESTAMP 类型的范围如下:
时间戳
二进制字面量
时间
0
00000000 00000000 00000000 00000000
0000-00-00 00:00:00 UTC
1
00000000 00000000 00000000 00000001
1970-01-01 00:00:01 UTC
2^31-1, 2147483647
01111111 11111111 11111111 11111111
2038-01-19 03:14:07 UTC
TIMESTAMP 类型的时区处理:
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.) By default, the current time zone for each connection is the server’s time. The time zone can be set on a per-connection basis. As long as the time zone setting remains constant, you get back the same value you store. If you store a TIMESTAMP value, and then change the time zone and retrieve the value, the retrieved value is different from the value you stored. This occurs because the same time zone was not used for conversion in both directions. The current time zone is available as the value of the time_zone system variable. For more information, see Section 5.1.13, “MySQL Server Time Zone Support”.
A date and time combination. The supported range is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999'. MySQL displays DATETIME values in YYYY-MM-DD hh:mm:ss[.fraction] format, but permits assignment of values to DATETIME columns using either strings or numbers.
An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value of 0 signifies that there is no fractional part. If omitted, the default precision is 0.
A DATETIME or TIMESTAMP value can include a trailing fractional seconds part in up to microseconds (6 digits) precision. In particular, any fractional part in a value inserted into a DATETIME or TIMESTAMP column is stored rather than discarded. With the fractional part included, the format for these values is 'YYYY-MM-DD hh:mm:ss[.fraction]', the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999', and the range for TIMESTAMP values is '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.999999'. The fractional part should always be separated from the rest of the time by a decimal point; no other fractional seconds delimiter is recognized. For information about fractional seconds support in MySQL, see Section 11.2.7, “Fractional Seconds in Time Values”.
Invalid DATE, DATETIME, or TIMESTAMP values are converted to the “zero” value of the appropriate type ('0000-00-00' or '0000-00-00 00:00:00'), if the SQL mode permits this conversion. The precise behavior depends on which if any of strict SQL mode and the NO_ZERO_DATE SQL mode are enabled; see Section 5.1.10, “Server SQL Modes”.
对于 MyISAM,Data_length 是数据文件的长度(以字节为单位)。 对于 InnoDB,Data_length 是聚簇索引 clustered index 大约分配的内存量(以字节为单位)。
Index_length
对于 MyISAM,Index_length 是索引文件的长度(以字节为单位)。 对于 InnoDB,Index_length 是非聚簇索引 non-clustered index 大约分配的内存量(以字节为单位)。
Auto_increment
下一个 AUTO_INCREMENT 值。
表结构
查看列名(三者等价):
1 2 3
$ show columns from table_name [from db_name]; $ show columns from [db_name.]table_name; $ desc table_name; // 简写形式
索引
1
$ show index from table_name;
建表语句
1
$ show create table table_name;
查看用户权限
显示一个用户的权限,显示结果类似于 GRANT 命令:
1 2 3 4 5 6 7
$ show grants [for user_name@'192.168.0.%'];
+---------------------------------------------------------------------------------------------+ | Grants for user_name@192.168.0.% | +---------------------------------------------------------------------------------------------+ | GRANT SELECT, INSERT, UPDATE, DELETE ON `db_name`.`table_name` TO 'user_name'@'192.168.0.%' | +---------------------------------------------------------------------------------------------+
// 查看当前 DB 进程 $ show processlist; $ show full processlist; +----------+-----------+--------------------+---------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----------+-----------+--------------------+---------+---------+------+-------+------------------+ | 33702451 | user_name | 192.168.0.200:49764 | db_name | Query | 0 | init | show processlist | +----------+-----------+--------------------+---------+---------+------+-------+------------------+