intws= node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0);
Nodes= node.next; if (s == null || s.waitStatus > 0) { s = null; for (Nodet= tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }
能够响应中断。synchronized 的问题是,持有锁 A 后,如果尝试获取锁 B 失败,那么线程就进入阻塞状态,一旦发生死锁,就没有任何机会来唤醒阻塞的线程。但如果阻塞状态的线程能够响应中断信号,也就是说当我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁 A。这样就破坏了不可抢占条件了。
Unsafe 类提供了 compareAndSwapObject、compareAndSwapInt、compareAndSwapLong方法来实现的对 Object、int、long 类型的 CAS 操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * 以原子方式更新对象字段的值。 */ booleancompareAndSwapObject(Object o, long offset, Object expected, Object x);
/** * 以原子方式更新 int 类型的对象字段的值。 */ booleancompareAndSwapInt(Object o, long offset, int expected, int x);
/** * 以原子方式更新 long 类型的对象字段的值。 */ booleancompareAndSwapLong(Object o, long offset, long expected, long x);
Unsafe 类中的 CAS 方法是 native 方法。native 关键字表明这些方法是用本地代码(通常是 C 或 C++)实现的,而不是用 Java 实现的。这些方法直接调用底层的、具有原子性的 CPU 指令来实现。
由于 CAS 操作可能会因为并发冲突而失败,因此通常会伴随着自旋,而所谓自旋,其实就是循环尝试。
Unsafe#getAndAddInt 源码:
1 2 3 4 5 6 7 8 9 10
// 原子地获取并增加整数值 publicfinalintgetAndAddInt(Object o, long offset, int delta) { int v; do { // 以 volatile 方式获取对象 o 在内存偏移量 offset 处的整数值 v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, v + delta)); // 返回旧值 return v; }
CAS 的问题
一般情况下,CAS 比锁性能更高。因为 CAS 是一种非阻塞算法,所以其避免了线程阻塞和唤醒的等待时间。但是,事物总会有利有弊,CAS 也存在三大问题:
ABA 问题
循环时间长开销大
只能保证一个共享变量的原子性
ABA 问题
如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过,这就是 ABA 问题。
ABA 问题的解决思路是在变量前面追加上版本号或者时间戳。J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。
staticclassThreadLocalMap { // ... staticclassEntryextendsWeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value;
Semaphore 是共享锁的一种实现,它默认构造 AQS 的 state 值为 permits,你可以将 permits 的值理解为许可证的数量,只有拿到许可证的线程才能执行。
调用semaphore.acquire() ,线程尝试获取许可证,如果 state >= 0 的话,则表示可以获取成功。如果获取成功的话,使用 CAS 操作去修改 state 的值 state=state-1。如果 state<0 的话,则表示许可证数量不足。此时会创建一个 Node 节点加入阻塞队列,挂起当前线程。
调用semaphore.release(); ,线程尝试释放许可证,并使用 CAS 操作去修改 state 的值 state=state+1。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 state 的值 state=state-1 ,如果 state>=0 则获取令牌成功,否则重新进入阻塞队列,挂起线程。
publicinterfaceCallable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call()throws Exception; }
而对于Thread.suspend 和 Thread.resume 而言,它们的问题在于:如果线程调用 Thread.suspend,它并不会释放锁,就开始进入休眠,但此时有可能仍持有锁,这样就容易导致死锁问题。因为这把锁在线程被 Thread.resume 之前,是不会被释放的。假设线程 A 调用了 Thread.suspend 方法让线程 B 挂起,线程 B 进入休眠,而线程 B 又刚好持有一把锁,此时假设线程 A 想访问线程 B 持有的锁,但由于线程 B 并没有释放锁就进入休眠了,所以对于线程 A 而言,此时拿不到锁,也会陷入阻塞,那么线程 A 和线程 B 就都无法继续向下执行。
程序一般都是 CPU 计算和 I/O 操作交叉执行的,由于 I/O 设备的速度相对于 CPU 来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算;和 I/O 密集型计算相对的就是 CPU 密集型计算了,CPU 密集型计算大部分场景下都是纯 CPU 计算。I/O 密集型程序和 CPU 密集型程序,计算最佳线程数的方法是不同的。
对于 CPU 密集型的计算场景,理论上“线程的数量=CPU 核数”就是最合适的。不过在工程上,线程的数量一般会设置为“CPU 核数+1”,这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证 CPU 的利用率。
对于 I/O 密集型计算场景,最佳的线程数是与程序中 CPU 计算和 I/O 操作的耗时比相关的,我们可以总结出这样一个公式:
一般多线程执行的任务类型可以分为 CPU 密集型和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。
CPU 密集型任务:这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
I/O 密集型任务:这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。
<!-- Checks the placement of right curly braces ('}') for else, try, and catch tokens. The policy to verify is specified using property option. option: 右大括号是否单独一行显示 tokens: 定义检查的类型 --> <modulename="RightCurly"> <propertyname="option"value="alone"/> </module>
<!-- Checks for illegal instantiations where a factory method is preferred. Rationale: Depending on the project, for some classes it might be preferable to create instances through factory methods rather than calling the constructor. A simple example is the java.lang.Boolean class. In order to save memory and CPU cycles, it is preferable to use the predefined constants TRUE and FALSE. Constructor invocations should be replaced by calls to Boolean.valueOf(). Some extremely performance sensitive projects may require the use of factory methods for other classes as well, to enforce the usage of number caches or object pools. --> <modulename="IllegalInstantiation"> <propertyname="classes"value="java.lang.Boolean"/> </module>
<!-- Checks for Naming Conventions. 命名规范 --> <!-- local, final variables, including catch parameters --> <modulename="LocalFinalVariableName"/>
<!-- local, non-final variables, including catch parameters--> <modulename="LocalVariableName"/>
<!-- Checks for redundant exceptions declared in throws clause such as duplicates, unchecked exceptions or subclasses of another declared exception. 检查是否抛出了多余的异常 <module name="RedundantThrows"> <property name="logLoadErrors" value="true"/> <property name="suppressLoadErrors" value="true"/> </module> -->
<!-- Checks for overly complicated boolean expressions. Currently finds code like if (b == true), b || true, !false, etc. 检查boolean值是否冗余的地方 Rationale: Complex boolean logic makes code hard to understand and maintain. --> <modulename="SimplifyBooleanExpression"/>
<!-- Checks for overly complicated boolean return statements. For example the following code 检查是否存在过度复杂的boolean返回值 if (valid()) return false; else return true; could be written as return !valid(); The Idea for this Check has been shamelessly stolen from the equivalent PMD rule. --> <modulename="SimplifyBooleanReturn"/>
<!-- Checks that a class which has only private constructors is declared as final.只有私有构造器的类必须声明为final--> <modulename="FinalClass"/>
<!-- Make sure that utility classes (classes that contain only static methods or fields in their API) do not have a public constructor. 确保Utils类(只提供static方法和属性的类)没有public构造器。 Rationale: Instantiating utility classes does not make sense. Hence the constructors should either be private or (if you want to allow subclassing) protected. A common mistake is forgetting to hide the default constructor. If you make the constructor protected you may want to consider the following constructor implementation technique to disallow instantiating subclasses: public class StringUtils // not final to allow subclassing { protected StringUtils() { throw new UnsupportedOperationException(); // prevents calls from subclass } public static int count(char c, String s) { // ... } } <module name="HideUtilityClassConstructor"/> -->
<!-- Checks visibility of class members. Only static final members may be public; other class members must be private unless property protectedAllowed or packageAllowed is set. 检查class成员属性可见性。只有static final 修饰的成员是可以public的。其他的成员属性必需是private的,除非属性protectedAllowed或者packageAllowed设置了true. Public members are not flagged if the name matches the public member regular expression (contains "^serialVersionUID$" by default). Note: Checkstyle 2 used to include "^f[A-Z][a-zA-Z0-9]*$" in the default pattern to allow CMP for EJB 1.1 with the default settings. With EJB 2.0 it is not longer necessary to have public access for persistent fields, hence the default has been changed. Rationale: Enforce encapsulation. 强制封装 --> <modulename="VisibilityModifier"/>
<!-- Checks the style of array type definitions. Some like Java-style: public static void main(String[] args) and some like C-style: public static void main(String args[]) 检查再定义数组时,采用java风格还是c风格,例如:int[] num是java风格,int num[]是c风格。默认是java风格--> <modulename="ArrayTypeStyle"> </module>
<!-- Checks that there are no "magic numbers", where a magic number is a numeric literal that is not defined as a constant. By default, -1, 0, 1, and 2 are not considered to be magic numbers. <module name="MagicNumber"> </module> -->
<!-- A check for TODO: comments. Actually it is a generic regular expression matcher on Java comments. To check for other patterns in Java comments, set property format. 检查是否存在TODO(待处理) TODO是javaIDE自动生成的。一般代码写完后要去掉。 --> <modulename="TodoComment"/>
<!-- Checks that long constants are defined with an upper ell. That is ' L' and not 'l'. This is in accordance to the Java Language Specification, Section 3.10.1. 检查是否在long类型是否定义了大写的L.字母小写l和数字1(一)很相似。 looks a lot like 1. --> <modulename="UpperEll"/>
<!-- Checks that switch statement has "default" clause. 检查switch语句是否有‘default’从句 Rationale: It's usually a good idea to introduce a default case in every switch statement. Even if the developer is sure that all currently possible cases are covered, this should be expressed in the default branch, e.g. by using an assertion. This way the code is protected aginst later changes, e.g. introduction of new types in an enumeration type. --> <modulename="MissingSwitchDefault"/>
<!-- Checks the number of parameters of a method or constructor. max default 7个. --> <modulename="ParameterNumber"> <propertyname="max"value="5"/> </module>
<!-- Checks for long methods and constructors. max default 150行. max=300 设置长度300 --> <modulename="MethodLength"> <propertyname="max"value="300"/> </module>
$ ls | grep mysql mysql-community.repo mysql-community-source.repo
更新 yum:
1 2
yum clean all yum makecache
(3)查看 rpm 安装状态
1 2 3 4 5 6
$ yum search mysql | grep server mysql-community-common.i686 : MySQL database common files for server and client mysql-community-common.x86_64 : MySQL database common files for server and mysql-community-test.x86_64 : Test suite for the MySQL database server : administering MySQL servers mysql-community-server.x86_64 : A very fast and reliable SQL database server
$ mysql -h 127.0.0.1 -P 3306 -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 13501 Server version: 8.0.19 MySQL Community Server - GPL
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
查看连接
连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。
-- 为指定用户配置指定权限 GRANT privileges ON databasename.tablename TO'username'@'host'WITHGRANT OPTION; -- 为 root 用户分配所有权限 GRANTALLON*.*TO'root'@'%' IDENTIFIED BY'密码'WITHGRANT OPTION;
撤销授权
命令:
1
REVOKE privilege ON databasename.tablename FROM'username'@'host';
说明:
privilege, databasename, tablename:同授权部分
例子:
1
REVOKESELECTON*.*FROM'pig'@'%';
注意:
假如你在给用户'pig'@'%'授权的时候是这样的(或类似的):GRANT SELECT ON test.user TO 'pig'@'%',则在使用REVOKE SELECT ON *.* FROM 'pig'@'%';命令并不能撤销该用户对 test 数据库中 user 表的SELECT 操作。相反,如果授权使用的是GRANT SELECT ON *.* TO 'pig'@'%';则REVOKE SELECT ON test.user FROM 'pig'@'%';命令也不能撤销该用户对 test 数据库中 user 表的Select权限。
具体信息可以用命令SHOW GRANTS FOR 'pig'@'%'; 查看。
查看授权
1 2
-- 查看用户权限 SHOW GRANTS FOR'root'@'%';
更改用户密码
1
SET PASSWORD FOR'username'@'host'= PASSWORD('newpassword');
-- a. 创建 slave 用户 CREATEUSER'slave'@'%' IDENTIFIED WITH mysql_native_password BY'密码'; -- 为 slave 赋予 REPLICATION SLAVE 权限 GRANT REPLICATION SLAVE ON*.*TO'slave'@'%';
-- b. 或者,创建 slave 用户,并指定该用户能在任意主机上登录 -- 如果有多个从节点,又想让所有从节点都使用统一的用户名、密码认证,可以考虑这种方式 CREATEUSER'slave'@'%' IDENTIFIED WITH mysql_native_password BY'密码'; GRANT REPLICATION SLAVE ON*.*TO'slave'@'%';
-- 刷新授权表信息 FLUSH PRIVILEGES;
注意:在 MySQL 8 中,默认密码验证不再是 password。所以在创建用户时,create user 'username'@'%' identified by 'password'; 客户端是无法连接服务的。所以,需要加上 IDENTIFIED WITH mysql_native_password BY 'password'
补充用户管理 SQL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-- 查看所有用户 SELECTDISTINCT CONCAT('User: ''', user, '''@''', host, ''';') AS query FROM mysql.user;
-- 查看用户权限 SHOW GRANTS FOR'root'@'%';
-- 创建用户 -- a. 创建 slave 用户,并指定该用户只能在主机 192.168.8.11 上登录 CREATEUSER'slave'@'192.168.8.11' IDENTIFIED WITH mysql_native_password BY'密码'; -- 为 slave 赋予 REPLICATION SLAVE 权限 GRANT REPLICATION SLAVE ON*.*TO'slave'@'192.168.8.11';
MASTER_LOG_FILE 和 MASTER_LOG_POS 参数要分别与 show master status 指令获得的 File 和 Position 属性值对应。
MASTER_HOST 是主节点的 HOST。
MASTER_USER 和 MASTER_PASSWORD 是在主节点上注册的用户及密码。
(4)启动 slave 进程
1
mysql>start slave;
(5)查看主从同步状态
1
mysql>show slave status\G;
说明:如果以下两项参数均为 YES,说明配置正确。
Slave_IO_Running
Slave_SQL_Running
(6)将从节点设为只读
1 2 3 4 5 6 7 8 9 10 11
mysql>setglobal read_only=1; mysql>setglobal super_read_only=1; mysql>showglobal variables like "%read_only%"; +-----------------------+-------+ | Variable_name |Value| +-----------------------+-------+ | innodb_read_only | OFF | | read_only |ON| | super_read_only |ON| | transaction_read_only | OFF | +-----------------------+-------+
注:设置 slave 服务器为只读,并不影响主从同步。
慢查询
查看慢查询是否开启
1
show variables like'%slow_query_log';
可以通过 set global slow_query_log 命令设置慢查询是否开启:ON 表示开启;OFF 表示关闭。
1
setglobal slow_query_log='ON';
查看慢查询时间阈值
1
show variables like'%long_query_time%';
设置慢查询阈值
1
setglobal long_query_time =3;
隔离级别
查看隔离级别:
1 2 3 4 5 6 7 8 9 10 11
mysql>show variables like'transaction_isolation';
+-----------------------+----------------+
| Variable_name |Value|
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
MySQL 配置
大部分情况下,默认的基本配置已经足够应付大多数场景,不要轻易修改 MySQL 服务器配置,除非你明确知道修改项是有益的。
尽量不要使用 MySQL 的缓存功能,因为其要求每次请求参数完全相同,才能命中缓存。这种方式实际上并不高效,还会增加额外开销,实际业务场景中一般使用 Redis 等 key-value 存储来解决缓存问题,性能远高于 MySQL 的查询缓存。
配置文件路径
配置 MySQL 首先要确定配置文件在哪儿。
不同 Linux 操作系统上,MySQL 配置文件路径可能不同。通常的路径为 /etc/my.cnf 或 /etc/mysql/my.cnf 。
如果不知道配置文件路径,可以尝试以下操作:
1 2 3 4 5
# which mysqld /usr/sbin/mysqld # /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options' Default options are read from the following files in the given order: /etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
# 说明:影响 Innodb 缓冲区的刷新算法,建议从小到大配置,直到 zero free pages;innodb_lru_scan_depth \* innodb_buffer_pool_instances defines the amount of work performed by the page cleaner thread each second。 # 默认值 1024,建议值: 未知 innodb_lru_scan_depth = 1024
# 说明:默认为空,使用 data 目录,一般不改。 innodb_data_home_dir=/usr/local/mysql-5.7.21/data
# 说明:Defines the name,size,and attributes of InnoDB system tablespace data files。 # 默认值,不指定,默认为 ibdata1:12M:autoextend innodb_data_file_path = ibdata1:12M:autoextend