The fix looks good!
Managed databases often limit an access to these low-level things.
In the documentation you've quoted → there is and
The "Other methods to change the transaction isolation level" chapter. It contains an acceptable workaround by tuning the settings.php file, which works just fine.
To me it seems you do not have to clear __scopes manually; if you delete an entity in a normal way, all the scopes should gone as well.
I spotted your thread today because we had a similar situation - hundreds of thousands of entries in these tables. As a result of debugging it revealed that for months the oauth2_server_cron() function was unable to complete because of insufficient memory. 1 GB was not enough, and when I changed this to 3 GB the function was able to terminate - but it took 14 minutes, so better do it via drush.
For the future - I manually added the simple indexes in the database for the 'expires' column, since these conditions are asked for it.
$query = \Drupal::entityQuery($entity_type);
$query->condition('expires', 0, '<>');
$query->condition('expires', $request_time, '<=');
Indexes creation SQL:
CREATE INDEX oauth2_server_authorization_code__expires on oauth2_server_authorization_code (expires);
CREATE INDEX oauth2_server_token__expires on oauth2_server_token (expires);
Maybe there is a healthier way to define these indexes, via editing the @ContentEntityType definition, but it should be good as well since you are already in the direct database manipulation.