014、許可權和鎖
阿新 • • 發佈:2021-06-21
MySQL賬戶許可權控制
- 許可權申請流程要設定規範,合理,讓需求不明確者知難而退
- 辦公開發和測試環境可以放開許可權,測試和正式環境要嚴格控制資料庫寫許可權,並且讀許可權和對外業務服務分離
- 開發人員正式環境資料庫許可權分配原則:給單獨的不對外服務的正式庫只讀許可權,不能分配正式主庫寫許可權
- 特殊人員:需要許可權時,我們要問清楚做什麼,發郵件說明,儘量都由DBA全權代理
- 特殊賬號all privileges必須由DBA控制
mysql> show variables like 'read_only%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | read_only | OFF | +---------------+-------+ 1 row in set (0.00 sec) --該引數如果是on的狀態,針對具有update,delete,insert的許可權的普通賬號,會禁止DML操作 --但是對具有all privileges許可權或者root賬號不起作用。 --實驗 mysql> grant update,delete,insert on test.* to 'test_user'@'%' identified by '123456'; Query OK, 0 rows affected (0.06 sec) mysql> grant select on test.* to test_user; Query OK, 0 rows affected (0.01 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) [root@localhost ~]# mysql -utest_user -p123456 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | test | +--------------------+ 2 rows in set (0.00 sec) mysql> use test; mysql> select * from t1; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> insert into t1 select 2; ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
web賬戶許可權分配製度
- 寫庫賬號預設許可權為:select,insert,update,delete,不要給建表、改表(create,alter)等許可權
- 讀庫賬號預設許可權為select(配合MySQL read-only引數使用),確保從庫對所有非super許可權是隻讀的。
- 最好專庫專賬號,不要一個賬號管理多個庫,庫特別多的小公司根據情況特殊對待管理。
- 避免root使用者作為web連線用。
- web和資料庫分離的伺服器授權可以按ip或網段授權。
鎖的介紹
鎖是資料庫系統區別於檔案系統的一個關鍵特性,鎖機制用於管理對共享資源的併發訪問。innodb儲存引擎會在行級別上對錶資料上鎖,這固然不錯,不過innodb儲存引擎也會在資料庫內部其他多個地方使用鎖,從而允許對多種不同資源提供併發訪問。操作快取池的LRU列表,刪除,新增,移動LRU列表中的元素,為了保證一致性,必須有鎖的介入,資料庫系統使用鎖是為了支援對共享資源進行併發訪問,提供資料的完整性和一致性。 MySQL對錶加鎖(innodb)是在索引上,而Oracle是在資料塊上。 鎖型別- 共享鎖:s lock,允許事務讀一行資料;
- 排他鎖:x lock,允許事務刪除或者更新一行資料;
mysql> insert into t values(1,'a'),(2,'b'),(3,'c'),(4,'d');
Query OK, 4 rows affected (0.01 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from t;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
+------+------+
4 rows in set (0.00 sec)
第一個會話開啟事務:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update t set name='aa' where id = 1;
Query OK, 1 row affected (0.07 sec)
Rows matched: 1 Changed: 1 Warnings: 0
第二個會話更新同樣的行:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update t set name='aaa' where id=1; --發現一直在等待
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> show variables like '%innodb_lock_wait_timeout%'; --等待超時的時間
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set (0.00 sec)
第二個案例,鎖阻塞:
mysql> insert into t1 select 1;
Query OK, 1 row affected (0.06 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into t1 select 2;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into t1 select 3;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into t1 select 4;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into t1 select 7;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into t1 select 8;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select * from t1 where id < 6 lock in share mode;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
4 rows in set (0.00 sec)
第二個會話:
mysql> insert into t1 select 5;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
以上的現象叫做鎖堵塞。
鎖等待超時時間設定:
mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set (0.00 sec)
通過表檢視鎖情況
在information_schema庫下,有3張表可以很直觀的顯示鎖的情況: INNODB_LOCKS,INNODB_TRX,INNODB_LOCK_WAITS 這三張表都是memory儲存引擎。mysql> show create table INNODB_LOCKS \G;
*************************** 1. row ***************************
Table: INNODB_LOCKS
Create Table: CREATE TEMPORARY TABLE `INNODB_LOCKS` (
`lock_id` varchar(81) NOT NULL DEFAULT '',
`lock_trx_id` varchar(18) NOT NULL DEFAULT '',
`lock_mode` varchar(32) NOT NULL DEFAULT '',
`lock_type` varchar(32) NOT NULL DEFAULT '',
`lock_table` varchar(1024) NOT NULL DEFAULT '',
`lock_index` varchar(1024) DEFAULT NULL,
`lock_space` bigint(21) unsigned DEFAULT NULL,
`lock_page` bigint(21) unsigned DEFAULT NULL,
`lock_rec` bigint(21) unsigned DEFAULT NULL,
`lock_data` varchar(8192) DEFAULT NULL
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
只有在有鎖的情況下,這三個表才有資料:
mysql> select * from INNODB_LOCKS;
+---------------+-------------+-----------+-----------+-------------+-----------------+------------+-----------+----------+------------------------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+---------------+-------------+-----------+-----------+-------------+-----------------+------------+-----------+----------+------------------------+
| 274754:84:3:1 | 274754 | X | RECORD | `test`.`t1` | GEN_CLUST_INDEX | 84 | 3 | 1 | supremum pseudo-record |
| 274753:84:3:1 | 274753 | S | RECORD | `test`.`t1` | GEN_CLUST_INDEX | 84 | 3 | 1 | supremum pseudo-record |
+---------------+-------------+-----------+-----------+-------------+-----------------+------------+-----------+----------+------------------------+
2 rows in set (0.00 sec)
mysql> select * from INNODB_TRX;
+--------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
| trx_id | trx_state | trx_started | trx_requested_lock_id | trx_wait_started | trx_weight | trx_mysql_thread_id | trx_query | trx_operation_state | trx_tables_in_use | trx_tables_locked | trx_lock_structs | trx_lock_memory_bytes | trx_rows_locked | trx_rows_modified | trx_concurrency_tickets | trx_isolation_level | trx_unique_checks | trx_foreign_key_checks | trx_last_foreign_key_error | trx_adaptive_hash_latched | trx_adaptive_hash_timeout | trx_is_read_only | trx_autocommit_non_locking |
+--------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
| 274754 | LOCK WAIT | 2021-04-20 15:46:36 | 274754:84:3:1 | 2021-04-20 15:46:36 | 2 | 4 | insert into t1 select 5 | inserting | 1 | 1 | 2 | 360 | 1 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
| 274753 | RUNNING | 2021-04-20 14:38:13 | NULL | NULL | 2 | 1 | NULL | NULL | 0 | 0 | 2 | 360 | 7 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
+--------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
2 rows in set (0.00 sec)
mysql> select * from INNODB_LOCK_WAITS;
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| 274754 | 274754:84:3:1 | 274753 | 274753:84:3:1 |
+-------------------+-------------------+-----------------+------------------+
1 row in set (0.00 sec)
以上三張表,可以放在shell腳本里,結合grep、awk過濾正在執行的sql,佔用鎖的事務和表。
一致性非鎖定讀
一致性的非鎖定行讀: 是指innodb儲存引擎通過多版本控制的方式來讀取當前執行時間資料庫中行的資料。 如果讀取的行正在執行delete\update操作,這時讀取操作不會因此等待行上X鎖的釋放,相反innodb儲存引擎會去讀取行的一個快照資料,不需要等待訪問的行上的x鎖的釋放。 快照資料是指該行之前版本的資料,原理是UNDO段來實現,而UNDO用來在事務中回滾資料,因此快照資料本身沒有額外的開銷。 此外讀取快照資料是不需要上鎖的,因為沒有必要對歷史的資料進行修改。 非鎖定讀的機制大大提高了資料讀取的併發性,在innodb儲存引擎預設設定下,這是預設的讀取方式,即讀取不會佔用和等待表上的鎖,但在不同事務隔離級別下,讀取的方式不同,並不是每個事務隔離級別下讀取的都是一致性讀。 不同的事務隔離級別,快照資料的定義不相同:- 在read-committed事務隔離級別下,對於快照資料,非一致性讀總是讀取被鎖定行的最新一份快照資料;
- 在repeatable-read事務隔離級別下,對於快照資料,非一致性讀總是讀取事務開始時的行資料版本。
對select加鎖
innodb儲存引擎對select語句加鎖:- select …… for update對讀取的行記錄加一個X鎖,其他事務想在這些行上加任何鎖都會被阻塞;(read new version)
- select …… lock in share mode對讀取的行記錄加一個S鎖,其他事務可以向被鎖定的記錄加S鎖,但對於加X鎖,則會被阻塞
- select …… for update,select …… lock in share mode必須在一個事務中,當事務提交了,鎖也就釋放了,在使用上述兩句select鎖定語句時,務必加上begin,start transaction或者set autocommit=0
自增長和鎖
自增長在資料庫中是非常常見的一種屬性,也是很多dba或開發人員首選的主鍵方式。 在innodb儲存引擎的記憶體結構中,每個含有自增值的表都有一個增長計數器,當對有自增長計數器的表進行插入操作時,這個計數器會被初始化。 插入操作會根據這個自增長的計數器值加1賦自增長列,這個實現方式稱作auto-inc-locking,這種鎖其實是採用一種特殊的表鎖機制,為了提高插入效能,鎖並不是在一個事務完成後才釋放,而是在完成對自增長插入的sql語句後立即釋放。 auto-inc-locking從一定程度上提高了併發插入的效率,但這裡還是存在一些問題:- 首先對於有自增長值的列的併發插入效能較差,所以必須等待前一個插入的完成;
- 其次對於insert …… select的大資料量的插入,會影響插入的效能,因為另一個事務中的插入會被阻塞;
- INSERT-like:INSERT-like指所有的插入語句,如insert、replace、insert ... select、replace ... select、load data等;
- simple inserts:指能在插入前就確定插入行數的語句。這些語句包括insert、replace等。需要注意的是:simple insert不包含insert ... on dumplcate key update這類sql語句;
- bulk inserts:指在插入前不能確定得到插入行數的語句,如insert .. select、replace ... select、load data。
- innodb儲存引擎中的實現和myisam不同,myisam是表鎖的,自增長不用考慮併發插入的問題。因此在master用innodb儲存引擎,salve用myisam儲存引擎的replication架構下,必須考慮這種情況;
- innodb儲存引擎下,自增長的列必須是索引,並且是索引的第一個列,如果是第二個列則會報錯:
mysql> create table t( -> a int auto_increment, -> b int, -> key(b,a) -> ); ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
鎖的演算法
innodb儲存引擎有3種行鎖的演算法設計:- record lock:單個行記錄上的鎖;
- gap lock:間隙鎖,鎖定一個範圍,但不包括記錄本身;
- next-key lock:★鎖定一個範圍,並且鎖定記錄本身。